8#include <QGuiApplication>
12#include <QTemporaryFile>
15#include <QOperatingSystemVersion>
17#include <QtCore/private/qcore_mac_p.h>
18#include <QtGui/private/qmetallayer_p.h>
19#include <QtGui/qpa/qplatformwindow_p.h>
22#include <AppKit/AppKit.h>
24#include <UIKit/UIKit.h>
27#include <QuartzCore/CATransaction.h>
29#include <Metal/Metal.h>
36
37
38
39
40
41
42
43
44
47#error ARC not supported
56#define QRHI_METAL_DISABLE_BINARY_ARCHIVE
61#define QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
104
105
106
107
108
109
110
111
114
115
116
117
120
121
122
123
124
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
148
149
152
153
157 id<MTLLibrary> lib = nil;
158 id<MTLFunction> func = nil;
159 std::array<uint, 3> localSize = {};
160 uint outputVertexCount = 0;
161 QShaderDescription desc;
162 QShader::NativeResourceBindingMap nativeResourceBindingMap;
163 QShader::NativeShaderInfo nativeShaderInfo;
166 nativeResourceBindingMap.clear();
185 const QColor &colorClearValue,
186 const QRhiDepthStencilClearValue &depthStencilClearValue,
188 QRhiShadingRateMap *shadingRateMap);
190 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
220 id<MTLTexture> texture;
452 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
461 return patchCount *
sizeof(
float) * 128;
509 if (importDevice->dev) {
510 d->dev = (id<MTLDevice>) importDevice->dev;
512 if (importedCmdQueue)
513 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
515 qWarning(
"No MTLDevice given, cannot import");
529 return (v + byteAlign - 1) & ~(byteAlign - 1);
534 QMacAutoReleasePool pool;
537 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
551 return [cmdQueue commandBufferWithUnretainedReferences];
553 return [cmdQueue commandBuffer];
564 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
565 binArchDesc.url = sourceFileUrl;
567 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
568 [binArchDesc release];
570 const QString msg = QString::fromNSString(err.localizedDescription);
571 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
584 d->dev = MTLCreateSystemDefaultDevice();
587 qWarning(
"No MTLDevice");
591 const QString deviceName = QString::fromNSString([d->dev name]);
592 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
593 driverInfoStruct.deviceName = deviceName.toUtf8();
600 const MTLDeviceLocation deviceLocation = [d->dev location];
601 switch (deviceLocation) {
602 case MTLDeviceLocationBuiltIn:
603 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
605 case MTLDeviceLocationSlot:
606 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
608 case MTLDeviceLocationExternal:
609 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
615 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
618 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
619 osMajor = ver.majorVersion();
620 osMinor = ver.minorVersion();
622 if (importedCmdQueue)
623 [d->cmdQueue retain];
625 d->cmdQueue = [d->dev newCommandQueue];
627 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
631 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
632 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
633 d->captureScope.label = label.toNSString();
635#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
636 caps.maxTextureSize = 16384;
637 caps.baseVertexAndInstance =
true;
638 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
639 caps.maxThreadGroupSize = 1024;
640 caps.multiView =
true;
641#elif defined(Q_OS_TVOS)
642 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
643 caps.maxTextureSize = 16384;
645 caps.maxTextureSize = 8192;
646 caps.baseVertexAndInstance =
false;
647 caps.isAppleGPU =
true;
648#elif defined(Q_OS_IOS)
649 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
650 caps.maxTextureSize = 16384;
651 caps.baseVertexAndInstance =
true;
652 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
653 caps.maxTextureSize = 8192;
654 caps.baseVertexAndInstance =
false;
656 caps.maxTextureSize = 4096;
657 caps.baseVertexAndInstance =
false;
659 caps.isAppleGPU =
true;
660 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
661 caps.maxThreadGroupSize = 1024;
662 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
663 caps.multiView =
true;
666 caps.supportedSampleCounts = { 1 };
667 for (
int sampleCount : { 2, 4, 8 }) {
668 if ([d->dev supportsTextureSampleCount: sampleCount])
669 caps.supportedSampleCounts.append(sampleCount);
672 caps.indirectCommandBuffers = ([d->dev supportsFamily:MTLGPUFamilyApple5]
673 || [d->dev supportsFamily:MTLGPUFamilyMac2])
674 && [d->dev supportsFamily:MTLGPUFamilyMetal3];
676 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
677 if (caps.shadingRateMap && caps.multiView)
678 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
681 caps.depthClamp = [d->dev supportsFamily:MTLGPUFamilyApple3];
683 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
684 d->setupBinaryArchive();
686 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
687 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
697 for (QMetalShader &s : d->shaderCache)
699 d->shaderCache.clear();
701 [d->captureScope release];
702 d->captureScope = nil;
704 [d->icbArgumentBuffer release];
705 d->icbArgumentBuffer = nil;
707 [d->icbEncodeFunctionU32 release];
708 d->icbEncodeFunctionU32 = nil;
710 [d->icbEncodeFunctionU16 release];
711 d->icbEncodeFunctionU16 = nil;
713 [d->icbEncodePipelineU32 release];
714 d->icbEncodePipelineU32 = nil;
716 [d->icbEncodePipelineU16 release];
717 d->icbEncodePipelineU16 = nil;
724 [d->binArch release];
727 [d->cmdQueue release];
728 if (!importedCmdQueue)
738 return caps.supportedSampleCounts;
743 Q_UNUSED(sampleCount);
744 return { QSize(1, 1) };
749 return new QMetalSwapChain(
this);
752QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
754 return new QMetalBuffer(
this, type, usage, size);
781 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
782 0.0f, 1.0f, 0.0f, 0.0f,
783 0.0f, 0.0f, 0.5f, 0.5f,
784 0.0f, 0.0f, 0.0f, 1.0f);
792 bool supportsFamilyMac2 =
false;
793 bool supportsFamilyApple3 =
false;
796 supportsFamilyMac2 =
true;
798 supportsFamilyApple3 =
true;
800 supportsFamilyApple3 =
true;
804 if (format == QRhiTexture::BC5)
807 if (!supportsFamilyApple3) {
808 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
810 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
814 if (!supportsFamilyMac2)
815 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
824 case QRhi::MultisampleTexture:
826 case QRhi::MultisampleRenderBuffer:
828 case QRhi::DebugMarkers:
830 case QRhi::Timestamps:
832 case QRhi::Instancing:
834 case QRhi::CustomInstanceStepRate:
836 case QRhi::PrimitiveRestart:
838 case QRhi::NonDynamicUniformBuffers:
840 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
842 case QRhi::NPOTTextureRepeat:
844 case QRhi::RedOrAlpha8IsRed:
846 case QRhi::ElementIndexUint:
850 case QRhi::WideLines:
852 case QRhi::VertexShaderPointSize:
854 case QRhi::BaseVertex:
855 return caps.baseVertexAndInstance;
856 case QRhi::BaseInstance:
857 return caps.baseVertexAndInstance;
858 case QRhi::TriangleFanTopology:
860 case QRhi::ReadBackNonUniformBuffer:
862 case QRhi::ReadBackNonBaseMipLevel:
864 case QRhi::TexelFetch:
866 case QRhi::RenderToNonBaseMipLevel:
868 case QRhi::IntAttributes:
870 case QRhi::ScreenSpaceDerivatives:
872 case QRhi::ReadBackAnyTextureFormat:
874 case QRhi::PipelineCacheDataLoadSave:
876 case QRhi::ImageDataStride:
878 case QRhi::RenderBufferImport:
880 case QRhi::ThreeDimensionalTextures:
882 case QRhi::RenderTo3DTextureSlice:
884 case QRhi::TextureArrays:
886 case QRhi::Tessellation:
888 case QRhi::GeometryShader:
890 case QRhi::TextureArrayRange:
892 case QRhi::NonFillPolygonMode:
894 case QRhi::OneDimensionalTextures:
896 case QRhi::OneDimensionalTextureMipmaps:
898 case QRhi::HalfAttributes:
900 case QRhi::RenderToOneDimensionalTexture:
902 case QRhi::ThreeDimensionalTextureMipmaps:
904 case QRhi::MultiView:
905 return caps.multiView;
906 case QRhi::TextureViewFormat:
908 case QRhi::ResolveDepthStencil:
910 case QRhi::VariableRateShading:
912 case QRhi::VariableRateShadingMap:
913 return caps.shadingRateMap;
914 case QRhi::VariableRateShadingMapWithTexture:
916 case QRhi::PerRenderTargetBlending:
917 case QRhi::SampleVariables:
919 case QRhi::InstanceIndexIncludesBaseInstance:
921 case QRhi::DepthClamp:
922 return caps.depthClamp;
923 case QRhi::DrawIndirect:
925 case QRhi::DrawIndirectMulti:
926 case QRhi::ShaderDrawParameters:
937 case QRhi::TextureSizeMin:
939 case QRhi::TextureSizeMax:
940 return caps.maxTextureSize;
941 case QRhi::MaxColorAttachments:
943 case QRhi::FramesInFlight:
945 case QRhi::MaxAsyncReadbackFrames:
947 case QRhi::MaxThreadGroupsPerDimension:
949 case QRhi::MaxThreadsPerThreadGroup:
951 case QRhi::MaxThreadGroupX:
953 case QRhi::MaxThreadGroupY:
955 case QRhi::MaxThreadGroupZ:
956 return caps.maxThreadGroupSize;
957 case QRhi::TextureArraySizeMax:
959 case QRhi::MaxUniformBufferRange:
961 case QRhi::MaxVertexInputs:
963 case QRhi::MaxVertexOutputs:
965 case QRhi::ShadingRateImageTileSize:
975 return &nativeHandlesStruct;
980 return driverInfoStruct;
986 result.totalPipelineCreationTime = totalPipelineCreationTime();
996void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
1003 for (QMetalShader &s : d->shaderCache)
1006 d->shaderCache.clear();
1028 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1033 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1038 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1039 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1041 if (![d->binArch serializeToURL: url error: &err]) {
1042 const QString msg = QString::fromNSString(err.localizedDescription);
1044 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1049 if (!f.open(QIODevice::ReadOnly)) {
1050 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1053 const QByteArray blob = f.readAll();
1057 const quint32 dataSize = quint32(blob.size());
1059 data.resize(headerSize + dataSize);
1062 header.rhiId = pipelineCacheRhiId();
1063 header.arch = quint32(
sizeof(
void*));
1064 header.dataSize = quint32(dataSize);
1065 header.osMajor = osMajor;
1066 header.osMinor = osMinor;
1067 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1069 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1070 header.driver[driverStrLen] =
'\0';
1072 memcpy(data.data(), &header, headerSize);
1073 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1083 if (data.size() < qsizetype(headerSize)) {
1084 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1088 const size_t dataOffset = headerSize;
1090 memcpy(&header, data.constData(), headerSize);
1092 const quint32 rhiId = pipelineCacheRhiId();
1093 if (header.rhiId != rhiId) {
1094 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1095 rhiId, header.rhiId);
1099 const quint32 arch = quint32(
sizeof(
void*));
1100 if (header.arch != arch) {
1101 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1106 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1107 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1108 osMajor, osMinor, header.osMajor, header.osMinor);
1112 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1113 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1114 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1118 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
1119 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1123 const char *p = data.constData() + dataOffset;
1127 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1130 tmp.write(p, header.dataSize);
1133 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1134 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1135 if (
d->setupBinaryArchive(url))
1136 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1139QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1140 int sampleCount, QRhiRenderBuffer::Flags flags,
1141 QRhiTexture::Format backingFormatHint)
1143 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1147 const QSize &pixelSize,
int depth,
int arraySize,
1148 int sampleCount, QRhiTexture::Flags flags)
1150 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1154 QRhiSampler::Filter mipmapMode,
1155 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1157 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1162 return new QMetalShadingRateMap(
this);
1166 QRhiTextureRenderTarget::Flags flags)
1173 return new QMetalGraphicsPipeline(
this);
1178 return new QMetalComputePipeline(
this);
1183 return new QMetalShaderResourceBindings(
this);
1194 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1197 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1198 if (!map || map->isEmpty())
1201 auto it = map->constFind(binding);
1202 if (it != map->cend())
1213 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1214 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1217 case QMetalShaderResourceBindingsData::VERTEX:
1218 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1219 offsets: offsetBatch.resources.constData()
1220 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1222 case QMetalShaderResourceBindingsData::FRAGMENT:
1223 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1224 offsets: offsetBatch.resources.constData()
1225 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1227 case QMetalShaderResourceBindingsData::COMPUTE:
1228 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1229 offsets: offsetBatch.resources.constData()
1230 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1244 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1247 case QMetalShaderResourceBindingsData::VERTEX:
1248 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1249 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1251 case QMetalShaderResourceBindingsData::FRAGMENT:
1252 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1253 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1255 case QMetalShaderResourceBindingsData::COMPUTE:
1256 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1257 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1271 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1273 switch (encoderStage) {
1274 case QMetalShaderResourceBindingsData::VERTEX:
1275 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1276 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1278 case QMetalShaderResourceBindingsData::FRAGMENT:
1279 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1280 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1282 case QMetalShaderResourceBindingsData::COMPUTE:
1283 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1284 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1306 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1307 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1308 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1309 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1312 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1313 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1314 bindStageTextures(cbD, encoderStage, batch);
1317 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1318 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1319 bindStageSamplers(cbD, encoderStage, batch);
1326 case QMetalShaderResourceBindingsData::VERTEX:
1327 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1328 case QMetalShaderResourceBindingsData::TESSCTRL:
1329 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1330 case QMetalShaderResourceBindingsData::TESSEVAL:
1331 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1332 case QMetalShaderResourceBindingsData::FRAGMENT:
1333 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1334 case QMetalShaderResourceBindingsData::COMPUTE:
1335 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1338 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1343 int dynamicOffsetCount,
1344 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1345 bool offsetOnlyChange,
1346 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1350 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1351 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1353 case QRhiShaderResourceBinding::UniformBuffer:
1355 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1356 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1357 quint32 offset = b->u.ubuf.offset;
1358 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1359 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1360 if (dynOfs.first == b->binding) {
1361 offset = dynOfs.second;
1366 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1367 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1368 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1369 if (nativeBinding >= 0)
1370 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1375 case QRhiShaderResourceBinding::SampledTexture:
1376 case QRhiShaderResourceBinding::Texture:
1377 case QRhiShaderResourceBinding::Sampler:
1379 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1380 for (
int elem = 0; elem < data->count; ++elem) {
1381 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1382 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1384 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1385 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1390 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1391 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1392 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1393 if (textureBinding >= 0 && texD)
1394 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1395 if (samplerBinding >= 0)
1396 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1402 case QRhiShaderResourceBinding::ImageLoad:
1403 case QRhiShaderResourceBinding::ImageStore:
1404 case QRhiShaderResourceBinding::ImageLoadStore:
1406 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1407 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1409 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1410 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1411 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1412 if (nativeBinding >= 0)
1413 bindingData.res[stage].textures.append({ nativeBinding, t });
1418 case QRhiShaderResourceBinding::BufferLoad:
1419 case QRhiShaderResourceBinding::BufferStore:
1420 case QRhiShaderResourceBinding::BufferLoadStore:
1422 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1423 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1424 quint32 offset = b->u.sbuf.offset;
1425 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1426 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1427 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1428 if (nativeBinding >= 0)
1429 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1452 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1453 return a.nativeBinding < b.nativeBinding;
1456 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1457 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1458 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1461 bindingData.res[stage].bufferBatches.finish();
1462 bindingData.res[stage].bufferOffsetBatches.finish();
1464 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1465 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1466 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1468 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1469 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1470 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1471 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1475 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1478 if (offsetOnlyChange)
1481 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1482 return a.nativeBinding < b.nativeBinding;
1485 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1486 return a.nativeBinding < b.nativeBinding;
1489 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1490 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1492 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1493 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1495 bindingData.res[stage].textureBatches.finish();
1496 bindingData.res[stage].samplerBatches.finish();
1498 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1499 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1501 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1502 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1506 bindStageTextures(cbD, stage, batch);
1509 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1510 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1512 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1513 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1517 bindStageSamplers(cbD, stage, batch);
1521 cbD
->d->currentShaderResourceBindingState = bindingData;
1528 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1530 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1531 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1532 cbD
->d->currentDepthStencilState =
d->ds;
1535 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1539 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1542 if (rhiD->caps.depthClamp) {
1544 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1549 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1552 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1555 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1556 slopeScale: d->slopeScaledDepthBias
1573 cbD->currentPipelineGeneration = psD->generation;
1578 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1583 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1584 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1585 workBuf->lastActiveFrameSlot = -1;
1587 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1588 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1589 workBuf->lastActiveFrameSlot = -1;
1592 psD->lastActiveFrameSlot = currentFrameSlot;
1596 int dynamicOffsetCount,
1597 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1606 srb = gfxPsD->m_shaderResourceBindings;
1608 srb = compPsD->m_shaderResourceBindings;
1612 bool hasSlottedResourceInSrb =
false;
1613 bool hasDynamicOffsetInSrb =
false;
1614 bool resNeedsRebind =
false;
1616 bool pipelineChanged =
false;
1629 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1632 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1633 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1636 case QRhiShaderResourceBinding::UniformBuffer:
1639 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1640 sanityCheckResourceOwnership(bufD);
1643 hasSlottedResourceInSrb =
true;
1644 if (b->u.ubuf.hasDynamicOffset)
1645 hasDynamicOffsetInSrb =
true;
1646 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1647 resNeedsRebind =
true;
1648 bd.ubuf.id = bufD->m_id;
1649 bd.ubuf.generation = bufD->generation;
1651 bufD->lastActiveFrameSlot = currentFrameSlot;
1654 case QRhiShaderResourceBinding::SampledTexture:
1655 case QRhiShaderResourceBinding::Texture:
1656 case QRhiShaderResourceBinding::Sampler:
1658 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1659 if (bd.stex.count != data->count) {
1660 bd.stex.count = data->count;
1661 resNeedsRebind =
true;
1663 for (
int elem = 0; elem < data->count; ++elem) {
1666 Q_ASSERT(texD || samplerD);
1667 sanityCheckResourceOwnership(texD);
1668 sanityCheckResourceOwnership(samplerD);
1669 const quint64 texId = texD ? texD->m_id : 0;
1670 const uint texGen = texD ? texD->generation : 0;
1671 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1672 const uint samplerGen = samplerD ? samplerD->generation : 0;
1673 if (texGen != bd.stex.d[elem].texGeneration
1674 || texId != bd.stex.d[elem].texId
1675 || samplerGen != bd.stex.d[elem].samplerGeneration
1676 || samplerId != bd.stex.d[elem].samplerId)
1678 resNeedsRebind =
true;
1679 bd.stex.d[elem].texId = texId;
1680 bd.stex.d[elem].texGeneration = texGen;
1681 bd.stex.d[elem].samplerId = samplerId;
1682 bd.stex.d[elem].samplerGeneration = samplerGen;
1685 texD->lastActiveFrameSlot = currentFrameSlot;
1687 samplerD->lastActiveFrameSlot = currentFrameSlot;
1691 case QRhiShaderResourceBinding::ImageLoad:
1692 case QRhiShaderResourceBinding::ImageStore:
1693 case QRhiShaderResourceBinding::ImageLoadStore:
1696 sanityCheckResourceOwnership(texD);
1697 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1698 resNeedsRebind =
true;
1699 bd.simage.id = texD->m_id;
1700 bd.simage.generation = texD->generation;
1702 texD->lastActiveFrameSlot = currentFrameSlot;
1705 case QRhiShaderResourceBinding::BufferLoad:
1706 case QRhiShaderResourceBinding::BufferStore:
1707 case QRhiShaderResourceBinding::BufferLoadStore:
1710 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1711 sanityCheckResourceOwnership(bufD);
1713 if (needsBufferSizeBuffer) {
1714 for (
int i = 0; i < 6; ++i) {
1715 const QRhiShaderResourceBinding::StageFlag stage =
1716 QRhiShaderResourceBinding::StageFlag(1 << i);
1717 if (b->stage.testFlag(stage)) {
1718 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1724 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1725 resNeedsRebind =
true;
1726 bd.sbuf.id = bufD->m_id;
1727 bd.sbuf.generation = bufD->generation;
1729 bufD->lastActiveFrameSlot = currentFrameSlot;
1738 if (needsBufferSizeBuffer) {
1740 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1744 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1745 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1748 if (gfxPsD
->d->tess.enabled) {
1758 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1759 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1760 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1761 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1762 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1763 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1764 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1765 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1766 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1767 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1768 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1769 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1771 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1772 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
1774 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1775 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
1777 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1778 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
1781 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1782 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
1784 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1785 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
1789 for (
const auto &shader : shaders) {
1791 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1794 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1796 int maxNativeBinding = 0;
1797 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1798 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1800 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1802 Q_ASSERT(offset + size <= bufD->size());
1803 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1805 QMetalShaderResourceBindings::BoundResourceData bd;
1806 bd.sbuf.id = bufD->m_id;
1807 bd.sbuf.generation = bufD->generation;
1808 srbD->boundResourceData.append(bd);
1812 QVarLengthArray<
int, 8> bufferSizeBufferData;
1813 Q_ASSERT(storageBufferSizes.contains(shader.second));
1814 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1815 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1816 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1822 if (bufferSizeBufferData.size() <= index)
1823 bufferSizeBufferData.resize(index + 1);
1825 Q_ASSERT(sizes.contains(block.binding));
1826 bufferSizeBufferData[index] = sizes[block.binding];
1829 QRhiBufferData data;
1830 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1831 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1832 Q_ASSERT(offset + size <= bufD->size());
1833 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1836 offset += ((size + 31) / 32) * 32;
1840 bufD->lastActiveFrameSlot = currentFrameSlot;
1844 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1846 resNeedsRebind =
true;
1849 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1852 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1853 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1857 if (gfxPsD
->d->tess.enabled) {
1860 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1861 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1874 cbD->currentSrbGeneration = srbD->generation;
1877 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind
1878 && !srbChanged && !srbRebuilt && !pipelineChanged;
1879 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1884 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1885 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1890 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1891 QRhiBatchedBindings<NSUInteger> offsets;
1892 for (
int i = 0; i < bindingCount; ++i) {
1895 bufD->lastActiveFrameSlot = currentFrameSlot;
1896 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1897 buffers.feed(startBinding + i, mtlbuf);
1898 offsets.feed(startBinding + i, bindings[i].second);
1913 || buffers != cbD
->d->currentVertexInputsBuffers
1914 || offsets != cbD
->d->currentVertexInputOffsets)
1917 cbD
->d->currentVertexInputsBuffers = buffers;
1918 cbD
->d->currentVertexInputOffsets = offsets;
1920 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1921 const auto &bufferBatch(buffers.batches[i]);
1922 const auto &offsetBatch(offsets.batches[i]);
1923 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1924 bufferBatch.resources.constData()
1925 offsets: offsetBatch.resources.constData()
1926 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1933 ibufD->lastActiveFrameSlot = currentFrameSlot;
1935 cbD->currentIndexOffset = indexOffset;
1936 cbD->currentIndexFormat = indexFormat;
1946 const QSize outputSize = cbD->currentTarget->pixelSize();
1947 std::array<
float, 4> vp = cbD->currentViewport.viewport();
1948 float x = 0, y = 0, w = 0, h = 0;
1950 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
1953 w = outputSize.width();
1954 h = outputSize.height();
1957 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
1961 s.x = NSUInteger(x);
1962 s.y = NSUInteger(y);
1963 s.width = NSUInteger(w);
1964 s.height = NSUInteger(h);
1965 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1972 QSize outputSize = cbD->currentTarget->pixelSize();
1978 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1979 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1980 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1981 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1982 auto screenSize = [rateMap screenSize];
1983 outputSize = QSize(screenSize.width, screenSize.height);
1990 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1994 vp.originX =
double(x);
1995 vp.originY =
double(y);
1996 vp.width =
double(w);
1997 vp.height =
double(h);
1998 vp.znear =
double(viewport.minDepth());
1999 vp.zfar =
double(viewport.maxDepth());
2001 [cbD->d->currentRenderPassEncoder setViewport: vp];
2003 cbD->currentViewport = viewport;
2017 const QSize outputSize = cbD->currentTarget->pixelSize();
2021 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2025 s.x = NSUInteger(x);
2026 s.y = NSUInteger(y);
2027 s.width = NSUInteger(w);
2028 s.height = NSUInteger(h);
2030 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2040 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2041 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2049 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2055 Q_UNUSED(coarsePixelSize);
2061 if (cbD
->d->currentRenderPassEncoder) {
2062 [cbD->d->currentRenderPassEncoder endEncoding];
2063 cbD->d->currentRenderPassEncoder = nil;
2066 if (!maybeComputeEncoder)
2067 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2069 return maybeComputeEncoder;
2073 id<MTLComputeCommandEncoder> computeEncoder)
2075 if (computeEncoder) {
2076 [computeEncoder endEncoding];
2077 computeEncoder = nil;
2082 switch (cbD->currentTarget->resourceType()) {
2083 case QRhiResource::SwapChainRenderTarget:
2086 case QRhiResource::TextureRenderTarget:
2095 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2097 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2098 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2099 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2102 MTLLoadAction oldDepthLoad;
2103 MTLLoadAction oldStencilLoad;
2105 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2106 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2107 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2109 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2110 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2111 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2114 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2118 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2122 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2123 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2132 if (graphicsPipeline
->d->tess.failed)
2136 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2137 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2141 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2147 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2148 = tempComputeEncoder(cbD, cbD->d->tessellationComputeEncoder);
2149 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2153 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2154 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2155 if (args.type == TessDrawArgs::U16Indexed)
2156 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2157 else if (args.type == TessDrawArgs::U32Indexed)
2158 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2159 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2160 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2161 [computeEncoder setComputePipelineState: computePipelineState];
2166 cbD
->d->currentComputePassEncoder = computeEncoder;
2168 cbD->d->currentComputePassEncoder = nil;
2170 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2171 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2172 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2174 if (outputBufferBinding >= 0) {
2175 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2176 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2179 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2182 if (indexBufferBinding >= 0)
2183 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2185 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2186 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2187 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2188 [computeEncoder setBuffers: bufferBatch.resources.constData()
2189 offsets: offsetBatch.resources.constData()
2190 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2194 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2195 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2197 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2198 args.draw.vertexCount, args.draw.instanceCount)];
2201 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2202 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2207 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2208 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2209 [computeEncoder setComputePipelineState: computePipelineState];
2211 cbD
->d->currentComputePassEncoder = computeEncoder;
2213 cbD->d->currentComputePassEncoder = nil;
2215 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2216 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2217 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2218 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2219 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2220 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2222 if (outputBufferBinding >= 0) {
2223 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2224 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2227 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2230 if (patchOutputBufferBinding >= 0) {
2231 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2232 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2233 if (!tescPatchOutBuf)
2235 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2238 if (tessFactorBufferBinding >= 0) {
2239 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2240 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2243 if (paramsBufferBinding >= 0) {
2245 quint32 inControlPointCount;
2252 params.patchCount = patchCount;
2253 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2254 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2255 memcpy(p, ¶ms,
sizeof(params));
2256 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2259 if (vertOutBuf && inputBufferBinding >= 0)
2260 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2262 int sgSize =
int(computePipelineState.threadExecutionWidth);
2263 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2264 while (wgSize > caps.maxThreadGroupSize) {
2266 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2268 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2269 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2277 endTempComputeEncoding(cbD, cbD
->d->tessellationComputeEncoder);
2278 cbD->d->tessellationComputeEncoder = nil;
2287 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2292 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2293 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2294 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2295 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2297 if (outputBufferBinding >= 0 && tescOutBuf)
2298 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2300 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2301 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2303 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2304 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2305 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2308 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2310 patchCount: patchCount
2311 patchIndexBuffer: nil
2312 patchIndexBufferOffset: 0
2322 if (multiViewCount <= 1)
2326 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2327 if (viewMaskBufBinding == -1) {
2328 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2335 multiViewInfo.viewOffset = 0;
2336 multiViewInfo.viewCount = quint32(multiViewCount);
2340 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2341 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2342 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2343 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2347 *instanceCount *= multiViewCount;
2352 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2361 a.draw.vertexCount = vertexCount;
2362 a.draw.instanceCount = instanceCount;
2363 a.draw.firstVertex = firstVertex;
2364 a.draw.firstInstance = firstInstance;
2369 adjustForMultiViewDraw(&instanceCount, cb);
2371 if (caps.baseVertexAndInstance) {
2372 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2373 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2375 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2376 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2381 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2389 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2390 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2393 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2398 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2399 a.drawIndexed.indexCount = indexCount;
2400 a.drawIndexed.instanceCount = instanceCount;
2401 a.drawIndexed.firstIndex = firstIndex;
2402 a.drawIndexed.vertexOffset = vertexOffset;
2403 a.drawIndexed.firstInstance = firstInstance;
2404 a.drawIndexed.indexBuffer = mtlibuf;
2409 adjustForMultiViewDraw(&instanceCount, cb);
2411 if (caps.baseVertexAndInstance) {
2412 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2413 indexCount: indexCount
2414 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2415 indexBuffer: mtlibuf
2416 indexBufferOffset: indexOffset
2417 instanceCount: instanceCount
2418 baseVertex: vertexOffset
2419 baseInstance: firstInstance];
2421 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2422 indexCount: indexCount
2423 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2424 indexBuffer: mtlibuf
2425 indexBufferOffset: indexOffset
2426 instanceCount: instanceCount];
2431 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2438 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2439 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2441 NSUInteger offset = indirectBufferOffset;
2442 for (quint32 i = 0; i < drawCount; ++i) {
2443 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2444 indirectBuffer: indirectBufMtl
2445 indirectBufferOffset: offset];
2451 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2460 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
2464 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2465 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2478 static const quint32 ICB_DRAW_COUNT_THRESHOLD = 128;
2479 const bool useIcb = cbD->currentGraphicsPipeline
2480 && caps.indirectCommandBuffers
2481 && cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesIndirectDraws)
2482 && drawCount > ICB_DRAW_COUNT_THRESHOLD;
2488 if (!
d->icbEncodePipelineU32) {
2490 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2491 MTLCompileOptions *opts = [MTLCompileOptions
new];
2492 opts.languageVersion = MTLLanguageVersion2_1;
2493 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2496 qWarning(
"Failed to compile ICB encode kernel: %s",
2497 qPrintable(QString::fromNSString(err.localizedDescription)));
2501 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2502 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2504 if (!
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2505 qWarning(
"ICB encode kernel functions not found");
2510 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&err];
2511 if (!
d->icbEncodePipelineU32) {
2512 qWarning(
"Failed to create ICB encode compute pipeline (u32): %s",
2513 qPrintable(QString::fromNSString(err.localizedDescription)));
2518 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&err];
2519 if (!
d->icbEncodePipelineU16) {
2520 qWarning(
"Failed to create ICB encode compute pipeline (u16): %s",
2521 qPrintable(QString::fromNSString(err.localizedDescription)));
2531 if (icbOk && (!
d->icb ||
d->icbCapacity < drawCount)) {
2535 e.lastActiveFrameSlot = currentFrameSlot;
2536 e.stagingIcbBuffer.icb =
d->icb;
2537 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2538 d->releaseQueue.append(e);
2541 d->icbArgumentBuffer = nil;
2543 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2544 icbDesc.commandTypes = MTLIndirectCommandTypeDrawIndexed;
2545 icbDesc.inheritPipelineState = YES;
2546 icbDesc.inheritBuffers = YES;
2547 icbDesc.maxVertexBufferBindCount = 0;
2548 icbDesc.maxFragmentBufferBindCount = 0;
2549 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2550 maxCommandCount:drawCount
2551 options:MTLResourceStorageModePrivate];
2554 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2558 d->icbCapacity = drawCount;
2560 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunctionU32 newArgumentEncoderWithBufferIndex:1];
2561 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2562 options:MTLResourceStorageModeShared];
2563 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2564 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2574 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
2575 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
2576 const quint32 savedIndexOffset = cbD->currentIndexOffset;
2577 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
2580 [cbD->d->currentRenderPassEncoder endEncoding];
2581 cbD->d->currentRenderPassEncoder = nil;
2584 id<MTLComputeCommandEncoder> computeEncoder;
2586 const bool useU16 = (savedIndexFormat == QRhiCommandBuffer::IndexUInt16);
2587 id<MTLComputePipelineState> computePipeline = useU16 ?
d->icbEncodePipelineU16 :
d->icbEncodePipelineU32;
2589 computeEncoder = [cbD->d->cb computeCommandEncoder];
2590 uint32_t drawCountVal = drawCount;
2591 uint32_t metalPrimType = uint32_t(savedPipeline
->d->primitiveType);
2592 uint32_t strideVal = stride;
2594 [computeEncoder setComputePipelineState:computePipeline];
2595 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
2596 [computeEncoder setBuffer:d->icbArgumentBuffer offset:0 atIndex:1];
2597 [computeEncoder setBytes:&drawCountVal length:
sizeof(uint32_t) atIndex:2];
2598 [computeEncoder setBuffer:indexBufMtl offset:savedIndexOffset atIndex:3];
2599 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
2600 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
2601 [computeEncoder useResource:d->icb usage:MTLResourceUsageWrite];
2602 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
2603 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
2605 NSUInteger tw = computePipeline.threadExecutionWidth;
2606 [computeEncoder dispatchThreads:MTLSizeMake(drawCount, 1, 1)
2607 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
2611 endTempComputeEncoding(cbD, computeEncoder);
2620 if (savedFirstVertexBinding >= 0) {
2622 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
2623 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
2624 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
2625 const auto &bufferBatch(savedVertexBuffers.batches[i]);
2626 const auto &offsetBatch(savedVertexOffsets.batches[i]);
2627 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2628 bufferBatch.resources.constData()
2629 offsets: offsetBatch.resources.constData()
2630 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
2631 NSUInteger(bufferBatch.resources.count()))];
2636 cbD->currentIndexOffset = savedIndexOffset;
2637 cbD->currentIndexFormat = savedIndexFormat;
2640 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
2641 usage:MTLResourceUsageRead
2642 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2643 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
2644 usage:MTLResourceUsageRead
2645 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2646 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
2647 withRange:NSMakeRange(0, drawCount)];
2653 NSUInteger offset = indirectBufferOffset;
2654 for (quint32 i = 0; i < drawCount; ++i) {
2655 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2656 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2657 indexBuffer: indexBufMtl
2658 indexBufferOffset: cbD->currentIndexOffset
2659 indirectBuffer: indirectBufMtl
2660 indirectBufferOffset: offset];
2670 NSString *str = [NSString stringWithUTF8String: name.constData()];
2672 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2673 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2675 [cbD->d->cb pushDebugGroup: str];
2684 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2685 [cbD->d->currentRenderPassEncoder popDebugGroup];
2687 [cbD->d->cb popDebugGroup];
2696 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2697 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2702 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2728 currentFrameSlot = swapChainD->currentFrameSlot;
2733 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2741 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2742 if (sc != swapChainD)
2743 sc->waitUntilCompleted(currentFrameSlot);
2746 [d->captureScope beginScope];
2748 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2752 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2759 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2760 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2765 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2768 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2769 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2772 return QRhi::FrameOpSuccess;
2781 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2783 __block
int thisFrameSlot = currentFrameSlot;
2784 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2785 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2786 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2793 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2794 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2795 [drawableTexture release];
2799 if (flags.testFlag(QRhi::SkipPresent)) {
2801 [commandBuffer commit];
2803 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2805 if (swapChainD
->d->layer.presentsWithTransaction) {
2806 [commandBuffer commit];
2808 auto *metalLayer = swapChainD
->d->layer;
2809 auto presentWithTransaction = ^{
2810 [commandBuffer waitUntilScheduled];
2817 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2818 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2819 if (textureSize == surfaceSize) {
2822 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2823 << textureSize <<
"not matching surface size" << surfaceSize;
2827 if (NSThread.currentThread == NSThread.mainThread) {
2828 presentWithTransaction();
2830 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2831 Q_ASSERT(qtMetalLayer);
2833 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2837 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2838 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2844 if (qtMetalLayer.displayLock.tryLockForRead()) {
2846 qtMetalLayer.displayLock.unlock();
2848 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2849 <<
"due to" << qtMetalLayer <<
"needing display";
2855 [commandBuffer commit];
2859 [commandBuffer commit];
2866 [swapChainD->d->curDrawable release];
2867 swapChainD->d->curDrawable = nil;
2869 [d->captureScope endScope];
2873 return QRhi::FrameOpSuccess;
2880 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2882 for (QMetalSwapChain *sc : std::as_const(swapchains))
2883 sc->waitUntilCompleted(currentFrameSlot);
2885 d->ofr.active =
true;
2886 *cb = &
d->ofr.cbWrapper;
2887 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2890 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2891 d->ofr.lastGpuTime = 0;
2894 return QRhi::FrameOpSuccess;
2900 Q_ASSERT(
d->ofr.active);
2901 d->ofr.active =
false;
2903 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2907 [cb waitUntilCompleted];
2909 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2913 return QRhi::FrameOpSuccess;
2918 id<MTLCommandBuffer> cb = nil;
2921 if (
d->ofr.active) {
2924 cb =
d->ofr.cbWrapper.d->cb;
2929 cb = swapChainD->cbWrapper.d->cb;
2933 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2934 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2935 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2940 sc->waitUntilCompleted(i);
2946 [cb waitUntilCompleted];
2950 if (
d->ofr.active) {
2951 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2952 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2954 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2955 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2963 return QRhi::FrameOpSuccess;
2967 const QColor &colorClearValue,
2968 const QRhiDepthStencilClearValue &depthStencilClearValue,
2970 QRhiShadingRateMap *shadingRateMap)
2972 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2973 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2974 colorClearValue.alphaF());
2976 for (uint i = 0; i < uint(colorAttCount); ++i) {
2977 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2978 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2979 rp.colorAttachments[i].clearColor = c;
2982 if (hasDepthStencil) {
2983 rp.depthAttachment.loadAction = MTLLoadActionClear;
2984 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2985 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2986 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2987 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2988 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2992 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3000 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3001 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3002 if (imageSizeBytes > 0)
3003 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3008 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3011 const QPoint dp = subresDesc.destinationTopLeft();
3012 const QByteArray rawData = subresDesc.data();
3013 QImage img = subresDesc.image();
3014 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3015 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3017 if (!img.isNull()) {
3018 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3019 QSize size = img.size();
3020 int bpl = img.bytesPerLine();
3022 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3023 const int sx = subresDesc.sourceTopLeft().x();
3024 const int sy = subresDesc.sourceTopLeft().y();
3025 if (!subresDesc.sourceSize().isEmpty())
3026 size = subresDesc.sourceSize();
3027 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3028 if (size.width() == img.width()) {
3029 const int bpc = qMax(1, img.depth() / 8);
3030 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3031 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3032 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3033 size.height() * img.bytesPerLine());
3035 img = img.copy(sx, sy, size.width(), size.height());
3036 bpl = img.bytesPerLine();
3037 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3038 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3041 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3042 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3045 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3046 sourceOffset: NSUInteger(*curOfs)
3047 sourceBytesPerRow: NSUInteger(bpl)
3048 sourceBytesPerImage: 0
3049 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3050 toTexture: texD->d->tex
3051 destinationSlice: NSUInteger(is3D ? 0 : layer)
3052 destinationLevel: NSUInteger(level)
3053 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3054 options: MTLBlitOptionNone];
3056 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3057 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3058 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3059 const int subresw = subresSize.width();
3060 const int subresh = subresSize.height();
3062 if (subresDesc.sourceSize().isEmpty()) {
3066 w = subresDesc.sourceSize().width();
3067 h = subresDesc.sourceSize().height();
3072 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3074 const int dx = aligned(dp.x(), blockDim.width());
3075 const int dy = aligned(dp.y(), blockDim.height());
3076 if (dx + w != subresw)
3077 w = aligned(w, blockDim.width());
3078 if (dy + h != subresh)
3079 h = aligned(h, blockDim.height());
3081 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3083 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3084 sourceOffset: NSUInteger(*curOfs)
3085 sourceBytesPerRow: bpl
3086 sourceBytesPerImage: 0
3087 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3088 toTexture: texD->d->tex
3089 destinationSlice: NSUInteger(is3D ? 0 : layer)
3090 destinationLevel: NSUInteger(level)
3091 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3092 options: MTLBlitOptionNone];
3094 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3095 }
else if (!rawData.isEmpty()) {
3096 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3097 const int subresw = subresSize.width();
3098 const int subresh = subresSize.height();
3100 if (subresDesc.sourceSize().isEmpty()) {
3104 w = subresDesc.sourceSize().width();
3105 h = subresDesc.sourceSize().height();
3108 QSize size = clampedSubResourceUploadSize(QSize(w, h), dp, level, texD->m_pixelSize);
3109 quint32 bytesPerPixel = 0;
3110 textureFormatInfo(texD->m_format, size,
nullptr,
nullptr, &bytesPerPixel);
3111 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3112 bytesPerPixel, rawData.size());
3117 if (subresDesc.dataStride())
3118 bpl = subresDesc.dataStride();
3120 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3122 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3124 if (!size.isEmpty()) {
3125 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3126 sourceOffset: NSUInteger(*curOfs)
3127 sourceBytesPerRow: bpl
3128 sourceBytesPerImage: 0
3129 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3130 toTexture: texD->d->tex
3131 destinationSlice: NSUInteger(is3D ? 0 : layer)
3132 destinationLevel: NSUInteger(level)
3133 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3134 options: MTLBlitOptionNone];
3137 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3139 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3148 id<MTLBlitCommandEncoder> blitEnc = nil;
3149 auto ensureBlit = [&blitEnc, cbD,
this]() {
3151 blitEnc = [cbD->d->cb blitCommandEncoder];
3153 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
3161 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3163 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3164 bufD
->d->pendingUpdates[i].clear();
3165 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3171 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3172 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3174 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3178 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3179 if (bufD->m_type == QRhiBuffer::Dynamic) {
3180 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3182 u.result->data.resize(u.readSize);
3183 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3185 if (u.result->completed)
3186 u.result->completed();
3190 readback.buf = bufD
->d->buf[idx];
3191 readback.offset = u.offset;
3192 readback.readSize = u.readSize;
3193 readback.result = u.result;
3194 d->activeBufferReadbacks.append(readback);
3196 if (bufD->d->managed) {
3199 [blitEnc synchronizeResource:readback.buf];
3210 qsizetype stagingSize = 0;
3211 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3212 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3213 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3214 stagingSize += subresUploadByteSize(subresDesc);
3219 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3220 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3221 options: MTLResourceStorageModeShared];
3223 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3224 qsizetype curOfs = 0;
3225 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3226 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3227 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3228 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
3232 utexD->lastActiveFrameSlot = currentFrameSlot;
3236 e.lastActiveFrameSlot = currentFrameSlot;
3237 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3238 utexD->d->stagingBuf[currentFrameSlot] = nil;
3239 d->releaseQueue.append(e);
3244 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3245 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3246 const QPoint dp = u.desc.destinationTopLeft();
3247 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3248 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3249 const QPoint sp = u.desc.sourceTopLeft();
3252 [blitEnc copyFromTexture: srcD->d->tex
3253 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3254 sourceLevel: NSUInteger(u.desc.sourceLevel())
3255 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3256 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3257 toTexture: dstD->d->tex
3258 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3259 destinationLevel: NSUInteger(u.desc.destinationLevel())
3260 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3262 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3265 readback.activeFrameSlot = currentFrameSlot;
3266 readback.desc = u.rb;
3267 readback.result = u.result;
3276 qWarning(
"Multisample texture cannot be read back");
3279 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3280 if (u.rb.rect().isValid())
3283 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3284 readback.format = texD->m_format;
3286 texD->lastActiveFrameSlot = currentFrameSlot;
3290 if (u.rb.rect().isValid())
3293 rect = QRect({0, 0}, swapChainD->pixelSize);
3294 readback.format = swapChainD
->d->rhiColorFormat;
3298 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3300 readback.pixelSize = rect.size();
3303 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3304 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
3307 [blitEnc copyFromTexture: src
3308 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3309 sourceLevel: NSUInteger(u.rb.level())
3310 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3311 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3312 toBuffer: readback.buf
3313 destinationOffset: 0
3314 destinationBytesPerRow: bpl
3315 destinationBytesPerImage: 0
3316 options: MTLBlitOptionNone];
3318 d->activeTextureReadbacks.append(readback);
3322 [blitEnc generateMipmapsForTexture: utexD->d->tex];
3323 utexD->lastActiveFrameSlot = currentFrameSlot;
3329 [blitEnc popDebugGroup];
3330 [blitEnc endEncoding];
3339 if (bufD
->d->pendingUpdates[slot].isEmpty())
3342 void *p = [bufD->d->buf[slot] contents];
3343 quint32 changeBegin = UINT32_MAX;
3344 quint32 changeEnd = 0;
3345 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3346 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3347 if (u.offset < changeBegin)
3348 changeBegin = u.offset;
3349 if (u.offset + u.data.size() > changeEnd)
3350 changeEnd = u.offset + u.data.size();
3353 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3354 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3357 bufD
->d->pendingUpdates[slot].clear();
3367 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3373 QRhiRenderTarget *rt,
3374 const QColor &colorClearValue,
3375 const QRhiDepthStencilClearValue &depthStencilClearValue,
3376 QRhiResourceUpdateBatch *resourceUpdates,
3382 if (resourceUpdates)
3386 switch (rt->resourceType()) {
3387 case QRhiResource::SwapChainRenderTarget:
3391 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3394 depthStencilClearValue,
3402 if (!swapChainD
->d->curDrawable) {
3403 QMacAutoReleasePool pool;
3404 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3406 if (!swapChainD
->d->curDrawable) {
3407 qWarning(
"No drawable");
3410 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3415 color0.resolveTex = scTex;
3421 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3424 case QRhiResource::TextureRenderTarget:
3428 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3432 depthStencilClearValue,
3434 rtTex->m_desc.shadingRateMap());
3435 if (rtD->fb.preserveColor) {
3436 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3437 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3440 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3441 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3443 int colorAttCount = 0;
3444 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3448 if (it->texture()) {
3449 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3450 if (it->multiViewCount() >= 2)
3451 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3452 }
else if (it->renderBuffer()) {
3453 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3455 if (it->resolveTexture())
3456 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3458 if (rtTex->m_desc.depthStencilBuffer())
3459 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3460 if (rtTex->m_desc.depthTexture()) {
3462 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3463 if (depthTexture->arraySize() >= 2) {
3464 const int depthLayer = rtTex->m_desc.depthLayer();
3465 if (depthLayer >= 0) {
3466 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
3467 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
3468 if (colorAttCount == 0)
3469 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
3470 }
else if (colorAttCount == 0) {
3471 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3475 if (rtTex->m_desc.depthResolveTexture())
3476 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3477 if (rtTex->m_desc.shadingRateMap())
3478 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3487 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3488 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3489 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3490 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3491 if (rtD->fb.colorAtt[i].resolveTex) {
3492 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3493 : MTLStoreActionMultisampleResolve;
3494 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3495 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3496 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3501 Q_ASSERT(rtD->fb.dsTex);
3502 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3503 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3504 if (rtD->fb.depthNeedsStore)
3505 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3506 if (rtD->fb.dsResolveTex) {
3507 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3508 : MTLStoreActionMultisampleResolve;
3509 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3510 if (rtD->fb.hasStencil) {
3511 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3512 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3517 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3522 cbD->currentTarget = rt;
3530 [cbD->d->currentRenderPassEncoder endEncoding];
3533 cbD->currentTarget =
nullptr;
3535 if (resourceUpdates)
3540 QRhiResourceUpdateBatch *resourceUpdates,
3546 if (resourceUpdates)
3549 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3559 [cbD->d->currentComputePassEncoder endEncoding];
3562 if (resourceUpdates)
3575 cbD->currentPipelineGeneration = psD->generation;
3577 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3580 psD->lastActiveFrameSlot = currentFrameSlot;
3589 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3590 threadsPerThreadgroup: psD->d->localSize];
3595 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3596 [e.buffer.buffers[i] release];
3601 [e.renderbuffer.texture release];
3606 [e.texture.texture release];
3607 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3608 [e.texture.stagingBuffers[i] release];
3609 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3610 [e.texture.views[i] release];
3615 [e.sampler.samplerState release];
3620 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3622 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3636 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3637 [e.stagingBuffer.buffer release];
3639 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3640 [e.graphicsPipeline.pipelineState release];
3641 [e.graphicsPipeline.depthStencilState release];
3642 [e.graphicsPipeline.tessVertexComputeState[0] release];
3643 [e.graphicsPipeline.tessVertexComputeState[1] release];
3644 [e.graphicsPipeline.tessVertexComputeState[2] release];
3645 [e.graphicsPipeline.tessTessControlComputeState release];
3647 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3648 [e.computePipeline.pipelineState release];
3650 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3651 [e.shadingRateMap.rateMap release];
3653 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
3654 [e.stagingIcbBuffer.icb release];
3655 [e.stagingIcbBuffer.argBuffer release];
3660 d->releaseQueue.removeAt(i);
3667 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3669 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3671 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3672 readback.result->format = readback.format;
3673 readback.result->pixelSize = readback.pixelSize;
3674 readback.result->data.resize(
int(readback.bufSize));
3675 void *p = [readback.buf contents];
3676 memcpy(readback.result->data.data(), p, readback.bufSize);
3677 [readback.buf release];
3679 if (readback.result->completed)
3680 completedCallbacks.append(readback.result->completed);
3682 d->activeTextureReadbacks.remove(i);
3686 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3688 if (forced || currentFrameSlot == readback.activeFrameSlot
3689 || readback.activeFrameSlot < 0) {
3690 readback.result->data.resize(readback.readSize);
3691 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3693 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3695 if (readback.result->completed)
3696 completedCallbacks.append(readback.result->completed);
3698 d->activeBufferReadbacks.remove(i);
3702 for (
auto f : completedCallbacks)
3710 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3730 e.buffer.buffers[i] =
d->buf[i];
3732 d->pendingUpdates[i].clear();
3737 rhiD
->d->releaseQueue.append(e);
3738 rhiD->unregisterResource(
this);
3747 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3748 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3752 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3753 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3756 MTLResourceOptions opts = MTLResourceStorageModeShared;
3760 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3761 opts = MTLResourceStorageModeManaged;
3770 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3772 if (
int(m_usage) == WorkBufPoolUsage)
3777 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3778 if (!m_objectName.isEmpty()) {
3780 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3782 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3783 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3791 rhiD->registerResource(
this);
3803 b.objects[i] = &
d->buf[i];
3808 return { { &
d->buf[0] }, 1 };
3818 Q_ASSERT(m_type == Dynamic);
3820 Q_ASSERT(rhiD->inFrame);
3821 const int slot = rhiD->currentFrameSlot;
3822 void *p = [d->buf[slot] contents];
3823 return static_cast<
char *>(p);
3830 QRHI_RES_RHI(QRhiMetal);
3831 const int slot = rhiD->currentFrameSlot;
3832 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3843 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3845 case QRhiTexture::RGBA8:
3846 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3847 case QRhiTexture::BGRA8:
3848 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3849 case QRhiTexture::R8:
3851 return MTLPixelFormatR8Unorm;
3853 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3855 case QRhiTexture::R8SI:
3856 return MTLPixelFormatR8Sint;
3857 case QRhiTexture::R8UI:
3858 return MTLPixelFormatR8Uint;
3859 case QRhiTexture::RG8:
3861 return MTLPixelFormatRG8Unorm;
3863 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3865 case QRhiTexture::R16:
3866 return MTLPixelFormatR16Unorm;
3867 case QRhiTexture::RG16:
3868 return MTLPixelFormatRG16Unorm;
3869 case QRhiTexture::RED_OR_ALPHA8:
3870 return MTLPixelFormatR8Unorm;
3872 case QRhiTexture::RGBA16F:
3873 return MTLPixelFormatRGBA16Float;
3874 case QRhiTexture::RGBA32F:
3875 return MTLPixelFormatRGBA32Float;
3876 case QRhiTexture::R16F:
3877 return MTLPixelFormatR16Float;
3878 case QRhiTexture::R32F:
3879 return MTLPixelFormatR32Float;
3881 case QRhiTexture::RGB10A2:
3882 return MTLPixelFormatRGB10A2Unorm;
3884 case QRhiTexture::R32SI:
3885 return MTLPixelFormatR32Sint;
3886 case QRhiTexture::R32UI:
3887 return MTLPixelFormatR32Uint;
3888 case QRhiTexture::RG32SI:
3889 return MTLPixelFormatRG32Sint;
3890 case QRhiTexture::RG32UI:
3891 return MTLPixelFormatRG32Uint;
3892 case QRhiTexture::RGBA32SI:
3893 return MTLPixelFormatRGBA32Sint;
3894 case QRhiTexture::RGBA32UI:
3895 return MTLPixelFormatRGBA32Uint;
3898 case QRhiTexture::D16:
3899 return MTLPixelFormatDepth16Unorm;
3900 case QRhiTexture::D24:
3901 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3902 case QRhiTexture::D24S8:
3903 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3905 case QRhiTexture::D16:
3906 return MTLPixelFormatDepth32Float;
3907 case QRhiTexture::D24:
3908 return MTLPixelFormatDepth32Float;
3909 case QRhiTexture::D24S8:
3910 return MTLPixelFormatDepth32Float_Stencil8;
3912 case QRhiTexture::D32F:
3913 return MTLPixelFormatDepth32Float;
3914 case QRhiTexture::D32FS8:
3915 return MTLPixelFormatDepth32Float_Stencil8;
3918 case QRhiTexture::BC1:
3919 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3920 case QRhiTexture::BC2:
3921 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3922 case QRhiTexture::BC3:
3923 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3924 case QRhiTexture::BC4:
3925 return MTLPixelFormatBC4_RUnorm;
3926 case QRhiTexture::BC5:
3927 qWarning(
"QRhiMetal does not support BC5");
3928 return MTLPixelFormatInvalid;
3929 case QRhiTexture::BC6H:
3930 return MTLPixelFormatBC6H_RGBUfloat;
3931 case QRhiTexture::BC7:
3932 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3934 case QRhiTexture::BC1:
3935 case QRhiTexture::BC2:
3936 case QRhiTexture::BC3:
3937 case QRhiTexture::BC4:
3938 case QRhiTexture::BC5:
3939 case QRhiTexture::BC6H:
3940 case QRhiTexture::BC7:
3941 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3942 return MTLPixelFormatInvalid;
3946 case QRhiTexture::ETC2_RGB8:
3947 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3948 case QRhiTexture::ETC2_RGB8A1:
3949 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3950 case QRhiTexture::ETC2_RGBA8:
3951 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3953 case QRhiTexture::ASTC_4x4:
3954 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3955 case QRhiTexture::ASTC_5x4:
3956 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3957 case QRhiTexture::ASTC_5x5:
3958 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3959 case QRhiTexture::ASTC_6x5:
3960 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3961 case QRhiTexture::ASTC_6x6:
3962 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3963 case QRhiTexture::ASTC_8x5:
3964 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3965 case QRhiTexture::ASTC_8x6:
3966 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3967 case QRhiTexture::ASTC_8x8:
3968 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3969 case QRhiTexture::ASTC_10x5:
3970 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3971 case QRhiTexture::ASTC_10x6:
3972 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3973 case QRhiTexture::ASTC_10x8:
3974 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3975 case QRhiTexture::ASTC_10x10:
3976 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3977 case QRhiTexture::ASTC_12x10:
3978 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3979 case QRhiTexture::ASTC_12x12:
3980 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3982 case QRhiTexture::ETC2_RGB8:
3983 if (d->caps.isAppleGPU)
3984 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3985 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3986 return MTLPixelFormatInvalid;
3987 case QRhiTexture::ETC2_RGB8A1:
3988 if (d->caps.isAppleGPU)
3989 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3990 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3991 return MTLPixelFormatInvalid;
3992 case QRhiTexture::ETC2_RGBA8:
3993 if (d->caps.isAppleGPU)
3994 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3995 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3996 return MTLPixelFormatInvalid;
3997 case QRhiTexture::ASTC_4x4:
3998 if (d->caps.isAppleGPU)
3999 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
4000 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4001 return MTLPixelFormatInvalid;
4002 case QRhiTexture::ASTC_5x4:
4003 if (d->caps.isAppleGPU)
4004 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
4005 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4006 return MTLPixelFormatInvalid;
4007 case QRhiTexture::ASTC_5x5:
4008 if (d->caps.isAppleGPU)
4009 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
4010 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4011 return MTLPixelFormatInvalid;
4012 case QRhiTexture::ASTC_6x5:
4013 if (d->caps.isAppleGPU)
4014 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
4015 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4016 return MTLPixelFormatInvalid;
4017 case QRhiTexture::ASTC_6x6:
4018 if (d->caps.isAppleGPU)
4019 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
4020 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4021 return MTLPixelFormatInvalid;
4022 case QRhiTexture::ASTC_8x5:
4023 if (d->caps.isAppleGPU)
4024 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
4025 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4026 return MTLPixelFormatInvalid;
4027 case QRhiTexture::ASTC_8x6:
4028 if (d->caps.isAppleGPU)
4029 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
4030 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4031 return MTLPixelFormatInvalid;
4032 case QRhiTexture::ASTC_8x8:
4033 if (d->caps.isAppleGPU)
4034 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
4035 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4036 return MTLPixelFormatInvalid;
4037 case QRhiTexture::ASTC_10x5:
4038 if (d->caps.isAppleGPU)
4039 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
4040 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4041 return MTLPixelFormatInvalid;
4042 case QRhiTexture::ASTC_10x6:
4043 if (d->caps.isAppleGPU)
4044 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
4045 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4046 return MTLPixelFormatInvalid;
4047 case QRhiTexture::ASTC_10x8:
4048 if (d->caps.isAppleGPU)
4049 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
4050 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4051 return MTLPixelFormatInvalid;
4052 case QRhiTexture::ASTC_10x10:
4053 if (d->caps.isAppleGPU)
4054 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
4055 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4056 return MTLPixelFormatInvalid;
4057 case QRhiTexture::ASTC_12x10:
4058 if (d->caps.isAppleGPU)
4059 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
4060 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4061 return MTLPixelFormatInvalid;
4062 case QRhiTexture::ASTC_12x12:
4063 if (d->caps.isAppleGPU)
4064 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
4065 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4066 return MTLPixelFormatInvalid;
4071 return MTLPixelFormatInvalid;
4076 int sampleCount, QRhiRenderBuffer::Flags flags,
4077 QRhiTexture::Format backingFormatHint)
4098 e.renderbuffer.texture =
d->tex;
4103 rhiD
->d->releaseQueue.append(e);
4104 rhiD->unregisterResource(
this);
4113 if (m_pixelSize.isEmpty())
4117 samples = rhiD->effectiveSampleCount(m_sampleCount);
4119 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4120 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4121 desc.width = NSUInteger(m_pixelSize.width());
4122 desc.height = NSUInteger(m_pixelSize.height());
4124 desc.sampleCount = NSUInteger(
samples);
4125 desc.resourceOptions = MTLResourceStorageModePrivate;
4126 desc.usage = MTLTextureUsageRenderTarget;
4131 if (rhiD->caps.isAppleGPU) {
4132 desc.storageMode = MTLStorageModeMemoryless;
4133 d->format = MTLPixelFormatDepth32Float_Stencil8;
4135 desc.storageMode = MTLStorageModePrivate;
4136 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
4137 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
4140 desc.storageMode = MTLStorageModeMemoryless;
4141 d->format = MTLPixelFormatDepth32Float_Stencil8;
4143 desc.pixelFormat =
d->format;
4146 desc.storageMode = MTLStorageModePrivate;
4147 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4148 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
4150 d->format = MTLPixelFormatRGBA8Unorm;
4151 desc.pixelFormat =
d->format;
4158 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4161 if (!m_objectName.isEmpty())
4162 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4166 rhiD->registerResource(
this);
4172 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4173 return m_backingFormatHint;
4175 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4179 int arraySize,
int sampleCount, Flags flags)
4183 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4184 d->stagingBuf[i] = nil;
4186 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4187 d->perLevelViews[i] = nil;
4205 e.texture.texture = d->owns ? d->tex : nil;
4209 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
4210 d->stagingBuf[i] = nil;
4213 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4214 e.texture.views[i] =
d->perLevelViews[i];
4215 d->perLevelViews[i] = nil;
4220 rhiD
->d->releaseQueue.append(e);
4221 rhiD->unregisterResource(
this);
4230 const bool isCube = m_flags.testFlag(CubeMap);
4231 const bool is3D = m_flags.testFlag(ThreeDimensional);
4232 const bool isArray = m_flags.testFlag(TextureArray);
4233 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4234 const bool is1D = m_flags.testFlag(OneDimensional);
4236 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
4237 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
4240 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
4241 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
4242 samples = rhiD->effectiveSampleCount(m_sampleCount);
4245 qWarning(
"Cubemap texture cannot be multisample");
4249 qWarning(
"3D texture cannot be multisample");
4253 qWarning(
"Multisample texture cannot have mipmaps");
4257 if (isCube && is3D) {
4258 qWarning(
"Texture cannot be both cube and 3D");
4261 if (isArray && is3D) {
4262 qWarning(
"Texture cannot be both array and 3D");
4266 qWarning(
"Texture cannot be both 1D and 3D");
4269 if (is1D && isCube) {
4270 qWarning(
"Texture cannot be both 1D and cube");
4273 if (m_depth > 1 && !is3D) {
4274 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
4277 if (m_arraySize > 0 && !isArray) {
4278 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
4281 if (m_arraySize < 1 && isArray) {
4282 qWarning(
"Texture is an array but array size is %d", m_arraySize);
4286 if (!rhiD->textureFormatInfo(m_format, size,
nullptr,
nullptr,
nullptr))
4290 *adjustedSize = size;
4298 if (!prepareCreate(&size))
4301 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4303 const bool isCube = m_flags.testFlag(CubeMap);
4304 const bool is3D = m_flags.testFlag(ThreeDimensional);
4305 const bool isArray = m_flags.testFlag(TextureArray);
4306 const bool is1D = m_flags.testFlag(OneDimensional);
4308 desc.textureType = MTLTextureTypeCube;
4310 desc.textureType = MTLTextureType3D;
4312 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
4313 }
else if (isArray) {
4314 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
4316 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4318 desc.pixelFormat =
d->format;
4319 desc.width = NSUInteger(size.width());
4320 desc.height = NSUInteger(size.height());
4321 desc.depth = is3D ? qMax(1, m_depth) : 1;
4324 desc.sampleCount = NSUInteger(
samples);
4326 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
4327 desc.resourceOptions = MTLResourceStorageModePrivate;
4328 desc.storageMode = MTLStorageModePrivate;
4329 desc.usage = MTLTextureUsageShaderRead;
4330 if (m_flags.testFlag(RenderTarget))
4331 desc.usage |= MTLTextureUsageRenderTarget;
4332 if (m_flags.testFlag(UsedWithLoadStore))
4333 desc.usage |= MTLTextureUsageShaderWrite;
4336 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4339 if (!m_objectName.isEmpty())
4340 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4346 rhiD->registerResource(
this);
4352 id<MTLTexture> tex = id<MTLTexture>(src.object);
4356 if (!prepareCreate())
4366 rhiD->registerResource(
this);
4372 return {quint64(
d->tex), 0};
4378 if (perLevelViews[level])
4379 return perLevelViews[level];
4381 const MTLTextureType type = [tex textureType];
4382 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
4383 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
4384 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4385 levels: NSMakeRange(NSUInteger(level), 1)
4386 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4388 perLevelViews[level] = view;
4393 AddressMode u, AddressMode v, AddressMode w)
4407 if (!
d->samplerState)
4414 e.sampler.samplerState =
d->samplerState;
4415 d->samplerState = nil;
4419 rhiD
->d->releaseQueue.append(e);
4420 rhiD->unregisterResource(
this);
4427 case QRhiSampler::Nearest:
4428 return MTLSamplerMinMagFilterNearest;
4429 case QRhiSampler::Linear:
4430 return MTLSamplerMinMagFilterLinear;
4433 return MTLSamplerMinMagFilterNearest;
4440 case QRhiSampler::None:
4441 return MTLSamplerMipFilterNotMipmapped;
4442 case QRhiSampler::Nearest:
4443 return MTLSamplerMipFilterNearest;
4444 case QRhiSampler::Linear:
4445 return MTLSamplerMipFilterLinear;
4448 return MTLSamplerMipFilterNotMipmapped;
4455 case QRhiSampler::Repeat:
4456 return MTLSamplerAddressModeRepeat;
4457 case QRhiSampler::ClampToEdge:
4458 return MTLSamplerAddressModeClampToEdge;
4459 case QRhiSampler::Mirror:
4460 return MTLSamplerAddressModeMirrorRepeat;
4463 return MTLSamplerAddressModeClampToEdge;
4470 case QRhiSampler::Never:
4471 return MTLCompareFunctionNever;
4472 case QRhiSampler::Less:
4473 return MTLCompareFunctionLess;
4474 case QRhiSampler::Equal:
4475 return MTLCompareFunctionEqual;
4476 case QRhiSampler::LessOrEqual:
4477 return MTLCompareFunctionLessEqual;
4478 case QRhiSampler::Greater:
4479 return MTLCompareFunctionGreater;
4480 case QRhiSampler::NotEqual:
4481 return MTLCompareFunctionNotEqual;
4482 case QRhiSampler::GreaterOrEqual:
4483 return MTLCompareFunctionGreaterEqual;
4484 case QRhiSampler::Always:
4485 return MTLCompareFunctionAlways;
4488 return MTLCompareFunctionNever;
4494 if (
d->samplerState)
4497 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4498 desc.minFilter = toMetalFilter(m_minFilter);
4499 desc.magFilter = toMetalFilter(m_magFilter);
4500 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4501 desc.sAddressMode = toMetalAddressMode(m_addressU);
4502 desc.tAddressMode = toMetalAddressMode(m_addressV);
4503 desc.rAddressMode = toMetalAddressMode(m_addressW);
4504 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4507 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4512 rhiD->registerResource(
this);
4537 e.shadingRateMap.rateMap =
d->rateMap;
4542 rhiD
->d->releaseQueue.append(e);
4543 rhiD->unregisterResource(
this);
4552 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4556 [d->rateMap retain];
4561 rhiD->registerResource(
this);
4570 serializedFormatData.reserve(16);
4582 rhiD->unregisterResource(
this);
4616 serializedFormatData.clear();
4617 auto p =
std::back_inserter(serializedFormatData);
4639 rhiD->registerResource(rpD,
false);
4645 return serializedFormatData;
4667 return d->pixelSize;
4681 const QRhiTextureRenderTargetDescription &desc,
4698 rhiD->unregisterResource(
this);
4703 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4706 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4708 for (
int i = 0; i < colorAttachmentCount; ++i) {
4709 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4715 if (m_desc.depthTexture())
4716 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4717 else if (m_desc.depthStencilBuffer())
4718 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4720 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4725 rhiD->registerResource(rpD,
false);
4732 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4733 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4734 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4738 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4742 Q_ASSERT(texD || rbD);
4743 id<MTLTexture> dst = nil;
4747 if (attIndex == 0) {
4748 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4751 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4754 if (attIndex == 0) {
4755 d->pixelSize = rbD->pixelSize();
4762 colorAtt
.slice = is3D ? it->layer() : 0;
4763 colorAtt
.level = it->level();
4765 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4768 d->fb.colorAtt[attIndex] = colorAtt;
4772 if (hasDepthStencil) {
4773 if (m_desc.depthTexture()) {
4775 d->fb.dsTex = depthTexD
->d->tex;
4776 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4777 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4778 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4780 d->pixelSize = depthTexD->pixelSize();
4785 d->fb.dsTex = depthRbD
->d->tex;
4786 d->fb.hasStencil =
true;
4787 d->fb.depthNeedsStore =
false;
4788 d->fb.preserveDs =
false;
4790 d->pixelSize = depthRbD->pixelSize();
4794 if (m_desc.depthResolveTexture()) {
4796 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4803 if (d->colorAttCount > 0)
4804 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4806 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4808 rhiD->registerResource(
this,
false);
4814 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4817 return d->pixelSize;
4842 sortedBindings.clear();
4847 rhiD->unregisterResource(
this);
4852 if (!sortedBindings.isEmpty())
4856 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4859 rhiD->updateLayoutDesc(
this);
4861 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4862 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4863 if (!sortedBindings.isEmpty())
4864 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4868 boundResourceData.resize(sortedBindings.count());
4870 for (BoundResourceData &bd : boundResourceData)
4871 memset(&bd, 0,
sizeof(BoundResourceData));
4874 rhiD->registerResource(
this,
false);
4880 sortedBindings.clear();
4881 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4882 if (!flags.testFlag(BindingsAreSorted))
4883 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4885 for (BoundResourceData &bd : boundResourceData)
4886 memset(&bd, 0,
sizeof(BoundResourceData));
4910 d->tess.compVs[0].destroy();
4911 d->tess.compVs[1].destroy();
4912 d->tess.compVs[2].destroy();
4914 d->tess.compTesc.destroy();
4915 d->tess.vertTese.destroy();
4917 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4918 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4919 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4920 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4925 if (!
d->ps && !
d->ds
4926 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4927 && !
d->tess.tessControlComputeState)
4935 e.graphicsPipeline.pipelineState =
d->ps;
4936 e.graphicsPipeline.depthStencilState =
d->ds;
4937 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4938 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4941 d->tess.vertexComputeState = {};
4942 d->tess.tessControlComputeState = nil;
4946 rhiD
->d->releaseQueue.append(e);
4947 rhiD->unregisterResource(
this);
4954 case QRhiVertexInputAttribute::Float4:
4955 return MTLVertexFormatFloat4;
4956 case QRhiVertexInputAttribute::Float3:
4957 return MTLVertexFormatFloat3;
4958 case QRhiVertexInputAttribute::Float2:
4959 return MTLVertexFormatFloat2;
4960 case QRhiVertexInputAttribute::Float:
4961 return MTLVertexFormatFloat;
4962 case QRhiVertexInputAttribute::UNormByte4:
4963 return MTLVertexFormatUChar4Normalized;
4964 case QRhiVertexInputAttribute::UNormByte2:
4965 return MTLVertexFormatUChar2Normalized;
4966 case QRhiVertexInputAttribute::UNormByte:
4967 return MTLVertexFormatUCharNormalized;
4968 case QRhiVertexInputAttribute::UInt4:
4969 return MTLVertexFormatUInt4;
4970 case QRhiVertexInputAttribute::UInt3:
4971 return MTLVertexFormatUInt3;
4972 case QRhiVertexInputAttribute::UInt2:
4973 return MTLVertexFormatUInt2;
4974 case QRhiVertexInputAttribute::UInt:
4975 return MTLVertexFormatUInt;
4976 case QRhiVertexInputAttribute::SInt4:
4977 return MTLVertexFormatInt4;
4978 case QRhiVertexInputAttribute::SInt3:
4979 return MTLVertexFormatInt3;
4980 case QRhiVertexInputAttribute::SInt2:
4981 return MTLVertexFormatInt2;
4982 case QRhiVertexInputAttribute::SInt:
4983 return MTLVertexFormatInt;
4984 case QRhiVertexInputAttribute::Half4:
4985 return MTLVertexFormatHalf4;
4986 case QRhiVertexInputAttribute::Half3:
4987 return MTLVertexFormatHalf3;
4988 case QRhiVertexInputAttribute::Half2:
4989 return MTLVertexFormatHalf2;
4990 case QRhiVertexInputAttribute::Half:
4991 return MTLVertexFormatHalf;
4992 case QRhiVertexInputAttribute::UShort4:
4993 return MTLVertexFormatUShort4;
4994 case QRhiVertexInputAttribute::UShort3:
4995 return MTLVertexFormatUShort3;
4996 case QRhiVertexInputAttribute::UShort2:
4997 return MTLVertexFormatUShort2;
4998 case QRhiVertexInputAttribute::UShort:
4999 return MTLVertexFormatUShort;
5000 case QRhiVertexInputAttribute::SShort4:
5001 return MTLVertexFormatShort4;
5002 case QRhiVertexInputAttribute::SShort3:
5003 return MTLVertexFormatShort3;
5004 case QRhiVertexInputAttribute::SShort2:
5005 return MTLVertexFormatShort2;
5006 case QRhiVertexInputAttribute::SShort:
5007 return MTLVertexFormatShort;
5010 return MTLVertexFormatFloat4;
5017 case QRhiGraphicsPipeline::Zero:
5018 return MTLBlendFactorZero;
5019 case QRhiGraphicsPipeline::One:
5020 return MTLBlendFactorOne;
5021 case QRhiGraphicsPipeline::SrcColor:
5022 return MTLBlendFactorSourceColor;
5023 case QRhiGraphicsPipeline::OneMinusSrcColor:
5024 return MTLBlendFactorOneMinusSourceColor;
5025 case QRhiGraphicsPipeline::DstColor:
5026 return MTLBlendFactorDestinationColor;
5027 case QRhiGraphicsPipeline::OneMinusDstColor:
5028 return MTLBlendFactorOneMinusDestinationColor;
5029 case QRhiGraphicsPipeline::SrcAlpha:
5030 return MTLBlendFactorSourceAlpha;
5031 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
5032 return MTLBlendFactorOneMinusSourceAlpha;
5033 case QRhiGraphicsPipeline::DstAlpha:
5034 return MTLBlendFactorDestinationAlpha;
5035 case QRhiGraphicsPipeline::OneMinusDstAlpha:
5036 return MTLBlendFactorOneMinusDestinationAlpha;
5037 case QRhiGraphicsPipeline::ConstantColor:
5038 return MTLBlendFactorBlendColor;
5039 case QRhiGraphicsPipeline::ConstantAlpha:
5040 return MTLBlendFactorBlendAlpha;
5041 case QRhiGraphicsPipeline::OneMinusConstantColor:
5042 return MTLBlendFactorOneMinusBlendColor;
5043 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
5044 return MTLBlendFactorOneMinusBlendAlpha;
5045 case QRhiGraphicsPipeline::SrcAlphaSaturate:
5046 return MTLBlendFactorSourceAlphaSaturated;
5047 case QRhiGraphicsPipeline::Src1Color:
5048 return MTLBlendFactorSource1Color;
5049 case QRhiGraphicsPipeline::OneMinusSrc1Color:
5050 return MTLBlendFactorOneMinusSource1Color;
5051 case QRhiGraphicsPipeline::Src1Alpha:
5052 return MTLBlendFactorSource1Alpha;
5053 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
5054 return MTLBlendFactorOneMinusSource1Alpha;
5057 return MTLBlendFactorZero;
5064 case QRhiGraphicsPipeline::Add:
5065 return MTLBlendOperationAdd;
5066 case QRhiGraphicsPipeline::Subtract:
5067 return MTLBlendOperationSubtract;
5068 case QRhiGraphicsPipeline::ReverseSubtract:
5069 return MTLBlendOperationReverseSubtract;
5070 case QRhiGraphicsPipeline::Min:
5071 return MTLBlendOperationMin;
5072 case QRhiGraphicsPipeline::Max:
5073 return MTLBlendOperationMax;
5076 return MTLBlendOperationAdd;
5083 if (c.testFlag(QRhiGraphicsPipeline::R))
5084 f |= MTLColorWriteMaskRed;
5085 if (c.testFlag(QRhiGraphicsPipeline::G))
5086 f |= MTLColorWriteMaskGreen;
5087 if (c.testFlag(QRhiGraphicsPipeline::B))
5088 f |= MTLColorWriteMaskBlue;
5089 if (c.testFlag(QRhiGraphicsPipeline::A))
5090 f |= MTLColorWriteMaskAlpha;
5097 case QRhiGraphicsPipeline::Never:
5098 return MTLCompareFunctionNever;
5099 case QRhiGraphicsPipeline::Less:
5100 return MTLCompareFunctionLess;
5101 case QRhiGraphicsPipeline::Equal:
5102 return MTLCompareFunctionEqual;
5103 case QRhiGraphicsPipeline::LessOrEqual:
5104 return MTLCompareFunctionLessEqual;
5105 case QRhiGraphicsPipeline::Greater:
5106 return MTLCompareFunctionGreater;
5107 case QRhiGraphicsPipeline::NotEqual:
5108 return MTLCompareFunctionNotEqual;
5109 case QRhiGraphicsPipeline::GreaterOrEqual:
5110 return MTLCompareFunctionGreaterEqual;
5111 case QRhiGraphicsPipeline::Always:
5112 return MTLCompareFunctionAlways;
5115 return MTLCompareFunctionAlways;
5122 case QRhiGraphicsPipeline::StencilZero:
5123 return MTLStencilOperationZero;
5124 case QRhiGraphicsPipeline::Keep:
5125 return MTLStencilOperationKeep;
5126 case QRhiGraphicsPipeline::Replace:
5127 return MTLStencilOperationReplace;
5128 case QRhiGraphicsPipeline::IncrementAndClamp:
5129 return MTLStencilOperationIncrementClamp;
5130 case QRhiGraphicsPipeline::DecrementAndClamp:
5131 return MTLStencilOperationDecrementClamp;
5132 case QRhiGraphicsPipeline::Invert:
5133 return MTLStencilOperationInvert;
5134 case QRhiGraphicsPipeline::IncrementAndWrap:
5135 return MTLStencilOperationIncrementWrap;
5136 case QRhiGraphicsPipeline::DecrementAndWrap:
5137 return MTLStencilOperationDecrementWrap;
5140 return MTLStencilOperationKeep;
5147 case QRhiGraphicsPipeline::Triangles:
5148 return MTLPrimitiveTypeTriangle;
5149 case QRhiGraphicsPipeline::TriangleStrip:
5150 return MTLPrimitiveTypeTriangleStrip;
5151 case QRhiGraphicsPipeline::Lines:
5152 return MTLPrimitiveTypeLine;
5153 case QRhiGraphicsPipeline::LineStrip:
5154 return MTLPrimitiveTypeLineStrip;
5155 case QRhiGraphicsPipeline::Points:
5156 return MTLPrimitiveTypePoint;
5159 return MTLPrimitiveTypeTriangle;
5166 case QRhiGraphicsPipeline::Triangles:
5167 case QRhiGraphicsPipeline::TriangleStrip:
5168 case QRhiGraphicsPipeline::TriangleFan:
5169 return MTLPrimitiveTopologyClassTriangle;
5170 case QRhiGraphicsPipeline::Lines:
5171 case QRhiGraphicsPipeline::LineStrip:
5172 return MTLPrimitiveTopologyClassLine;
5173 case QRhiGraphicsPipeline::Points:
5174 return MTLPrimitiveTopologyClassPoint;
5177 return MTLPrimitiveTopologyClassTriangle;
5184 case QRhiGraphicsPipeline::None:
5185 return MTLCullModeNone;
5186 case QRhiGraphicsPipeline::Front:
5187 return MTLCullModeFront;
5188 case QRhiGraphicsPipeline::Back:
5189 return MTLCullModeBack;
5192 return MTLCullModeNone;
5199 case QRhiGraphicsPipeline::Fill:
5200 return MTLTriangleFillModeFill;
5201 case QRhiGraphicsPipeline::Line:
5202 return MTLTriangleFillModeLines;
5205 return MTLTriangleFillModeFill;
5212 case QShaderDescription::CwTessellationWindingOrder:
5213 return MTLWindingClockwise;
5214 case QShaderDescription::CcwTessellationWindingOrder:
5215 return MTLWindingCounterClockwise;
5218 return MTLWindingCounterClockwise;
5225 case QShaderDescription::EqualTessellationPartitioning:
5226 return MTLTessellationPartitionModePow2;
5227 case QShaderDescription::FractionalEvenTessellationPartitioning:
5228 return MTLTessellationPartitionModeFractionalEven;
5229 case QShaderDescription::FractionalOddTessellationPartitioning:
5230 return MTLTessellationPartitionModeFractionalOdd;
5233 return MTLTessellationPartitionModePow2;
5239 int v = version.version();
5240 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
5244 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
5246 QVarLengthArray<
int, 8> versions;
5247 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
5249 const QList<QShaderKey> shaders = shader.availableShaders();
5253 for (
const int &version : versions) {
5254 key = { QShader::Source::MetalLibShader, version, shaderVariant };
5255 if (shaders.contains(key))
5259 QShaderCode mtllib = shader.shader(key);
5260 if (!mtllib.shader().isEmpty()) {
5261 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
5262 size_t(mtllib.shader().size()),
5263 dispatch_get_global_queue(0, 0),
5264 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
5266 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
5267 dispatch_release(data);
5269 *entryPoint = mtllib.entryPoint();
5273 const QString msg = QString::fromNSString(err.localizedDescription);
5274 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
5278 for (
const int &version : versions) {
5279 key = { QShader::Source::MslShader, version, shaderVariant };
5280 if (shaders.contains(key))
5284 QShaderCode mslSource = shader.shader(key);
5285 if (mslSource.shader().isEmpty()) {
5286 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
5290 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
5291 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
5292 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
5294 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
5302 const QString msg = QString::fromNSString(err.localizedDescription);
5307 *entryPoint = mslSource.entryPoint();
5314 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
5319 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
5323 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
5324 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
5325 rpDesc.colorAttachments[0].blendingEnabled =
false;
5327 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
5328 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
5330 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
5331 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
5332 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
5333 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
5334 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
5335 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
5336 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
5337 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
5338 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
5339 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
5340 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
5347 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
5348 rpDesc.depthAttachmentPixelFormat = fmt;
5349#if defined(Q_OS_MACOS)
5350 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5352 if (fmt != MTLPixelFormatDepth32Float)
5354 rpDesc.stencilAttachmentPixelFormat = fmt;
5358 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5363 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5365 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5366 dsDesc.depthWriteEnabled = m_depthWrite;
5367 if (m_stencilTest) {
5368 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5369 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5370 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5371 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5372 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5373 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5374 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5376 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5377 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5378 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5379 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5380 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5381 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5382 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5388 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5389 d->cullMode = toMetalCullMode(m_cullMode);
5390 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5391 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5392 d->depthBias =
float(m_depthBias);
5393 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5403 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5406 const uint loc = uint(it->location());
5407 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5408 desc.attributes[loc].offset = NSUInteger(it->offset());
5409 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5411 int bindingIndex = 0;
5412 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5413 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5414 it != itEnd; ++it, ++bindingIndex)
5416 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5417 desc.layouts[layoutIdx].stepFunction =
5418 it->classification() == QRhiVertexInputBinding::PerInstance
5419 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5420 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5421 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5422 desc.layouts[layoutIdx].stepRate *= viewCount;
5423 desc.layouts[layoutIdx].stride = it->stride();
5434 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5437 const uint loc = uint(it->location());
5438 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5439 desc.attributes[loc].offset = NSUInteger(it->offset());
5440 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5442 int bindingIndex = 0;
5443 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5444 it != itEnd; ++it, ++bindingIndex)
5446 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5447 if (desc.indexBufferIndex) {
5448 desc.layouts[layoutIdx].stepFunction =
5449 it->classification() == QRhiVertexInputBinding::PerInstance
5450 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5452 desc.layouts[layoutIdx].stepFunction =
5453 it->classification() == QRhiVertexInputBinding::PerInstance
5454 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5456 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5457 desc.layouts[layoutIdx].stride = it->stride();
5464 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5465 rpDesc.binaryArchives = binArchArray;
5473 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5474 const QString msg = QString::fromNSString(err.localizedDescription);
5475 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5484 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5485 d->setupVertexInputDescriptor(vertexDesc);
5487 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5488 rpDesc.vertexDescriptor = vertexDesc;
5496 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5497 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5498 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5499 switch (shaderStage.type()) {
5500 case QRhiShaderStage::Vertex:
5503 [d->vs.func retain];
5504 rpDesc.vertexFunction = d->vs.func;
5506 case QRhiShaderStage::Fragment:
5509 [d->fs.func retain];
5510 rpDesc.fragmentFunction = d->fs.func;
5516 const QShader shader = shaderStage.shader();
5518 QByteArray entryPoint;
5519 QShaderKey activeKey;
5520 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5521 &error, &entryPoint, &activeKey);
5523 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5526 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5528 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5532 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5534 for (QMetalShader &s : rhiD->d->shaderCache)
5536 rhiD->d->shaderCache.clear();
5538 switch (shaderStage.type()) {
5539 case QRhiShaderStage::Vertex:
5542 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5543 d->vs.desc = shader.description();
5544 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5545 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5547 [d->vs.func retain];
5548 rpDesc.vertexFunction = func;
5550 case QRhiShaderStage::Fragment:
5553 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5554 d->fs.desc = shader.description();
5555 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5556 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5558 [d->fs.func retain];
5559 rpDesc.fragmentFunction = func;
5572 if (m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers)
5573 rpDesc.supportIndirectCommandBuffers = YES;
5575 if (m_multiViewCount >= 2)
5576 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5578 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5580 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5581 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5584 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5587 const QString msg = QString::fromNSString(err.localizedDescription);
5588 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5592 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5594 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5597 d->primitiveType = toMetalPrimitiveType(m_topology);
5605 switch (vertexCompVariant) {
5606 case QShader::NonIndexedVertexAsComputeShader:
5608 case QShader::UInt32IndexedVertexAsComputeShader:
5610 case QShader::UInt16IndexedVertexAsComputeShader:
5620 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5621 if (varIndex >= 0 && vertexComputeState[varIndex])
5622 return vertexComputeState[varIndex];
5624 id<MTLFunction> func = nil;
5626 func = compVs[varIndex].func;
5629 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5633 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5634 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5636 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5637 cpDesc.computeFunction = func;
5638 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5639 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5640 if (indexBufferBinding >= 0) {
5641 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5642 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5643 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5644 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5645 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5646 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5649 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5651 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5653 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5654 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5657 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5658 options: MTLPipelineOptionNone
5663 const QString msg = QString::fromNSString(err.localizedDescription);
5664 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5666 vertexComputeState[varIndex] = ps;
5674 if (tessControlComputeState)
5675 return tessControlComputeState;
5677 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5678 cpDesc.computeFunction = compTesc.func;
5680 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5682 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5683 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5686 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5687 options: MTLPipelineOptionNone
5692 const QString msg = QString::fromNSString(err.localizedDescription);
5693 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5695 tessControlComputeState = ps;
5703 return (indices >> index) & 0x1;
5706static inline void takeIndex(quint32 index, quint64 &indices)
5708 indices |= 1 << index;
5717 static const int maxVertexAttributes = 31;
5719 for (
int index = 0; index < maxVertexAttributes; ++index) {
5720 if (!indexTaken(index, indices))
5724 Q_UNREACHABLE_RETURN(-1);
5727static inline int aligned(quint32 offset, quint32 alignment)
5729 return ((offset + alignment - 1) / alignment) * alignment;
5737 for (
const int dim : variable.arrayDims)
5740 if (variable.type == QShaderDescription::VariableType::Struct) {
5741 for (
int element = 0; element < elements; ++element) {
5742 for (
const auto &member : variable.structMembers) {
5743 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5747 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5748 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5751 const quint32 alignment = size;
5752 vertexAlignment =
std::max(vertexAlignment, alignment);
5754 for (
int element = 0; element < elements; ++element) {
5756 offset = aligned(offset, alignment);
5763static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5767 for (
const int dim : variable.arrayDims)
5770 if (variable.type == QShaderDescription::VariableType::Struct) {
5771 for (
int element = 0; element < elements; ++element) {
5772 for (
const auto &member : variable.structMembers) {
5773 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5777 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5778 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5781 const quint32 alignment = size;
5782 vertexAlignment =
std::max(vertexAlignment, alignment);
5784 for (
int element = 0; element < elements; ++element) {
5785 Q_ASSERT(!indexTaken(index, indices));
5788 offset = aligned(offset, alignment);
5790 attributes[index].bufferIndex = binding;
5791 attributes[index].format = toMetalAttributeFormat(format);
5792 attributes[index].offset = offset;
5794 takeIndex(index, indices);
5796 if (indexTaken(index, indices))
5797 index = nextAttributeIndex(indices);
5804static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5806 if (a.size() == b.size()) {
5808 for (
int i = 0; i < a.size() && match; ++i) {
5809 match &= a[i].type == b[i].type
5810 && a[i].arrayDims == b[i].arrayDims
5811 && matches(a[i].structMembers, b[i].structMembers);
5819static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5821 return a.location == b.location
5823 && a.perPatch == b.perPatch
5824 && matches(a.structMembers, b.structMembers);
5873 if (pipeline
->d->ps)
5874 return pipeline
->d->ps;
5876 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5877 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5880 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5881 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5882 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5883 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5884 quint32 offsetInTescOutput = 0;
5885 quint32 offsetInTescPatchOutput = 0;
5886 quint32 offsetInTessFactorBuffer = 0;
5887 quint32 tescOutputAlignment = 0;
5888 quint32 tescPatchOutputAlignment = 0;
5889 quint32 tessFactorAlignment = 0;
5890 QSet<
int> usedBuffers;
5893 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5894 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5895 tescOutVars[tescOutVar.location] = tescOutVar;
5898 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5899 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5900 teseInVars[teseInVar.location] = teseInVar;
5903 quint64 indices = 0;
5905 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5907 int index = tescOutVar.location;
5909 quint32 *offset =
nullptr;
5910 quint32 *alignment =
nullptr;
5912 if (tescOutVar.perPatch) {
5913 binding = tescPatchOutputBufferBinding;
5914 offset = &offsetInTescPatchOutput;
5915 alignment = &tescPatchOutputAlignment;
5917 tescOutVar.arrayDims.removeLast();
5918 binding = tescOutputBufferBinding;
5919 offset = &offsetInTescOutput;
5920 alignment = &tescOutputAlignment;
5923 if (teseInVars.contains(index)) {
5925 if (!matches(teseInVars[index], tescOutVar)) {
5926 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5927 qWarning() <<
" tesc out:" << tescOutVar;
5928 qWarning() <<
" tese in:" << teseInVars[index];
5931 if (binding != -1) {
5932 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5933 usedBuffers << binding;
5935 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5936 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5940 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5941 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5944 teseInVars.remove(tescOutVar.location);
5947 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5948 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5951 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5952 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5953 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5956 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5957 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5958 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5960 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5961 bool tessLevelAdded =
false;
5963 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5965 QShaderDescription::InOutVariable variable;
5967 quint32 *offset =
nullptr;
5968 quint32 *alignment =
nullptr;
5970 switch (builtin.type) {
5971 case QShaderDescription::BuiltinType::PositionBuiltin:
5972 variable.type = QShaderDescription::VariableType::Vec4;
5973 binding = tescOutputBufferBinding;
5974 offset = &offsetInTescOutput;
5975 alignment = &tescOutputAlignment;
5977 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5978 variable.type = QShaderDescription::VariableType::Float;
5979 binding = tescOutputBufferBinding;
5980 offset = &offsetInTescOutput;
5981 alignment = &tescOutputAlignment;
5983 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5984 variable.type = QShaderDescription::VariableType::Float;
5985 variable.arrayDims = builtin.arrayDims;
5986 binding = tescOutputBufferBinding;
5987 offset = &offsetInTescOutput;
5988 alignment = &tescOutputAlignment;
5990 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5991 variable.type = QShaderDescription::VariableType::Half4;
5992 binding = tessFactorBufferBinding;
5993 offset = &offsetInTessFactorBuffer;
5994 tessLevelAdded = trianglesMode;
5995 alignment = &tessFactorAlignment;
5997 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5998 if (trianglesMode) {
5999 if (!tessLevelAdded) {
6000 variable.type = QShaderDescription::VariableType::Half4;
6001 binding = tessFactorBufferBinding;
6002 offsetInTessFactorBuffer = 0;
6003 offset = &offsetInTessFactorBuffer;
6004 alignment = &tessFactorAlignment;
6005 tessLevelAdded =
true;
6007 teseInBuiltins.remove(builtin.type);
6011 variable.type = QShaderDescription::VariableType::Half2;
6012 binding = tessFactorBufferBinding;
6013 offsetInTessFactorBuffer = 8;
6014 offset = &offsetInTessFactorBuffer;
6015 alignment = &tessFactorAlignment;
6023 if (teseInBuiltins.contains(builtin.type)) {
6024 if (binding != -1) {
6025 int index = nextAttributeIndex(indices);
6026 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
6027 usedBuffers << binding;
6029 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
6030 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6033 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6036 teseInBuiltins.remove(builtin.type);
6039 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
6040 switch (builtin.type) {
6041 case QShaderDescription::BuiltinType::PositionBuiltin:
6042 case QShaderDescription::BuiltinType::PointSizeBuiltin:
6043 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
6044 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
6051 if (usedBuffers.contains(tescOutputBufferBinding)) {
6052 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
6053 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
6056 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
6057 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6058 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
6061 if (usedBuffers.contains(tessFactorBufferBinding)) {
6062 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6063 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
6066 rpDesc.vertexDescriptor = vertexDesc;
6067 rpDesc.vertexFunction = vertTese.func;
6068 rpDesc.fragmentFunction = pipeline
->d->fs.func;
6074 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
6076 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
6081 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
6083 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6084 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
6087 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
6090 const QString msg = QString::fromNSString(err.localizedDescription);
6091 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
6095 pipeline->d->ps = ps;
6102 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
6105 for (QMetalBuffer *workBuf : *workBuffers) {
6106 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
6107 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6115 for (QMetalBuffer *workBuf : *workBuffers) {
6116 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
6117 workBuf->setSize(size);
6118 if (workBuf->create()) {
6119 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6130 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6133 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6137 workBuffers->append(buf);
6141 qWarning(
"Failed to acquire work buffer of size %u", size);
6149 QByteArray entryPoint;
6150 QShaderKey activeKey;
6152 const QShaderDescription tescDesc = tesc.description();
6153 const QShaderDescription teseDesc = tese.description();
6154 d->tess.inControlPointCount = uint(m_patchControlPointCount);
6155 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
6156 if (!
d->tess.outControlPointCount)
6157 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
6159 if (!
d->tess.outControlPointCount) {
6160 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
6161 d->tess.enabled =
false;
6162 d->tess.failed =
true;
6166 if (m_multiViewCount >= 2)
6167 qWarning(
"Multiview is not supported with tessellation");
6175 bool variantsPresent[3] = {};
6176 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
6177 for (
const QShaderKey &k : tessVertKeys) {
6178 switch (k.sourceVariant()) {
6179 case QShader::NonIndexedVertexAsComputeShader:
6180 variantsPresent[0] =
true;
6182 case QShader::UInt32IndexedVertexAsComputeShader:
6183 variantsPresent[1] =
true;
6185 case QShader::UInt16IndexedVertexAsComputeShader:
6186 variantsPresent[2] =
true;
6192 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
6193 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
6194 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
6195 "Try passing --msltess to qsb.");
6196 d->tess.enabled =
false;
6197 d->tess.failed =
true;
6202 for (QShader::Variant variant : {
6203 QShader::NonIndexedVertexAsComputeShader,
6204 QShader::UInt32IndexedVertexAsComputeShader,
6205 QShader::UInt16IndexedVertexAsComputeShader })
6207 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
6209 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
6210 d->tess.enabled =
false;
6211 d->tess.failed =
true;
6214 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
6216 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6218 d->tess.enabled =
false;
6219 d->tess.failed =
true;
6222 QMetalShader &compVs(d->tess.compVs[varIndex]);
6225 compVs.desc = tessVert.description();
6226 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
6227 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
6230 if (!d->tess.vsCompPipeline(rhiD, variant)) {
6231 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
6232 d->tess.enabled =
false;
6233 d->tess.failed =
true;
6241 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
6242 if (!tessControlLib) {
6243 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
6244 d->tess.enabled =
false;
6245 d->tess.failed =
true;
6248 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
6249 if (!tessControlFunc) {
6250 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6251 [tessControlLib release];
6252 d->tess.enabled =
false;
6253 d->tess.failed =
true;
6256 d->tess.compTesc.lib = tessControlLib;
6257 d->tess.compTesc.func = tessControlFunc;
6258 d->tess.compTesc.desc = tesc.description();
6259 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
6260 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
6261 if (!
d->tess.tescCompPipeline(rhiD)) {
6262 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
6263 d->tess.enabled =
false;
6264 d->tess.failed =
true;
6269 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
6271 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
6272 d->tess.enabled =
false;
6273 d->tess.failed =
true;
6276 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
6277 if (!tessEvalFunc) {
6278 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6279 [tessEvalLib release];
6280 d->tess.enabled =
false;
6281 d->tess.failed =
true;
6284 d->tess.vertTese.lib = tessEvalLib;
6285 d->tess.vertTese.func = tessEvalFunc;
6286 d->tess.vertTese.desc = tese.description();
6287 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
6288 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
6290 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
6292 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
6293 d->tess.enabled =
false;
6294 d->tess.failed =
true;
6297 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
6299 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6301 d->tess.enabled =
false;
6302 d->tess.failed =
true;
6305 d->fs.lib = fragLib;
6306 d->fs.func = fragFunc;
6307 d->fs.desc = tessFrag.description();
6308 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
6309 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
6311 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
6312 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
6313 d->tess.enabled =
false;
6314 d->tess.failed =
true;
6318 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
6320 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
6334 rhiD->pipelineCreationStart();
6335 if (!rhiD->sanityCheckGraphicsPipeline(
this))
6343 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6344 switch (shaderStage.type()) {
6345 case QRhiShaderStage::Vertex:
6346 tessVert = shaderStage.shader();
6348 case QRhiShaderStage::TessellationControl:
6349 tesc = shaderStage.shader();
6351 case QRhiShaderStage::TessellationEvaluation:
6352 tese = shaderStage.shader();
6354 case QRhiShaderStage::Fragment:
6355 tessFrag = shaderStage.shader();
6361 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6362 d->tess.failed =
false;
6364 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6370 QVarLengthArray<QMetalShader *, 6> shaders;
6371 if (
d->tess.enabled) {
6372 shaders.append(&
d->tess.compVs[0]);
6373 shaders.append(&
d->tess.compVs[1]);
6374 shaders.append(&
d->tess.compVs[2]);
6375 shaders.append(&
d->tess.compTesc);
6376 shaders.append(&
d->tess.vertTese);
6378 shaders.append(&
d->vs);
6380 shaders.append(&
d->fs);
6382 for (QMetalShader *shader : shaders) {
6383 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6384 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6385 shader->nativeResourceBindingMap[binding] = {binding, -1};
6386 int maxNativeBinding = 0;
6387 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6388 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6392 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6397 if (!d->bufferSizeBuffer)
6398 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6404 rhiD->pipelineCreationEnd();
6407 rhiD->registerResource(
this);
6436 e.computePipeline.pipelineState =
d->ps;
6441 rhiD
->d->releaseQueue.append(e);
6442 rhiD->unregisterResource(
this);
6449 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6450 cpDesc.binaryArchives = binArchArray;
6458 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6459 const QString msg = QString::fromNSString(err.localizedDescription);
6460 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6471 rhiD->pipelineCreationStart();
6473 auto cacheIt = rhiD
->d->shaderCache.constFind(m_shaderStage);
6474 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6477 const QShader shader = m_shaderStage.shader();
6479 QByteArray entryPoint;
6480 QShaderKey activeKey;
6481 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6482 &error, &entryPoint, &activeKey);
6484 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6487 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6489 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6495 d->cs.localSize = shader.description().computeShaderLocalSize();
6496 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6497 d->cs.desc = shader.description();
6498 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6501 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6502 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6503 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
6506 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6507 for (QMetalShader &s : rhiD->d->shaderCache)
6509 rhiD
->d->shaderCache.clear();
6511 rhiD
->d->shaderCache.insert(m_shaderStage,
d->cs);
6515 [d->cs.func retain];
6517 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6519 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6520 cpDesc.computeFunction =
d->cs.func;
6522 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6524 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6525 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6528 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6529 options: MTLPipelineOptionNone
6534 const QString msg = QString::fromNSString(err.localizedDescription);
6535 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6540 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6542 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6543 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6547 if (!d->bufferSizeBuffer)
6548 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6554 rhiD->pipelineCreationEnd();
6557 rhiD->registerResource(
this);
6581 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6582 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6583 return &nativeHandlesStruct;
6589 d->currentRenderPassEncoder = nil;
6590 d->currentComputePassEncoder = nil;
6591 d->tessellationComputeEncoder = nil;
6592 d->currentPassRpDesc = nil;
6599 currentTarget =
nullptr;
6607 currentPipelineGeneration = 0;
6610 currentSrbGeneration = 0;
6613 currentIndexOffset = 0;
6614 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6619 currentDepthBiasValues = { 0.0f, 0.0f };
6621 currentViewport = {};
6623 d->currentShaderResourceBindingState = {};
6624 d->currentDepthStencilState = nil;
6626 d->currentVertexInputsBuffers.clear();
6627 d->currentVertexInputOffsets.clear();
6637 d->sem[i] =
nullptr;
6638 d->msaaTex[i] = nil;
6658 dispatch_release(
d->sem[i]);
6659 d->sem[i] =
nullptr;
6664 [d->msaaTex[i] release];
6665 d->msaaTex[i] = nil;
6671 [d->curDrawable release];
6672 d->curDrawable = nil;
6676 rhiD->swapchains.remove(
this);
6677 rhiD->unregisterResource(
this);
6697 CALayer *layer =
nullptr;
6699 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6700 layer = cocoaWindow->contentLayer();
6702 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6705 return static_cast<CAMetalLayer *>(layer);
6714 d.reserved[0] = layerForWindow(window);
6721 CAMetalLayer *layer =
d->layer;
6723 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6726 int height = (
int)layer.bounds.size.height;
6727 int width = (
int)layer.bounds.size.width;
6728 width *= layer.contentsScale;
6729 height *= layer.contentsScale;
6730 return QSize(width, height);
6735 if (f == HDRExtendedSrgbLinear) {
6737 }
else if (f == HDR10) {
6739 }
else if (f == HDRExtendedDisplayP3Linear) {
6753 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6759 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6760 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6762 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6765 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6769 rhiD->registerResource(rpD,
false);
6776 samples = rhiD->effectiveSampleCount(m_sampleCount);
6778 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6779 d->colorFormat = MTLPixelFormatRGBA16Float;
6780 d->rhiColorFormat = QRhiTexture::RGBA16F;
6783 if (m_format == HDR10) {
6784 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6785 d->rhiColorFormat = QRhiTexture::RGB10A2;
6788 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6789 d->rhiColorFormat = QRhiTexture::BGRA8;
6798 dispatch_semaphore_t sem =
d->sem[slot];
6799 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6800 dispatch_semaphore_signal(sem);
6807 const bool needsRegistration = !window || window != m_window;
6809 if (window && window != m_window)
6814 if (needsRegistration || !rhiD->swapchains.contains(
this))
6815 rhiD->swapchains.insert(
this);
6819 if (window->surfaceType() != QSurface::MetalSurface) {
6820 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6824 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6828 if (
d->colorFormat !=
d->layer.pixelFormat)
6829 d->layer.pixelFormat =
d->colorFormat;
6831 if (m_format == HDRExtendedSrgbLinear) {
6832 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6833 d->layer.wantsExtendedDynamicRangeContent = YES;
6834 }
else if (m_format == HDR10) {
6835 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6836 d->layer.wantsExtendedDynamicRangeContent = YES;
6837 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6838 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6839 d->layer.wantsExtendedDynamicRangeContent = YES;
6842 if (m_flags.testFlag(UsedAsTransferSource))
6843 d->layer.framebufferOnly = NO;
6846 if (m_flags.testFlag(NoVSync))
6847 d->layer.displaySyncEnabled = NO;
6850 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6851 d->layer.opaque = NO;
6852 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6857 d->layer.opaque = NO;
6859 d->layer.opaque = YES;
6865 int width = (
int)
d->layer.bounds.size.width;
6866 int height = (
int)
d->layer.bounds.size.height;
6867 CGSize layerSize = CGSizeMake(width, height);
6868 const float scaleFactor =
d->layer.contentsScale;
6869 layerSize.width *= scaleFactor;
6870 layerSize.height *= scaleFactor;
6871 d->layer.drawableSize = layerSize;
6873 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6874 pixelSize = m_currentPixelSize;
6876 [d->layer setDevice: rhiD->d->dev];
6878 [d->curDrawable release];
6879 d->curDrawable = nil;
6890 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6891 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6892 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6893 m_depthStencil->sampleCount(), m_sampleCount);
6895 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6896 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6897 m_depthStencil->setPixelSize(pixelSize);
6898 if (!m_depthStencil->create())
6899 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6900 pixelSize.width(), pixelSize.height());
6902 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6903 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6904 pixelSize.width(), pixelSize.height());
6908 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6909 rtWrapper.d->pixelSize = pixelSize;
6915 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6916 pixelSize.width(), pixelSize.height(), scaleFactor);
6919 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6920 desc.textureType = MTLTextureType2DMultisample;
6921 desc.pixelFormat =
d->colorFormat;
6922 desc.width = NSUInteger(pixelSize.width());
6923 desc.height = NSUInteger(pixelSize.height());
6924 desc.sampleCount = NSUInteger(
samples);
6925 desc.resourceOptions = MTLResourceStorageModePrivate;
6926 desc.storageMode = MTLStorageModePrivate;
6927 desc.usage = MTLTextureUsageRenderTarget;
6929 if (
d->msaaTex[i]) {
6933 e.renderbuffer.texture =
d->msaaTex[i];
6934 rhiD
->d->releaseQueue.append(e);
6936 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6941 rhiD->registerResource(
this);
6957#if defined(Q_OS_MACOS)
6958 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6959 NSScreen *screen = view.window.screen;
6960 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6961 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6962#elif defined(Q_OS_IOS)
6963 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6964 UIScreen *screen = view.window.windowScene.screen;
6965 info.limits.colorComponentValue.maxColorComponentValue =
6966 view.window.windowScene.screen.currentEDRHeadroom;
6967 info.limits.colorComponentValue.maxPotentialColorComponentValue =
6968 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 > argBuffer
id< MTLBuffer > stagingBuffers[QMTL_FRAMES_IN_FLIGHT]
id< MTLComputePipelineState > tessTessControlComputeState
id< MTLIndirectCommandBuffer > icb
id< MTLRenderPipelineState > pipelineState
id< MTLBuffer > buffers[QMTL_FRAMES_IN_FLIGHT]
id< MTLTexture > views[QRhi::MAX_MIP_LEVELS]
QRhiReadbackDescription desc
QRhiReadbackResult * result
QRhiTexture::Format format
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
float maxPotentialColorComponentValue
LuminanceBehavior luminanceBehavior
float maxColorComponentValue
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h