6#include <QGuiApplication>
10#include <QTemporaryFile>
13#include <QOperatingSystemVersion>
15#include <QtCore/private/qcore_mac_p.h>
16#include <QtGui/private/qmetallayer_p.h>
17#include <QtGui/qpa/qplatformwindow_p.h>
20#include <AppKit/AppKit.h>
22#include <UIKit/UIKit.h>
25#include <QuartzCore/CATransaction.h>
27#include <Metal/Metal.h>
32
33
34
35
36
37
38
39
40
43#error ARC not supported
52#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
57#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
100
101
102
103
104
105
106
107
110
111
112
113
116
117
118
119
120
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
144
145
148
149
162 nativeResourceBindingMap.clear();
181 const QColor &colorClearValue,
182 const QRhiDepthStencilClearValue &depthStencilClearValue,
184 QRhiShadingRateMap *shadingRateMap);
186 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
215 id<MTLTexture> texture;
433 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
442 return patchCount *
sizeof(
float) * 128;
490 if (importDevice->dev) {
491 d->dev = (id<MTLDevice>) importDevice->dev;
493 if (importedCmdQueue)
494 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
496 qWarning(
"No MTLDevice given, cannot import");
510 return (v + byteAlign - 1) & ~(byteAlign - 1);
516 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
530 return [cmdQueue commandBufferWithUnretainedReferences];
532 return [cmdQueue commandBuffer];
543 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
544 binArchDesc.url = sourceFileUrl;
546 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
547 [binArchDesc release];
549 const QString msg = QString::fromNSString(err.localizedDescription);
550 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
563 d->dev = MTLCreateSystemDefaultDevice();
566 qWarning(
"No MTLDevice");
570 const QString deviceName = QString::fromNSString([d->dev name]);
571 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
572 driverInfoStruct.deviceName = deviceName.toUtf8();
579 const MTLDeviceLocation deviceLocation = [d->dev location];
580 switch (deviceLocation) {
581 case MTLDeviceLocationBuiltIn:
582 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
584 case MTLDeviceLocationSlot:
585 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
587 case MTLDeviceLocationExternal:
588 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
594 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
597 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
598 osMajor = ver.majorVersion();
599 osMinor = ver.minorVersion();
601 if (importedCmdQueue)
602 [d->cmdQueue retain];
604 d->cmdQueue = [d->dev newCommandQueue];
606 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
610 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
611 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
612 d->captureScope.label = label.toNSString();
614#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
615 caps.maxTextureSize = 16384;
616 caps.baseVertexAndInstance =
true;
617 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
618 caps.maxThreadGroupSize = 1024;
619 caps.multiView =
true;
620#elif defined(Q_OS_TVOS)
621 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
622 caps.maxTextureSize = 16384;
624 caps.maxTextureSize = 8192;
625 caps.baseVertexAndInstance =
false;
626 caps.isAppleGPU =
true;
627#elif defined(Q_OS_IOS)
628 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
629 caps.maxTextureSize = 16384;
630 caps.baseVertexAndInstance =
true;
631 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
632 caps.maxTextureSize = 8192;
633 caps.baseVertexAndInstance =
false;
635 caps.maxTextureSize = 4096;
636 caps.baseVertexAndInstance =
false;
638 caps.isAppleGPU =
true;
639 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
640 caps.maxThreadGroupSize = 1024;
641 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
642 caps.multiView =
true;
645 caps.supportedSampleCounts = { 1 };
646 for (
int sampleCount : { 2, 4, 8 }) {
647 if ([d->dev supportsTextureSampleCount: sampleCount])
648 caps.supportedSampleCounts.append(sampleCount);
651 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
652 if (caps.shadingRateMap && caps.multiView)
653 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
655 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
656 d->setupBinaryArchive();
658 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
659 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
669 for (QMetalShader &s : d->shaderCache)
671 d->shaderCache.clear();
673 [d->captureScope release];
674 d->captureScope = nil;
676 [d->binArch release];
679 [d->cmdQueue release];
680 if (!importedCmdQueue)
690 return caps.supportedSampleCounts;
695 Q_UNUSED(sampleCount);
696 return { QSize(1, 1) };
701 return new QMetalSwapChain(
this);
704QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
706 return new QMetalBuffer(
this, type, usage, size);
733 if (m.isIdentity()) {
735 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
736 0.0f, 1.0f, 0.0f, 0.0f,
737 0.0f, 0.0f, 0.5f, 0.5f,
738 0.0f, 0.0f, 0.0f, 1.0f);
747 bool supportsFamilyMac2 =
false;
748 bool supportsFamilyApple3 =
false;
751 supportsFamilyMac2 =
true;
753 supportsFamilyApple3 =
true;
755 supportsFamilyApple3 =
true;
759 if (format == QRhiTexture::BC5)
762 if (!supportsFamilyApple3) {
763 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
765 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
769 if (!supportsFamilyMac2)
770 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
779 case QRhi::MultisampleTexture:
781 case QRhi::MultisampleRenderBuffer:
783 case QRhi::DebugMarkers:
785 case QRhi::Timestamps:
787 case QRhi::Instancing:
789 case QRhi::CustomInstanceStepRate:
791 case QRhi::PrimitiveRestart:
793 case QRhi::NonDynamicUniformBuffers:
795 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
797 case QRhi::NPOTTextureRepeat:
799 case QRhi::RedOrAlpha8IsRed:
801 case QRhi::ElementIndexUint:
805 case QRhi::WideLines:
807 case QRhi::VertexShaderPointSize:
809 case QRhi::BaseVertex:
810 return caps.baseVertexAndInstance;
811 case QRhi::BaseInstance:
812 return caps.baseVertexAndInstance;
813 case QRhi::TriangleFanTopology:
815 case QRhi::ReadBackNonUniformBuffer:
817 case QRhi::ReadBackNonBaseMipLevel:
819 case QRhi::TexelFetch:
821 case QRhi::RenderToNonBaseMipLevel:
823 case QRhi::IntAttributes:
825 case QRhi::ScreenSpaceDerivatives:
827 case QRhi::ReadBackAnyTextureFormat:
829 case QRhi::PipelineCacheDataLoadSave:
831 case QRhi::ImageDataStride:
833 case QRhi::RenderBufferImport:
835 case QRhi::ThreeDimensionalTextures:
837 case QRhi::RenderTo3DTextureSlice:
839 case QRhi::TextureArrays:
841 case QRhi::Tessellation:
843 case QRhi::GeometryShader:
845 case QRhi::TextureArrayRange:
847 case QRhi::NonFillPolygonMode:
849 case QRhi::OneDimensionalTextures:
851 case QRhi::OneDimensionalTextureMipmaps:
853 case QRhi::HalfAttributes:
855 case QRhi::RenderToOneDimensionalTexture:
857 case QRhi::ThreeDimensionalTextureMipmaps:
859 case QRhi::MultiView:
860 return caps.multiView;
861 case QRhi::TextureViewFormat:
863 case QRhi::ResolveDepthStencil:
865 case QRhi::VariableRateShading:
867 case QRhi::VariableRateShadingMap:
868 return caps.shadingRateMap;
869 case QRhi::VariableRateShadingMapWithTexture:
871 case QRhi::PerRenderTargetBlending:
872 case QRhi::SampleVariables:
874 case QRhi::InstanceIndexIncludesBaseInstance:
885 case QRhi::TextureSizeMin:
887 case QRhi::TextureSizeMax:
888 return caps.maxTextureSize;
889 case QRhi::MaxColorAttachments:
891 case QRhi::FramesInFlight:
893 case QRhi::MaxAsyncReadbackFrames:
895 case QRhi::MaxThreadGroupsPerDimension:
897 case QRhi::MaxThreadsPerThreadGroup:
899 case QRhi::MaxThreadGroupX:
901 case QRhi::MaxThreadGroupY:
903 case QRhi::MaxThreadGroupZ:
904 return caps.maxThreadGroupSize;
905 case QRhi::TextureArraySizeMax:
907 case QRhi::MaxUniformBufferRange:
909 case QRhi::MaxVertexInputs:
911 case QRhi::MaxVertexOutputs:
913 case QRhi::ShadingRateImageTileSize:
923 return &nativeHandlesStruct;
928 return driverInfoStruct;
934 result.totalPipelineCreationTime = totalPipelineCreationTime();
944void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
951 for (QMetalShader &s : d->shaderCache)
954 d->shaderCache.clear();
976 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
981 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
986 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
987 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
989 if (![d->binArch serializeToURL: url error: &err]) {
990 const QString msg = QString::fromNSString(err.localizedDescription);
992 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
997 if (!f.open(QIODevice::ReadOnly)) {
998 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1001 const QByteArray blob = f.readAll();
1005 const quint32 dataSize = quint32(blob.size());
1007 data.resize(headerSize + dataSize);
1010 header.rhiId = pipelineCacheRhiId();
1011 header.arch = quint32(
sizeof(
void*));
1012 header.dataSize = quint32(dataSize);
1013 header.osMajor = osMajor;
1014 header.osMinor = osMinor;
1015 const size_t driverStrLen = qMin(
sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1017 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1018 header.driver[driverStrLen] =
'\0';
1020 memcpy(data.data(), &header, headerSize);
1021 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1031 if (data.size() < qsizetype(headerSize)) {
1032 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1036 const size_t dataOffset = headerSize;
1038 memcpy(&header, data.constData(), headerSize);
1040 const quint32 rhiId = pipelineCacheRhiId();
1041 if (header.rhiId != rhiId) {
1042 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1043 rhiId, header.rhiId);
1047 const quint32 arch = quint32(
sizeof(
void*));
1048 if (header.arch != arch) {
1049 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1054 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1055 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1056 osMajor, osMinor, header.osMajor, header.osMinor);
1060 const size_t driverStrLen = qMin(
sizeof(header.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1061 if (strncmp(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1062 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1066 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1067 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1071 const char *p = data.constData() + dataOffset;
1075 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1078 tmp.write(p, header.dataSize);
1081 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1082 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1083 if (d->setupBinaryArchive(url))
1084 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1087QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1088 int sampleCount, QRhiRenderBuffer::Flags flags,
1089 QRhiTexture::Format backingFormatHint)
1091 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1095 const QSize &pixelSize,
int depth,
int arraySize,
1096 int sampleCount, QRhiTexture::Flags flags)
1098 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1102 QRhiSampler::Filter mipmapMode,
1103 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1105 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1110 return new QMetalShadingRateMap(
this);
1114 QRhiTextureRenderTarget::Flags flags)
1121 return new QMetalGraphicsPipeline(
this);
1126 return new QMetalComputePipeline(
this);
1131 return new QMetalShaderResourceBindings(
this);
1142 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1145 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1146 if (!map || map->isEmpty())
1149 auto it = map->constFind(binding);
1150 if (it != map->cend())
1161 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1162 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1165 case QMetalShaderResourceBindingsData::VERTEX:
1166 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1167 offsets: offsetBatch.resources.constData()
1168 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1170 case QMetalShaderResourceBindingsData::FRAGMENT:
1171 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1172 offsets: offsetBatch.resources.constData()
1173 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1175 case QMetalShaderResourceBindingsData::COMPUTE:
1176 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1177 offsets: offsetBatch.resources.constData()
1178 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1192 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1195 case QMetalShaderResourceBindingsData::VERTEX:
1196 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1197 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1199 case QMetalShaderResourceBindingsData::FRAGMENT:
1200 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1201 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1203 case QMetalShaderResourceBindingsData::COMPUTE:
1204 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1205 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1219 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1221 switch (encoderStage) {
1222 case QMetalShaderResourceBindingsData::VERTEX:
1223 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1224 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1226 case QMetalShaderResourceBindingsData::FRAGMENT:
1227 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1228 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1230 case QMetalShaderResourceBindingsData::COMPUTE:
1231 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1232 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1254 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1255 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1256 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1257 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1260 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1261 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1262 bindStageTextures(cbD, encoderStage, batch);
1265 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1266 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1267 bindStageSamplers(cbD, encoderStage, batch);
1274 case QMetalShaderResourceBindingsData::VERTEX:
1275 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1276 case QMetalShaderResourceBindingsData::TESSCTRL:
1277 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1278 case QMetalShaderResourceBindingsData::TESSEVAL:
1279 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1280 case QMetalShaderResourceBindingsData::FRAGMENT:
1281 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1282 case QMetalShaderResourceBindingsData::COMPUTE:
1283 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1286 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1291 int dynamicOffsetCount,
1292 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1293 bool offsetOnlyChange,
1294 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1298 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1299 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1301 case QRhiShaderResourceBinding::UniformBuffer:
1303 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1304 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1305 quint32 offset = b->u.ubuf.offset;
1306 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1307 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1308 if (dynOfs.first == b->binding) {
1309 offset = dynOfs.second;
1314 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1315 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1316 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1317 if (nativeBinding >= 0)
1318 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1323 case QRhiShaderResourceBinding::SampledTexture:
1324 case QRhiShaderResourceBinding::Texture:
1325 case QRhiShaderResourceBinding::Sampler:
1327 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1328 for (
int elem = 0; elem < data->count; ++elem) {
1329 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1330 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1332 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1333 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1338 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1339 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1340 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1341 if (textureBinding >= 0 && texD)
1342 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1343 if (samplerBinding >= 0)
1344 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1350 case QRhiShaderResourceBinding::ImageLoad:
1351 case QRhiShaderResourceBinding::ImageStore:
1352 case QRhiShaderResourceBinding::ImageLoadStore:
1354 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1355 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1357 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1358 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1359 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1360 if (nativeBinding >= 0)
1361 bindingData.res[stage].textures.append({ nativeBinding, t });
1366 case QRhiShaderResourceBinding::BufferLoad:
1367 case QRhiShaderResourceBinding::BufferStore:
1368 case QRhiShaderResourceBinding::BufferLoadStore:
1370 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1371 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1372 quint32 offset = b->u.sbuf.offset;
1373 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1374 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1375 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1376 if (nativeBinding >= 0)
1377 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1400 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1401 return a.nativeBinding < b.nativeBinding;
1404 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1405 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1406 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1409 bindingData.res[stage].bufferBatches.finish();
1410 bindingData.res[stage].bufferOffsetBatches.finish();
1412 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1413 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1414 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1416 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1417 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1418 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1419 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1423 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1426 if (offsetOnlyChange)
1429 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1430 return a.nativeBinding < b.nativeBinding;
1433 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1434 return a.nativeBinding < b.nativeBinding;
1437 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1438 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1440 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1441 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1443 bindingData.res[stage].textureBatches.finish();
1444 bindingData.res[stage].samplerBatches.finish();
1446 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1447 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1449 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1450 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1454 bindStageTextures(cbD, stage, batch);
1457 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1458 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1460 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1461 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1465 bindStageSamplers(cbD, stage, batch);
1469 cbD
->d->currentShaderResourceBindingState = bindingData;
1474 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1476 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1477 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1478 cbD
->d->currentDepthStencilState =
d->ds;
1482 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1486 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1490 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1493 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1496 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1497 slopeScale: d->slopeScaledDepthBias
1514 cbD->currentPipelineGeneration = psD->generation;
1516 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1521 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1522 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1523 workBuf->lastActiveFrameSlot = -1;
1525 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1526 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1527 workBuf->lastActiveFrameSlot = -1;
1530 psD->lastActiveFrameSlot = currentFrameSlot;
1534 int dynamicOffsetCount,
1535 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1544 srb = gfxPsD->m_shaderResourceBindings;
1546 srb = compPsD->m_shaderResourceBindings;
1550 bool hasSlottedResourceInSrb =
false;
1551 bool hasDynamicOffsetInSrb =
false;
1552 bool resNeedsRebind =
false;
1554 bool pipelineChanged =
false;
1567 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1570 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1571 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1574 case QRhiShaderResourceBinding::UniformBuffer:
1577 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1580 hasSlottedResourceInSrb =
true;
1581 if (b->u.ubuf.hasDynamicOffset)
1582 hasDynamicOffsetInSrb =
true;
1583 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1584 resNeedsRebind =
true;
1585 bd.ubuf.id = bufD->m_id;
1586 bd.ubuf.generation = bufD->generation;
1588 bufD->lastActiveFrameSlot = currentFrameSlot;
1591 case QRhiShaderResourceBinding::SampledTexture:
1592 case QRhiShaderResourceBinding::Texture:
1593 case QRhiShaderResourceBinding::Sampler:
1595 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1596 if (bd.stex.count != data->count) {
1597 bd.stex.count = data->count;
1598 resNeedsRebind =
true;
1600 for (
int elem = 0; elem < data->count; ++elem) {
1603 Q_ASSERT(texD || samplerD);
1604 const quint64 texId = texD ? texD->m_id : 0;
1605 const uint texGen = texD ? texD->generation : 0;
1606 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1607 const uint samplerGen = samplerD ? samplerD->generation : 0;
1608 if (texGen != bd.stex.d[elem].texGeneration
1609 || texId != bd.stex.d[elem].texId
1610 || samplerGen != bd.stex.d[elem].samplerGeneration
1611 || samplerId != bd.stex.d[elem].samplerId)
1613 resNeedsRebind =
true;
1614 bd.stex.d[elem].texId = texId;
1615 bd.stex.d[elem].texGeneration = texGen;
1616 bd.stex.d[elem].samplerId = samplerId;
1617 bd.stex.d[elem].samplerGeneration = samplerGen;
1620 texD->lastActiveFrameSlot = currentFrameSlot;
1622 samplerD->lastActiveFrameSlot = currentFrameSlot;
1626 case QRhiShaderResourceBinding::ImageLoad:
1627 case QRhiShaderResourceBinding::ImageStore:
1628 case QRhiShaderResourceBinding::ImageLoadStore:
1631 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1632 resNeedsRebind =
true;
1633 bd.simage.id = texD->m_id;
1634 bd.simage.generation = texD->generation;
1636 texD->lastActiveFrameSlot = currentFrameSlot;
1639 case QRhiShaderResourceBinding::BufferLoad:
1640 case QRhiShaderResourceBinding::BufferStore:
1641 case QRhiShaderResourceBinding::BufferLoadStore:
1644 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1646 if (needsBufferSizeBuffer) {
1647 for (
int i = 0; i < 6; ++i) {
1648 const QRhiShaderResourceBinding::StageFlag stage =
1649 QRhiShaderResourceBinding::StageFlag(1 << i);
1650 if (b->stage.testFlag(stage)) {
1651 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1657 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1658 resNeedsRebind =
true;
1659 bd.sbuf.id = bufD->m_id;
1660 bd.sbuf.generation = bufD->generation;
1662 bufD->lastActiveFrameSlot = currentFrameSlot;
1671 if (needsBufferSizeBuffer) {
1673 QVarLengthArray<QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1677 Q_ASSERT(compPsD->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1678 shaders.append(qMakePair(&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage));
1681 if (gfxPsD
->d->tess.enabled) {
1691 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1692 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1693 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1694 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1695 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1696 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1697 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1698 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1699 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1700 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1701 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1702 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1704 if (gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1705 shaders.append(qMakePair(&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage));
1707 if (gfxPsD->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1708 shaders.append(qMakePair(&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage));
1710 if (gfxPsD->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1711 shaders.append(qMakePair(&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage));
1714 if (gfxPsD->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1715 shaders.append(qMakePair(&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage));
1717 if (gfxPsD->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1718 shaders.append(qMakePair(&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage));
1722 for (
const QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag> &shader : shaders) {
1724 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1727 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1729 int maxNativeBinding = 0;
1730 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1731 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1733 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1735 Q_ASSERT(offset + size <= bufD->size());
1736 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1738 QMetalShaderResourceBindings::BoundResourceData bd;
1739 bd.sbuf.id = bufD->m_id;
1740 bd.sbuf.generation = bufD->generation;
1741 srbD->boundResourceData.append(bd);
1745 QVarLengthArray<
int, 8> bufferSizeBufferData;
1746 Q_ASSERT(storageBufferSizes.contains(shader.second));
1747 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1748 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1749 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1755 if (bufferSizeBufferData.size() <= index)
1756 bufferSizeBufferData.resize(index + 1);
1758 Q_ASSERT(sizes.contains(block.binding));
1759 bufferSizeBufferData[index] = sizes[block.binding];
1762 QRhiBufferData data;
1763 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1764 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1765 Q_ASSERT(offset + size <= bufD->size());
1766 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1769 offset += ((size + 31) / 32) * 32;
1773 bufD->lastActiveFrameSlot = currentFrameSlot;
1777 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1779 resNeedsRebind =
true;
1782 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1785 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1786 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1790 if (gfxPsD
->d->tess.enabled) {
1793 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1794 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1807 cbD->currentSrbGeneration = srbD->generation;
1810 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1811 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1816 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1817 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1822 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1823 QRhiBatchedBindings<NSUInteger> offsets;
1824 for (
int i = 0; i < bindingCount; ++i) {
1827 bufD->lastActiveFrameSlot = currentFrameSlot;
1828 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1829 buffers.feed(startBinding + i, mtlbuf);
1830 offsets.feed(startBinding + i, bindings[i].second);
1845 || buffers != cbD
->d->currentVertexInputsBuffers
1846 || offsets != cbD
->d->currentVertexInputOffsets)
1849 cbD
->d->currentVertexInputsBuffers = buffers;
1850 cbD
->d->currentVertexInputOffsets = offsets;
1852 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1853 const auto &bufferBatch(buffers.batches[i]);
1854 const auto &offsetBatch(offsets.batches[i]);
1855 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1856 bufferBatch.resources.constData()
1857 offsets: offsetBatch.resources.constData()
1858 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1865 ibufD->lastActiveFrameSlot = currentFrameSlot;
1867 cbD->currentIndexOffset = indexOffset;
1868 cbD->currentIndexFormat = indexFormat;
1878 QSize outputSize = cbD->currentTarget->pixelSize();
1884 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1885 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1886 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1887 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1888 auto screenSize = [rateMap screenSize];
1889 outputSize = QSize(screenSize.width, screenSize.height);
1896 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1900 vp.originX =
double(x);
1901 vp.originY =
double(y);
1902 vp.width =
double(w);
1903 vp.height =
double(h);
1904 vp.znear =
double(viewport.minDepth());
1905 vp.zfar =
double(viewport.maxDepth());
1907 [cbD->d->currentRenderPassEncoder setViewport: vp];
1909 if (cbD->currentGraphicsPipeline
1910 && !cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
1912 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1913 s.x = NSUInteger(x);
1914 s.y = NSUInteger(y);
1915 s.width = NSUInteger(w);
1916 s.height = NSUInteger(h);
1917 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1925 Q_ASSERT(!cbD->currentGraphicsPipeline
1926 || cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
1927 const QSize outputSize = cbD->currentTarget->pixelSize();
1931 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1935 s.x = NSUInteger(x);
1936 s.y = NSUInteger(y);
1937 s.width = NSUInteger(w);
1938 s.height = NSUInteger(h);
1940 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1948 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
1949 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
1957 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
1963 Q_UNUSED(coarsePixelSize);
1968 if (cbD
->d->currentRenderPassEncoder) {
1969 [cbD->d->currentRenderPassEncoder endEncoding];
1970 cbD->d->currentRenderPassEncoder = nil;
1973 if (!cbD->d->tessellationComputeEncoder)
1974 cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
1976 return cbD
->d->tessellationComputeEncoder;
1981 if (cbD
->d->tessellationComputeEncoder) {
1982 [cbD->d->tessellationComputeEncoder endEncoding];
1983 cbD->d->tessellationComputeEncoder = nil;
1988 switch (cbD->currentTarget->resourceType()) {
1989 case QRhiResource::SwapChainRenderTarget:
1992 case QRhiResource::TextureRenderTarget:
2001 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2003 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2004 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2005 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2008 MTLLoadAction oldDepthLoad;
2009 MTLLoadAction oldStencilLoad;
2011 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2012 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2013 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2015 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2016 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2017 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2020 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2024 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2028 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2029 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2038 if (graphicsPipeline
->d->tess.failed)
2042 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2043 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2047 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2053 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
2057 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2058 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2059 if (args.type == TessDrawArgs::U16Indexed)
2060 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2061 else if (args.type == TessDrawArgs::U32Indexed)
2062 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2063 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2064 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2065 [computeEncoder setComputePipelineState: computePipelineState];
2070 cbD
->d->currentComputePassEncoder = computeEncoder;
2072 cbD->d->currentComputePassEncoder = nil;
2074 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2075 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2076 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2078 if (outputBufferBinding >= 0) {
2079 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2080 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2083 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2086 if (indexBufferBinding >= 0)
2087 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2089 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2090 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2091 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2092 [computeEncoder setBuffers: bufferBatch.resources.constData()
2093 offsets: offsetBatch.resources.constData()
2094 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2098 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2099 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2101 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2102 args.draw.vertexCount, args.draw.instanceCount)];
2105 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2106 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2111 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2112 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2113 [computeEncoder setComputePipelineState: computePipelineState];
2115 cbD
->d->currentComputePassEncoder = computeEncoder;
2117 cbD->d->currentComputePassEncoder = nil;
2119 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2120 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2121 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2122 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2123 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2124 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2126 if (outputBufferBinding >= 0) {
2127 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2128 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2131 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2134 if (patchOutputBufferBinding >= 0) {
2135 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2136 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2137 if (!tescPatchOutBuf)
2139 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2142 if (tessFactorBufferBinding >= 0) {
2143 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2144 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2147 if (paramsBufferBinding >= 0) {
2149 quint32 inControlPointCount;
2156 params.patchCount = patchCount;
2157 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2158 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2159 memcpy(p, ¶ms,
sizeof(params));
2160 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2163 if (vertOutBuf && inputBufferBinding >= 0)
2164 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2166 int sgSize =
int(computePipelineState.threadExecutionWidth);
2167 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2168 while (wgSize > caps.maxThreadGroupSize) {
2170 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2172 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2173 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2190 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2195 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2196 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2197 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2198 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2200 if (outputBufferBinding >= 0 && tescOutBuf)
2201 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2203 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2204 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2206 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2207 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2208 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2211 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2213 patchCount: patchCount
2214 patchIndexBuffer: nil
2215 patchIndexBufferOffset: 0
2225 if (multiViewCount <= 1)
2229 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2230 if (viewMaskBufBinding == -1) {
2231 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2238 multiViewInfo.viewOffset = 0;
2239 multiViewInfo.viewCount = quint32(multiViewCount);
2243 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2244 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2245 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2246 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2250 *instanceCount *= multiViewCount;
2255 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2264 a.draw.vertexCount = vertexCount;
2265 a.draw.instanceCount = instanceCount;
2266 a.draw.firstVertex = firstVertex;
2267 a.draw.firstInstance = firstInstance;
2272 adjustForMultiViewDraw(&instanceCount, cb);
2274 if (caps.baseVertexAndInstance) {
2275 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2276 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2278 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2279 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2284 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2292 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2293 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2296 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2301 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2302 a.drawIndexed.indexCount = indexCount;
2303 a.drawIndexed.instanceCount = instanceCount;
2304 a.drawIndexed.firstIndex = firstIndex;
2305 a.drawIndexed.vertexOffset = vertexOffset;
2306 a.drawIndexed.firstInstance = firstInstance;
2307 a.drawIndexed.indexBuffer = mtlibuf;
2312 adjustForMultiViewDraw(&instanceCount, cb);
2314 if (caps.baseVertexAndInstance) {
2315 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2316 indexCount: indexCount
2317 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2318 indexBuffer: mtlibuf
2319 indexBufferOffset: indexOffset
2320 instanceCount: instanceCount
2321 baseVertex: vertexOffset
2322 baseInstance: firstInstance];
2324 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2325 indexCount: indexCount
2326 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2327 indexBuffer: mtlibuf
2328 indexBufferOffset: indexOffset
2329 instanceCount: instanceCount];
2338 NSString *str = [NSString stringWithUTF8String: name.constData()];
2340 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2341 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2343 [cbD->d->cb pushDebugGroup: str];
2352 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2353 [cbD->d->currentRenderPassEncoder popDebugGroup];
2355 [cbD->d->cb popDebugGroup];
2364 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2365 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2370 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2396 currentFrameSlot = swapChainD->currentFrameSlot;
2401 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2409 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2410 if (sc != swapChainD)
2411 sc->waitUntilCompleted(currentFrameSlot);
2414 [d->captureScope beginScope];
2416 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2420 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2427 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2428 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2433 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2436 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2437 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2440 return QRhi::FrameOpSuccess;
2449 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2451 __block
int thisFrameSlot = currentFrameSlot;
2452 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2453 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2454 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2461 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2462 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2463 [drawableTexture release];
2467 if (flags.testFlag(QRhi::SkipPresent)) {
2469 [commandBuffer commit];
2471 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2473 if (swapChainD
->d->layer.presentsWithTransaction) {
2474 [commandBuffer commit];
2476 auto *metalLayer = swapChainD
->d->layer;
2477 auto presentWithTransaction = ^{
2478 [commandBuffer waitUntilScheduled];
2485 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2486 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2487 if (textureSize == surfaceSize) {
2490 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2491 << textureSize <<
"not matching surface size" << surfaceSize;
2495 if (NSThread.currentThread == NSThread.mainThread) {
2496 presentWithTransaction();
2498 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2499 Q_ASSERT(qtMetalLayer);
2501 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2505 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2506 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2512 if (qtMetalLayer.displayLock.tryLockForRead()) {
2514 qtMetalLayer.displayLock.unlock();
2516 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2517 <<
"due to" << qtMetalLayer <<
"needing display";
2523 [commandBuffer commit];
2527 [commandBuffer commit];
2534 [swapChainD->d->curDrawable release];
2535 swapChainD->d->curDrawable = nil;
2537 [d->captureScope endScope];
2541 return QRhi::FrameOpSuccess;
2548 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2550 for (QMetalSwapChain *sc : std::as_const(swapchains))
2551 sc->waitUntilCompleted(currentFrameSlot);
2553 d->ofr.active =
true;
2554 *cb = &
d->ofr.cbWrapper;
2555 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2558 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2559 d->ofr.lastGpuTime = 0;
2562 return QRhi::FrameOpSuccess;
2568 Q_ASSERT(
d->ofr.active);
2569 d->ofr.active =
false;
2571 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2575 [cb waitUntilCompleted];
2577 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2581 return QRhi::FrameOpSuccess;
2586 id<MTLCommandBuffer> cb = nil;
2589 if (
d->ofr.active) {
2592 cb =
d->ofr.cbWrapper.d->cb;
2597 cb = swapChainD->cbWrapper.d->cb;
2601 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2602 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2603 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2608 sc->waitUntilCompleted(i);
2614 [cb waitUntilCompleted];
2618 if (
d->ofr.active) {
2619 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2620 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2622 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2623 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2631 return QRhi::FrameOpSuccess;
2635 const QColor &colorClearValue,
2636 const QRhiDepthStencilClearValue &depthStencilClearValue,
2638 QRhiShadingRateMap *shadingRateMap)
2640 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2641 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2642 colorClearValue.alphaF());
2644 for (uint i = 0; i < uint(colorAttCount); ++i) {
2645 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2646 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2647 rp.colorAttachments[i].clearColor = c;
2650 if (hasDepthStencil) {
2651 rp.depthAttachment.loadAction = MTLLoadActionClear;
2652 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2653 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2654 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2655 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2656 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2660 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
2668 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2669 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2670 if (imageSizeBytes > 0)
2671 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2676 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
2679 const QPoint dp = subresDesc.destinationTopLeft();
2680 const QByteArray rawData = subresDesc.data();
2681 QImage img = subresDesc.image();
2682 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2683 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
2685 if (!img.isNull()) {
2686 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2687 int w = img.width();
2688 int h = img.height();
2689 int bpl = img.bytesPerLine();
2691 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2692 const int sx = subresDesc.sourceTopLeft().x();
2693 const int sy = subresDesc.sourceTopLeft().y();
2694 if (!subresDesc.sourceSize().isEmpty()) {
2695 w = subresDesc.sourceSize().width();
2696 h = subresDesc.sourceSize().height();
2698 if (w == img.width()) {
2699 const int bpc = qMax(1, img.depth() / 8);
2700 Q_ASSERT(h * img.bytesPerLine() <= fullImageSizeBytes);
2701 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
2702 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
2703 h * img.bytesPerLine());
2705 img = img.copy(sx, sy, w, h);
2706 bpl = img.bytesPerLine();
2707 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2708 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2711 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2714 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2715 sourceOffset: NSUInteger(*curOfs)
2716 sourceBytesPerRow: NSUInteger(bpl)
2717 sourceBytesPerImage: 0
2718 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2719 toTexture: texD->d->tex
2720 destinationSlice: NSUInteger(is3D ? 0 : layer)
2721 destinationLevel: NSUInteger(level)
2722 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2723 options: MTLBlitOptionNone];
2725 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2726 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2727 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2728 const int subresw = subresSize.width();
2729 const int subresh = subresSize.height();
2731 if (subresDesc.sourceSize().isEmpty()) {
2735 w = subresDesc.sourceSize().width();
2736 h = subresDesc.sourceSize().height();
2741 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
2743 const int dx = aligned(dp.x(), blockDim.width());
2744 const int dy = aligned(dp.y(), blockDim.height());
2745 if (dx + w != subresw)
2746 w = aligned(w, blockDim.width());
2747 if (dy + h != subresh)
2748 h = aligned(h, blockDim.height());
2750 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2752 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2753 sourceOffset: NSUInteger(*curOfs)
2754 sourceBytesPerRow: bpl
2755 sourceBytesPerImage: 0
2756 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2757 toTexture: texD->d->tex
2758 destinationSlice: NSUInteger(is3D ? 0 : layer)
2759 destinationLevel: NSUInteger(level)
2760 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2761 options: MTLBlitOptionNone];
2763 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2764 }
else if (!rawData.isEmpty()) {
2765 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2766 const int subresw = subresSize.width();
2767 const int subresh = subresSize.height();
2769 if (subresDesc.sourceSize().isEmpty()) {
2773 w = subresDesc.sourceSize().width();
2774 h = subresDesc.sourceSize().height();
2778 if (subresDesc.dataStride())
2779 bpl = subresDesc.dataStride();
2781 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
2783 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2785 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2786 sourceOffset: NSUInteger(*curOfs)
2787 sourceBytesPerRow: bpl
2788 sourceBytesPerImage: 0
2789 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2790 toTexture: texD->d->tex
2791 destinationSlice: NSUInteger(is3D ? 0 : layer)
2792 destinationLevel: NSUInteger(level)
2793 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2794 options: MTLBlitOptionNone];
2796 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2798 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2807 id<MTLBlitCommandEncoder> blitEnc = nil;
2808 auto ensureBlit = [&blitEnc, cbD,
this]() {
2810 blitEnc = [cbD->d->cb blitCommandEncoder];
2812 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
2820 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2822 if (u.offset == 0 && u
.data.size() == bufD->m_size)
2823 bufD
->d->pendingUpdates[i].clear();
2824 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2830 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2831 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
2833 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2837 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2838 if (bufD->m_type == QRhiBuffer::Dynamic) {
2839 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
2841 u.result->data.resize(u.readSize);
2842 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2844 if (u.result->completed)
2845 u.result->completed();
2849 readback.buf = bufD
->d->buf[idx];
2850 readback.offset = u.offset;
2851 readback.readSize = u.readSize;
2852 readback.result = u.result;
2853 d->activeBufferReadbacks.append(readback);
2855 if (bufD->d->managed) {
2858 [blitEnc synchronizeResource:readback.buf];
2869 qsizetype stagingSize = 0;
2870 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2871 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2872 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2873 stagingSize += subresUploadByteSize(subresDesc);
2878 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2879 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2880 options: MTLResourceStorageModeShared];
2882 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2883 qsizetype curOfs = 0;
2884 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2885 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2886 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2887 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2891 utexD->lastActiveFrameSlot = currentFrameSlot;
2895 e.lastActiveFrameSlot = currentFrameSlot;
2896 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2897 utexD->d->stagingBuf[currentFrameSlot] = nil;
2898 d->releaseQueue.append(e);
2903 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2904 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2905 const QPoint dp = u.desc.destinationTopLeft();
2906 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2907 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2908 const QPoint sp = u.desc.sourceTopLeft();
2911 [blitEnc copyFromTexture: srcD->d->tex
2912 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2913 sourceLevel: NSUInteger(u.desc.sourceLevel())
2914 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2915 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2916 toTexture: dstD->d->tex
2917 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2918 destinationLevel: NSUInteger(u.desc.destinationLevel())
2919 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2921 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2924 readback.activeFrameSlot = currentFrameSlot;
2925 readback.desc = u.rb;
2926 readback.result = u.result;
2935 qWarning(
"Multisample texture cannot be read back");
2938 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2939 if (u.rb.rect().isValid())
2942 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
2943 readback.format = texD->m_format;
2945 texD->lastActiveFrameSlot = currentFrameSlot;
2949 if (u.rb.rect().isValid())
2952 rect = QRect({0, 0}, swapChainD->pixelSize);
2953 readback.format = swapChainD
->d->rhiColorFormat;
2957 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2959 readback.pixelSize = rect.size();
2962 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
2963 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2966 [blitEnc copyFromTexture: src
2967 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2968 sourceLevel: NSUInteger(u.rb.level())
2969 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
2970 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
2971 toBuffer: readback.buf
2972 destinationOffset: 0
2973 destinationBytesPerRow: bpl
2974 destinationBytesPerImage: 0
2975 options: MTLBlitOptionNone];
2977 d->activeTextureReadbacks.append(readback);
2981 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2982 utexD->lastActiveFrameSlot = currentFrameSlot;
2988 [blitEnc popDebugGroup];
2989 [blitEnc endEncoding];
2998 if (bufD
->d->pendingUpdates[slot].isEmpty())
3001 void *p = [bufD->d->buf[slot] contents];
3002 quint32 changeBegin = UINT32_MAX;
3003 quint32 changeEnd = 0;
3004 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3005 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3006 if (u.offset < changeBegin)
3007 changeBegin = u.offset;
3008 if (u.offset + u.data.size() > changeEnd)
3009 changeEnd = u.offset + u.data.size();
3012 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3013 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3016 bufD
->d->pendingUpdates[slot].clear();
3021 executeBufferHostWritesForSlot(bufD, bufD->d->slotted ? currentFrameSlot : 0);
3026 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3032 QRhiRenderTarget *rt,
3033 const QColor &colorClearValue,
3034 const QRhiDepthStencilClearValue &depthStencilClearValue,
3035 QRhiResourceUpdateBatch *resourceUpdates,
3041 if (resourceUpdates)
3045 switch (rt->resourceType()) {
3046 case QRhiResource::SwapChainRenderTarget:
3050 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3053 depthStencilClearValue,
3061 if (!swapChainD
->d->curDrawable) {
3062 QMacAutoReleasePool pool;
3063 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3065 if (!swapChainD
->d->curDrawable) {
3066 qWarning(
"No drawable");
3069 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3074 color0.resolveTex = scTex;
3080 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3083 case QRhiResource::TextureRenderTarget:
3087 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3091 depthStencilClearValue,
3093 rtTex->m_desc.shadingRateMap());
3094 if (rtD->fb.preserveColor) {
3095 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3096 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3099 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3100 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3102 int colorAttCount = 0;
3103 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3107 if (it->texture()) {
3108 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3109 if (it->multiViewCount() >= 2)
3110 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3111 }
else if (it->renderBuffer()) {
3112 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3114 if (it->resolveTexture())
3115 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3117 if (rtTex->m_desc.depthStencilBuffer())
3118 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3119 if (rtTex->m_desc.depthTexture()) {
3121 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3122 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3123 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3125 if (rtTex->m_desc.depthResolveTexture())
3126 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3127 if (rtTex->m_desc.shadingRateMap())
3128 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3137 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3138 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3139 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3140 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3141 if (rtD->fb.colorAtt[i].resolveTex) {
3142 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3143 : MTLStoreActionMultisampleResolve;
3144 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3145 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3146 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3151 Q_ASSERT(rtD->fb.dsTex);
3152 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3153 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3154 if (rtD->fb.depthNeedsStore)
3155 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3156 if (rtD->fb.dsResolveTex) {
3157 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3158 : MTLStoreActionMultisampleResolve;
3159 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3160 if (rtD->fb.hasStencil) {
3161 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3162 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3167 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3172 cbD->currentTarget = rt;
3180 [cbD->d->currentRenderPassEncoder endEncoding];
3183 cbD->currentTarget =
nullptr;
3185 if (resourceUpdates)
3190 QRhiResourceUpdateBatch *resourceUpdates,
3196 if (resourceUpdates)
3199 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3209 [cbD->d->currentComputePassEncoder endEncoding];
3212 if (resourceUpdates)
3225 cbD->currentPipelineGeneration = psD->generation;
3227 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3230 psD->lastActiveFrameSlot = currentFrameSlot;
3239 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3240 threadsPerThreadgroup: psD->d->localSize];
3245 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3246 [e.buffer.buffers[i] release];
3251 [e.renderbuffer.texture release];
3256 [e.texture.texture release];
3257 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3258 [e.texture.stagingBuffers[i] release];
3259 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3260 [e.texture.views[i] release];
3265 [e.sampler.samplerState release];
3270 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3272 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3286 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3287 [e.stagingBuffer.buffer release];
3289 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3290 [e.graphicsPipeline.pipelineState release];
3291 [e.graphicsPipeline.depthStencilState release];
3292 [e.graphicsPipeline.tessVertexComputeState[0] release];
3293 [e.graphicsPipeline.tessVertexComputeState[1] release];
3294 [e.graphicsPipeline.tessVertexComputeState[2] release];
3295 [e.graphicsPipeline.tessTessControlComputeState release];
3297 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3298 [e.computePipeline.pipelineState release];
3300 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3301 [e.shadingRateMap.rateMap release];
3306 d->releaseQueue.removeAt(i);
3313 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3315 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3317 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3318 readback.result->format = readback.format;
3319 readback.result->pixelSize = readback.pixelSize;
3320 readback.result->data.resize(
int(readback.bufSize));
3321 void *p = [readback.buf contents];
3322 memcpy(readback.result->data.data(), p, readback.bufSize);
3323 [readback.buf release];
3325 if (readback.result->completed)
3326 completedCallbacks.append(readback.result->completed);
3328 d->activeTextureReadbacks.remove(i);
3332 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3334 if (forced || currentFrameSlot == readback.activeFrameSlot
3335 || readback.activeFrameSlot < 0) {
3336 readback.result->data.resize(readback.readSize);
3337 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3339 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3341 if (readback.result->completed)
3342 completedCallbacks.append(readback.result->completed);
3344 d->activeBufferReadbacks.remove(i);
3348 for (
auto f : completedCallbacks)
3356 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3376 e.buffer.buffers[i] =
d->buf[i];
3378 d->pendingUpdates[i].clear();
3383 rhiD
->d->releaseQueue.append(e);
3384 rhiD->unregisterResource(
this);
3393 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3394 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3398 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3399 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3402 MTLResourceOptions opts = MTLResourceStorageModeShared;
3406 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3407 opts = MTLResourceStorageModeManaged;
3416 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3418 if (
int(m_usage) == WorkBufPoolUsage)
3423 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3424 if (!m_objectName.isEmpty()) {
3426 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3428 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3429 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3437 rhiD->registerResource(
this);
3449 b.objects[i] = &
d->buf[i];
3454 return { { &
d->buf[0] }, 1 };
3464 Q_ASSERT(m_type == Dynamic);
3466 Q_ASSERT(rhiD->inFrame);
3467 const int slot = rhiD->currentFrameSlot;
3468 void *p = [d->buf[slot] contents];
3469 return static_cast<
char *>(p);
3476 QRHI_RES_RHI(QRhiMetal);
3477 const int slot = rhiD->currentFrameSlot;
3478 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3489 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3491 case QRhiTexture::RGBA8:
3492 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3493 case QRhiTexture::BGRA8:
3494 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3495 case QRhiTexture::R8:
3497 return MTLPixelFormatR8Unorm;
3499 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3501 case QRhiTexture::R8SI:
3502 return MTLPixelFormatR8Sint;
3503 case QRhiTexture::R8UI:
3504 return MTLPixelFormatR8Uint;
3505 case QRhiTexture::RG8:
3507 return MTLPixelFormatRG8Unorm;
3509 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3511 case QRhiTexture::R16:
3512 return MTLPixelFormatR16Unorm;
3513 case QRhiTexture::RG16:
3514 return MTLPixelFormatRG16Unorm;
3515 case QRhiTexture::RED_OR_ALPHA8:
3516 return MTLPixelFormatR8Unorm;
3518 case QRhiTexture::RGBA16F:
3519 return MTLPixelFormatRGBA16Float;
3520 case QRhiTexture::RGBA32F:
3521 return MTLPixelFormatRGBA32Float;
3522 case QRhiTexture::R16F:
3523 return MTLPixelFormatR16Float;
3524 case QRhiTexture::R32F:
3525 return MTLPixelFormatR32Float;
3527 case QRhiTexture::RGB10A2:
3528 return MTLPixelFormatRGB10A2Unorm;
3530 case QRhiTexture::R32SI:
3531 return MTLPixelFormatR32Sint;
3532 case QRhiTexture::R32UI:
3533 return MTLPixelFormatR32Uint;
3534 case QRhiTexture::RG32SI:
3535 return MTLPixelFormatRG32Sint;
3536 case QRhiTexture::RG32UI:
3537 return MTLPixelFormatRG32Uint;
3538 case QRhiTexture::RGBA32SI:
3539 return MTLPixelFormatRGBA32Sint;
3540 case QRhiTexture::RGBA32UI:
3541 return MTLPixelFormatRGBA32Uint;
3544 case QRhiTexture::D16:
3545 return MTLPixelFormatDepth16Unorm;
3546 case QRhiTexture::D24:
3547 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3548 case QRhiTexture::D24S8:
3549 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3551 case QRhiTexture::D16:
3552 return MTLPixelFormatDepth32Float;
3553 case QRhiTexture::D24:
3554 return MTLPixelFormatDepth32Float;
3555 case QRhiTexture::D24S8:
3556 return MTLPixelFormatDepth32Float_Stencil8;
3558 case QRhiTexture::D32F:
3559 return MTLPixelFormatDepth32Float;
3560 case QRhiTexture::D32FS8:
3561 return MTLPixelFormatDepth32Float_Stencil8;
3564 case QRhiTexture::BC1:
3565 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3566 case QRhiTexture::BC2:
3567 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3568 case QRhiTexture::BC3:
3569 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3570 case QRhiTexture::BC4:
3571 return MTLPixelFormatBC4_RUnorm;
3572 case QRhiTexture::BC5:
3573 qWarning(
"QRhiMetal does not support BC5");
3574 return MTLPixelFormatInvalid;
3575 case QRhiTexture::BC6H:
3576 return MTLPixelFormatBC6H_RGBUfloat;
3577 case QRhiTexture::BC7:
3578 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3580 case QRhiTexture::BC1:
3581 case QRhiTexture::BC2:
3582 case QRhiTexture::BC3:
3583 case QRhiTexture::BC4:
3584 case QRhiTexture::BC5:
3585 case QRhiTexture::BC6H:
3586 case QRhiTexture::BC7:
3587 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3588 return MTLPixelFormatInvalid;
3592 case QRhiTexture::ETC2_RGB8:
3593 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3594 case QRhiTexture::ETC2_RGB8A1:
3595 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3596 case QRhiTexture::ETC2_RGBA8:
3597 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3599 case QRhiTexture::ASTC_4x4:
3600 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3601 case QRhiTexture::ASTC_5x4:
3602 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3603 case QRhiTexture::ASTC_5x5:
3604 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3605 case QRhiTexture::ASTC_6x5:
3606 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3607 case QRhiTexture::ASTC_6x6:
3608 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3609 case QRhiTexture::ASTC_8x5:
3610 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3611 case QRhiTexture::ASTC_8x6:
3612 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3613 case QRhiTexture::ASTC_8x8:
3614 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3615 case QRhiTexture::ASTC_10x5:
3616 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3617 case QRhiTexture::ASTC_10x6:
3618 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3619 case QRhiTexture::ASTC_10x8:
3620 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3621 case QRhiTexture::ASTC_10x10:
3622 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3623 case QRhiTexture::ASTC_12x10:
3624 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3625 case QRhiTexture::ASTC_12x12:
3626 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3628 case QRhiTexture::ETC2_RGB8:
3629 if (d->caps.isAppleGPU)
3630 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3631 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3632 return MTLPixelFormatInvalid;
3633 case QRhiTexture::ETC2_RGB8A1:
3634 if (d->caps.isAppleGPU)
3635 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3636 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3637 return MTLPixelFormatInvalid;
3638 case QRhiTexture::ETC2_RGBA8:
3639 if (d->caps.isAppleGPU)
3640 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3641 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3642 return MTLPixelFormatInvalid;
3643 case QRhiTexture::ASTC_4x4:
3644 if (d->caps.isAppleGPU)
3645 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3646 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3647 return MTLPixelFormatInvalid;
3648 case QRhiTexture::ASTC_5x4:
3649 if (d->caps.isAppleGPU)
3650 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3651 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3652 return MTLPixelFormatInvalid;
3653 case QRhiTexture::ASTC_5x5:
3654 if (d->caps.isAppleGPU)
3655 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3656 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3657 return MTLPixelFormatInvalid;
3658 case QRhiTexture::ASTC_6x5:
3659 if (d->caps.isAppleGPU)
3660 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3661 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3662 return MTLPixelFormatInvalid;
3663 case QRhiTexture::ASTC_6x6:
3664 if (d->caps.isAppleGPU)
3665 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3666 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3667 return MTLPixelFormatInvalid;
3668 case QRhiTexture::ASTC_8x5:
3669 if (d->caps.isAppleGPU)
3670 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3671 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3672 return MTLPixelFormatInvalid;
3673 case QRhiTexture::ASTC_8x6:
3674 if (d->caps.isAppleGPU)
3675 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3676 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3677 return MTLPixelFormatInvalid;
3678 case QRhiTexture::ASTC_8x8:
3679 if (d->caps.isAppleGPU)
3680 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3681 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3682 return MTLPixelFormatInvalid;
3683 case QRhiTexture::ASTC_10x5:
3684 if (d->caps.isAppleGPU)
3685 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3686 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3687 return MTLPixelFormatInvalid;
3688 case QRhiTexture::ASTC_10x6:
3689 if (d->caps.isAppleGPU)
3690 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3691 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3692 return MTLPixelFormatInvalid;
3693 case QRhiTexture::ASTC_10x8:
3694 if (d->caps.isAppleGPU)
3695 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3696 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3697 return MTLPixelFormatInvalid;
3698 case QRhiTexture::ASTC_10x10:
3699 if (d->caps.isAppleGPU)
3700 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3701 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3702 return MTLPixelFormatInvalid;
3703 case QRhiTexture::ASTC_12x10:
3704 if (d->caps.isAppleGPU)
3705 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3706 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3707 return MTLPixelFormatInvalid;
3708 case QRhiTexture::ASTC_12x12:
3709 if (d->caps.isAppleGPU)
3710 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3711 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3712 return MTLPixelFormatInvalid;
3717 return MTLPixelFormatInvalid;
3722 int sampleCount, QRhiRenderBuffer::Flags flags,
3723 QRhiTexture::Format backingFormatHint)
3744 e.renderbuffer.texture =
d->tex;
3749 rhiD
->d->releaseQueue.append(e);
3750 rhiD->unregisterResource(
this);
3759 if (m_pixelSize.isEmpty())
3763 samples = rhiD->effectiveSampleCount(m_sampleCount);
3765 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3766 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3767 desc.width = NSUInteger(m_pixelSize.width());
3768 desc.height = NSUInteger(m_pixelSize.height());
3770 desc.sampleCount = NSUInteger(
samples);
3771 desc.resourceOptions = MTLResourceStorageModePrivate;
3772 desc.usage = MTLTextureUsageRenderTarget;
3777 if (rhiD->caps.isAppleGPU) {
3778 desc.storageMode = MTLStorageModeMemoryless;
3779 d->format = MTLPixelFormatDepth32Float_Stencil8;
3781 desc.storageMode = MTLStorageModePrivate;
3782 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
3783 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3786 desc.storageMode = MTLStorageModeMemoryless;
3787 d->format = MTLPixelFormatDepth32Float_Stencil8;
3789 desc.pixelFormat =
d->format;
3792 desc.storageMode = MTLStorageModePrivate;
3793 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3794 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
3796 d->format = MTLPixelFormatRGBA8Unorm;
3797 desc.pixelFormat =
d->format;
3804 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3807 if (!m_objectName.isEmpty())
3808 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3812 rhiD->registerResource(
this);
3818 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3819 return m_backingFormatHint;
3821 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3825 int arraySize,
int sampleCount, Flags flags)
3829 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3830 d->stagingBuf[i] = nil;
3832 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3833 d->perLevelViews[i] = nil;
3851 e.texture.texture = d->owns ? d->tex : nil;
3855 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
3856 d->stagingBuf[i] = nil;
3859 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3860 e.texture.views[i] =
d->perLevelViews[i];
3861 d->perLevelViews[i] = nil;
3866 rhiD
->d->releaseQueue.append(e);
3867 rhiD->unregisterResource(
this);
3876 const bool isCube = m_flags.testFlag(CubeMap);
3877 const bool is3D = m_flags.testFlag(ThreeDimensional);
3878 const bool isArray = m_flags.testFlag(TextureArray);
3879 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3880 const bool is1D = m_flags.testFlag(OneDimensional);
3882 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3883 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3886 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
3887 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3888 samples = rhiD->effectiveSampleCount(m_sampleCount);
3891 qWarning(
"Cubemap texture cannot be multisample");
3895 qWarning(
"3D texture cannot be multisample");
3899 qWarning(
"Multisample texture cannot have mipmaps");
3903 if (isCube && is3D) {
3904 qWarning(
"Texture cannot be both cube and 3D");
3907 if (isArray && is3D) {
3908 qWarning(
"Texture cannot be both array and 3D");
3912 qWarning(
"Texture cannot be both 1D and 3D");
3915 if (is1D && isCube) {
3916 qWarning(
"Texture cannot be both 1D and cube");
3919 if (m_depth > 1 && !is3D) {
3920 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
3923 if (m_arraySize > 0 && !isArray) {
3924 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
3927 if (m_arraySize < 1 && isArray) {
3928 qWarning(
"Texture is an array but array size is %d", m_arraySize);
3933 *adjustedSize = size;
3941 if (!prepareCreate(&size))
3944 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3946 const bool isCube = m_flags.testFlag(CubeMap);
3947 const bool is3D = m_flags.testFlag(ThreeDimensional);
3948 const bool isArray = m_flags.testFlag(TextureArray);
3949 const bool is1D = m_flags.testFlag(OneDimensional);
3951 desc.textureType = MTLTextureTypeCube;
3953 desc.textureType = MTLTextureType3D;
3955 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
3956 }
else if (isArray) {
3957 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3959 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3961 desc.pixelFormat =
d->format;
3962 desc.width = NSUInteger(size.width());
3963 desc.height = NSUInteger(size.height());
3964 desc.depth = is3D ? qMax(1, m_depth) : 1;
3967 desc.sampleCount = NSUInteger(
samples);
3969 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
3970 desc.resourceOptions = MTLResourceStorageModePrivate;
3971 desc.storageMode = MTLStorageModePrivate;
3972 desc.usage = MTLTextureUsageShaderRead;
3973 if (m_flags.testFlag(RenderTarget))
3974 desc.usage |= MTLTextureUsageRenderTarget;
3975 if (m_flags.testFlag(UsedWithLoadStore))
3976 desc.usage |= MTLTextureUsageShaderWrite;
3979 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3982 if (!m_objectName.isEmpty())
3983 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3989 rhiD->registerResource(
this);
3995 id<MTLTexture> tex = id<MTLTexture>(src.object);
3999 if (!prepareCreate())
4009 rhiD->registerResource(
this);
4015 return {quint64(
d->tex), 0};
4021 if (perLevelViews[level])
4022 return perLevelViews[level];
4024 const MTLTextureType type = [tex textureType];
4025 const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
4026 const bool isArray = q->m_flags.testFlag(QRhiTexture::TextureArray);
4027 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4028 levels: NSMakeRange(NSUInteger(level), 1)
4029 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4031 perLevelViews[level] = view;
4036 AddressMode u, AddressMode v, AddressMode w)
4050 if (!
d->samplerState)
4057 e.sampler.samplerState =
d->samplerState;
4058 d->samplerState = nil;
4062 rhiD
->d->releaseQueue.append(e);
4063 rhiD->unregisterResource(
this);
4070 case QRhiSampler::Nearest:
4071 return MTLSamplerMinMagFilterNearest;
4072 case QRhiSampler::Linear:
4073 return MTLSamplerMinMagFilterLinear;
4076 return MTLSamplerMinMagFilterNearest;
4083 case QRhiSampler::None:
4084 return MTLSamplerMipFilterNotMipmapped;
4085 case QRhiSampler::Nearest:
4086 return MTLSamplerMipFilterNearest;
4087 case QRhiSampler::Linear:
4088 return MTLSamplerMipFilterLinear;
4091 return MTLSamplerMipFilterNotMipmapped;
4098 case QRhiSampler::Repeat:
4099 return MTLSamplerAddressModeRepeat;
4100 case QRhiSampler::ClampToEdge:
4101 return MTLSamplerAddressModeClampToEdge;
4102 case QRhiSampler::Mirror:
4103 return MTLSamplerAddressModeMirrorRepeat;
4106 return MTLSamplerAddressModeClampToEdge;
4113 case QRhiSampler::Never:
4114 return MTLCompareFunctionNever;
4115 case QRhiSampler::Less:
4116 return MTLCompareFunctionLess;
4117 case QRhiSampler::Equal:
4118 return MTLCompareFunctionEqual;
4119 case QRhiSampler::LessOrEqual:
4120 return MTLCompareFunctionLessEqual;
4121 case QRhiSampler::Greater:
4122 return MTLCompareFunctionGreater;
4123 case QRhiSampler::NotEqual:
4124 return MTLCompareFunctionNotEqual;
4125 case QRhiSampler::GreaterOrEqual:
4126 return MTLCompareFunctionGreaterEqual;
4127 case QRhiSampler::Always:
4128 return MTLCompareFunctionAlways;
4131 return MTLCompareFunctionNever;
4137 if (
d->samplerState)
4140 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4141 desc.minFilter = toMetalFilter(m_minFilter);
4142 desc.magFilter = toMetalFilter(m_magFilter);
4143 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4144 desc.sAddressMode = toMetalAddressMode(m_addressU);
4145 desc.tAddressMode = toMetalAddressMode(m_addressV);
4146 desc.rAddressMode = toMetalAddressMode(m_addressW);
4147 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4150 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4155 rhiD->registerResource(
this);
4180 e.shadingRateMap.rateMap =
d->rateMap;
4185 rhiD
->d->releaseQueue.append(e);
4186 rhiD->unregisterResource(
this);
4195 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4199 [d->rateMap retain];
4204 rhiD->registerResource(
this);
4213 serializedFormatData.reserve(16);
4225 rhiD->unregisterResource(
this);
4259 serializedFormatData.clear();
4260 auto p = std::back_inserter(serializedFormatData);
4282 rhiD->registerResource(rpD,
false);
4288 return serializedFormatData;
4310 return d->pixelSize;
4324 const QRhiTextureRenderTargetDescription &desc,
4341 rhiD->unregisterResource(
this);
4346 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4349 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4351 for (
int i = 0; i < colorAttachmentCount; ++i) {
4352 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4358 if (m_desc.depthTexture())
4359 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4360 else if (m_desc.depthStencilBuffer())
4361 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4363 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4368 rhiD->registerResource(rpD,
false);
4375 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4376 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4377 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4381 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4385 Q_ASSERT(texD || rbD);
4386 id<MTLTexture> dst = nil;
4390 if (attIndex == 0) {
4391 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4394 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4397 if (attIndex == 0) {
4398 d->pixelSize = rbD->pixelSize();
4405 colorAtt
.slice = is3D ? it->layer() : 0;
4406 colorAtt
.level = it->level();
4408 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4411 d->fb.colorAtt[attIndex] = colorAtt;
4415 if (hasDepthStencil) {
4416 if (m_desc.depthTexture()) {
4418 d->fb.dsTex = depthTexD
->d->tex;
4419 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4420 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4421 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4423 d->pixelSize = depthTexD->pixelSize();
4428 d->fb.dsTex = depthRbD
->d->tex;
4429 d->fb.hasStencil =
true;
4430 d->fb.depthNeedsStore =
false;
4431 d->fb.preserveDs =
false;
4433 d->pixelSize = depthRbD->pixelSize();
4437 if (m_desc.depthResolveTexture()) {
4439 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4446 if (d->colorAttCount > 0)
4447 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4449 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4451 rhiD->registerResource(
this,
false);
4457 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4460 return d->pixelSize;
4485 sortedBindings.clear();
4490 rhiD->unregisterResource(
this);
4495 if (!sortedBindings.isEmpty())
4499 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4502 rhiD->updateLayoutDesc(
this);
4504 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4505 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4506 if (!sortedBindings.isEmpty())
4507 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4511 boundResourceData.resize(sortedBindings.count());
4513 for (BoundResourceData &bd : boundResourceData)
4514 memset(&bd, 0,
sizeof(BoundResourceData));
4517 rhiD->registerResource(
this,
false);
4523 sortedBindings.clear();
4524 std::copy(m_bindings.cbegin(), m_bindings.cend(), std::back_inserter(sortedBindings));
4525 if (!flags.testFlag(BindingsAreSorted))
4526 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4528 for (BoundResourceData &bd : boundResourceData)
4529 memset(&bd, 0,
sizeof(BoundResourceData));
4553 d->tess.compVs[0].destroy();
4554 d->tess.compVs[1].destroy();
4555 d->tess.compVs[2].destroy();
4557 d->tess.compTesc.destroy();
4558 d->tess.vertTese.destroy();
4560 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4561 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4562 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4563 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4568 if (!
d->ps && !
d->ds
4569 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4570 && !
d->tess.tessControlComputeState)
4578 e.graphicsPipeline.pipelineState =
d->ps;
4579 e.graphicsPipeline.depthStencilState =
d->ds;
4580 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4581 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4584 d->tess.vertexComputeState = {};
4585 d->tess.tessControlComputeState = nil;
4589 rhiD
->d->releaseQueue.append(e);
4590 rhiD->unregisterResource(
this);
4597 case QRhiVertexInputAttribute::Float4:
4598 return MTLVertexFormatFloat4;
4599 case QRhiVertexInputAttribute::Float3:
4600 return MTLVertexFormatFloat3;
4601 case QRhiVertexInputAttribute::Float2:
4602 return MTLVertexFormatFloat2;
4603 case QRhiVertexInputAttribute::Float:
4604 return MTLVertexFormatFloat;
4605 case QRhiVertexInputAttribute::UNormByte4:
4606 return MTLVertexFormatUChar4Normalized;
4607 case QRhiVertexInputAttribute::UNormByte2:
4608 return MTLVertexFormatUChar2Normalized;
4609 case QRhiVertexInputAttribute::UNormByte:
4610 return MTLVertexFormatUCharNormalized;
4611 case QRhiVertexInputAttribute::UInt4:
4612 return MTLVertexFormatUInt4;
4613 case QRhiVertexInputAttribute::UInt3:
4614 return MTLVertexFormatUInt3;
4615 case QRhiVertexInputAttribute::UInt2:
4616 return MTLVertexFormatUInt2;
4617 case QRhiVertexInputAttribute::UInt:
4618 return MTLVertexFormatUInt;
4619 case QRhiVertexInputAttribute::SInt4:
4620 return MTLVertexFormatInt4;
4621 case QRhiVertexInputAttribute::SInt3:
4622 return MTLVertexFormatInt3;
4623 case QRhiVertexInputAttribute::SInt2:
4624 return MTLVertexFormatInt2;
4625 case QRhiVertexInputAttribute::SInt:
4626 return MTLVertexFormatInt;
4627 case QRhiVertexInputAttribute::Half4:
4628 return MTLVertexFormatHalf4;
4629 case QRhiVertexInputAttribute::Half3:
4630 return MTLVertexFormatHalf3;
4631 case QRhiVertexInputAttribute::Half2:
4632 return MTLVertexFormatHalf2;
4633 case QRhiVertexInputAttribute::Half:
4634 return MTLVertexFormatHalf;
4635 case QRhiVertexInputAttribute::UShort4:
4636 return MTLVertexFormatUShort4;
4637 case QRhiVertexInputAttribute::UShort3:
4638 return MTLVertexFormatUShort3;
4639 case QRhiVertexInputAttribute::UShort2:
4640 return MTLVertexFormatUShort2;
4641 case QRhiVertexInputAttribute::UShort:
4642 return MTLVertexFormatUShort;
4643 case QRhiVertexInputAttribute::SShort4:
4644 return MTLVertexFormatShort4;
4645 case QRhiVertexInputAttribute::SShort3:
4646 return MTLVertexFormatShort3;
4647 case QRhiVertexInputAttribute::SShort2:
4648 return MTLVertexFormatShort2;
4649 case QRhiVertexInputAttribute::SShort:
4650 return MTLVertexFormatShort;
4653 return MTLVertexFormatFloat4;
4660 case QRhiGraphicsPipeline::Zero:
4661 return MTLBlendFactorZero;
4662 case QRhiGraphicsPipeline::One:
4663 return MTLBlendFactorOne;
4664 case QRhiGraphicsPipeline::SrcColor:
4665 return MTLBlendFactorSourceColor;
4666 case QRhiGraphicsPipeline::OneMinusSrcColor:
4667 return MTLBlendFactorOneMinusSourceColor;
4668 case QRhiGraphicsPipeline::DstColor:
4669 return MTLBlendFactorDestinationColor;
4670 case QRhiGraphicsPipeline::OneMinusDstColor:
4671 return MTLBlendFactorOneMinusDestinationColor;
4672 case QRhiGraphicsPipeline::SrcAlpha:
4673 return MTLBlendFactorSourceAlpha;
4674 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4675 return MTLBlendFactorOneMinusSourceAlpha;
4676 case QRhiGraphicsPipeline::DstAlpha:
4677 return MTLBlendFactorDestinationAlpha;
4678 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4679 return MTLBlendFactorOneMinusDestinationAlpha;
4680 case QRhiGraphicsPipeline::ConstantColor:
4681 return MTLBlendFactorBlendColor;
4682 case QRhiGraphicsPipeline::ConstantAlpha:
4683 return MTLBlendFactorBlendAlpha;
4684 case QRhiGraphicsPipeline::OneMinusConstantColor:
4685 return MTLBlendFactorOneMinusBlendColor;
4686 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4687 return MTLBlendFactorOneMinusBlendAlpha;
4688 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4689 return MTLBlendFactorSourceAlphaSaturated;
4690 case QRhiGraphicsPipeline::Src1Color:
4691 return MTLBlendFactorSource1Color;
4692 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4693 return MTLBlendFactorOneMinusSource1Color;
4694 case QRhiGraphicsPipeline::Src1Alpha:
4695 return MTLBlendFactorSource1Alpha;
4696 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4697 return MTLBlendFactorOneMinusSource1Alpha;
4700 return MTLBlendFactorZero;
4707 case QRhiGraphicsPipeline::Add:
4708 return MTLBlendOperationAdd;
4709 case QRhiGraphicsPipeline::Subtract:
4710 return MTLBlendOperationSubtract;
4711 case QRhiGraphicsPipeline::ReverseSubtract:
4712 return MTLBlendOperationReverseSubtract;
4713 case QRhiGraphicsPipeline::Min:
4714 return MTLBlendOperationMin;
4715 case QRhiGraphicsPipeline::Max:
4716 return MTLBlendOperationMax;
4719 return MTLBlendOperationAdd;
4726 if (c.testFlag(QRhiGraphicsPipeline::R))
4727 f |= MTLColorWriteMaskRed;
4728 if (c.testFlag(QRhiGraphicsPipeline::G))
4729 f |= MTLColorWriteMaskGreen;
4730 if (c.testFlag(QRhiGraphicsPipeline::B))
4731 f |= MTLColorWriteMaskBlue;
4732 if (c.testFlag(QRhiGraphicsPipeline::A))
4733 f |= MTLColorWriteMaskAlpha;
4740 case QRhiGraphicsPipeline::Never:
4741 return MTLCompareFunctionNever;
4742 case QRhiGraphicsPipeline::Less:
4743 return MTLCompareFunctionLess;
4744 case QRhiGraphicsPipeline::Equal:
4745 return MTLCompareFunctionEqual;
4746 case QRhiGraphicsPipeline::LessOrEqual:
4747 return MTLCompareFunctionLessEqual;
4748 case QRhiGraphicsPipeline::Greater:
4749 return MTLCompareFunctionGreater;
4750 case QRhiGraphicsPipeline::NotEqual:
4751 return MTLCompareFunctionNotEqual;
4752 case QRhiGraphicsPipeline::GreaterOrEqual:
4753 return MTLCompareFunctionGreaterEqual;
4754 case QRhiGraphicsPipeline::Always:
4755 return MTLCompareFunctionAlways;
4758 return MTLCompareFunctionAlways;
4765 case QRhiGraphicsPipeline::StencilZero:
4766 return MTLStencilOperationZero;
4767 case QRhiGraphicsPipeline::Keep:
4768 return MTLStencilOperationKeep;
4769 case QRhiGraphicsPipeline::Replace:
4770 return MTLStencilOperationReplace;
4771 case QRhiGraphicsPipeline::IncrementAndClamp:
4772 return MTLStencilOperationIncrementClamp;
4773 case QRhiGraphicsPipeline::DecrementAndClamp:
4774 return MTLStencilOperationDecrementClamp;
4775 case QRhiGraphicsPipeline::Invert:
4776 return MTLStencilOperationInvert;
4777 case QRhiGraphicsPipeline::IncrementAndWrap:
4778 return MTLStencilOperationIncrementWrap;
4779 case QRhiGraphicsPipeline::DecrementAndWrap:
4780 return MTLStencilOperationDecrementWrap;
4783 return MTLStencilOperationKeep;
4790 case QRhiGraphicsPipeline::Triangles:
4791 return MTLPrimitiveTypeTriangle;
4792 case QRhiGraphicsPipeline::TriangleStrip:
4793 return MTLPrimitiveTypeTriangleStrip;
4794 case QRhiGraphicsPipeline::Lines:
4795 return MTLPrimitiveTypeLine;
4796 case QRhiGraphicsPipeline::LineStrip:
4797 return MTLPrimitiveTypeLineStrip;
4798 case QRhiGraphicsPipeline::Points:
4799 return MTLPrimitiveTypePoint;
4802 return MTLPrimitiveTypeTriangle;
4809 case QRhiGraphicsPipeline::Triangles:
4810 case QRhiGraphicsPipeline::TriangleStrip:
4811 case QRhiGraphicsPipeline::TriangleFan:
4812 return MTLPrimitiveTopologyClassTriangle;
4813 case QRhiGraphicsPipeline::Lines:
4814 case QRhiGraphicsPipeline::LineStrip:
4815 return MTLPrimitiveTopologyClassLine;
4816 case QRhiGraphicsPipeline::Points:
4817 return MTLPrimitiveTopologyClassPoint;
4820 return MTLPrimitiveTopologyClassTriangle;
4827 case QRhiGraphicsPipeline::None:
4828 return MTLCullModeNone;
4829 case QRhiGraphicsPipeline::Front:
4830 return MTLCullModeFront;
4831 case QRhiGraphicsPipeline::Back:
4832 return MTLCullModeBack;
4835 return MTLCullModeNone;
4842 case QRhiGraphicsPipeline::Fill:
4843 return MTLTriangleFillModeFill;
4844 case QRhiGraphicsPipeline::Line:
4845 return MTLTriangleFillModeLines;
4848 return MTLTriangleFillModeFill;
4855 case QShaderDescription::CwTessellationWindingOrder:
4856 return MTLWindingClockwise;
4857 case QShaderDescription::CcwTessellationWindingOrder:
4858 return MTLWindingCounterClockwise;
4861 return MTLWindingCounterClockwise;
4868 case QShaderDescription::EqualTessellationPartitioning:
4869 return MTLTessellationPartitionModePow2;
4870 case QShaderDescription::FractionalEvenTessellationPartitioning:
4871 return MTLTessellationPartitionModeFractionalEven;
4872 case QShaderDescription::FractionalOddTessellationPartitioning:
4873 return MTLTessellationPartitionModeFractionalOdd;
4876 return MTLTessellationPartitionModePow2;
4882 int v = version.version();
4883 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
4887 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4889 QVarLengthArray<
int, 8> versions;
4890 if (@available(macOS 13, iOS 16, *))
4892 if (@available(macOS 12, iOS 15, *))
4894 versions << 23 << 22 << 21 << 20 << 12;
4896 const QList<QShaderKey> shaders = shader.availableShaders();
4900 for (
const int &version : versions) {
4901 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4902 if (shaders.contains(key))
4906 QShaderCode mtllib = shader.shader(key);
4907 if (!mtllib.shader().isEmpty()) {
4908 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4909 size_t(mtllib.shader().size()),
4910 dispatch_get_global_queue(0, 0),
4911 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4913 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4914 dispatch_release(data);
4916 *entryPoint = mtllib.entryPoint();
4920 const QString msg = QString::fromNSString(err.localizedDescription);
4921 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
4925 for (
const int &version : versions) {
4926 key = { QShader::Source::MslShader, version, shaderVariant };
4927 if (shaders.contains(key))
4931 QShaderCode mslSource = shader.shader(key);
4932 if (mslSource.shader().isEmpty()) {
4933 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
4937 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4938 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4939 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4941 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4949 const QString msg = QString::fromNSString(err.localizedDescription);
4954 *entryPoint = mslSource.entryPoint();
4961 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
4966 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
4970 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
4971 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
4972 rpDesc.colorAttachments[0].blendingEnabled =
false;
4974 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
4975 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
4977 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
4978 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
4979 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
4980 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
4981 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
4982 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
4983 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
4984 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
4985 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
4986 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
4987 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
4994 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
4995 rpDesc.depthAttachmentPixelFormat = fmt;
4996#if defined(Q_OS_MACOS)
4997 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
4999 if (fmt != MTLPixelFormatDepth32Float)
5001 rpDesc.stencilAttachmentPixelFormat = fmt;
5005 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5010 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5012 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5013 dsDesc.depthWriteEnabled = m_depthWrite;
5014 if (m_stencilTest) {
5015 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5016 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5017 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5018 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5019 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5020 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5021 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5023 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5024 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5025 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5026 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5027 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5028 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5029 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5035 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5036 d->cullMode = toMetalCullMode(m_cullMode);
5037 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5038 d->depthBias =
float(m_depthBias);
5039 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5049 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5052 const uint loc = uint(it->location());
5053 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5054 desc.attributes[loc].offset = NSUInteger(it->offset());
5055 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5057 int bindingIndex = 0;
5058 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5059 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5060 it != itEnd; ++it, ++bindingIndex)
5062 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5063 desc.layouts[layoutIdx].stepFunction =
5064 it->classification() == QRhiVertexInputBinding::PerInstance
5065 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5066 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5067 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5068 desc.layouts[layoutIdx].stepRate *= viewCount;
5069 desc.layouts[layoutIdx].stride = it->stride();
5080 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5083 const uint loc = uint(it->location());
5084 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5085 desc.attributes[loc].offset = NSUInteger(it->offset());
5086 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5088 int bindingIndex = 0;
5089 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5090 it != itEnd; ++it, ++bindingIndex)
5092 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5093 if (desc.indexBufferIndex) {
5094 desc.layouts[layoutIdx].stepFunction =
5095 it->classification() == QRhiVertexInputBinding::PerInstance
5096 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5098 desc.layouts[layoutIdx].stepFunction =
5099 it->classification() == QRhiVertexInputBinding::PerInstance
5100 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5102 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5103 desc.layouts[layoutIdx].stride = it->stride();
5110 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5111 rpDesc.binaryArchives = binArchArray;
5119 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5120 const QString msg = QString::fromNSString(err.localizedDescription);
5121 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5130 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5131 d->setupVertexInputDescriptor(vertexDesc);
5133 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5134 rpDesc.vertexDescriptor = vertexDesc;
5142 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5143 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5144 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5145 switch (shaderStage.type()) {
5146 case QRhiShaderStage::Vertex:
5149 [d->vs.func retain];
5150 rpDesc.vertexFunction = d->vs.func;
5152 case QRhiShaderStage::Fragment:
5155 [d->fs.func retain];
5156 rpDesc.fragmentFunction = d->fs.func;
5162 const QShader shader = shaderStage.shader();
5164 QByteArray entryPoint;
5165 QShaderKey activeKey;
5166 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5167 &error, &entryPoint, &activeKey);
5169 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5172 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5174 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5178 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5180 for (QMetalShader &s : rhiD->d->shaderCache)
5182 rhiD->d->shaderCache.clear();
5184 switch (shaderStage.type()) {
5185 case QRhiShaderStage::Vertex:
5188 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5189 d->vs.desc = shader.description();
5190 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5191 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5193 [d->vs.func retain];
5194 rpDesc.vertexFunction = func;
5196 case QRhiShaderStage::Fragment:
5199 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5200 d->fs.desc = shader.description();
5201 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5202 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5204 [d->fs.func retain];
5205 rpDesc.fragmentFunction = func;
5218 if (m_multiViewCount >= 2)
5219 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5221 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5223 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5224 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5227 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5230 const QString msg = QString::fromNSString(err.localizedDescription);
5231 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5235 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5237 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5240 d->primitiveType = toMetalPrimitiveType(m_topology);
5248 switch (vertexCompVariant) {
5249 case QShader::NonIndexedVertexAsComputeShader:
5251 case QShader::UInt32IndexedVertexAsComputeShader:
5253 case QShader::UInt16IndexedVertexAsComputeShader:
5263 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5264 if (varIndex >= 0 && vertexComputeState[varIndex])
5265 return vertexComputeState[varIndex];
5267 id<MTLFunction> func = nil;
5269 func = compVs[varIndex].func;
5272 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5276 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5277 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5279 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5280 cpDesc.computeFunction = func;
5281 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5282 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5283 if (indexBufferBinding >= 0) {
5284 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5285 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5286 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5287 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5288 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5289 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5292 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5294 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5296 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5297 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5300 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5301 options: MTLPipelineOptionNone
5306 const QString msg = QString::fromNSString(err.localizedDescription);
5307 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5309 vertexComputeState[varIndex] = ps;
5317 if (tessControlComputeState)
5318 return tessControlComputeState;
5320 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5321 cpDesc.computeFunction = compTesc.func;
5323 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5325 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5326 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5329 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5330 options: MTLPipelineOptionNone
5335 const QString msg = QString::fromNSString(err.localizedDescription);
5336 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5338 tessControlComputeState = ps;
5346 return (indices >> index) & 0x1;
5349static inline void takeIndex(quint32 index, quint64 &indices)
5351 indices |= 1 << index;
5360 static const int maxVertexAttributes = 31;
5362 for (
int index = 0; index < maxVertexAttributes; ++index) {
5363 if (!indexTaken(index, indices))
5367 Q_UNREACHABLE_RETURN(-1);
5370static inline int aligned(quint32 offset, quint32 alignment)
5372 return ((offset + alignment - 1) / alignment) * alignment;
5380 for (
const int dim : variable.arrayDims)
5383 if (variable.type == QShaderDescription::VariableType::Struct) {
5384 for (
int element = 0; element < elements; ++element) {
5385 for (
const auto &member : variable.structMembers) {
5386 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5390 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5391 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5394 const quint32 alignment = size;
5395 vertexAlignment =
std::max(vertexAlignment, alignment);
5397 for (
int element = 0; element < elements; ++element) {
5399 offset = aligned(offset, alignment);
5406static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5410 for (
const int dim : variable.arrayDims)
5413 if (variable.type == QShaderDescription::VariableType::Struct) {
5414 for (
int element = 0; element < elements; ++element) {
5415 for (
const auto &member : variable.structMembers) {
5416 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5420 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5421 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5424 const quint32 alignment = size;
5425 vertexAlignment =
std::max(vertexAlignment, alignment);
5427 for (
int element = 0; element < elements; ++element) {
5428 Q_ASSERT(!indexTaken(index, indices));
5431 offset = aligned(offset, alignment);
5433 attributes[index].bufferIndex = binding;
5434 attributes[index].format = toMetalAttributeFormat(format);
5435 attributes[index].offset = offset;
5437 takeIndex(index, indices);
5439 if (indexTaken(index, indices))
5440 index = nextAttributeIndex(indices);
5447static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5449 if (a.size() == b.size()) {
5451 for (
int i = 0; i < a.size() && match; ++i) {
5452 match &= a[i].type == b[i].type
5453 && a[i].arrayDims == b[i].arrayDims
5454 && matches(a[i].structMembers, b[i].structMembers);
5462static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5464 return a.location == b.location
5466 && a.perPatch == b.perPatch
5467 && matches(a.structMembers, b.structMembers);
5516 if (pipeline
->d->ps)
5517 return pipeline
->d->ps;
5519 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5520 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5523 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5524 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5525 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5526 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5527 quint32 offsetInTescOutput = 0;
5528 quint32 offsetInTescPatchOutput = 0;
5529 quint32 offsetInTessFactorBuffer = 0;
5530 quint32 tescOutputAlignment = 0;
5531 quint32 tescPatchOutputAlignment = 0;
5532 quint32 tessFactorAlignment = 0;
5533 QSet<
int> usedBuffers;
5536 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5537 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5538 tescOutVars[tescOutVar.location] = tescOutVar;
5541 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5542 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5543 teseInVars[teseInVar.location] = teseInVar;
5546 quint64 indices = 0;
5548 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5550 int index = tescOutVar.location;
5552 quint32 *offset =
nullptr;
5553 quint32 *alignment =
nullptr;
5555 if (tescOutVar.perPatch) {
5556 binding = tescPatchOutputBufferBinding;
5557 offset = &offsetInTescPatchOutput;
5558 alignment = &tescPatchOutputAlignment;
5560 tescOutVar.arrayDims.removeLast();
5561 binding = tescOutputBufferBinding;
5562 offset = &offsetInTescOutput;
5563 alignment = &tescOutputAlignment;
5566 if (teseInVars.contains(index)) {
5568 if (!matches(teseInVars[index], tescOutVar)) {
5569 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5570 qWarning() <<
" tesc out:" << tescOutVar;
5571 qWarning() <<
" tese in:" << teseInVars[index];
5574 if (binding != -1) {
5575 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5576 usedBuffers << binding;
5578 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5579 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5583 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5584 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5587 teseInVars.remove(tescOutVar.location);
5590 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5591 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5594 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5595 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5596 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5599 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5600 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5601 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5603 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5604 bool tessLevelAdded =
false;
5606 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5608 QShaderDescription::InOutVariable variable;
5610 quint32 *offset =
nullptr;
5611 quint32 *alignment =
nullptr;
5613 switch (builtin.type) {
5614 case QShaderDescription::BuiltinType::PositionBuiltin:
5615 variable.type = QShaderDescription::VariableType::Vec4;
5616 binding = tescOutputBufferBinding;
5617 offset = &offsetInTescOutput;
5618 alignment = &tescOutputAlignment;
5620 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5621 variable.type = QShaderDescription::VariableType::Float;
5622 binding = tescOutputBufferBinding;
5623 offset = &offsetInTescOutput;
5624 alignment = &tescOutputAlignment;
5626 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5627 variable.type = QShaderDescription::VariableType::Float;
5628 variable.arrayDims = builtin.arrayDims;
5629 binding = tescOutputBufferBinding;
5630 offset = &offsetInTescOutput;
5631 alignment = &tescOutputAlignment;
5633 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5634 variable.type = QShaderDescription::VariableType::Half4;
5635 binding = tessFactorBufferBinding;
5636 offset = &offsetInTessFactorBuffer;
5637 tessLevelAdded = trianglesMode;
5638 alignment = &tessFactorAlignment;
5640 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5641 if (trianglesMode) {
5642 if (!tessLevelAdded) {
5643 variable.type = QShaderDescription::VariableType::Half4;
5644 binding = tessFactorBufferBinding;
5645 offsetInTessFactorBuffer = 0;
5646 offset = &offsetInTessFactorBuffer;
5647 alignment = &tessFactorAlignment;
5648 tessLevelAdded =
true;
5650 teseInBuiltins.remove(builtin.type);
5654 variable.type = QShaderDescription::VariableType::Half2;
5655 binding = tessFactorBufferBinding;
5656 offsetInTessFactorBuffer = 8;
5657 offset = &offsetInTessFactorBuffer;
5658 alignment = &tessFactorAlignment;
5666 if (teseInBuiltins.contains(builtin.type)) {
5667 if (binding != -1) {
5668 int index = nextAttributeIndex(indices);
5669 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5670 usedBuffers << binding;
5672 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5673 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5676 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5679 teseInBuiltins.remove(builtin.type);
5682 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5683 switch (builtin.type) {
5684 case QShaderDescription::BuiltinType::PositionBuiltin:
5685 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5686 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5687 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5694 if (usedBuffers.contains(tescOutputBufferBinding)) {
5695 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5696 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5699 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5700 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5701 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5704 if (usedBuffers.contains(tessFactorBufferBinding)) {
5705 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5706 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
5709 rpDesc.vertexDescriptor = vertexDesc;
5710 rpDesc.vertexFunction = vertTese.func;
5711 rpDesc.fragmentFunction = pipeline
->d->fs.func;
5717 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5719 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5724 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5726 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5727 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5730 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5733 const QString msg = QString::fromNSString(err.localizedDescription);
5734 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5738 pipeline->d->ps = ps;
5745 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
5748 for (QMetalBuffer *workBuf : *workBuffers) {
5749 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
5750 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5758 for (QMetalBuffer *workBuf : *workBuffers) {
5759 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
5760 workBuf->setSize(size);
5761 if (workBuf->create()) {
5762 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5773 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5776 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5780 workBuffers->append(buf);
5784 qWarning(
"Failed to acquire work buffer of size %u", size);
5792 QByteArray entryPoint;
5793 QShaderKey activeKey;
5795 const QShaderDescription tescDesc = tesc.description();
5796 const QShaderDescription teseDesc = tese.description();
5797 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5798 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5799 if (!
d->tess.outControlPointCount)
5800 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5802 if (!
d->tess.outControlPointCount) {
5803 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5804 d->tess.enabled =
false;
5805 d->tess.failed =
true;
5809 if (m_multiViewCount >= 2)
5810 qWarning(
"Multiview is not supported with tessellation");
5818 bool variantsPresent[3] = {};
5819 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5820 for (
const QShaderKey &k : tessVertKeys) {
5821 switch (k.sourceVariant()) {
5822 case QShader::NonIndexedVertexAsComputeShader:
5823 variantsPresent[0] =
true;
5825 case QShader::UInt32IndexedVertexAsComputeShader:
5826 variantsPresent[1] =
true;
5828 case QShader::UInt16IndexedVertexAsComputeShader:
5829 variantsPresent[2] =
true;
5835 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5836 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5837 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5838 "Try passing --msltess to qsb.");
5839 d->tess.enabled =
false;
5840 d->tess.failed =
true;
5845 for (QShader::Variant variant : {
5846 QShader::NonIndexedVertexAsComputeShader,
5847 QShader::UInt32IndexedVertexAsComputeShader,
5848 QShader::UInt16IndexedVertexAsComputeShader })
5850 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5852 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
5853 d->tess.enabled =
false;
5854 d->tess.failed =
true;
5857 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5859 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5861 d->tess.enabled =
false;
5862 d->tess.failed =
true;
5865 QMetalShader &compVs(d->tess.compVs[varIndex]);
5868 compVs.desc = tessVert.description();
5869 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5870 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5873 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5874 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
5875 d->tess.enabled =
false;
5876 d->tess.failed =
true;
5884 id<MTLLibrary> tessControlLib = rhiD->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5885 if (!tessControlLib) {
5886 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5887 d->tess.enabled =
false;
5888 d->tess.failed =
true;
5891 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
5892 if (!tessControlFunc) {
5893 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5894 [tessControlLib release];
5895 d->tess.enabled =
false;
5896 d->tess.failed =
true;
5899 d->tess.compTesc.lib = tessControlLib;
5900 d->tess.compTesc.func = tessControlFunc;
5901 d->tess.compTesc.desc = tesc.description();
5902 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5903 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5904 if (!
d->tess.tescCompPipeline(rhiD)) {
5905 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
5906 d->tess.enabled =
false;
5907 d->tess.failed =
true;
5912 id<MTLLibrary> tessEvalLib = rhiD->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5914 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5915 d->tess.enabled =
false;
5916 d->tess.failed =
true;
5919 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5920 if (!tessEvalFunc) {
5921 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5922 [tessEvalLib release];
5923 d->tess.enabled =
false;
5924 d->tess.failed =
true;
5927 d->tess.vertTese.lib = tessEvalLib;
5928 d->tess.vertTese.func = tessEvalFunc;
5929 d->tess.vertTese.desc = tese.description();
5930 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5931 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5933 id<MTLLibrary> fragLib = rhiD->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5935 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5936 d->tess.enabled =
false;
5937 d->tess.failed =
true;
5940 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
5942 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5944 d->tess.enabled =
false;
5945 d->tess.failed =
true;
5948 d->fs.lib = fragLib;
5949 d->fs.func = fragFunc;
5950 d->fs.desc = tessFrag.description();
5951 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5952 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5954 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
5955 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5956 d->tess.enabled =
false;
5957 d->tess.failed =
true;
5961 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5963 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5977 rhiD->pipelineCreationStart();
5978 if (!rhiD->sanityCheckGraphicsPipeline(
this))
5986 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5987 switch (shaderStage.type()) {
5988 case QRhiShaderStage::Vertex:
5989 tessVert = shaderStage.shader();
5991 case QRhiShaderStage::TessellationControl:
5992 tesc = shaderStage.shader();
5994 case QRhiShaderStage::TessellationEvaluation:
5995 tese = shaderStage.shader();
5997 case QRhiShaderStage::Fragment:
5998 tessFrag = shaderStage.shader();
6004 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6005 d->tess.failed =
false;
6007 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6013 QVarLengthArray<QMetalShader *, 6> shaders;
6014 if (
d->tess.enabled) {
6015 shaders.append(&
d->tess.compVs[0]);
6016 shaders.append(&
d->tess.compVs[1]);
6017 shaders.append(&
d->tess.compVs[2]);
6018 shaders.append(&
d->tess.compTesc);
6019 shaders.append(&
d->tess.vertTese);
6021 shaders.append(&
d->vs);
6023 shaders.append(&
d->fs);
6025 for (QMetalShader *shader : shaders) {
6026 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6027 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6028 shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6029 int maxNativeBinding = 0;
6030 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6031 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6035 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6040 if (!d->bufferSizeBuffer)
6041 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6047 rhiD->pipelineCreationEnd();
6050 rhiD->registerResource(
this);
6079 e.computePipeline.pipelineState =
d->ps;
6084 rhiD
->d->releaseQueue.append(e);
6085 rhiD->unregisterResource(
this);
6092 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6093 cpDesc.binaryArchives = binArchArray;
6101 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6102 const QString msg = QString::fromNSString(err.localizedDescription);
6103 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6114 rhiD->pipelineCreationStart();
6116 auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
6117 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6120 const QShader shader = m_shaderStage.shader();
6122 QByteArray entryPoint;
6123 QShaderKey activeKey;
6124 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6125 &error, &entryPoint, &activeKey);
6127 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6130 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6132 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6138 d->cs.localSize = shader.description().computeShaderLocalSize();
6139 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6140 d->cs.desc = shader.description();
6141 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6144 if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6145 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6146 d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6149 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6150 for (QMetalShader &s : rhiD->d->shaderCache)
6152 rhiD
->d->shaderCache.clear();
6154 rhiD->d->shaderCache.insert(m_shaderStage, d->cs);
6158 [d->cs.func retain];
6160 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6162 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6163 cpDesc.computeFunction =
d->cs.func;
6165 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6167 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6168 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6171 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6172 options: MTLPipelineOptionNone
6177 const QString msg = QString::fromNSString(err.localizedDescription);
6178 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6183 if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6185 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6186 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6190 if (!d->bufferSizeBuffer)
6191 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6197 rhiD->pipelineCreationEnd();
6200 rhiD->registerResource(
this);
6224 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6225 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6226 return &nativeHandlesStruct;
6232 d->currentRenderPassEncoder = nil;
6233 d->currentComputePassEncoder = nil;
6234 d->tessellationComputeEncoder = nil;
6235 d->currentPassRpDesc = nil;
6242 currentTarget =
nullptr;
6250 currentPipelineGeneration = 0;
6253 currentSrbGeneration = 0;
6256 currentIndexOffset = 0;
6257 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6261 currentDepthBiasValues = { 0.0f, 0.0f };
6263 d->currentShaderResourceBindingState = {};
6264 d->currentDepthStencilState = nil;
6266 d->currentVertexInputsBuffers.clear();
6267 d->currentVertexInputOffsets.clear();
6277 d->sem[i] =
nullptr;
6278 d->msaaTex[i] = nil;
6298 dispatch_release(
d->sem[i]);
6299 d->sem[i] =
nullptr;
6304 [d->msaaTex[i] release];
6305 d->msaaTex[i] = nil;
6311 [d->curDrawable release];
6312 d->curDrawable = nil;
6316 rhiD->swapchains.remove(
this);
6317 rhiD->unregisterResource(
this);
6337 CALayer *layer =
nullptr;
6339 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6340 layer = cocoaWindow->contentLayer();
6342 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6345 return static_cast<CAMetalLayer *>(layer);
6354 d.reserved[0] = layerForWindow(window);
6361 CAMetalLayer *layer =
d->layer;
6363 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6366 int height = (
int)layer.bounds.size.height;
6367 int width = (
int)layer.bounds.size.width;
6368 width *= layer.contentsScale;
6369 height *= layer.contentsScale;
6370 return QSize(width, height);
6375 if (f == HDRExtendedSrgbLinear) {
6376 if (@available(iOS 16.0, *))
6380 }
else if (f == HDR10) {
6381 if (@available(iOS 16.0, *))
6385 }
else if (f == HDRExtendedDisplayP3Linear) {
6399 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6405 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6406 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6408 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6411 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6415 rhiD->registerResource(rpD,
false);
6422 samples = rhiD->effectiveSampleCount(m_sampleCount);
6424 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6425 d->colorFormat = MTLPixelFormatRGBA16Float;
6426 d->rhiColorFormat = QRhiTexture::RGBA16F;
6429 if (m_format == HDR10) {
6430 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6431 d->rhiColorFormat = QRhiTexture::RGB10A2;
6434 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6435 d->rhiColorFormat = QRhiTexture::BGRA8;
6444 dispatch_semaphore_t sem =
d->sem[slot];
6445 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6446 dispatch_semaphore_signal(sem);
6453 const bool needsRegistration = !window || window != m_window;
6455 if (window && window != m_window)
6460 if (needsRegistration || !rhiD->swapchains.contains(
this))
6461 rhiD->swapchains.insert(
this);
6465 if (window->surfaceType() != QSurface::MetalSurface) {
6466 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6470 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6474 if (
d->colorFormat !=
d->layer.pixelFormat)
6475 d->layer.pixelFormat =
d->colorFormat;
6477 if (m_format == HDRExtendedSrgbLinear) {
6478 if (@available(iOS 16.0, *)) {
6479 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6480 d->layer.wantsExtendedDynamicRangeContent = YES;
6482 }
else if (m_format == HDR10) {
6483 if (@available(iOS 16.0, *)) {
6484 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6485 d->layer.wantsExtendedDynamicRangeContent = YES;
6487 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6488 if (@available(iOS 16.0, *)) {
6489 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6490 d->layer.wantsExtendedDynamicRangeContent = YES;
6494 if (m_flags.testFlag(UsedAsTransferSource))
6495 d->layer.framebufferOnly = NO;
6498 if (m_flags.testFlag(NoVSync))
6499 d->layer.displaySyncEnabled = NO;
6502 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6503 d->layer.opaque = NO;
6504 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6509 d->layer.opaque = NO;
6511 d->layer.opaque = YES;
6517 int width = (
int)
d->layer.bounds.size.width;
6518 int height = (
int)
d->layer.bounds.size.height;
6519 CGSize layerSize = CGSizeMake(width, height);
6520 const float scaleFactor =
d->layer.contentsScale;
6521 layerSize.width *= scaleFactor;
6522 layerSize.height *= scaleFactor;
6523 d->layer.drawableSize = layerSize;
6525 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6526 pixelSize = m_currentPixelSize;
6528 [d->layer setDevice: rhiD->d->dev];
6530 [d->curDrawable release];
6531 d->curDrawable = nil;
6542 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6543 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6544 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6545 m_depthStencil->sampleCount(), m_sampleCount);
6547 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6548 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6549 m_depthStencil->setPixelSize(pixelSize);
6550 if (!m_depthStencil->create())
6551 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6552 pixelSize.width(), pixelSize.height());
6554 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6555 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6556 pixelSize.width(), pixelSize.height());
6560 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6561 rtWrapper.d->pixelSize = pixelSize;
6567 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6568 pixelSize.width(), pixelSize.height(), scaleFactor);
6571 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6572 desc.textureType = MTLTextureType2DMultisample;
6573 desc.pixelFormat =
d->colorFormat;
6574 desc.width = NSUInteger(pixelSize.width());
6575 desc.height = NSUInteger(pixelSize.height());
6576 desc.sampleCount = NSUInteger(
samples);
6577 desc.resourceOptions = MTLResourceStorageModePrivate;
6578 desc.storageMode = MTLStorageModePrivate;
6579 desc.usage = MTLTextureUsageRenderTarget;
6581 if (
d->msaaTex[i]) {
6585 e.renderbuffer.texture =
d->msaaTex[i];
6586 rhiD
->d->releaseQueue.append(e);
6588 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6593 rhiD->registerResource(
this);
6609#if defined(Q_OS_MACOS)
6610 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6611 NSScreen *screen = view.window.screen;
6612 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6613 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6614#elif defined(Q_OS_IOS)
6615 if (@available(iOS 16.0, *)) {
6616 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6617 UIScreen *screen = view.window.windowScene.screen;
6618 info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
6619 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