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 if (m.isIdentity()) {
783 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
784 0.0f, 1.0f, 0.0f, 0.0f,
785 0.0f, 0.0f, 0.5f, 0.5f,
786 0.0f, 0.0f, 0.0f, 1.0f);
795 bool supportsFamilyMac2 =
false;
796 bool supportsFamilyApple3 =
false;
799 supportsFamilyMac2 =
true;
801 supportsFamilyApple3 =
true;
803 supportsFamilyApple3 =
true;
807 if (format == QRhiTexture::BC5)
810 if (!supportsFamilyApple3) {
811 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
813 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
817 if (!supportsFamilyMac2)
818 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
827 case QRhi::MultisampleTexture:
829 case QRhi::MultisampleRenderBuffer:
831 case QRhi::DebugMarkers:
833 case QRhi::Timestamps:
835 case QRhi::Instancing:
837 case QRhi::CustomInstanceStepRate:
839 case QRhi::PrimitiveRestart:
841 case QRhi::NonDynamicUniformBuffers:
843 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
845 case QRhi::NPOTTextureRepeat:
847 case QRhi::RedOrAlpha8IsRed:
849 case QRhi::ElementIndexUint:
853 case QRhi::WideLines:
855 case QRhi::VertexShaderPointSize:
857 case QRhi::BaseVertex:
858 return caps.baseVertexAndInstance;
859 case QRhi::BaseInstance:
860 return caps.baseVertexAndInstance;
861 case QRhi::TriangleFanTopology:
863 case QRhi::ReadBackNonUniformBuffer:
865 case QRhi::ReadBackNonBaseMipLevel:
867 case QRhi::TexelFetch:
869 case QRhi::RenderToNonBaseMipLevel:
871 case QRhi::IntAttributes:
873 case QRhi::ScreenSpaceDerivatives:
875 case QRhi::ReadBackAnyTextureFormat:
877 case QRhi::PipelineCacheDataLoadSave:
879 case QRhi::ImageDataStride:
881 case QRhi::RenderBufferImport:
883 case QRhi::ThreeDimensionalTextures:
885 case QRhi::RenderTo3DTextureSlice:
887 case QRhi::TextureArrays:
889 case QRhi::Tessellation:
891 case QRhi::GeometryShader:
893 case QRhi::TextureArrayRange:
895 case QRhi::NonFillPolygonMode:
897 case QRhi::OneDimensionalTextures:
899 case QRhi::OneDimensionalTextureMipmaps:
901 case QRhi::HalfAttributes:
903 case QRhi::RenderToOneDimensionalTexture:
905 case QRhi::ThreeDimensionalTextureMipmaps:
907 case QRhi::MultiView:
908 return caps.multiView;
909 case QRhi::TextureViewFormat:
911 case QRhi::ResolveDepthStencil:
913 case QRhi::VariableRateShading:
915 case QRhi::VariableRateShadingMap:
916 return caps.shadingRateMap;
917 case QRhi::VariableRateShadingMapWithTexture:
919 case QRhi::PerRenderTargetBlending:
920 case QRhi::SampleVariables:
922 case QRhi::InstanceIndexIncludesBaseInstance:
924 case QRhi::DepthClamp:
925 return caps.depthClamp;
926 case QRhi::DrawIndirect:
928 case QRhi::DrawIndirectMulti:
939 case QRhi::TextureSizeMin:
941 case QRhi::TextureSizeMax:
942 return caps.maxTextureSize;
943 case QRhi::MaxColorAttachments:
945 case QRhi::FramesInFlight:
947 case QRhi::MaxAsyncReadbackFrames:
949 case QRhi::MaxThreadGroupsPerDimension:
951 case QRhi::MaxThreadsPerThreadGroup:
953 case QRhi::MaxThreadGroupX:
955 case QRhi::MaxThreadGroupY:
957 case QRhi::MaxThreadGroupZ:
958 return caps.maxThreadGroupSize;
959 case QRhi::TextureArraySizeMax:
961 case QRhi::MaxUniformBufferRange:
963 case QRhi::MaxVertexInputs:
965 case QRhi::MaxVertexOutputs:
967 case QRhi::ShadingRateImageTileSize:
977 return &nativeHandlesStruct;
982 return driverInfoStruct;
988 result.totalPipelineCreationTime = totalPipelineCreationTime();
998void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
1005 for (QMetalShader &s : d->shaderCache)
1008 d->shaderCache.clear();
1030 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1035 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1040 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1041 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1043 if (![d->binArch serializeToURL: url error: &err]) {
1044 const QString msg = QString::fromNSString(err.localizedDescription);
1046 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1051 if (!f.open(QIODevice::ReadOnly)) {
1052 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1055 const QByteArray blob = f.readAll();
1059 const quint32 dataSize = quint32(blob.size());
1061 data.resize(headerSize + dataSize);
1064 header.rhiId = pipelineCacheRhiId();
1065 header.arch = quint32(
sizeof(
void*));
1066 header.dataSize = quint32(dataSize);
1067 header.osMajor = osMajor;
1068 header.osMinor = osMinor;
1069 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1071 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1072 header.driver[driverStrLen] =
'\0';
1074 memcpy(data.data(), &header, headerSize);
1075 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1085 if (data.size() < qsizetype(headerSize)) {
1086 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1090 const size_t dataOffset = headerSize;
1092 memcpy(&header, data.constData(), headerSize);
1094 const quint32 rhiId = pipelineCacheRhiId();
1095 if (header.rhiId != rhiId) {
1096 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1097 rhiId, header.rhiId);
1101 const quint32 arch = quint32(
sizeof(
void*));
1102 if (header.arch != arch) {
1103 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1108 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1109 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1110 osMajor, osMinor, header.osMajor, header.osMinor);
1114 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1115 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1116 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1120 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1121 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1125 const char *p = data.constData() + dataOffset;
1129 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1132 tmp.write(p, header.dataSize);
1135 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1136 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1137 if (
d->setupBinaryArchive(url))
1138 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1141QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1142 int sampleCount, QRhiRenderBuffer::Flags flags,
1143 QRhiTexture::Format backingFormatHint)
1145 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1149 const QSize &pixelSize,
int depth,
int arraySize,
1150 int sampleCount, QRhiTexture::Flags flags)
1152 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1156 QRhiSampler::Filter mipmapMode,
1157 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1159 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1164 return new QMetalShadingRateMap(
this);
1168 QRhiTextureRenderTarget::Flags flags)
1175 return new QMetalGraphicsPipeline(
this);
1180 return new QMetalComputePipeline(
this);
1185 return new QMetalShaderResourceBindings(
this);
1196 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1199 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1200 if (!map || map->isEmpty())
1203 auto it = map->constFind(binding);
1204 if (it != map->cend())
1215 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1216 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1219 case QMetalShaderResourceBindingsData::VERTEX:
1220 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1221 offsets: offsetBatch.resources.constData()
1222 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1224 case QMetalShaderResourceBindingsData::FRAGMENT:
1225 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1226 offsets: offsetBatch.resources.constData()
1227 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1229 case QMetalShaderResourceBindingsData::COMPUTE:
1230 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1231 offsets: offsetBatch.resources.constData()
1232 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1246 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1249 case QMetalShaderResourceBindingsData::VERTEX:
1250 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1251 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1253 case QMetalShaderResourceBindingsData::FRAGMENT:
1254 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1255 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1257 case QMetalShaderResourceBindingsData::COMPUTE:
1258 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1259 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1273 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1275 switch (encoderStage) {
1276 case QMetalShaderResourceBindingsData::VERTEX:
1277 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1278 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1280 case QMetalShaderResourceBindingsData::FRAGMENT:
1281 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1282 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1284 case QMetalShaderResourceBindingsData::COMPUTE:
1285 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1286 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1308 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1309 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1310 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1311 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1314 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1315 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1316 bindStageTextures(cbD, encoderStage, batch);
1319 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1320 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1321 bindStageSamplers(cbD, encoderStage, batch);
1328 case QMetalShaderResourceBindingsData::VERTEX:
1329 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1330 case QMetalShaderResourceBindingsData::TESSCTRL:
1331 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1332 case QMetalShaderResourceBindingsData::TESSEVAL:
1333 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1334 case QMetalShaderResourceBindingsData::FRAGMENT:
1335 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1336 case QMetalShaderResourceBindingsData::COMPUTE:
1337 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1340 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1345 int dynamicOffsetCount,
1346 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1347 bool offsetOnlyChange,
1348 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1352 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1353 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1355 case QRhiShaderResourceBinding::UniformBuffer:
1357 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1358 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1359 quint32 offset = b->u.ubuf.offset;
1360 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1361 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1362 if (dynOfs.first == b->binding) {
1363 offset = dynOfs.second;
1368 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1369 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1370 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1371 if (nativeBinding >= 0)
1372 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1377 case QRhiShaderResourceBinding::SampledTexture:
1378 case QRhiShaderResourceBinding::Texture:
1379 case QRhiShaderResourceBinding::Sampler:
1381 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1382 for (
int elem = 0; elem < data->count; ++elem) {
1383 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1384 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1386 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1387 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1392 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1393 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1394 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1395 if (textureBinding >= 0 && texD)
1396 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1397 if (samplerBinding >= 0)
1398 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1404 case QRhiShaderResourceBinding::ImageLoad:
1405 case QRhiShaderResourceBinding::ImageStore:
1406 case QRhiShaderResourceBinding::ImageLoadStore:
1408 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1409 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1411 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1412 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1413 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1414 if (nativeBinding >= 0)
1415 bindingData.res[stage].textures.append({ nativeBinding, t });
1420 case QRhiShaderResourceBinding::BufferLoad:
1421 case QRhiShaderResourceBinding::BufferStore:
1422 case QRhiShaderResourceBinding::BufferLoadStore:
1424 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1425 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1426 quint32 offset = b->u.sbuf.offset;
1427 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1428 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1429 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1430 if (nativeBinding >= 0)
1431 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1454 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1455 return a.nativeBinding < b.nativeBinding;
1458 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1459 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1460 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1463 bindingData.res[stage].bufferBatches.finish();
1464 bindingData.res[stage].bufferOffsetBatches.finish();
1466 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1467 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1468 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1470 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1471 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1472 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1473 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1477 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1480 if (offsetOnlyChange)
1483 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1484 return a.nativeBinding < b.nativeBinding;
1487 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1488 return a.nativeBinding < b.nativeBinding;
1491 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1492 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1494 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1495 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1497 bindingData.res[stage].textureBatches.finish();
1498 bindingData.res[stage].samplerBatches.finish();
1500 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1501 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1503 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1504 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1508 bindStageTextures(cbD, stage, batch);
1511 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1512 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1514 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1515 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1519 bindStageSamplers(cbD, stage, batch);
1523 cbD
->d->currentShaderResourceBindingState = bindingData;
1530 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1532 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1533 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1534 cbD
->d->currentDepthStencilState =
d->ds;
1537 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1541 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1544 if (rhiD->caps.depthClamp) {
1546 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1551 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1554 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1557 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1558 slopeScale: d->slopeScaledDepthBias
1575 cbD->currentPipelineGeneration = psD->generation;
1580 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1585 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1586 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1587 workBuf->lastActiveFrameSlot = -1;
1589 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1590 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1591 workBuf->lastActiveFrameSlot = -1;
1594 psD->lastActiveFrameSlot = currentFrameSlot;
1598 int dynamicOffsetCount,
1599 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1608 srb = gfxPsD->m_shaderResourceBindings;
1610 srb = compPsD->m_shaderResourceBindings;
1614 bool hasSlottedResourceInSrb =
false;
1615 bool hasDynamicOffsetInSrb =
false;
1616 bool resNeedsRebind =
false;
1618 bool pipelineChanged =
false;
1631 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1634 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1635 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1638 case QRhiShaderResourceBinding::UniformBuffer:
1641 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1642 sanityCheckResourceOwnership(bufD);
1645 hasSlottedResourceInSrb =
true;
1646 if (b->u.ubuf.hasDynamicOffset)
1647 hasDynamicOffsetInSrb =
true;
1648 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1649 resNeedsRebind =
true;
1650 bd.ubuf.id = bufD->m_id;
1651 bd.ubuf.generation = bufD->generation;
1653 bufD->lastActiveFrameSlot = currentFrameSlot;
1656 case QRhiShaderResourceBinding::SampledTexture:
1657 case QRhiShaderResourceBinding::Texture:
1658 case QRhiShaderResourceBinding::Sampler:
1660 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1661 if (bd.stex.count != data->count) {
1662 bd.stex.count = data->count;
1663 resNeedsRebind =
true;
1665 for (
int elem = 0; elem < data->count; ++elem) {
1668 Q_ASSERT(texD || samplerD);
1669 sanityCheckResourceOwnership(texD);
1670 sanityCheckResourceOwnership(samplerD);
1671 const quint64 texId = texD ? texD->m_id : 0;
1672 const uint texGen = texD ? texD->generation : 0;
1673 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1674 const uint samplerGen = samplerD ? samplerD->generation : 0;
1675 if (texGen != bd.stex.d[elem].texGeneration
1676 || texId != bd.stex.d[elem].texId
1677 || samplerGen != bd.stex.d[elem].samplerGeneration
1678 || samplerId != bd.stex.d[elem].samplerId)
1680 resNeedsRebind =
true;
1681 bd.stex.d[elem].texId = texId;
1682 bd.stex.d[elem].texGeneration = texGen;
1683 bd.stex.d[elem].samplerId = samplerId;
1684 bd.stex.d[elem].samplerGeneration = samplerGen;
1687 texD->lastActiveFrameSlot = currentFrameSlot;
1689 samplerD->lastActiveFrameSlot = currentFrameSlot;
1693 case QRhiShaderResourceBinding::ImageLoad:
1694 case QRhiShaderResourceBinding::ImageStore:
1695 case QRhiShaderResourceBinding::ImageLoadStore:
1698 sanityCheckResourceOwnership(texD);
1699 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1700 resNeedsRebind =
true;
1701 bd.simage.id = texD->m_id;
1702 bd.simage.generation = texD->generation;
1704 texD->lastActiveFrameSlot = currentFrameSlot;
1707 case QRhiShaderResourceBinding::BufferLoad:
1708 case QRhiShaderResourceBinding::BufferStore:
1709 case QRhiShaderResourceBinding::BufferLoadStore:
1712 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1713 sanityCheckResourceOwnership(bufD);
1715 if (needsBufferSizeBuffer) {
1716 for (
int i = 0; i < 6; ++i) {
1717 const QRhiShaderResourceBinding::StageFlag stage =
1718 QRhiShaderResourceBinding::StageFlag(1 << i);
1719 if (b->stage.testFlag(stage)) {
1720 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1726 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1727 resNeedsRebind =
true;
1728 bd.sbuf.id = bufD->m_id;
1729 bd.sbuf.generation = bufD->generation;
1731 bufD->lastActiveFrameSlot = currentFrameSlot;
1740 if (needsBufferSizeBuffer) {
1742 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1746 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1747 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1750 if (gfxPsD
->d->tess.enabled) {
1760 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1761 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1762 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1763 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1764 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1765 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1766 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1767 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1768 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1769 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1770 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1771 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1773 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1774 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
1776 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1777 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
1779 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1780 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
1783 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1784 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
1786 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1787 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
1791 for (
const auto &shader : shaders) {
1793 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1796 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1798 int maxNativeBinding = 0;
1799 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1800 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1802 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1804 Q_ASSERT(offset + size <= bufD->size());
1805 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1807 QMetalShaderResourceBindings::BoundResourceData bd;
1808 bd.sbuf.id = bufD->m_id;
1809 bd.sbuf.generation = bufD->generation;
1810 srbD->boundResourceData.append(bd);
1814 QVarLengthArray<
int, 8> bufferSizeBufferData;
1815 Q_ASSERT(storageBufferSizes.contains(shader.second));
1816 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1817 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1818 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1824 if (bufferSizeBufferData.size() <= index)
1825 bufferSizeBufferData.resize(index + 1);
1827 Q_ASSERT(sizes.contains(block.binding));
1828 bufferSizeBufferData[index] = sizes[block.binding];
1831 QRhiBufferData data;
1832 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1833 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1834 Q_ASSERT(offset + size <= bufD->size());
1835 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1838 offset += ((size + 31) / 32) * 32;
1842 bufD->lastActiveFrameSlot = currentFrameSlot;
1846 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1848 resNeedsRebind =
true;
1851 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1854 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1855 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1859 if (gfxPsD
->d->tess.enabled) {
1862 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1863 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1876 cbD->currentSrbGeneration = srbD->generation;
1879 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1880 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1885 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1886 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1891 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1892 QRhiBatchedBindings<NSUInteger> offsets;
1893 for (
int i = 0; i < bindingCount; ++i) {
1896 bufD->lastActiveFrameSlot = currentFrameSlot;
1897 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1898 buffers.feed(startBinding + i, mtlbuf);
1899 offsets.feed(startBinding + i, bindings[i].second);
1914 || buffers != cbD
->d->currentVertexInputsBuffers
1915 || offsets != cbD
->d->currentVertexInputOffsets)
1918 cbD
->d->currentVertexInputsBuffers = buffers;
1919 cbD
->d->currentVertexInputOffsets = offsets;
1921 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1922 const auto &bufferBatch(buffers.batches[i]);
1923 const auto &offsetBatch(offsets.batches[i]);
1924 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1925 bufferBatch.resources.constData()
1926 offsets: offsetBatch.resources.constData()
1927 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1934 ibufD->lastActiveFrameSlot = currentFrameSlot;
1936 cbD->currentIndexOffset = indexOffset;
1937 cbD->currentIndexFormat = indexFormat;
1947 const QSize outputSize = cbD->currentTarget->pixelSize();
1948 std::array<
float, 4> vp = cbD->currentViewport.viewport();
1949 float x = 0, y = 0, w = 0, h = 0;
1951 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
1954 w = outputSize.width();
1955 h = outputSize.height();
1958 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
1962 s.x = NSUInteger(x);
1963 s.y = NSUInteger(y);
1964 s.width = NSUInteger(w);
1965 s.height = NSUInteger(h);
1966 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1973 QSize outputSize = cbD->currentTarget->pixelSize();
1979 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1980 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1981 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1982 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1983 auto screenSize = [rateMap screenSize];
1984 outputSize = QSize(screenSize.width, screenSize.height);
1991 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1995 vp.originX =
double(x);
1996 vp.originY =
double(y);
1997 vp.width =
double(w);
1998 vp.height =
double(h);
1999 vp.znear =
double(viewport.minDepth());
2000 vp.zfar =
double(viewport.maxDepth());
2002 [cbD->d->currentRenderPassEncoder setViewport: vp];
2004 cbD->currentViewport = viewport;
2018 const QSize outputSize = cbD->currentTarget->pixelSize();
2022 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2026 s.x = NSUInteger(x);
2027 s.y = NSUInteger(y);
2028 s.width = NSUInteger(w);
2029 s.height = NSUInteger(h);
2031 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2041 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2042 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2050 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2056 Q_UNUSED(coarsePixelSize);
2062 if (cbD
->d->currentRenderPassEncoder) {
2063 [cbD->d->currentRenderPassEncoder endEncoding];
2064 cbD->d->currentRenderPassEncoder = nil;
2067 if (!maybeComputeEncoder)
2068 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2070 return maybeComputeEncoder;
2074 id<MTLComputeCommandEncoder> computeEncoder)
2076 if (computeEncoder) {
2077 [computeEncoder endEncoding];
2078 computeEncoder = nil;
2083 switch (cbD->currentTarget->resourceType()) {
2084 case QRhiResource::SwapChainRenderTarget:
2087 case QRhiResource::TextureRenderTarget:
2096 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2098 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2099 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2100 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2103 MTLLoadAction oldDepthLoad;
2104 MTLLoadAction oldStencilLoad;
2106 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2107 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2108 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2110 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2111 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2112 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2115 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2119 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2123 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2124 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2133 if (graphicsPipeline
->d->tess.failed)
2137 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2138 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2142 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2148 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2149 = tempComputeEncoder(cbD, cbD->d->tessellationComputeEncoder);
2150 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2154 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2155 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2156 if (args.type == TessDrawArgs::U16Indexed)
2157 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2158 else if (args.type == TessDrawArgs::U32Indexed)
2159 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2160 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2161 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2162 [computeEncoder setComputePipelineState: computePipelineState];
2167 cbD
->d->currentComputePassEncoder = computeEncoder;
2169 cbD->d->currentComputePassEncoder = nil;
2171 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2172 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2173 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2175 if (outputBufferBinding >= 0) {
2176 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2177 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2180 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2183 if (indexBufferBinding >= 0)
2184 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2186 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2187 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2188 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2189 [computeEncoder setBuffers: bufferBatch.resources.constData()
2190 offsets: offsetBatch.resources.constData()
2191 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2195 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2196 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2198 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2199 args.draw.vertexCount, args.draw.instanceCount)];
2202 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2203 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2208 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2209 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2210 [computeEncoder setComputePipelineState: computePipelineState];
2212 cbD
->d->currentComputePassEncoder = computeEncoder;
2214 cbD->d->currentComputePassEncoder = nil;
2216 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2217 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2218 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2219 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2220 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2221 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2223 if (outputBufferBinding >= 0) {
2224 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2225 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2228 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2231 if (patchOutputBufferBinding >= 0) {
2232 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2233 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2234 if (!tescPatchOutBuf)
2236 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2239 if (tessFactorBufferBinding >= 0) {
2240 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2241 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2244 if (paramsBufferBinding >= 0) {
2246 quint32 inControlPointCount;
2253 params.patchCount = patchCount;
2254 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2255 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2256 memcpy(p, ¶ms,
sizeof(params));
2257 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2260 if (vertOutBuf && inputBufferBinding >= 0)
2261 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2263 int sgSize =
int(computePipelineState.threadExecutionWidth);
2264 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2265 while (wgSize > caps.maxThreadGroupSize) {
2267 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2269 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2270 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2278 endTempComputeEncoding(cbD, cbD
->d->tessellationComputeEncoder);
2279 cbD->d->tessellationComputeEncoder = nil;
2288 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2293 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2294 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2295 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2296 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2298 if (outputBufferBinding >= 0 && tescOutBuf)
2299 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2301 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2302 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2304 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2305 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2306 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2309 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2311 patchCount: patchCount
2312 patchIndexBuffer: nil
2313 patchIndexBufferOffset: 0
2323 if (multiViewCount <= 1)
2327 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2328 if (viewMaskBufBinding == -1) {
2329 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2336 multiViewInfo.viewOffset = 0;
2337 multiViewInfo.viewCount = quint32(multiViewCount);
2341 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2342 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2343 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2344 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2348 *instanceCount *= multiViewCount;
2353 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2362 a.draw.vertexCount = vertexCount;
2363 a.draw.instanceCount = instanceCount;
2364 a.draw.firstVertex = firstVertex;
2365 a.draw.firstInstance = firstInstance;
2370 adjustForMultiViewDraw(&instanceCount, cb);
2372 if (caps.baseVertexAndInstance) {
2373 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2374 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2376 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2377 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2382 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2390 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2391 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2394 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2399 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2400 a.drawIndexed.indexCount = indexCount;
2401 a.drawIndexed.instanceCount = instanceCount;
2402 a.drawIndexed.firstIndex = firstIndex;
2403 a.drawIndexed.vertexOffset = vertexOffset;
2404 a.drawIndexed.firstInstance = firstInstance;
2405 a.drawIndexed.indexBuffer = mtlibuf;
2410 adjustForMultiViewDraw(&instanceCount, cb);
2412 if (caps.baseVertexAndInstance) {
2413 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2414 indexCount: indexCount
2415 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2416 indexBuffer: mtlibuf
2417 indexBufferOffset: indexOffset
2418 instanceCount: instanceCount
2419 baseVertex: vertexOffset
2420 baseInstance: firstInstance];
2422 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2423 indexCount: indexCount
2424 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2425 indexBuffer: mtlibuf
2426 indexBufferOffset: indexOffset
2427 instanceCount: instanceCount];
2432 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2439 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2440 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2442 NSUInteger offset = indirectBufferOffset;
2443 for (quint32 i = 0; i < drawCount; ++i) {
2444 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2445 indirectBuffer: indirectBufMtl
2446 indirectBufferOffset: offset];
2452 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2461 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
2465 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2466 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2479 static const quint32 ICB_DRAW_COUNT_THRESHOLD = 128;
2480 const bool useIcb = cbD->currentGraphicsPipeline
2481 && caps.indirectCommandBuffers
2482 && cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesIndirectDraws)
2483 && drawCount > ICB_DRAW_COUNT_THRESHOLD;
2489 if (!
d->icbEncodePipelineU32) {
2491 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2492 MTLCompileOptions *opts = [MTLCompileOptions
new];
2493 opts.languageVersion = MTLLanguageVersion2_1;
2494 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2497 qWarning(
"Failed to compile ICB encode kernel: %s",
2498 qPrintable(QString::fromNSString(err.localizedDescription)));
2502 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2503 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2505 if (!
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2506 qWarning(
"ICB encode kernel functions not found");
2511 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&err];
2512 if (!
d->icbEncodePipelineU32) {
2513 qWarning(
"Failed to create ICB encode compute pipeline (u32): %s",
2514 qPrintable(QString::fromNSString(err.localizedDescription)));
2519 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&err];
2520 if (!
d->icbEncodePipelineU16) {
2521 qWarning(
"Failed to create ICB encode compute pipeline (u16): %s",
2522 qPrintable(QString::fromNSString(err.localizedDescription)));
2532 if (icbOk && (!
d->icb ||
d->icbCapacity < drawCount)) {
2536 e.lastActiveFrameSlot = currentFrameSlot;
2537 e.stagingIcbBuffer.icb =
d->icb;
2538 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2539 d->releaseQueue.append(e);
2542 d->icbArgumentBuffer = nil;
2544 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2545 icbDesc.commandTypes = MTLIndirectCommandTypeDrawIndexed;
2546 icbDesc.inheritPipelineState = YES;
2547 icbDesc.inheritBuffers = YES;
2548 icbDesc.maxVertexBufferBindCount = 0;
2549 icbDesc.maxFragmentBufferBindCount = 0;
2550 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2551 maxCommandCount:drawCount
2552 options:MTLResourceStorageModePrivate];
2555 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2559 d->icbCapacity = drawCount;
2561 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunctionU32 newArgumentEncoderWithBufferIndex:1];
2562 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2563 options:MTLResourceStorageModeShared];
2564 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2565 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2575 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
2576 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
2577 const quint32 savedIndexOffset = cbD->currentIndexOffset;
2578 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
2581 [cbD->d->currentRenderPassEncoder endEncoding];
2582 cbD->d->currentRenderPassEncoder = nil;
2585 id<MTLComputeCommandEncoder> computeEncoder;
2587 const bool useU16 = (savedIndexFormat == QRhiCommandBuffer::IndexUInt16);
2588 id<MTLComputePipelineState> computePipeline = useU16 ?
d->icbEncodePipelineU16 :
d->icbEncodePipelineU32;
2590 computeEncoder = [cbD->d->cb computeCommandEncoder];
2591 uint32_t drawCountVal = drawCount;
2592 uint32_t metalPrimType = uint32_t(savedPipeline
->d->primitiveType);
2593 uint32_t strideVal = stride;
2595 [computeEncoder setComputePipelineState:computePipeline];
2596 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
2597 [computeEncoder setBuffer:d->icbArgumentBuffer offset:0 atIndex:1];
2598 [computeEncoder setBytes:&drawCountVal length:
sizeof(uint32_t) atIndex:2];
2599 [computeEncoder setBuffer:indexBufMtl offset:savedIndexOffset atIndex:3];
2600 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
2601 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
2602 [computeEncoder useResource:d->icb usage:MTLResourceUsageWrite];
2603 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
2604 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
2606 NSUInteger tw = computePipeline.threadExecutionWidth;
2607 [computeEncoder dispatchThreads:MTLSizeMake(drawCount, 1, 1)
2608 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
2612 endTempComputeEncoding(cbD, computeEncoder);
2621 if (savedFirstVertexBinding >= 0) {
2623 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
2624 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
2625 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
2626 const auto &bufferBatch(savedVertexBuffers.batches[i]);
2627 const auto &offsetBatch(savedVertexOffsets.batches[i]);
2628 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2629 bufferBatch.resources.constData()
2630 offsets: offsetBatch.resources.constData()
2631 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
2632 NSUInteger(bufferBatch.resources.count()))];
2637 cbD->currentIndexOffset = savedIndexOffset;
2638 cbD->currentIndexFormat = savedIndexFormat;
2641 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
2642 usage:MTLResourceUsageRead
2643 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2644 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
2645 usage:MTLResourceUsageRead
2646 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2647 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
2648 withRange:NSMakeRange(0, drawCount)];
2654 NSUInteger offset = indirectBufferOffset;
2655 for (quint32 i = 0; i < drawCount; ++i) {
2656 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2657 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2658 indexBuffer: indexBufMtl
2659 indexBufferOffset: cbD->currentIndexOffset
2660 indirectBuffer: indirectBufMtl
2661 indirectBufferOffset: offset];
2671 NSString *str = [NSString stringWithUTF8String: name.constData()];
2673 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2674 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2676 [cbD->d->cb pushDebugGroup: str];
2685 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2686 [cbD->d->currentRenderPassEncoder popDebugGroup];
2688 [cbD->d->cb popDebugGroup];
2697 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2698 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2703 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2729 currentFrameSlot = swapChainD->currentFrameSlot;
2734 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2742 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2743 if (sc != swapChainD)
2744 sc->waitUntilCompleted(currentFrameSlot);
2747 [d->captureScope beginScope];
2749 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2753 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2760 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2761 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2766 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2769 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2770 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2773 return QRhi::FrameOpSuccess;
2782 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2784 __block
int thisFrameSlot = currentFrameSlot;
2785 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2786 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2787 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2794 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2795 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2796 [drawableTexture release];
2800 if (flags.testFlag(QRhi::SkipPresent)) {
2802 [commandBuffer commit];
2804 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2806 if (swapChainD
->d->layer.presentsWithTransaction) {
2807 [commandBuffer commit];
2809 auto *metalLayer = swapChainD
->d->layer;
2810 auto presentWithTransaction = ^{
2811 [commandBuffer waitUntilScheduled];
2818 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2819 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2820 if (textureSize == surfaceSize) {
2823 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2824 << textureSize <<
"not matching surface size" << surfaceSize;
2828 if (NSThread.currentThread == NSThread.mainThread) {
2829 presentWithTransaction();
2831 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2832 Q_ASSERT(qtMetalLayer);
2834 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2838 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2839 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2845 if (qtMetalLayer.displayLock.tryLockForRead()) {
2847 qtMetalLayer.displayLock.unlock();
2849 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2850 <<
"due to" << qtMetalLayer <<
"needing display";
2856 [commandBuffer commit];
2860 [commandBuffer commit];
2867 [swapChainD->d->curDrawable release];
2868 swapChainD->d->curDrawable = nil;
2870 [d->captureScope endScope];
2874 return QRhi::FrameOpSuccess;
2881 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2883 for (QMetalSwapChain *sc : std::as_const(swapchains))
2884 sc->waitUntilCompleted(currentFrameSlot);
2886 d->ofr.active =
true;
2887 *cb = &
d->ofr.cbWrapper;
2888 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2891 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2892 d->ofr.lastGpuTime = 0;
2895 return QRhi::FrameOpSuccess;
2901 Q_ASSERT(
d->ofr.active);
2902 d->ofr.active =
false;
2904 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2908 [cb waitUntilCompleted];
2910 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2914 return QRhi::FrameOpSuccess;
2919 id<MTLCommandBuffer> cb = nil;
2922 if (
d->ofr.active) {
2925 cb =
d->ofr.cbWrapper.d->cb;
2930 cb = swapChainD->cbWrapper.d->cb;
2934 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2935 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2936 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2941 sc->waitUntilCompleted(i);
2947 [cb waitUntilCompleted];
2951 if (
d->ofr.active) {
2952 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2953 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2955 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2956 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2964 return QRhi::FrameOpSuccess;
2968 const QColor &colorClearValue,
2969 const QRhiDepthStencilClearValue &depthStencilClearValue,
2971 QRhiShadingRateMap *shadingRateMap)
2973 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2974 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2975 colorClearValue.alphaF());
2977 for (uint i = 0; i < uint(colorAttCount); ++i) {
2978 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2979 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2980 rp.colorAttachments[i].clearColor = c;
2983 if (hasDepthStencil) {
2984 rp.depthAttachment.loadAction = MTLLoadActionClear;
2985 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2986 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2987 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2988 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2989 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2993 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3001 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3002 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3003 if (imageSizeBytes > 0)
3004 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3009 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3012 const QPoint dp = subresDesc.destinationTopLeft();
3013 const QByteArray rawData = subresDesc.data();
3014 QImage img = subresDesc.image();
3015 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3016 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3018 if (!img.isNull()) {
3019 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3020 QSize size = img.size();
3021 int bpl = img.bytesPerLine();
3023 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3024 const int sx = subresDesc.sourceTopLeft().x();
3025 const int sy = subresDesc.sourceTopLeft().y();
3026 if (!subresDesc.sourceSize().isEmpty())
3027 size = subresDesc.sourceSize();
3028 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3029 if (size.width() == img.width()) {
3030 const int bpc = qMax(1, img.depth() / 8);
3031 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3032 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3033 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3034 size.height() * img.bytesPerLine());
3036 img = img.copy(sx, sy, size.width(), size.height());
3037 bpl = img.bytesPerLine();
3038 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3039 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3042 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3043 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3046 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3047 sourceOffset: NSUInteger(*curOfs)
3048 sourceBytesPerRow: NSUInteger(bpl)
3049 sourceBytesPerImage: 0
3050 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3051 toTexture: texD->d->tex
3052 destinationSlice: NSUInteger(is3D ? 0 : layer)
3053 destinationLevel: NSUInteger(level)
3054 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3055 options: MTLBlitOptionNone];
3057 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3058 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3059 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3060 const int subresw = subresSize.width();
3061 const int subresh = subresSize.height();
3063 if (subresDesc.sourceSize().isEmpty()) {
3067 w = subresDesc.sourceSize().width();
3068 h = subresDesc.sourceSize().height();
3073 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3075 const int dx = aligned(dp.x(), blockDim.width());
3076 const int dy = aligned(dp.y(), blockDim.height());
3077 if (dx + w != subresw)
3078 w = aligned(w, blockDim.width());
3079 if (dy + h != subresh)
3080 h = aligned(h, blockDim.height());
3082 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3084 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3085 sourceOffset: NSUInteger(*curOfs)
3086 sourceBytesPerRow: bpl
3087 sourceBytesPerImage: 0
3088 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3089 toTexture: texD->d->tex
3090 destinationSlice: NSUInteger(is3D ? 0 : layer)
3091 destinationLevel: NSUInteger(level)
3092 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3093 options: MTLBlitOptionNone];
3095 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3096 }
else if (!rawData.isEmpty()) {
3097 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3098 const int subresw = subresSize.width();
3099 const int subresh = subresSize.height();
3101 if (subresDesc.sourceSize().isEmpty()) {
3105 w = subresDesc.sourceSize().width();
3106 h = subresDesc.sourceSize().height();
3110 if (subresDesc.dataStride())
3111 bpl = subresDesc.dataStride();
3113 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3115 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3117 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3118 sourceOffset: NSUInteger(*curOfs)
3119 sourceBytesPerRow: bpl
3120 sourceBytesPerImage: 0
3121 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3122 toTexture: texD->d->tex
3123 destinationSlice: NSUInteger(is3D ? 0 : layer)
3124 destinationLevel: NSUInteger(level)
3125 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3126 options: MTLBlitOptionNone];
3128 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3130 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3139 id<MTLBlitCommandEncoder> blitEnc = nil;
3140 auto ensureBlit = [&blitEnc, cbD,
this]() {
3142 blitEnc = [cbD->d->cb blitCommandEncoder];
3144 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
3152 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3154 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3155 bufD
->d->pendingUpdates[i].clear();
3156 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3162 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3163 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3165 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3169 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3170 if (bufD->m_type == QRhiBuffer::Dynamic) {
3171 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3173 u.result->data.resize(u.readSize);
3174 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3176 if (u.result->completed)
3177 u.result->completed();
3181 readback.buf = bufD
->d->buf[idx];
3182 readback.offset = u.offset;
3183 readback.readSize = u.readSize;
3184 readback.result = u.result;
3185 d->activeBufferReadbacks.append(readback);
3187 if (bufD->d->managed) {
3190 [blitEnc synchronizeResource:readback.buf];
3201 qsizetype stagingSize = 0;
3202 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3203 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3204 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3205 stagingSize += subresUploadByteSize(subresDesc);
3210 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3211 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3212 options: MTLResourceStorageModeShared];
3214 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3215 qsizetype curOfs = 0;
3216 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3217 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3218 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3219 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
3223 utexD->lastActiveFrameSlot = currentFrameSlot;
3227 e.lastActiveFrameSlot = currentFrameSlot;
3228 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3229 utexD->d->stagingBuf[currentFrameSlot] = nil;
3230 d->releaseQueue.append(e);
3235 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3236 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3237 const QPoint dp = u.desc.destinationTopLeft();
3238 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3239 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3240 const QPoint sp = u.desc.sourceTopLeft();
3243 [blitEnc copyFromTexture: srcD->d->tex
3244 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3245 sourceLevel: NSUInteger(u.desc.sourceLevel())
3246 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3247 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3248 toTexture: dstD->d->tex
3249 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3250 destinationLevel: NSUInteger(u.desc.destinationLevel())
3251 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3253 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3256 readback.activeFrameSlot = currentFrameSlot;
3257 readback.desc = u.rb;
3258 readback.result = u.result;
3267 qWarning(
"Multisample texture cannot be read back");
3270 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3271 if (u.rb.rect().isValid())
3274 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3275 readback.format = texD->m_format;
3277 texD->lastActiveFrameSlot = currentFrameSlot;
3281 if (u.rb.rect().isValid())
3284 rect = QRect({0, 0}, swapChainD->pixelSize);
3285 readback.format = swapChainD
->d->rhiColorFormat;
3289 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3291 readback.pixelSize = rect.size();
3294 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3295 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
3298 [blitEnc copyFromTexture: src
3299 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3300 sourceLevel: NSUInteger(u.rb.level())
3301 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3302 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3303 toBuffer: readback.buf
3304 destinationOffset: 0
3305 destinationBytesPerRow: bpl
3306 destinationBytesPerImage: 0
3307 options: MTLBlitOptionNone];
3309 d->activeTextureReadbacks.append(readback);
3313 [blitEnc generateMipmapsForTexture: utexD->d->tex];
3314 utexD->lastActiveFrameSlot = currentFrameSlot;
3320 [blitEnc popDebugGroup];
3321 [blitEnc endEncoding];
3330 if (bufD
->d->pendingUpdates[slot].isEmpty())
3333 void *p = [bufD->d->buf[slot] contents];
3334 quint32 changeBegin = UINT32_MAX;
3335 quint32 changeEnd = 0;
3336 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3337 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3338 if (u.offset < changeBegin)
3339 changeBegin = u.offset;
3340 if (u.offset + u.data.size() > changeEnd)
3341 changeEnd = u.offset + u.data.size();
3344 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3345 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3348 bufD
->d->pendingUpdates[slot].clear();
3358 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3364 QRhiRenderTarget *rt,
3365 const QColor &colorClearValue,
3366 const QRhiDepthStencilClearValue &depthStencilClearValue,
3367 QRhiResourceUpdateBatch *resourceUpdates,
3373 if (resourceUpdates)
3377 switch (rt->resourceType()) {
3378 case QRhiResource::SwapChainRenderTarget:
3382 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3385 depthStencilClearValue,
3393 if (!swapChainD
->d->curDrawable) {
3394 QMacAutoReleasePool pool;
3395 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3397 if (!swapChainD
->d->curDrawable) {
3398 qWarning(
"No drawable");
3401 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3406 color0.resolveTex = scTex;
3412 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3415 case QRhiResource::TextureRenderTarget:
3419 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3423 depthStencilClearValue,
3425 rtTex->m_desc.shadingRateMap());
3426 if (rtD->fb.preserveColor) {
3427 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3428 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3431 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3432 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3434 int colorAttCount = 0;
3435 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3439 if (it->texture()) {
3440 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3441 if (it->multiViewCount() >= 2)
3442 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3443 }
else if (it->renderBuffer()) {
3444 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3446 if (it->resolveTexture())
3447 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3449 if (rtTex->m_desc.depthStencilBuffer())
3450 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3451 if (rtTex->m_desc.depthTexture()) {
3453 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3454 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3455 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3457 if (rtTex->m_desc.depthResolveTexture())
3458 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3459 if (rtTex->m_desc.shadingRateMap())
3460 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3469 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3470 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3471 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3472 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3473 if (rtD->fb.colorAtt[i].resolveTex) {
3474 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3475 : MTLStoreActionMultisampleResolve;
3476 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3477 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3478 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3483 Q_ASSERT(rtD->fb.dsTex);
3484 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3485 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3486 if (rtD->fb.depthNeedsStore)
3487 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3488 if (rtD->fb.dsResolveTex) {
3489 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3490 : MTLStoreActionMultisampleResolve;
3491 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3492 if (rtD->fb.hasStencil) {
3493 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3494 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3499 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3504 cbD->currentTarget = rt;
3512 [cbD->d->currentRenderPassEncoder endEncoding];
3515 cbD->currentTarget =
nullptr;
3517 if (resourceUpdates)
3522 QRhiResourceUpdateBatch *resourceUpdates,
3528 if (resourceUpdates)
3531 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3541 [cbD->d->currentComputePassEncoder endEncoding];
3544 if (resourceUpdates)
3557 cbD->currentPipelineGeneration = psD->generation;
3559 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3562 psD->lastActiveFrameSlot = currentFrameSlot;
3571 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3572 threadsPerThreadgroup: psD->d->localSize];
3577 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3578 [e.buffer.buffers[i] release];
3583 [e.renderbuffer.texture release];
3588 [e.texture.texture release];
3589 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3590 [e.texture.stagingBuffers[i] release];
3591 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3592 [e.texture.views[i] release];
3597 [e.sampler.samplerState release];
3602 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3604 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3618 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3619 [e.stagingBuffer.buffer release];
3621 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3622 [e.graphicsPipeline.pipelineState release];
3623 [e.graphicsPipeline.depthStencilState release];
3624 [e.graphicsPipeline.tessVertexComputeState[0] release];
3625 [e.graphicsPipeline.tessVertexComputeState[1] release];
3626 [e.graphicsPipeline.tessVertexComputeState[2] release];
3627 [e.graphicsPipeline.tessTessControlComputeState release];
3629 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3630 [e.computePipeline.pipelineState release];
3632 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3633 [e.shadingRateMap.rateMap release];
3635 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
3636 [e.stagingIcbBuffer.icb release];
3637 [e.stagingIcbBuffer.argBuffer release];
3642 d->releaseQueue.removeAt(i);
3649 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3651 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3653 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3654 readback.result->format = readback.format;
3655 readback.result->pixelSize = readback.pixelSize;
3656 readback.result->data.resize(
int(readback.bufSize));
3657 void *p = [readback.buf contents];
3658 memcpy(readback.result->data.data(), p, readback.bufSize);
3659 [readback.buf release];
3661 if (readback.result->completed)
3662 completedCallbacks.append(readback.result->completed);
3664 d->activeTextureReadbacks.remove(i);
3668 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3670 if (forced || currentFrameSlot == readback.activeFrameSlot
3671 || readback.activeFrameSlot < 0) {
3672 readback.result->data.resize(readback.readSize);
3673 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3675 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3677 if (readback.result->completed)
3678 completedCallbacks.append(readback.result->completed);
3680 d->activeBufferReadbacks.remove(i);
3684 for (
auto f : completedCallbacks)
3692 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3712 e.buffer.buffers[i] =
d->buf[i];
3714 d->pendingUpdates[i].clear();
3719 rhiD
->d->releaseQueue.append(e);
3720 rhiD->unregisterResource(
this);
3729 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3730 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3734 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3735 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3738 MTLResourceOptions opts = MTLResourceStorageModeShared;
3742 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3743 opts = MTLResourceStorageModeManaged;
3752 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3754 if (
int(m_usage) == WorkBufPoolUsage)
3759 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3760 if (!m_objectName.isEmpty()) {
3762 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3764 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3765 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3773 rhiD->registerResource(
this);
3785 b.objects[i] = &
d->buf[i];
3790 return { { &
d->buf[0] }, 1 };
3800 Q_ASSERT(m_type == Dynamic);
3802 Q_ASSERT(rhiD->inFrame);
3803 const int slot = rhiD->currentFrameSlot;
3804 void *p = [d->buf[slot] contents];
3805 return static_cast<
char *>(p);
3812 QRHI_RES_RHI(QRhiMetal);
3813 const int slot = rhiD->currentFrameSlot;
3814 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3825 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3827 case QRhiTexture::RGBA8:
3828 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3829 case QRhiTexture::BGRA8:
3830 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3831 case QRhiTexture::R8:
3833 return MTLPixelFormatR8Unorm;
3835 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3837 case QRhiTexture::R8SI:
3838 return MTLPixelFormatR8Sint;
3839 case QRhiTexture::R8UI:
3840 return MTLPixelFormatR8Uint;
3841 case QRhiTexture::RG8:
3843 return MTLPixelFormatRG8Unorm;
3845 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3847 case QRhiTexture::R16:
3848 return MTLPixelFormatR16Unorm;
3849 case QRhiTexture::RG16:
3850 return MTLPixelFormatRG16Unorm;
3851 case QRhiTexture::RED_OR_ALPHA8:
3852 return MTLPixelFormatR8Unorm;
3854 case QRhiTexture::RGBA16F:
3855 return MTLPixelFormatRGBA16Float;
3856 case QRhiTexture::RGBA32F:
3857 return MTLPixelFormatRGBA32Float;
3858 case QRhiTexture::R16F:
3859 return MTLPixelFormatR16Float;
3860 case QRhiTexture::R32F:
3861 return MTLPixelFormatR32Float;
3863 case QRhiTexture::RGB10A2:
3864 return MTLPixelFormatRGB10A2Unorm;
3866 case QRhiTexture::R32SI:
3867 return MTLPixelFormatR32Sint;
3868 case QRhiTexture::R32UI:
3869 return MTLPixelFormatR32Uint;
3870 case QRhiTexture::RG32SI:
3871 return MTLPixelFormatRG32Sint;
3872 case QRhiTexture::RG32UI:
3873 return MTLPixelFormatRG32Uint;
3874 case QRhiTexture::RGBA32SI:
3875 return MTLPixelFormatRGBA32Sint;
3876 case QRhiTexture::RGBA32UI:
3877 return MTLPixelFormatRGBA32Uint;
3880 case QRhiTexture::D16:
3881 return MTLPixelFormatDepth16Unorm;
3882 case QRhiTexture::D24:
3883 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3884 case QRhiTexture::D24S8:
3885 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3887 case QRhiTexture::D16:
3888 return MTLPixelFormatDepth32Float;
3889 case QRhiTexture::D24:
3890 return MTLPixelFormatDepth32Float;
3891 case QRhiTexture::D24S8:
3892 return MTLPixelFormatDepth32Float_Stencil8;
3894 case QRhiTexture::D32F:
3895 return MTLPixelFormatDepth32Float;
3896 case QRhiTexture::D32FS8:
3897 return MTLPixelFormatDepth32Float_Stencil8;
3900 case QRhiTexture::BC1:
3901 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3902 case QRhiTexture::BC2:
3903 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3904 case QRhiTexture::BC3:
3905 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3906 case QRhiTexture::BC4:
3907 return MTLPixelFormatBC4_RUnorm;
3908 case QRhiTexture::BC5:
3909 qWarning(
"QRhiMetal does not support BC5");
3910 return MTLPixelFormatInvalid;
3911 case QRhiTexture::BC6H:
3912 return MTLPixelFormatBC6H_RGBUfloat;
3913 case QRhiTexture::BC7:
3914 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3916 case QRhiTexture::BC1:
3917 case QRhiTexture::BC2:
3918 case QRhiTexture::BC3:
3919 case QRhiTexture::BC4:
3920 case QRhiTexture::BC5:
3921 case QRhiTexture::BC6H:
3922 case QRhiTexture::BC7:
3923 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3924 return MTLPixelFormatInvalid;
3928 case QRhiTexture::ETC2_RGB8:
3929 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3930 case QRhiTexture::ETC2_RGB8A1:
3931 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3932 case QRhiTexture::ETC2_RGBA8:
3933 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3935 case QRhiTexture::ASTC_4x4:
3936 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3937 case QRhiTexture::ASTC_5x4:
3938 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3939 case QRhiTexture::ASTC_5x5:
3940 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3941 case QRhiTexture::ASTC_6x5:
3942 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3943 case QRhiTexture::ASTC_6x6:
3944 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3945 case QRhiTexture::ASTC_8x5:
3946 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3947 case QRhiTexture::ASTC_8x6:
3948 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3949 case QRhiTexture::ASTC_8x8:
3950 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3951 case QRhiTexture::ASTC_10x5:
3952 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3953 case QRhiTexture::ASTC_10x6:
3954 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3955 case QRhiTexture::ASTC_10x8:
3956 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3957 case QRhiTexture::ASTC_10x10:
3958 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3959 case QRhiTexture::ASTC_12x10:
3960 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3961 case QRhiTexture::ASTC_12x12:
3962 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3964 case QRhiTexture::ETC2_RGB8:
3965 if (d->caps.isAppleGPU)
3966 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3967 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3968 return MTLPixelFormatInvalid;
3969 case QRhiTexture::ETC2_RGB8A1:
3970 if (d->caps.isAppleGPU)
3971 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3972 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3973 return MTLPixelFormatInvalid;
3974 case QRhiTexture::ETC2_RGBA8:
3975 if (d->caps.isAppleGPU)
3976 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3977 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3978 return MTLPixelFormatInvalid;
3979 case QRhiTexture::ASTC_4x4:
3980 if (d->caps.isAppleGPU)
3981 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3982 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3983 return MTLPixelFormatInvalid;
3984 case QRhiTexture::ASTC_5x4:
3985 if (d->caps.isAppleGPU)
3986 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3987 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3988 return MTLPixelFormatInvalid;
3989 case QRhiTexture::ASTC_5x5:
3990 if (d->caps.isAppleGPU)
3991 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3992 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3993 return MTLPixelFormatInvalid;
3994 case QRhiTexture::ASTC_6x5:
3995 if (d->caps.isAppleGPU)
3996 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3997 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3998 return MTLPixelFormatInvalid;
3999 case QRhiTexture::ASTC_6x6:
4000 if (d->caps.isAppleGPU)
4001 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
4002 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4003 return MTLPixelFormatInvalid;
4004 case QRhiTexture::ASTC_8x5:
4005 if (d->caps.isAppleGPU)
4006 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
4007 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4008 return MTLPixelFormatInvalid;
4009 case QRhiTexture::ASTC_8x6:
4010 if (d->caps.isAppleGPU)
4011 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
4012 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4013 return MTLPixelFormatInvalid;
4014 case QRhiTexture::ASTC_8x8:
4015 if (d->caps.isAppleGPU)
4016 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
4017 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4018 return MTLPixelFormatInvalid;
4019 case QRhiTexture::ASTC_10x5:
4020 if (d->caps.isAppleGPU)
4021 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
4022 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4023 return MTLPixelFormatInvalid;
4024 case QRhiTexture::ASTC_10x6:
4025 if (d->caps.isAppleGPU)
4026 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
4027 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4028 return MTLPixelFormatInvalid;
4029 case QRhiTexture::ASTC_10x8:
4030 if (d->caps.isAppleGPU)
4031 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
4032 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4033 return MTLPixelFormatInvalid;
4034 case QRhiTexture::ASTC_10x10:
4035 if (d->caps.isAppleGPU)
4036 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
4037 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4038 return MTLPixelFormatInvalid;
4039 case QRhiTexture::ASTC_12x10:
4040 if (d->caps.isAppleGPU)
4041 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
4042 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4043 return MTLPixelFormatInvalid;
4044 case QRhiTexture::ASTC_12x12:
4045 if (d->caps.isAppleGPU)
4046 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
4047 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4048 return MTLPixelFormatInvalid;
4053 return MTLPixelFormatInvalid;
4058 int sampleCount, QRhiRenderBuffer::Flags flags,
4059 QRhiTexture::Format backingFormatHint)
4080 e.renderbuffer.texture =
d->tex;
4085 rhiD
->d->releaseQueue.append(e);
4086 rhiD->unregisterResource(
this);
4095 if (m_pixelSize.isEmpty())
4099 samples = rhiD->effectiveSampleCount(m_sampleCount);
4101 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4102 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4103 desc.width = NSUInteger(m_pixelSize.width());
4104 desc.height = NSUInteger(m_pixelSize.height());
4106 desc.sampleCount = NSUInteger(
samples);
4107 desc.resourceOptions = MTLResourceStorageModePrivate;
4108 desc.usage = MTLTextureUsageRenderTarget;
4113 if (rhiD->caps.isAppleGPU) {
4114 desc.storageMode = MTLStorageModeMemoryless;
4115 d->format = MTLPixelFormatDepth32Float_Stencil8;
4117 desc.storageMode = MTLStorageModePrivate;
4118 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
4119 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
4122 desc.storageMode = MTLStorageModeMemoryless;
4123 d->format = MTLPixelFormatDepth32Float_Stencil8;
4125 desc.pixelFormat =
d->format;
4128 desc.storageMode = MTLStorageModePrivate;
4129 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4130 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
4132 d->format = MTLPixelFormatRGBA8Unorm;
4133 desc.pixelFormat =
d->format;
4140 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4143 if (!m_objectName.isEmpty())
4144 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4148 rhiD->registerResource(
this);
4154 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4155 return m_backingFormatHint;
4157 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4161 int arraySize,
int sampleCount, Flags flags)
4165 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4166 d->stagingBuf[i] = nil;
4168 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4169 d->perLevelViews[i] = nil;
4187 e.texture.texture = d->owns ? d->tex : nil;
4191 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
4192 d->stagingBuf[i] = nil;
4195 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4196 e.texture.views[i] =
d->perLevelViews[i];
4197 d->perLevelViews[i] = nil;
4202 rhiD
->d->releaseQueue.append(e);
4203 rhiD->unregisterResource(
this);
4212 const bool isCube = m_flags.testFlag(CubeMap);
4213 const bool is3D = m_flags.testFlag(ThreeDimensional);
4214 const bool isArray = m_flags.testFlag(TextureArray);
4215 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4216 const bool is1D = m_flags.testFlag(OneDimensional);
4218 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
4219 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
4222 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
4223 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
4224 samples = rhiD->effectiveSampleCount(m_sampleCount);
4227 qWarning(
"Cubemap texture cannot be multisample");
4231 qWarning(
"3D texture cannot be multisample");
4235 qWarning(
"Multisample texture cannot have mipmaps");
4239 if (isCube && is3D) {
4240 qWarning(
"Texture cannot be both cube and 3D");
4243 if (isArray && is3D) {
4244 qWarning(
"Texture cannot be both array and 3D");
4248 qWarning(
"Texture cannot be both 1D and 3D");
4251 if (is1D && isCube) {
4252 qWarning(
"Texture cannot be both 1D and cube");
4255 if (m_depth > 1 && !is3D) {
4256 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
4259 if (m_arraySize > 0 && !isArray) {
4260 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
4263 if (m_arraySize < 1 && isArray) {
4264 qWarning(
"Texture is an array but array size is %d", m_arraySize);
4269 *adjustedSize = size;
4277 if (!prepareCreate(&size))
4280 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4282 const bool isCube = m_flags.testFlag(CubeMap);
4283 const bool is3D = m_flags.testFlag(ThreeDimensional);
4284 const bool isArray = m_flags.testFlag(TextureArray);
4285 const bool is1D = m_flags.testFlag(OneDimensional);
4287 desc.textureType = MTLTextureTypeCube;
4289 desc.textureType = MTLTextureType3D;
4291 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
4292 }
else if (isArray) {
4293 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
4295 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4297 desc.pixelFormat =
d->format;
4298 desc.width = NSUInteger(size.width());
4299 desc.height = NSUInteger(size.height());
4300 desc.depth = is3D ? qMax(1, m_depth) : 1;
4303 desc.sampleCount = NSUInteger(
samples);
4305 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
4306 desc.resourceOptions = MTLResourceStorageModePrivate;
4307 desc.storageMode = MTLStorageModePrivate;
4308 desc.usage = MTLTextureUsageShaderRead;
4309 if (m_flags.testFlag(RenderTarget))
4310 desc.usage |= MTLTextureUsageRenderTarget;
4311 if (m_flags.testFlag(UsedWithLoadStore))
4312 desc.usage |= MTLTextureUsageShaderWrite;
4315 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4318 if (!m_objectName.isEmpty())
4319 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4325 rhiD->registerResource(
this);
4331 id<MTLTexture> tex = id<MTLTexture>(src.object);
4335 if (!prepareCreate())
4345 rhiD->registerResource(
this);
4351 return {quint64(
d->tex), 0};
4357 if (perLevelViews[level])
4358 return perLevelViews[level];
4360 const MTLTextureType type = [tex textureType];
4361 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
4362 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
4363 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4364 levels: NSMakeRange(NSUInteger(level), 1)
4365 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4367 perLevelViews[level] = view;
4372 AddressMode u, AddressMode v, AddressMode w)
4386 if (!
d->samplerState)
4393 e.sampler.samplerState =
d->samplerState;
4394 d->samplerState = nil;
4398 rhiD
->d->releaseQueue.append(e);
4399 rhiD->unregisterResource(
this);
4406 case QRhiSampler::Nearest:
4407 return MTLSamplerMinMagFilterNearest;
4408 case QRhiSampler::Linear:
4409 return MTLSamplerMinMagFilterLinear;
4412 return MTLSamplerMinMagFilterNearest;
4419 case QRhiSampler::None:
4420 return MTLSamplerMipFilterNotMipmapped;
4421 case QRhiSampler::Nearest:
4422 return MTLSamplerMipFilterNearest;
4423 case QRhiSampler::Linear:
4424 return MTLSamplerMipFilterLinear;
4427 return MTLSamplerMipFilterNotMipmapped;
4434 case QRhiSampler::Repeat:
4435 return MTLSamplerAddressModeRepeat;
4436 case QRhiSampler::ClampToEdge:
4437 return MTLSamplerAddressModeClampToEdge;
4438 case QRhiSampler::Mirror:
4439 return MTLSamplerAddressModeMirrorRepeat;
4442 return MTLSamplerAddressModeClampToEdge;
4449 case QRhiSampler::Never:
4450 return MTLCompareFunctionNever;
4451 case QRhiSampler::Less:
4452 return MTLCompareFunctionLess;
4453 case QRhiSampler::Equal:
4454 return MTLCompareFunctionEqual;
4455 case QRhiSampler::LessOrEqual:
4456 return MTLCompareFunctionLessEqual;
4457 case QRhiSampler::Greater:
4458 return MTLCompareFunctionGreater;
4459 case QRhiSampler::NotEqual:
4460 return MTLCompareFunctionNotEqual;
4461 case QRhiSampler::GreaterOrEqual:
4462 return MTLCompareFunctionGreaterEqual;
4463 case QRhiSampler::Always:
4464 return MTLCompareFunctionAlways;
4467 return MTLCompareFunctionNever;
4473 if (
d->samplerState)
4476 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4477 desc.minFilter = toMetalFilter(m_minFilter);
4478 desc.magFilter = toMetalFilter(m_magFilter);
4479 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4480 desc.sAddressMode = toMetalAddressMode(m_addressU);
4481 desc.tAddressMode = toMetalAddressMode(m_addressV);
4482 desc.rAddressMode = toMetalAddressMode(m_addressW);
4483 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4486 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4491 rhiD->registerResource(
this);
4516 e.shadingRateMap.rateMap =
d->rateMap;
4521 rhiD
->d->releaseQueue.append(e);
4522 rhiD->unregisterResource(
this);
4531 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4535 [d->rateMap retain];
4540 rhiD->registerResource(
this);
4549 serializedFormatData.reserve(16);
4561 rhiD->unregisterResource(
this);
4595 serializedFormatData.clear();
4596 auto p =
std::back_inserter(serializedFormatData);
4618 rhiD->registerResource(rpD,
false);
4624 return serializedFormatData;
4646 return d->pixelSize;
4660 const QRhiTextureRenderTargetDescription &desc,
4677 rhiD->unregisterResource(
this);
4682 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4685 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4687 for (
int i = 0; i < colorAttachmentCount; ++i) {
4688 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4694 if (m_desc.depthTexture())
4695 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4696 else if (m_desc.depthStencilBuffer())
4697 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4699 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4704 rhiD->registerResource(rpD,
false);
4711 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4712 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4713 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4717 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4721 Q_ASSERT(texD || rbD);
4722 id<MTLTexture> dst = nil;
4726 if (attIndex == 0) {
4727 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4730 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4733 if (attIndex == 0) {
4734 d->pixelSize = rbD->pixelSize();
4741 colorAtt
.slice = is3D ? it->layer() : 0;
4742 colorAtt
.level = it->level();
4744 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4747 d->fb.colorAtt[attIndex] = colorAtt;
4751 if (hasDepthStencil) {
4752 if (m_desc.depthTexture()) {
4754 d->fb.dsTex = depthTexD
->d->tex;
4755 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4756 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4757 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4759 d->pixelSize = depthTexD->pixelSize();
4764 d->fb.dsTex = depthRbD
->d->tex;
4765 d->fb.hasStencil =
true;
4766 d->fb.depthNeedsStore =
false;
4767 d->fb.preserveDs =
false;
4769 d->pixelSize = depthRbD->pixelSize();
4773 if (m_desc.depthResolveTexture()) {
4775 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4782 if (d->colorAttCount > 0)
4783 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4785 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4787 rhiD->registerResource(
this,
false);
4793 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4796 return d->pixelSize;
4821 sortedBindings.clear();
4826 rhiD->unregisterResource(
this);
4831 if (!sortedBindings.isEmpty())
4835 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4838 rhiD->updateLayoutDesc(
this);
4840 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4841 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4842 if (!sortedBindings.isEmpty())
4843 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4847 boundResourceData.resize(sortedBindings.count());
4849 for (BoundResourceData &bd : boundResourceData)
4850 memset(&bd, 0,
sizeof(BoundResourceData));
4853 rhiD->registerResource(
this,
false);
4859 sortedBindings.clear();
4860 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4861 if (!flags.testFlag(BindingsAreSorted))
4862 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4864 for (BoundResourceData &bd : boundResourceData)
4865 memset(&bd, 0,
sizeof(BoundResourceData));
4889 d->tess.compVs[0].destroy();
4890 d->tess.compVs[1].destroy();
4891 d->tess.compVs[2].destroy();
4893 d->tess.compTesc.destroy();
4894 d->tess.vertTese.destroy();
4896 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4897 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4898 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4899 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4904 if (!
d->ps && !
d->ds
4905 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4906 && !
d->tess.tessControlComputeState)
4914 e.graphicsPipeline.pipelineState =
d->ps;
4915 e.graphicsPipeline.depthStencilState =
d->ds;
4916 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4917 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4920 d->tess.vertexComputeState = {};
4921 d->tess.tessControlComputeState = nil;
4925 rhiD
->d->releaseQueue.append(e);
4926 rhiD->unregisterResource(
this);
4933 case QRhiVertexInputAttribute::Float4:
4934 return MTLVertexFormatFloat4;
4935 case QRhiVertexInputAttribute::Float3:
4936 return MTLVertexFormatFloat3;
4937 case QRhiVertexInputAttribute::Float2:
4938 return MTLVertexFormatFloat2;
4939 case QRhiVertexInputAttribute::Float:
4940 return MTLVertexFormatFloat;
4941 case QRhiVertexInputAttribute::UNormByte4:
4942 return MTLVertexFormatUChar4Normalized;
4943 case QRhiVertexInputAttribute::UNormByte2:
4944 return MTLVertexFormatUChar2Normalized;
4945 case QRhiVertexInputAttribute::UNormByte:
4946 return MTLVertexFormatUCharNormalized;
4947 case QRhiVertexInputAttribute::UInt4:
4948 return MTLVertexFormatUInt4;
4949 case QRhiVertexInputAttribute::UInt3:
4950 return MTLVertexFormatUInt3;
4951 case QRhiVertexInputAttribute::UInt2:
4952 return MTLVertexFormatUInt2;
4953 case QRhiVertexInputAttribute::UInt:
4954 return MTLVertexFormatUInt;
4955 case QRhiVertexInputAttribute::SInt4:
4956 return MTLVertexFormatInt4;
4957 case QRhiVertexInputAttribute::SInt3:
4958 return MTLVertexFormatInt3;
4959 case QRhiVertexInputAttribute::SInt2:
4960 return MTLVertexFormatInt2;
4961 case QRhiVertexInputAttribute::SInt:
4962 return MTLVertexFormatInt;
4963 case QRhiVertexInputAttribute::Half4:
4964 return MTLVertexFormatHalf4;
4965 case QRhiVertexInputAttribute::Half3:
4966 return MTLVertexFormatHalf3;
4967 case QRhiVertexInputAttribute::Half2:
4968 return MTLVertexFormatHalf2;
4969 case QRhiVertexInputAttribute::Half:
4970 return MTLVertexFormatHalf;
4971 case QRhiVertexInputAttribute::UShort4:
4972 return MTLVertexFormatUShort4;
4973 case QRhiVertexInputAttribute::UShort3:
4974 return MTLVertexFormatUShort3;
4975 case QRhiVertexInputAttribute::UShort2:
4976 return MTLVertexFormatUShort2;
4977 case QRhiVertexInputAttribute::UShort:
4978 return MTLVertexFormatUShort;
4979 case QRhiVertexInputAttribute::SShort4:
4980 return MTLVertexFormatShort4;
4981 case QRhiVertexInputAttribute::SShort3:
4982 return MTLVertexFormatShort3;
4983 case QRhiVertexInputAttribute::SShort2:
4984 return MTLVertexFormatShort2;
4985 case QRhiVertexInputAttribute::SShort:
4986 return MTLVertexFormatShort;
4989 return MTLVertexFormatFloat4;
4996 case QRhiGraphicsPipeline::Zero:
4997 return MTLBlendFactorZero;
4998 case QRhiGraphicsPipeline::One:
4999 return MTLBlendFactorOne;
5000 case QRhiGraphicsPipeline::SrcColor:
5001 return MTLBlendFactorSourceColor;
5002 case QRhiGraphicsPipeline::OneMinusSrcColor:
5003 return MTLBlendFactorOneMinusSourceColor;
5004 case QRhiGraphicsPipeline::DstColor:
5005 return MTLBlendFactorDestinationColor;
5006 case QRhiGraphicsPipeline::OneMinusDstColor:
5007 return MTLBlendFactorOneMinusDestinationColor;
5008 case QRhiGraphicsPipeline::SrcAlpha:
5009 return MTLBlendFactorSourceAlpha;
5010 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
5011 return MTLBlendFactorOneMinusSourceAlpha;
5012 case QRhiGraphicsPipeline::DstAlpha:
5013 return MTLBlendFactorDestinationAlpha;
5014 case QRhiGraphicsPipeline::OneMinusDstAlpha:
5015 return MTLBlendFactorOneMinusDestinationAlpha;
5016 case QRhiGraphicsPipeline::ConstantColor:
5017 return MTLBlendFactorBlendColor;
5018 case QRhiGraphicsPipeline::ConstantAlpha:
5019 return MTLBlendFactorBlendAlpha;
5020 case QRhiGraphicsPipeline::OneMinusConstantColor:
5021 return MTLBlendFactorOneMinusBlendColor;
5022 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
5023 return MTLBlendFactorOneMinusBlendAlpha;
5024 case QRhiGraphicsPipeline::SrcAlphaSaturate:
5025 return MTLBlendFactorSourceAlphaSaturated;
5026 case QRhiGraphicsPipeline::Src1Color:
5027 return MTLBlendFactorSource1Color;
5028 case QRhiGraphicsPipeline::OneMinusSrc1Color:
5029 return MTLBlendFactorOneMinusSource1Color;
5030 case QRhiGraphicsPipeline::Src1Alpha:
5031 return MTLBlendFactorSource1Alpha;
5032 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
5033 return MTLBlendFactorOneMinusSource1Alpha;
5036 return MTLBlendFactorZero;
5043 case QRhiGraphicsPipeline::Add:
5044 return MTLBlendOperationAdd;
5045 case QRhiGraphicsPipeline::Subtract:
5046 return MTLBlendOperationSubtract;
5047 case QRhiGraphicsPipeline::ReverseSubtract:
5048 return MTLBlendOperationReverseSubtract;
5049 case QRhiGraphicsPipeline::Min:
5050 return MTLBlendOperationMin;
5051 case QRhiGraphicsPipeline::Max:
5052 return MTLBlendOperationMax;
5055 return MTLBlendOperationAdd;
5062 if (c.testFlag(QRhiGraphicsPipeline::R))
5063 f |= MTLColorWriteMaskRed;
5064 if (c.testFlag(QRhiGraphicsPipeline::G))
5065 f |= MTLColorWriteMaskGreen;
5066 if (c.testFlag(QRhiGraphicsPipeline::B))
5067 f |= MTLColorWriteMaskBlue;
5068 if (c.testFlag(QRhiGraphicsPipeline::A))
5069 f |= MTLColorWriteMaskAlpha;
5076 case QRhiGraphicsPipeline::Never:
5077 return MTLCompareFunctionNever;
5078 case QRhiGraphicsPipeline::Less:
5079 return MTLCompareFunctionLess;
5080 case QRhiGraphicsPipeline::Equal:
5081 return MTLCompareFunctionEqual;
5082 case QRhiGraphicsPipeline::LessOrEqual:
5083 return MTLCompareFunctionLessEqual;
5084 case QRhiGraphicsPipeline::Greater:
5085 return MTLCompareFunctionGreater;
5086 case QRhiGraphicsPipeline::NotEqual:
5087 return MTLCompareFunctionNotEqual;
5088 case QRhiGraphicsPipeline::GreaterOrEqual:
5089 return MTLCompareFunctionGreaterEqual;
5090 case QRhiGraphicsPipeline::Always:
5091 return MTLCompareFunctionAlways;
5094 return MTLCompareFunctionAlways;
5101 case QRhiGraphicsPipeline::StencilZero:
5102 return MTLStencilOperationZero;
5103 case QRhiGraphicsPipeline::Keep:
5104 return MTLStencilOperationKeep;
5105 case QRhiGraphicsPipeline::Replace:
5106 return MTLStencilOperationReplace;
5107 case QRhiGraphicsPipeline::IncrementAndClamp:
5108 return MTLStencilOperationIncrementClamp;
5109 case QRhiGraphicsPipeline::DecrementAndClamp:
5110 return MTLStencilOperationDecrementClamp;
5111 case QRhiGraphicsPipeline::Invert:
5112 return MTLStencilOperationInvert;
5113 case QRhiGraphicsPipeline::IncrementAndWrap:
5114 return MTLStencilOperationIncrementWrap;
5115 case QRhiGraphicsPipeline::DecrementAndWrap:
5116 return MTLStencilOperationDecrementWrap;
5119 return MTLStencilOperationKeep;
5126 case QRhiGraphicsPipeline::Triangles:
5127 return MTLPrimitiveTypeTriangle;
5128 case QRhiGraphicsPipeline::TriangleStrip:
5129 return MTLPrimitiveTypeTriangleStrip;
5130 case QRhiGraphicsPipeline::Lines:
5131 return MTLPrimitiveTypeLine;
5132 case QRhiGraphicsPipeline::LineStrip:
5133 return MTLPrimitiveTypeLineStrip;
5134 case QRhiGraphicsPipeline::Points:
5135 return MTLPrimitiveTypePoint;
5138 return MTLPrimitiveTypeTriangle;
5145 case QRhiGraphicsPipeline::Triangles:
5146 case QRhiGraphicsPipeline::TriangleStrip:
5147 case QRhiGraphicsPipeline::TriangleFan:
5148 return MTLPrimitiveTopologyClassTriangle;
5149 case QRhiGraphicsPipeline::Lines:
5150 case QRhiGraphicsPipeline::LineStrip:
5151 return MTLPrimitiveTopologyClassLine;
5152 case QRhiGraphicsPipeline::Points:
5153 return MTLPrimitiveTopologyClassPoint;
5156 return MTLPrimitiveTopologyClassTriangle;
5163 case QRhiGraphicsPipeline::None:
5164 return MTLCullModeNone;
5165 case QRhiGraphicsPipeline::Front:
5166 return MTLCullModeFront;
5167 case QRhiGraphicsPipeline::Back:
5168 return MTLCullModeBack;
5171 return MTLCullModeNone;
5178 case QRhiGraphicsPipeline::Fill:
5179 return MTLTriangleFillModeFill;
5180 case QRhiGraphicsPipeline::Line:
5181 return MTLTriangleFillModeLines;
5184 return MTLTriangleFillModeFill;
5191 case QShaderDescription::CwTessellationWindingOrder:
5192 return MTLWindingClockwise;
5193 case QShaderDescription::CcwTessellationWindingOrder:
5194 return MTLWindingCounterClockwise;
5197 return MTLWindingCounterClockwise;
5204 case QShaderDescription::EqualTessellationPartitioning:
5205 return MTLTessellationPartitionModePow2;
5206 case QShaderDescription::FractionalEvenTessellationPartitioning:
5207 return MTLTessellationPartitionModeFractionalEven;
5208 case QShaderDescription::FractionalOddTessellationPartitioning:
5209 return MTLTessellationPartitionModeFractionalOdd;
5212 return MTLTessellationPartitionModePow2;
5218 int v = version.version();
5219 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
5223 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
5225 QVarLengthArray<
int, 8> versions;
5226 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
5228 const QList<QShaderKey> shaders = shader.availableShaders();
5232 for (
const int &version : versions) {
5233 key = { QShader::Source::MetalLibShader, version, shaderVariant };
5234 if (shaders.contains(key))
5238 QShaderCode mtllib = shader.shader(key);
5239 if (!mtllib.shader().isEmpty()) {
5240 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
5241 size_t(mtllib.shader().size()),
5242 dispatch_get_global_queue(0, 0),
5243 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
5245 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
5246 dispatch_release(data);
5248 *entryPoint = mtllib.entryPoint();
5252 const QString msg = QString::fromNSString(err.localizedDescription);
5253 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
5257 for (
const int &version : versions) {
5258 key = { QShader::Source::MslShader, version, shaderVariant };
5259 if (shaders.contains(key))
5263 QShaderCode mslSource = shader.shader(key);
5264 if (mslSource.shader().isEmpty()) {
5265 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
5269 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
5270 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
5271 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
5273 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
5281 const QString msg = QString::fromNSString(err.localizedDescription);
5286 *entryPoint = mslSource.entryPoint();
5293 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
5298 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
5302 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
5303 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
5304 rpDesc.colorAttachments[0].blendingEnabled =
false;
5306 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
5307 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
5309 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
5310 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
5311 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
5312 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
5313 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
5314 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
5315 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
5316 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
5317 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
5318 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
5319 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
5326 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
5327 rpDesc.depthAttachmentPixelFormat = fmt;
5328#if defined(Q_OS_MACOS)
5329 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5331 if (fmt != MTLPixelFormatDepth32Float)
5333 rpDesc.stencilAttachmentPixelFormat = fmt;
5337 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5342 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5344 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5345 dsDesc.depthWriteEnabled = m_depthWrite;
5346 if (m_stencilTest) {
5347 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5348 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5349 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5350 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5351 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5352 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5353 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5355 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5356 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5357 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5358 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5359 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5360 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5361 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5367 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5368 d->cullMode = toMetalCullMode(m_cullMode);
5369 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5370 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5371 d->depthBias =
float(m_depthBias);
5372 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5382 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5385 const uint loc = uint(it->location());
5386 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5387 desc.attributes[loc].offset = NSUInteger(it->offset());
5388 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5390 int bindingIndex = 0;
5391 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5392 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5393 it != itEnd; ++it, ++bindingIndex)
5395 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5396 desc.layouts[layoutIdx].stepFunction =
5397 it->classification() == QRhiVertexInputBinding::PerInstance
5398 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5399 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5400 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5401 desc.layouts[layoutIdx].stepRate *= viewCount;
5402 desc.layouts[layoutIdx].stride = it->stride();
5413 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5416 const uint loc = uint(it->location());
5417 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5418 desc.attributes[loc].offset = NSUInteger(it->offset());
5419 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5421 int bindingIndex = 0;
5422 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5423 it != itEnd; ++it, ++bindingIndex)
5425 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5426 if (desc.indexBufferIndex) {
5427 desc.layouts[layoutIdx].stepFunction =
5428 it->classification() == QRhiVertexInputBinding::PerInstance
5429 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5431 desc.layouts[layoutIdx].stepFunction =
5432 it->classification() == QRhiVertexInputBinding::PerInstance
5433 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5435 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5436 desc.layouts[layoutIdx].stride = it->stride();
5443 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5444 rpDesc.binaryArchives = binArchArray;
5452 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5453 const QString msg = QString::fromNSString(err.localizedDescription);
5454 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5463 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5464 d->setupVertexInputDescriptor(vertexDesc);
5466 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5467 rpDesc.vertexDescriptor = vertexDesc;
5475 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5476 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5477 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5478 switch (shaderStage.type()) {
5479 case QRhiShaderStage::Vertex:
5482 [d->vs.func retain];
5483 rpDesc.vertexFunction = d->vs.func;
5485 case QRhiShaderStage::Fragment:
5488 [d->fs.func retain];
5489 rpDesc.fragmentFunction = d->fs.func;
5495 const QShader shader = shaderStage.shader();
5497 QByteArray entryPoint;
5498 QShaderKey activeKey;
5499 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5500 &error, &entryPoint, &activeKey);
5502 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5505 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5507 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5511 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5513 for (QMetalShader &s : rhiD->d->shaderCache)
5515 rhiD->d->shaderCache.clear();
5517 switch (shaderStage.type()) {
5518 case QRhiShaderStage::Vertex:
5521 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5522 d->vs.desc = shader.description();
5523 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5524 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5526 [d->vs.func retain];
5527 rpDesc.vertexFunction = func;
5529 case QRhiShaderStage::Fragment:
5532 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5533 d->fs.desc = shader.description();
5534 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5535 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5537 [d->fs.func retain];
5538 rpDesc.fragmentFunction = func;
5551 if (m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers)
5552 rpDesc.supportIndirectCommandBuffers = YES;
5554 if (m_multiViewCount >= 2)
5555 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5557 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5559 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5560 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5563 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5566 const QString msg = QString::fromNSString(err.localizedDescription);
5567 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5571 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5573 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5576 d->primitiveType = toMetalPrimitiveType(m_topology);
5584 switch (vertexCompVariant) {
5585 case QShader::NonIndexedVertexAsComputeShader:
5587 case QShader::UInt32IndexedVertexAsComputeShader:
5589 case QShader::UInt16IndexedVertexAsComputeShader:
5599 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5600 if (varIndex >= 0 && vertexComputeState[varIndex])
5601 return vertexComputeState[varIndex];
5603 id<MTLFunction> func = nil;
5605 func = compVs[varIndex].func;
5608 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5612 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5613 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5615 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5616 cpDesc.computeFunction = func;
5617 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5618 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5619 if (indexBufferBinding >= 0) {
5620 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5621 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5622 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5623 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5624 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5625 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5628 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5630 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5632 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5633 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5636 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5637 options: MTLPipelineOptionNone
5642 const QString msg = QString::fromNSString(err.localizedDescription);
5643 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5645 vertexComputeState[varIndex] = ps;
5653 if (tessControlComputeState)
5654 return tessControlComputeState;
5656 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5657 cpDesc.computeFunction = compTesc.func;
5659 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5661 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5662 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5665 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5666 options: MTLPipelineOptionNone
5671 const QString msg = QString::fromNSString(err.localizedDescription);
5672 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5674 tessControlComputeState = ps;
5682 return (indices >> index) & 0x1;
5685static inline void takeIndex(quint32 index, quint64 &indices)
5687 indices |= 1 << index;
5696 static const int maxVertexAttributes = 31;
5698 for (
int index = 0; index < maxVertexAttributes; ++index) {
5699 if (!indexTaken(index, indices))
5703 Q_UNREACHABLE_RETURN(-1);
5706static inline int aligned(quint32 offset, quint32 alignment)
5708 return ((offset + alignment - 1) / alignment) * alignment;
5716 for (
const int dim : variable.arrayDims)
5719 if (variable.type == QShaderDescription::VariableType::Struct) {
5720 for (
int element = 0; element < elements; ++element) {
5721 for (
const auto &member : variable.structMembers) {
5722 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5726 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5727 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5730 const quint32 alignment = size;
5731 vertexAlignment =
std::max(vertexAlignment, alignment);
5733 for (
int element = 0; element < elements; ++element) {
5735 offset = aligned(offset, alignment);
5742static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5746 for (
const int dim : variable.arrayDims)
5749 if (variable.type == QShaderDescription::VariableType::Struct) {
5750 for (
int element = 0; element < elements; ++element) {
5751 for (
const auto &member : variable.structMembers) {
5752 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5756 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5757 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5760 const quint32 alignment = size;
5761 vertexAlignment =
std::max(vertexAlignment, alignment);
5763 for (
int element = 0; element < elements; ++element) {
5764 Q_ASSERT(!indexTaken(index, indices));
5767 offset = aligned(offset, alignment);
5769 attributes[index].bufferIndex = binding;
5770 attributes[index].format = toMetalAttributeFormat(format);
5771 attributes[index].offset = offset;
5773 takeIndex(index, indices);
5775 if (indexTaken(index, indices))
5776 index = nextAttributeIndex(indices);
5783static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5785 if (a.size() == b.size()) {
5787 for (
int i = 0; i < a.size() && match; ++i) {
5788 match &= a[i].type == b[i].type
5789 && a[i].arrayDims == b[i].arrayDims
5790 && matches(a[i].structMembers, b[i].structMembers);
5798static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5800 return a.location == b.location
5802 && a.perPatch == b.perPatch
5803 && matches(a.structMembers, b.structMembers);
5852 if (pipeline
->d->ps)
5853 return pipeline
->d->ps;
5855 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5856 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5859 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5860 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5861 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5862 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5863 quint32 offsetInTescOutput = 0;
5864 quint32 offsetInTescPatchOutput = 0;
5865 quint32 offsetInTessFactorBuffer = 0;
5866 quint32 tescOutputAlignment = 0;
5867 quint32 tescPatchOutputAlignment = 0;
5868 quint32 tessFactorAlignment = 0;
5869 QSet<
int> usedBuffers;
5872 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5873 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5874 tescOutVars[tescOutVar.location] = tescOutVar;
5877 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5878 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5879 teseInVars[teseInVar.location] = teseInVar;
5882 quint64 indices = 0;
5884 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5886 int index = tescOutVar.location;
5888 quint32 *offset =
nullptr;
5889 quint32 *alignment =
nullptr;
5891 if (tescOutVar.perPatch) {
5892 binding = tescPatchOutputBufferBinding;
5893 offset = &offsetInTescPatchOutput;
5894 alignment = &tescPatchOutputAlignment;
5896 tescOutVar.arrayDims.removeLast();
5897 binding = tescOutputBufferBinding;
5898 offset = &offsetInTescOutput;
5899 alignment = &tescOutputAlignment;
5902 if (teseInVars.contains(index)) {
5904 if (!matches(teseInVars[index], tescOutVar)) {
5905 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5906 qWarning() <<
" tesc out:" << tescOutVar;
5907 qWarning() <<
" tese in:" << teseInVars[index];
5910 if (binding != -1) {
5911 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5912 usedBuffers << binding;
5914 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5915 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5919 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5920 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5923 teseInVars.remove(tescOutVar.location);
5926 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5927 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5930 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5931 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5932 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5935 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5936 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5937 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5939 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5940 bool tessLevelAdded =
false;
5942 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5944 QShaderDescription::InOutVariable variable;
5946 quint32 *offset =
nullptr;
5947 quint32 *alignment =
nullptr;
5949 switch (builtin.type) {
5950 case QShaderDescription::BuiltinType::PositionBuiltin:
5951 variable.type = QShaderDescription::VariableType::Vec4;
5952 binding = tescOutputBufferBinding;
5953 offset = &offsetInTescOutput;
5954 alignment = &tescOutputAlignment;
5956 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5957 variable.type = QShaderDescription::VariableType::Float;
5958 binding = tescOutputBufferBinding;
5959 offset = &offsetInTescOutput;
5960 alignment = &tescOutputAlignment;
5962 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5963 variable.type = QShaderDescription::VariableType::Float;
5964 variable.arrayDims = builtin.arrayDims;
5965 binding = tescOutputBufferBinding;
5966 offset = &offsetInTescOutput;
5967 alignment = &tescOutputAlignment;
5969 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5970 variable.type = QShaderDescription::VariableType::Half4;
5971 binding = tessFactorBufferBinding;
5972 offset = &offsetInTessFactorBuffer;
5973 tessLevelAdded = trianglesMode;
5974 alignment = &tessFactorAlignment;
5976 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5977 if (trianglesMode) {
5978 if (!tessLevelAdded) {
5979 variable.type = QShaderDescription::VariableType::Half4;
5980 binding = tessFactorBufferBinding;
5981 offsetInTessFactorBuffer = 0;
5982 offset = &offsetInTessFactorBuffer;
5983 alignment = &tessFactorAlignment;
5984 tessLevelAdded =
true;
5986 teseInBuiltins.remove(builtin.type);
5990 variable.type = QShaderDescription::VariableType::Half2;
5991 binding = tessFactorBufferBinding;
5992 offsetInTessFactorBuffer = 8;
5993 offset = &offsetInTessFactorBuffer;
5994 alignment = &tessFactorAlignment;
6002 if (teseInBuiltins.contains(builtin.type)) {
6003 if (binding != -1) {
6004 int index = nextAttributeIndex(indices);
6005 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
6006 usedBuffers << binding;
6008 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
6009 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6012 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6015 teseInBuiltins.remove(builtin.type);
6018 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
6019 switch (builtin.type) {
6020 case QShaderDescription::BuiltinType::PositionBuiltin:
6021 case QShaderDescription::BuiltinType::PointSizeBuiltin:
6022 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
6023 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
6030 if (usedBuffers.contains(tescOutputBufferBinding)) {
6031 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
6032 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
6035 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
6036 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6037 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
6040 if (usedBuffers.contains(tessFactorBufferBinding)) {
6041 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6042 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
6045 rpDesc.vertexDescriptor = vertexDesc;
6046 rpDesc.vertexFunction = vertTese.func;
6047 rpDesc.fragmentFunction = pipeline
->d->fs.func;
6053 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
6055 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
6060 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
6062 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6063 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
6066 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
6069 const QString msg = QString::fromNSString(err.localizedDescription);
6070 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
6074 pipeline->d->ps = ps;
6081 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
6084 for (QMetalBuffer *workBuf : *workBuffers) {
6085 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
6086 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6094 for (QMetalBuffer *workBuf : *workBuffers) {
6095 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
6096 workBuf->setSize(size);
6097 if (workBuf->create()) {
6098 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6109 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6112 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6116 workBuffers->append(buf);
6120 qWarning(
"Failed to acquire work buffer of size %u", size);
6128 QByteArray entryPoint;
6129 QShaderKey activeKey;
6131 const QShaderDescription tescDesc = tesc.description();
6132 const QShaderDescription teseDesc = tese.description();
6133 d->tess.inControlPointCount = uint(m_patchControlPointCount);
6134 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
6135 if (!
d->tess.outControlPointCount)
6136 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
6138 if (!
d->tess.outControlPointCount) {
6139 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
6140 d->tess.enabled =
false;
6141 d->tess.failed =
true;
6145 if (m_multiViewCount >= 2)
6146 qWarning(
"Multiview is not supported with tessellation");
6154 bool variantsPresent[3] = {};
6155 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
6156 for (
const QShaderKey &k : tessVertKeys) {
6157 switch (k.sourceVariant()) {
6158 case QShader::NonIndexedVertexAsComputeShader:
6159 variantsPresent[0] =
true;
6161 case QShader::UInt32IndexedVertexAsComputeShader:
6162 variantsPresent[1] =
true;
6164 case QShader::UInt16IndexedVertexAsComputeShader:
6165 variantsPresent[2] =
true;
6171 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
6172 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
6173 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
6174 "Try passing --msltess to qsb.");
6175 d->tess.enabled =
false;
6176 d->tess.failed =
true;
6181 for (QShader::Variant variant : {
6182 QShader::NonIndexedVertexAsComputeShader,
6183 QShader::UInt32IndexedVertexAsComputeShader,
6184 QShader::UInt16IndexedVertexAsComputeShader })
6186 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
6188 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
6189 d->tess.enabled =
false;
6190 d->tess.failed =
true;
6193 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
6195 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6197 d->tess.enabled =
false;
6198 d->tess.failed =
true;
6201 QMetalShader &compVs(d->tess.compVs[varIndex]);
6204 compVs.desc = tessVert.description();
6205 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
6206 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
6209 if (!d->tess.vsCompPipeline(rhiD, variant)) {
6210 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
6211 d->tess.enabled =
false;
6212 d->tess.failed =
true;
6220 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
6221 if (!tessControlLib) {
6222 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
6223 d->tess.enabled =
false;
6224 d->tess.failed =
true;
6227 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
6228 if (!tessControlFunc) {
6229 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6230 [tessControlLib release];
6231 d->tess.enabled =
false;
6232 d->tess.failed =
true;
6235 d->tess.compTesc.lib = tessControlLib;
6236 d->tess.compTesc.func = tessControlFunc;
6237 d->tess.compTesc.desc = tesc.description();
6238 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
6239 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
6240 if (!
d->tess.tescCompPipeline(rhiD)) {
6241 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
6242 d->tess.enabled =
false;
6243 d->tess.failed =
true;
6248 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
6250 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
6251 d->tess.enabled =
false;
6252 d->tess.failed =
true;
6255 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
6256 if (!tessEvalFunc) {
6257 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6258 [tessEvalLib release];
6259 d->tess.enabled =
false;
6260 d->tess.failed =
true;
6263 d->tess.vertTese.lib = tessEvalLib;
6264 d->tess.vertTese.func = tessEvalFunc;
6265 d->tess.vertTese.desc = tese.description();
6266 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
6267 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
6269 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
6271 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
6272 d->tess.enabled =
false;
6273 d->tess.failed =
true;
6276 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
6278 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6280 d->tess.enabled =
false;
6281 d->tess.failed =
true;
6284 d->fs.lib = fragLib;
6285 d->fs.func = fragFunc;
6286 d->fs.desc = tessFrag.description();
6287 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
6288 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
6290 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
6291 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
6292 d->tess.enabled =
false;
6293 d->tess.failed =
true;
6297 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
6299 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
6313 rhiD->pipelineCreationStart();
6314 if (!rhiD->sanityCheckGraphicsPipeline(
this))
6322 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6323 switch (shaderStage.type()) {
6324 case QRhiShaderStage::Vertex:
6325 tessVert = shaderStage.shader();
6327 case QRhiShaderStage::TessellationControl:
6328 tesc = shaderStage.shader();
6330 case QRhiShaderStage::TessellationEvaluation:
6331 tese = shaderStage.shader();
6333 case QRhiShaderStage::Fragment:
6334 tessFrag = shaderStage.shader();
6340 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6341 d->tess.failed =
false;
6343 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6349 QVarLengthArray<QMetalShader *, 6> shaders;
6350 if (
d->tess.enabled) {
6351 shaders.append(&
d->tess.compVs[0]);
6352 shaders.append(&
d->tess.compVs[1]);
6353 shaders.append(&
d->tess.compVs[2]);
6354 shaders.append(&
d->tess.compTesc);
6355 shaders.append(&
d->tess.vertTese);
6357 shaders.append(&
d->vs);
6359 shaders.append(&
d->fs);
6361 for (QMetalShader *shader : shaders) {
6362 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6363 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6364 shader->nativeResourceBindingMap[binding] = {binding, -1};
6365 int maxNativeBinding = 0;
6366 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6367 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6371 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6376 if (!d->bufferSizeBuffer)
6377 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6383 rhiD->pipelineCreationEnd();
6386 rhiD->registerResource(
this);
6415 e.computePipeline.pipelineState =
d->ps;
6420 rhiD
->d->releaseQueue.append(e);
6421 rhiD->unregisterResource(
this);
6428 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6429 cpDesc.binaryArchives = binArchArray;
6437 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6438 const QString msg = QString::fromNSString(err.localizedDescription);
6439 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6450 rhiD->pipelineCreationStart();
6452 auto cacheIt = rhiD
->d->shaderCache.constFind(m_shaderStage);
6453 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6456 const QShader shader = m_shaderStage.shader();
6458 QByteArray entryPoint;
6459 QShaderKey activeKey;
6460 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6461 &error, &entryPoint, &activeKey);
6463 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6466 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6468 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6474 d->cs.localSize = shader.description().computeShaderLocalSize();
6475 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6476 d->cs.desc = shader.description();
6477 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6480 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6481 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6482 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
6485 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6486 for (QMetalShader &s : rhiD->d->shaderCache)
6488 rhiD
->d->shaderCache.clear();
6490 rhiD
->d->shaderCache.insert(m_shaderStage,
d->cs);
6494 [d->cs.func retain];
6496 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6498 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6499 cpDesc.computeFunction =
d->cs.func;
6501 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6503 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6504 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6507 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6508 options: MTLPipelineOptionNone
6513 const QString msg = QString::fromNSString(err.localizedDescription);
6514 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6519 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6521 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6522 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6526 if (!d->bufferSizeBuffer)
6527 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6533 rhiD->pipelineCreationEnd();
6536 rhiD->registerResource(
this);
6560 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6561 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6562 return &nativeHandlesStruct;
6568 d->currentRenderPassEncoder = nil;
6569 d->currentComputePassEncoder = nil;
6570 d->tessellationComputeEncoder = nil;
6571 d->currentPassRpDesc = nil;
6578 currentTarget =
nullptr;
6586 currentPipelineGeneration = 0;
6589 currentSrbGeneration = 0;
6592 currentIndexOffset = 0;
6593 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6598 currentDepthBiasValues = { 0.0f, 0.0f };
6600 currentViewport = {};
6602 d->currentShaderResourceBindingState = {};
6603 d->currentDepthStencilState = nil;
6605 d->currentVertexInputsBuffers.clear();
6606 d->currentVertexInputOffsets.clear();
6616 d->sem[i] =
nullptr;
6617 d->msaaTex[i] = nil;
6637 dispatch_release(
d->sem[i]);
6638 d->sem[i] =
nullptr;
6643 [d->msaaTex[i] release];
6644 d->msaaTex[i] = nil;
6650 [d->curDrawable release];
6651 d->curDrawable = nil;
6655 rhiD->swapchains.remove(
this);
6656 rhiD->unregisterResource(
this);
6676 CALayer *layer =
nullptr;
6678 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6679 layer = cocoaWindow->contentLayer();
6681 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6684 return static_cast<CAMetalLayer *>(layer);
6693 d.reserved[0] = layerForWindow(window);
6700 CAMetalLayer *layer =
d->layer;
6702 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6705 int height = (
int)layer.bounds.size.height;
6706 int width = (
int)layer.bounds.size.width;
6707 width *= layer.contentsScale;
6708 height *= layer.contentsScale;
6709 return QSize(width, height);
6714 if (f == HDRExtendedSrgbLinear) {
6716 }
else if (f == HDR10) {
6718 }
else if (f == HDRExtendedDisplayP3Linear) {
6732 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6738 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6739 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6741 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6744 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6748 rhiD->registerResource(rpD,
false);
6755 samples = rhiD->effectiveSampleCount(m_sampleCount);
6757 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6758 d->colorFormat = MTLPixelFormatRGBA16Float;
6759 d->rhiColorFormat = QRhiTexture::RGBA16F;
6762 if (m_format == HDR10) {
6763 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6764 d->rhiColorFormat = QRhiTexture::RGB10A2;
6767 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6768 d->rhiColorFormat = QRhiTexture::BGRA8;
6777 dispatch_semaphore_t sem =
d->sem[slot];
6778 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6779 dispatch_semaphore_signal(sem);
6786 const bool needsRegistration = !window || window != m_window;
6788 if (window && window != m_window)
6793 if (needsRegistration || !rhiD->swapchains.contains(
this))
6794 rhiD->swapchains.insert(
this);
6798 if (window->surfaceType() != QSurface::MetalSurface) {
6799 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6803 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6807 if (
d->colorFormat !=
d->layer.pixelFormat)
6808 d->layer.pixelFormat =
d->colorFormat;
6810 if (m_format == HDRExtendedSrgbLinear) {
6811 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6812 d->layer.wantsExtendedDynamicRangeContent = YES;
6813 }
else if (m_format == HDR10) {
6814 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6815 d->layer.wantsExtendedDynamicRangeContent = YES;
6816 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6817 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6818 d->layer.wantsExtendedDynamicRangeContent = YES;
6821 if (m_flags.testFlag(UsedAsTransferSource))
6822 d->layer.framebufferOnly = NO;
6825 if (m_flags.testFlag(NoVSync))
6826 d->layer.displaySyncEnabled = NO;
6829 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6830 d->layer.opaque = NO;
6831 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6836 d->layer.opaque = NO;
6838 d->layer.opaque = YES;
6844 int width = (
int)
d->layer.bounds.size.width;
6845 int height = (
int)
d->layer.bounds.size.height;
6846 CGSize layerSize = CGSizeMake(width, height);
6847 const float scaleFactor =
d->layer.contentsScale;
6848 layerSize.width *= scaleFactor;
6849 layerSize.height *= scaleFactor;
6850 d->layer.drawableSize = layerSize;
6852 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6853 pixelSize = m_currentPixelSize;
6855 [d->layer setDevice: rhiD->d->dev];
6857 [d->curDrawable release];
6858 d->curDrawable = nil;
6869 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6870 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6871 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6872 m_depthStencil->sampleCount(), m_sampleCount);
6874 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6875 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6876 m_depthStencil->setPixelSize(pixelSize);
6877 if (!m_depthStencil->create())
6878 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6879 pixelSize.width(), pixelSize.height());
6881 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6882 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6883 pixelSize.width(), pixelSize.height());
6887 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6888 rtWrapper.d->pixelSize = pixelSize;
6894 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6895 pixelSize.width(), pixelSize.height(), scaleFactor);
6898 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6899 desc.textureType = MTLTextureType2DMultisample;
6900 desc.pixelFormat =
d->colorFormat;
6901 desc.width = NSUInteger(pixelSize.width());
6902 desc.height = NSUInteger(pixelSize.height());
6903 desc.sampleCount = NSUInteger(
samples);
6904 desc.resourceOptions = MTLResourceStorageModePrivate;
6905 desc.storageMode = MTLStorageModePrivate;
6906 desc.usage = MTLTextureUsageRenderTarget;
6908 if (
d->msaaTex[i]) {
6912 e.renderbuffer.texture =
d->msaaTex[i];
6913 rhiD
->d->releaseQueue.append(e);
6915 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6920 rhiD->registerResource(
this);
6936#if defined(Q_OS_MACOS)
6937 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6938 NSScreen *screen = view.window.screen;
6939 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6940 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6941#elif defined(Q_OS_IOS)
6942 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6943 UIScreen *screen = view.window.windowScene.screen;
6944 info.limits.colorComponentValue.maxColorComponentValue =
6945 view.window.windowScene.screen.currentEDRHeadroom;
6946 info.limits.colorComponentValue.maxPotentialColorComponentValue =
6947 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