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 (data.size() < qsizetype(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 && !srbChanged && !srbRebuilt;
1878 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1883 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1884 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1889 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1890 QRhiBatchedBindings<NSUInteger> offsets;
1891 for (
int i = 0; i < bindingCount; ++i) {
1894 bufD->lastActiveFrameSlot = currentFrameSlot;
1895 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1896 buffers.feed(startBinding + i, mtlbuf);
1897 offsets.feed(startBinding + i, bindings[i].second);
1912 || buffers != cbD
->d->currentVertexInputsBuffers
1913 || offsets != cbD
->d->currentVertexInputOffsets)
1916 cbD
->d->currentVertexInputsBuffers = buffers;
1917 cbD
->d->currentVertexInputOffsets = offsets;
1919 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1920 const auto &bufferBatch(buffers.batches[i]);
1921 const auto &offsetBatch(offsets.batches[i]);
1922 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1923 bufferBatch.resources.constData()
1924 offsets: offsetBatch.resources.constData()
1925 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1932 ibufD->lastActiveFrameSlot = currentFrameSlot;
1934 cbD->currentIndexOffset = indexOffset;
1935 cbD->currentIndexFormat = indexFormat;
1945 const QSize outputSize = cbD->currentTarget->pixelSize();
1946 std::array<
float, 4> vp = cbD->currentViewport.viewport();
1947 float x = 0, y = 0, w = 0, h = 0;
1949 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
1952 w = outputSize.width();
1953 h = outputSize.height();
1956 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
1960 s.x = NSUInteger(x);
1961 s.y = NSUInteger(y);
1962 s.width = NSUInteger(w);
1963 s.height = NSUInteger(h);
1964 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1971 QSize outputSize = cbD->currentTarget->pixelSize();
1977 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1978 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1979 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1980 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1981 auto screenSize = [rateMap screenSize];
1982 outputSize = QSize(screenSize.width, screenSize.height);
1989 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1993 vp.originX =
double(x);
1994 vp.originY =
double(y);
1995 vp.width =
double(w);
1996 vp.height =
double(h);
1997 vp.znear =
double(viewport.minDepth());
1998 vp.zfar =
double(viewport.maxDepth());
2000 [cbD->d->currentRenderPassEncoder setViewport: vp];
2002 cbD->currentViewport = viewport;
2016 const QSize outputSize = cbD->currentTarget->pixelSize();
2020 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2024 s.x = NSUInteger(x);
2025 s.y = NSUInteger(y);
2026 s.width = NSUInteger(w);
2027 s.height = NSUInteger(h);
2029 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2039 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2040 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2048 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2054 Q_UNUSED(coarsePixelSize);
2060 if (cbD
->d->currentRenderPassEncoder) {
2061 [cbD->d->currentRenderPassEncoder endEncoding];
2062 cbD->d->currentRenderPassEncoder = nil;
2065 if (!maybeComputeEncoder)
2066 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2068 return maybeComputeEncoder;
2072 id<MTLComputeCommandEncoder> computeEncoder)
2074 if (computeEncoder) {
2075 [computeEncoder endEncoding];
2076 computeEncoder = nil;
2081 switch (cbD->currentTarget->resourceType()) {
2082 case QRhiResource::SwapChainRenderTarget:
2085 case QRhiResource::TextureRenderTarget:
2094 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2096 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2097 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2098 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2101 MTLLoadAction oldDepthLoad;
2102 MTLLoadAction oldStencilLoad;
2104 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2105 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2106 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2108 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2109 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2110 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2113 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2117 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2121 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2122 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2131 if (graphicsPipeline
->d->tess.failed)
2135 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2136 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2140 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2146 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2147 = tempComputeEncoder(cbD, cbD->d->tessellationComputeEncoder);
2148 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2152 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2153 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2154 if (args.type == TessDrawArgs::U16Indexed)
2155 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2156 else if (args.type == TessDrawArgs::U32Indexed)
2157 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2158 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2159 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2160 [computeEncoder setComputePipelineState: computePipelineState];
2165 cbD
->d->currentComputePassEncoder = computeEncoder;
2167 cbD->d->currentComputePassEncoder = nil;
2169 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2170 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2171 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2173 if (outputBufferBinding >= 0) {
2174 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2175 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2178 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2181 if (indexBufferBinding >= 0)
2182 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2184 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2185 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2186 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2187 [computeEncoder setBuffers: bufferBatch.resources.constData()
2188 offsets: offsetBatch.resources.constData()
2189 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2193 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2194 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2196 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2197 args.draw.vertexCount, args.draw.instanceCount)];
2200 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2201 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2206 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2207 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2208 [computeEncoder setComputePipelineState: computePipelineState];
2210 cbD
->d->currentComputePassEncoder = computeEncoder;
2212 cbD->d->currentComputePassEncoder = nil;
2214 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2215 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2216 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2217 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2218 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2219 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2221 if (outputBufferBinding >= 0) {
2222 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2223 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2226 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2229 if (patchOutputBufferBinding >= 0) {
2230 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2231 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2232 if (!tescPatchOutBuf)
2234 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2237 if (tessFactorBufferBinding >= 0) {
2238 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2239 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2242 if (paramsBufferBinding >= 0) {
2244 quint32 inControlPointCount;
2251 params.patchCount = patchCount;
2252 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2253 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2254 memcpy(p, ¶ms,
sizeof(params));
2255 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2258 if (vertOutBuf && inputBufferBinding >= 0)
2259 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2261 int sgSize =
int(computePipelineState.threadExecutionWidth);
2262 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2263 while (wgSize > caps.maxThreadGroupSize) {
2265 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2267 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2268 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2276 endTempComputeEncoding(cbD, cbD
->d->tessellationComputeEncoder);
2277 cbD->d->tessellationComputeEncoder = nil;
2286 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2291 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2292 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2293 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2294 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2296 if (outputBufferBinding >= 0 && tescOutBuf)
2297 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2299 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2300 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2302 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2303 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2304 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2307 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2309 patchCount: patchCount
2310 patchIndexBuffer: nil
2311 patchIndexBufferOffset: 0
2321 if (multiViewCount <= 1)
2325 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2326 if (viewMaskBufBinding == -1) {
2327 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2334 multiViewInfo.viewOffset = 0;
2335 multiViewInfo.viewCount = quint32(multiViewCount);
2339 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2340 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2341 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2342 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2346 *instanceCount *= multiViewCount;
2351 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2360 a.draw.vertexCount = vertexCount;
2361 a.draw.instanceCount = instanceCount;
2362 a.draw.firstVertex = firstVertex;
2363 a.draw.firstInstance = firstInstance;
2368 adjustForMultiViewDraw(&instanceCount, cb);
2370 if (caps.baseVertexAndInstance) {
2371 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2372 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2374 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2375 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2380 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2388 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2389 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2392 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2397 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2398 a.drawIndexed.indexCount = indexCount;
2399 a.drawIndexed.instanceCount = instanceCount;
2400 a.drawIndexed.firstIndex = firstIndex;
2401 a.drawIndexed.vertexOffset = vertexOffset;
2402 a.drawIndexed.firstInstance = firstInstance;
2403 a.drawIndexed.indexBuffer = mtlibuf;
2408 adjustForMultiViewDraw(&instanceCount, cb);
2410 if (caps.baseVertexAndInstance) {
2411 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2412 indexCount: indexCount
2413 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2414 indexBuffer: mtlibuf
2415 indexBufferOffset: indexOffset
2416 instanceCount: instanceCount
2417 baseVertex: vertexOffset
2418 baseInstance: firstInstance];
2420 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2421 indexCount: indexCount
2422 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2423 indexBuffer: mtlibuf
2424 indexBufferOffset: indexOffset
2425 instanceCount: instanceCount];
2430 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2437 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2438 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2440 NSUInteger offset = indirectBufferOffset;
2441 for (quint32 i = 0; i < drawCount; ++i) {
2442 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2443 indirectBuffer: indirectBufMtl
2444 indirectBufferOffset: offset];
2450 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2459 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
2463 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2464 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2477 static const quint32 ICB_DRAW_COUNT_THRESHOLD = 128;
2478 const bool useIcb = cbD->currentGraphicsPipeline
2479 && caps.indirectCommandBuffers
2480 && cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesIndirectDraws)
2481 && drawCount > ICB_DRAW_COUNT_THRESHOLD;
2487 if (!
d->icbEncodePipelineU32) {
2489 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2490 MTLCompileOptions *opts = [MTLCompileOptions
new];
2491 opts.languageVersion = MTLLanguageVersion2_1;
2492 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2495 qWarning(
"Failed to compile ICB encode kernel: %s",
2496 qPrintable(QString::fromNSString(err.localizedDescription)));
2500 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2501 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2503 if (!
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2504 qWarning(
"ICB encode kernel functions not found");
2509 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&err];
2510 if (!
d->icbEncodePipelineU32) {
2511 qWarning(
"Failed to create ICB encode compute pipeline (u32): %s",
2512 qPrintable(QString::fromNSString(err.localizedDescription)));
2517 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&err];
2518 if (!
d->icbEncodePipelineU16) {
2519 qWarning(
"Failed to create ICB encode compute pipeline (u16): %s",
2520 qPrintable(QString::fromNSString(err.localizedDescription)));
2530 if (icbOk && (!
d->icb ||
d->icbCapacity < drawCount)) {
2534 e.lastActiveFrameSlot = currentFrameSlot;
2535 e.stagingIcbBuffer.icb =
d->icb;
2536 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2537 d->releaseQueue.append(e);
2540 d->icbArgumentBuffer = nil;
2542 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2543 icbDesc.commandTypes = MTLIndirectCommandTypeDrawIndexed;
2544 icbDesc.inheritPipelineState = YES;
2545 icbDesc.inheritBuffers = YES;
2546 icbDesc.maxVertexBufferBindCount = 0;
2547 icbDesc.maxFragmentBufferBindCount = 0;
2548 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2549 maxCommandCount:drawCount
2550 options:MTLResourceStorageModePrivate];
2553 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2557 d->icbCapacity = drawCount;
2559 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunctionU32 newArgumentEncoderWithBufferIndex:1];
2560 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2561 options:MTLResourceStorageModeShared];
2562 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2563 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2573 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
2574 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
2575 const quint32 savedIndexOffset = cbD->currentIndexOffset;
2576 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
2579 [cbD->d->currentRenderPassEncoder endEncoding];
2580 cbD->d->currentRenderPassEncoder = nil;
2583 id<MTLComputeCommandEncoder> computeEncoder;
2585 const bool useU16 = (savedIndexFormat == QRhiCommandBuffer::IndexUInt16);
2586 id<MTLComputePipelineState> computePipeline = useU16 ?
d->icbEncodePipelineU16 :
d->icbEncodePipelineU32;
2588 computeEncoder = [cbD->d->cb computeCommandEncoder];
2589 uint32_t drawCountVal = drawCount;
2590 uint32_t metalPrimType = uint32_t(savedPipeline
->d->primitiveType);
2591 uint32_t strideVal = stride;
2593 [computeEncoder setComputePipelineState:computePipeline];
2594 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
2595 [computeEncoder setBuffer:d->icbArgumentBuffer offset:0 atIndex:1];
2596 [computeEncoder setBytes:&drawCountVal length:
sizeof(uint32_t) atIndex:2];
2597 [computeEncoder setBuffer:indexBufMtl offset:savedIndexOffset atIndex:3];
2598 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
2599 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
2600 [computeEncoder useResource:d->icb usage:MTLResourceUsageWrite];
2601 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
2602 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
2604 NSUInteger tw = computePipeline.threadExecutionWidth;
2605 [computeEncoder dispatchThreads:MTLSizeMake(drawCount, 1, 1)
2606 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
2610 endTempComputeEncoding(cbD, computeEncoder);
2619 if (savedFirstVertexBinding >= 0) {
2621 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
2622 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
2623 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
2624 const auto &bufferBatch(savedVertexBuffers.batches[i]);
2625 const auto &offsetBatch(savedVertexOffsets.batches[i]);
2626 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2627 bufferBatch.resources.constData()
2628 offsets: offsetBatch.resources.constData()
2629 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
2630 NSUInteger(bufferBatch.resources.count()))];
2635 cbD->currentIndexOffset = savedIndexOffset;
2636 cbD->currentIndexFormat = savedIndexFormat;
2639 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
2640 usage:MTLResourceUsageRead
2641 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2642 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
2643 usage:MTLResourceUsageRead
2644 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2645 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
2646 withRange:NSMakeRange(0, drawCount)];
2652 NSUInteger offset = indirectBufferOffset;
2653 for (quint32 i = 0; i < drawCount; ++i) {
2654 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2655 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2656 indexBuffer: indexBufMtl
2657 indexBufferOffset: cbD->currentIndexOffset
2658 indirectBuffer: indirectBufMtl
2659 indirectBufferOffset: offset];
2669 NSString *str = [NSString stringWithUTF8String: name.constData()];
2671 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2672 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2674 [cbD->d->cb pushDebugGroup: str];
2683 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2684 [cbD->d->currentRenderPassEncoder popDebugGroup];
2686 [cbD->d->cb popDebugGroup];
2695 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2696 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2701 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2727 currentFrameSlot = swapChainD->currentFrameSlot;
2732 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2740 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2741 if (sc != swapChainD)
2742 sc->waitUntilCompleted(currentFrameSlot);
2745 [d->captureScope beginScope];
2747 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2751 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2758 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2759 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2764 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2767 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2768 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2771 return QRhi::FrameOpSuccess;
2780 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2782 __block
int thisFrameSlot = currentFrameSlot;
2783 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2784 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2785 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2792 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2793 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2794 [drawableTexture release];
2798 if (flags.testFlag(QRhi::SkipPresent)) {
2800 [commandBuffer commit];
2802 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2804 if (swapChainD
->d->layer.presentsWithTransaction) {
2805 [commandBuffer commit];
2807 auto *metalLayer = swapChainD
->d->layer;
2808 auto presentWithTransaction = ^{
2809 [commandBuffer waitUntilScheduled];
2816 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2817 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2818 if (textureSize == surfaceSize) {
2821 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2822 << textureSize <<
"not matching surface size" << surfaceSize;
2826 if (NSThread.currentThread == NSThread.mainThread) {
2827 presentWithTransaction();
2829 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2830 Q_ASSERT(qtMetalLayer);
2832 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2836 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2837 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2843 if (qtMetalLayer.displayLock.tryLockForRead()) {
2845 qtMetalLayer.displayLock.unlock();
2847 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2848 <<
"due to" << qtMetalLayer <<
"needing display";
2854 [commandBuffer commit];
2858 [commandBuffer commit];
2865 [swapChainD->d->curDrawable release];
2866 swapChainD->d->curDrawable = nil;
2868 [d->captureScope endScope];
2872 return QRhi::FrameOpSuccess;
2879 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2881 for (QMetalSwapChain *sc : std::as_const(swapchains))
2882 sc->waitUntilCompleted(currentFrameSlot);
2884 d->ofr.active =
true;
2885 *cb = &
d->ofr.cbWrapper;
2886 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2889 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2890 d->ofr.lastGpuTime = 0;
2893 return QRhi::FrameOpSuccess;
2899 Q_ASSERT(
d->ofr.active);
2900 d->ofr.active =
false;
2902 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2906 [cb waitUntilCompleted];
2908 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2912 return QRhi::FrameOpSuccess;
2917 id<MTLCommandBuffer> cb = nil;
2920 if (
d->ofr.active) {
2923 cb =
d->ofr.cbWrapper.d->cb;
2928 cb = swapChainD->cbWrapper.d->cb;
2932 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2933 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2934 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2939 sc->waitUntilCompleted(i);
2945 [cb waitUntilCompleted];
2949 if (
d->ofr.active) {
2950 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2951 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2953 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2954 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2962 return QRhi::FrameOpSuccess;
2966 const QColor &colorClearValue,
2967 const QRhiDepthStencilClearValue &depthStencilClearValue,
2969 QRhiShadingRateMap *shadingRateMap)
2971 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2972 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2973 colorClearValue.alphaF());
2975 for (uint i = 0; i < uint(colorAttCount); ++i) {
2976 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2977 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2978 rp.colorAttachments[i].clearColor = c;
2981 if (hasDepthStencil) {
2982 rp.depthAttachment.loadAction = MTLLoadActionClear;
2983 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2984 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2985 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2986 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2987 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2991 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
2999 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3000 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3001 if (imageSizeBytes > 0)
3002 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3007 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3010 const QPoint dp = subresDesc.destinationTopLeft();
3011 const QByteArray rawData = subresDesc.data();
3012 QImage img = subresDesc.image();
3013 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3014 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3016 if (!img.isNull()) {
3017 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3018 QSize size = img.size();
3019 int bpl = img.bytesPerLine();
3021 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3022 const int sx = subresDesc.sourceTopLeft().x();
3023 const int sy = subresDesc.sourceTopLeft().y();
3024 if (!subresDesc.sourceSize().isEmpty())
3025 size = subresDesc.sourceSize();
3026 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3027 if (size.width() == img.width()) {
3028 const int bpc = qMax(1, img.depth() / 8);
3029 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3030 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3031 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3032 size.height() * img.bytesPerLine());
3034 img = img.copy(sx, sy, size.width(), size.height());
3035 bpl = img.bytesPerLine();
3036 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3037 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3040 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3041 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3044 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3045 sourceOffset: NSUInteger(*curOfs)
3046 sourceBytesPerRow: NSUInteger(bpl)
3047 sourceBytesPerImage: 0
3048 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3049 toTexture: texD->d->tex
3050 destinationSlice: NSUInteger(is3D ? 0 : layer)
3051 destinationLevel: NSUInteger(level)
3052 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3053 options: MTLBlitOptionNone];
3055 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3056 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3057 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3058 const int subresw = subresSize.width();
3059 const int subresh = subresSize.height();
3061 if (subresDesc.sourceSize().isEmpty()) {
3065 w = subresDesc.sourceSize().width();
3066 h = subresDesc.sourceSize().height();
3071 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3073 const int dx = aligned(dp.x(), blockDim.width());
3074 const int dy = aligned(dp.y(), blockDim.height());
3075 if (dx + w != subresw)
3076 w = aligned(w, blockDim.width());
3077 if (dy + h != subresh)
3078 h = aligned(h, blockDim.height());
3080 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3082 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3083 sourceOffset: NSUInteger(*curOfs)
3084 sourceBytesPerRow: bpl
3085 sourceBytesPerImage: 0
3086 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3087 toTexture: texD->d->tex
3088 destinationSlice: NSUInteger(is3D ? 0 : layer)
3089 destinationLevel: NSUInteger(level)
3090 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3091 options: MTLBlitOptionNone];
3093 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3094 }
else if (!rawData.isEmpty()) {
3095 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3096 const int subresw = subresSize.width();
3097 const int subresh = subresSize.height();
3099 if (subresDesc.sourceSize().isEmpty()) {
3103 w = subresDesc.sourceSize().width();
3104 h = subresDesc.sourceSize().height();
3108 if (subresDesc.dataStride())
3109 bpl = subresDesc.dataStride();
3111 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3113 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3115 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3116 sourceOffset: NSUInteger(*curOfs)
3117 sourceBytesPerRow: bpl
3118 sourceBytesPerImage: 0
3119 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3120 toTexture: texD->d->tex
3121 destinationSlice: NSUInteger(is3D ? 0 : layer)
3122 destinationLevel: NSUInteger(level)
3123 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3124 options: MTLBlitOptionNone];
3126 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3128 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3137 id<MTLBlitCommandEncoder> blitEnc = nil;
3138 auto ensureBlit = [&blitEnc, cbD,
this]() {
3140 blitEnc = [cbD->d->cb blitCommandEncoder];
3142 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
3150 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3152 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3153 bufD
->d->pendingUpdates[i].clear();
3154 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3160 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3161 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3163 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3167 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3168 if (bufD->m_type == QRhiBuffer::Dynamic) {
3169 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3171 u.result->data.resize(u.readSize);
3172 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3174 if (u.result->completed)
3175 u.result->completed();
3179 readback.buf = bufD
->d->buf[idx];
3180 readback.offset = u.offset;
3181 readback.readSize = u.readSize;
3182 readback.result = u.result;
3183 d->activeBufferReadbacks.append(readback);
3185 if (bufD->d->managed) {
3188 [blitEnc synchronizeResource:readback.buf];
3199 qsizetype stagingSize = 0;
3200 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3201 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3202 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3203 stagingSize += subresUploadByteSize(subresDesc);
3208 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3209 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3210 options: MTLResourceStorageModeShared];
3212 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3213 qsizetype curOfs = 0;
3214 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3215 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3216 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3217 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
3221 utexD->lastActiveFrameSlot = currentFrameSlot;
3225 e.lastActiveFrameSlot = currentFrameSlot;
3226 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3227 utexD->d->stagingBuf[currentFrameSlot] = nil;
3228 d->releaseQueue.append(e);
3233 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3234 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3235 const QPoint dp = u.desc.destinationTopLeft();
3236 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3237 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3238 const QPoint sp = u.desc.sourceTopLeft();
3241 [blitEnc copyFromTexture: srcD->d->tex
3242 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3243 sourceLevel: NSUInteger(u.desc.sourceLevel())
3244 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3245 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3246 toTexture: dstD->d->tex
3247 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3248 destinationLevel: NSUInteger(u.desc.destinationLevel())
3249 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3251 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3254 readback.activeFrameSlot = currentFrameSlot;
3255 readback.desc = u.rb;
3256 readback.result = u.result;
3265 qWarning(
"Multisample texture cannot be read back");
3268 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3269 if (u.rb.rect().isValid())
3272 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3273 readback.format = texD->m_format;
3275 texD->lastActiveFrameSlot = currentFrameSlot;
3279 if (u.rb.rect().isValid())
3282 rect = QRect({0, 0}, swapChainD->pixelSize);
3283 readback.format = swapChainD
->d->rhiColorFormat;
3287 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3289 readback.pixelSize = rect.size();
3292 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3293 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
3296 [blitEnc copyFromTexture: src
3297 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3298 sourceLevel: NSUInteger(u.rb.level())
3299 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3300 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3301 toBuffer: readback.buf
3302 destinationOffset: 0
3303 destinationBytesPerRow: bpl
3304 destinationBytesPerImage: 0
3305 options: MTLBlitOptionNone];
3307 d->activeTextureReadbacks.append(readback);
3311 [blitEnc generateMipmapsForTexture: utexD->d->tex];
3312 utexD->lastActiveFrameSlot = currentFrameSlot;
3318 [blitEnc popDebugGroup];
3319 [blitEnc endEncoding];
3328 if (bufD
->d->pendingUpdates[slot].isEmpty())
3331 void *p = [bufD->d->buf[slot] contents];
3332 quint32 changeBegin = UINT32_MAX;
3333 quint32 changeEnd = 0;
3334 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3335 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3336 if (u.offset < changeBegin)
3337 changeBegin = u.offset;
3338 if (u.offset + u.data.size() > changeEnd)
3339 changeEnd = u.offset + u.data.size();
3342 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3343 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3346 bufD
->d->pendingUpdates[slot].clear();
3356 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3362 QRhiRenderTarget *rt,
3363 const QColor &colorClearValue,
3364 const QRhiDepthStencilClearValue &depthStencilClearValue,
3365 QRhiResourceUpdateBatch *resourceUpdates,
3371 if (resourceUpdates)
3375 switch (rt->resourceType()) {
3376 case QRhiResource::SwapChainRenderTarget:
3380 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3383 depthStencilClearValue,
3391 if (!swapChainD
->d->curDrawable) {
3392 QMacAutoReleasePool pool;
3393 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3395 if (!swapChainD
->d->curDrawable) {
3396 qWarning(
"No drawable");
3399 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3404 color0.resolveTex = scTex;
3410 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3413 case QRhiResource::TextureRenderTarget:
3417 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3421 depthStencilClearValue,
3423 rtTex->m_desc.shadingRateMap());
3424 if (rtD->fb.preserveColor) {
3425 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3426 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3429 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3430 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3432 int colorAttCount = 0;
3433 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3437 if (it->texture()) {
3438 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3439 if (it->multiViewCount() >= 2)
3440 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3441 }
else if (it->renderBuffer()) {
3442 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3444 if (it->resolveTexture())
3445 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3447 if (rtTex->m_desc.depthStencilBuffer())
3448 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3449 if (rtTex->m_desc.depthTexture()) {
3451 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3452 if (depthTexture->arraySize() >= 2) {
3453 const int depthLayer = rtTex->m_desc.depthLayer();
3454 if (depthLayer >= 0) {
3455 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
3456 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
3457 if (colorAttCount == 0)
3458 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
3459 }
else if (colorAttCount == 0) {
3460 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3464 if (rtTex->m_desc.depthResolveTexture())
3465 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3466 if (rtTex->m_desc.shadingRateMap())
3467 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3476 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3477 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3478 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3479 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3480 if (rtD->fb.colorAtt[i].resolveTex) {
3481 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3482 : MTLStoreActionMultisampleResolve;
3483 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3484 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3485 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3490 Q_ASSERT(rtD->fb.dsTex);
3491 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3492 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3493 if (rtD->fb.depthNeedsStore)
3494 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3495 if (rtD->fb.dsResolveTex) {
3496 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3497 : MTLStoreActionMultisampleResolve;
3498 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3499 if (rtD->fb.hasStencil) {
3500 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3501 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3506 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3511 cbD->currentTarget = rt;
3519 [cbD->d->currentRenderPassEncoder endEncoding];
3522 cbD->currentTarget =
nullptr;
3524 if (resourceUpdates)
3529 QRhiResourceUpdateBatch *resourceUpdates,
3535 if (resourceUpdates)
3538 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3548 [cbD->d->currentComputePassEncoder endEncoding];
3551 if (resourceUpdates)
3564 cbD->currentPipelineGeneration = psD->generation;
3566 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3569 psD->lastActiveFrameSlot = currentFrameSlot;
3578 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3579 threadsPerThreadgroup: psD->d->localSize];
3584 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3585 [e.buffer.buffers[i] release];
3590 [e.renderbuffer.texture release];
3595 [e.texture.texture release];
3596 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3597 [e.texture.stagingBuffers[i] release];
3598 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3599 [e.texture.views[i] release];
3604 [e.sampler.samplerState release];
3609 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3611 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3625 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3626 [e.stagingBuffer.buffer release];
3628 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3629 [e.graphicsPipeline.pipelineState release];
3630 [e.graphicsPipeline.depthStencilState release];
3631 [e.graphicsPipeline.tessVertexComputeState[0] release];
3632 [e.graphicsPipeline.tessVertexComputeState[1] release];
3633 [e.graphicsPipeline.tessVertexComputeState[2] release];
3634 [e.graphicsPipeline.tessTessControlComputeState release];
3636 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3637 [e.computePipeline.pipelineState release];
3639 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3640 [e.shadingRateMap.rateMap release];
3642 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
3643 [e.stagingIcbBuffer.icb release];
3644 [e.stagingIcbBuffer.argBuffer release];
3649 d->releaseQueue.removeAt(i);
3656 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3658 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3660 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3661 readback.result->format = readback.format;
3662 readback.result->pixelSize = readback.pixelSize;
3663 readback.result->data.resize(
int(readback.bufSize));
3664 void *p = [readback.buf contents];
3665 memcpy(readback.result->data.data(), p, readback.bufSize);
3666 [readback.buf release];
3668 if (readback.result->completed)
3669 completedCallbacks.append(readback.result->completed);
3671 d->activeTextureReadbacks.remove(i);
3675 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3677 if (forced || currentFrameSlot == readback.activeFrameSlot
3678 || readback.activeFrameSlot < 0) {
3679 readback.result->data.resize(readback.readSize);
3680 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3682 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3684 if (readback.result->completed)
3685 completedCallbacks.append(readback.result->completed);
3687 d->activeBufferReadbacks.remove(i);
3691 for (
auto f : completedCallbacks)
3699 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3719 e.buffer.buffers[i] =
d->buf[i];
3721 d->pendingUpdates[i].clear();
3726 rhiD
->d->releaseQueue.append(e);
3727 rhiD->unregisterResource(
this);
3736 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3737 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3741 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3742 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3745 MTLResourceOptions opts = MTLResourceStorageModeShared;
3749 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3750 opts = MTLResourceStorageModeManaged;
3759 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3761 if (
int(m_usage) == WorkBufPoolUsage)
3766 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3767 if (!m_objectName.isEmpty()) {
3769 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3771 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3772 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3780 rhiD->registerResource(
this);
3792 b.objects[i] = &
d->buf[i];
3797 return { { &
d->buf[0] }, 1 };
3807 Q_ASSERT(m_type == Dynamic);
3809 Q_ASSERT(rhiD->inFrame);
3810 const int slot = rhiD->currentFrameSlot;
3811 void *p = [d->buf[slot] contents];
3812 return static_cast<
char *>(p);
3819 QRHI_RES_RHI(QRhiMetal);
3820 const int slot = rhiD->currentFrameSlot;
3821 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3832 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3834 case QRhiTexture::RGBA8:
3835 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3836 case QRhiTexture::BGRA8:
3837 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3838 case QRhiTexture::R8:
3840 return MTLPixelFormatR8Unorm;
3842 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3844 case QRhiTexture::R8SI:
3845 return MTLPixelFormatR8Sint;
3846 case QRhiTexture::R8UI:
3847 return MTLPixelFormatR8Uint;
3848 case QRhiTexture::RG8:
3850 return MTLPixelFormatRG8Unorm;
3852 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3854 case QRhiTexture::R16:
3855 return MTLPixelFormatR16Unorm;
3856 case QRhiTexture::RG16:
3857 return MTLPixelFormatRG16Unorm;
3858 case QRhiTexture::RED_OR_ALPHA8:
3859 return MTLPixelFormatR8Unorm;
3861 case QRhiTexture::RGBA16F:
3862 return MTLPixelFormatRGBA16Float;
3863 case QRhiTexture::RGBA32F:
3864 return MTLPixelFormatRGBA32Float;
3865 case QRhiTexture::R16F:
3866 return MTLPixelFormatR16Float;
3867 case QRhiTexture::R32F:
3868 return MTLPixelFormatR32Float;
3870 case QRhiTexture::RGB10A2:
3871 return MTLPixelFormatRGB10A2Unorm;
3873 case QRhiTexture::R32SI:
3874 return MTLPixelFormatR32Sint;
3875 case QRhiTexture::R32UI:
3876 return MTLPixelFormatR32Uint;
3877 case QRhiTexture::RG32SI:
3878 return MTLPixelFormatRG32Sint;
3879 case QRhiTexture::RG32UI:
3880 return MTLPixelFormatRG32Uint;
3881 case QRhiTexture::RGBA32SI:
3882 return MTLPixelFormatRGBA32Sint;
3883 case QRhiTexture::RGBA32UI:
3884 return MTLPixelFormatRGBA32Uint;
3887 case QRhiTexture::D16:
3888 return MTLPixelFormatDepth16Unorm;
3889 case QRhiTexture::D24:
3890 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3891 case QRhiTexture::D24S8:
3892 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3894 case QRhiTexture::D16:
3895 return MTLPixelFormatDepth32Float;
3896 case QRhiTexture::D24:
3897 return MTLPixelFormatDepth32Float;
3898 case QRhiTexture::D24S8:
3899 return MTLPixelFormatDepth32Float_Stencil8;
3901 case QRhiTexture::D32F:
3902 return MTLPixelFormatDepth32Float;
3903 case QRhiTexture::D32FS8:
3904 return MTLPixelFormatDepth32Float_Stencil8;
3907 case QRhiTexture::BC1:
3908 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3909 case QRhiTexture::BC2:
3910 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3911 case QRhiTexture::BC3:
3912 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3913 case QRhiTexture::BC4:
3914 return MTLPixelFormatBC4_RUnorm;
3915 case QRhiTexture::BC5:
3916 qWarning(
"QRhiMetal does not support BC5");
3917 return MTLPixelFormatInvalid;
3918 case QRhiTexture::BC6H:
3919 return MTLPixelFormatBC6H_RGBUfloat;
3920 case QRhiTexture::BC7:
3921 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3923 case QRhiTexture::BC1:
3924 case QRhiTexture::BC2:
3925 case QRhiTexture::BC3:
3926 case QRhiTexture::BC4:
3927 case QRhiTexture::BC5:
3928 case QRhiTexture::BC6H:
3929 case QRhiTexture::BC7:
3930 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3931 return MTLPixelFormatInvalid;
3935 case QRhiTexture::ETC2_RGB8:
3936 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3937 case QRhiTexture::ETC2_RGB8A1:
3938 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3939 case QRhiTexture::ETC2_RGBA8:
3940 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3942 case QRhiTexture::ASTC_4x4:
3943 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3944 case QRhiTexture::ASTC_5x4:
3945 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3946 case QRhiTexture::ASTC_5x5:
3947 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3948 case QRhiTexture::ASTC_6x5:
3949 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3950 case QRhiTexture::ASTC_6x6:
3951 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3952 case QRhiTexture::ASTC_8x5:
3953 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3954 case QRhiTexture::ASTC_8x6:
3955 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3956 case QRhiTexture::ASTC_8x8:
3957 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3958 case QRhiTexture::ASTC_10x5:
3959 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3960 case QRhiTexture::ASTC_10x6:
3961 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3962 case QRhiTexture::ASTC_10x8:
3963 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3964 case QRhiTexture::ASTC_10x10:
3965 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3966 case QRhiTexture::ASTC_12x10:
3967 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3968 case QRhiTexture::ASTC_12x12:
3969 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3971 case QRhiTexture::ETC2_RGB8:
3972 if (d->caps.isAppleGPU)
3973 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3974 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3975 return MTLPixelFormatInvalid;
3976 case QRhiTexture::ETC2_RGB8A1:
3977 if (d->caps.isAppleGPU)
3978 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3979 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3980 return MTLPixelFormatInvalid;
3981 case QRhiTexture::ETC2_RGBA8:
3982 if (d->caps.isAppleGPU)
3983 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3984 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3985 return MTLPixelFormatInvalid;
3986 case QRhiTexture::ASTC_4x4:
3987 if (d->caps.isAppleGPU)
3988 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3989 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3990 return MTLPixelFormatInvalid;
3991 case QRhiTexture::ASTC_5x4:
3992 if (d->caps.isAppleGPU)
3993 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3994 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3995 return MTLPixelFormatInvalid;
3996 case QRhiTexture::ASTC_5x5:
3997 if (d->caps.isAppleGPU)
3998 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3999 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4000 return MTLPixelFormatInvalid;
4001 case QRhiTexture::ASTC_6x5:
4002 if (d->caps.isAppleGPU)
4003 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
4004 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4005 return MTLPixelFormatInvalid;
4006 case QRhiTexture::ASTC_6x6:
4007 if (d->caps.isAppleGPU)
4008 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
4009 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4010 return MTLPixelFormatInvalid;
4011 case QRhiTexture::ASTC_8x5:
4012 if (d->caps.isAppleGPU)
4013 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
4014 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4015 return MTLPixelFormatInvalid;
4016 case QRhiTexture::ASTC_8x6:
4017 if (d->caps.isAppleGPU)
4018 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
4019 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4020 return MTLPixelFormatInvalid;
4021 case QRhiTexture::ASTC_8x8:
4022 if (d->caps.isAppleGPU)
4023 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
4024 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4025 return MTLPixelFormatInvalid;
4026 case QRhiTexture::ASTC_10x5:
4027 if (d->caps.isAppleGPU)
4028 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
4029 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4030 return MTLPixelFormatInvalid;
4031 case QRhiTexture::ASTC_10x6:
4032 if (d->caps.isAppleGPU)
4033 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
4034 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4035 return MTLPixelFormatInvalid;
4036 case QRhiTexture::ASTC_10x8:
4037 if (d->caps.isAppleGPU)
4038 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
4039 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4040 return MTLPixelFormatInvalid;
4041 case QRhiTexture::ASTC_10x10:
4042 if (d->caps.isAppleGPU)
4043 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
4044 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4045 return MTLPixelFormatInvalid;
4046 case QRhiTexture::ASTC_12x10:
4047 if (d->caps.isAppleGPU)
4048 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
4049 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4050 return MTLPixelFormatInvalid;
4051 case QRhiTexture::ASTC_12x12:
4052 if (d->caps.isAppleGPU)
4053 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
4054 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4055 return MTLPixelFormatInvalid;
4060 return MTLPixelFormatInvalid;
4065 int sampleCount, QRhiRenderBuffer::Flags flags,
4066 QRhiTexture::Format backingFormatHint)
4087 e.renderbuffer.texture =
d->tex;
4092 rhiD
->d->releaseQueue.append(e);
4093 rhiD->unregisterResource(
this);
4102 if (m_pixelSize.isEmpty())
4106 samples = rhiD->effectiveSampleCount(m_sampleCount);
4108 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4109 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4110 desc.width = NSUInteger(m_pixelSize.width());
4111 desc.height = NSUInteger(m_pixelSize.height());
4113 desc.sampleCount = NSUInteger(
samples);
4114 desc.resourceOptions = MTLResourceStorageModePrivate;
4115 desc.usage = MTLTextureUsageRenderTarget;
4120 if (rhiD->caps.isAppleGPU) {
4121 desc.storageMode = MTLStorageModeMemoryless;
4122 d->format = MTLPixelFormatDepth32Float_Stencil8;
4124 desc.storageMode = MTLStorageModePrivate;
4125 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
4126 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
4129 desc.storageMode = MTLStorageModeMemoryless;
4130 d->format = MTLPixelFormatDepth32Float_Stencil8;
4132 desc.pixelFormat =
d->format;
4135 desc.storageMode = MTLStorageModePrivate;
4136 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4137 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
4139 d->format = MTLPixelFormatRGBA8Unorm;
4140 desc.pixelFormat =
d->format;
4147 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4150 if (!m_objectName.isEmpty())
4151 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4155 rhiD->registerResource(
this);
4161 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4162 return m_backingFormatHint;
4164 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4168 int arraySize,
int sampleCount, Flags flags)
4172 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4173 d->stagingBuf[i] = nil;
4175 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4176 d->perLevelViews[i] = nil;
4194 e.texture.texture = d->owns ? d->tex : nil;
4198 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
4199 d->stagingBuf[i] = nil;
4202 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4203 e.texture.views[i] =
d->perLevelViews[i];
4204 d->perLevelViews[i] = nil;
4209 rhiD
->d->releaseQueue.append(e);
4210 rhiD->unregisterResource(
this);
4219 const bool isCube = m_flags.testFlag(CubeMap);
4220 const bool is3D = m_flags.testFlag(ThreeDimensional);
4221 const bool isArray = m_flags.testFlag(TextureArray);
4222 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4223 const bool is1D = m_flags.testFlag(OneDimensional);
4225 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
4226 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
4229 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
4230 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
4231 samples = rhiD->effectiveSampleCount(m_sampleCount);
4234 qWarning(
"Cubemap texture cannot be multisample");
4238 qWarning(
"3D texture cannot be multisample");
4242 qWarning(
"Multisample texture cannot have mipmaps");
4246 if (isCube && is3D) {
4247 qWarning(
"Texture cannot be both cube and 3D");
4250 if (isArray && is3D) {
4251 qWarning(
"Texture cannot be both array and 3D");
4255 qWarning(
"Texture cannot be both 1D and 3D");
4258 if (is1D && isCube) {
4259 qWarning(
"Texture cannot be both 1D and cube");
4262 if (m_depth > 1 && !is3D) {
4263 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
4266 if (m_arraySize > 0 && !isArray) {
4267 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
4270 if (m_arraySize < 1 && isArray) {
4271 qWarning(
"Texture is an array but array size is %d", m_arraySize);
4276 *adjustedSize = size;
4284 if (!prepareCreate(&size))
4287 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4289 const bool isCube = m_flags.testFlag(CubeMap);
4290 const bool is3D = m_flags.testFlag(ThreeDimensional);
4291 const bool isArray = m_flags.testFlag(TextureArray);
4292 const bool is1D = m_flags.testFlag(OneDimensional);
4294 desc.textureType = MTLTextureTypeCube;
4296 desc.textureType = MTLTextureType3D;
4298 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
4299 }
else if (isArray) {
4300 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
4302 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4304 desc.pixelFormat =
d->format;
4305 desc.width = NSUInteger(size.width());
4306 desc.height = NSUInteger(size.height());
4307 desc.depth = is3D ? qMax(1, m_depth) : 1;
4310 desc.sampleCount = NSUInteger(
samples);
4312 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
4313 desc.resourceOptions = MTLResourceStorageModePrivate;
4314 desc.storageMode = MTLStorageModePrivate;
4315 desc.usage = MTLTextureUsageShaderRead;
4316 if (m_flags.testFlag(RenderTarget))
4317 desc.usage |= MTLTextureUsageRenderTarget;
4318 if (m_flags.testFlag(UsedWithLoadStore))
4319 desc.usage |= MTLTextureUsageShaderWrite;
4322 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4325 if (!m_objectName.isEmpty())
4326 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4332 rhiD->registerResource(
this);
4338 id<MTLTexture> tex = id<MTLTexture>(src.object);
4342 if (!prepareCreate())
4352 rhiD->registerResource(
this);
4358 return {quint64(
d->tex), 0};
4364 if (perLevelViews[level])
4365 return perLevelViews[level];
4367 const MTLTextureType type = [tex textureType];
4368 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
4369 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
4370 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4371 levels: NSMakeRange(NSUInteger(level), 1)
4372 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4374 perLevelViews[level] = view;
4379 AddressMode u, AddressMode v, AddressMode w)
4393 if (!
d->samplerState)
4400 e.sampler.samplerState =
d->samplerState;
4401 d->samplerState = nil;
4405 rhiD
->d->releaseQueue.append(e);
4406 rhiD->unregisterResource(
this);
4413 case QRhiSampler::Nearest:
4414 return MTLSamplerMinMagFilterNearest;
4415 case QRhiSampler::Linear:
4416 return MTLSamplerMinMagFilterLinear;
4419 return MTLSamplerMinMagFilterNearest;
4426 case QRhiSampler::None:
4427 return MTLSamplerMipFilterNotMipmapped;
4428 case QRhiSampler::Nearest:
4429 return MTLSamplerMipFilterNearest;
4430 case QRhiSampler::Linear:
4431 return MTLSamplerMipFilterLinear;
4434 return MTLSamplerMipFilterNotMipmapped;
4441 case QRhiSampler::Repeat:
4442 return MTLSamplerAddressModeRepeat;
4443 case QRhiSampler::ClampToEdge:
4444 return MTLSamplerAddressModeClampToEdge;
4445 case QRhiSampler::Mirror:
4446 return MTLSamplerAddressModeMirrorRepeat;
4449 return MTLSamplerAddressModeClampToEdge;
4456 case QRhiSampler::Never:
4457 return MTLCompareFunctionNever;
4458 case QRhiSampler::Less:
4459 return MTLCompareFunctionLess;
4460 case QRhiSampler::Equal:
4461 return MTLCompareFunctionEqual;
4462 case QRhiSampler::LessOrEqual:
4463 return MTLCompareFunctionLessEqual;
4464 case QRhiSampler::Greater:
4465 return MTLCompareFunctionGreater;
4466 case QRhiSampler::NotEqual:
4467 return MTLCompareFunctionNotEqual;
4468 case QRhiSampler::GreaterOrEqual:
4469 return MTLCompareFunctionGreaterEqual;
4470 case QRhiSampler::Always:
4471 return MTLCompareFunctionAlways;
4474 return MTLCompareFunctionNever;
4480 if (
d->samplerState)
4483 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4484 desc.minFilter = toMetalFilter(m_minFilter);
4485 desc.magFilter = toMetalFilter(m_magFilter);
4486 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4487 desc.sAddressMode = toMetalAddressMode(m_addressU);
4488 desc.tAddressMode = toMetalAddressMode(m_addressV);
4489 desc.rAddressMode = toMetalAddressMode(m_addressW);
4490 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4493 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4498 rhiD->registerResource(
this);
4523 e.shadingRateMap.rateMap =
d->rateMap;
4528 rhiD
->d->releaseQueue.append(e);
4529 rhiD->unregisterResource(
this);
4538 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4542 [d->rateMap retain];
4547 rhiD->registerResource(
this);
4556 serializedFormatData.reserve(16);
4568 rhiD->unregisterResource(
this);
4602 serializedFormatData.clear();
4603 auto p =
std::back_inserter(serializedFormatData);
4625 rhiD->registerResource(rpD,
false);
4631 return serializedFormatData;
4653 return d->pixelSize;
4667 const QRhiTextureRenderTargetDescription &desc,
4684 rhiD->unregisterResource(
this);
4689 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4692 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4694 for (
int i = 0; i < colorAttachmentCount; ++i) {
4695 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4701 if (m_desc.depthTexture())
4702 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4703 else if (m_desc.depthStencilBuffer())
4704 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4706 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4711 rhiD->registerResource(rpD,
false);
4718 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4719 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4720 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4724 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4728 Q_ASSERT(texD || rbD);
4729 id<MTLTexture> dst = nil;
4733 if (attIndex == 0) {
4734 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4737 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4740 if (attIndex == 0) {
4741 d->pixelSize = rbD->pixelSize();
4748 colorAtt
.slice = is3D ? it->layer() : 0;
4749 colorAtt
.level = it->level();
4751 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4754 d->fb.colorAtt[attIndex] = colorAtt;
4758 if (hasDepthStencil) {
4759 if (m_desc.depthTexture()) {
4761 d->fb.dsTex = depthTexD
->d->tex;
4762 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4763 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4764 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4766 d->pixelSize = depthTexD->pixelSize();
4771 d->fb.dsTex = depthRbD
->d->tex;
4772 d->fb.hasStencil =
true;
4773 d->fb.depthNeedsStore =
false;
4774 d->fb.preserveDs =
false;
4776 d->pixelSize = depthRbD->pixelSize();
4780 if (m_desc.depthResolveTexture()) {
4782 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4789 if (d->colorAttCount > 0)
4790 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4792 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4794 rhiD->registerResource(
this,
false);
4800 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4803 return d->pixelSize;
4828 sortedBindings.clear();
4833 rhiD->unregisterResource(
this);
4838 if (!sortedBindings.isEmpty())
4842 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4845 rhiD->updateLayoutDesc(
this);
4847 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4848 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4849 if (!sortedBindings.isEmpty())
4850 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4854 boundResourceData.resize(sortedBindings.count());
4856 for (BoundResourceData &bd : boundResourceData)
4857 memset(&bd, 0,
sizeof(BoundResourceData));
4860 rhiD->registerResource(
this,
false);
4866 sortedBindings.clear();
4867 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4868 if (!flags.testFlag(BindingsAreSorted))
4869 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4871 for (BoundResourceData &bd : boundResourceData)
4872 memset(&bd, 0,
sizeof(BoundResourceData));
4896 d->tess.compVs[0].destroy();
4897 d->tess.compVs[1].destroy();
4898 d->tess.compVs[2].destroy();
4900 d->tess.compTesc.destroy();
4901 d->tess.vertTese.destroy();
4903 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4904 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4905 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4906 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4911 if (!
d->ps && !
d->ds
4912 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4913 && !
d->tess.tessControlComputeState)
4921 e.graphicsPipeline.pipelineState =
d->ps;
4922 e.graphicsPipeline.depthStencilState =
d->ds;
4923 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4924 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4927 d->tess.vertexComputeState = {};
4928 d->tess.tessControlComputeState = nil;
4932 rhiD
->d->releaseQueue.append(e);
4933 rhiD->unregisterResource(
this);
4940 case QRhiVertexInputAttribute::Float4:
4941 return MTLVertexFormatFloat4;
4942 case QRhiVertexInputAttribute::Float3:
4943 return MTLVertexFormatFloat3;
4944 case QRhiVertexInputAttribute::Float2:
4945 return MTLVertexFormatFloat2;
4946 case QRhiVertexInputAttribute::Float:
4947 return MTLVertexFormatFloat;
4948 case QRhiVertexInputAttribute::UNormByte4:
4949 return MTLVertexFormatUChar4Normalized;
4950 case QRhiVertexInputAttribute::UNormByte2:
4951 return MTLVertexFormatUChar2Normalized;
4952 case QRhiVertexInputAttribute::UNormByte:
4953 return MTLVertexFormatUCharNormalized;
4954 case QRhiVertexInputAttribute::UInt4:
4955 return MTLVertexFormatUInt4;
4956 case QRhiVertexInputAttribute::UInt3:
4957 return MTLVertexFormatUInt3;
4958 case QRhiVertexInputAttribute::UInt2:
4959 return MTLVertexFormatUInt2;
4960 case QRhiVertexInputAttribute::UInt:
4961 return MTLVertexFormatUInt;
4962 case QRhiVertexInputAttribute::SInt4:
4963 return MTLVertexFormatInt4;
4964 case QRhiVertexInputAttribute::SInt3:
4965 return MTLVertexFormatInt3;
4966 case QRhiVertexInputAttribute::SInt2:
4967 return MTLVertexFormatInt2;
4968 case QRhiVertexInputAttribute::SInt:
4969 return MTLVertexFormatInt;
4970 case QRhiVertexInputAttribute::Half4:
4971 return MTLVertexFormatHalf4;
4972 case QRhiVertexInputAttribute::Half3:
4973 return MTLVertexFormatHalf3;
4974 case QRhiVertexInputAttribute::Half2:
4975 return MTLVertexFormatHalf2;
4976 case QRhiVertexInputAttribute::Half:
4977 return MTLVertexFormatHalf;
4978 case QRhiVertexInputAttribute::UShort4:
4979 return MTLVertexFormatUShort4;
4980 case QRhiVertexInputAttribute::UShort3:
4981 return MTLVertexFormatUShort3;
4982 case QRhiVertexInputAttribute::UShort2:
4983 return MTLVertexFormatUShort2;
4984 case QRhiVertexInputAttribute::UShort:
4985 return MTLVertexFormatUShort;
4986 case QRhiVertexInputAttribute::SShort4:
4987 return MTLVertexFormatShort4;
4988 case QRhiVertexInputAttribute::SShort3:
4989 return MTLVertexFormatShort3;
4990 case QRhiVertexInputAttribute::SShort2:
4991 return MTLVertexFormatShort2;
4992 case QRhiVertexInputAttribute::SShort:
4993 return MTLVertexFormatShort;
4996 return MTLVertexFormatFloat4;
5003 case QRhiGraphicsPipeline::Zero:
5004 return MTLBlendFactorZero;
5005 case QRhiGraphicsPipeline::One:
5006 return MTLBlendFactorOne;
5007 case QRhiGraphicsPipeline::SrcColor:
5008 return MTLBlendFactorSourceColor;
5009 case QRhiGraphicsPipeline::OneMinusSrcColor:
5010 return MTLBlendFactorOneMinusSourceColor;
5011 case QRhiGraphicsPipeline::DstColor:
5012 return MTLBlendFactorDestinationColor;
5013 case QRhiGraphicsPipeline::OneMinusDstColor:
5014 return MTLBlendFactorOneMinusDestinationColor;
5015 case QRhiGraphicsPipeline::SrcAlpha:
5016 return MTLBlendFactorSourceAlpha;
5017 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
5018 return MTLBlendFactorOneMinusSourceAlpha;
5019 case QRhiGraphicsPipeline::DstAlpha:
5020 return MTLBlendFactorDestinationAlpha;
5021 case QRhiGraphicsPipeline::OneMinusDstAlpha:
5022 return MTLBlendFactorOneMinusDestinationAlpha;
5023 case QRhiGraphicsPipeline::ConstantColor:
5024 return MTLBlendFactorBlendColor;
5025 case QRhiGraphicsPipeline::ConstantAlpha:
5026 return MTLBlendFactorBlendAlpha;
5027 case QRhiGraphicsPipeline::OneMinusConstantColor:
5028 return MTLBlendFactorOneMinusBlendColor;
5029 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
5030 return MTLBlendFactorOneMinusBlendAlpha;
5031 case QRhiGraphicsPipeline::SrcAlphaSaturate:
5032 return MTLBlendFactorSourceAlphaSaturated;
5033 case QRhiGraphicsPipeline::Src1Color:
5034 return MTLBlendFactorSource1Color;
5035 case QRhiGraphicsPipeline::OneMinusSrc1Color:
5036 return MTLBlendFactorOneMinusSource1Color;
5037 case QRhiGraphicsPipeline::Src1Alpha:
5038 return MTLBlendFactorSource1Alpha;
5039 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
5040 return MTLBlendFactorOneMinusSource1Alpha;
5043 return MTLBlendFactorZero;
5050 case QRhiGraphicsPipeline::Add:
5051 return MTLBlendOperationAdd;
5052 case QRhiGraphicsPipeline::Subtract:
5053 return MTLBlendOperationSubtract;
5054 case QRhiGraphicsPipeline::ReverseSubtract:
5055 return MTLBlendOperationReverseSubtract;
5056 case QRhiGraphicsPipeline::Min:
5057 return MTLBlendOperationMin;
5058 case QRhiGraphicsPipeline::Max:
5059 return MTLBlendOperationMax;
5062 return MTLBlendOperationAdd;
5069 if (c.testFlag(QRhiGraphicsPipeline::R))
5070 f |= MTLColorWriteMaskRed;
5071 if (c.testFlag(QRhiGraphicsPipeline::G))
5072 f |= MTLColorWriteMaskGreen;
5073 if (c.testFlag(QRhiGraphicsPipeline::B))
5074 f |= MTLColorWriteMaskBlue;
5075 if (c.testFlag(QRhiGraphicsPipeline::A))
5076 f |= MTLColorWriteMaskAlpha;
5083 case QRhiGraphicsPipeline::Never:
5084 return MTLCompareFunctionNever;
5085 case QRhiGraphicsPipeline::Less:
5086 return MTLCompareFunctionLess;
5087 case QRhiGraphicsPipeline::Equal:
5088 return MTLCompareFunctionEqual;
5089 case QRhiGraphicsPipeline::LessOrEqual:
5090 return MTLCompareFunctionLessEqual;
5091 case QRhiGraphicsPipeline::Greater:
5092 return MTLCompareFunctionGreater;
5093 case QRhiGraphicsPipeline::NotEqual:
5094 return MTLCompareFunctionNotEqual;
5095 case QRhiGraphicsPipeline::GreaterOrEqual:
5096 return MTLCompareFunctionGreaterEqual;
5097 case QRhiGraphicsPipeline::Always:
5098 return MTLCompareFunctionAlways;
5101 return MTLCompareFunctionAlways;
5108 case QRhiGraphicsPipeline::StencilZero:
5109 return MTLStencilOperationZero;
5110 case QRhiGraphicsPipeline::Keep:
5111 return MTLStencilOperationKeep;
5112 case QRhiGraphicsPipeline::Replace:
5113 return MTLStencilOperationReplace;
5114 case QRhiGraphicsPipeline::IncrementAndClamp:
5115 return MTLStencilOperationIncrementClamp;
5116 case QRhiGraphicsPipeline::DecrementAndClamp:
5117 return MTLStencilOperationDecrementClamp;
5118 case QRhiGraphicsPipeline::Invert:
5119 return MTLStencilOperationInvert;
5120 case QRhiGraphicsPipeline::IncrementAndWrap:
5121 return MTLStencilOperationIncrementWrap;
5122 case QRhiGraphicsPipeline::DecrementAndWrap:
5123 return MTLStencilOperationDecrementWrap;
5126 return MTLStencilOperationKeep;
5133 case QRhiGraphicsPipeline::Triangles:
5134 return MTLPrimitiveTypeTriangle;
5135 case QRhiGraphicsPipeline::TriangleStrip:
5136 return MTLPrimitiveTypeTriangleStrip;
5137 case QRhiGraphicsPipeline::Lines:
5138 return MTLPrimitiveTypeLine;
5139 case QRhiGraphicsPipeline::LineStrip:
5140 return MTLPrimitiveTypeLineStrip;
5141 case QRhiGraphicsPipeline::Points:
5142 return MTLPrimitiveTypePoint;
5145 return MTLPrimitiveTypeTriangle;
5152 case QRhiGraphicsPipeline::Triangles:
5153 case QRhiGraphicsPipeline::TriangleStrip:
5154 case QRhiGraphicsPipeline::TriangleFan:
5155 return MTLPrimitiveTopologyClassTriangle;
5156 case QRhiGraphicsPipeline::Lines:
5157 case QRhiGraphicsPipeline::LineStrip:
5158 return MTLPrimitiveTopologyClassLine;
5159 case QRhiGraphicsPipeline::Points:
5160 return MTLPrimitiveTopologyClassPoint;
5163 return MTLPrimitiveTopologyClassTriangle;
5170 case QRhiGraphicsPipeline::None:
5171 return MTLCullModeNone;
5172 case QRhiGraphicsPipeline::Front:
5173 return MTLCullModeFront;
5174 case QRhiGraphicsPipeline::Back:
5175 return MTLCullModeBack;
5178 return MTLCullModeNone;
5185 case QRhiGraphicsPipeline::Fill:
5186 return MTLTriangleFillModeFill;
5187 case QRhiGraphicsPipeline::Line:
5188 return MTLTriangleFillModeLines;
5191 return MTLTriangleFillModeFill;
5198 case QShaderDescription::CwTessellationWindingOrder:
5199 return MTLWindingClockwise;
5200 case QShaderDescription::CcwTessellationWindingOrder:
5201 return MTLWindingCounterClockwise;
5204 return MTLWindingCounterClockwise;
5211 case QShaderDescription::EqualTessellationPartitioning:
5212 return MTLTessellationPartitionModePow2;
5213 case QShaderDescription::FractionalEvenTessellationPartitioning:
5214 return MTLTessellationPartitionModeFractionalEven;
5215 case QShaderDescription::FractionalOddTessellationPartitioning:
5216 return MTLTessellationPartitionModeFractionalOdd;
5219 return MTLTessellationPartitionModePow2;
5225 int v = version.version();
5226 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
5230 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
5232 QVarLengthArray<
int, 8> versions;
5233 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
5235 const QList<QShaderKey> shaders = shader.availableShaders();
5239 for (
const int &version : versions) {
5240 key = { QShader::Source::MetalLibShader, version, shaderVariant };
5241 if (shaders.contains(key))
5245 QShaderCode mtllib = shader.shader(key);
5246 if (!mtllib.shader().isEmpty()) {
5247 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
5248 size_t(mtllib.shader().size()),
5249 dispatch_get_global_queue(0, 0),
5250 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
5252 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
5253 dispatch_release(data);
5255 *entryPoint = mtllib.entryPoint();
5259 const QString msg = QString::fromNSString(err.localizedDescription);
5260 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
5264 for (
const int &version : versions) {
5265 key = { QShader::Source::MslShader, version, shaderVariant };
5266 if (shaders.contains(key))
5270 QShaderCode mslSource = shader.shader(key);
5271 if (mslSource.shader().isEmpty()) {
5272 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
5276 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
5277 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
5278 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
5280 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
5288 const QString msg = QString::fromNSString(err.localizedDescription);
5293 *entryPoint = mslSource.entryPoint();
5300 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
5305 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
5309 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
5310 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
5311 rpDesc.colorAttachments[0].blendingEnabled =
false;
5313 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
5314 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
5316 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
5317 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
5318 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
5319 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
5320 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
5321 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
5322 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
5323 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
5324 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
5325 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
5326 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
5333 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
5334 rpDesc.depthAttachmentPixelFormat = fmt;
5335#if defined(Q_OS_MACOS)
5336 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5338 if (fmt != MTLPixelFormatDepth32Float)
5340 rpDesc.stencilAttachmentPixelFormat = fmt;
5344 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5349 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5351 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5352 dsDesc.depthWriteEnabled = m_depthWrite;
5353 if (m_stencilTest) {
5354 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5355 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5356 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5357 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5358 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5359 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5360 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5362 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5363 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5364 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5365 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5366 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5367 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5368 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5374 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5375 d->cullMode = toMetalCullMode(m_cullMode);
5376 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5377 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5378 d->depthBias =
float(m_depthBias);
5379 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5389 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5392 const uint loc = uint(it->location());
5393 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5394 desc.attributes[loc].offset = NSUInteger(it->offset());
5395 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5397 int bindingIndex = 0;
5398 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5399 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5400 it != itEnd; ++it, ++bindingIndex)
5402 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5403 desc.layouts[layoutIdx].stepFunction =
5404 it->classification() == QRhiVertexInputBinding::PerInstance
5405 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5406 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5407 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5408 desc.layouts[layoutIdx].stepRate *= viewCount;
5409 desc.layouts[layoutIdx].stride = it->stride();
5420 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5423 const uint loc = uint(it->location());
5424 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5425 desc.attributes[loc].offset = NSUInteger(it->offset());
5426 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5428 int bindingIndex = 0;
5429 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5430 it != itEnd; ++it, ++bindingIndex)
5432 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5433 if (desc.indexBufferIndex) {
5434 desc.layouts[layoutIdx].stepFunction =
5435 it->classification() == QRhiVertexInputBinding::PerInstance
5436 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5438 desc.layouts[layoutIdx].stepFunction =
5439 it->classification() == QRhiVertexInputBinding::PerInstance
5440 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5442 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5443 desc.layouts[layoutIdx].stride = it->stride();
5450 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5451 rpDesc.binaryArchives = binArchArray;
5459 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5460 const QString msg = QString::fromNSString(err.localizedDescription);
5461 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5470 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5471 d->setupVertexInputDescriptor(vertexDesc);
5473 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5474 rpDesc.vertexDescriptor = vertexDesc;
5482 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5483 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5484 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5485 switch (shaderStage.type()) {
5486 case QRhiShaderStage::Vertex:
5489 [d->vs.func retain];
5490 rpDesc.vertexFunction = d->vs.func;
5492 case QRhiShaderStage::Fragment:
5495 [d->fs.func retain];
5496 rpDesc.fragmentFunction = d->fs.func;
5502 const QShader shader = shaderStage.shader();
5504 QByteArray entryPoint;
5505 QShaderKey activeKey;
5506 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5507 &error, &entryPoint, &activeKey);
5509 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5512 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5514 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5518 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5520 for (QMetalShader &s : rhiD->d->shaderCache)
5522 rhiD->d->shaderCache.clear();
5524 switch (shaderStage.type()) {
5525 case QRhiShaderStage::Vertex:
5528 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5529 d->vs.desc = shader.description();
5530 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5531 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5533 [d->vs.func retain];
5534 rpDesc.vertexFunction = func;
5536 case QRhiShaderStage::Fragment:
5539 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5540 d->fs.desc = shader.description();
5541 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5542 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5544 [d->fs.func retain];
5545 rpDesc.fragmentFunction = func;
5558 if (m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers)
5559 rpDesc.supportIndirectCommandBuffers = YES;
5561 if (m_multiViewCount >= 2)
5562 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5564 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5566 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5567 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5570 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5573 const QString msg = QString::fromNSString(err.localizedDescription);
5574 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5578 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5580 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5583 d->primitiveType = toMetalPrimitiveType(m_topology);
5591 switch (vertexCompVariant) {
5592 case QShader::NonIndexedVertexAsComputeShader:
5594 case QShader::UInt32IndexedVertexAsComputeShader:
5596 case QShader::UInt16IndexedVertexAsComputeShader:
5606 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5607 if (varIndex >= 0 && vertexComputeState[varIndex])
5608 return vertexComputeState[varIndex];
5610 id<MTLFunction> func = nil;
5612 func = compVs[varIndex].func;
5615 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5619 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5620 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5622 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5623 cpDesc.computeFunction = func;
5624 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5625 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5626 if (indexBufferBinding >= 0) {
5627 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5628 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5629 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5630 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5631 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5632 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5635 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5637 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5639 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5640 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5643 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5644 options: MTLPipelineOptionNone
5649 const QString msg = QString::fromNSString(err.localizedDescription);
5650 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5652 vertexComputeState[varIndex] = ps;
5660 if (tessControlComputeState)
5661 return tessControlComputeState;
5663 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5664 cpDesc.computeFunction = compTesc.func;
5666 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5668 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5669 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5672 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5673 options: MTLPipelineOptionNone
5678 const QString msg = QString::fromNSString(err.localizedDescription);
5679 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5681 tessControlComputeState = ps;
5689 return (indices >> index) & 0x1;
5692static inline void takeIndex(quint32 index, quint64 &indices)
5694 indices |= 1 << index;
5703 static const int maxVertexAttributes = 31;
5705 for (
int index = 0; index < maxVertexAttributes; ++index) {
5706 if (!indexTaken(index, indices))
5710 Q_UNREACHABLE_RETURN(-1);
5713static inline int aligned(quint32 offset, quint32 alignment)
5715 return ((offset + alignment - 1) / alignment) * alignment;
5723 for (
const int dim : variable.arrayDims)
5726 if (variable.type == QShaderDescription::VariableType::Struct) {
5727 for (
int element = 0; element < elements; ++element) {
5728 for (
const auto &member : variable.structMembers) {
5729 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5733 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5734 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5737 const quint32 alignment = size;
5738 vertexAlignment =
std::max(vertexAlignment, alignment);
5740 for (
int element = 0; element < elements; ++element) {
5742 offset = aligned(offset, alignment);
5749static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5753 for (
const int dim : variable.arrayDims)
5756 if (variable.type == QShaderDescription::VariableType::Struct) {
5757 for (
int element = 0; element < elements; ++element) {
5758 for (
const auto &member : variable.structMembers) {
5759 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5763 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5764 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5767 const quint32 alignment = size;
5768 vertexAlignment =
std::max(vertexAlignment, alignment);
5770 for (
int element = 0; element < elements; ++element) {
5771 Q_ASSERT(!indexTaken(index, indices));
5774 offset = aligned(offset, alignment);
5776 attributes[index].bufferIndex = binding;
5777 attributes[index].format = toMetalAttributeFormat(format);
5778 attributes[index].offset = offset;
5780 takeIndex(index, indices);
5782 if (indexTaken(index, indices))
5783 index = nextAttributeIndex(indices);
5790static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5792 if (a.size() == b.size()) {
5794 for (
int i = 0; i < a.size() && match; ++i) {
5795 match &= a[i].type == b[i].type
5796 && a[i].arrayDims == b[i].arrayDims
5797 && matches(a[i].structMembers, b[i].structMembers);
5805static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5807 return a.location == b.location
5809 && a.perPatch == b.perPatch
5810 && matches(a.structMembers, b.structMembers);
5859 if (pipeline
->d->ps)
5860 return pipeline
->d->ps;
5862 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5863 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5866 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5867 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5868 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5869 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5870 quint32 offsetInTescOutput = 0;
5871 quint32 offsetInTescPatchOutput = 0;
5872 quint32 offsetInTessFactorBuffer = 0;
5873 quint32 tescOutputAlignment = 0;
5874 quint32 tescPatchOutputAlignment = 0;
5875 quint32 tessFactorAlignment = 0;
5876 QSet<
int> usedBuffers;
5879 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5880 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5881 tescOutVars[tescOutVar.location] = tescOutVar;
5884 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5885 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5886 teseInVars[teseInVar.location] = teseInVar;
5889 quint64 indices = 0;
5891 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5893 int index = tescOutVar.location;
5895 quint32 *offset =
nullptr;
5896 quint32 *alignment =
nullptr;
5898 if (tescOutVar.perPatch) {
5899 binding = tescPatchOutputBufferBinding;
5900 offset = &offsetInTescPatchOutput;
5901 alignment = &tescPatchOutputAlignment;
5903 tescOutVar.arrayDims.removeLast();
5904 binding = tescOutputBufferBinding;
5905 offset = &offsetInTescOutput;
5906 alignment = &tescOutputAlignment;
5909 if (teseInVars.contains(index)) {
5911 if (!matches(teseInVars[index], tescOutVar)) {
5912 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5913 qWarning() <<
" tesc out:" << tescOutVar;
5914 qWarning() <<
" tese in:" << teseInVars[index];
5917 if (binding != -1) {
5918 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5919 usedBuffers << binding;
5921 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5922 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5926 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5927 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5930 teseInVars.remove(tescOutVar.location);
5933 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5934 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5937 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5938 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5939 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5942 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5943 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5944 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5946 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5947 bool tessLevelAdded =
false;
5949 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5951 QShaderDescription::InOutVariable variable;
5953 quint32 *offset =
nullptr;
5954 quint32 *alignment =
nullptr;
5956 switch (builtin.type) {
5957 case QShaderDescription::BuiltinType::PositionBuiltin:
5958 variable.type = QShaderDescription::VariableType::Vec4;
5959 binding = tescOutputBufferBinding;
5960 offset = &offsetInTescOutput;
5961 alignment = &tescOutputAlignment;
5963 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5964 variable.type = QShaderDescription::VariableType::Float;
5965 binding = tescOutputBufferBinding;
5966 offset = &offsetInTescOutput;
5967 alignment = &tescOutputAlignment;
5969 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5970 variable.type = QShaderDescription::VariableType::Float;
5971 variable.arrayDims = builtin.arrayDims;
5972 binding = tescOutputBufferBinding;
5973 offset = &offsetInTescOutput;
5974 alignment = &tescOutputAlignment;
5976 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5977 variable.type = QShaderDescription::VariableType::Half4;
5978 binding = tessFactorBufferBinding;
5979 offset = &offsetInTessFactorBuffer;
5980 tessLevelAdded = trianglesMode;
5981 alignment = &tessFactorAlignment;
5983 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5984 if (trianglesMode) {
5985 if (!tessLevelAdded) {
5986 variable.type = QShaderDescription::VariableType::Half4;
5987 binding = tessFactorBufferBinding;
5988 offsetInTessFactorBuffer = 0;
5989 offset = &offsetInTessFactorBuffer;
5990 alignment = &tessFactorAlignment;
5991 tessLevelAdded =
true;
5993 teseInBuiltins.remove(builtin.type);
5997 variable.type = QShaderDescription::VariableType::Half2;
5998 binding = tessFactorBufferBinding;
5999 offsetInTessFactorBuffer = 8;
6000 offset = &offsetInTessFactorBuffer;
6001 alignment = &tessFactorAlignment;
6009 if (teseInBuiltins.contains(builtin.type)) {
6010 if (binding != -1) {
6011 int index = nextAttributeIndex(indices);
6012 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
6013 usedBuffers << binding;
6015 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
6016 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6019 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6022 teseInBuiltins.remove(builtin.type);
6025 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
6026 switch (builtin.type) {
6027 case QShaderDescription::BuiltinType::PositionBuiltin:
6028 case QShaderDescription::BuiltinType::PointSizeBuiltin:
6029 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
6030 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
6037 if (usedBuffers.contains(tescOutputBufferBinding)) {
6038 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
6039 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
6042 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
6043 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6044 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
6047 if (usedBuffers.contains(tessFactorBufferBinding)) {
6048 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6049 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
6052 rpDesc.vertexDescriptor = vertexDesc;
6053 rpDesc.vertexFunction = vertTese.func;
6054 rpDesc.fragmentFunction = pipeline
->d->fs.func;
6060 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
6062 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
6067 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
6069 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6070 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
6073 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
6076 const QString msg = QString::fromNSString(err.localizedDescription);
6077 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
6081 pipeline->d->ps = ps;
6088 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
6091 for (QMetalBuffer *workBuf : *workBuffers) {
6092 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
6093 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6101 for (QMetalBuffer *workBuf : *workBuffers) {
6102 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
6103 workBuf->setSize(size);
6104 if (workBuf->create()) {
6105 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6116 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6119 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6123 workBuffers->append(buf);
6127 qWarning(
"Failed to acquire work buffer of size %u", size);
6135 QByteArray entryPoint;
6136 QShaderKey activeKey;
6138 const QShaderDescription tescDesc = tesc.description();
6139 const QShaderDescription teseDesc = tese.description();
6140 d->tess.inControlPointCount = uint(m_patchControlPointCount);
6141 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
6142 if (!
d->tess.outControlPointCount)
6143 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
6145 if (!
d->tess.outControlPointCount) {
6146 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
6147 d->tess.enabled =
false;
6148 d->tess.failed =
true;
6152 if (m_multiViewCount >= 2)
6153 qWarning(
"Multiview is not supported with tessellation");
6161 bool variantsPresent[3] = {};
6162 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
6163 for (
const QShaderKey &k : tessVertKeys) {
6164 switch (k.sourceVariant()) {
6165 case QShader::NonIndexedVertexAsComputeShader:
6166 variantsPresent[0] =
true;
6168 case QShader::UInt32IndexedVertexAsComputeShader:
6169 variantsPresent[1] =
true;
6171 case QShader::UInt16IndexedVertexAsComputeShader:
6172 variantsPresent[2] =
true;
6178 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
6179 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
6180 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
6181 "Try passing --msltess to qsb.");
6182 d->tess.enabled =
false;
6183 d->tess.failed =
true;
6188 for (QShader::Variant variant : {
6189 QShader::NonIndexedVertexAsComputeShader,
6190 QShader::UInt32IndexedVertexAsComputeShader,
6191 QShader::UInt16IndexedVertexAsComputeShader })
6193 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
6195 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
6196 d->tess.enabled =
false;
6197 d->tess.failed =
true;
6200 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
6202 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6204 d->tess.enabled =
false;
6205 d->tess.failed =
true;
6208 QMetalShader &compVs(d->tess.compVs[varIndex]);
6211 compVs.desc = tessVert.description();
6212 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
6213 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
6216 if (!d->tess.vsCompPipeline(rhiD, variant)) {
6217 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
6218 d->tess.enabled =
false;
6219 d->tess.failed =
true;
6227 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
6228 if (!tessControlLib) {
6229 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
6230 d->tess.enabled =
false;
6231 d->tess.failed =
true;
6234 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
6235 if (!tessControlFunc) {
6236 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6237 [tessControlLib release];
6238 d->tess.enabled =
false;
6239 d->tess.failed =
true;
6242 d->tess.compTesc.lib = tessControlLib;
6243 d->tess.compTesc.func = tessControlFunc;
6244 d->tess.compTesc.desc = tesc.description();
6245 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
6246 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
6247 if (!
d->tess.tescCompPipeline(rhiD)) {
6248 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
6249 d->tess.enabled =
false;
6250 d->tess.failed =
true;
6255 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
6257 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
6258 d->tess.enabled =
false;
6259 d->tess.failed =
true;
6262 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
6263 if (!tessEvalFunc) {
6264 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6265 [tessEvalLib release];
6266 d->tess.enabled =
false;
6267 d->tess.failed =
true;
6270 d->tess.vertTese.lib = tessEvalLib;
6271 d->tess.vertTese.func = tessEvalFunc;
6272 d->tess.vertTese.desc = tese.description();
6273 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
6274 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
6276 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
6278 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
6279 d->tess.enabled =
false;
6280 d->tess.failed =
true;
6283 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
6285 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6287 d->tess.enabled =
false;
6288 d->tess.failed =
true;
6291 d->fs.lib = fragLib;
6292 d->fs.func = fragFunc;
6293 d->fs.desc = tessFrag.description();
6294 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
6295 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
6297 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
6298 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
6299 d->tess.enabled =
false;
6300 d->tess.failed =
true;
6304 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
6306 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
6320 rhiD->pipelineCreationStart();
6321 if (!rhiD->sanityCheckGraphicsPipeline(
this))
6329 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6330 switch (shaderStage.type()) {
6331 case QRhiShaderStage::Vertex:
6332 tessVert = shaderStage.shader();
6334 case QRhiShaderStage::TessellationControl:
6335 tesc = shaderStage.shader();
6337 case QRhiShaderStage::TessellationEvaluation:
6338 tese = shaderStage.shader();
6340 case QRhiShaderStage::Fragment:
6341 tessFrag = shaderStage.shader();
6347 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6348 d->tess.failed =
false;
6350 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6356 QVarLengthArray<QMetalShader *, 6> shaders;
6357 if (
d->tess.enabled) {
6358 shaders.append(&
d->tess.compVs[0]);
6359 shaders.append(&
d->tess.compVs[1]);
6360 shaders.append(&
d->tess.compVs[2]);
6361 shaders.append(&
d->tess.compTesc);
6362 shaders.append(&
d->tess.vertTese);
6364 shaders.append(&
d->vs);
6366 shaders.append(&
d->fs);
6368 for (QMetalShader *shader : shaders) {
6369 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6370 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6371 shader->nativeResourceBindingMap[binding] = {binding, -1};
6372 int maxNativeBinding = 0;
6373 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6374 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6378 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6383 if (!d->bufferSizeBuffer)
6384 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6390 rhiD->pipelineCreationEnd();
6393 rhiD->registerResource(
this);
6422 e.computePipeline.pipelineState =
d->ps;
6427 rhiD
->d->releaseQueue.append(e);
6428 rhiD->unregisterResource(
this);
6435 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6436 cpDesc.binaryArchives = binArchArray;
6444 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6445 const QString msg = QString::fromNSString(err.localizedDescription);
6446 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6457 rhiD->pipelineCreationStart();
6459 auto cacheIt = rhiD
->d->shaderCache.constFind(m_shaderStage);
6460 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6463 const QShader shader = m_shaderStage.shader();
6465 QByteArray entryPoint;
6466 QShaderKey activeKey;
6467 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6468 &error, &entryPoint, &activeKey);
6470 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6473 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6475 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6481 d->cs.localSize = shader.description().computeShaderLocalSize();
6482 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6483 d->cs.desc = shader.description();
6484 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6487 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6488 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6489 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
6492 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6493 for (QMetalShader &s : rhiD->d->shaderCache)
6495 rhiD
->d->shaderCache.clear();
6497 rhiD
->d->shaderCache.insert(m_shaderStage,
d->cs);
6501 [d->cs.func retain];
6503 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6505 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6506 cpDesc.computeFunction =
d->cs.func;
6508 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6510 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6511 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6514 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6515 options: MTLPipelineOptionNone
6520 const QString msg = QString::fromNSString(err.localizedDescription);
6521 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6526 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6528 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6529 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6533 if (!d->bufferSizeBuffer)
6534 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6540 rhiD->pipelineCreationEnd();
6543 rhiD->registerResource(
this);
6567 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6568 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6569 return &nativeHandlesStruct;
6575 d->currentRenderPassEncoder = nil;
6576 d->currentComputePassEncoder = nil;
6577 d->tessellationComputeEncoder = nil;
6578 d->currentPassRpDesc = nil;
6585 currentTarget =
nullptr;
6593 currentPipelineGeneration = 0;
6596 currentSrbGeneration = 0;
6599 currentIndexOffset = 0;
6600 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6605 currentDepthBiasValues = { 0.0f, 0.0f };
6607 currentViewport = {};
6609 d->currentShaderResourceBindingState = {};
6610 d->currentDepthStencilState = nil;
6612 d->currentVertexInputsBuffers.clear();
6613 d->currentVertexInputOffsets.clear();
6623 d->sem[i] =
nullptr;
6624 d->msaaTex[i] = nil;
6644 dispatch_release(
d->sem[i]);
6645 d->sem[i] =
nullptr;
6650 [d->msaaTex[i] release];
6651 d->msaaTex[i] = nil;
6657 [d->curDrawable release];
6658 d->curDrawable = nil;
6662 rhiD->swapchains.remove(
this);
6663 rhiD->unregisterResource(
this);
6683 CALayer *layer =
nullptr;
6685 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6686 layer = cocoaWindow->contentLayer();
6688 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6691 return static_cast<CAMetalLayer *>(layer);
6700 d.reserved[0] = layerForWindow(window);
6707 CAMetalLayer *layer =
d->layer;
6709 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6712 int height = (
int)layer.bounds.size.height;
6713 int width = (
int)layer.bounds.size.width;
6714 width *= layer.contentsScale;
6715 height *= layer.contentsScale;
6716 return QSize(width, height);
6721 if (f == HDRExtendedSrgbLinear) {
6723 }
else if (f == HDR10) {
6725 }
else if (f == HDRExtendedDisplayP3Linear) {
6739 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6745 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6746 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6748 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6751 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6755 rhiD->registerResource(rpD,
false);
6762 samples = rhiD->effectiveSampleCount(m_sampleCount);
6764 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6765 d->colorFormat = MTLPixelFormatRGBA16Float;
6766 d->rhiColorFormat = QRhiTexture::RGBA16F;
6769 if (m_format == HDR10) {
6770 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6771 d->rhiColorFormat = QRhiTexture::RGB10A2;
6774 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6775 d->rhiColorFormat = QRhiTexture::BGRA8;
6784 dispatch_semaphore_t sem =
d->sem[slot];
6785 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6786 dispatch_semaphore_signal(sem);
6793 const bool needsRegistration = !window || window != m_window;
6795 if (window && window != m_window)
6800 if (needsRegistration || !rhiD->swapchains.contains(
this))
6801 rhiD->swapchains.insert(
this);
6805 if (window->surfaceType() != QSurface::MetalSurface) {
6806 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6810 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6814 if (
d->colorFormat !=
d->layer.pixelFormat)
6815 d->layer.pixelFormat =
d->colorFormat;
6817 if (m_format == HDRExtendedSrgbLinear) {
6818 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6819 d->layer.wantsExtendedDynamicRangeContent = YES;
6820 }
else if (m_format == HDR10) {
6821 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6822 d->layer.wantsExtendedDynamicRangeContent = YES;
6823 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6824 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6825 d->layer.wantsExtendedDynamicRangeContent = YES;
6828 if (m_flags.testFlag(UsedAsTransferSource))
6829 d->layer.framebufferOnly = NO;
6832 if (m_flags.testFlag(NoVSync))
6833 d->layer.displaySyncEnabled = NO;
6836 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6837 d->layer.opaque = NO;
6838 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6843 d->layer.opaque = NO;
6845 d->layer.opaque = YES;
6851 int width = (
int)
d->layer.bounds.size.width;
6852 int height = (
int)
d->layer.bounds.size.height;
6853 CGSize layerSize = CGSizeMake(width, height);
6854 const float scaleFactor =
d->layer.contentsScale;
6855 layerSize.width *= scaleFactor;
6856 layerSize.height *= scaleFactor;
6857 d->layer.drawableSize = layerSize;
6859 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6860 pixelSize = m_currentPixelSize;
6862 [d->layer setDevice: rhiD->d->dev];
6864 [d->curDrawable release];
6865 d->curDrawable = nil;
6876 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6877 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6878 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6879 m_depthStencil->sampleCount(), m_sampleCount);
6881 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6882 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6883 m_depthStencil->setPixelSize(pixelSize);
6884 if (!m_depthStencil->create())
6885 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6886 pixelSize.width(), pixelSize.height());
6888 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6889 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6890 pixelSize.width(), pixelSize.height());
6894 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6895 rtWrapper.d->pixelSize = pixelSize;
6901 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6902 pixelSize.width(), pixelSize.height(), scaleFactor);
6905 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6906 desc.textureType = MTLTextureType2DMultisample;
6907 desc.pixelFormat =
d->colorFormat;
6908 desc.width = NSUInteger(pixelSize.width());
6909 desc.height = NSUInteger(pixelSize.height());
6910 desc.sampleCount = NSUInteger(
samples);
6911 desc.resourceOptions = MTLResourceStorageModePrivate;
6912 desc.storageMode = MTLStorageModePrivate;
6913 desc.usage = MTLTextureUsageRenderTarget;
6915 if (
d->msaaTex[i]) {
6919 e.renderbuffer.texture =
d->msaaTex[i];
6920 rhiD
->d->releaseQueue.append(e);
6922 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6927 rhiD->registerResource(
this);
6943#if defined(Q_OS_MACOS)
6944 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6945 NSScreen *screen = view.window.screen;
6946 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6947 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6948#elif defined(Q_OS_IOS)
6949 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6950 UIScreen *screen = view.window.windowScene.screen;
6951 info.limits.colorComponentValue.maxColorComponentValue =
6952 view.window.windowScene.screen.currentEDRHeadroom;
6953 info.limits.colorComponentValue.maxPotentialColorComponentValue =
6954 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