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>
34
35
36
37
38
39
40
41
42
45#error ARC not supported
54#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
59#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
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
97
98
102
103
104
105
106
107
108
109
112
113
114
115
118
119
120
121
122
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
146
147
150
151
155 id<MTLLibrary> lib = nil;
156 id<MTLFunction> func = nil;
157 std::array<uint, 3> localSize = {};
158 uint outputVertexCount = 0;
159 QShaderDescription desc;
160 QShader::NativeResourceBindingMap nativeResourceBindingMap;
161 QShader::NativeShaderInfo nativeShaderInfo;
164 nativeResourceBindingMap.clear();
183 const QColor &colorClearValue,
184 const QRhiDepthStencilClearValue &depthStencilClearValue,
186 QRhiShadingRateMap *shadingRateMap);
188 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
217 id<MTLTexture> texture;
436 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
445 return patchCount *
sizeof(
float) * 128;
493 if (importDevice->dev) {
494 d->dev = (id<MTLDevice>) importDevice->dev;
496 if (importedCmdQueue)
497 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
499 qWarning(
"No MTLDevice given, cannot import");
513 return (v + byteAlign - 1) & ~(byteAlign - 1);
518 QMacAutoReleasePool pool;
521 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
535 return [cmdQueue commandBufferWithUnretainedReferences];
537 return [cmdQueue commandBuffer];
548 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
549 binArchDesc.url = sourceFileUrl;
551 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
552 [binArchDesc release];
554 const QString msg = QString::fromNSString(err.localizedDescription);
555 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
568 d->dev = MTLCreateSystemDefaultDevice();
571 qWarning(
"No MTLDevice");
575 const QString deviceName = QString::fromNSString([d->dev name]);
576 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
577 driverInfoStruct.deviceName = deviceName.toUtf8();
584 const MTLDeviceLocation deviceLocation = [d->dev location];
585 switch (deviceLocation) {
586 case MTLDeviceLocationBuiltIn:
587 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
589 case MTLDeviceLocationSlot:
590 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
592 case MTLDeviceLocationExternal:
593 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
599 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
602 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
603 osMajor = ver.majorVersion();
604 osMinor = ver.minorVersion();
606 if (importedCmdQueue)
607 [d->cmdQueue retain];
609 d->cmdQueue = [d->dev newCommandQueue];
611 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
615 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
616 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
617 d->captureScope.label = label.toNSString();
619#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
620 caps.maxTextureSize = 16384;
621 caps.baseVertexAndInstance =
true;
622 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
623 caps.maxThreadGroupSize = 1024;
624 caps.multiView =
true;
625#elif defined(Q_OS_TVOS)
626 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
627 caps.maxTextureSize = 16384;
629 caps.maxTextureSize = 8192;
630 caps.baseVertexAndInstance =
false;
631 caps.isAppleGPU =
true;
632#elif defined(Q_OS_IOS)
633 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
634 caps.maxTextureSize = 16384;
635 caps.baseVertexAndInstance =
true;
636 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
637 caps.maxTextureSize = 8192;
638 caps.baseVertexAndInstance =
false;
640 caps.maxTextureSize = 4096;
641 caps.baseVertexAndInstance =
false;
643 caps.isAppleGPU =
true;
644 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
645 caps.maxThreadGroupSize = 1024;
646 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
647 caps.multiView =
true;
650 caps.supportedSampleCounts = { 1 };
651 for (
int sampleCount : { 2, 4, 8 }) {
652 if ([d->dev supportsTextureSampleCount: sampleCount])
653 caps.supportedSampleCounts.append(sampleCount);
656 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
657 if (caps.shadingRateMap && caps.multiView)
658 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
660 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
661 d->setupBinaryArchive();
663 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
664 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
674 for (QMetalShader &s : d->shaderCache)
676 d->shaderCache.clear();
678 [d->captureScope release];
679 d->captureScope = nil;
681 [d->binArch release];
684 [d->cmdQueue release];
685 if (!importedCmdQueue)
695 return caps.supportedSampleCounts;
700 Q_UNUSED(sampleCount);
701 return { QSize(1, 1) };
706 return new QMetalSwapChain(
this);
709QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
711 return new QMetalBuffer(
this, type, usage, size);
738 if (m.isIdentity()) {
740 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
741 0.0f, 1.0f, 0.0f, 0.0f,
742 0.0f, 0.0f, 0.5f, 0.5f,
743 0.0f, 0.0f, 0.0f, 1.0f);
752 bool supportsFamilyMac2 =
false;
753 bool supportsFamilyApple3 =
false;
756 supportsFamilyMac2 =
true;
758 supportsFamilyApple3 =
true;
760 supportsFamilyApple3 =
true;
764 if (format == QRhiTexture::BC5)
767 if (!supportsFamilyApple3) {
768 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
770 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
774 if (!supportsFamilyMac2)
775 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
784 case QRhi::MultisampleTexture:
786 case QRhi::MultisampleRenderBuffer:
788 case QRhi::DebugMarkers:
790 case QRhi::Timestamps:
792 case QRhi::Instancing:
794 case QRhi::CustomInstanceStepRate:
796 case QRhi::PrimitiveRestart:
798 case QRhi::NonDynamicUniformBuffers:
800 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
802 case QRhi::NPOTTextureRepeat:
804 case QRhi::RedOrAlpha8IsRed:
806 case QRhi::ElementIndexUint:
810 case QRhi::WideLines:
812 case QRhi::VertexShaderPointSize:
814 case QRhi::BaseVertex:
815 return caps.baseVertexAndInstance;
816 case QRhi::BaseInstance:
817 return caps.baseVertexAndInstance;
818 case QRhi::TriangleFanTopology:
820 case QRhi::ReadBackNonUniformBuffer:
822 case QRhi::ReadBackNonBaseMipLevel:
824 case QRhi::TexelFetch:
826 case QRhi::RenderToNonBaseMipLevel:
828 case QRhi::IntAttributes:
830 case QRhi::ScreenSpaceDerivatives:
832 case QRhi::ReadBackAnyTextureFormat:
834 case QRhi::PipelineCacheDataLoadSave:
836 case QRhi::ImageDataStride:
838 case QRhi::RenderBufferImport:
840 case QRhi::ThreeDimensionalTextures:
842 case QRhi::RenderTo3DTextureSlice:
844 case QRhi::TextureArrays:
846 case QRhi::Tessellation:
848 case QRhi::GeometryShader:
850 case QRhi::TextureArrayRange:
852 case QRhi::NonFillPolygonMode:
854 case QRhi::OneDimensionalTextures:
856 case QRhi::OneDimensionalTextureMipmaps:
858 case QRhi::HalfAttributes:
860 case QRhi::RenderToOneDimensionalTexture:
862 case QRhi::ThreeDimensionalTextureMipmaps:
864 case QRhi::MultiView:
865 return caps.multiView;
866 case QRhi::TextureViewFormat:
868 case QRhi::ResolveDepthStencil:
870 case QRhi::VariableRateShading:
872 case QRhi::VariableRateShadingMap:
873 return caps.shadingRateMap;
874 case QRhi::VariableRateShadingMapWithTexture:
876 case QRhi::PerRenderTargetBlending:
877 case QRhi::SampleVariables:
879 case QRhi::InstanceIndexIncludesBaseInstance:
890 case QRhi::TextureSizeMin:
892 case QRhi::TextureSizeMax:
893 return caps.maxTextureSize;
894 case QRhi::MaxColorAttachments:
896 case QRhi::FramesInFlight:
898 case QRhi::MaxAsyncReadbackFrames:
900 case QRhi::MaxThreadGroupsPerDimension:
902 case QRhi::MaxThreadsPerThreadGroup:
904 case QRhi::MaxThreadGroupX:
906 case QRhi::MaxThreadGroupY:
908 case QRhi::MaxThreadGroupZ:
909 return caps.maxThreadGroupSize;
910 case QRhi::TextureArraySizeMax:
912 case QRhi::MaxUniformBufferRange:
914 case QRhi::MaxVertexInputs:
916 case QRhi::MaxVertexOutputs:
918 case QRhi::ShadingRateImageTileSize:
928 return &nativeHandlesStruct;
933 return driverInfoStruct;
939 result.totalPipelineCreationTime = totalPipelineCreationTime();
949void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
956 for (QMetalShader &s : d->shaderCache)
959 d->shaderCache.clear();
981 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
986 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
991 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
992 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
994 if (![d->binArch serializeToURL: url error: &err]) {
995 const QString msg = QString::fromNSString(err.localizedDescription);
997 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1002 if (!f.open(QIODevice::ReadOnly)) {
1003 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1006 const QByteArray blob = f.readAll();
1010 const quint32 dataSize = quint32(blob.size());
1012 data.resize(headerSize + dataSize);
1015 header.rhiId = pipelineCacheRhiId();
1016 header.arch = quint32(
sizeof(
void*));
1017 header.dataSize = quint32(dataSize);
1018 header.osMajor = osMajor;
1019 header.osMinor = osMinor;
1020 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1022 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1023 header.driver[driverStrLen] =
'\0';
1025 memcpy(data.data(), &header, headerSize);
1026 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1036 if (data.size() < qsizetype(headerSize)) {
1037 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1041 const size_t dataOffset = headerSize;
1043 memcpy(&header, data.constData(), headerSize);
1045 const quint32 rhiId = pipelineCacheRhiId();
1046 if (header.rhiId != rhiId) {
1047 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1048 rhiId, header.rhiId);
1052 const quint32 arch = quint32(
sizeof(
void*));
1053 if (header.arch != arch) {
1054 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1059 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1060 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1061 osMajor, osMinor, header.osMajor, header.osMinor);
1065 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1066 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1067 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1071 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1072 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1076 const char *p = data.constData() + dataOffset;
1080 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1083 tmp.write(p, header.dataSize);
1086 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1087 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1088 if (
d->setupBinaryArchive(url))
1089 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1092QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1093 int sampleCount, QRhiRenderBuffer::Flags flags,
1094 QRhiTexture::Format backingFormatHint)
1096 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1100 const QSize &pixelSize,
int depth,
int arraySize,
1101 int sampleCount, QRhiTexture::Flags flags)
1103 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1107 QRhiSampler::Filter mipmapMode,
1108 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1110 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1115 return new QMetalShadingRateMap(
this);
1119 QRhiTextureRenderTarget::Flags flags)
1126 return new QMetalGraphicsPipeline(
this);
1131 return new QMetalComputePipeline(
this);
1136 return new QMetalShaderResourceBindings(
this);
1147 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1150 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1151 if (!map || map->isEmpty())
1154 auto it = map->constFind(binding);
1155 if (it != map->cend())
1166 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1167 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1170 case QMetalShaderResourceBindingsData::VERTEX:
1171 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1172 offsets: offsetBatch.resources.constData()
1173 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1175 case QMetalShaderResourceBindingsData::FRAGMENT:
1176 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1177 offsets: offsetBatch.resources.constData()
1178 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1180 case QMetalShaderResourceBindingsData::COMPUTE:
1181 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1182 offsets: offsetBatch.resources.constData()
1183 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1197 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1200 case QMetalShaderResourceBindingsData::VERTEX:
1201 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1202 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1204 case QMetalShaderResourceBindingsData::FRAGMENT:
1205 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1206 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1208 case QMetalShaderResourceBindingsData::COMPUTE:
1209 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1210 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1224 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1226 switch (encoderStage) {
1227 case QMetalShaderResourceBindingsData::VERTEX:
1228 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1229 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1231 case QMetalShaderResourceBindingsData::FRAGMENT:
1232 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1233 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1235 case QMetalShaderResourceBindingsData::COMPUTE:
1236 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1237 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1259 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1260 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1261 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1262 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1265 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1266 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1267 bindStageTextures(cbD, encoderStage, batch);
1270 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1271 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1272 bindStageSamplers(cbD, encoderStage, batch);
1279 case QMetalShaderResourceBindingsData::VERTEX:
1280 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1281 case QMetalShaderResourceBindingsData::TESSCTRL:
1282 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1283 case QMetalShaderResourceBindingsData::TESSEVAL:
1284 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1285 case QMetalShaderResourceBindingsData::FRAGMENT:
1286 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1287 case QMetalShaderResourceBindingsData::COMPUTE:
1288 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1291 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1296 int dynamicOffsetCount,
1297 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1298 bool offsetOnlyChange,
1299 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1303 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1304 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1306 case QRhiShaderResourceBinding::UniformBuffer:
1308 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1309 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1310 quint32 offset = b->u.ubuf.offset;
1311 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1312 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1313 if (dynOfs.first == b->binding) {
1314 offset = dynOfs.second;
1319 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1320 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1321 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1322 if (nativeBinding >= 0)
1323 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1328 case QRhiShaderResourceBinding::SampledTexture:
1329 case QRhiShaderResourceBinding::Texture:
1330 case QRhiShaderResourceBinding::Sampler:
1332 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1333 for (
int elem = 0; elem < data->count; ++elem) {
1334 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1335 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1337 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1338 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1343 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1344 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1345 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1346 if (textureBinding >= 0 && texD)
1347 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1348 if (samplerBinding >= 0)
1349 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1355 case QRhiShaderResourceBinding::ImageLoad:
1356 case QRhiShaderResourceBinding::ImageStore:
1357 case QRhiShaderResourceBinding::ImageLoadStore:
1359 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1360 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1362 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1363 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1364 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1365 if (nativeBinding >= 0)
1366 bindingData.res[stage].textures.append({ nativeBinding, t });
1371 case QRhiShaderResourceBinding::BufferLoad:
1372 case QRhiShaderResourceBinding::BufferStore:
1373 case QRhiShaderResourceBinding::BufferLoadStore:
1375 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1376 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1377 quint32 offset = b->u.sbuf.offset;
1378 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1379 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1380 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1381 if (nativeBinding >= 0)
1382 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1405 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1406 return a.nativeBinding < b.nativeBinding;
1409 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1410 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1411 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1414 bindingData.res[stage].bufferBatches.finish();
1415 bindingData.res[stage].bufferOffsetBatches.finish();
1417 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1418 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1419 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1421 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1422 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1423 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1424 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1428 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1431 if (offsetOnlyChange)
1434 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1435 return a.nativeBinding < b.nativeBinding;
1438 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1439 return a.nativeBinding < b.nativeBinding;
1442 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1443 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1445 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1446 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1448 bindingData.res[stage].textureBatches.finish();
1449 bindingData.res[stage].samplerBatches.finish();
1451 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1452 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1454 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1455 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1459 bindStageTextures(cbD, stage, batch);
1462 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1463 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1465 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1466 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1470 bindStageSamplers(cbD, stage, batch);
1474 cbD
->d->currentShaderResourceBindingState = bindingData;
1479 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1481 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1482 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1483 cbD
->d->currentDepthStencilState =
d->ds;
1486 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1490 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1494 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1498 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1501 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1504 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1505 slopeScale: d->slopeScaledDepthBias
1522 cbD->currentPipelineGeneration = psD->generation;
1524 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1529 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1530 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1531 workBuf->lastActiveFrameSlot = -1;
1533 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1534 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1535 workBuf->lastActiveFrameSlot = -1;
1538 psD->lastActiveFrameSlot = currentFrameSlot;
1542 int dynamicOffsetCount,
1543 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1552 srb = gfxPsD->m_shaderResourceBindings;
1554 srb = compPsD->m_shaderResourceBindings;
1558 bool hasSlottedResourceInSrb =
false;
1559 bool hasDynamicOffsetInSrb =
false;
1560 bool resNeedsRebind =
false;
1562 bool pipelineChanged =
false;
1575 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1578 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1579 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1582 case QRhiShaderResourceBinding::UniformBuffer:
1585 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1588 hasSlottedResourceInSrb =
true;
1589 if (b->u.ubuf.hasDynamicOffset)
1590 hasDynamicOffsetInSrb =
true;
1591 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1592 resNeedsRebind =
true;
1593 bd.ubuf.id = bufD->m_id;
1594 bd.ubuf.generation = bufD->generation;
1596 bufD->lastActiveFrameSlot = currentFrameSlot;
1599 case QRhiShaderResourceBinding::SampledTexture:
1600 case QRhiShaderResourceBinding::Texture:
1601 case QRhiShaderResourceBinding::Sampler:
1603 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1604 if (bd.stex.count != data->count) {
1605 bd.stex.count = data->count;
1606 resNeedsRebind =
true;
1608 for (
int elem = 0; elem < data->count; ++elem) {
1611 Q_ASSERT(texD || samplerD);
1612 const quint64 texId = texD ? texD->m_id : 0;
1613 const uint texGen = texD ? texD->generation : 0;
1614 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1615 const uint samplerGen = samplerD ? samplerD->generation : 0;
1616 if (texGen != bd.stex.d[elem].texGeneration
1617 || texId != bd.stex.d[elem].texId
1618 || samplerGen != bd.stex.d[elem].samplerGeneration
1619 || samplerId != bd.stex.d[elem].samplerId)
1621 resNeedsRebind =
true;
1622 bd.stex.d[elem].texId = texId;
1623 bd.stex.d[elem].texGeneration = texGen;
1624 bd.stex.d[elem].samplerId = samplerId;
1625 bd.stex.d[elem].samplerGeneration = samplerGen;
1628 texD->lastActiveFrameSlot = currentFrameSlot;
1630 samplerD->lastActiveFrameSlot = currentFrameSlot;
1634 case QRhiShaderResourceBinding::ImageLoad:
1635 case QRhiShaderResourceBinding::ImageStore:
1636 case QRhiShaderResourceBinding::ImageLoadStore:
1639 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1640 resNeedsRebind =
true;
1641 bd.simage.id = texD->m_id;
1642 bd.simage.generation = texD->generation;
1644 texD->lastActiveFrameSlot = currentFrameSlot;
1647 case QRhiShaderResourceBinding::BufferLoad:
1648 case QRhiShaderResourceBinding::BufferStore:
1649 case QRhiShaderResourceBinding::BufferLoadStore:
1652 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1654 if (needsBufferSizeBuffer) {
1655 for (
int i = 0; i < 6; ++i) {
1656 const QRhiShaderResourceBinding::StageFlag stage =
1657 QRhiShaderResourceBinding::StageFlag(1 << i);
1658 if (b->stage.testFlag(stage)) {
1659 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1665 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1666 resNeedsRebind =
true;
1667 bd.sbuf.id = bufD->m_id;
1668 bd.sbuf.generation = bufD->generation;
1670 bufD->lastActiveFrameSlot = currentFrameSlot;
1679 if (needsBufferSizeBuffer) {
1681 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1685 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1686 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1689 if (gfxPsD
->d->tess.enabled) {
1699 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1700 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1701 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1702 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1703 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1704 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1705 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1706 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1707 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1708 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1709 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1710 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1712 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1713 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
1715 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1716 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
1718 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1719 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
1722 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1723 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
1725 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1726 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
1730 for (
const auto &shader : shaders) {
1732 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1735 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1737 int maxNativeBinding = 0;
1738 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1739 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1741 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1743 Q_ASSERT(offset + size <= bufD->size());
1744 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1746 QMetalShaderResourceBindings::BoundResourceData bd;
1747 bd.sbuf.id = bufD->m_id;
1748 bd.sbuf.generation = bufD->generation;
1749 srbD->boundResourceData.append(bd);
1753 QVarLengthArray<
int, 8> bufferSizeBufferData;
1754 Q_ASSERT(storageBufferSizes.contains(shader.second));
1755 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1756 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1757 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1763 if (bufferSizeBufferData.size() <= index)
1764 bufferSizeBufferData.resize(index + 1);
1766 Q_ASSERT(sizes.contains(block.binding));
1767 bufferSizeBufferData[index] = sizes[block.binding];
1770 QRhiBufferData data;
1771 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1772 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1773 Q_ASSERT(offset + size <= bufD->size());
1774 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1777 offset += ((size + 31) / 32) * 32;
1781 bufD->lastActiveFrameSlot = currentFrameSlot;
1785 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1787 resNeedsRebind =
true;
1790 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1793 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1794 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1798 if (gfxPsD
->d->tess.enabled) {
1801 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1802 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1815 cbD->currentSrbGeneration = srbD->generation;
1818 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1819 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1824 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1825 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1830 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1831 QRhiBatchedBindings<NSUInteger> offsets;
1832 for (
int i = 0; i < bindingCount; ++i) {
1835 bufD->lastActiveFrameSlot = currentFrameSlot;
1836 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1837 buffers.feed(startBinding + i, mtlbuf);
1838 offsets.feed(startBinding + i, bindings[i].second);
1853 || buffers != cbD
->d->currentVertexInputsBuffers
1854 || offsets != cbD
->d->currentVertexInputOffsets)
1857 cbD
->d->currentVertexInputsBuffers = buffers;
1858 cbD
->d->currentVertexInputOffsets = offsets;
1860 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1861 const auto &bufferBatch(buffers.batches[i]);
1862 const auto &offsetBatch(offsets.batches[i]);
1863 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1864 bufferBatch.resources.constData()
1865 offsets: offsetBatch.resources.constData()
1866 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1873 ibufD->lastActiveFrameSlot = currentFrameSlot;
1875 cbD->currentIndexOffset = indexOffset;
1876 cbD->currentIndexFormat = indexFormat;
1886 QSize outputSize = cbD->currentTarget->pixelSize();
1892 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1893 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1894 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1895 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1896 auto screenSize = [rateMap screenSize];
1897 outputSize = QSize(screenSize.width, screenSize.height);
1904 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1908 vp.originX =
double(x);
1909 vp.originY =
double(y);
1910 vp.width =
double(w);
1911 vp.height =
double(h);
1912 vp.znear =
double(viewport.minDepth());
1913 vp.zfar =
double(viewport.maxDepth());
1915 [cbD->d->currentRenderPassEncoder setViewport: vp];
1920 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, viewport.viewport(), &x, &y, &w, &h);
1921 s.x = NSUInteger(x);
1922 s.y = NSUInteger(y);
1923 s.width = NSUInteger(w);
1924 s.height = NSUInteger(h);
1925 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1935 const QSize outputSize = cbD->currentTarget->pixelSize();
1939 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
1943 s.x = NSUInteger(x);
1944 s.y = NSUInteger(y);
1945 s.width = NSUInteger(w);
1946 s.height = NSUInteger(h);
1948 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1956 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
1957 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
1965 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
1971 Q_UNUSED(coarsePixelSize);
1976 if (cbD
->d->currentRenderPassEncoder) {
1977 [cbD->d->currentRenderPassEncoder endEncoding];
1978 cbD->d->currentRenderPassEncoder = nil;
1981 if (!cbD->d->tessellationComputeEncoder)
1982 cbD->d->tessellationComputeEncoder = [cbD->d->cb computeCommandEncoder];
1984 return cbD
->d->tessellationComputeEncoder;
1989 if (cbD
->d->tessellationComputeEncoder) {
1990 [cbD->d->tessellationComputeEncoder endEncoding];
1991 cbD->d->tessellationComputeEncoder = nil;
1996 switch (cbD->currentTarget->resourceType()) {
1997 case QRhiResource::SwapChainRenderTarget:
2000 case QRhiResource::TextureRenderTarget:
2009 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2011 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2012 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2013 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2016 MTLLoadAction oldDepthLoad;
2017 MTLLoadAction oldStencilLoad;
2019 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2020 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2021 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2023 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2024 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2025 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2028 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2032 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2036 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2037 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2046 if (graphicsPipeline
->d->tess.failed)
2050 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2051 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2055 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2061 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
2065 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2066 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2067 if (args.type == TessDrawArgs::U16Indexed)
2068 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2069 else if (args.type == TessDrawArgs::U32Indexed)
2070 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2071 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2072 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2073 [computeEncoder setComputePipelineState: computePipelineState];
2078 cbD
->d->currentComputePassEncoder = computeEncoder;
2080 cbD->d->currentComputePassEncoder = nil;
2082 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2083 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2084 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2086 if (outputBufferBinding >= 0) {
2087 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2088 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2091 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2094 if (indexBufferBinding >= 0)
2095 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2097 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2098 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2099 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2100 [computeEncoder setBuffers: bufferBatch.resources.constData()
2101 offsets: offsetBatch.resources.constData()
2102 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2106 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2107 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2109 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2110 args.draw.vertexCount, args.draw.instanceCount)];
2113 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2114 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2119 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2120 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2121 [computeEncoder setComputePipelineState: computePipelineState];
2123 cbD
->d->currentComputePassEncoder = computeEncoder;
2125 cbD->d->currentComputePassEncoder = nil;
2127 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2128 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2129 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2130 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2131 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2132 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2134 if (outputBufferBinding >= 0) {
2135 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2136 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2139 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2142 if (patchOutputBufferBinding >= 0) {
2143 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2144 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2145 if (!tescPatchOutBuf)
2147 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2150 if (tessFactorBufferBinding >= 0) {
2151 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2152 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2155 if (paramsBufferBinding >= 0) {
2157 quint32 inControlPointCount;
2164 params.patchCount = patchCount;
2165 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2166 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2167 memcpy(p, ¶ms,
sizeof(params));
2168 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2171 if (vertOutBuf && inputBufferBinding >= 0)
2172 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2174 int sgSize =
int(computePipelineState.threadExecutionWidth);
2175 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2176 while (wgSize > caps.maxThreadGroupSize) {
2178 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2180 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2181 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2198 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2203 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2204 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2205 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2206 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2208 if (outputBufferBinding >= 0 && tescOutBuf)
2209 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2211 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2212 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2214 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2215 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2216 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2219 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2221 patchCount: patchCount
2222 patchIndexBuffer: nil
2223 patchIndexBufferOffset: 0
2233 if (multiViewCount <= 1)
2237 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2238 if (viewMaskBufBinding == -1) {
2239 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2246 multiViewInfo.viewOffset = 0;
2247 multiViewInfo.viewCount = quint32(multiViewCount);
2251 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2252 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2253 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2254 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2258 *instanceCount *= multiViewCount;
2263 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2272 a.draw.vertexCount = vertexCount;
2273 a.draw.instanceCount = instanceCount;
2274 a.draw.firstVertex = firstVertex;
2275 a.draw.firstInstance = firstInstance;
2280 adjustForMultiViewDraw(&instanceCount, cb);
2282 if (caps.baseVertexAndInstance) {
2283 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2284 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2286 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2287 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2292 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2300 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2301 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2304 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2309 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2310 a.drawIndexed.indexCount = indexCount;
2311 a.drawIndexed.instanceCount = instanceCount;
2312 a.drawIndexed.firstIndex = firstIndex;
2313 a.drawIndexed.vertexOffset = vertexOffset;
2314 a.drawIndexed.firstInstance = firstInstance;
2315 a.drawIndexed.indexBuffer = mtlibuf;
2320 adjustForMultiViewDraw(&instanceCount, cb);
2322 if (caps.baseVertexAndInstance) {
2323 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2324 indexCount: indexCount
2325 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2326 indexBuffer: mtlibuf
2327 indexBufferOffset: indexOffset
2328 instanceCount: instanceCount
2329 baseVertex: vertexOffset
2330 baseInstance: firstInstance];
2332 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2333 indexCount: indexCount
2334 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2335 indexBuffer: mtlibuf
2336 indexBufferOffset: indexOffset
2337 instanceCount: instanceCount];
2346 NSString *str = [NSString stringWithUTF8String: name.constData()];
2348 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2349 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2351 [cbD->d->cb pushDebugGroup: str];
2360 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2361 [cbD->d->currentRenderPassEncoder popDebugGroup];
2363 [cbD->d->cb popDebugGroup];
2372 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2373 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2378 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2404 currentFrameSlot = swapChainD->currentFrameSlot;
2409 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2417 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2418 if (sc != swapChainD)
2419 sc->waitUntilCompleted(currentFrameSlot);
2422 [d->captureScope beginScope];
2424 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2428 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2435 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2436 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2441 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2444 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2445 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2448 return QRhi::FrameOpSuccess;
2457 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2459 __block
int thisFrameSlot = currentFrameSlot;
2460 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2461 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2462 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2469 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2470 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2471 [drawableTexture release];
2475 if (flags.testFlag(QRhi::SkipPresent)) {
2477 [commandBuffer commit];
2479 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2481 if (swapChainD
->d->layer.presentsWithTransaction) {
2482 [commandBuffer commit];
2484 auto *metalLayer = swapChainD
->d->layer;
2485 auto presentWithTransaction = ^{
2486 [commandBuffer waitUntilScheduled];
2493 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2494 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2495 if (textureSize == surfaceSize) {
2498 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2499 << textureSize <<
"not matching surface size" << surfaceSize;
2503 if (NSThread.currentThread == NSThread.mainThread) {
2504 presentWithTransaction();
2506 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2507 Q_ASSERT(qtMetalLayer);
2509 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2513 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2514 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2520 if (qtMetalLayer.displayLock.tryLockForRead()) {
2522 qtMetalLayer.displayLock.unlock();
2524 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2525 <<
"due to" << qtMetalLayer <<
"needing display";
2531 [commandBuffer commit];
2535 [commandBuffer commit];
2542 [swapChainD->d->curDrawable release];
2543 swapChainD->d->curDrawable = nil;
2545 [d->captureScope endScope];
2549 return QRhi::FrameOpSuccess;
2556 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2558 for (QMetalSwapChain *sc : std::as_const(swapchains))
2559 sc->waitUntilCompleted(currentFrameSlot);
2561 d->ofr.active =
true;
2562 *cb = &
d->ofr.cbWrapper;
2563 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2566 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2567 d->ofr.lastGpuTime = 0;
2570 return QRhi::FrameOpSuccess;
2576 Q_ASSERT(
d->ofr.active);
2577 d->ofr.active =
false;
2579 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2583 [cb waitUntilCompleted];
2585 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2589 return QRhi::FrameOpSuccess;
2594 id<MTLCommandBuffer> cb = nil;
2597 if (
d->ofr.active) {
2600 cb =
d->ofr.cbWrapper.d->cb;
2605 cb = swapChainD->cbWrapper.d->cb;
2609 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2610 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2611 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2616 sc->waitUntilCompleted(i);
2622 [cb waitUntilCompleted];
2626 if (
d->ofr.active) {
2627 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2628 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2630 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2631 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2639 return QRhi::FrameOpSuccess;
2643 const QColor &colorClearValue,
2644 const QRhiDepthStencilClearValue &depthStencilClearValue,
2646 QRhiShadingRateMap *shadingRateMap)
2648 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2649 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2650 colorClearValue.alphaF());
2652 for (uint i = 0; i < uint(colorAttCount); ++i) {
2653 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2654 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2655 rp.colorAttachments[i].clearColor = c;
2658 if (hasDepthStencil) {
2659 rp.depthAttachment.loadAction = MTLLoadActionClear;
2660 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2661 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2662 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2663 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2664 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2668 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
2676 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2677 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2678 if (imageSizeBytes > 0)
2679 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2684 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
2687 const QPoint dp = subresDesc.destinationTopLeft();
2688 const QByteArray rawData = subresDesc.data();
2689 QImage img = subresDesc.image();
2690 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2691 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
2693 if (!img.isNull()) {
2694 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2695 QSize size = img.size();
2696 int bpl = img.bytesPerLine();
2698 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2699 const int sx = subresDesc.sourceTopLeft().x();
2700 const int sy = subresDesc.sourceTopLeft().y();
2701 if (!subresDesc.sourceSize().isEmpty())
2702 size = subresDesc.sourceSize();
2703 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
2704 if (size.width() == img.width()) {
2705 const int bpc = qMax(1, img.depth() / 8);
2706 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
2707 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
2708 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
2709 size.height() * img.bytesPerLine());
2711 img = img.copy(sx, sy, size.width(), size.height());
2712 bpl = img.bytesPerLine();
2713 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2714 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2717 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
2718 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2721 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2722 sourceOffset: NSUInteger(*curOfs)
2723 sourceBytesPerRow: NSUInteger(bpl)
2724 sourceBytesPerImage: 0
2725 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
2726 toTexture: texD->d->tex
2727 destinationSlice: NSUInteger(is3D ? 0 : layer)
2728 destinationLevel: NSUInteger(level)
2729 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2730 options: MTLBlitOptionNone];
2732 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2733 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2734 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2735 const int subresw = subresSize.width();
2736 const int subresh = subresSize.height();
2738 if (subresDesc.sourceSize().isEmpty()) {
2742 w = subresDesc.sourceSize().width();
2743 h = subresDesc.sourceSize().height();
2748 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
2750 const int dx = aligned(dp.x(), blockDim.width());
2751 const int dy = aligned(dp.y(), blockDim.height());
2752 if (dx + w != subresw)
2753 w = aligned(w, blockDim.width());
2754 if (dy + h != subresh)
2755 h = aligned(h, blockDim.height());
2757 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2759 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2760 sourceOffset: NSUInteger(*curOfs)
2761 sourceBytesPerRow: bpl
2762 sourceBytesPerImage: 0
2763 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2764 toTexture: texD->d->tex
2765 destinationSlice: NSUInteger(is3D ? 0 : layer)
2766 destinationLevel: NSUInteger(level)
2767 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2768 options: MTLBlitOptionNone];
2770 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2771 }
else if (!rawData.isEmpty()) {
2772 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2773 const int subresw = subresSize.width();
2774 const int subresh = subresSize.height();
2776 if (subresDesc.sourceSize().isEmpty()) {
2780 w = subresDesc.sourceSize().width();
2781 h = subresDesc.sourceSize().height();
2785 if (subresDesc.dataStride())
2786 bpl = subresDesc.dataStride();
2788 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
2790 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2792 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2793 sourceOffset: NSUInteger(*curOfs)
2794 sourceBytesPerRow: bpl
2795 sourceBytesPerImage: 0
2796 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2797 toTexture: texD->d->tex
2798 destinationSlice: NSUInteger(is3D ? 0 : layer)
2799 destinationLevel: NSUInteger(level)
2800 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2801 options: MTLBlitOptionNone];
2803 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2805 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2814 id<MTLBlitCommandEncoder> blitEnc = nil;
2815 auto ensureBlit = [&blitEnc, cbD,
this]() {
2817 blitEnc = [cbD->d->cb blitCommandEncoder];
2819 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
2827 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2829 if (u.offset == 0 && u
.data.size() == bufD->m_size)
2830 bufD
->d->pendingUpdates[i].clear();
2831 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2837 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2838 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
2840 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
2844 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2845 if (bufD->m_type == QRhiBuffer::Dynamic) {
2846 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
2848 u.result->data.resize(u.readSize);
2849 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2851 if (u.result->completed)
2852 u.result->completed();
2856 readback.buf = bufD
->d->buf[idx];
2857 readback.offset = u.offset;
2858 readback.readSize = u.readSize;
2859 readback.result = u.result;
2860 d->activeBufferReadbacks.append(readback);
2862 if (bufD->d->managed) {
2865 [blitEnc synchronizeResource:readback.buf];
2876 qsizetype stagingSize = 0;
2877 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2878 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2879 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2880 stagingSize += subresUploadByteSize(subresDesc);
2885 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2886 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2887 options: MTLResourceStorageModeShared];
2889 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2890 qsizetype curOfs = 0;
2891 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2892 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2893 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2894 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2898 utexD->lastActiveFrameSlot = currentFrameSlot;
2902 e.lastActiveFrameSlot = currentFrameSlot;
2903 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2904 utexD->d->stagingBuf[currentFrameSlot] = nil;
2905 d->releaseQueue.append(e);
2910 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2911 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2912 const QPoint dp = u.desc.destinationTopLeft();
2913 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2914 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2915 const QPoint sp = u.desc.sourceTopLeft();
2918 [blitEnc copyFromTexture: srcD->d->tex
2919 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2920 sourceLevel: NSUInteger(u.desc.sourceLevel())
2921 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2922 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2923 toTexture: dstD->d->tex
2924 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2925 destinationLevel: NSUInteger(u.desc.destinationLevel())
2926 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2928 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2931 readback.activeFrameSlot = currentFrameSlot;
2932 readback.desc = u.rb;
2933 readback.result = u.result;
2942 qWarning(
"Multisample texture cannot be read back");
2945 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2946 if (u.rb.rect().isValid())
2949 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
2950 readback.format = texD->m_format;
2952 texD->lastActiveFrameSlot = currentFrameSlot;
2956 if (u.rb.rect().isValid())
2959 rect = QRect({0, 0}, swapChainD->pixelSize);
2960 readback.format = swapChainD
->d->rhiColorFormat;
2964 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2966 readback.pixelSize = rect.size();
2969 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
2970 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2973 [blitEnc copyFromTexture: src
2974 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2975 sourceLevel: NSUInteger(u.rb.level())
2976 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
2977 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
2978 toBuffer: readback.buf
2979 destinationOffset: 0
2980 destinationBytesPerRow: bpl
2981 destinationBytesPerImage: 0
2982 options: MTLBlitOptionNone];
2984 d->activeTextureReadbacks.append(readback);
2988 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2989 utexD->lastActiveFrameSlot = currentFrameSlot;
2995 [blitEnc popDebugGroup];
2996 [blitEnc endEncoding];
3005 if (bufD
->d->pendingUpdates[slot].isEmpty())
3008 void *p = [bufD->d->buf[slot] contents];
3009 quint32 changeBegin = UINT32_MAX;
3010 quint32 changeEnd = 0;
3011 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3012 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3013 if (u.offset < changeBegin)
3014 changeBegin = u.offset;
3015 if (u.offset + u.data.size() > changeEnd)
3016 changeEnd = u.offset + u.data.size();
3019 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3020 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3023 bufD
->d->pendingUpdates[slot].clear();
3033 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3039 QRhiRenderTarget *rt,
3040 const QColor &colorClearValue,
3041 const QRhiDepthStencilClearValue &depthStencilClearValue,
3042 QRhiResourceUpdateBatch *resourceUpdates,
3048 if (resourceUpdates)
3052 switch (rt->resourceType()) {
3053 case QRhiResource::SwapChainRenderTarget:
3057 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3060 depthStencilClearValue,
3068 if (!swapChainD
->d->curDrawable) {
3069 QMacAutoReleasePool pool;
3070 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3072 if (!swapChainD
->d->curDrawable) {
3073 qWarning(
"No drawable");
3076 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3081 color0.resolveTex = scTex;
3087 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3090 case QRhiResource::TextureRenderTarget:
3094 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3098 depthStencilClearValue,
3100 rtTex->m_desc.shadingRateMap());
3101 if (rtD->fb.preserveColor) {
3102 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3103 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3106 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3107 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3109 int colorAttCount = 0;
3110 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3114 if (it->texture()) {
3115 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3116 if (it->multiViewCount() >= 2)
3117 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3118 }
else if (it->renderBuffer()) {
3119 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3121 if (it->resolveTexture())
3122 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3124 if (rtTex->m_desc.depthStencilBuffer())
3125 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3126 if (rtTex->m_desc.depthTexture()) {
3128 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3129 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3130 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3132 if (rtTex->m_desc.depthResolveTexture())
3133 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3134 if (rtTex->m_desc.shadingRateMap())
3135 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3144 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3145 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3146 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3147 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3148 if (rtD->fb.colorAtt[i].resolveTex) {
3149 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3150 : MTLStoreActionMultisampleResolve;
3151 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3152 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3153 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3158 Q_ASSERT(rtD->fb.dsTex);
3159 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3160 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3161 if (rtD->fb.depthNeedsStore)
3162 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3163 if (rtD->fb.dsResolveTex) {
3164 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3165 : MTLStoreActionMultisampleResolve;
3166 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3167 if (rtD->fb.hasStencil) {
3168 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3169 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3174 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3179 cbD->currentTarget = rt;
3187 [cbD->d->currentRenderPassEncoder endEncoding];
3190 cbD->currentTarget =
nullptr;
3192 if (resourceUpdates)
3197 QRhiResourceUpdateBatch *resourceUpdates,
3203 if (resourceUpdates)
3206 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3216 [cbD->d->currentComputePassEncoder endEncoding];
3219 if (resourceUpdates)
3232 cbD->currentPipelineGeneration = psD->generation;
3234 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3237 psD->lastActiveFrameSlot = currentFrameSlot;
3246 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3247 threadsPerThreadgroup: psD->d->localSize];
3252 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3253 [e.buffer.buffers[i] release];
3258 [e.renderbuffer.texture release];
3263 [e.texture.texture release];
3264 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3265 [e.texture.stagingBuffers[i] release];
3266 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3267 [e.texture.views[i] release];
3272 [e.sampler.samplerState release];
3277 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3279 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3293 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3294 [e.stagingBuffer.buffer release];
3296 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3297 [e.graphicsPipeline.pipelineState release];
3298 [e.graphicsPipeline.depthStencilState release];
3299 [e.graphicsPipeline.tessVertexComputeState[0] release];
3300 [e.graphicsPipeline.tessVertexComputeState[1] release];
3301 [e.graphicsPipeline.tessVertexComputeState[2] release];
3302 [e.graphicsPipeline.tessTessControlComputeState release];
3304 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3305 [e.computePipeline.pipelineState release];
3307 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3308 [e.shadingRateMap.rateMap release];
3313 d->releaseQueue.removeAt(i);
3320 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3322 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3324 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3325 readback.result->format = readback.format;
3326 readback.result->pixelSize = readback.pixelSize;
3327 readback.result->data.resize(
int(readback.bufSize));
3328 void *p = [readback.buf contents];
3329 memcpy(readback.result->data.data(), p, readback.bufSize);
3330 [readback.buf release];
3332 if (readback.result->completed)
3333 completedCallbacks.append(readback.result->completed);
3335 d->activeTextureReadbacks.remove(i);
3339 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3341 if (forced || currentFrameSlot == readback.activeFrameSlot
3342 || readback.activeFrameSlot < 0) {
3343 readback.result->data.resize(readback.readSize);
3344 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3346 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3348 if (readback.result->completed)
3349 completedCallbacks.append(readback.result->completed);
3351 d->activeBufferReadbacks.remove(i);
3355 for (
auto f : completedCallbacks)
3363 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3383 e.buffer.buffers[i] =
d->buf[i];
3385 d->pendingUpdates[i].clear();
3390 rhiD
->d->releaseQueue.append(e);
3391 rhiD->unregisterResource(
this);
3400 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3401 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3405 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3406 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3409 MTLResourceOptions opts = MTLResourceStorageModeShared;
3413 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3414 opts = MTLResourceStorageModeManaged;
3423 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3425 if (
int(m_usage) == WorkBufPoolUsage)
3430 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3431 if (!m_objectName.isEmpty()) {
3433 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3435 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3436 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3444 rhiD->registerResource(
this);
3456 b.objects[i] = &
d->buf[i];
3461 return { { &
d->buf[0] }, 1 };
3471 Q_ASSERT(m_type == Dynamic);
3473 Q_ASSERT(rhiD->inFrame);
3474 const int slot = rhiD->currentFrameSlot;
3475 void *p = [d->buf[slot] contents];
3476 return static_cast<
char *>(p);
3483 QRHI_RES_RHI(QRhiMetal);
3484 const int slot = rhiD->currentFrameSlot;
3485 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3496 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3498 case QRhiTexture::RGBA8:
3499 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3500 case QRhiTexture::BGRA8:
3501 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3502 case QRhiTexture::R8:
3504 return MTLPixelFormatR8Unorm;
3506 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3508 case QRhiTexture::R8SI:
3509 return MTLPixelFormatR8Sint;
3510 case QRhiTexture::R8UI:
3511 return MTLPixelFormatR8Uint;
3512 case QRhiTexture::RG8:
3514 return MTLPixelFormatRG8Unorm;
3516 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3518 case QRhiTexture::R16:
3519 return MTLPixelFormatR16Unorm;
3520 case QRhiTexture::RG16:
3521 return MTLPixelFormatRG16Unorm;
3522 case QRhiTexture::RED_OR_ALPHA8:
3523 return MTLPixelFormatR8Unorm;
3525 case QRhiTexture::RGBA16F:
3526 return MTLPixelFormatRGBA16Float;
3527 case QRhiTexture::RGBA32F:
3528 return MTLPixelFormatRGBA32Float;
3529 case QRhiTexture::R16F:
3530 return MTLPixelFormatR16Float;
3531 case QRhiTexture::R32F:
3532 return MTLPixelFormatR32Float;
3534 case QRhiTexture::RGB10A2:
3535 return MTLPixelFormatRGB10A2Unorm;
3537 case QRhiTexture::R32SI:
3538 return MTLPixelFormatR32Sint;
3539 case QRhiTexture::R32UI:
3540 return MTLPixelFormatR32Uint;
3541 case QRhiTexture::RG32SI:
3542 return MTLPixelFormatRG32Sint;
3543 case QRhiTexture::RG32UI:
3544 return MTLPixelFormatRG32Uint;
3545 case QRhiTexture::RGBA32SI:
3546 return MTLPixelFormatRGBA32Sint;
3547 case QRhiTexture::RGBA32UI:
3548 return MTLPixelFormatRGBA32Uint;
3551 case QRhiTexture::D16:
3552 return MTLPixelFormatDepth16Unorm;
3553 case QRhiTexture::D24:
3554 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3555 case QRhiTexture::D24S8:
3556 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3558 case QRhiTexture::D16:
3559 return MTLPixelFormatDepth32Float;
3560 case QRhiTexture::D24:
3561 return MTLPixelFormatDepth32Float;
3562 case QRhiTexture::D24S8:
3563 return MTLPixelFormatDepth32Float_Stencil8;
3565 case QRhiTexture::D32F:
3566 return MTLPixelFormatDepth32Float;
3567 case QRhiTexture::D32FS8:
3568 return MTLPixelFormatDepth32Float_Stencil8;
3571 case QRhiTexture::BC1:
3572 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3573 case QRhiTexture::BC2:
3574 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3575 case QRhiTexture::BC3:
3576 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3577 case QRhiTexture::BC4:
3578 return MTLPixelFormatBC4_RUnorm;
3579 case QRhiTexture::BC5:
3580 qWarning(
"QRhiMetal does not support BC5");
3581 return MTLPixelFormatInvalid;
3582 case QRhiTexture::BC6H:
3583 return MTLPixelFormatBC6H_RGBUfloat;
3584 case QRhiTexture::BC7:
3585 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3587 case QRhiTexture::BC1:
3588 case QRhiTexture::BC2:
3589 case QRhiTexture::BC3:
3590 case QRhiTexture::BC4:
3591 case QRhiTexture::BC5:
3592 case QRhiTexture::BC6H:
3593 case QRhiTexture::BC7:
3594 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3595 return MTLPixelFormatInvalid;
3599 case QRhiTexture::ETC2_RGB8:
3600 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3601 case QRhiTexture::ETC2_RGB8A1:
3602 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3603 case QRhiTexture::ETC2_RGBA8:
3604 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3606 case QRhiTexture::ASTC_4x4:
3607 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3608 case QRhiTexture::ASTC_5x4:
3609 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3610 case QRhiTexture::ASTC_5x5:
3611 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3612 case QRhiTexture::ASTC_6x5:
3613 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3614 case QRhiTexture::ASTC_6x6:
3615 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3616 case QRhiTexture::ASTC_8x5:
3617 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3618 case QRhiTexture::ASTC_8x6:
3619 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3620 case QRhiTexture::ASTC_8x8:
3621 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3622 case QRhiTexture::ASTC_10x5:
3623 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3624 case QRhiTexture::ASTC_10x6:
3625 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3626 case QRhiTexture::ASTC_10x8:
3627 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3628 case QRhiTexture::ASTC_10x10:
3629 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3630 case QRhiTexture::ASTC_12x10:
3631 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3632 case QRhiTexture::ASTC_12x12:
3633 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3635 case QRhiTexture::ETC2_RGB8:
3636 if (d->caps.isAppleGPU)
3637 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3638 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3639 return MTLPixelFormatInvalid;
3640 case QRhiTexture::ETC2_RGB8A1:
3641 if (d->caps.isAppleGPU)
3642 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3643 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3644 return MTLPixelFormatInvalid;
3645 case QRhiTexture::ETC2_RGBA8:
3646 if (d->caps.isAppleGPU)
3647 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3648 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3649 return MTLPixelFormatInvalid;
3650 case QRhiTexture::ASTC_4x4:
3651 if (d->caps.isAppleGPU)
3652 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3653 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3654 return MTLPixelFormatInvalid;
3655 case QRhiTexture::ASTC_5x4:
3656 if (d->caps.isAppleGPU)
3657 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3658 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3659 return MTLPixelFormatInvalid;
3660 case QRhiTexture::ASTC_5x5:
3661 if (d->caps.isAppleGPU)
3662 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3663 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3664 return MTLPixelFormatInvalid;
3665 case QRhiTexture::ASTC_6x5:
3666 if (d->caps.isAppleGPU)
3667 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3668 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3669 return MTLPixelFormatInvalid;
3670 case QRhiTexture::ASTC_6x6:
3671 if (d->caps.isAppleGPU)
3672 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3673 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3674 return MTLPixelFormatInvalid;
3675 case QRhiTexture::ASTC_8x5:
3676 if (d->caps.isAppleGPU)
3677 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3678 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3679 return MTLPixelFormatInvalid;
3680 case QRhiTexture::ASTC_8x6:
3681 if (d->caps.isAppleGPU)
3682 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3683 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3684 return MTLPixelFormatInvalid;
3685 case QRhiTexture::ASTC_8x8:
3686 if (d->caps.isAppleGPU)
3687 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3688 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3689 return MTLPixelFormatInvalid;
3690 case QRhiTexture::ASTC_10x5:
3691 if (d->caps.isAppleGPU)
3692 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3693 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3694 return MTLPixelFormatInvalid;
3695 case QRhiTexture::ASTC_10x6:
3696 if (d->caps.isAppleGPU)
3697 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3698 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3699 return MTLPixelFormatInvalid;
3700 case QRhiTexture::ASTC_10x8:
3701 if (d->caps.isAppleGPU)
3702 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3703 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3704 return MTLPixelFormatInvalid;
3705 case QRhiTexture::ASTC_10x10:
3706 if (d->caps.isAppleGPU)
3707 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3708 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3709 return MTLPixelFormatInvalid;
3710 case QRhiTexture::ASTC_12x10:
3711 if (d->caps.isAppleGPU)
3712 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3713 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3714 return MTLPixelFormatInvalid;
3715 case QRhiTexture::ASTC_12x12:
3716 if (d->caps.isAppleGPU)
3717 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3718 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3719 return MTLPixelFormatInvalid;
3724 return MTLPixelFormatInvalid;
3729 int sampleCount, QRhiRenderBuffer::Flags flags,
3730 QRhiTexture::Format backingFormatHint)
3751 e.renderbuffer.texture =
d->tex;
3756 rhiD
->d->releaseQueue.append(e);
3757 rhiD->unregisterResource(
this);
3766 if (m_pixelSize.isEmpty())
3770 samples = rhiD->effectiveSampleCount(m_sampleCount);
3772 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3773 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3774 desc.width = NSUInteger(m_pixelSize.width());
3775 desc.height = NSUInteger(m_pixelSize.height());
3777 desc.sampleCount = NSUInteger(
samples);
3778 desc.resourceOptions = MTLResourceStorageModePrivate;
3779 desc.usage = MTLTextureUsageRenderTarget;
3784 if (rhiD->caps.isAppleGPU) {
3785 desc.storageMode = MTLStorageModeMemoryless;
3786 d->format = MTLPixelFormatDepth32Float_Stencil8;
3788 desc.storageMode = MTLStorageModePrivate;
3789 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
3790 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3793 desc.storageMode = MTLStorageModeMemoryless;
3794 d->format = MTLPixelFormatDepth32Float_Stencil8;
3796 desc.pixelFormat =
d->format;
3799 desc.storageMode = MTLStorageModePrivate;
3800 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3801 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
3803 d->format = MTLPixelFormatRGBA8Unorm;
3804 desc.pixelFormat =
d->format;
3811 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3814 if (!m_objectName.isEmpty())
3815 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3819 rhiD->registerResource(
this);
3825 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
3826 return m_backingFormatHint;
3828 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
3832 int arraySize,
int sampleCount, Flags flags)
3836 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3837 d->stagingBuf[i] = nil;
3839 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3840 d->perLevelViews[i] = nil;
3858 e.texture.texture = d->owns ? d->tex : nil;
3862 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
3863 d->stagingBuf[i] = nil;
3866 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
3867 e.texture.views[i] =
d->perLevelViews[i];
3868 d->perLevelViews[i] = nil;
3873 rhiD
->d->releaseQueue.append(e);
3874 rhiD->unregisterResource(
this);
3883 const bool isCube = m_flags.testFlag(CubeMap);
3884 const bool is3D = m_flags.testFlag(ThreeDimensional);
3885 const bool isArray = m_flags.testFlag(TextureArray);
3886 const bool hasMipMaps = m_flags.testFlag(MipMapped);
3887 const bool is1D = m_flags.testFlag(OneDimensional);
3889 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
3890 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
3893 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
3894 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
3895 samples = rhiD->effectiveSampleCount(m_sampleCount);
3898 qWarning(
"Cubemap texture cannot be multisample");
3902 qWarning(
"3D texture cannot be multisample");
3906 qWarning(
"Multisample texture cannot have mipmaps");
3910 if (isCube && is3D) {
3911 qWarning(
"Texture cannot be both cube and 3D");
3914 if (isArray && is3D) {
3915 qWarning(
"Texture cannot be both array and 3D");
3919 qWarning(
"Texture cannot be both 1D and 3D");
3922 if (is1D && isCube) {
3923 qWarning(
"Texture cannot be both 1D and cube");
3926 if (m_depth > 1 && !is3D) {
3927 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
3930 if (m_arraySize > 0 && !isArray) {
3931 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
3934 if (m_arraySize < 1 && isArray) {
3935 qWarning(
"Texture is an array but array size is %d", m_arraySize);
3940 *adjustedSize = size;
3948 if (!prepareCreate(&size))
3951 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
3953 const bool isCube = m_flags.testFlag(CubeMap);
3954 const bool is3D = m_flags.testFlag(ThreeDimensional);
3955 const bool isArray = m_flags.testFlag(TextureArray);
3956 const bool is1D = m_flags.testFlag(OneDimensional);
3958 desc.textureType = MTLTextureTypeCube;
3960 desc.textureType = MTLTextureType3D;
3962 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
3963 }
else if (isArray) {
3964 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
3966 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
3968 desc.pixelFormat =
d->format;
3969 desc.width = NSUInteger(size.width());
3970 desc.height = NSUInteger(size.height());
3971 desc.depth = is3D ? qMax(1, m_depth) : 1;
3974 desc.sampleCount = NSUInteger(
samples);
3976 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
3977 desc.resourceOptions = MTLResourceStorageModePrivate;
3978 desc.storageMode = MTLStorageModePrivate;
3979 desc.usage = MTLTextureUsageShaderRead;
3980 if (m_flags.testFlag(RenderTarget))
3981 desc.usage |= MTLTextureUsageRenderTarget;
3982 if (m_flags.testFlag(UsedWithLoadStore))
3983 desc.usage |= MTLTextureUsageShaderWrite;
3986 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
3989 if (!m_objectName.isEmpty())
3990 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
3996 rhiD->registerResource(
this);
4002 id<MTLTexture> tex = id<MTLTexture>(src.object);
4006 if (!prepareCreate())
4016 rhiD->registerResource(
this);
4022 return {quint64(
d->tex), 0};
4028 if (perLevelViews[level])
4029 return perLevelViews[level];
4031 const MTLTextureType type = [tex textureType];
4032 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
4033 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
4034 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4035 levels: NSMakeRange(NSUInteger(level), 1)
4036 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4038 perLevelViews[level] = view;
4043 AddressMode u, AddressMode v, AddressMode w)
4057 if (!
d->samplerState)
4064 e.sampler.samplerState =
d->samplerState;
4065 d->samplerState = nil;
4069 rhiD
->d->releaseQueue.append(e);
4070 rhiD->unregisterResource(
this);
4077 case QRhiSampler::Nearest:
4078 return MTLSamplerMinMagFilterNearest;
4079 case QRhiSampler::Linear:
4080 return MTLSamplerMinMagFilterLinear;
4083 return MTLSamplerMinMagFilterNearest;
4090 case QRhiSampler::None:
4091 return MTLSamplerMipFilterNotMipmapped;
4092 case QRhiSampler::Nearest:
4093 return MTLSamplerMipFilterNearest;
4094 case QRhiSampler::Linear:
4095 return MTLSamplerMipFilterLinear;
4098 return MTLSamplerMipFilterNotMipmapped;
4105 case QRhiSampler::Repeat:
4106 return MTLSamplerAddressModeRepeat;
4107 case QRhiSampler::ClampToEdge:
4108 return MTLSamplerAddressModeClampToEdge;
4109 case QRhiSampler::Mirror:
4110 return MTLSamplerAddressModeMirrorRepeat;
4113 return MTLSamplerAddressModeClampToEdge;
4120 case QRhiSampler::Never:
4121 return MTLCompareFunctionNever;
4122 case QRhiSampler::Less:
4123 return MTLCompareFunctionLess;
4124 case QRhiSampler::Equal:
4125 return MTLCompareFunctionEqual;
4126 case QRhiSampler::LessOrEqual:
4127 return MTLCompareFunctionLessEqual;
4128 case QRhiSampler::Greater:
4129 return MTLCompareFunctionGreater;
4130 case QRhiSampler::NotEqual:
4131 return MTLCompareFunctionNotEqual;
4132 case QRhiSampler::GreaterOrEqual:
4133 return MTLCompareFunctionGreaterEqual;
4134 case QRhiSampler::Always:
4135 return MTLCompareFunctionAlways;
4138 return MTLCompareFunctionNever;
4144 if (
d->samplerState)
4147 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4148 desc.minFilter = toMetalFilter(m_minFilter);
4149 desc.magFilter = toMetalFilter(m_magFilter);
4150 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4151 desc.sAddressMode = toMetalAddressMode(m_addressU);
4152 desc.tAddressMode = toMetalAddressMode(m_addressV);
4153 desc.rAddressMode = toMetalAddressMode(m_addressW);
4154 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4157 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4162 rhiD->registerResource(
this);
4187 e.shadingRateMap.rateMap =
d->rateMap;
4192 rhiD
->d->releaseQueue.append(e);
4193 rhiD->unregisterResource(
this);
4202 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4206 [d->rateMap retain];
4211 rhiD->registerResource(
this);
4220 serializedFormatData.reserve(16);
4232 rhiD->unregisterResource(
this);
4266 serializedFormatData.clear();
4267 auto p =
std::back_inserter(serializedFormatData);
4289 rhiD->registerResource(rpD,
false);
4295 return serializedFormatData;
4317 return d->pixelSize;
4331 const QRhiTextureRenderTargetDescription &desc,
4348 rhiD->unregisterResource(
this);
4353 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4356 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4358 for (
int i = 0; i < colorAttachmentCount; ++i) {
4359 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4365 if (m_desc.depthTexture())
4366 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4367 else if (m_desc.depthStencilBuffer())
4368 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4370 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4375 rhiD->registerResource(rpD,
false);
4382 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4383 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4384 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4388 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4392 Q_ASSERT(texD || rbD);
4393 id<MTLTexture> dst = nil;
4397 if (attIndex == 0) {
4398 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4401 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4404 if (attIndex == 0) {
4405 d->pixelSize = rbD->pixelSize();
4412 colorAtt
.slice = is3D ? it->layer() : 0;
4413 colorAtt
.level = it->level();
4415 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4418 d->fb.colorAtt[attIndex] = colorAtt;
4422 if (hasDepthStencil) {
4423 if (m_desc.depthTexture()) {
4425 d->fb.dsTex = depthTexD
->d->tex;
4426 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4427 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4428 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4430 d->pixelSize = depthTexD->pixelSize();
4435 d->fb.dsTex = depthRbD
->d->tex;
4436 d->fb.hasStencil =
true;
4437 d->fb.depthNeedsStore =
false;
4438 d->fb.preserveDs =
false;
4440 d->pixelSize = depthRbD->pixelSize();
4444 if (m_desc.depthResolveTexture()) {
4446 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4453 if (d->colorAttCount > 0)
4454 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4456 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4458 rhiD->registerResource(
this,
false);
4464 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4467 return d->pixelSize;
4492 sortedBindings.clear();
4497 rhiD->unregisterResource(
this);
4502 if (!sortedBindings.isEmpty())
4506 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4509 rhiD->updateLayoutDesc(
this);
4511 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4512 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4513 if (!sortedBindings.isEmpty())
4514 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4518 boundResourceData.resize(sortedBindings.count());
4520 for (BoundResourceData &bd : boundResourceData)
4521 memset(&bd, 0,
sizeof(BoundResourceData));
4524 rhiD->registerResource(
this,
false);
4530 sortedBindings.clear();
4531 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4532 if (!flags.testFlag(BindingsAreSorted))
4533 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4535 for (BoundResourceData &bd : boundResourceData)
4536 memset(&bd, 0,
sizeof(BoundResourceData));
4560 d->tess.compVs[0].destroy();
4561 d->tess.compVs[1].destroy();
4562 d->tess.compVs[2].destroy();
4564 d->tess.compTesc.destroy();
4565 d->tess.vertTese.destroy();
4567 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4568 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4569 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4570 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4575 if (!
d->ps && !
d->ds
4576 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4577 && !
d->tess.tessControlComputeState)
4585 e.graphicsPipeline.pipelineState =
d->ps;
4586 e.graphicsPipeline.depthStencilState =
d->ds;
4587 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4588 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4591 d->tess.vertexComputeState = {};
4592 d->tess.tessControlComputeState = nil;
4596 rhiD
->d->releaseQueue.append(e);
4597 rhiD->unregisterResource(
this);
4604 case QRhiVertexInputAttribute::Float4:
4605 return MTLVertexFormatFloat4;
4606 case QRhiVertexInputAttribute::Float3:
4607 return MTLVertexFormatFloat3;
4608 case QRhiVertexInputAttribute::Float2:
4609 return MTLVertexFormatFloat2;
4610 case QRhiVertexInputAttribute::Float:
4611 return MTLVertexFormatFloat;
4612 case QRhiVertexInputAttribute::UNormByte4:
4613 return MTLVertexFormatUChar4Normalized;
4614 case QRhiVertexInputAttribute::UNormByte2:
4615 return MTLVertexFormatUChar2Normalized;
4616 case QRhiVertexInputAttribute::UNormByte:
4617 return MTLVertexFormatUCharNormalized;
4618 case QRhiVertexInputAttribute::UInt4:
4619 return MTLVertexFormatUInt4;
4620 case QRhiVertexInputAttribute::UInt3:
4621 return MTLVertexFormatUInt3;
4622 case QRhiVertexInputAttribute::UInt2:
4623 return MTLVertexFormatUInt2;
4624 case QRhiVertexInputAttribute::UInt:
4625 return MTLVertexFormatUInt;
4626 case QRhiVertexInputAttribute::SInt4:
4627 return MTLVertexFormatInt4;
4628 case QRhiVertexInputAttribute::SInt3:
4629 return MTLVertexFormatInt3;
4630 case QRhiVertexInputAttribute::SInt2:
4631 return MTLVertexFormatInt2;
4632 case QRhiVertexInputAttribute::SInt:
4633 return MTLVertexFormatInt;
4634 case QRhiVertexInputAttribute::Half4:
4635 return MTLVertexFormatHalf4;
4636 case QRhiVertexInputAttribute::Half3:
4637 return MTLVertexFormatHalf3;
4638 case QRhiVertexInputAttribute::Half2:
4639 return MTLVertexFormatHalf2;
4640 case QRhiVertexInputAttribute::Half:
4641 return MTLVertexFormatHalf;
4642 case QRhiVertexInputAttribute::UShort4:
4643 return MTLVertexFormatUShort4;
4644 case QRhiVertexInputAttribute::UShort3:
4645 return MTLVertexFormatUShort3;
4646 case QRhiVertexInputAttribute::UShort2:
4647 return MTLVertexFormatUShort2;
4648 case QRhiVertexInputAttribute::UShort:
4649 return MTLVertexFormatUShort;
4650 case QRhiVertexInputAttribute::SShort4:
4651 return MTLVertexFormatShort4;
4652 case QRhiVertexInputAttribute::SShort3:
4653 return MTLVertexFormatShort3;
4654 case QRhiVertexInputAttribute::SShort2:
4655 return MTLVertexFormatShort2;
4656 case QRhiVertexInputAttribute::SShort:
4657 return MTLVertexFormatShort;
4660 return MTLVertexFormatFloat4;
4667 case QRhiGraphicsPipeline::Zero:
4668 return MTLBlendFactorZero;
4669 case QRhiGraphicsPipeline::One:
4670 return MTLBlendFactorOne;
4671 case QRhiGraphicsPipeline::SrcColor:
4672 return MTLBlendFactorSourceColor;
4673 case QRhiGraphicsPipeline::OneMinusSrcColor:
4674 return MTLBlendFactorOneMinusSourceColor;
4675 case QRhiGraphicsPipeline::DstColor:
4676 return MTLBlendFactorDestinationColor;
4677 case QRhiGraphicsPipeline::OneMinusDstColor:
4678 return MTLBlendFactorOneMinusDestinationColor;
4679 case QRhiGraphicsPipeline::SrcAlpha:
4680 return MTLBlendFactorSourceAlpha;
4681 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4682 return MTLBlendFactorOneMinusSourceAlpha;
4683 case QRhiGraphicsPipeline::DstAlpha:
4684 return MTLBlendFactorDestinationAlpha;
4685 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4686 return MTLBlendFactorOneMinusDestinationAlpha;
4687 case QRhiGraphicsPipeline::ConstantColor:
4688 return MTLBlendFactorBlendColor;
4689 case QRhiGraphicsPipeline::ConstantAlpha:
4690 return MTLBlendFactorBlendAlpha;
4691 case QRhiGraphicsPipeline::OneMinusConstantColor:
4692 return MTLBlendFactorOneMinusBlendColor;
4693 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4694 return MTLBlendFactorOneMinusBlendAlpha;
4695 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4696 return MTLBlendFactorSourceAlphaSaturated;
4697 case QRhiGraphicsPipeline::Src1Color:
4698 return MTLBlendFactorSource1Color;
4699 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4700 return MTLBlendFactorOneMinusSource1Color;
4701 case QRhiGraphicsPipeline::Src1Alpha:
4702 return MTLBlendFactorSource1Alpha;
4703 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4704 return MTLBlendFactorOneMinusSource1Alpha;
4707 return MTLBlendFactorZero;
4714 case QRhiGraphicsPipeline::Add:
4715 return MTLBlendOperationAdd;
4716 case QRhiGraphicsPipeline::Subtract:
4717 return MTLBlendOperationSubtract;
4718 case QRhiGraphicsPipeline::ReverseSubtract:
4719 return MTLBlendOperationReverseSubtract;
4720 case QRhiGraphicsPipeline::Min:
4721 return MTLBlendOperationMin;
4722 case QRhiGraphicsPipeline::Max:
4723 return MTLBlendOperationMax;
4726 return MTLBlendOperationAdd;
4733 if (c.testFlag(QRhiGraphicsPipeline::R))
4734 f |= MTLColorWriteMaskRed;
4735 if (c.testFlag(QRhiGraphicsPipeline::G))
4736 f |= MTLColorWriteMaskGreen;
4737 if (c.testFlag(QRhiGraphicsPipeline::B))
4738 f |= MTLColorWriteMaskBlue;
4739 if (c.testFlag(QRhiGraphicsPipeline::A))
4740 f |= MTLColorWriteMaskAlpha;
4747 case QRhiGraphicsPipeline::Never:
4748 return MTLCompareFunctionNever;
4749 case QRhiGraphicsPipeline::Less:
4750 return MTLCompareFunctionLess;
4751 case QRhiGraphicsPipeline::Equal:
4752 return MTLCompareFunctionEqual;
4753 case QRhiGraphicsPipeline::LessOrEqual:
4754 return MTLCompareFunctionLessEqual;
4755 case QRhiGraphicsPipeline::Greater:
4756 return MTLCompareFunctionGreater;
4757 case QRhiGraphicsPipeline::NotEqual:
4758 return MTLCompareFunctionNotEqual;
4759 case QRhiGraphicsPipeline::GreaterOrEqual:
4760 return MTLCompareFunctionGreaterEqual;
4761 case QRhiGraphicsPipeline::Always:
4762 return MTLCompareFunctionAlways;
4765 return MTLCompareFunctionAlways;
4772 case QRhiGraphicsPipeline::StencilZero:
4773 return MTLStencilOperationZero;
4774 case QRhiGraphicsPipeline::Keep:
4775 return MTLStencilOperationKeep;
4776 case QRhiGraphicsPipeline::Replace:
4777 return MTLStencilOperationReplace;
4778 case QRhiGraphicsPipeline::IncrementAndClamp:
4779 return MTLStencilOperationIncrementClamp;
4780 case QRhiGraphicsPipeline::DecrementAndClamp:
4781 return MTLStencilOperationDecrementClamp;
4782 case QRhiGraphicsPipeline::Invert:
4783 return MTLStencilOperationInvert;
4784 case QRhiGraphicsPipeline::IncrementAndWrap:
4785 return MTLStencilOperationIncrementWrap;
4786 case QRhiGraphicsPipeline::DecrementAndWrap:
4787 return MTLStencilOperationDecrementWrap;
4790 return MTLStencilOperationKeep;
4797 case QRhiGraphicsPipeline::Triangles:
4798 return MTLPrimitiveTypeTriangle;
4799 case QRhiGraphicsPipeline::TriangleStrip:
4800 return MTLPrimitiveTypeTriangleStrip;
4801 case QRhiGraphicsPipeline::Lines:
4802 return MTLPrimitiveTypeLine;
4803 case QRhiGraphicsPipeline::LineStrip:
4804 return MTLPrimitiveTypeLineStrip;
4805 case QRhiGraphicsPipeline::Points:
4806 return MTLPrimitiveTypePoint;
4809 return MTLPrimitiveTypeTriangle;
4816 case QRhiGraphicsPipeline::Triangles:
4817 case QRhiGraphicsPipeline::TriangleStrip:
4818 case QRhiGraphicsPipeline::TriangleFan:
4819 return MTLPrimitiveTopologyClassTriangle;
4820 case QRhiGraphicsPipeline::Lines:
4821 case QRhiGraphicsPipeline::LineStrip:
4822 return MTLPrimitiveTopologyClassLine;
4823 case QRhiGraphicsPipeline::Points:
4824 return MTLPrimitiveTopologyClassPoint;
4827 return MTLPrimitiveTopologyClassTriangle;
4834 case QRhiGraphicsPipeline::None:
4835 return MTLCullModeNone;
4836 case QRhiGraphicsPipeline::Front:
4837 return MTLCullModeFront;
4838 case QRhiGraphicsPipeline::Back:
4839 return MTLCullModeBack;
4842 return MTLCullModeNone;
4849 case QRhiGraphicsPipeline::Fill:
4850 return MTLTriangleFillModeFill;
4851 case QRhiGraphicsPipeline::Line:
4852 return MTLTriangleFillModeLines;
4855 return MTLTriangleFillModeFill;
4862 case QShaderDescription::CwTessellationWindingOrder:
4863 return MTLWindingClockwise;
4864 case QShaderDescription::CcwTessellationWindingOrder:
4865 return MTLWindingCounterClockwise;
4868 return MTLWindingCounterClockwise;
4875 case QShaderDescription::EqualTessellationPartitioning:
4876 return MTLTessellationPartitionModePow2;
4877 case QShaderDescription::FractionalEvenTessellationPartitioning:
4878 return MTLTessellationPartitionModeFractionalEven;
4879 case QShaderDescription::FractionalOddTessellationPartitioning:
4880 return MTLTessellationPartitionModeFractionalOdd;
4883 return MTLTessellationPartitionModePow2;
4889 int v = version.version();
4890 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
4894 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4896 QVarLengthArray<
int, 8> versions;
4897 if (@available(macOS 13, iOS 16, *))
4899 if (@available(macOS 12, iOS 15, *))
4901 versions << 23 << 22 << 21 << 20 << 12;
4903 const QList<QShaderKey> shaders = shader.availableShaders();
4907 for (
const int &version : versions) {
4908 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4909 if (shaders.contains(key))
4913 QShaderCode mtllib = shader.shader(key);
4914 if (!mtllib.shader().isEmpty()) {
4915 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4916 size_t(mtllib.shader().size()),
4917 dispatch_get_global_queue(0, 0),
4918 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4920 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4921 dispatch_release(data);
4923 *entryPoint = mtllib.entryPoint();
4927 const QString msg = QString::fromNSString(err.localizedDescription);
4928 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
4932 for (
const int &version : versions) {
4933 key = { QShader::Source::MslShader, version, shaderVariant };
4934 if (shaders.contains(key))
4938 QShaderCode mslSource = shader.shader(key);
4939 if (mslSource.shader().isEmpty()) {
4940 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
4944 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4945 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4946 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4948 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4956 const QString msg = QString::fromNSString(err.localizedDescription);
4961 *entryPoint = mslSource.entryPoint();
4968 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
4973 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
4977 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
4978 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
4979 rpDesc.colorAttachments[0].blendingEnabled =
false;
4981 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
4982 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
4984 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
4985 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
4986 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
4987 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
4988 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
4989 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
4990 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
4991 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
4992 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
4993 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
4994 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
5001 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
5002 rpDesc.depthAttachmentPixelFormat = fmt;
5003#if defined(Q_OS_MACOS)
5004 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5006 if (fmt != MTLPixelFormatDepth32Float)
5008 rpDesc.stencilAttachmentPixelFormat = fmt;
5012 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5017 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5019 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5020 dsDesc.depthWriteEnabled = m_depthWrite;
5021 if (m_stencilTest) {
5022 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5023 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5024 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5025 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5026 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5027 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5028 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5030 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5031 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5032 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5033 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5034 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5035 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5036 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5042 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5043 d->cullMode = toMetalCullMode(m_cullMode);
5044 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5045 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5046 d->depthBias =
float(m_depthBias);
5047 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5057 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5060 const uint loc = uint(it->location());
5061 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5062 desc.attributes[loc].offset = NSUInteger(it->offset());
5063 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5065 int bindingIndex = 0;
5066 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5067 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5068 it != itEnd; ++it, ++bindingIndex)
5070 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5071 desc.layouts[layoutIdx].stepFunction =
5072 it->classification() == QRhiVertexInputBinding::PerInstance
5073 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5074 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5075 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5076 desc.layouts[layoutIdx].stepRate *= viewCount;
5077 desc.layouts[layoutIdx].stride = it->stride();
5088 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5091 const uint loc = uint(it->location());
5092 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5093 desc.attributes[loc].offset = NSUInteger(it->offset());
5094 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5096 int bindingIndex = 0;
5097 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5098 it != itEnd; ++it, ++bindingIndex)
5100 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5101 if (desc.indexBufferIndex) {
5102 desc.layouts[layoutIdx].stepFunction =
5103 it->classification() == QRhiVertexInputBinding::PerInstance
5104 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5106 desc.layouts[layoutIdx].stepFunction =
5107 it->classification() == QRhiVertexInputBinding::PerInstance
5108 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5110 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5111 desc.layouts[layoutIdx].stride = it->stride();
5118 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5119 rpDesc.binaryArchives = binArchArray;
5127 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5128 const QString msg = QString::fromNSString(err.localizedDescription);
5129 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5138 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5139 d->setupVertexInputDescriptor(vertexDesc);
5141 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5142 rpDesc.vertexDescriptor = vertexDesc;
5150 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5151 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5152 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5153 switch (shaderStage.type()) {
5154 case QRhiShaderStage::Vertex:
5157 [d->vs.func retain];
5158 rpDesc.vertexFunction = d->vs.func;
5160 case QRhiShaderStage::Fragment:
5163 [d->fs.func retain];
5164 rpDesc.fragmentFunction = d->fs.func;
5170 const QShader shader = shaderStage.shader();
5172 QByteArray entryPoint;
5173 QShaderKey activeKey;
5174 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5175 &error, &entryPoint, &activeKey);
5177 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5180 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5182 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5186 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5188 for (QMetalShader &s : rhiD->d->shaderCache)
5190 rhiD->d->shaderCache.clear();
5192 switch (shaderStage.type()) {
5193 case QRhiShaderStage::Vertex:
5196 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5197 d->vs.desc = shader.description();
5198 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5199 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5201 [d->vs.func retain];
5202 rpDesc.vertexFunction = func;
5204 case QRhiShaderStage::Fragment:
5207 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5208 d->fs.desc = shader.description();
5209 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5210 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5212 [d->fs.func retain];
5213 rpDesc.fragmentFunction = func;
5226 if (m_multiViewCount >= 2)
5227 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5229 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5231 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5232 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5235 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5238 const QString msg = QString::fromNSString(err.localizedDescription);
5239 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5243 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5245 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5248 d->primitiveType = toMetalPrimitiveType(m_topology);
5256 switch (vertexCompVariant) {
5257 case QShader::NonIndexedVertexAsComputeShader:
5259 case QShader::UInt32IndexedVertexAsComputeShader:
5261 case QShader::UInt16IndexedVertexAsComputeShader:
5271 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5272 if (varIndex >= 0 && vertexComputeState[varIndex])
5273 return vertexComputeState[varIndex];
5275 id<MTLFunction> func = nil;
5277 func = compVs[varIndex].func;
5280 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5284 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5285 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5287 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5288 cpDesc.computeFunction = func;
5289 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5290 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5291 if (indexBufferBinding >= 0) {
5292 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5293 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5294 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5295 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5296 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5297 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5300 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5302 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5304 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5305 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5308 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5309 options: MTLPipelineOptionNone
5314 const QString msg = QString::fromNSString(err.localizedDescription);
5315 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5317 vertexComputeState[varIndex] = ps;
5325 if (tessControlComputeState)
5326 return tessControlComputeState;
5328 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5329 cpDesc.computeFunction = compTesc.func;
5331 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5333 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5334 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5337 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5338 options: MTLPipelineOptionNone
5343 const QString msg = QString::fromNSString(err.localizedDescription);
5344 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5346 tessControlComputeState = ps;
5354 return (indices >> index) & 0x1;
5357static inline void takeIndex(quint32 index, quint64 &indices)
5359 indices |= 1 << index;
5368 static const int maxVertexAttributes = 31;
5370 for (
int index = 0; index < maxVertexAttributes; ++index) {
5371 if (!indexTaken(index, indices))
5375 Q_UNREACHABLE_RETURN(-1);
5378static inline int aligned(quint32 offset, quint32 alignment)
5380 return ((offset + alignment - 1) / alignment) * alignment;
5388 for (
const int dim : variable.arrayDims)
5391 if (variable.type == QShaderDescription::VariableType::Struct) {
5392 for (
int element = 0; element < elements; ++element) {
5393 for (
const auto &member : variable.structMembers) {
5394 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5398 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5399 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5402 const quint32 alignment = size;
5403 vertexAlignment =
std::max(vertexAlignment, alignment);
5405 for (
int element = 0; element < elements; ++element) {
5407 offset = aligned(offset, alignment);
5414static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5418 for (
const int dim : variable.arrayDims)
5421 if (variable.type == QShaderDescription::VariableType::Struct) {
5422 for (
int element = 0; element < elements; ++element) {
5423 for (
const auto &member : variable.structMembers) {
5424 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5428 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5429 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5432 const quint32 alignment = size;
5433 vertexAlignment =
std::max(vertexAlignment, alignment);
5435 for (
int element = 0; element < elements; ++element) {
5436 Q_ASSERT(!indexTaken(index, indices));
5439 offset = aligned(offset, alignment);
5441 attributes[index].bufferIndex = binding;
5442 attributes[index].format = toMetalAttributeFormat(format);
5443 attributes[index].offset = offset;
5445 takeIndex(index, indices);
5447 if (indexTaken(index, indices))
5448 index = nextAttributeIndex(indices);
5455static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5457 if (a.size() == b.size()) {
5459 for (
int i = 0; i < a.size() && match; ++i) {
5460 match &= a[i].type == b[i].type
5461 && a[i].arrayDims == b[i].arrayDims
5462 && matches(a[i].structMembers, b[i].structMembers);
5470static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5472 return a.location == b.location
5474 && a.perPatch == b.perPatch
5475 && matches(a.structMembers, b.structMembers);
5524 if (pipeline
->d->ps)
5525 return pipeline
->d->ps;
5527 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5528 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5531 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5532 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5533 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5534 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5535 quint32 offsetInTescOutput = 0;
5536 quint32 offsetInTescPatchOutput = 0;
5537 quint32 offsetInTessFactorBuffer = 0;
5538 quint32 tescOutputAlignment = 0;
5539 quint32 tescPatchOutputAlignment = 0;
5540 quint32 tessFactorAlignment = 0;
5541 QSet<
int> usedBuffers;
5544 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5545 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5546 tescOutVars[tescOutVar.location] = tescOutVar;
5549 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5550 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5551 teseInVars[teseInVar.location] = teseInVar;
5554 quint64 indices = 0;
5556 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5558 int index = tescOutVar.location;
5560 quint32 *offset =
nullptr;
5561 quint32 *alignment =
nullptr;
5563 if (tescOutVar.perPatch) {
5564 binding = tescPatchOutputBufferBinding;
5565 offset = &offsetInTescPatchOutput;
5566 alignment = &tescPatchOutputAlignment;
5568 tescOutVar.arrayDims.removeLast();
5569 binding = tescOutputBufferBinding;
5570 offset = &offsetInTescOutput;
5571 alignment = &tescOutputAlignment;
5574 if (teseInVars.contains(index)) {
5576 if (!matches(teseInVars[index], tescOutVar)) {
5577 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5578 qWarning() <<
" tesc out:" << tescOutVar;
5579 qWarning() <<
" tese in:" << teseInVars[index];
5582 if (binding != -1) {
5583 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5584 usedBuffers << binding;
5586 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5587 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5591 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5592 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5595 teseInVars.remove(tescOutVar.location);
5598 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5599 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5602 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5603 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5604 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5607 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5608 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5609 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5611 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5612 bool tessLevelAdded =
false;
5614 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5616 QShaderDescription::InOutVariable variable;
5618 quint32 *offset =
nullptr;
5619 quint32 *alignment =
nullptr;
5621 switch (builtin.type) {
5622 case QShaderDescription::BuiltinType::PositionBuiltin:
5623 variable.type = QShaderDescription::VariableType::Vec4;
5624 binding = tescOutputBufferBinding;
5625 offset = &offsetInTescOutput;
5626 alignment = &tescOutputAlignment;
5628 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5629 variable.type = QShaderDescription::VariableType::Float;
5630 binding = tescOutputBufferBinding;
5631 offset = &offsetInTescOutput;
5632 alignment = &tescOutputAlignment;
5634 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5635 variable.type = QShaderDescription::VariableType::Float;
5636 variable.arrayDims = builtin.arrayDims;
5637 binding = tescOutputBufferBinding;
5638 offset = &offsetInTescOutput;
5639 alignment = &tescOutputAlignment;
5641 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5642 variable.type = QShaderDescription::VariableType::Half4;
5643 binding = tessFactorBufferBinding;
5644 offset = &offsetInTessFactorBuffer;
5645 tessLevelAdded = trianglesMode;
5646 alignment = &tessFactorAlignment;
5648 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5649 if (trianglesMode) {
5650 if (!tessLevelAdded) {
5651 variable.type = QShaderDescription::VariableType::Half4;
5652 binding = tessFactorBufferBinding;
5653 offsetInTessFactorBuffer = 0;
5654 offset = &offsetInTessFactorBuffer;
5655 alignment = &tessFactorAlignment;
5656 tessLevelAdded =
true;
5658 teseInBuiltins.remove(builtin.type);
5662 variable.type = QShaderDescription::VariableType::Half2;
5663 binding = tessFactorBufferBinding;
5664 offsetInTessFactorBuffer = 8;
5665 offset = &offsetInTessFactorBuffer;
5666 alignment = &tessFactorAlignment;
5674 if (teseInBuiltins.contains(builtin.type)) {
5675 if (binding != -1) {
5676 int index = nextAttributeIndex(indices);
5677 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5678 usedBuffers << binding;
5680 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5681 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5684 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5687 teseInBuiltins.remove(builtin.type);
5690 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5691 switch (builtin.type) {
5692 case QShaderDescription::BuiltinType::PositionBuiltin:
5693 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5694 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5695 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5702 if (usedBuffers.contains(tescOutputBufferBinding)) {
5703 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5704 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5707 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5708 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5709 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5712 if (usedBuffers.contains(tessFactorBufferBinding)) {
5713 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5714 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
5717 rpDesc.vertexDescriptor = vertexDesc;
5718 rpDesc.vertexFunction = vertTese.func;
5719 rpDesc.fragmentFunction = pipeline
->d->fs.func;
5725 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5727 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5732 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5734 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5735 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5738 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5741 const QString msg = QString::fromNSString(err.localizedDescription);
5742 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5746 pipeline->d->ps = ps;
5753 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
5756 for (QMetalBuffer *workBuf : *workBuffers) {
5757 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
5758 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5766 for (QMetalBuffer *workBuf : *workBuffers) {
5767 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
5768 workBuf->setSize(size);
5769 if (workBuf->create()) {
5770 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
5781 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5784 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
5788 workBuffers->append(buf);
5792 qWarning(
"Failed to acquire work buffer of size %u", size);
5800 QByteArray entryPoint;
5801 QShaderKey activeKey;
5803 const QShaderDescription tescDesc = tesc.description();
5804 const QShaderDescription teseDesc = tese.description();
5805 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5806 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5807 if (!
d->tess.outControlPointCount)
5808 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5810 if (!
d->tess.outControlPointCount) {
5811 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5812 d->tess.enabled =
false;
5813 d->tess.failed =
true;
5817 if (m_multiViewCount >= 2)
5818 qWarning(
"Multiview is not supported with tessellation");
5826 bool variantsPresent[3] = {};
5827 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5828 for (
const QShaderKey &k : tessVertKeys) {
5829 switch (k.sourceVariant()) {
5830 case QShader::NonIndexedVertexAsComputeShader:
5831 variantsPresent[0] =
true;
5833 case QShader::UInt32IndexedVertexAsComputeShader:
5834 variantsPresent[1] =
true;
5836 case QShader::UInt16IndexedVertexAsComputeShader:
5837 variantsPresent[2] =
true;
5843 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5844 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5845 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5846 "Try passing --msltess to qsb.");
5847 d->tess.enabled =
false;
5848 d->tess.failed =
true;
5853 for (QShader::Variant variant : {
5854 QShader::NonIndexedVertexAsComputeShader,
5855 QShader::UInt32IndexedVertexAsComputeShader,
5856 QShader::UInt16IndexedVertexAsComputeShader })
5858 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5860 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
5861 d->tess.enabled =
false;
5862 d->tess.failed =
true;
5865 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5867 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5869 d->tess.enabled =
false;
5870 d->tess.failed =
true;
5873 QMetalShader &compVs(d->tess.compVs[varIndex]);
5876 compVs.desc = tessVert.description();
5877 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5878 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5881 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5882 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
5883 d->tess.enabled =
false;
5884 d->tess.failed =
true;
5892 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5893 if (!tessControlLib) {
5894 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5895 d->tess.enabled =
false;
5896 d->tess.failed =
true;
5899 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
5900 if (!tessControlFunc) {
5901 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5902 [tessControlLib release];
5903 d->tess.enabled =
false;
5904 d->tess.failed =
true;
5907 d->tess.compTesc.lib = tessControlLib;
5908 d->tess.compTesc.func = tessControlFunc;
5909 d->tess.compTesc.desc = tesc.description();
5910 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5911 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5912 if (!
d->tess.tescCompPipeline(rhiD)) {
5913 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
5914 d->tess.enabled =
false;
5915 d->tess.failed =
true;
5920 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5922 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5923 d->tess.enabled =
false;
5924 d->tess.failed =
true;
5927 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5928 if (!tessEvalFunc) {
5929 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5930 [tessEvalLib release];
5931 d->tess.enabled =
false;
5932 d->tess.failed =
true;
5935 d->tess.vertTese.lib = tessEvalLib;
5936 d->tess.vertTese.func = tessEvalFunc;
5937 d->tess.vertTese.desc = tese.description();
5938 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5939 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5941 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5943 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5944 d->tess.enabled =
false;
5945 d->tess.failed =
true;
5948 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
5950 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5952 d->tess.enabled =
false;
5953 d->tess.failed =
true;
5956 d->fs.lib = fragLib;
5957 d->fs.func = fragFunc;
5958 d->fs.desc = tessFrag.description();
5959 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5960 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5962 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
5963 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5964 d->tess.enabled =
false;
5965 d->tess.failed =
true;
5969 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5971 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5985 rhiD->pipelineCreationStart();
5986 if (!rhiD->sanityCheckGraphicsPipeline(
this))
5994 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5995 switch (shaderStage.type()) {
5996 case QRhiShaderStage::Vertex:
5997 tessVert = shaderStage.shader();
5999 case QRhiShaderStage::TessellationControl:
6000 tesc = shaderStage.shader();
6002 case QRhiShaderStage::TessellationEvaluation:
6003 tese = shaderStage.shader();
6005 case QRhiShaderStage::Fragment:
6006 tessFrag = shaderStage.shader();
6012 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6013 d->tess.failed =
false;
6015 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6021 QVarLengthArray<QMetalShader *, 6> shaders;
6022 if (
d->tess.enabled) {
6023 shaders.append(&
d->tess.compVs[0]);
6024 shaders.append(&
d->tess.compVs[1]);
6025 shaders.append(&
d->tess.compVs[2]);
6026 shaders.append(&
d->tess.compTesc);
6027 shaders.append(&
d->tess.vertTese);
6029 shaders.append(&
d->vs);
6031 shaders.append(&
d->fs);
6033 for (QMetalShader *shader : shaders) {
6034 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6035 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6036 shader->nativeResourceBindingMap[binding] = {binding, -1};
6037 int maxNativeBinding = 0;
6038 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6039 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6043 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6048 if (!d->bufferSizeBuffer)
6049 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6055 rhiD->pipelineCreationEnd();
6058 rhiD->registerResource(
this);
6087 e.computePipeline.pipelineState =
d->ps;
6092 rhiD
->d->releaseQueue.append(e);
6093 rhiD->unregisterResource(
this);
6100 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6101 cpDesc.binaryArchives = binArchArray;
6109 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6110 const QString msg = QString::fromNSString(err.localizedDescription);
6111 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6122 rhiD->pipelineCreationStart();
6124 auto cacheIt = rhiD
->d->shaderCache.constFind(m_shaderStage);
6125 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6128 const QShader shader = m_shaderStage.shader();
6130 QByteArray entryPoint;
6131 QShaderKey activeKey;
6132 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6133 &error, &entryPoint, &activeKey);
6135 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6138 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6140 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6146 d->cs.localSize = shader.description().computeShaderLocalSize();
6147 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6148 d->cs.desc = shader.description();
6149 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6152 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6153 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6154 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
6157 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6158 for (QMetalShader &s : rhiD->d->shaderCache)
6160 rhiD
->d->shaderCache.clear();
6162 rhiD
->d->shaderCache.insert(m_shaderStage,
d->cs);
6166 [d->cs.func retain];
6168 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6170 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6171 cpDesc.computeFunction =
d->cs.func;
6173 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6175 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6176 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6179 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6180 options: MTLPipelineOptionNone
6185 const QString msg = QString::fromNSString(err.localizedDescription);
6186 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6191 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6193 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6194 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6198 if (!d->bufferSizeBuffer)
6199 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6205 rhiD->pipelineCreationEnd();
6208 rhiD->registerResource(
this);
6232 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6233 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6234 return &nativeHandlesStruct;
6240 d->currentRenderPassEncoder = nil;
6241 d->currentComputePassEncoder = nil;
6242 d->tessellationComputeEncoder = nil;
6243 d->currentPassRpDesc = nil;
6250 currentTarget =
nullptr;
6258 currentPipelineGeneration = 0;
6261 currentSrbGeneration = 0;
6264 currentIndexOffset = 0;
6265 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6270 currentDepthBiasValues = { 0.0f, 0.0f };
6272 d->currentShaderResourceBindingState = {};
6273 d->currentDepthStencilState = nil;
6275 d->currentVertexInputsBuffers.clear();
6276 d->currentVertexInputOffsets.clear();
6286 d->sem[i] =
nullptr;
6287 d->msaaTex[i] = nil;
6307 dispatch_release(
d->sem[i]);
6308 d->sem[i] =
nullptr;
6313 [d->msaaTex[i] release];
6314 d->msaaTex[i] = nil;
6320 [d->curDrawable release];
6321 d->curDrawable = nil;
6325 rhiD->swapchains.remove(
this);
6326 rhiD->unregisterResource(
this);
6346 CALayer *layer =
nullptr;
6348 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6349 layer = cocoaWindow->contentLayer();
6351 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6354 return static_cast<CAMetalLayer *>(layer);
6363 d.reserved[0] = layerForWindow(window);
6370 CAMetalLayer *layer =
d->layer;
6372 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6375 int height = (
int)layer.bounds.size.height;
6376 int width = (
int)layer.bounds.size.width;
6377 width *= layer.contentsScale;
6378 height *= layer.contentsScale;
6379 return QSize(width, height);
6384 if (f == HDRExtendedSrgbLinear) {
6385 if (@available(iOS 16.0, *))
6389 }
else if (f == HDR10) {
6390 if (@available(iOS 16.0, *))
6394 }
else if (f == HDRExtendedDisplayP3Linear) {
6408 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6414 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6415 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6417 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6420 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6424 rhiD->registerResource(rpD,
false);
6431 samples = rhiD->effectiveSampleCount(m_sampleCount);
6433 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6434 d->colorFormat = MTLPixelFormatRGBA16Float;
6435 d->rhiColorFormat = QRhiTexture::RGBA16F;
6438 if (m_format == HDR10) {
6439 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6440 d->rhiColorFormat = QRhiTexture::RGB10A2;
6443 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6444 d->rhiColorFormat = QRhiTexture::BGRA8;
6453 dispatch_semaphore_t sem =
d->sem[slot];
6454 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6455 dispatch_semaphore_signal(sem);
6462 const bool needsRegistration = !window || window != m_window;
6464 if (window && window != m_window)
6469 if (needsRegistration || !rhiD->swapchains.contains(
this))
6470 rhiD->swapchains.insert(
this);
6474 if (window->surfaceType() != QSurface::MetalSurface) {
6475 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6479 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6483 if (
d->colorFormat !=
d->layer.pixelFormat)
6484 d->layer.pixelFormat =
d->colorFormat;
6486 if (m_format == HDRExtendedSrgbLinear) {
6487 if (@available(iOS 16.0, *)) {
6488 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6489 d->layer.wantsExtendedDynamicRangeContent = YES;
6491 }
else if (m_format == HDR10) {
6492 if (@available(iOS 16.0, *)) {
6493 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6494 d->layer.wantsExtendedDynamicRangeContent = YES;
6496 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6497 if (@available(iOS 16.0, *)) {
6498 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6499 d->layer.wantsExtendedDynamicRangeContent = YES;
6503 if (m_flags.testFlag(UsedAsTransferSource))
6504 d->layer.framebufferOnly = NO;
6507 if (m_flags.testFlag(NoVSync))
6508 d->layer.displaySyncEnabled = NO;
6511 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6512 d->layer.opaque = NO;
6513 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6518 d->layer.opaque = NO;
6520 d->layer.opaque = YES;
6526 int width = (
int)
d->layer.bounds.size.width;
6527 int height = (
int)
d->layer.bounds.size.height;
6528 CGSize layerSize = CGSizeMake(width, height);
6529 const float scaleFactor =
d->layer.contentsScale;
6530 layerSize.width *= scaleFactor;
6531 layerSize.height *= scaleFactor;
6532 d->layer.drawableSize = layerSize;
6534 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6535 pixelSize = m_currentPixelSize;
6537 [d->layer setDevice: rhiD->d->dev];
6539 [d->curDrawable release];
6540 d->curDrawable = nil;
6551 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6552 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6553 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6554 m_depthStencil->sampleCount(), m_sampleCount);
6556 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6557 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6558 m_depthStencil->setPixelSize(pixelSize);
6559 if (!m_depthStencil->create())
6560 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6561 pixelSize.width(), pixelSize.height());
6563 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6564 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6565 pixelSize.width(), pixelSize.height());
6569 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6570 rtWrapper.d->pixelSize = pixelSize;
6576 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6577 pixelSize.width(), pixelSize.height(), scaleFactor);
6580 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6581 desc.textureType = MTLTextureType2DMultisample;
6582 desc.pixelFormat =
d->colorFormat;
6583 desc.width = NSUInteger(pixelSize.width());
6584 desc.height = NSUInteger(pixelSize.height());
6585 desc.sampleCount = NSUInteger(
samples);
6586 desc.resourceOptions = MTLResourceStorageModePrivate;
6587 desc.storageMode = MTLStorageModePrivate;
6588 desc.usage = MTLTextureUsageRenderTarget;
6590 if (
d->msaaTex[i]) {
6594 e.renderbuffer.texture =
d->msaaTex[i];
6595 rhiD
->d->releaseQueue.append(e);
6597 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6602 rhiD->registerResource(
this);
6618#if defined(Q_OS_MACOS)
6619 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6620 NSScreen *screen = view.window.screen;
6621 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6622 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6623#elif defined(Q_OS_IOS)
6624 if (@available(iOS 16.0, *)) {
6625 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6626 UIScreen *screen = view.window.windowScene.screen;
6627 info.limits.colorComponentValue.maxColorComponentValue = view.window.windowScene.screen.currentEDRHeadroom;
6628 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