7#include <QGuiApplication>
11#include <QTemporaryFile>
14#include <QOperatingSystemVersion>
16#include <QtCore/private/qcore_mac_p.h>
17#include <QtGui/private/qmetallayer_p.h>
18#include <QtGui/qpa/qplatformwindow_p.h>
21#include <AppKit/AppKit.h>
23#include <UIKit/UIKit.h>
26#include <QuartzCore/CATransaction.h>
28#include <Metal/Metal.h>
35
36
37
38
39
40
41
42
43
46#error ARC not supported
55#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
60#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
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
97
98
99
103
104
105
106
107
108
109
110
113
114
115
116
119
120
121
122
123
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
147
148
151
152
156 id<MTLLibrary> lib = nil;
157 id<MTLFunction> func = nil;
158 std::array<uint, 3> localSize = {};
159 uint outputVertexCount = 0;
160 QShaderDescription desc;
161 QShader::NativeResourceBindingMap nativeResourceBindingMap;
162 QShader::NativeShaderInfo nativeShaderInfo;
165 nativeResourceBindingMap.clear();
184 const QColor &colorClearValue,
185 const QRhiDepthStencilClearValue &depthStencilClearValue,
187 QRhiShadingRateMap *shadingRateMap);
189 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
218 id<MTLTexture> texture;
437 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
446 return patchCount *
sizeof(
float) * 128;
494 if (importDevice->dev) {
495 d->dev = (id<MTLDevice>) importDevice->dev;
497 if (importedCmdQueue)
498 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
500 qWarning(
"No MTLDevice given, cannot import");
514 return (v + byteAlign - 1) & ~(byteAlign - 1);
519 QMacAutoReleasePool pool;
522 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
536 return [cmdQueue commandBufferWithUnretainedReferences];
538 return [cmdQueue commandBuffer];
549 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
550 binArchDesc.url = sourceFileUrl;
552 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
553 [binArchDesc release];
555 const QString msg = QString::fromNSString(err.localizedDescription);
556 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
569 d->dev = MTLCreateSystemDefaultDevice();
572 qWarning(
"No MTLDevice");
576 const QString deviceName = QString::fromNSString([d->dev name]);
577 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
578 driverInfoStruct.deviceName = deviceName.toUtf8();
585 const MTLDeviceLocation deviceLocation = [d->dev location];
586 switch (deviceLocation) {
587 case MTLDeviceLocationBuiltIn:
588 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
590 case MTLDeviceLocationSlot:
591 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
593 case MTLDeviceLocationExternal:
594 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
600 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
603 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
604 osMajor = ver.majorVersion();
605 osMinor = ver.minorVersion();
607 if (importedCmdQueue)
608 [d->cmdQueue retain];
610 d->cmdQueue = [d->dev newCommandQueue];
612 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
616 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
617 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
618 d->captureScope.label = label.toNSString();
620#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
621 caps.maxTextureSize = 16384;
622 caps.baseVertexAndInstance =
true;
623 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
624 caps.maxThreadGroupSize = 1024;
625 caps.multiView =
true;
626#elif defined(Q_OS_TVOS)
627 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
628 caps.maxTextureSize = 16384;
630 caps.maxTextureSize = 8192;
631 caps.baseVertexAndInstance =
false;
632 caps.isAppleGPU =
true;
633#elif defined(Q_OS_IOS)
634 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
635 caps.maxTextureSize = 16384;
636 caps.baseVertexAndInstance =
true;
637 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
638 caps.maxTextureSize = 8192;
639 caps.baseVertexAndInstance =
false;
641 caps.maxTextureSize = 4096;
642 caps.baseVertexAndInstance =
false;
644 caps.isAppleGPU =
true;
645 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
646 caps.maxThreadGroupSize = 1024;
647 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
648 caps.multiView =
true;
651 caps.supportedSampleCounts = { 1 };
652 for (
int sampleCount : { 2, 4, 8 }) {
653 if ([d->dev supportsTextureSampleCount: sampleCount])
654 caps.supportedSampleCounts.append(sampleCount);
657 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
658 if (caps.shadingRateMap && caps.multiView)
659 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
662 caps.depthClamp = [d->dev supportsFamily:MTLGPUFamilyApple3];
664 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
665 d->setupBinaryArchive();
667 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
668 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
678 for (QMetalShader &s : d->shaderCache)
680 d->shaderCache.clear();
682 [d->captureScope release];
683 d->captureScope = nil;
685 [d->binArch release];
688 [d->cmdQueue release];
689 if (!importedCmdQueue)
699 return caps.supportedSampleCounts;
704 Q_UNUSED(sampleCount);
705 return { QSize(1, 1) };
710 return new QMetalSwapChain(
this);
713QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
715 return new QMetalBuffer(
this, type, usage, size);
742 if (m.isIdentity()) {
744 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
745 0.0f, 1.0f, 0.0f, 0.0f,
746 0.0f, 0.0f, 0.5f, 0.5f,
747 0.0f, 0.0f, 0.0f, 1.0f);
756 bool supportsFamilyMac2 =
false;
757 bool supportsFamilyApple3 =
false;
760 supportsFamilyMac2 =
true;
762 supportsFamilyApple3 =
true;
764 supportsFamilyApple3 =
true;
768 if (format == QRhiTexture::BC5)
771 if (!supportsFamilyApple3) {
772 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
774 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
778 if (!supportsFamilyMac2)
779 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
788 case QRhi::MultisampleTexture:
790 case QRhi::MultisampleRenderBuffer:
792 case QRhi::DebugMarkers:
794 case QRhi::Timestamps:
796 case QRhi::Instancing:
798 case QRhi::CustomInstanceStepRate:
800 case QRhi::PrimitiveRestart:
802 case QRhi::NonDynamicUniformBuffers:
804 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
806 case QRhi::NPOTTextureRepeat:
808 case QRhi::RedOrAlpha8IsRed:
810 case QRhi::ElementIndexUint:
814 case QRhi::WideLines:
816 case QRhi::VertexShaderPointSize:
818 case QRhi::BaseVertex:
819 return caps.baseVertexAndInstance;
820 case QRhi::BaseInstance:
821 return caps.baseVertexAndInstance;
822 case QRhi::TriangleFanTopology:
824 case QRhi::ReadBackNonUniformBuffer:
826 case QRhi::ReadBackNonBaseMipLevel:
828 case QRhi::TexelFetch:
830 case QRhi::RenderToNonBaseMipLevel:
832 case QRhi::IntAttributes:
834 case QRhi::ScreenSpaceDerivatives:
836 case QRhi::ReadBackAnyTextureFormat:
838 case QRhi::PipelineCacheDataLoadSave:
840 case QRhi::ImageDataStride:
842 case QRhi::RenderBufferImport:
844 case QRhi::ThreeDimensionalTextures:
846 case QRhi::RenderTo3DTextureSlice:
848 case QRhi::TextureArrays:
850 case QRhi::Tessellation:
852 case QRhi::GeometryShader:
854 case QRhi::TextureArrayRange:
856 case QRhi::NonFillPolygonMode:
858 case QRhi::OneDimensionalTextures:
860 case QRhi::OneDimensionalTextureMipmaps:
862 case QRhi::HalfAttributes:
864 case QRhi::RenderToOneDimensionalTexture:
866 case QRhi::ThreeDimensionalTextureMipmaps:
868 case QRhi::MultiView:
869 return caps.multiView;
870 case QRhi::TextureViewFormat:
872 case QRhi::ResolveDepthStencil:
874 case QRhi::VariableRateShading:
876 case QRhi::VariableRateShadingMap:
877 return caps.shadingRateMap;
878 case QRhi::VariableRateShadingMapWithTexture:
880 case QRhi::PerRenderTargetBlending:
881 case QRhi::SampleVariables:
883 case QRhi::InstanceIndexIncludesBaseInstance:
885 case QRhi::DepthClamp:
886 return caps.depthClamp;
896 case QRhi::TextureSizeMin:
898 case QRhi::TextureSizeMax:
899 return caps.maxTextureSize;
900 case QRhi::MaxColorAttachments:
902 case QRhi::FramesInFlight:
904 case QRhi::MaxAsyncReadbackFrames:
906 case QRhi::MaxThreadGroupsPerDimension:
908 case QRhi::MaxThreadsPerThreadGroup:
910 case QRhi::MaxThreadGroupX:
912 case QRhi::MaxThreadGroupY:
914 case QRhi::MaxThreadGroupZ:
915 return caps.maxThreadGroupSize;
916 case QRhi::TextureArraySizeMax:
918 case QRhi::MaxUniformBufferRange:
920 case QRhi::MaxVertexInputs:
922 case QRhi::MaxVertexOutputs:
924 case QRhi::ShadingRateImageTileSize:
934 return &nativeHandlesStruct;
939 return driverInfoStruct;
945 result.totalPipelineCreationTime = totalPipelineCreationTime();
955void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
962 for (QMetalShader &s : d->shaderCache)
965 d->shaderCache.clear();
987 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
992 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
997 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
998 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1000 if (![d->binArch serializeToURL: url error: &err]) {
1001 const QString msg = QString::fromNSString(err.localizedDescription);
1003 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1008 if (!f.open(QIODevice::ReadOnly)) {
1009 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1012 const QByteArray blob = f.readAll();
1016 const quint32 dataSize = quint32(blob.size());
1018 data.resize(headerSize + dataSize);
1021 header.rhiId = pipelineCacheRhiId();
1022 header.arch = quint32(
sizeof(
void*));
1023 header.dataSize = quint32(dataSize);
1024 header.osMajor = osMajor;
1025 header.osMinor = osMinor;
1026 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1028 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1029 header.driver[driverStrLen] =
'\0';
1031 memcpy(data.data(), &header, headerSize);
1032 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1042 if (data.size() < qsizetype(headerSize)) {
1043 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1047 const size_t dataOffset = headerSize;
1049 memcpy(&header, data.constData(), headerSize);
1051 const quint32 rhiId = pipelineCacheRhiId();
1052 if (header.rhiId != rhiId) {
1053 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1054 rhiId, header.rhiId);
1058 const quint32 arch = quint32(
sizeof(
void*));
1059 if (header.arch != arch) {
1060 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1065 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1066 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1067 osMajor, osMinor, header.osMajor, header.osMinor);
1071 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1072 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1073 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1077 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1078 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1082 const char *p = data.constData() + dataOffset;
1086 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1089 tmp.write(p, header.dataSize);
1092 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1093 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1094 if (
d->setupBinaryArchive(url))
1095 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1098QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1099 int sampleCount, QRhiRenderBuffer::Flags flags,
1100 QRhiTexture::Format backingFormatHint)
1102 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1106 const QSize &pixelSize,
int depth,
int arraySize,
1107 int sampleCount, QRhiTexture::Flags flags)
1109 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1113 QRhiSampler::Filter mipmapMode,
1114 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1116 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1121 return new QMetalShadingRateMap(
this);
1125 QRhiTextureRenderTarget::Flags flags)
1132 return new QMetalGraphicsPipeline(
this);
1137 return new QMetalComputePipeline(
this);
1142 return new QMetalShaderResourceBindings(
this);
1153 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1156 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1157 if (!map || map->isEmpty())
1160 auto it = map->constFind(binding);
1161 if (it != map->cend())
1172 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1173 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1176 case QMetalShaderResourceBindingsData::VERTEX:
1177 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1178 offsets: offsetBatch.resources.constData()
1179 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1181 case QMetalShaderResourceBindingsData::FRAGMENT:
1182 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1183 offsets: offsetBatch.resources.constData()
1184 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1186 case QMetalShaderResourceBindingsData::COMPUTE:
1187 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1188 offsets: offsetBatch.resources.constData()
1189 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1203 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1206 case QMetalShaderResourceBindingsData::VERTEX:
1207 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1208 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1210 case QMetalShaderResourceBindingsData::FRAGMENT:
1211 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1212 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1214 case QMetalShaderResourceBindingsData::COMPUTE:
1215 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1216 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1230 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1232 switch (encoderStage) {
1233 case QMetalShaderResourceBindingsData::VERTEX:
1234 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1235 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1237 case QMetalShaderResourceBindingsData::FRAGMENT:
1238 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1239 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1241 case QMetalShaderResourceBindingsData::COMPUTE:
1242 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1243 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1265 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1266 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1267 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1268 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1271 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1272 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1273 bindStageTextures(cbD, encoderStage, batch);
1276 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1277 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1278 bindStageSamplers(cbD, encoderStage, batch);
1285 case QMetalShaderResourceBindingsData::VERTEX:
1286 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1287 case QMetalShaderResourceBindingsData::TESSCTRL:
1288 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1289 case QMetalShaderResourceBindingsData::TESSEVAL:
1290 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1291 case QMetalShaderResourceBindingsData::FRAGMENT:
1292 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1293 case QMetalShaderResourceBindingsData::COMPUTE:
1294 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1297 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1302 int dynamicOffsetCount,
1303 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1304 bool offsetOnlyChange,
1305 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1309 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1310 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1312 case QRhiShaderResourceBinding::UniformBuffer:
1314 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1315 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1316 quint32 offset = b->u.ubuf.offset;
1317 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1318 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1319 if (dynOfs.first == b->binding) {
1320 offset = dynOfs.second;
1325 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1326 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1327 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1328 if (nativeBinding >= 0)
1329 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1334 case QRhiShaderResourceBinding::SampledTexture:
1335 case QRhiShaderResourceBinding::Texture:
1336 case QRhiShaderResourceBinding::Sampler:
1338 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1339 for (
int elem = 0; elem < data->count; ++elem) {
1340 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1341 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1343 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1344 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1349 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1350 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1351 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1352 if (textureBinding >= 0 && texD)
1353 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1354 if (samplerBinding >= 0)
1355 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1361 case QRhiShaderResourceBinding::ImageLoad:
1362 case QRhiShaderResourceBinding::ImageStore:
1363 case QRhiShaderResourceBinding::ImageLoadStore:
1365 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1366 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1368 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1369 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1370 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1371 if (nativeBinding >= 0)
1372 bindingData.res[stage].textures.append({ nativeBinding, t });
1377 case QRhiShaderResourceBinding::BufferLoad:
1378 case QRhiShaderResourceBinding::BufferStore:
1379 case QRhiShaderResourceBinding::BufferLoadStore:
1381 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1382 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1383 quint32 offset = b->u.sbuf.offset;
1384 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1385 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1386 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1387 if (nativeBinding >= 0)
1388 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1411 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1412 return a.nativeBinding < b.nativeBinding;
1415 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1416 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1417 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1420 bindingData.res[stage].bufferBatches.finish();
1421 bindingData.res[stage].bufferOffsetBatches.finish();
1423 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1424 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1425 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1427 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1428 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1429 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1430 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1434 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1437 if (offsetOnlyChange)
1440 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1441 return a.nativeBinding < b.nativeBinding;
1444 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1445 return a.nativeBinding < b.nativeBinding;
1448 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1449 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1451 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1452 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1454 bindingData.res[stage].textureBatches.finish();
1455 bindingData.res[stage].samplerBatches.finish();
1457 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1458 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1460 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1461 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1465 bindStageTextures(cbD, stage, batch);
1468 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1469 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1471 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1472 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1476 bindStageSamplers(cbD, stage, batch);
1480 cbD
->d->currentShaderResourceBindingState = bindingData;
1487 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1489 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1490 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1491 cbD
->d->currentDepthStencilState =
d->ds;
1494 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1498 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1501 if (rhiD->caps.depthClamp) {
1503 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1508 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1511 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1514 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1515 slopeScale: d->slopeScaledDepthBias
1532 cbD->currentPipelineGeneration = psD->generation;
1534 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1539 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1540 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1541 workBuf->lastActiveFrameSlot = -1;
1543 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1544 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1545 workBuf->lastActiveFrameSlot = -1;
1548 psD->lastActiveFrameSlot = currentFrameSlot;
1552 int dynamicOffsetCount,
1553 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1562 srb = gfxPsD->m_shaderResourceBindings;
1564 srb = compPsD->m_shaderResourceBindings;
1568 bool hasSlottedResourceInSrb =
false;
1569 bool hasDynamicOffsetInSrb =
false;
1570 bool resNeedsRebind =
false;
1572 bool pipelineChanged =
false;
1585 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1588 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1589 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1592 case QRhiShaderResourceBinding::UniformBuffer:
1595 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1598 hasSlottedResourceInSrb =
true;
1599 if (b->u.ubuf.hasDynamicOffset)
1600 hasDynamicOffsetInSrb =
true;
1601 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1602 resNeedsRebind =
true;
1603 bd.ubuf.id = bufD->m_id;
1604 bd.ubuf.generation = bufD->generation;
1606 bufD->lastActiveFrameSlot = currentFrameSlot;
1609 case QRhiShaderResourceBinding::SampledTexture:
1610 case QRhiShaderResourceBinding::Texture:
1611 case QRhiShaderResourceBinding::Sampler:
1613 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1614 if (bd.stex.count != data->count) {
1615 bd.stex.count = data->count;
1616 resNeedsRebind =
true;
1618 for (
int elem = 0; elem < data->count; ++elem) {
1621 Q_ASSERT(texD || samplerD);
1622 const quint64 texId = texD ? texD->m_id : 0;
1623 const uint texGen = texD ? texD->generation : 0;
1624 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1625 const uint samplerGen = samplerD ? samplerD->generation : 0;
1626 if (texGen != bd.stex.d[elem].texGeneration
1627 || texId != bd.stex.d[elem].texId
1628 || samplerGen != bd.stex.d[elem].samplerGeneration
1629 || samplerId != bd.stex.d[elem].samplerId)
1631 resNeedsRebind =
true;
1632 bd.stex.d[elem].texId = texId;
1633 bd.stex.d[elem].texGeneration = texGen;
1634 bd.stex.d[elem].samplerId = samplerId;
1635 bd.stex.d[elem].samplerGeneration = samplerGen;
1638 texD->lastActiveFrameSlot = currentFrameSlot;
1640 samplerD->lastActiveFrameSlot = currentFrameSlot;
1644 case QRhiShaderResourceBinding::ImageLoad:
1645 case QRhiShaderResourceBinding::ImageStore:
1646 case QRhiShaderResourceBinding::ImageLoadStore:
1649 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1650 resNeedsRebind =
true;
1651 bd.simage.id = texD->m_id;
1652 bd.simage.generation = texD->generation;
1654 texD->lastActiveFrameSlot = currentFrameSlot;
1657 case QRhiShaderResourceBinding::BufferLoad:
1658 case QRhiShaderResourceBinding::BufferStore:
1659 case QRhiShaderResourceBinding::BufferLoadStore:
1662 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1664 if (needsBufferSizeBuffer) {
1665 for (
int i = 0; i < 6; ++i) {
1666 const QRhiShaderResourceBinding::StageFlag stage =
1667 QRhiShaderResourceBinding::StageFlag(1 << i);
1668 if (b->stage.testFlag(stage)) {
1669 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1675 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1676 resNeedsRebind =
true;
1677 bd.sbuf.id = bufD->m_id;
1678 bd.sbuf.generation = bufD->generation;
1680 bufD->lastActiveFrameSlot = currentFrameSlot;
1689 if (needsBufferSizeBuffer) {
1691 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1695 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1696 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1699 if (gfxPsD
->d->tess.enabled) {
1709 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1710 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1711 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1712 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1713 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1714 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1715 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1716 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1717 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1718 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1719 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1720 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1722 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1723 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
1725 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1726 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
1728 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1729 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
1732 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1733 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
1735 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1736 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
1740 for (
const auto &shader : shaders) {
1742 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1745 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1747 int maxNativeBinding = 0;
1748 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1749 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1751 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1753 Q_ASSERT(offset + size <= bufD->size());
1754 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1756 QMetalShaderResourceBindings::BoundResourceData bd;
1757 bd.sbuf.id = bufD->m_id;
1758 bd.sbuf.generation = bufD->generation;
1759 srbD->boundResourceData.append(bd);
1763 QVarLengthArray<
int, 8> bufferSizeBufferData;
1764 Q_ASSERT(storageBufferSizes.contains(shader.second));
1765 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1766 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1767 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1773 if (bufferSizeBufferData.size() <= index)
1774 bufferSizeBufferData.resize(index + 1);
1776 Q_ASSERT(sizes.contains(block.binding));
1777 bufferSizeBufferData[index] = sizes[block.binding];
1780 QRhiBufferData data;
1781 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1782 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1783 Q_ASSERT(offset + size <= bufD->size());
1784 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1787 offset += ((size + 31) / 32) * 32;
1791 bufD->lastActiveFrameSlot = currentFrameSlot;
1795 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1797 resNeedsRebind =
true;
1800 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1803 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1804 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1808 if (gfxPsD
->d->tess.enabled) {
1811 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1812 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1825 cbD->currentSrbGeneration = srbD->generation;
1828 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1829 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1834 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1835 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1840 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1841 QRhiBatchedBindings<NSUInteger> offsets;
1842 for (
int i = 0; i < bindingCount; ++i) {
1845 bufD->lastActiveFrameSlot = currentFrameSlot;
1846 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1847 buffers.feed(startBinding + i, mtlbuf);
1848 offsets.feed(startBinding + i, bindings[i].second);
1863 || buffers != cbD
->d->currentVertexInputsBuffers
1864 || offsets != cbD
->d->currentVertexInputOffsets)
1867 cbD
->d->currentVertexInputsBuffers = buffers;
1868 cbD
->d->currentVertexInputOffsets = offsets;
1870 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1871 const auto &bufferBatch(buffers.batches[i]);
1872 const auto &offsetBatch(offsets.batches[i]);
1873 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1874 bufferBatch.resources.constData()
1875 offsets: offsetBatch.resources.constData()
1876 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1883 ibufD->lastActiveFrameSlot = currentFrameSlot;
1885 cbD->currentIndexOffset = indexOffset;
1886 cbD->currentIndexFormat = indexFormat;
1896 QSize outputSize = cbD->currentTarget->pixelSize();
1902 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1903 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1904 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1905 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1906 auto screenSize = [rateMap screenSize];
1907 outputSize = QSize(screenSize.width, screenSize.height);
1914 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1918 vp.originX =
double(x);
1919 vp.originY =
double(y);
1920 vp.width =
double(w);
1921 vp.height =
double(h);
1922 vp.znear =
double(viewport.minDepth());
1923 vp.zfar =
double(viewport.maxDepth());
1925 [cbD->d->currentRenderPassEncoder setViewport: vp];
1930 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1931 s.x = NSUInteger(x);
1932 s.y = NSUInteger(y);
1933 s.width = NSUInteger(w);
1934 s.height = NSUInteger(h);
1935 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1945 const QSize outputSize = cbD->currentTarget->pixelSize();
1949 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1953 s.x = NSUInteger(x);
1954 s.y = NSUInteger(y);
1955 s.width = NSUInteger(w);
1956 s.height = NSUInteger(h);
1958 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1966 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
1967 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
1975 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
1981 Q_UNUSED(coarsePixelSize);
1986 if (cbD
->d->currentRenderPassEncoder) {
1987 [cbD->d->currentRenderPassEncoder endEncoding];
1988 cbD->d->currentRenderPassEncoder = nil;
1991 if (!cbD->d->tessellationComputeEncoder)
1992 cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
1994 return cbD
->d->tessellationComputeEncoder;
1999 if (cbD
->d->tessellationComputeEncoder) {
2000 [cbD->d->tessellationComputeEncoder endEncoding];
2001 cbD->d->tessellationComputeEncoder = nil;
2006 switch (cbD->currentTarget->resourceType()) {
2007 case QRhiResource::SwapChainRenderTarget:
2010 case QRhiResource::TextureRenderTarget:
2019 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2021 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2022 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2023 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2026 MTLLoadAction oldDepthLoad;
2027 MTLLoadAction oldStencilLoad;
2029 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2030 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2031 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2033 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2034 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2035 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2038 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2042 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2046 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2047 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2056 if (graphicsPipeline
->d->tess.failed)
2060 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2061 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2065 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2071 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
2075 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2076 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2077 if (args.type == TessDrawArgs::U16Indexed)
2078 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2079 else if (args.type == TessDrawArgs::U32Indexed)
2080 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2081 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2082 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2083 [computeEncoder setComputePipelineState: computePipelineState];
2088 cbD
->d->currentComputePassEncoder = computeEncoder;
2090 cbD->d->currentComputePassEncoder = nil;
2092 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2093 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2094 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2096 if (outputBufferBinding >= 0) {
2097 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2098 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2101 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2104 if (indexBufferBinding >= 0)
2105 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2107 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2108 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2109 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2110 [computeEncoder setBuffers: bufferBatch.resources.constData()
2111 offsets: offsetBatch.resources.constData()
2112 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2116 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2117 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2119 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2120 args.draw.vertexCount, args.draw.instanceCount)];
2123 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2124 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2129 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2130 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2131 [computeEncoder setComputePipelineState: computePipelineState];
2133 cbD
->d->currentComputePassEncoder = computeEncoder;
2135 cbD->d->currentComputePassEncoder = nil;
2137 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2138 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2139 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2140 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2141 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2142 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2144 if (outputBufferBinding >= 0) {
2145 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2146 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2149 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2152 if (patchOutputBufferBinding >= 0) {
2153 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2154 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2155 if (!tescPatchOutBuf)
2157 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2160 if (tessFactorBufferBinding >= 0) {
2161 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2162 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2165 if (paramsBufferBinding >= 0) {
2167 quint32 inControlPointCount;
2174 params.patchCount = patchCount;
2175 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2176 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2177 memcpy(p, ¶ms,
sizeof(params));
2178 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2181 if (vertOutBuf && inputBufferBinding >= 0)
2182 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2184 int sgSize =
int(computePipelineState.threadExecutionWidth);
2185 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2186 while (wgSize > caps.maxThreadGroupSize) {
2188 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2190 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2191 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2208 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2213 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2214 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2215 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2216 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2218 if (outputBufferBinding >= 0 && tescOutBuf)
2219 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2221 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2222 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2224 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2225 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2226 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2229 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2231 patchCount: patchCount
2232 patchIndexBuffer: nil
2233 patchIndexBufferOffset: 0
2243 if (multiViewCount <= 1)
2247 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2248 if (viewMaskBufBinding == -1) {
2249 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2256 multiViewInfo.viewOffset = 0;
2257 multiViewInfo.viewCount = quint32(multiViewCount);
2261 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2262 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2263 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2264 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2268 *instanceCount *= multiViewCount;
2273 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2282 a.draw.vertexCount = vertexCount;
2283 a.draw.instanceCount = instanceCount;
2284 a.draw.firstVertex = firstVertex;
2285 a.draw.firstInstance = firstInstance;
2290 adjustForMultiViewDraw(&instanceCount, cb);
2292 if (caps.baseVertexAndInstance) {
2293 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2294 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2296 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2297 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2302 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2310 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2311 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2314 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2319 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2320 a.drawIndexed.indexCount = indexCount;
2321 a.drawIndexed.instanceCount = instanceCount;
2322 a.drawIndexed.firstIndex = firstIndex;
2323 a.drawIndexed.vertexOffset = vertexOffset;
2324 a.drawIndexed.firstInstance = firstInstance;
2325 a.drawIndexed.indexBuffer = mtlibuf;
2330 adjustForMultiViewDraw(&instanceCount, cb);
2332 if (caps.baseVertexAndInstance) {
2333 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2334 indexCount: indexCount
2335 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2336 indexBuffer: mtlibuf
2337 indexBufferOffset: indexOffset
2338 instanceCount: instanceCount
2339 baseVertex: vertexOffset
2340 baseInstance: firstInstance];
2342 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2343 indexCount: indexCount
2344 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2345 indexBuffer: mtlibuf
2346 indexBufferOffset: indexOffset
2347 instanceCount: instanceCount];
2356 NSString *str = [NSString stringWithUTF8String: name.constData()];
2358 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2359 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2361 [cbD->d->cb pushDebugGroup: str];
2370 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2371 [cbD->d->currentRenderPassEncoder popDebugGroup];
2373 [cbD->d->cb popDebugGroup];
2382 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2383 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2388 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2414 currentFrameSlot = swapChainD->currentFrameSlot;
2419 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2427 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2428 if (sc != swapChainD)
2429 sc->waitUntilCompleted(currentFrameSlot);
2432 [d->captureScope beginScope];
2434 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2438 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2445 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2446 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2451 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2454 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2455 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2458 return QRhi::FrameOpSuccess;
2467 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2469 __block
int thisFrameSlot = currentFrameSlot;
2470 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2471 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2472 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2479 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2480 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2481 [drawableTexture release];
2485 if (flags.testFlag(QRhi::SkipPresent)) {
2487 [commandBuffer commit];
2489 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2491 if (swapChainD
->d->layer.presentsWithTransaction) {
2492 [commandBuffer commit];
2494 auto *metalLayer = swapChainD
->d->layer;
2495 auto presentWithTransaction = ^{
2496 [commandBuffer waitUntilScheduled];
2503 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2504 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2505 if (textureSize == surfaceSize) {
2508 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2509 << textureSize <<
"not matching surface size" << surfaceSize;
2513 if (NSThread.currentThread == NSThread.mainThread) {
2514 presentWithTransaction();
2516 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2517 Q_ASSERT(qtMetalLayer);
2519 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2523 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2524 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2530 if (qtMetalLayer.displayLock.tryLockForRead()) {
2532 qtMetalLayer.displayLock.unlock();
2534 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2535 <<
"due to" << qtMetalLayer <<
"needing display";
2541 [commandBuffer commit];
2545 [commandBuffer commit];
2552 [swapChainD->d->curDrawable release];
2553 swapChainD->d->curDrawable = nil;
2555 [d->captureScope endScope];
2559 return QRhi::FrameOpSuccess;
2566 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2568 for (QMetalSwapChain *sc : std::as_const(swapchains))
2569 sc->waitUntilCompleted(currentFrameSlot);
2571 d->ofr.active =
true;
2572 *cb = &
d->ofr.cbWrapper;
2573 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2576 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2577 d->ofr.lastGpuTime = 0;
2580 return QRhi::FrameOpSuccess;
2586 Q_ASSERT(
d->ofr.active);
2587 d->ofr.active =
false;
2589 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2593 [cb waitUntilCompleted];
2595 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2599 return QRhi::FrameOpSuccess;
2604 id<MTLCommandBuffer> cb = nil;
2607 if (
d->ofr.active) {
2610 cb =
d->ofr.cbWrapper.d->cb;
2615 cb = swapChainD->cbWrapper.d->cb;
2619 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2620 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2621 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2626 sc->waitUntilCompleted(i);
2632 [cb waitUntilCompleted];
2636 if (
d->ofr.active) {
2637 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2638 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2640 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2641 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2649 return QRhi::FrameOpSuccess;
2653 const QColor &colorClearValue,
2654 const QRhiDepthStencilClearValue &depthStencilClearValue,
2656 QRhiShadingRateMap *shadingRateMap)
2658 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2659 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2660 colorClearValue.alphaF());
2662 for (uint i = 0; i < uint(colorAttCount); ++i) {
2663 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2664 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2665 rp.colorAttachments[i].clearColor = c;
2668 if (hasDepthStencil) {
2669 rp.depthAttachment.loadAction = MTLLoadActionClear;
2670 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2671 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2672 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2673 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2674 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2678 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
2686 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2687 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2688 if (imageSizeBytes > 0)
2689 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2694 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
2697 const QPoint dp = subresDesc.destinationTopLeft();
2698 const QByteArray rawData = subresDesc.data();
2699 QImage img = subresDesc.image();
2700 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2701 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
2703 if (!img.isNull()) {
2704 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2705 QSize size = img.size();
2706 int bpl = img.bytesPerLine();
2708 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2709 const int sx = subresDesc.sourceTopLeft().x();
2710 const int sy = subresDesc.sourceTopLeft().y();
2711 if (!subresDesc.sourceSize().isEmpty())
2712 size = subresDesc.sourceSize();
2713 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
2714 if (size.width() == img.width()) {
2715 const int bpc = qMax(1, img.depth() / 8);
2716 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
2717 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
2718 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
2719 size.height() * img.bytesPerLine());
2721 img = img.copy(sx, sy, size.width(), size.height());
2722 bpl = img.bytesPerLine();
2723 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2724 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2727 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
2728 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2731 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2732 sourceOffset: NSUInteger(*curOfs)
2733 sourceBytesPerRow: NSUInteger(bpl)
2734 sourceBytesPerImage: 0
2735 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
2736 toTexture: texD->d->tex
2737 destinationSlice: NSUInteger(is3D ? 0 : layer)
2738 destinationLevel: NSUInteger(level)
2739 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2740 options: MTLBlitOptionNone];
2742 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2743 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2744 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2745 const int subresw = subresSize.width();
2746 const int subresh = subresSize.height();
2748 if (subresDesc.sourceSize().isEmpty()) {
2752 w = subresDesc.sourceSize().width();
2753 h = subresDesc.sourceSize().height();
2758 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
2760 const int dx = aligned(dp.x(), blockDim.width());
2761 const int dy = aligned(dp.y(), blockDim.height());
2762 if (dx + w != subresw)
2763 w = aligned(w, blockDim.width());
2764 if (dy + h != subresh)
2765 h = aligned(h, blockDim.height());
2767 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2769 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2770 sourceOffset: NSUInteger(*curOfs)
2771 sourceBytesPerRow: bpl
2772 sourceBytesPerImage: 0
2773 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2774 toTexture: texD->d->tex
2775 destinationSlice: NSUInteger(is3D ? 0 : layer)
2776 destinationLevel: NSUInteger(level)
2777 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2778 options: MTLBlitOptionNone];
2780 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2781 }
else if (!rawData.isEmpty()) {
2782 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2783 const int subresw = subresSize.width();
2784 const int subresh = subresSize.height();
2786 if (subresDesc.sourceSize().isEmpty()) {
2790 w = subresDesc.sourceSize().width();
2791 h = subresDesc.sourceSize().height();
2795 if (subresDesc.dataStride())
2796 bpl = subresDesc.dataStride();
2798 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
2800 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2802 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2803 sourceOffset: NSUInteger(*curOfs)
2804 sourceBytesPerRow: bpl
2805 sourceBytesPerImage: 0
2806 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2807 toTexture: texD->d->tex
2808 destinationSlice: NSUInteger(is3D ? 0 : layer)
2809 destinationLevel: NSUInteger(level)
2810 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2811 options: MTLBlitOptionNone];
2813 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2815 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2824 id<MTLBlitCommandEncoder> blitEnc = nil;
2825 auto ensureBlit = [&blitEnc, cbD,
this]() {
2827 blitEnc = [cbD->d->cb blitCommandEncoder];
2829 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
2837 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2839 if (u.offset == 0 && u
.data.size() == bufD->m_size)
2840 bufD
->d->pendingUpdates[i].clear();
2841 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2847 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2848 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
2850 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2854 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2855 if (bufD->m_type == QRhiBuffer::Dynamic) {
2856 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
2858 u.result->data.resize(u.readSize);
2859 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2861 if (u.result->completed)
2862 u.result->completed();
2866 readback.buf = bufD
->d->buf[idx];
2867 readback.offset = u.offset;
2868 readback.readSize = u.readSize;
2869 readback.result = u.result;
2870 d->activeBufferReadbacks.append(readback);
2872 if (bufD->d->managed) {
2875 [blitEnc synchronizeResource:readback.buf];
2886 qsizetype stagingSize = 0;
2887 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2888 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2889 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2890 stagingSize += subresUploadByteSize(subresDesc);
2895 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2896 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2897 options: MTLResourceStorageModeShared];
2899 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2900 qsizetype curOfs = 0;
2901 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2902 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2903 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2904 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2908 utexD->lastActiveFrameSlot = currentFrameSlot;
2912 e.lastActiveFrameSlot = currentFrameSlot;
2913 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2914 utexD->d->stagingBuf[currentFrameSlot] = nil;
2915 d->releaseQueue.append(e);
2920 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2921 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2922 const QPoint dp = u.desc.destinationTopLeft();
2923 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2924 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2925 const QPoint sp = u.desc.sourceTopLeft();
2928 [blitEnc copyFromTexture: srcD->d->tex
2929 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2930 sourceLevel: NSUInteger(u.desc.sourceLevel())
2931 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2932 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2933 toTexture: dstD->d->tex
2934 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2935 destinationLevel: NSUInteger(u.desc.destinationLevel())
2936 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2938 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2941 readback.activeFrameSlot = currentFrameSlot;
2942 readback.desc = u.rb;
2943 readback.result = u.result;
2952 qWarning(
"Multisample texture cannot be read back");
2955 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2956 if (u.rb.rect().isValid())
2959 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
2960 readback.format = texD->m_format;
2962 texD->lastActiveFrameSlot = currentFrameSlot;
2966 if (u.rb.rect().isValid())
2969 rect = QRect({0, 0}, swapChainD->pixelSize);
2970 readback.format = swapChainD
->d->rhiColorFormat;
2974 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2976 readback.pixelSize = rect.size();
2979 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
2980 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2983 [blitEnc copyFromTexture: src
2984 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2985 sourceLevel: NSUInteger(u.rb.level())
2986 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
2987 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
2988 toBuffer: readback.buf
2989 destinationOffset: 0
2990 destinationBytesPerRow: bpl
2991 destinationBytesPerImage: 0
2992 options: MTLBlitOptionNone];
2994 d->activeTextureReadbacks.append(readback);
2998 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2999 utexD->lastActiveFrameSlot = currentFrameSlot;
3005 [blitEnc popDebugGroup];
3006 [blitEnc endEncoding];
3015 if (bufD
->d->pendingUpdates[slot].isEmpty())
3018 void *p = [bufD->d->buf[slot] contents];
3019 quint32 changeBegin = UINT32_MAX;
3020 quint32 changeEnd = 0;
3021 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3022 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3023 if (u.offset < changeBegin)
3024 changeBegin = u.offset;
3025 if (u.offset + u.data.size() > changeEnd)
3026 changeEnd = u.offset + u.data.size();
3029 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3030 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3033 bufD
->d->pendingUpdates[slot].clear();
3043 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3049 QRhiRenderTarget *rt,
3050 const QColor &colorClearValue,
3051 const QRhiDepthStencilClearValue &depthStencilClearValue,
3052 QRhiResourceUpdateBatch *resourceUpdates,
3058 if (resourceUpdates)
3062 switch (rt->resourceType()) {
3063 case QRhiResource::SwapChainRenderTarget:
3067 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3070 depthStencilClearValue,
3078 if (!swapChainD
->d->curDrawable) {
3079 QMacAutoReleasePool pool;
3080 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3082 if (!swapChainD
->d->curDrawable) {
3083 qWarning(
"No drawable");
3086 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3091 color0.resolveTex = scTex;
3097 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3100 case QRhiResource::TextureRenderTarget:
3104 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3108 depthStencilClearValue,
3110 rtTex->m_desc.shadingRateMap());
3111 if (rtD->fb.preserveColor) {
3112 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3113 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3116 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3117 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3119 int colorAttCount = 0;
3120 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3124 if (it->texture()) {
3125 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3126 if (it->multiViewCount() >= 2)
3127 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3128 }
else if (it->renderBuffer()) {
3129 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3131 if (it->resolveTexture())
3132 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3134 if (rtTex->m_desc.depthStencilBuffer())
3135 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3136 if (rtTex->m_desc.depthTexture()) {
3138 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3139 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3140 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3142 if (rtTex->m_desc.depthResolveTexture())
3143 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3144 if (rtTex->m_desc.shadingRateMap())
3145 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3154 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3155 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3156 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3157 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3158 if (rtD->fb.colorAtt[i].resolveTex) {
3159 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3160 : MTLStoreActionMultisampleResolve;
3161 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3162 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3163 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3168 Q_ASSERT(rtD->fb.dsTex);
3169 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3170 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3171 if (rtD->fb.depthNeedsStore)
3172 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3173 if (rtD->fb.dsResolveTex) {
3174 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3175 : MTLStoreActionMultisampleResolve;
3176 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3177 if (rtD->fb.hasStencil) {
3178 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3179 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3184 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3189 cbD->currentTarget = rt;
3197 [cbD->d->currentRenderPassEncoder endEncoding];
3200 cbD->currentTarget =
nullptr;
3202 if (resourceUpdates)
3207 QRhiResourceUpdateBatch *resourceUpdates,
3213 if (resourceUpdates)
3216 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3226 [cbD->d->currentComputePassEncoder endEncoding];
3229 if (resourceUpdates)
3242 cbD->currentPipelineGeneration = psD->generation;
3244 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3247 psD->lastActiveFrameSlot = currentFrameSlot;
3256 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3257 threadsPerThreadgroup: psD->d->localSize];
3262 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3263 [e.buffer.buffers[i] release];
3268 [e.renderbuffer.texture release];
3273 [e.texture.texture release];
3274 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3275 [e.texture.stagingBuffers[i] release];
3276 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3277 [e.texture.views[i] release];
3282 [e.sampler.samplerState release];
3287 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3289 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3303 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3304 [e.stagingBuffer.buffer release];
3306 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3307 [e.graphicsPipeline.pipelineState release];
3308 [e.graphicsPipeline.depthStencilState release];
3309 [e.graphicsPipeline.tessVertexComputeState[0] release];
3310 [e.graphicsPipeline.tessVertexComputeState[1] release];
3311 [e.graphicsPipeline.tessVertexComputeState[2] release];
3312 [e.graphicsPipeline.tessTessControlComputeState release];
3314 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3315 [e.computePipeline.pipelineState release];
3317 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3318 [e.shadingRateMap.rateMap release];
3323 d->releaseQueue.removeAt(i);
3330 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3332 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3334 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3335 readback.result->format = readback.format;
3336 readback.result->pixelSize = readback.pixelSize;
3337 readback.result->data.resize(
int(readback.bufSize));
3338 void *p = [readback.buf contents];
3339 memcpy(readback.result->data.data(), p, readback.bufSize);
3340 [readback.buf release];
3342 if (readback.result->completed)
3343 completedCallbacks.append(readback.result->completed);
3345 d->activeTextureReadbacks.remove(i);
3349 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3351 if (forced || currentFrameSlot == readback.activeFrameSlot
3352 || readback.activeFrameSlot < 0) {
3353 readback.result->data.resize(readback.readSize);
3354 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3356 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3358 if (readback.result->completed)
3359 completedCallbacks.append(readback.result->completed);
3361 d->activeBufferReadbacks.remove(i);
3365 for (
auto f : completedCallbacks)
3373 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3393 e.buffer.buffers[i] =
d->buf[i];
3395 d->pendingUpdates[i].clear();
3400 rhiD
->d->releaseQueue.append(e);
3401 rhiD->unregisterResource(
this);
3410 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3411 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3415 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3416 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3419 MTLResourceOptions opts = MTLResourceStorageModeShared;
3423 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3424 opts = MTLResourceStorageModeManaged;
3433 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3435 if (
int(m_usage) == WorkBufPoolUsage)
3440 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3441 if (!m_objectName.isEmpty()) {
3443 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3445 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3446 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3454 rhiD->registerResource(
this);
3466 b.objects[i] = &
d->buf[i];
3471 return { { &
d->buf[0] }, 1 };
3481 Q_ASSERT(m_type == Dynamic);
3483 Q_ASSERT(rhiD->inFrame);
3484 const int slot = rhiD->currentFrameSlot;
3485 void *p = [d->buf[slot] contents];
3486 return static_cast<
char *>(p);
3493 QRHI_RES_RHI(QRhiMetal);
3494 const int slot = rhiD->currentFrameSlot;
3495 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3506 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3508 case QRhiTexture::RGBA8:
3509 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3510 case QRhiTexture::BGRA8:
3511 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3512 case QRhiTexture::R8:
3514 return MTLPixelFormatR8Unorm;
3516 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3518 case QRhiTexture::R8SI:
3519 return MTLPixelFormatR8Sint;
3520 case QRhiTexture::R8UI:
3521 return MTLPixelFormatR8Uint;
3522 case QRhiTexture::RG8:
3524 return MTLPixelFormatRG8Unorm;
3526 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3528 case QRhiTexture::R16:
3529 return MTLPixelFormatR16Unorm;
3530 case QRhiTexture::RG16:
3531 return MTLPixelFormatRG16Unorm;
3532 case QRhiTexture::RED_OR_ALPHA8:
3533 return MTLPixelFormatR8Unorm;
3535 case QRhiTexture::RGBA16F:
3536 return MTLPixelFormatRGBA16Float;
3537 case QRhiTexture::RGBA32F:
3538 return MTLPixelFormatRGBA32Float;
3539 case QRhiTexture::R16F:
3540 return MTLPixelFormatR16Float;
3541 case QRhiTexture::R32F:
3542 return MTLPixelFormatR32Float;
3544 case QRhiTexture::RGB10A2:
3545 return MTLPixelFormatRGB10A2Unorm;
3547 case QRhiTexture::R32SI:
3548 return MTLPixelFormatR32Sint;
3549 case QRhiTexture::R32UI:
3550 return MTLPixelFormatR32Uint;
3551 case QRhiTexture::RG32SI:
3552 return MTLPixelFormatRG32Sint;
3553 case QRhiTexture::RG32UI:
3554 return MTLPixelFormatRG32Uint;
3555 case QRhiTexture::RGBA32SI:
3556 return MTLPixelFormatRGBA32Sint;
3557 case QRhiTexture::RGBA32UI:
3558 return MTLPixelFormatRGBA32Uint;
3561 case QRhiTexture::D16:
3562 return MTLPixelFormatDepth16Unorm;
3563 case QRhiTexture::D24:
3564 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3565 case QRhiTexture::D24S8:
3566 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3568 case QRhiTexture::D16:
3569 return MTLPixelFormatDepth32Float;
3570 case QRhiTexture::D24:
3571 return MTLPixelFormatDepth32Float;
3572 case QRhiTexture::D24S8:
3573 return MTLPixelFormatDepth32Float_Stencil8;
3575 case QRhiTexture::D32F:
3576 return MTLPixelFormatDepth32Float;
3577 case QRhiTexture::D32FS8:
3578 return MTLPixelFormatDepth32Float_Stencil8;
3581 case QRhiTexture::BC1:
3582 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3583 case QRhiTexture::BC2:
3584 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3585 case QRhiTexture::BC3:
3586 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3587 case QRhiTexture::BC4:
3588 return MTLPixelFormatBC4_RUnorm;
3589 case QRhiTexture::BC5:
3590 qWarning(
"QRhiMetal does not support BC5");
3591 return MTLPixelFormatInvalid;
3592 case QRhiTexture::BC6H:
3593 return MTLPixelFormatBC6H_RGBUfloat;
3594 case QRhiTexture::BC7:
3595 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3597 case QRhiTexture::BC1:
3598 case QRhiTexture::BC2:
3599 case QRhiTexture::BC3:
3600 case QRhiTexture::BC4:
3601 case QRhiTexture::BC5:
3602 case QRhiTexture::BC6H:
3603 case QRhiTexture::BC7:
3604 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3605 return MTLPixelFormatInvalid;
3609 case QRhiTexture::ETC2_RGB8:
3610 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3611 case QRhiTexture::ETC2_RGB8A1:
3612 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3613 case QRhiTexture::ETC2_RGBA8:
3614 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3616 case QRhiTexture::ASTC_4x4:
3617 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3618 case QRhiTexture::ASTC_5x4:
3619 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3620 case QRhiTexture::ASTC_5x5:
3621 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3622 case QRhiTexture::ASTC_6x5:
3623 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3624 case QRhiTexture::ASTC_6x6:
3625 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3626 case QRhiTexture::ASTC_8x5:
3627 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3628 case QRhiTexture::ASTC_8x6:
3629 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3630 case QRhiTexture::ASTC_8x8:
3631 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3632 case QRhiTexture::ASTC_10x5:
3633 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3634 case QRhiTexture::ASTC_10x6:
3635 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3636 case QRhiTexture::ASTC_10x8:
3637 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3638 case QRhiTexture::ASTC_10x10:
3639 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3640 case QRhiTexture::ASTC_12x10:
3641 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3642 case QRhiTexture::ASTC_12x12:
3643 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3645 case QRhiTexture::ETC2_RGB8:
3646 if (d->caps.isAppleGPU)
3647 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3648 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3649 return MTLPixelFormatInvalid;
3650 case QRhiTexture::ETC2_RGB8A1:
3651 if (d->caps.isAppleGPU)
3652 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3653 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3654 return MTLPixelFormatInvalid;
3655 case QRhiTexture::ETC2_RGBA8:
3656 if (d->caps.isAppleGPU)
3657 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3658 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3659 return MTLPixelFormatInvalid;
3660 case QRhiTexture::ASTC_4x4:
3661 if (d->caps.isAppleGPU)
3662 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3663 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3664 return MTLPixelFormatInvalid;
3665 case QRhiTexture::ASTC_5x4:
3666 if (d->caps.isAppleGPU)
3667 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3668 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3669 return MTLPixelFormatInvalid;
3670 case QRhiTexture::ASTC_5x5:
3671 if (d->caps.isAppleGPU)
3672 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3673 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3674 return MTLPixelFormatInvalid;
3675 case QRhiTexture::ASTC_6x5:
3676 if (d->caps.isAppleGPU)
3677 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3678 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3679 return MTLPixelFormatInvalid;
3680 case QRhiTexture::ASTC_6x6:
3681 if (d->caps.isAppleGPU)
3682 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3683 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3684 return MTLPixelFormatInvalid;
3685 case QRhiTexture::ASTC_8x5:
3686 if (d->caps.isAppleGPU)
3687 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3688 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3689 return MTLPixelFormatInvalid;
3690 case QRhiTexture::ASTC_8x6:
3691 if (d->caps.isAppleGPU)
3692 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3693 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3694 return MTLPixelFormatInvalid;
3695 case QRhiTexture::ASTC_8x8:
3696 if (d->caps.isAppleGPU)
3697 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3698 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3699 return MTLPixelFormatInvalid;
3700 case QRhiTexture::ASTC_10x5:
3701 if (d->caps.isAppleGPU)
3702 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3703 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3704 return MTLPixelFormatInvalid;
3705 case QRhiTexture::ASTC_10x6:
3706 if (d->caps.isAppleGPU)
3707 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3708 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3709 return MTLPixelFormatInvalid;
3710 case QRhiTexture::ASTC_10x8:
3711 if (d->caps.isAppleGPU)
3712 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3713 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3714 return MTLPixelFormatInvalid;
3715 case QRhiTexture::ASTC_10x10:
3716 if (d->caps.isAppleGPU)
3717 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3718 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3719 return MTLPixelFormatInvalid;
3720 case QRhiTexture::ASTC_12x10:
3721 if (d->caps.isAppleGPU)
3722 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3723 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3724 return MTLPixelFormatInvalid;
3725 case QRhiTexture::ASTC_12x12:
3726 if (d->caps.isAppleGPU)
3727 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3728 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3729 return MTLPixelFormatInvalid;
3734 return MTLPixelFormatInvalid;
3739 int sampleCount, QRhiRenderBuffer::Flags flags,
3740 QRhiTexture::Format backingFormatHint)
3761 e.renderbuffer.texture =
d->tex;
3766 rhiD
->d->releaseQueue.append(e);
3767 rhiD->unregisterResource(
this);
3776 if (m_pixelSize.isEmpty())
3780 samples = rhiD->effectiveSampleCount(m_sampleCount);
3782 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3783 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3784 desc.width = NSUInteger(m_pixelSize.width());
3785 desc.height = NSUInteger(m_pixelSize.height());
3787 desc.sampleCount = NSUInteger(
samples);
3788 desc.resourceOptions = MTLResourceStorageModePrivate;
3789 desc.usage = MTLTextureUsageRenderTarget;
3794 if (rhiD->caps.isAppleGPU) {
3795 desc.storageMode = MTLStorageModeMemoryless;
3796 d->format = MTLPixelFormatDepth32Float_Stencil8;
3798 desc.storageMode = MTLStorageModePrivate;
3799 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
3800 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3803 desc.storageMode = MTLStorageModeMemoryless;
3804 d->format = MTLPixelFormatDepth32Float_Stencil8;
3806 desc.pixelFormat =
d->format;
3809 desc.storageMode = MTLStorageModePrivate;
3810 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3811 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
3813 d->format = MTLPixelFormatRGBA8Unorm;
3814 desc.pixelFormat =
d->format;
3821 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3824 if (!m_objectName.isEmpty())
3825 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3829 rhiD->registerResource(
this);
3835 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3836 return m_backingFormatHint;
3838 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3842 int arraySize,
int sampleCount, Flags flags)
3846 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3847 d->stagingBuf[i] = nil;
3849 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3850 d->perLevelViews[i] = nil;
3868 e.texture.texture = d->owns ? d->tex : nil;
3872 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
3873 d->stagingBuf[i] = nil;
3876 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3877 e.texture.views[i] =
d->perLevelViews[i];
3878 d->perLevelViews[i] = nil;
3883 rhiD
->d->releaseQueue.append(e);
3884 rhiD->unregisterResource(
this);
3893 const bool isCube = m_flags.testFlag(CubeMap);
3894 const bool is3D = m_flags.testFlag(ThreeDimensional);
3895 const bool isArray = m_flags.testFlag(TextureArray);
3896 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3897 const bool is1D = m_flags.testFlag(OneDimensional);
3899 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3900 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3903 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
3904 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3905 samples = rhiD->effectiveSampleCount(m_sampleCount);
3908 qWarning(
"Cubemap texture cannot be multisample");
3912 qWarning(
"3D texture cannot be multisample");
3916 qWarning(
"Multisample texture cannot have mipmaps");
3920 if (isCube && is3D) {
3921 qWarning(
"Texture cannot be both cube and 3D");
3924 if (isArray && is3D) {
3925 qWarning(
"Texture cannot be both array and 3D");
3929 qWarning(
"Texture cannot be both 1D and 3D");
3932 if (is1D && isCube) {
3933 qWarning(
"Texture cannot be both 1D and cube");
3936 if (m_depth > 1 && !is3D) {
3937 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
3940 if (m_arraySize > 0 && !isArray) {
3941 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
3944 if (m_arraySize < 1 && isArray) {
3945 qWarning(
"Texture is an array but array size is %d", m_arraySize);
3950 *adjustedSize = size;
3958 if (!prepareCreate(&size))
3961 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3963 const bool isCube = m_flags.testFlag(CubeMap);
3964 const bool is3D = m_flags.testFlag(ThreeDimensional);
3965 const bool isArray = m_flags.testFlag(TextureArray);
3966 const bool is1D = m_flags.testFlag(OneDimensional);
3968 desc.textureType = MTLTextureTypeCube;
3970 desc.textureType = MTLTextureType3D;
3972 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
3973 }
else if (isArray) {
3974 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3976 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3978 desc.pixelFormat =
d->format;
3979 desc.width = NSUInteger(size.width());
3980 desc.height = NSUInteger(size.height());
3981 desc.depth = is3D ? qMax(1, m_depth) : 1;
3984 desc.sampleCount = NSUInteger(
samples);
3986 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
3987 desc.resourceOptions = MTLResourceStorageModePrivate;
3988 desc.storageMode = MTLStorageModePrivate;
3989 desc.usage = MTLTextureUsageShaderRead;
3990 if (m_flags.testFlag(RenderTarget))
3991 desc.usage |= MTLTextureUsageRenderTarget;
3992 if (m_flags.testFlag(UsedWithLoadStore))
3993 desc.usage |= MTLTextureUsageShaderWrite;
3996 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3999 if (!m_objectName.isEmpty())
4000 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4006 rhiD->registerResource(
this);
4012 id<MTLTexture> tex = id<MTLTexture>(src.object);
4016 if (!prepareCreate())
4026 rhiD->registerResource(
this);
4032 return {quint64(
d->tex), 0};
4038 if (perLevelViews[level])
4039 return perLevelViews[level];
4041 const MTLTextureType type = [tex textureType];
4042 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
4043 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
4044 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4045 levels: NSMakeRange(NSUInteger(level), 1)
4046 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4048 perLevelViews[level] = view;
4053 AddressMode u, AddressMode v, AddressMode w)
4067 if (!
d->samplerState)
4074 e.sampler.samplerState =
d->samplerState;
4075 d->samplerState = nil;
4079 rhiD
->d->releaseQueue.append(e);
4080 rhiD->unregisterResource(
this);
4087 case QRhiSampler::Nearest:
4088 return MTLSamplerMinMagFilterNearest;
4089 case QRhiSampler::Linear:
4090 return MTLSamplerMinMagFilterLinear;
4093 return MTLSamplerMinMagFilterNearest;
4100 case QRhiSampler::None:
4101 return MTLSamplerMipFilterNotMipmapped;
4102 case QRhiSampler::Nearest:
4103 return MTLSamplerMipFilterNearest;
4104 case QRhiSampler::Linear:
4105 return MTLSamplerMipFilterLinear;
4108 return MTLSamplerMipFilterNotMipmapped;
4115 case QRhiSampler::Repeat:
4116 return MTLSamplerAddressModeRepeat;
4117 case QRhiSampler::ClampToEdge:
4118 return MTLSamplerAddressModeClampToEdge;
4119 case QRhiSampler::Mirror:
4120 return MTLSamplerAddressModeMirrorRepeat;
4123 return MTLSamplerAddressModeClampToEdge;
4130 case QRhiSampler::Never:
4131 return MTLCompareFunctionNever;
4132 case QRhiSampler::Less:
4133 return MTLCompareFunctionLess;
4134 case QRhiSampler::Equal:
4135 return MTLCompareFunctionEqual;
4136 case QRhiSampler::LessOrEqual:
4137 return MTLCompareFunctionLessEqual;
4138 case QRhiSampler::Greater:
4139 return MTLCompareFunctionGreater;
4140 case QRhiSampler::NotEqual:
4141 return MTLCompareFunctionNotEqual;
4142 case QRhiSampler::GreaterOrEqual:
4143 return MTLCompareFunctionGreaterEqual;
4144 case QRhiSampler::Always:
4145 return MTLCompareFunctionAlways;
4148 return MTLCompareFunctionNever;
4154 if (
d->samplerState)
4157 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4158 desc.minFilter = toMetalFilter(m_minFilter);
4159 desc.magFilter = toMetalFilter(m_magFilter);
4160 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4161 desc.sAddressMode = toMetalAddressMode(m_addressU);
4162 desc.tAddressMode = toMetalAddressMode(m_addressV);
4163 desc.rAddressMode = toMetalAddressMode(m_addressW);
4164 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4167 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4172 rhiD->registerResource(
this);
4197 e.shadingRateMap.rateMap =
d->rateMap;
4202 rhiD
->d->releaseQueue.append(e);
4203 rhiD->unregisterResource(
this);
4212 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4216 [d->rateMap retain];
4221 rhiD->registerResource(
this);
4230 serializedFormatData.reserve(16);
4242 rhiD->unregisterResource(
this);
4276 serializedFormatData.clear();
4277 auto p =
std::back_inserter(serializedFormatData);
4299 rhiD->registerResource(rpD,
false);
4305 return serializedFormatData;
4327 return d->pixelSize;
4341 const QRhiTextureRenderTargetDescription &desc,
4358 rhiD->unregisterResource(
this);
4363 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4366 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4368 for (
int i = 0; i < colorAttachmentCount; ++i) {
4369 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4375 if (m_desc.depthTexture())
4376 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4377 else if (m_desc.depthStencilBuffer())
4378 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4380 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4385 rhiD->registerResource(rpD,
false);
4392 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4393 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4394 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4398 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4402 Q_ASSERT(texD || rbD);
4403 id<MTLTexture> dst = nil;
4407 if (attIndex == 0) {
4408 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4411 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4414 if (attIndex == 0) {
4415 d->pixelSize = rbD->pixelSize();
4422 colorAtt
.slice = is3D ? it->layer() : 0;
4423 colorAtt
.level = it->level();
4425 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4428 d->fb.colorAtt[attIndex] = colorAtt;
4432 if (hasDepthStencil) {
4433 if (m_desc.depthTexture()) {
4435 d->fb.dsTex = depthTexD
->d->tex;
4436 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4437 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4438 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4440 d->pixelSize = depthTexD->pixelSize();
4445 d->fb.dsTex = depthRbD
->d->tex;
4446 d->fb.hasStencil =
true;
4447 d->fb.depthNeedsStore =
false;
4448 d->fb.preserveDs =
false;
4450 d->pixelSize = depthRbD->pixelSize();
4454 if (m_desc.depthResolveTexture()) {
4456 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4463 if (d->colorAttCount > 0)
4464 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4466 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4468 rhiD->registerResource(
this,
false);
4474 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4477 return d->pixelSize;
4502 sortedBindings.clear();
4507 rhiD->unregisterResource(
this);
4512 if (!sortedBindings.isEmpty())
4516 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4519 rhiD->updateLayoutDesc(
this);
4521 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4522 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4523 if (!sortedBindings.isEmpty())
4524 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4528 boundResourceData.resize(sortedBindings.count());
4530 for (BoundResourceData &bd : boundResourceData)
4531 memset(&bd, 0,
sizeof(BoundResourceData));
4534 rhiD->registerResource(
this,
false);
4540 sortedBindings.clear();
4541 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4542 if (!flags.testFlag(BindingsAreSorted))
4543 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4545 for (BoundResourceData &bd : boundResourceData)
4546 memset(&bd, 0,
sizeof(BoundResourceData));
4570 d->tess.compVs[0].destroy();
4571 d->tess.compVs[1].destroy();
4572 d->tess.compVs[2].destroy();
4574 d->tess.compTesc.destroy();
4575 d->tess.vertTese.destroy();
4577 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4578 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4579 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4580 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4585 if (!
d->ps && !
d->ds
4586 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4587 && !
d->tess.tessControlComputeState)
4595 e.graphicsPipeline.pipelineState =
d->ps;
4596 e.graphicsPipeline.depthStencilState =
d->ds;
4597 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4598 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4601 d->tess.vertexComputeState = {};
4602 d->tess.tessControlComputeState = nil;
4606 rhiD
->d->releaseQueue.append(e);
4607 rhiD->unregisterResource(
this);
4614 case QRhiVertexInputAttribute::Float4:
4615 return MTLVertexFormatFloat4;
4616 case QRhiVertexInputAttribute::Float3:
4617 return MTLVertexFormatFloat3;
4618 case QRhiVertexInputAttribute::Float2:
4619 return MTLVertexFormatFloat2;
4620 case QRhiVertexInputAttribute::Float:
4621 return MTLVertexFormatFloat;
4622 case QRhiVertexInputAttribute::UNormByte4:
4623 return MTLVertexFormatUChar4Normalized;
4624 case QRhiVertexInputAttribute::UNormByte2:
4625 return MTLVertexFormatUChar2Normalized;
4626 case QRhiVertexInputAttribute::UNormByte:
4627 return MTLVertexFormatUCharNormalized;
4628 case QRhiVertexInputAttribute::UInt4:
4629 return MTLVertexFormatUInt4;
4630 case QRhiVertexInputAttribute::UInt3:
4631 return MTLVertexFormatUInt3;
4632 case QRhiVertexInputAttribute::UInt2:
4633 return MTLVertexFormatUInt2;
4634 case QRhiVertexInputAttribute::UInt:
4635 return MTLVertexFormatUInt;
4636 case QRhiVertexInputAttribute::SInt4:
4637 return MTLVertexFormatInt4;
4638 case QRhiVertexInputAttribute::SInt3:
4639 return MTLVertexFormatInt3;
4640 case QRhiVertexInputAttribute::SInt2:
4641 return MTLVertexFormatInt2;
4642 case QRhiVertexInputAttribute::SInt:
4643 return MTLVertexFormatInt;
4644 case QRhiVertexInputAttribute::Half4:
4645 return MTLVertexFormatHalf4;
4646 case QRhiVertexInputAttribute::Half3:
4647 return MTLVertexFormatHalf3;
4648 case QRhiVertexInputAttribute::Half2:
4649 return MTLVertexFormatHalf2;
4650 case QRhiVertexInputAttribute::Half:
4651 return MTLVertexFormatHalf;
4652 case QRhiVertexInputAttribute::UShort4:
4653 return MTLVertexFormatUShort4;
4654 case QRhiVertexInputAttribute::UShort3:
4655 return MTLVertexFormatUShort3;
4656 case QRhiVertexInputAttribute::UShort2:
4657 return MTLVertexFormatUShort2;
4658 case QRhiVertexInputAttribute::UShort:
4659 return MTLVertexFormatUShort;
4660 case QRhiVertexInputAttribute::SShort4:
4661 return MTLVertexFormatShort4;
4662 case QRhiVertexInputAttribute::SShort3:
4663 return MTLVertexFormatShort3;
4664 case QRhiVertexInputAttribute::SShort2:
4665 return MTLVertexFormatShort2;
4666 case QRhiVertexInputAttribute::SShort:
4667 return MTLVertexFormatShort;
4670 return MTLVertexFormatFloat4;
4677 case QRhiGraphicsPipeline::Zero:
4678 return MTLBlendFactorZero;
4679 case QRhiGraphicsPipeline::One:
4680 return MTLBlendFactorOne;
4681 case QRhiGraphicsPipeline::SrcColor:
4682 return MTLBlendFactorSourceColor;
4683 case QRhiGraphicsPipeline::OneMinusSrcColor:
4684 return MTLBlendFactorOneMinusSourceColor;
4685 case QRhiGraphicsPipeline::DstColor:
4686 return MTLBlendFactorDestinationColor;
4687 case QRhiGraphicsPipeline::OneMinusDstColor:
4688 return MTLBlendFactorOneMinusDestinationColor;
4689 case QRhiGraphicsPipeline::SrcAlpha:
4690 return MTLBlendFactorSourceAlpha;
4691 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4692 return MTLBlendFactorOneMinusSourceAlpha;
4693 case QRhiGraphicsPipeline::DstAlpha:
4694 return MTLBlendFactorDestinationAlpha;
4695 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4696 return MTLBlendFactorOneMinusDestinationAlpha;
4697 case QRhiGraphicsPipeline::ConstantColor:
4698 return MTLBlendFactorBlendColor;
4699 case QRhiGraphicsPipeline::ConstantAlpha:
4700 return MTLBlendFactorBlendAlpha;
4701 case QRhiGraphicsPipeline::OneMinusConstantColor:
4702 return MTLBlendFactorOneMinusBlendColor;
4703 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4704 return MTLBlendFactorOneMinusBlendAlpha;
4705 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4706 return MTLBlendFactorSourceAlphaSaturated;
4707 case QRhiGraphicsPipeline::Src1Color:
4708 return MTLBlendFactorSource1Color;
4709 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4710 return MTLBlendFactorOneMinusSource1Color;
4711 case QRhiGraphicsPipeline::Src1Alpha:
4712 return MTLBlendFactorSource1Alpha;
4713 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4714 return MTLBlendFactorOneMinusSource1Alpha;
4717 return MTLBlendFactorZero;
4724 case QRhiGraphicsPipeline::Add:
4725 return MTLBlendOperationAdd;
4726 case QRhiGraphicsPipeline::Subtract:
4727 return MTLBlendOperationSubtract;
4728 case QRhiGraphicsPipeline::ReverseSubtract:
4729 return MTLBlendOperationReverseSubtract;
4730 case QRhiGraphicsPipeline::Min:
4731 return MTLBlendOperationMin;
4732 case QRhiGraphicsPipeline::Max:
4733 return MTLBlendOperationMax;
4736 return MTLBlendOperationAdd;
4743 if (c.testFlag(QRhiGraphicsPipeline::R))
4744 f |= MTLColorWriteMaskRed;
4745 if (c.testFlag(QRhiGraphicsPipeline::G))
4746 f |= MTLColorWriteMaskGreen;
4747 if (c.testFlag(QRhiGraphicsPipeline::B))
4748 f |= MTLColorWriteMaskBlue;
4749 if (c.testFlag(QRhiGraphicsPipeline::A))
4750 f |= MTLColorWriteMaskAlpha;
4757 case QRhiGraphicsPipeline::Never:
4758 return MTLCompareFunctionNever;
4759 case QRhiGraphicsPipeline::Less:
4760 return MTLCompareFunctionLess;
4761 case QRhiGraphicsPipeline::Equal:
4762 return MTLCompareFunctionEqual;
4763 case QRhiGraphicsPipeline::LessOrEqual:
4764 return MTLCompareFunctionLessEqual;
4765 case QRhiGraphicsPipeline::Greater:
4766 return MTLCompareFunctionGreater;
4767 case QRhiGraphicsPipeline::NotEqual:
4768 return MTLCompareFunctionNotEqual;
4769 case QRhiGraphicsPipeline::GreaterOrEqual:
4770 return MTLCompareFunctionGreaterEqual;
4771 case QRhiGraphicsPipeline::Always:
4772 return MTLCompareFunctionAlways;
4775 return MTLCompareFunctionAlways;
4782 case QRhiGraphicsPipeline::StencilZero:
4783 return MTLStencilOperationZero;
4784 case QRhiGraphicsPipeline::Keep:
4785 return MTLStencilOperationKeep;
4786 case QRhiGraphicsPipeline::Replace:
4787 return MTLStencilOperationReplace;
4788 case QRhiGraphicsPipeline::IncrementAndClamp:
4789 return MTLStencilOperationIncrementClamp;
4790 case QRhiGraphicsPipeline::DecrementAndClamp:
4791 return MTLStencilOperationDecrementClamp;
4792 case QRhiGraphicsPipeline::Invert:
4793 return MTLStencilOperationInvert;
4794 case QRhiGraphicsPipeline::IncrementAndWrap:
4795 return MTLStencilOperationIncrementWrap;
4796 case QRhiGraphicsPipeline::DecrementAndWrap:
4797 return MTLStencilOperationDecrementWrap;
4800 return MTLStencilOperationKeep;
4807 case QRhiGraphicsPipeline::Triangles:
4808 return MTLPrimitiveTypeTriangle;
4809 case QRhiGraphicsPipeline::TriangleStrip:
4810 return MTLPrimitiveTypeTriangleStrip;
4811 case QRhiGraphicsPipeline::Lines:
4812 return MTLPrimitiveTypeLine;
4813 case QRhiGraphicsPipeline::LineStrip:
4814 return MTLPrimitiveTypeLineStrip;
4815 case QRhiGraphicsPipeline::Points:
4816 return MTLPrimitiveTypePoint;
4819 return MTLPrimitiveTypeTriangle;
4826 case QRhiGraphicsPipeline::Triangles:
4827 case QRhiGraphicsPipeline::TriangleStrip:
4828 case QRhiGraphicsPipeline::TriangleFan:
4829 return MTLPrimitiveTopologyClassTriangle;
4830 case QRhiGraphicsPipeline::Lines:
4831 case QRhiGraphicsPipeline::LineStrip:
4832 return MTLPrimitiveTopologyClassLine;
4833 case QRhiGraphicsPipeline::Points:
4834 return MTLPrimitiveTopologyClassPoint;
4837 return MTLPrimitiveTopologyClassTriangle;
4844 case QRhiGraphicsPipeline::None:
4845 return MTLCullModeNone;
4846 case QRhiGraphicsPipeline::Front:
4847 return MTLCullModeFront;
4848 case QRhiGraphicsPipeline::Back:
4849 return MTLCullModeBack;
4852 return MTLCullModeNone;
4859 case QRhiGraphicsPipeline::Fill:
4860 return MTLTriangleFillModeFill;
4861 case QRhiGraphicsPipeline::Line:
4862 return MTLTriangleFillModeLines;
4865 return MTLTriangleFillModeFill;
4872 case QShaderDescription::CwTessellationWindingOrder:
4873 return MTLWindingClockwise;
4874 case QShaderDescription::CcwTessellationWindingOrder:
4875 return MTLWindingCounterClockwise;
4878 return MTLWindingCounterClockwise;
4885 case QShaderDescription::EqualTessellationPartitioning:
4886 return MTLTessellationPartitionModePow2;
4887 case QShaderDescription::FractionalEvenTessellationPartitioning:
4888 return MTLTessellationPartitionModeFractionalEven;
4889 case QShaderDescription::FractionalOddTessellationPartitioning:
4890 return MTLTessellationPartitionModeFractionalOdd;
4893 return MTLTessellationPartitionModePow2;
4899 int v = version.version();
4900 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
4904 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4906 QVarLengthArray<
int, 8> versions;
4907 if (@available(macOS 13, iOS 16, *))
4909 if (@available(macOS 12, iOS 15, *))
4911 versions << 23 << 22 << 21 << 20 << 12;
4913 const QList<QShaderKey> shaders = shader.availableShaders();
4917 for (
const int &version : versions) {
4918 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4919 if (shaders.contains(key))
4923 QShaderCode mtllib = shader.shader(key);
4924 if (!mtllib.shader().isEmpty()) {
4925 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4926 size_t(mtllib.shader().size()),
4927 dispatch_get_global_queue(0, 0),
4928 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4930 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4931 dispatch_release(data);
4933 *entryPoint = mtllib.entryPoint();
4937 const QString msg = QString::fromNSString(err.localizedDescription);
4938 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
4942 for (
const int &version : versions) {
4943 key = { QShader::Source::MslShader, version, shaderVariant };
4944 if (shaders.contains(key))
4948 QShaderCode mslSource = shader.shader(key);
4949 if (mslSource.shader().isEmpty()) {
4950 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
4954 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4955 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4956 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4958 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4966 const QString msg = QString::fromNSString(err.localizedDescription);
4971 *entryPoint = mslSource.entryPoint();
4978 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
4983 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
4987 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
4988 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
4989 rpDesc.colorAttachments[0].blendingEnabled =
false;
4991 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
4992 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
4994 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
4995 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
4996 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
4997 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
4998 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
4999 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
5000 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
5001 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
5002 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
5003 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
5004 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
5011 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
5012 rpDesc.depthAttachmentPixelFormat = fmt;
5013#if defined(Q_OS_MACOS)
5014 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5016 if (fmt != MTLPixelFormatDepth32Float)
5018 rpDesc.stencilAttachmentPixelFormat = fmt;
5022 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5027 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5029 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5030 dsDesc.depthWriteEnabled = m_depthWrite;
5031 if (m_stencilTest) {
5032 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5033 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5034 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5035 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5036 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5037 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5038 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5040 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5041 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5042 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5043 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5044 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5045 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5046 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5052 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5053 d->cullMode = toMetalCullMode(m_cullMode);
5054 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5055 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5056 d->depthBias =
float(m_depthBias);
5057 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5067 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5070 const uint loc = uint(it->location());
5071 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5072 desc.attributes[loc].offset = NSUInteger(it->offset());
5073 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5075 int bindingIndex = 0;
5076 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5077 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5078 it != itEnd; ++it, ++bindingIndex)
5080 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5081 desc.layouts[layoutIdx].stepFunction =
5082 it->classification() == QRhiVertexInputBinding::PerInstance
5083 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5084 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5085 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5086 desc.layouts[layoutIdx].stepRate *= viewCount;
5087 desc.layouts[layoutIdx].stride = it->stride();
5098 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5101 const uint loc = uint(it->location());
5102 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5103 desc.attributes[loc].offset = NSUInteger(it->offset());
5104 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5106 int bindingIndex = 0;
5107 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5108 it != itEnd; ++it, ++bindingIndex)
5110 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5111 if (desc.indexBufferIndex) {
5112 desc.layouts[layoutIdx].stepFunction =
5113 it->classification() == QRhiVertexInputBinding::PerInstance
5114 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5116 desc.layouts[layoutIdx].stepFunction =
5117 it->classification() == QRhiVertexInputBinding::PerInstance
5118 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5120 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5121 desc.layouts[layoutIdx].stride = it->stride();
5128 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5129 rpDesc.binaryArchives = binArchArray;
5137 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5138 const QString msg = QString::fromNSString(err.localizedDescription);
5139 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5148 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5149 d->setupVertexInputDescriptor(vertexDesc);
5151 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5152 rpDesc.vertexDescriptor = vertexDesc;
5160 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5161 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5162 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5163 switch (shaderStage.type()) {
5164 case QRhiShaderStage::Vertex:
5167 [d->vs.func retain];
5168 rpDesc.vertexFunction = d->vs.func;
5170 case QRhiShaderStage::Fragment:
5173 [d->fs.func retain];
5174 rpDesc.fragmentFunction = d->fs.func;
5180 const QShader shader = shaderStage.shader();
5182 QByteArray entryPoint;
5183 QShaderKey activeKey;
5184 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5185 &error, &entryPoint, &activeKey);
5187 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5190 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5192 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5196 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5198 for (QMetalShader &s : rhiD->d->shaderCache)
5200 rhiD->d->shaderCache.clear();
5202 switch (shaderStage.type()) {
5203 case QRhiShaderStage::Vertex:
5206 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5207 d->vs.desc = shader.description();
5208 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5209 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5211 [d->vs.func retain];
5212 rpDesc.vertexFunction = func;
5214 case QRhiShaderStage::Fragment:
5217 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5218 d->fs.desc = shader.description();
5219 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5220 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5222 [d->fs.func retain];
5223 rpDesc.fragmentFunction = func;
5236 if (m_multiViewCount >= 2)
5237 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5239 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5241 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5242 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5245 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5248 const QString msg = QString::fromNSString(err.localizedDescription);
5249 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5253 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5255 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5258 d->primitiveType = toMetalPrimitiveType(m_topology);
5266 switch (vertexCompVariant) {
5267 case QShader::NonIndexedVertexAsComputeShader:
5269 case QShader::UInt32IndexedVertexAsComputeShader:
5271 case QShader::UInt16IndexedVertexAsComputeShader:
5281 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5282 if (varIndex >= 0 && vertexComputeState[varIndex])
5283 return vertexComputeState[varIndex];
5285 id<MTLFunction> func = nil;
5287 func = compVs[varIndex].func;
5290 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5294 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5295 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5297 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5298 cpDesc.computeFunction = func;
5299 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5300 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5301 if (indexBufferBinding >= 0) {
5302 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5303 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5304 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5305 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5306 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5307 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5310 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5312 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5314 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5315 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5318 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5319 options: MTLPipelineOptionNone
5324 const QString msg = QString::fromNSString(err.localizedDescription);
5325 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5327 vertexComputeState[varIndex] = ps;
5335 if (tessControlComputeState)
5336 return tessControlComputeState;
5338 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5339 cpDesc.computeFunction = compTesc.func;
5341 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5343 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5344 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5347 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5348 options: MTLPipelineOptionNone
5353 const QString msg = QString::fromNSString(err.localizedDescription);
5354 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5356 tessControlComputeState = ps;
5364 return (indices >> index) & 0x1;
5367static inline void takeIndex(quint32 index, quint64 &indices)
5369 indices |= 1 << index;
5378 static const int maxVertexAttributes = 31;
5380 for (
int index = 0; index < maxVertexAttributes; ++index) {
5381 if (!indexTaken(index, indices))
5385 Q_UNREACHABLE_RETURN(-1);
5388static inline int aligned(quint32 offset, quint32 alignment)
5390 return ((offset + alignment - 1) / alignment) * alignment;
5398 for (
const int dim : variable.arrayDims)
5401 if (variable.type == QShaderDescription::VariableType::Struct) {
5402 for (
int element = 0; element < elements; ++element) {
5403 for (
const auto &member : variable.structMembers) {
5404 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5408 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5409 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5412 const quint32 alignment = size;
5413 vertexAlignment =
std::max(vertexAlignment, alignment);
5415 for (
int element = 0; element < elements; ++element) {
5417 offset = aligned(offset, alignment);
5424static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5428 for (
const int dim : variable.arrayDims)
5431 if (variable.type == QShaderDescription::VariableType::Struct) {
5432 for (
int element = 0; element < elements; ++element) {
5433 for (
const auto &member : variable.structMembers) {
5434 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5438 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5439 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5442 const quint32 alignment = size;
5443 vertexAlignment =
std::max(vertexAlignment, alignment);
5445 for (
int element = 0; element < elements; ++element) {
5446 Q_ASSERT(!indexTaken(index, indices));
5449 offset = aligned(offset, alignment);
5451 attributes[index].bufferIndex = binding;
5452 attributes[index].format = toMetalAttributeFormat(format);
5453 attributes[index].offset = offset;
5455 takeIndex(index, indices);
5457 if (indexTaken(index, indices))
5458 index = nextAttributeIndex(indices);
5465static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5467 if (a.size() == b.size()) {
5469 for (
int i = 0; i < a.size() && match; ++i) {
5470 match &= a[i].type == b[i].type
5471 && a[i].arrayDims == b[i].arrayDims
5472 && matches(a[i].structMembers, b[i].structMembers);
5480static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5482 return a.location == b.location
5484 && a.perPatch == b.perPatch
5485 && matches(a.structMembers, b.structMembers);
5534 if (pipeline
->d->ps)
5535 return pipeline
->d->ps;
5537 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5538 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5541 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5542 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5543 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5544 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5545 quint32 offsetInTescOutput = 0;
5546 quint32 offsetInTescPatchOutput = 0;
5547 quint32 offsetInTessFactorBuffer = 0;
5548 quint32 tescOutputAlignment = 0;
5549 quint32 tescPatchOutputAlignment = 0;
5550 quint32 tessFactorAlignment = 0;
5551 QSet<
int> usedBuffers;
5554 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5555 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5556 tescOutVars[tescOutVar.location] = tescOutVar;
5559 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5560 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5561 teseInVars[teseInVar.location] = teseInVar;
5564 quint64 indices = 0;
5566 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5568 int index = tescOutVar.location;
5570 quint32 *offset =
nullptr;
5571 quint32 *alignment =
nullptr;
5573 if (tescOutVar.perPatch) {
5574 binding = tescPatchOutputBufferBinding;
5575 offset = &offsetInTescPatchOutput;
5576 alignment = &tescPatchOutputAlignment;
5578 tescOutVar.arrayDims.removeLast();
5579 binding = tescOutputBufferBinding;
5580 offset = &offsetInTescOutput;
5581 alignment = &tescOutputAlignment;
5584 if (teseInVars.contains(index)) {
5586 if (!matches(teseInVars[index], tescOutVar)) {
5587 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5588 qWarning() <<
" tesc out:" << tescOutVar;
5589 qWarning() <<
" tese in:" << teseInVars[index];
5592 if (binding != -1) {
5593 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5594 usedBuffers << binding;
5596 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5597 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5601 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5602 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5605 teseInVars.remove(tescOutVar.location);
5608 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5609 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5612 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5613 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5614 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5617 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5618 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5619 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5621 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5622 bool tessLevelAdded =
false;
5624 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5626 QShaderDescription::InOutVariable variable;
5628 quint32 *offset =
nullptr;
5629 quint32 *alignment =
nullptr;
5631 switch (builtin.type) {
5632 case QShaderDescription::BuiltinType::PositionBuiltin:
5633 variable.type = QShaderDescription::VariableType::Vec4;
5634 binding = tescOutputBufferBinding;
5635 offset = &offsetInTescOutput;
5636 alignment = &tescOutputAlignment;
5638 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5639 variable.type = QShaderDescription::VariableType::Float;
5640 binding = tescOutputBufferBinding;
5641 offset = &offsetInTescOutput;
5642 alignment = &tescOutputAlignment;
5644 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5645 variable.type = QShaderDescription::VariableType::Float;
5646 variable.arrayDims = builtin.arrayDims;
5647 binding = tescOutputBufferBinding;
5648 offset = &offsetInTescOutput;
5649 alignment = &tescOutputAlignment;
5651 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5652 variable.type = QShaderDescription::VariableType::Half4;
5653 binding = tessFactorBufferBinding;
5654 offset = &offsetInTessFactorBuffer;
5655 tessLevelAdded = trianglesMode;
5656 alignment = &tessFactorAlignment;
5658 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5659 if (trianglesMode) {
5660 if (!tessLevelAdded) {
5661 variable.type = QShaderDescription::VariableType::Half4;
5662 binding = tessFactorBufferBinding;
5663 offsetInTessFactorBuffer = 0;
5664 offset = &offsetInTessFactorBuffer;
5665 alignment = &tessFactorAlignment;
5666 tessLevelAdded =
true;
5668 teseInBuiltins.remove(builtin.type);
5672 variable.type = QShaderDescription::VariableType::Half2;
5673 binding = tessFactorBufferBinding;
5674 offsetInTessFactorBuffer = 8;
5675 offset = &offsetInTessFactorBuffer;
5676 alignment = &tessFactorAlignment;
5684 if (teseInBuiltins.contains(builtin.type)) {
5685 if (binding != -1) {
5686 int index = nextAttributeIndex(indices);
5687 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5688 usedBuffers << binding;
5690 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5691 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5694 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5697 teseInBuiltins.remove(builtin.type);
5700 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5701 switch (builtin.type) {
5702 case QShaderDescription::BuiltinType::PositionBuiltin:
5703 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5704 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5705 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5712 if (usedBuffers.contains(tescOutputBufferBinding)) {
5713 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5714 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5717 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5718 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5719 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5722 if (usedBuffers.contains(tessFactorBufferBinding)) {
5723 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5724 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
5727 rpDesc.vertexDescriptor = vertexDesc;
5728 rpDesc.vertexFunction = vertTese.func;
5729 rpDesc.fragmentFunction = pipeline
->d->fs.func;
5735 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5737 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5742 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5744 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5745 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5748 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5751 const QString msg = QString::fromNSString(err.localizedDescription);
5752 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5756 pipeline->d->ps = ps;
5763 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
5766 for (QMetalBuffer *workBuf : *workBuffers) {
5767 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
5768 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5776 for (QMetalBuffer *workBuf : *workBuffers) {
5777 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
5778 workBuf->setSize(size);
5779 if (workBuf->create()) {
5780 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5791 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5794 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5798 workBuffers->append(buf);
5802 qWarning(
"Failed to acquire work buffer of size %u", size);
5810 QByteArray entryPoint;
5811 QShaderKey activeKey;
5813 const QShaderDescription tescDesc = tesc.description();
5814 const QShaderDescription teseDesc = tese.description();
5815 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5816 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5817 if (!
d->tess.outControlPointCount)
5818 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5820 if (!
d->tess.outControlPointCount) {
5821 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5822 d->tess.enabled =
false;
5823 d->tess.failed =
true;
5827 if (m_multiViewCount >= 2)
5828 qWarning(
"Multiview is not supported with tessellation");
5836 bool variantsPresent[3] = {};
5837 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5838 for (
const QShaderKey &k : tessVertKeys) {
5839 switch (k.sourceVariant()) {
5840 case QShader::NonIndexedVertexAsComputeShader:
5841 variantsPresent[0] =
true;
5843 case QShader::UInt32IndexedVertexAsComputeShader:
5844 variantsPresent[1] =
true;
5846 case QShader::UInt16IndexedVertexAsComputeShader:
5847 variantsPresent[2] =
true;
5853 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5854 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5855 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5856 "Try passing --msltess to qsb.");
5857 d->tess.enabled =
false;
5858 d->tess.failed =
true;
5863 for (QShader::Variant variant : {
5864 QShader::NonIndexedVertexAsComputeShader,
5865 QShader::UInt32IndexedVertexAsComputeShader,
5866 QShader::UInt16IndexedVertexAsComputeShader })
5868 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5870 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
5871 d->tess.enabled =
false;
5872 d->tess.failed =
true;
5875 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5877 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5879 d->tess.enabled =
false;
5880 d->tess.failed =
true;
5883 QMetalShader &compVs(d->tess.compVs[varIndex]);
5886 compVs.desc = tessVert.description();
5887 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5888 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5891 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5892 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
5893 d->tess.enabled =
false;
5894 d->tess.failed =
true;
5902 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5903 if (!tessControlLib) {
5904 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5905 d->tess.enabled =
false;
5906 d->tess.failed =
true;
5909 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
5910 if (!tessControlFunc) {
5911 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5912 [tessControlLib release];
5913 d->tess.enabled =
false;
5914 d->tess.failed =
true;
5917 d->tess.compTesc.lib = tessControlLib;
5918 d->tess.compTesc.func = tessControlFunc;
5919 d->tess.compTesc.desc = tesc.description();
5920 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5921 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5922 if (!
d->tess.tescCompPipeline(rhiD)) {
5923 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
5924 d->tess.enabled =
false;
5925 d->tess.failed =
true;
5930 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5932 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5933 d->tess.enabled =
false;
5934 d->tess.failed =
true;
5937 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5938 if (!tessEvalFunc) {
5939 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5940 [tessEvalLib release];
5941 d->tess.enabled =
false;
5942 d->tess.failed =
true;
5945 d->tess.vertTese.lib = tessEvalLib;
5946 d->tess.vertTese.func = tessEvalFunc;
5947 d->tess.vertTese.desc = tese.description();
5948 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5949 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5951 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5953 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5954 d->tess.enabled =
false;
5955 d->tess.failed =
true;
5958 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
5960 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5962 d->tess.enabled =
false;
5963 d->tess.failed =
true;
5966 d->fs.lib = fragLib;
5967 d->fs.func = fragFunc;
5968 d->fs.desc = tessFrag.description();
5969 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5970 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5972 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
5973 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5974 d->tess.enabled =
false;
5975 d->tess.failed =
true;
5979 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5981 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5995 rhiD->pipelineCreationStart();
5996 if (!rhiD->sanityCheckGraphicsPipeline(
this))
6004 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6005 switch (shaderStage.type()) {
6006 case QRhiShaderStage::Vertex:
6007 tessVert = shaderStage.shader();
6009 case QRhiShaderStage::TessellationControl:
6010 tesc = shaderStage.shader();
6012 case QRhiShaderStage::TessellationEvaluation:
6013 tese = shaderStage.shader();
6015 case QRhiShaderStage::Fragment:
6016 tessFrag = shaderStage.shader();
6022 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6023 d->tess.failed =
false;
6025 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6031 QVarLengthArray<QMetalShader *, 6> shaders;
6032 if (
d->tess.enabled) {
6033 shaders.append(&
d->tess.compVs[0]);
6034 shaders.append(&
d->tess.compVs[1]);
6035 shaders.append(&
d->tess.compVs[2]);
6036 shaders.append(&
d->tess.compTesc);
6037 shaders.append(&
d->tess.vertTese);
6039 shaders.append(&
d->vs);
6041 shaders.append(&
d->fs);
6043 for (QMetalShader *shader : shaders) {
6044 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6045 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6046 shader->nativeResourceBindingMap[binding] = {binding, -1};
6047 int maxNativeBinding = 0;
6048 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6049 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6053 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6058 if (!d->bufferSizeBuffer)
6059 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6065 rhiD->pipelineCreationEnd();
6068 rhiD->registerResource(
this);
6097 e.computePipeline.pipelineState =
d->ps;
6102 rhiD
->d->releaseQueue.append(e);
6103 rhiD->unregisterResource(
this);
6110 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6111 cpDesc.binaryArchives = binArchArray;
6119 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6120 const QString msg = QString::fromNSString(err.localizedDescription);
6121 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6132 rhiD->pipelineCreationStart();
6134 auto cacheIt = rhiD
->d->shaderCache.constFind(m_shaderStage);
6135 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6138 const QShader shader = m_shaderStage.shader();
6140 QByteArray entryPoint;
6141 QShaderKey activeKey;
6142 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6143 &error, &entryPoint, &activeKey);
6145 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6148 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6150 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6156 d->cs.localSize = shader.description().computeShaderLocalSize();
6157 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6158 d->cs.desc = shader.description();
6159 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6162 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6163 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6164 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
6167 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6168 for (QMetalShader &s : rhiD->d->shaderCache)
6170 rhiD
->d->shaderCache.clear();
6172 rhiD
->d->shaderCache.insert(m_shaderStage,
d->cs);
6176 [d->cs.func retain];
6178 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6180 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6181 cpDesc.computeFunction =
d->cs.func;
6183 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6185 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6186 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6189 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6190 options: MTLPipelineOptionNone
6195 const QString msg = QString::fromNSString(err.localizedDescription);
6196 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6201 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6203 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6204 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6208 if (!d->bufferSizeBuffer)
6209 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6215 rhiD->pipelineCreationEnd();
6218 rhiD->registerResource(
this);
6242 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6243 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6244 return &nativeHandlesStruct;
6250 d->currentRenderPassEncoder = nil;
6251 d->currentComputePassEncoder = nil;
6252 d->tessellationComputeEncoder = nil;
6253 d->currentPassRpDesc = nil;
6260 currentTarget =
nullptr;
6268 currentPipelineGeneration = 0;
6271 currentSrbGeneration = 0;
6274 currentIndexOffset = 0;
6275 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6280 currentDepthBiasValues = { 0.0f, 0.0f };
6282 d->currentShaderResourceBindingState = {};
6283 d->currentDepthStencilState = nil;
6285 d->currentVertexInputsBuffers.clear();
6286 d->currentVertexInputOffsets.clear();
6296 d->sem[i] =
nullptr;
6297 d->msaaTex[i] = nil;
6317 dispatch_release(
d->sem[i]);
6318 d->sem[i] =
nullptr;
6323 [d->msaaTex[i] release];
6324 d->msaaTex[i] = nil;
6330 [d->curDrawable release];
6331 d->curDrawable = nil;
6335 rhiD->swapchains.remove(
this);
6336 rhiD->unregisterResource(
this);
6356 CALayer *layer =
nullptr;
6358 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6359 layer = cocoaWindow->contentLayer();
6361 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6364 return static_cast<CAMetalLayer *>(layer);
6373 d.reserved[0] = layerForWindow(window);
6380 CAMetalLayer *layer =
d->layer;
6382 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6385 int height = (
int)layer.bounds.size.height;
6386 int width = (
int)layer.bounds.size.width;
6387 width *= layer.contentsScale;
6388 height *= layer.contentsScale;
6389 return QSize(width, height);
6394 if (f == HDRExtendedSrgbLinear) {
6395 if (@available(iOS 16.0, *))
6399 }
else if (f == HDR10) {
6400 if (@available(iOS 16.0, *))
6404 }
else if (f == HDRExtendedDisplayP3Linear) {
6418 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6424 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6425 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6427 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6430 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6434 rhiD->registerResource(rpD,
false);
6441 samples = rhiD->effectiveSampleCount(m_sampleCount);
6443 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6444 d->colorFormat = MTLPixelFormatRGBA16Float;
6445 d->rhiColorFormat = QRhiTexture::RGBA16F;
6448 if (m_format == HDR10) {
6449 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6450 d->rhiColorFormat = QRhiTexture::RGB10A2;
6453 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6454 d->rhiColorFormat = QRhiTexture::BGRA8;
6463 dispatch_semaphore_t sem =
d->sem[slot];
6464 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6465 dispatch_semaphore_signal(sem);
6472 const bool needsRegistration = !window || window != m_window;
6474 if (window && window != m_window)
6479 if (needsRegistration || !rhiD->swapchains.contains(
this))
6480 rhiD->swapchains.insert(
this);
6484 if (window->surfaceType() != QSurface::MetalSurface) {
6485 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6489 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6493 if (
d->colorFormat !=
d->layer.pixelFormat)
6494 d->layer.pixelFormat =
d->colorFormat;
6496 if (m_format == HDRExtendedSrgbLinear) {
6497 if (@available(iOS 16.0, *)) {
6498 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6499 d->layer.wantsExtendedDynamicRangeContent = YES;
6501 }
else if (m_format == HDR10) {
6502 if (@available(iOS 16.0, *)) {
6503 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6504 d->layer.wantsExtendedDynamicRangeContent = YES;
6506 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6507 if (@available(iOS 16.0, *)) {
6508 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6509 d->layer.wantsExtendedDynamicRangeContent = YES;
6513 if (m_flags.testFlag(UsedAsTransferSource))
6514 d->layer.framebufferOnly = NO;
6517 if (m_flags.testFlag(NoVSync))
6518 d->layer.displaySyncEnabled = NO;
6521 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6522 d->layer.opaque = NO;
6523 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6528 d->layer.opaque = NO;
6530 d->layer.opaque = YES;
6536 int width = (
int)
d->layer.bounds.size.width;
6537 int height = (
int)
d->layer.bounds.size.height;
6538 CGSize layerSize = CGSizeMake(width, height);
6539 const float scaleFactor =
d->layer.contentsScale;
6540 layerSize.width *= scaleFactor;
6541 layerSize.height *= scaleFactor;
6542 d->layer.drawableSize = layerSize;
6544 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6545 pixelSize = m_currentPixelSize;
6547 [d->layer setDevice: rhiD->d->dev];
6549 [d->curDrawable release];
6550 d->curDrawable = nil;
6561 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6562 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6563 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6564 m_depthStencil->sampleCount(), m_sampleCount);
6566 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6567 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6568 m_depthStencil->setPixelSize(pixelSize);
6569 if (!m_depthStencil->create())
6570 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6571 pixelSize.width(), pixelSize.height());
6573 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6574 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6575 pixelSize.width(), pixelSize.height());
6579 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6580 rtWrapper.d->pixelSize = pixelSize;
6586 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6587 pixelSize.width(), pixelSize.height(), scaleFactor);
6590 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6591 desc.textureType = MTLTextureType2DMultisample;
6592 desc.pixelFormat =
d->colorFormat;
6593 desc.width = NSUInteger(pixelSize.width());
6594 desc.height = NSUInteger(pixelSize.height());
6595 desc.sampleCount = NSUInteger(
samples);
6596 desc.resourceOptions = MTLResourceStorageModePrivate;
6597 desc.storageMode = MTLStorageModePrivate;
6598 desc.usage = MTLTextureUsageRenderTarget;
6600 if (
d->msaaTex[i]) {
6604 e.renderbuffer.texture =
d->msaaTex[i];
6605 rhiD
->d->releaseQueue.append(e);
6607 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6612 rhiD->registerResource(
this);
6628#if defined(Q_OS_MACOS)
6629 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6630 NSScreen *screen = view.window.screen;
6631 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6632 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6633#elif defined(Q_OS_IOS)
6634 if (@available(iOS 16.0, *)) {
6635 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6636 UIScreen *screen = view.window.windowScene.screen;
6637 info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
6638 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.potentialEDRHeadroom;
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanQueueSubmitParams::waitSemaphoreCount
id< MTLTexture > viewForLevel(int level)
id< MTLTexture > perLevelViews[QRhi::MAX_MIP_LEVELS]
id< MTLBuffer > stagingBuf[QMTL_FRAMES_IN_FLIGHT]
QMetalTextureData(QMetalTexture *t)
~QMetalTextureRenderTarget()
float devicePixelRatio() const override
QMetalRenderTargetData * d
QMetalTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags)
bool create() override
Creates the corresponding native graphics resources.
QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor() override
int sampleCount() const override
QSize pixelSize() const override
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize, int depth, int arraySize, int sampleCount, Flags flags)
bool prepareCreate(QSize *adjustedSize=nullptr)
NativeTexture nativeTexture() override
bool create() override
Creates the corresponding native graphics resources.
void destroy() override
Releases (or requests deferred releasing of) the underlying native graphics resources.
bool createFrom(NativeTexture src) override
Similar to create(), except that no new native textures are created.
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