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:
929 case QRhi::ShaderDrawParameters:
940 case QRhi::TextureSizeMin:
942 case QRhi::TextureSizeMax:
943 return caps.maxTextureSize;
944 case QRhi::MaxColorAttachments:
946 case QRhi::FramesInFlight:
948 case QRhi::MaxAsyncReadbackFrames:
950 case QRhi::MaxThreadGroupsPerDimension:
952 case QRhi::MaxThreadsPerThreadGroup:
954 case QRhi::MaxThreadGroupX:
956 case QRhi::MaxThreadGroupY:
958 case QRhi::MaxThreadGroupZ:
959 return caps.maxThreadGroupSize;
960 case QRhi::TextureArraySizeMax:
962 case QRhi::MaxUniformBufferRange:
964 case QRhi::MaxVertexInputs:
966 case QRhi::MaxVertexOutputs:
968 case QRhi::ShadingRateImageTileSize:
978 return &nativeHandlesStruct;
983 return driverInfoStruct;
989 result.totalPipelineCreationTime = totalPipelineCreationTime();
999void QRhiMetal::setQueueSubmitParams(QRhiNativeHandles *)
1006 for (QMetalShader &s : d->shaderCache)
1009 d->shaderCache.clear();
1031 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1036 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1041 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1042 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1044 if (![d->binArch serializeToURL: url error: &err]) {
1045 const QString msg = QString::fromNSString(err.localizedDescription);
1047 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1052 if (!f.open(QIODevice::ReadOnly)) {
1053 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1056 const QByteArray blob = f.readAll();
1060 const quint32 dataSize = quint32(blob.size());
1062 data.resize(headerSize + dataSize);
1065 header.rhiId = pipelineCacheRhiId();
1066 header.arch = quint32(
sizeof(
void*));
1067 header.dataSize = quint32(dataSize);
1068 header.osMajor = osMajor;
1069 header.osMinor = osMinor;
1070 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1072 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1073 header.driver[driverStrLen] =
'\0';
1075 memcpy(data.data(), &header, headerSize);
1076 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1086 if (data.size() < qsizetype(headerSize)) {
1087 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1091 const size_t dataOffset = headerSize;
1093 memcpy(&header, data.constData(), headerSize);
1095 const quint32 rhiId = pipelineCacheRhiId();
1096 if (header.rhiId != rhiId) {
1097 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1098 rhiId, header.rhiId);
1102 const quint32 arch = quint32(
sizeof(
void*));
1103 if (header.arch != arch) {
1104 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1109 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1110 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1111 osMajor, osMinor, header.osMajor, header.osMinor);
1115 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1116 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1117 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1121 if (data.size() < qsizetype(dataOffset + header.dataSize)) {
1122 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1126 const char *p = data.constData() + dataOffset;
1130 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1133 tmp.write(p, header.dataSize);
1136 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1137 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1138 if (
d->setupBinaryArchive(url))
1139 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1142QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1143 int sampleCount, QRhiRenderBuffer::Flags flags,
1144 QRhiTexture::Format backingFormatHint)
1146 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1150 const QSize &pixelSize,
int depth,
int arraySize,
1151 int sampleCount, QRhiTexture::Flags flags)
1153 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1157 QRhiSampler::Filter mipmapMode,
1158 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1160 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1165 return new QMetalShadingRateMap(
this);
1169 QRhiTextureRenderTarget::Flags flags)
1176 return new QMetalGraphicsPipeline(
this);
1181 return new QMetalComputePipeline(
this);
1186 return new QMetalShaderResourceBindings(
this);
1197 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1200 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1201 if (!map || map->isEmpty())
1204 auto it = map->constFind(binding);
1205 if (it != map->cend())
1216 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1217 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1220 case QMetalShaderResourceBindingsData::VERTEX:
1221 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1222 offsets: offsetBatch.resources.constData()
1223 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1225 case QMetalShaderResourceBindingsData::FRAGMENT:
1226 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1227 offsets: offsetBatch.resources.constData()
1228 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1230 case QMetalShaderResourceBindingsData::COMPUTE:
1231 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1232 offsets: offsetBatch.resources.constData()
1233 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1247 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1250 case QMetalShaderResourceBindingsData::VERTEX:
1251 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1252 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1254 case QMetalShaderResourceBindingsData::FRAGMENT:
1255 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1256 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1258 case QMetalShaderResourceBindingsData::COMPUTE:
1259 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1260 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1274 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1276 switch (encoderStage) {
1277 case QMetalShaderResourceBindingsData::VERTEX:
1278 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1279 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1281 case QMetalShaderResourceBindingsData::FRAGMENT:
1282 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1283 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1285 case QMetalShaderResourceBindingsData::COMPUTE:
1286 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1287 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1309 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1310 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1311 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1312 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1315 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1316 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1317 bindStageTextures(cbD, encoderStage, batch);
1320 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1321 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1322 bindStageSamplers(cbD, encoderStage, batch);
1329 case QMetalShaderResourceBindingsData::VERTEX:
1330 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1331 case QMetalShaderResourceBindingsData::TESSCTRL:
1332 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1333 case QMetalShaderResourceBindingsData::TESSEVAL:
1334 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1335 case QMetalShaderResourceBindingsData::FRAGMENT:
1336 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1337 case QMetalShaderResourceBindingsData::COMPUTE:
1338 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1341 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1346 int dynamicOffsetCount,
1347 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1348 bool offsetOnlyChange,
1349 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES])
1353 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1354 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1356 case QRhiShaderResourceBinding::UniformBuffer:
1358 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1359 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1360 quint32 offset = b->u.ubuf.offset;
1361 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1362 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1363 if (dynOfs.first == b->binding) {
1364 offset = dynOfs.second;
1369 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1370 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1371 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1372 if (nativeBinding >= 0)
1373 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1378 case QRhiShaderResourceBinding::SampledTexture:
1379 case QRhiShaderResourceBinding::Texture:
1380 case QRhiShaderResourceBinding::Sampler:
1382 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1383 for (
int elem = 0; elem < data->count; ++elem) {
1384 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1385 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1387 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1388 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1393 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1394 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1395 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1396 if (textureBinding >= 0 && texD)
1397 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1398 if (samplerBinding >= 0)
1399 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1405 case QRhiShaderResourceBinding::ImageLoad:
1406 case QRhiShaderResourceBinding::ImageStore:
1407 case QRhiShaderResourceBinding::ImageLoadStore:
1409 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1410 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1412 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1413 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1414 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1415 if (nativeBinding >= 0)
1416 bindingData.res[stage].textures.append({ nativeBinding, t });
1421 case QRhiShaderResourceBinding::BufferLoad:
1422 case QRhiShaderResourceBinding::BufferStore:
1423 case QRhiShaderResourceBinding::BufferLoadStore:
1425 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1426 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1427 quint32 offset = b->u.sbuf.offset;
1428 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1429 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1430 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1431 if (nativeBinding >= 0)
1432 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1455 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1456 return a.nativeBinding < b.nativeBinding;
1459 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1460 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1461 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1464 bindingData.res[stage].bufferBatches.finish();
1465 bindingData.res[stage].bufferOffsetBatches.finish();
1467 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1468 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1469 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1471 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1472 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1473 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1474 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1478 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1481 if (offsetOnlyChange)
1484 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1485 return a.nativeBinding < b.nativeBinding;
1488 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1489 return a.nativeBinding < b.nativeBinding;
1492 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1493 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1495 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1496 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1498 bindingData.res[stage].textureBatches.finish();
1499 bindingData.res[stage].samplerBatches.finish();
1501 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1502 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1504 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1505 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1509 bindStageTextures(cbD, stage, batch);
1512 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1513 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1515 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1516 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1520 bindStageSamplers(cbD, stage, batch);
1524 cbD
->d->currentShaderResourceBindingState = bindingData;
1531 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1533 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1534 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1535 cbD
->d->currentDepthStencilState =
d->ds;
1538 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1542 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1545 if (rhiD->caps.depthClamp) {
1547 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1552 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1555 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1558 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1559 slopeScale: d->slopeScaledDepthBias
1576 cbD->currentPipelineGeneration = psD->generation;
1581 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1586 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1587 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1588 workBuf->lastActiveFrameSlot = -1;
1590 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1591 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1592 workBuf->lastActiveFrameSlot = -1;
1595 psD->lastActiveFrameSlot = currentFrameSlot;
1599 int dynamicOffsetCount,
1600 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1609 srb = gfxPsD->m_shaderResourceBindings;
1611 srb = compPsD->m_shaderResourceBindings;
1615 bool hasSlottedResourceInSrb =
false;
1616 bool hasDynamicOffsetInSrb =
false;
1617 bool resNeedsRebind =
false;
1619 bool pipelineChanged =
false;
1632 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1635 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1636 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1639 case QRhiShaderResourceBinding::UniformBuffer:
1642 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1643 sanityCheckResourceOwnership(bufD);
1646 hasSlottedResourceInSrb =
true;
1647 if (b->u.ubuf.hasDynamicOffset)
1648 hasDynamicOffsetInSrb =
true;
1649 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1650 resNeedsRebind =
true;
1651 bd.ubuf.id = bufD->m_id;
1652 bd.ubuf.generation = bufD->generation;
1654 bufD->lastActiveFrameSlot = currentFrameSlot;
1657 case QRhiShaderResourceBinding::SampledTexture:
1658 case QRhiShaderResourceBinding::Texture:
1659 case QRhiShaderResourceBinding::Sampler:
1661 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1662 if (bd.stex.count != data->count) {
1663 bd.stex.count = data->count;
1664 resNeedsRebind =
true;
1666 for (
int elem = 0; elem < data->count; ++elem) {
1669 Q_ASSERT(texD || samplerD);
1670 sanityCheckResourceOwnership(texD);
1671 sanityCheckResourceOwnership(samplerD);
1672 const quint64 texId = texD ? texD->m_id : 0;
1673 const uint texGen = texD ? texD->generation : 0;
1674 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1675 const uint samplerGen = samplerD ? samplerD->generation : 0;
1676 if (texGen != bd.stex.d[elem].texGeneration
1677 || texId != bd.stex.d[elem].texId
1678 || samplerGen != bd.stex.d[elem].samplerGeneration
1679 || samplerId != bd.stex.d[elem].samplerId)
1681 resNeedsRebind =
true;
1682 bd.stex.d[elem].texId = texId;
1683 bd.stex.d[elem].texGeneration = texGen;
1684 bd.stex.d[elem].samplerId = samplerId;
1685 bd.stex.d[elem].samplerGeneration = samplerGen;
1688 texD->lastActiveFrameSlot = currentFrameSlot;
1690 samplerD->lastActiveFrameSlot = currentFrameSlot;
1694 case QRhiShaderResourceBinding::ImageLoad:
1695 case QRhiShaderResourceBinding::ImageStore:
1696 case QRhiShaderResourceBinding::ImageLoadStore:
1699 sanityCheckResourceOwnership(texD);
1700 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1701 resNeedsRebind =
true;
1702 bd.simage.id = texD->m_id;
1703 bd.simage.generation = texD->generation;
1705 texD->lastActiveFrameSlot = currentFrameSlot;
1708 case QRhiShaderResourceBinding::BufferLoad:
1709 case QRhiShaderResourceBinding::BufferStore:
1710 case QRhiShaderResourceBinding::BufferLoadStore:
1713 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1714 sanityCheckResourceOwnership(bufD);
1716 if (needsBufferSizeBuffer) {
1717 for (
int i = 0; i < 6; ++i) {
1718 const QRhiShaderResourceBinding::StageFlag stage =
1719 QRhiShaderResourceBinding::StageFlag(1 << i);
1720 if (b->stage.testFlag(stage)) {
1721 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1727 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1728 resNeedsRebind =
true;
1729 bd.sbuf.id = bufD->m_id;
1730 bd.sbuf.generation = bufD->generation;
1732 bufD->lastActiveFrameSlot = currentFrameSlot;
1741 if (needsBufferSizeBuffer) {
1743 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1747 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1748 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1751 if (gfxPsD
->d->tess.enabled) {
1761 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
1762 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
1763 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1764 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1765 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1766 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1767 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1768 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1769 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1770 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1771 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1772 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1774 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1775 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
1777 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1778 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
1780 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1781 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
1784 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1785 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
1787 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1788 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
1792 for (
const auto &shader : shaders) {
1794 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1797 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1799 int maxNativeBinding = 0;
1800 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1801 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1803 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1805 Q_ASSERT(offset + size <= bufD->size());
1806 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1808 QMetalShaderResourceBindings::BoundResourceData bd;
1809 bd.sbuf.id = bufD->m_id;
1810 bd.sbuf.generation = bufD->generation;
1811 srbD->boundResourceData.append(bd);
1815 QVarLengthArray<
int, 8> bufferSizeBufferData;
1816 Q_ASSERT(storageBufferSizes.contains(shader.second));
1817 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1818 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1819 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1825 if (bufferSizeBufferData.size() <= index)
1826 bufferSizeBufferData.resize(index + 1);
1828 Q_ASSERT(sizes.contains(block.binding));
1829 bufferSizeBufferData[index] = sizes[block.binding];
1832 QRhiBufferData data;
1833 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1834 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1835 Q_ASSERT(offset + size <= bufD->size());
1836 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1839 offset += ((size + 31) / 32) * 32;
1843 bufD->lastActiveFrameSlot = currentFrameSlot;
1847 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1849 resNeedsRebind =
true;
1852 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1855 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
1856 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1860 if (gfxPsD
->d->tess.enabled) {
1863 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
1864 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
1877 cbD->currentSrbGeneration = srbD->generation;
1880 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1881 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1886 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1887 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1892 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1893 QRhiBatchedBindings<NSUInteger> offsets;
1894 for (
int i = 0; i < bindingCount; ++i) {
1897 bufD->lastActiveFrameSlot = currentFrameSlot;
1898 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1899 buffers.feed(startBinding + i, mtlbuf);
1900 offsets.feed(startBinding + i, bindings[i].second);
1915 || buffers != cbD
->d->currentVertexInputsBuffers
1916 || offsets != cbD
->d->currentVertexInputOffsets)
1919 cbD
->d->currentVertexInputsBuffers = buffers;
1920 cbD
->d->currentVertexInputOffsets = offsets;
1922 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1923 const auto &bufferBatch(buffers.batches[i]);
1924 const auto &offsetBatch(offsets.batches[i]);
1925 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1926 bufferBatch.resources.constData()
1927 offsets: offsetBatch.resources.constData()
1928 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1935 ibufD->lastActiveFrameSlot = currentFrameSlot;
1937 cbD->currentIndexOffset = indexOffset;
1938 cbD->currentIndexFormat = indexFormat;
1948 const QSize outputSize = cbD->currentTarget->pixelSize();
1949 std::array<
float, 4> vp = cbD->currentViewport.viewport();
1950 float x = 0, y = 0, w = 0, h = 0;
1952 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
1955 w = outputSize.width();
1956 h = outputSize.height();
1959 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
1963 s.x = NSUInteger(x);
1964 s.y = NSUInteger(y);
1965 s.width = NSUInteger(w);
1966 s.height = NSUInteger(h);
1967 [cbD->d->currentRenderPassEncoder setScissorRect: s];
1974 QSize outputSize = cbD->currentTarget->pixelSize();
1980 if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
1981 QRhiTextureRenderTarget *rt =
static_cast<QRhiTextureRenderTarget *>(cbD->currentTarget);
1982 if (QRhiShadingRateMap *srm = rt->description().shadingRateMap()) {
1983 if (id<MTLRasterizationRateMap> rateMap =
QRHI_RES(QMetalShadingRateMap, srm)->d->rateMap) {
1984 auto screenSize = [rateMap screenSize];
1985 outputSize = QSize(screenSize.width, screenSize.height);
1992 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
1996 vp.originX =
double(x);
1997 vp.originY =
double(y);
1998 vp.width =
double(w);
1999 vp.height =
double(h);
2000 vp.znear =
double(viewport.minDepth());
2001 vp.zfar =
double(viewport.maxDepth());
2003 [cbD->d->currentRenderPassEncoder setViewport: vp];
2005 cbD->currentViewport = viewport;
2019 const QSize outputSize = cbD->currentTarget->pixelSize();
2023 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2027 s.x = NSUInteger(x);
2028 s.y = NSUInteger(y);
2029 s.width = NSUInteger(w);
2030 s.height = NSUInteger(h);
2032 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2042 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2043 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2051 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2057 Q_UNUSED(coarsePixelSize);
2063 if (cbD
->d->currentRenderPassEncoder) {
2064 [cbD->d->currentRenderPassEncoder endEncoding];
2065 cbD->d->currentRenderPassEncoder = nil;
2068 if (!maybeComputeEncoder)
2069 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2071 return maybeComputeEncoder;
2075 id<MTLComputeCommandEncoder> computeEncoder)
2077 if (computeEncoder) {
2078 [computeEncoder endEncoding];
2079 computeEncoder = nil;
2084 switch (cbD->currentTarget->resourceType()) {
2085 case QRhiResource::SwapChainRenderTarget:
2088 case QRhiResource::TextureRenderTarget:
2097 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2099 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2100 if (cbD->d->currentPassRpDesc.colorAttachments[i].storeAction != MTLStoreActionDontCare)
2101 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2104 MTLLoadAction oldDepthLoad;
2105 MTLLoadAction oldStencilLoad;
2107 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2108 if (cbD->d->currentPassRpDesc.depthAttachment.storeAction != MTLStoreActionDontCare)
2109 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2111 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2112 if (cbD->d->currentPassRpDesc.stencilAttachment.storeAction != MTLStoreActionDontCare)
2113 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2116 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2120 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2124 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2125 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2134 if (graphicsPipeline
->d->tess.failed)
2138 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2139 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2143 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2149 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2150 = tempComputeEncoder(cbD, cbD->d->tessellationComputeEncoder);
2151 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2155 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2156 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2157 if (args.type == TessDrawArgs::U16Indexed)
2158 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2159 else if (args.type == TessDrawArgs::U32Indexed)
2160 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2161 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2162 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2163 [computeEncoder setComputePipelineState: computePipelineState];
2168 cbD
->d->currentComputePassEncoder = computeEncoder;
2170 cbD->d->currentComputePassEncoder = nil;
2172 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2173 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2174 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2176 if (outputBufferBinding >= 0) {
2177 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2178 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2181 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2184 if (indexBufferBinding >= 0)
2185 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2187 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2188 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2189 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2190 [computeEncoder setBuffers: bufferBatch.resources.constData()
2191 offsets: offsetBatch.resources.constData()
2192 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2196 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2197 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2199 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2200 args.draw.vertexCount, args.draw.instanceCount)];
2203 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2204 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2209 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2210 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2211 [computeEncoder setComputePipelineState: computePipelineState];
2213 cbD
->d->currentComputePassEncoder = computeEncoder;
2215 cbD->d->currentComputePassEncoder = nil;
2217 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2218 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2219 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2220 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2221 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2222 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2224 if (outputBufferBinding >= 0) {
2225 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2226 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2229 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2232 if (patchOutputBufferBinding >= 0) {
2233 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2234 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2235 if (!tescPatchOutBuf)
2237 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2240 if (tessFactorBufferBinding >= 0) {
2241 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2242 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2245 if (paramsBufferBinding >= 0) {
2247 quint32 inControlPointCount;
2254 params.patchCount = patchCount;
2255 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2256 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2257 memcpy(p, ¶ms,
sizeof(params));
2258 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2261 if (vertOutBuf && inputBufferBinding >= 0)
2262 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2264 int sgSize =
int(computePipelineState.threadExecutionWidth);
2265 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2266 while (wgSize > caps.maxThreadGroupSize) {
2268 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2270 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2271 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2279 endTempComputeEncoding(cbD, cbD
->d->tessellationComputeEncoder);
2280 cbD->d->tessellationComputeEncoder = nil;
2289 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2294 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2295 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2296 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2297 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2299 if (outputBufferBinding >= 0 && tescOutBuf)
2300 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2302 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2303 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2305 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2306 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2307 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2310 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2312 patchCount: patchCount
2313 patchIndexBuffer: nil
2314 patchIndexBufferOffset: 0
2324 if (multiViewCount <= 1)
2328 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2329 if (viewMaskBufBinding == -1) {
2330 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2337 multiViewInfo.viewOffset = 0;
2338 multiViewInfo.viewCount = quint32(multiViewCount);
2342 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2343 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2344 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2345 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2349 *instanceCount *= multiViewCount;
2354 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2363 a.draw.vertexCount = vertexCount;
2364 a.draw.instanceCount = instanceCount;
2365 a.draw.firstVertex = firstVertex;
2366 a.draw.firstInstance = firstInstance;
2371 adjustForMultiViewDraw(&instanceCount, cb);
2373 if (caps.baseVertexAndInstance) {
2374 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2375 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2377 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2378 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2383 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2391 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2392 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2395 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2400 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2401 a.drawIndexed.indexCount = indexCount;
2402 a.drawIndexed.instanceCount = instanceCount;
2403 a.drawIndexed.firstIndex = firstIndex;
2404 a.drawIndexed.vertexOffset = vertexOffset;
2405 a.drawIndexed.firstInstance = firstInstance;
2406 a.drawIndexed.indexBuffer = mtlibuf;
2411 adjustForMultiViewDraw(&instanceCount, cb);
2413 if (caps.baseVertexAndInstance) {
2414 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2415 indexCount: indexCount
2416 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2417 indexBuffer: mtlibuf
2418 indexBufferOffset: indexOffset
2419 instanceCount: instanceCount
2420 baseVertex: vertexOffset
2421 baseInstance: firstInstance];
2423 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2424 indexCount: indexCount
2425 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2426 indexBuffer: mtlibuf
2427 indexBufferOffset: indexOffset
2428 instanceCount: instanceCount];
2433 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2440 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2441 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2443 NSUInteger offset = indirectBufferOffset;
2444 for (quint32 i = 0; i < drawCount; ++i) {
2445 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2446 indirectBuffer: indirectBufMtl
2447 indirectBufferOffset: offset];
2453 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
2462 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
2466 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
2467 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
2480 static const quint32 ICB_DRAW_COUNT_THRESHOLD = 128;
2481 const bool useIcb = cbD->currentGraphicsPipeline
2482 && caps.indirectCommandBuffers
2483 && cbD->currentGraphicsPipeline->m_flags.testFlag(QRhiGraphicsPipeline::UsesIndirectDraws)
2484 && drawCount > ICB_DRAW_COUNT_THRESHOLD;
2490 if (!
d->icbEncodePipelineU32) {
2492 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2493 MTLCompileOptions *opts = [MTLCompileOptions
new];
2494 opts.languageVersion = MTLLanguageVersion2_1;
2495 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2498 qWarning(
"Failed to compile ICB encode kernel: %s",
2499 qPrintable(QString::fromNSString(err.localizedDescription)));
2503 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2504 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2506 if (!
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2507 qWarning(
"ICB encode kernel functions not found");
2512 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&err];
2513 if (!
d->icbEncodePipelineU32) {
2514 qWarning(
"Failed to create ICB encode compute pipeline (u32): %s",
2515 qPrintable(QString::fromNSString(err.localizedDescription)));
2520 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&err];
2521 if (!
d->icbEncodePipelineU16) {
2522 qWarning(
"Failed to create ICB encode compute pipeline (u16): %s",
2523 qPrintable(QString::fromNSString(err.localizedDescription)));
2533 if (icbOk && (!
d->icb ||
d->icbCapacity < drawCount)) {
2537 e.lastActiveFrameSlot = currentFrameSlot;
2538 e.stagingIcbBuffer.icb =
d->icb;
2539 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2540 d->releaseQueue.append(e);
2543 d->icbArgumentBuffer = nil;
2545 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2546 icbDesc.commandTypes = MTLIndirectCommandTypeDrawIndexed;
2547 icbDesc.inheritPipelineState = YES;
2548 icbDesc.inheritBuffers = YES;
2549 icbDesc.maxVertexBufferBindCount = 0;
2550 icbDesc.maxFragmentBufferBindCount = 0;
2551 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2552 maxCommandCount:drawCount
2553 options:MTLResourceStorageModePrivate];
2556 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2560 d->icbCapacity = drawCount;
2562 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunctionU32 newArgumentEncoderWithBufferIndex:1];
2563 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2564 options:MTLResourceStorageModeShared];
2565 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2566 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2576 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
2577 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
2578 const quint32 savedIndexOffset = cbD->currentIndexOffset;
2579 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
2582 [cbD->d->currentRenderPassEncoder endEncoding];
2583 cbD->d->currentRenderPassEncoder = nil;
2586 id<MTLComputeCommandEncoder> computeEncoder;
2588 const bool useU16 = (savedIndexFormat == QRhiCommandBuffer::IndexUInt16);
2589 id<MTLComputePipelineState> computePipeline = useU16 ?
d->icbEncodePipelineU16 :
d->icbEncodePipelineU32;
2591 computeEncoder = [cbD->d->cb computeCommandEncoder];
2592 uint32_t drawCountVal = drawCount;
2593 uint32_t metalPrimType = uint32_t(savedPipeline
->d->primitiveType);
2594 uint32_t strideVal = stride;
2596 [computeEncoder setComputePipelineState:computePipeline];
2597 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
2598 [computeEncoder setBuffer:d->icbArgumentBuffer offset:0 atIndex:1];
2599 [computeEncoder setBytes:&drawCountVal length:
sizeof(uint32_t) atIndex:2];
2600 [computeEncoder setBuffer:indexBufMtl offset:savedIndexOffset atIndex:3];
2601 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
2602 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
2603 [computeEncoder useResource:d->icb usage:MTLResourceUsageWrite];
2604 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
2605 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
2607 NSUInteger tw = computePipeline.threadExecutionWidth;
2608 [computeEncoder dispatchThreads:MTLSizeMake(drawCount, 1, 1)
2609 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
2613 endTempComputeEncoding(cbD, computeEncoder);
2622 if (savedFirstVertexBinding >= 0) {
2624 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
2625 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
2626 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
2627 const auto &bufferBatch(savedVertexBuffers.batches[i]);
2628 const auto &offsetBatch(savedVertexOffsets.batches[i]);
2629 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2630 bufferBatch.resources.constData()
2631 offsets: offsetBatch.resources.constData()
2632 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
2633 NSUInteger(bufferBatch.resources.count()))];
2638 cbD->currentIndexOffset = savedIndexOffset;
2639 cbD->currentIndexFormat = savedIndexFormat;
2642 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
2643 usage:MTLResourceUsageRead
2644 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2645 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
2646 usage:MTLResourceUsageRead
2647 stages:MTLRenderStageVertex | MTLRenderStageFragment];
2648 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
2649 withRange:NSMakeRange(0, drawCount)];
2655 NSUInteger offset = indirectBufferOffset;
2656 for (quint32 i = 0; i < drawCount; ++i) {
2657 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2658 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2659 indexBuffer: indexBufMtl
2660 indexBufferOffset: cbD->currentIndexOffset
2661 indirectBuffer: indirectBufMtl
2662 indirectBufferOffset: offset];
2672 NSString *str = [NSString stringWithUTF8String: name.constData()];
2674 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2675 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
2677 [cbD->d->cb pushDebugGroup: str];
2686 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2687 [cbD->d->currentRenderPassEncoder popDebugGroup];
2689 [cbD->d->cb popDebugGroup];
2698 if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
2699 [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
2704 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
2730 currentFrameSlot = swapChainD->currentFrameSlot;
2735 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
2743 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2744 if (sc != swapChainD)
2745 sc->waitUntilCompleted(currentFrameSlot);
2748 [d->captureScope beginScope];
2750 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2754 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
2761 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
2762 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
2767 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
2770 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
2771 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
2774 return QRhi::FrameOpSuccess;
2783 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2785 __block
int thisFrameSlot = currentFrameSlot;
2786 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2787 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2788 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2795 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2796 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2797 [drawableTexture release];
2801 if (flags.testFlag(QRhi::SkipPresent)) {
2803 [commandBuffer commit];
2805 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2807 if (swapChainD
->d->layer.presentsWithTransaction) {
2808 [commandBuffer commit];
2810 auto *metalLayer = swapChainD
->d->layer;
2811 auto presentWithTransaction = ^{
2812 [commandBuffer waitUntilScheduled];
2819 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2820 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2821 if (textureSize == surfaceSize) {
2824 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2825 << textureSize <<
"not matching surface size" << surfaceSize;
2829 if (NSThread.currentThread == NSThread.mainThread) {
2830 presentWithTransaction();
2832 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2833 Q_ASSERT(qtMetalLayer);
2835 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2839 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2840 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2846 if (qtMetalLayer.displayLock.tryLockForRead()) {
2848 qtMetalLayer.displayLock.unlock();
2850 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2851 <<
"due to" << qtMetalLayer <<
"needing display";
2857 [commandBuffer commit];
2861 [commandBuffer commit];
2868 [swapChainD->d->curDrawable release];
2869 swapChainD->d->curDrawable = nil;
2871 [d->captureScope endScope];
2875 return QRhi::FrameOpSuccess;
2882 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2884 for (QMetalSwapChain *sc : std::as_const(swapchains))
2885 sc->waitUntilCompleted(currentFrameSlot);
2887 d->ofr.active =
true;
2888 *cb = &
d->ofr.cbWrapper;
2889 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2892 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
2893 d->ofr.lastGpuTime = 0;
2896 return QRhi::FrameOpSuccess;
2902 Q_ASSERT(
d->ofr.active);
2903 d->ofr.active =
false;
2905 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
2909 [cb waitUntilCompleted];
2911 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2915 return QRhi::FrameOpSuccess;
2920 id<MTLCommandBuffer> cb = nil;
2923 if (
d->ofr.active) {
2926 cb =
d->ofr.cbWrapper.d->cb;
2931 cb = swapChainD->cbWrapper.d->cb;
2935 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
2936 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2937 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
2942 sc->waitUntilCompleted(i);
2948 [cb waitUntilCompleted];
2952 if (
d->ofr.active) {
2953 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
2954 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
2956 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2957 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
2965 return QRhi::FrameOpSuccess;
2969 const QColor &colorClearValue,
2970 const QRhiDepthStencilClearValue &depthStencilClearValue,
2972 QRhiShadingRateMap *shadingRateMap)
2974 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
2975 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
2976 colorClearValue.alphaF());
2978 for (uint i = 0; i < uint(colorAttCount); ++i) {
2979 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
2980 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
2981 rp.colorAttachments[i].clearColor = c;
2984 if (hasDepthStencil) {
2985 rp.depthAttachment.loadAction = MTLLoadActionClear;
2986 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
2987 rp.stencilAttachment.loadAction = MTLLoadActionClear;
2988 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
2989 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
2990 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
2994 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3002 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3003 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3004 if (imageSizeBytes > 0)
3005 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3010 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3013 const QPoint dp = subresDesc.destinationTopLeft();
3014 const QByteArray rawData = subresDesc.data();
3015 QImage img = subresDesc.image();
3016 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3017 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3019 if (!img.isNull()) {
3020 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3021 QSize size = img.size();
3022 int bpl = img.bytesPerLine();
3024 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3025 const int sx = subresDesc.sourceTopLeft().x();
3026 const int sy = subresDesc.sourceTopLeft().y();
3027 if (!subresDesc.sourceSize().isEmpty())
3028 size = subresDesc.sourceSize();
3029 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3030 if (size.width() == img.width()) {
3031 const int bpc = qMax(1, img.depth() / 8);
3032 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3033 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3034 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3035 size.height() * img.bytesPerLine());
3037 img = img.copy(sx, sy, size.width(), size.height());
3038 bpl = img.bytesPerLine();
3039 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3040 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3043 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3044 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3047 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3048 sourceOffset: NSUInteger(*curOfs)
3049 sourceBytesPerRow: NSUInteger(bpl)
3050 sourceBytesPerImage: 0
3051 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3052 toTexture: texD->d->tex
3053 destinationSlice: NSUInteger(is3D ? 0 : layer)
3054 destinationLevel: NSUInteger(level)
3055 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3056 options: MTLBlitOptionNone];
3058 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3059 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3060 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3061 const int subresw = subresSize.width();
3062 const int subresh = subresSize.height();
3064 if (subresDesc.sourceSize().isEmpty()) {
3068 w = subresDesc.sourceSize().width();
3069 h = subresDesc.sourceSize().height();
3074 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3076 const int dx = aligned(dp.x(), blockDim.width());
3077 const int dy = aligned(dp.y(), blockDim.height());
3078 if (dx + w != subresw)
3079 w = aligned(w, blockDim.width());
3080 if (dy + h != subresh)
3081 h = aligned(h, blockDim.height());
3083 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3085 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3086 sourceOffset: NSUInteger(*curOfs)
3087 sourceBytesPerRow: bpl
3088 sourceBytesPerImage: 0
3089 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3090 toTexture: texD->d->tex
3091 destinationSlice: NSUInteger(is3D ? 0 : layer)
3092 destinationLevel: NSUInteger(level)
3093 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3094 options: MTLBlitOptionNone];
3096 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3097 }
else if (!rawData.isEmpty()) {
3098 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3099 const int subresw = subresSize.width();
3100 const int subresh = subresSize.height();
3102 if (subresDesc.sourceSize().isEmpty()) {
3106 w = subresDesc.sourceSize().width();
3107 h = subresDesc.sourceSize().height();
3111 if (subresDesc.dataStride())
3112 bpl = subresDesc.dataStride();
3114 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3116 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3118 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3119 sourceOffset: NSUInteger(*curOfs)
3120 sourceBytesPerRow: bpl
3121 sourceBytesPerImage: 0
3122 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3123 toTexture: texD->d->tex
3124 destinationSlice: NSUInteger(is3D ? 0 : layer)
3125 destinationLevel: NSUInteger(level)
3126 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3127 options: MTLBlitOptionNone];
3129 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3131 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3140 id<MTLBlitCommandEncoder> blitEnc = nil;
3141 auto ensureBlit = [&blitEnc, cbD,
this]() {
3143 blitEnc = [cbD->d->cb blitCommandEncoder];
3145 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
3153 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3155 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3156 bufD
->d->pendingUpdates[i].clear();
3157 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3163 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3164 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3166 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3170 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3171 if (bufD->m_type == QRhiBuffer::Dynamic) {
3172 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3174 u.result->data.resize(u.readSize);
3175 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3177 if (u.result->completed)
3178 u.result->completed();
3182 readback.buf = bufD
->d->buf[idx];
3183 readback.offset = u.offset;
3184 readback.readSize = u.readSize;
3185 readback.result = u.result;
3186 d->activeBufferReadbacks.append(readback);
3188 if (bufD->d->managed) {
3191 [blitEnc synchronizeResource:readback.buf];
3202 qsizetype stagingSize = 0;
3203 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3204 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3205 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3206 stagingSize += subresUploadByteSize(subresDesc);
3211 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3212 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3213 options: MTLResourceStorageModeShared];
3215 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3216 qsizetype curOfs = 0;
3217 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
3218 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
3219 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
3220 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
3224 utexD->lastActiveFrameSlot = currentFrameSlot;
3228 e.lastActiveFrameSlot = currentFrameSlot;
3229 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3230 utexD->d->stagingBuf[currentFrameSlot] = nil;
3231 d->releaseQueue.append(e);
3236 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3237 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3238 const QPoint dp = u.desc.destinationTopLeft();
3239 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3240 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3241 const QPoint sp = u.desc.sourceTopLeft();
3244 [blitEnc copyFromTexture: srcD->d->tex
3245 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3246 sourceLevel: NSUInteger(u.desc.sourceLevel())
3247 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3248 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3249 toTexture: dstD->d->tex
3250 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3251 destinationLevel: NSUInteger(u.desc.destinationLevel())
3252 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3254 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3257 readback.activeFrameSlot = currentFrameSlot;
3258 readback.desc = u.rb;
3259 readback.result = u.result;
3268 qWarning(
"Multisample texture cannot be read back");
3271 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3272 if (u.rb.rect().isValid())
3275 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3276 readback.format = texD->m_format;
3278 texD->lastActiveFrameSlot = currentFrameSlot;
3282 if (u.rb.rect().isValid())
3285 rect = QRect({0, 0}, swapChainD->pixelSize);
3286 readback.format = swapChainD
->d->rhiColorFormat;
3290 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3292 readback.pixelSize = rect.size();
3295 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3296 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
3299 [blitEnc copyFromTexture: src
3300 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3301 sourceLevel: NSUInteger(u.rb.level())
3302 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3303 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3304 toBuffer: readback.buf
3305 destinationOffset: 0
3306 destinationBytesPerRow: bpl
3307 destinationBytesPerImage: 0
3308 options: MTLBlitOptionNone];
3310 d->activeTextureReadbacks.append(readback);
3314 [blitEnc generateMipmapsForTexture: utexD->d->tex];
3315 utexD->lastActiveFrameSlot = currentFrameSlot;
3321 [blitEnc popDebugGroup];
3322 [blitEnc endEncoding];
3331 if (bufD
->d->pendingUpdates[slot].isEmpty())
3334 void *p = [bufD->d->buf[slot] contents];
3335 quint32 changeBegin = UINT32_MAX;
3336 quint32 changeEnd = 0;
3337 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3338 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3339 if (u.offset < changeBegin)
3340 changeBegin = u.offset;
3341 if (u.offset + u.data.size() > changeEnd)
3342 changeEnd = u.offset + u.data.size();
3345 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3346 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3349 bufD
->d->pendingUpdates[slot].clear();
3359 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3365 QRhiRenderTarget *rt,
3366 const QColor &colorClearValue,
3367 const QRhiDepthStencilClearValue &depthStencilClearValue,
3368 QRhiResourceUpdateBatch *resourceUpdates,
3374 if (resourceUpdates)
3378 switch (rt->resourceType()) {
3379 case QRhiResource::SwapChainRenderTarget:
3383 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3386 depthStencilClearValue,
3394 if (!swapChainD
->d->curDrawable) {
3395 QMacAutoReleasePool pool;
3396 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3398 if (!swapChainD
->d->curDrawable) {
3399 qWarning(
"No drawable");
3402 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
3407 color0.resolveTex = scTex;
3413 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3416 case QRhiResource::TextureRenderTarget:
3420 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3424 depthStencilClearValue,
3426 rtTex->m_desc.shadingRateMap());
3427 if (rtD->fb.preserveColor) {
3428 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3429 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3432 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3433 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3435 int colorAttCount = 0;
3436 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3440 if (it->texture()) {
3441 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3442 if (it->multiViewCount() >= 2)
3443 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3444 }
else if (it->renderBuffer()) {
3445 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3447 if (it->resolveTexture())
3448 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3450 if (rtTex->m_desc.depthStencilBuffer())
3451 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3452 if (rtTex->m_desc.depthTexture()) {
3454 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3455 if (depthTexture->arraySize() >= 2) {
3456 const int depthLayer = rtTex->m_desc.depthLayer();
3457 if (depthLayer >= 0) {
3458 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
3459 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
3460 if (colorAttCount == 0)
3461 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
3462 }
else if (colorAttCount == 0) {
3463 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3467 if (rtTex->m_desc.depthResolveTexture())
3468 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3469 if (rtTex->m_desc.shadingRateMap())
3470 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3479 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3480 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3481 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3482 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3483 if (rtD->fb.colorAtt[i].resolveTex) {
3484 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3485 : MTLStoreActionMultisampleResolve;
3486 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3487 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3488 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3493 Q_ASSERT(rtD->fb.dsTex);
3494 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3495 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3496 if (rtD->fb.depthNeedsStore)
3497 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3498 if (rtD->fb.dsResolveTex) {
3499 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3500 : MTLStoreActionMultisampleResolve;
3501 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3502 if (rtD->fb.hasStencil) {
3503 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3504 cbD
->d->currentPassRpDesc.stencilAttachment.storeAction = cbD
->d->currentPassRpDesc.depthAttachment.storeAction;
3509 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3514 cbD->currentTarget = rt;
3522 [cbD->d->currentRenderPassEncoder endEncoding];
3525 cbD->currentTarget =
nullptr;
3527 if (resourceUpdates)
3532 QRhiResourceUpdateBatch *resourceUpdates,
3538 if (resourceUpdates)
3541 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
3551 [cbD->d->currentComputePassEncoder endEncoding];
3554 if (resourceUpdates)
3567 cbD->currentPipelineGeneration = psD->generation;
3569 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
3572 psD->lastActiveFrameSlot = currentFrameSlot;
3581 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
3582 threadsPerThreadgroup: psD->d->localSize];
3587 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3588 [e.buffer.buffers[i] release];
3593 [e.renderbuffer.texture release];
3598 [e.texture.texture release];
3599 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3600 [e.texture.stagingBuffers[i] release];
3601 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
3602 [e.texture.views[i] release];
3607 [e.sampler.samplerState release];
3612 for (
int i =
d->releaseQueue.count() - 1; i >= 0; --i) {
3614 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
3628 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
3629 [e.stagingBuffer.buffer release];
3631 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
3632 [e.graphicsPipeline.pipelineState release];
3633 [e.graphicsPipeline.depthStencilState release];
3634 [e.graphicsPipeline.tessVertexComputeState[0] release];
3635 [e.graphicsPipeline.tessVertexComputeState[1] release];
3636 [e.graphicsPipeline.tessVertexComputeState[2] release];
3637 [e.graphicsPipeline.tessTessControlComputeState release];
3639 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
3640 [e.computePipeline.pipelineState release];
3642 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
3643 [e.shadingRateMap.rateMap release];
3645 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
3646 [e.stagingIcbBuffer.icb release];
3647 [e.stagingIcbBuffer.argBuffer release];
3652 d->releaseQueue.removeAt(i);
3659 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
3661 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
3663 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
3664 readback.result->format = readback.format;
3665 readback.result->pixelSize = readback.pixelSize;
3666 readback.result->data.resize(
int(readback.bufSize));
3667 void *p = [readback.buf contents];
3668 memcpy(readback.result->data.data(), p, readback.bufSize);
3669 [readback.buf release];
3671 if (readback.result->completed)
3672 completedCallbacks.append(readback.result->completed);
3674 d->activeTextureReadbacks.remove(i);
3678 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
3680 if (forced || currentFrameSlot == readback.activeFrameSlot
3681 || readback.activeFrameSlot < 0) {
3682 readback.result->data.resize(readback.readSize);
3683 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
3685 memcpy(readback.result->data.data(), p + readback.offset, size_t(readback.readSize));
3687 if (readback.result->completed)
3688 completedCallbacks.append(readback.result->completed);
3690 d->activeBufferReadbacks.remove(i);
3694 for (
auto f : completedCallbacks)
3702 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
3722 e.buffer.buffers[i] =
d->buf[i];
3724 d->pendingUpdates[i].clear();
3729 rhiD
->d->releaseQueue.append(e);
3730 rhiD->unregisterResource(
this);
3739 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
3740 qWarning(
"StorageBuffer cannot be combined with Dynamic");
3744 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
3745 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
3748 MTLResourceOptions opts = MTLResourceStorageModeShared;
3752 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
3753 opts = MTLResourceStorageModeManaged;
3762 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer);
3764 if (
int(m_usage) == WorkBufPoolUsage)
3769 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
3770 if (!m_objectName.isEmpty()) {
3772 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
3774 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
3775 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
3783 rhiD->registerResource(
this);
3795 b.objects[i] = &
d->buf[i];
3800 return { { &
d->buf[0] }, 1 };
3810 Q_ASSERT(m_type == Dynamic);
3812 Q_ASSERT(rhiD->inFrame);
3813 const int slot = rhiD->currentFrameSlot;
3814 void *p = [d->buf[slot] contents];
3815 return static_cast<
char *>(p);
3822 QRHI_RES_RHI(QRhiMetal);
3823 const int slot = rhiD->currentFrameSlot;
3824 [d->buf[slot] didModifyRange: NSMakeRange(0, NSUInteger(m_size))];
3835 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3837 case QRhiTexture::RGBA8:
3838 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3839 case QRhiTexture::BGRA8:
3840 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3841 case QRhiTexture::R8:
3843 return MTLPixelFormatR8Unorm;
3845 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3847 case QRhiTexture::R8SI:
3848 return MTLPixelFormatR8Sint;
3849 case QRhiTexture::R8UI:
3850 return MTLPixelFormatR8Uint;
3851 case QRhiTexture::RG8:
3853 return MTLPixelFormatRG8Unorm;
3855 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3857 case QRhiTexture::R16:
3858 return MTLPixelFormatR16Unorm;
3859 case QRhiTexture::RG16:
3860 return MTLPixelFormatRG16Unorm;
3861 case QRhiTexture::RED_OR_ALPHA8:
3862 return MTLPixelFormatR8Unorm;
3864 case QRhiTexture::RGBA16F:
3865 return MTLPixelFormatRGBA16Float;
3866 case QRhiTexture::RGBA32F:
3867 return MTLPixelFormatRGBA32Float;
3868 case QRhiTexture::R16F:
3869 return MTLPixelFormatR16Float;
3870 case QRhiTexture::R32F:
3871 return MTLPixelFormatR32Float;
3873 case QRhiTexture::RGB10A2:
3874 return MTLPixelFormatRGB10A2Unorm;
3876 case QRhiTexture::R32SI:
3877 return MTLPixelFormatR32Sint;
3878 case QRhiTexture::R32UI:
3879 return MTLPixelFormatR32Uint;
3880 case QRhiTexture::RG32SI:
3881 return MTLPixelFormatRG32Sint;
3882 case QRhiTexture::RG32UI:
3883 return MTLPixelFormatRG32Uint;
3884 case QRhiTexture::RGBA32SI:
3885 return MTLPixelFormatRGBA32Sint;
3886 case QRhiTexture::RGBA32UI:
3887 return MTLPixelFormatRGBA32Uint;
3890 case QRhiTexture::D16:
3891 return MTLPixelFormatDepth16Unorm;
3892 case QRhiTexture::D24:
3893 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3894 case QRhiTexture::D24S8:
3895 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3897 case QRhiTexture::D16:
3898 return MTLPixelFormatDepth32Float;
3899 case QRhiTexture::D24:
3900 return MTLPixelFormatDepth32Float;
3901 case QRhiTexture::D24S8:
3902 return MTLPixelFormatDepth32Float_Stencil8;
3904 case QRhiTexture::D32F:
3905 return MTLPixelFormatDepth32Float;
3906 case QRhiTexture::D32FS8:
3907 return MTLPixelFormatDepth32Float_Stencil8;
3910 case QRhiTexture::BC1:
3911 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3912 case QRhiTexture::BC2:
3913 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3914 case QRhiTexture::BC3:
3915 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3916 case QRhiTexture::BC4:
3917 return MTLPixelFormatBC4_RUnorm;
3918 case QRhiTexture::BC5:
3919 qWarning(
"QRhiMetal does not support BC5");
3920 return MTLPixelFormatInvalid;
3921 case QRhiTexture::BC6H:
3922 return MTLPixelFormatBC6H_RGBUfloat;
3923 case QRhiTexture::BC7:
3924 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3926 case QRhiTexture::BC1:
3927 case QRhiTexture::BC2:
3928 case QRhiTexture::BC3:
3929 case QRhiTexture::BC4:
3930 case QRhiTexture::BC5:
3931 case QRhiTexture::BC6H:
3932 case QRhiTexture::BC7:
3933 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3934 return MTLPixelFormatInvalid;
3938 case QRhiTexture::ETC2_RGB8:
3939 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3940 case QRhiTexture::ETC2_RGB8A1:
3941 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3942 case QRhiTexture::ETC2_RGBA8:
3943 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3945 case QRhiTexture::ASTC_4x4:
3946 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3947 case QRhiTexture::ASTC_5x4:
3948 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3949 case QRhiTexture::ASTC_5x5:
3950 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3951 case QRhiTexture::ASTC_6x5:
3952 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3953 case QRhiTexture::ASTC_6x6:
3954 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3955 case QRhiTexture::ASTC_8x5:
3956 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3957 case QRhiTexture::ASTC_8x6:
3958 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3959 case QRhiTexture::ASTC_8x8:
3960 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3961 case QRhiTexture::ASTC_10x5:
3962 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3963 case QRhiTexture::ASTC_10x6:
3964 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3965 case QRhiTexture::ASTC_10x8:
3966 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3967 case QRhiTexture::ASTC_10x10:
3968 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3969 case QRhiTexture::ASTC_12x10:
3970 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3971 case QRhiTexture::ASTC_12x12:
3972 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3974 case QRhiTexture::ETC2_RGB8:
3975 if (d->caps.isAppleGPU)
3976 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3977 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3978 return MTLPixelFormatInvalid;
3979 case QRhiTexture::ETC2_RGB8A1:
3980 if (d->caps.isAppleGPU)
3981 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3982 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3983 return MTLPixelFormatInvalid;
3984 case QRhiTexture::ETC2_RGBA8:
3985 if (d->caps.isAppleGPU)
3986 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3987 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3988 return MTLPixelFormatInvalid;
3989 case QRhiTexture::ASTC_4x4:
3990 if (d->caps.isAppleGPU)
3991 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3992 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3993 return MTLPixelFormatInvalid;
3994 case QRhiTexture::ASTC_5x4:
3995 if (d->caps.isAppleGPU)
3996 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3997 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3998 return MTLPixelFormatInvalid;
3999 case QRhiTexture::ASTC_5x5:
4000 if (d->caps.isAppleGPU)
4001 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
4002 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4003 return MTLPixelFormatInvalid;
4004 case QRhiTexture::ASTC_6x5:
4005 if (d->caps.isAppleGPU)
4006 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
4007 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4008 return MTLPixelFormatInvalid;
4009 case QRhiTexture::ASTC_6x6:
4010 if (d->caps.isAppleGPU)
4011 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
4012 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4013 return MTLPixelFormatInvalid;
4014 case QRhiTexture::ASTC_8x5:
4015 if (d->caps.isAppleGPU)
4016 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
4017 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4018 return MTLPixelFormatInvalid;
4019 case QRhiTexture::ASTC_8x6:
4020 if (d->caps.isAppleGPU)
4021 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
4022 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4023 return MTLPixelFormatInvalid;
4024 case QRhiTexture::ASTC_8x8:
4025 if (d->caps.isAppleGPU)
4026 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
4027 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4028 return MTLPixelFormatInvalid;
4029 case QRhiTexture::ASTC_10x5:
4030 if (d->caps.isAppleGPU)
4031 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
4032 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4033 return MTLPixelFormatInvalid;
4034 case QRhiTexture::ASTC_10x6:
4035 if (d->caps.isAppleGPU)
4036 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
4037 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4038 return MTLPixelFormatInvalid;
4039 case QRhiTexture::ASTC_10x8:
4040 if (d->caps.isAppleGPU)
4041 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
4042 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4043 return MTLPixelFormatInvalid;
4044 case QRhiTexture::ASTC_10x10:
4045 if (d->caps.isAppleGPU)
4046 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
4047 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4048 return MTLPixelFormatInvalid;
4049 case QRhiTexture::ASTC_12x10:
4050 if (d->caps.isAppleGPU)
4051 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
4052 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4053 return MTLPixelFormatInvalid;
4054 case QRhiTexture::ASTC_12x12:
4055 if (d->caps.isAppleGPU)
4056 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
4057 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
4058 return MTLPixelFormatInvalid;
4063 return MTLPixelFormatInvalid;
4068 int sampleCount, QRhiRenderBuffer::Flags flags,
4069 QRhiTexture::Format backingFormatHint)
4090 e.renderbuffer.texture =
d->tex;
4095 rhiD
->d->releaseQueue.append(e);
4096 rhiD->unregisterResource(
this);
4105 if (m_pixelSize.isEmpty())
4109 samples = rhiD->effectiveSampleCount(m_sampleCount);
4111 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4112 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4113 desc.width = NSUInteger(m_pixelSize.width());
4114 desc.height = NSUInteger(m_pixelSize.height());
4116 desc.sampleCount = NSUInteger(
samples);
4117 desc.resourceOptions = MTLResourceStorageModePrivate;
4118 desc.usage = MTLTextureUsageRenderTarget;
4123 if (rhiD->caps.isAppleGPU) {
4124 desc.storageMode = MTLStorageModeMemoryless;
4125 d->format = MTLPixelFormatDepth32Float_Stencil8;
4127 desc.storageMode = MTLStorageModePrivate;
4128 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
4129 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
4132 desc.storageMode = MTLStorageModeMemoryless;
4133 d->format = MTLPixelFormatDepth32Float_Stencil8;
4135 desc.pixelFormat =
d->format;
4138 desc.storageMode = MTLStorageModePrivate;
4139 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4140 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
4142 d->format = MTLPixelFormatRGBA8Unorm;
4143 desc.pixelFormat =
d->format;
4150 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4153 if (!m_objectName.isEmpty())
4154 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4158 rhiD->registerResource(
this);
4164 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
4165 return m_backingFormatHint;
4167 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4171 int arraySize,
int sampleCount, Flags flags)
4175 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4176 d->stagingBuf[i] = nil;
4178 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4179 d->perLevelViews[i] = nil;
4197 e.texture.texture = d->owns ? d->tex : nil;
4201 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
4202 d->stagingBuf[i] = nil;
4205 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
4206 e.texture.views[i] =
d->perLevelViews[i];
4207 d->perLevelViews[i] = nil;
4212 rhiD
->d->releaseQueue.append(e);
4213 rhiD->unregisterResource(
this);
4222 const bool isCube = m_flags.testFlag(CubeMap);
4223 const bool is3D = m_flags.testFlag(ThreeDimensional);
4224 const bool isArray = m_flags.testFlag(TextureArray);
4225 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4226 const bool is1D = m_flags.testFlag(OneDimensional);
4228 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
4229 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
4232 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
4233 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
4234 samples = rhiD->effectiveSampleCount(m_sampleCount);
4237 qWarning(
"Cubemap texture cannot be multisample");
4241 qWarning(
"3D texture cannot be multisample");
4245 qWarning(
"Multisample texture cannot have mipmaps");
4249 if (isCube && is3D) {
4250 qWarning(
"Texture cannot be both cube and 3D");
4253 if (isArray && is3D) {
4254 qWarning(
"Texture cannot be both array and 3D");
4258 qWarning(
"Texture cannot be both 1D and 3D");
4261 if (is1D && isCube) {
4262 qWarning(
"Texture cannot be both 1D and cube");
4265 if (m_depth > 1 && !is3D) {
4266 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
4269 if (m_arraySize > 0 && !isArray) {
4270 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
4273 if (m_arraySize < 1 && isArray) {
4274 qWarning(
"Texture is an array but array size is %d", m_arraySize);
4279 *adjustedSize = size;
4287 if (!prepareCreate(&size))
4290 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
4292 const bool isCube = m_flags.testFlag(CubeMap);
4293 const bool is3D = m_flags.testFlag(ThreeDimensional);
4294 const bool isArray = m_flags.testFlag(TextureArray);
4295 const bool is1D = m_flags.testFlag(OneDimensional);
4297 desc.textureType = MTLTextureTypeCube;
4299 desc.textureType = MTLTextureType3D;
4301 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
4302 }
else if (isArray) {
4303 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
4305 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
4307 desc.pixelFormat =
d->format;
4308 desc.width = NSUInteger(size.width());
4309 desc.height = NSUInteger(size.height());
4310 desc.depth = is3D ? qMax(1, m_depth) : 1;
4313 desc.sampleCount = NSUInteger(
samples);
4315 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
4316 desc.resourceOptions = MTLResourceStorageModePrivate;
4317 desc.storageMode = MTLStorageModePrivate;
4318 desc.usage = MTLTextureUsageShaderRead;
4319 if (m_flags.testFlag(RenderTarget))
4320 desc.usage |= MTLTextureUsageRenderTarget;
4321 if (m_flags.testFlag(UsedWithLoadStore))
4322 desc.usage |= MTLTextureUsageShaderWrite;
4325 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
4328 if (!m_objectName.isEmpty())
4329 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
4335 rhiD->registerResource(
this);
4341 id<MTLTexture> tex = id<MTLTexture>(src.object);
4345 if (!prepareCreate())
4355 rhiD->registerResource(
this);
4361 return {quint64(
d->tex), 0};
4367 if (perLevelViews[level])
4368 return perLevelViews[level];
4370 const MTLTextureType type = [tex textureType];
4371 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
4372 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
4373 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
4374 levels: NSMakeRange(NSUInteger(level), 1)
4375 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
4377 perLevelViews[level] = view;
4382 AddressMode u, AddressMode v, AddressMode w)
4396 if (!
d->samplerState)
4403 e.sampler.samplerState =
d->samplerState;
4404 d->samplerState = nil;
4408 rhiD
->d->releaseQueue.append(e);
4409 rhiD->unregisterResource(
this);
4416 case QRhiSampler::Nearest:
4417 return MTLSamplerMinMagFilterNearest;
4418 case QRhiSampler::Linear:
4419 return MTLSamplerMinMagFilterLinear;
4422 return MTLSamplerMinMagFilterNearest;
4429 case QRhiSampler::None:
4430 return MTLSamplerMipFilterNotMipmapped;
4431 case QRhiSampler::Nearest:
4432 return MTLSamplerMipFilterNearest;
4433 case QRhiSampler::Linear:
4434 return MTLSamplerMipFilterLinear;
4437 return MTLSamplerMipFilterNotMipmapped;
4444 case QRhiSampler::Repeat:
4445 return MTLSamplerAddressModeRepeat;
4446 case QRhiSampler::ClampToEdge:
4447 return MTLSamplerAddressModeClampToEdge;
4448 case QRhiSampler::Mirror:
4449 return MTLSamplerAddressModeMirrorRepeat;
4452 return MTLSamplerAddressModeClampToEdge;
4459 case QRhiSampler::Never:
4460 return MTLCompareFunctionNever;
4461 case QRhiSampler::Less:
4462 return MTLCompareFunctionLess;
4463 case QRhiSampler::Equal:
4464 return MTLCompareFunctionEqual;
4465 case QRhiSampler::LessOrEqual:
4466 return MTLCompareFunctionLessEqual;
4467 case QRhiSampler::Greater:
4468 return MTLCompareFunctionGreater;
4469 case QRhiSampler::NotEqual:
4470 return MTLCompareFunctionNotEqual;
4471 case QRhiSampler::GreaterOrEqual:
4472 return MTLCompareFunctionGreaterEqual;
4473 case QRhiSampler::Always:
4474 return MTLCompareFunctionAlways;
4477 return MTLCompareFunctionNever;
4483 if (
d->samplerState)
4486 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
4487 desc.minFilter = toMetalFilter(m_minFilter);
4488 desc.magFilter = toMetalFilter(m_magFilter);
4489 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
4490 desc.sAddressMode = toMetalAddressMode(m_addressU);
4491 desc.tAddressMode = toMetalAddressMode(m_addressV);
4492 desc.rAddressMode = toMetalAddressMode(m_addressW);
4493 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
4496 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
4501 rhiD->registerResource(
this);
4526 e.shadingRateMap.rateMap =
d->rateMap;
4531 rhiD
->d->releaseQueue.append(e);
4532 rhiD->unregisterResource(
this);
4541 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
4545 [d->rateMap retain];
4550 rhiD->registerResource(
this);
4559 serializedFormatData.reserve(16);
4571 rhiD->unregisterResource(
this);
4605 serializedFormatData.clear();
4606 auto p =
std::back_inserter(serializedFormatData);
4628 rhiD->registerResource(rpD,
false);
4634 return serializedFormatData;
4656 return d->pixelSize;
4670 const QRhiTextureRenderTargetDescription &desc,
4687 rhiD->unregisterResource(
this);
4692 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
4695 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4697 for (
int i = 0; i < colorAttachmentCount; ++i) {
4698 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
4704 if (m_desc.depthTexture())
4705 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format);
4706 else if (m_desc.depthStencilBuffer())
4707 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
4709 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
4714 rhiD->registerResource(rpD,
false);
4721 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4722 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4723 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4727 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4731 Q_ASSERT(texD || rbD);
4732 id<MTLTexture> dst = nil;
4736 if (attIndex == 0) {
4737 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4740 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4743 if (attIndex == 0) {
4744 d->pixelSize = rbD->pixelSize();
4751 colorAtt
.slice = is3D ? it->layer() : 0;
4752 colorAtt
.level = it->level();
4754 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4757 d->fb.colorAtt[attIndex] = colorAtt;
4761 if (hasDepthStencil) {
4762 if (m_desc.depthTexture()) {
4764 d->fb.dsTex = depthTexD
->d->tex;
4765 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4766 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4767 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4769 d->pixelSize = depthTexD->pixelSize();
4774 d->fb.dsTex = depthRbD
->d->tex;
4775 d->fb.hasStencil =
true;
4776 d->fb.depthNeedsStore =
false;
4777 d->fb.preserveDs =
false;
4779 d->pixelSize = depthRbD->pixelSize();
4783 if (m_desc.depthResolveTexture()) {
4785 d->fb.dsResolveTex = depthResolveTexD
->d->tex;
4792 if (d->colorAttCount > 0)
4793 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4795 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4797 rhiD->registerResource(
this,
false);
4803 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
4806 return d->pixelSize;
4831 sortedBindings.clear();
4836 rhiD->unregisterResource(
this);
4841 if (!sortedBindings.isEmpty())
4845 if (!rhiD->sanityCheckShaderResourceBindings(
this))
4848 rhiD->updateLayoutDesc(
this);
4850 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4851 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4852 if (!sortedBindings.isEmpty())
4853 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
4857 boundResourceData.resize(sortedBindings.count());
4859 for (BoundResourceData &bd : boundResourceData)
4860 memset(&bd, 0,
sizeof(BoundResourceData));
4863 rhiD->registerResource(
this,
false);
4869 sortedBindings.clear();
4870 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
4871 if (!flags.testFlag(BindingsAreSorted))
4872 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
4874 for (BoundResourceData &bd : boundResourceData)
4875 memset(&bd, 0,
sizeof(BoundResourceData));
4899 d->tess.compVs[0].destroy();
4900 d->tess.compVs[1].destroy();
4901 d->tess.compVs[2].destroy();
4903 d->tess.compTesc.destroy();
4904 d->tess.vertTese.destroy();
4906 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
4907 d->extraBufMgr.deviceLocalWorkBuffers.clear();
4908 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
4909 d->extraBufMgr.hostVisibleWorkBuffers.clear();
4914 if (!
d->ps && !
d->ds
4915 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
4916 && !
d->tess.tessControlComputeState)
4924 e.graphicsPipeline.pipelineState =
d->ps;
4925 e.graphicsPipeline.depthStencilState =
d->ds;
4926 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
4927 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
4930 d->tess.vertexComputeState = {};
4931 d->tess.tessControlComputeState = nil;
4935 rhiD
->d->releaseQueue.append(e);
4936 rhiD->unregisterResource(
this);
4943 case QRhiVertexInputAttribute::Float4:
4944 return MTLVertexFormatFloat4;
4945 case QRhiVertexInputAttribute::Float3:
4946 return MTLVertexFormatFloat3;
4947 case QRhiVertexInputAttribute::Float2:
4948 return MTLVertexFormatFloat2;
4949 case QRhiVertexInputAttribute::Float:
4950 return MTLVertexFormatFloat;
4951 case QRhiVertexInputAttribute::UNormByte4:
4952 return MTLVertexFormatUChar4Normalized;
4953 case QRhiVertexInputAttribute::UNormByte2:
4954 return MTLVertexFormatUChar2Normalized;
4955 case QRhiVertexInputAttribute::UNormByte:
4956 return MTLVertexFormatUCharNormalized;
4957 case QRhiVertexInputAttribute::UInt4:
4958 return MTLVertexFormatUInt4;
4959 case QRhiVertexInputAttribute::UInt3:
4960 return MTLVertexFormatUInt3;
4961 case QRhiVertexInputAttribute::UInt2:
4962 return MTLVertexFormatUInt2;
4963 case QRhiVertexInputAttribute::UInt:
4964 return MTLVertexFormatUInt;
4965 case QRhiVertexInputAttribute::SInt4:
4966 return MTLVertexFormatInt4;
4967 case QRhiVertexInputAttribute::SInt3:
4968 return MTLVertexFormatInt3;
4969 case QRhiVertexInputAttribute::SInt2:
4970 return MTLVertexFormatInt2;
4971 case QRhiVertexInputAttribute::SInt:
4972 return MTLVertexFormatInt;
4973 case QRhiVertexInputAttribute::Half4:
4974 return MTLVertexFormatHalf4;
4975 case QRhiVertexInputAttribute::Half3:
4976 return MTLVertexFormatHalf3;
4977 case QRhiVertexInputAttribute::Half2:
4978 return MTLVertexFormatHalf2;
4979 case QRhiVertexInputAttribute::Half:
4980 return MTLVertexFormatHalf;
4981 case QRhiVertexInputAttribute::UShort4:
4982 return MTLVertexFormatUShort4;
4983 case QRhiVertexInputAttribute::UShort3:
4984 return MTLVertexFormatUShort3;
4985 case QRhiVertexInputAttribute::UShort2:
4986 return MTLVertexFormatUShort2;
4987 case QRhiVertexInputAttribute::UShort:
4988 return MTLVertexFormatUShort;
4989 case QRhiVertexInputAttribute::SShort4:
4990 return MTLVertexFormatShort4;
4991 case QRhiVertexInputAttribute::SShort3:
4992 return MTLVertexFormatShort3;
4993 case QRhiVertexInputAttribute::SShort2:
4994 return MTLVertexFormatShort2;
4995 case QRhiVertexInputAttribute::SShort:
4996 return MTLVertexFormatShort;
4999 return MTLVertexFormatFloat4;
5006 case QRhiGraphicsPipeline::Zero:
5007 return MTLBlendFactorZero;
5008 case QRhiGraphicsPipeline::One:
5009 return MTLBlendFactorOne;
5010 case QRhiGraphicsPipeline::SrcColor:
5011 return MTLBlendFactorSourceColor;
5012 case QRhiGraphicsPipeline::OneMinusSrcColor:
5013 return MTLBlendFactorOneMinusSourceColor;
5014 case QRhiGraphicsPipeline::DstColor:
5015 return MTLBlendFactorDestinationColor;
5016 case QRhiGraphicsPipeline::OneMinusDstColor:
5017 return MTLBlendFactorOneMinusDestinationColor;
5018 case QRhiGraphicsPipeline::SrcAlpha:
5019 return MTLBlendFactorSourceAlpha;
5020 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
5021 return MTLBlendFactorOneMinusSourceAlpha;
5022 case QRhiGraphicsPipeline::DstAlpha:
5023 return MTLBlendFactorDestinationAlpha;
5024 case QRhiGraphicsPipeline::OneMinusDstAlpha:
5025 return MTLBlendFactorOneMinusDestinationAlpha;
5026 case QRhiGraphicsPipeline::ConstantColor:
5027 return MTLBlendFactorBlendColor;
5028 case QRhiGraphicsPipeline::ConstantAlpha:
5029 return MTLBlendFactorBlendAlpha;
5030 case QRhiGraphicsPipeline::OneMinusConstantColor:
5031 return MTLBlendFactorOneMinusBlendColor;
5032 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
5033 return MTLBlendFactorOneMinusBlendAlpha;
5034 case QRhiGraphicsPipeline::SrcAlphaSaturate:
5035 return MTLBlendFactorSourceAlphaSaturated;
5036 case QRhiGraphicsPipeline::Src1Color:
5037 return MTLBlendFactorSource1Color;
5038 case QRhiGraphicsPipeline::OneMinusSrc1Color:
5039 return MTLBlendFactorOneMinusSource1Color;
5040 case QRhiGraphicsPipeline::Src1Alpha:
5041 return MTLBlendFactorSource1Alpha;
5042 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
5043 return MTLBlendFactorOneMinusSource1Alpha;
5046 return MTLBlendFactorZero;
5053 case QRhiGraphicsPipeline::Add:
5054 return MTLBlendOperationAdd;
5055 case QRhiGraphicsPipeline::Subtract:
5056 return MTLBlendOperationSubtract;
5057 case QRhiGraphicsPipeline::ReverseSubtract:
5058 return MTLBlendOperationReverseSubtract;
5059 case QRhiGraphicsPipeline::Min:
5060 return MTLBlendOperationMin;
5061 case QRhiGraphicsPipeline::Max:
5062 return MTLBlendOperationMax;
5065 return MTLBlendOperationAdd;
5072 if (c.testFlag(QRhiGraphicsPipeline::R))
5073 f |= MTLColorWriteMaskRed;
5074 if (c.testFlag(QRhiGraphicsPipeline::G))
5075 f |= MTLColorWriteMaskGreen;
5076 if (c.testFlag(QRhiGraphicsPipeline::B))
5077 f |= MTLColorWriteMaskBlue;
5078 if (c.testFlag(QRhiGraphicsPipeline::A))
5079 f |= MTLColorWriteMaskAlpha;
5086 case QRhiGraphicsPipeline::Never:
5087 return MTLCompareFunctionNever;
5088 case QRhiGraphicsPipeline::Less:
5089 return MTLCompareFunctionLess;
5090 case QRhiGraphicsPipeline::Equal:
5091 return MTLCompareFunctionEqual;
5092 case QRhiGraphicsPipeline::LessOrEqual:
5093 return MTLCompareFunctionLessEqual;
5094 case QRhiGraphicsPipeline::Greater:
5095 return MTLCompareFunctionGreater;
5096 case QRhiGraphicsPipeline::NotEqual:
5097 return MTLCompareFunctionNotEqual;
5098 case QRhiGraphicsPipeline::GreaterOrEqual:
5099 return MTLCompareFunctionGreaterEqual;
5100 case QRhiGraphicsPipeline::Always:
5101 return MTLCompareFunctionAlways;
5104 return MTLCompareFunctionAlways;
5111 case QRhiGraphicsPipeline::StencilZero:
5112 return MTLStencilOperationZero;
5113 case QRhiGraphicsPipeline::Keep:
5114 return MTLStencilOperationKeep;
5115 case QRhiGraphicsPipeline::Replace:
5116 return MTLStencilOperationReplace;
5117 case QRhiGraphicsPipeline::IncrementAndClamp:
5118 return MTLStencilOperationIncrementClamp;
5119 case QRhiGraphicsPipeline::DecrementAndClamp:
5120 return MTLStencilOperationDecrementClamp;
5121 case QRhiGraphicsPipeline::Invert:
5122 return MTLStencilOperationInvert;
5123 case QRhiGraphicsPipeline::IncrementAndWrap:
5124 return MTLStencilOperationIncrementWrap;
5125 case QRhiGraphicsPipeline::DecrementAndWrap:
5126 return MTLStencilOperationDecrementWrap;
5129 return MTLStencilOperationKeep;
5136 case QRhiGraphicsPipeline::Triangles:
5137 return MTLPrimitiveTypeTriangle;
5138 case QRhiGraphicsPipeline::TriangleStrip:
5139 return MTLPrimitiveTypeTriangleStrip;
5140 case QRhiGraphicsPipeline::Lines:
5141 return MTLPrimitiveTypeLine;
5142 case QRhiGraphicsPipeline::LineStrip:
5143 return MTLPrimitiveTypeLineStrip;
5144 case QRhiGraphicsPipeline::Points:
5145 return MTLPrimitiveTypePoint;
5148 return MTLPrimitiveTypeTriangle;
5155 case QRhiGraphicsPipeline::Triangles:
5156 case QRhiGraphicsPipeline::TriangleStrip:
5157 case QRhiGraphicsPipeline::TriangleFan:
5158 return MTLPrimitiveTopologyClassTriangle;
5159 case QRhiGraphicsPipeline::Lines:
5160 case QRhiGraphicsPipeline::LineStrip:
5161 return MTLPrimitiveTopologyClassLine;
5162 case QRhiGraphicsPipeline::Points:
5163 return MTLPrimitiveTopologyClassPoint;
5166 return MTLPrimitiveTopologyClassTriangle;
5173 case QRhiGraphicsPipeline::None:
5174 return MTLCullModeNone;
5175 case QRhiGraphicsPipeline::Front:
5176 return MTLCullModeFront;
5177 case QRhiGraphicsPipeline::Back:
5178 return MTLCullModeBack;
5181 return MTLCullModeNone;
5188 case QRhiGraphicsPipeline::Fill:
5189 return MTLTriangleFillModeFill;
5190 case QRhiGraphicsPipeline::Line:
5191 return MTLTriangleFillModeLines;
5194 return MTLTriangleFillModeFill;
5201 case QShaderDescription::CwTessellationWindingOrder:
5202 return MTLWindingClockwise;
5203 case QShaderDescription::CcwTessellationWindingOrder:
5204 return MTLWindingCounterClockwise;
5207 return MTLWindingCounterClockwise;
5214 case QShaderDescription::EqualTessellationPartitioning:
5215 return MTLTessellationPartitionModePow2;
5216 case QShaderDescription::FractionalEvenTessellationPartitioning:
5217 return MTLTessellationPartitionModeFractionalEven;
5218 case QShaderDescription::FractionalOddTessellationPartitioning:
5219 return MTLTessellationPartitionModeFractionalOdd;
5222 return MTLTessellationPartitionModePow2;
5228 int v = version.version();
5229 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
5233 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
5235 QVarLengthArray<
int, 8> versions;
5236 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
5238 const QList<QShaderKey> shaders = shader.availableShaders();
5242 for (
const int &version : versions) {
5243 key = { QShader::Source::MetalLibShader, version, shaderVariant };
5244 if (shaders.contains(key))
5248 QShaderCode mtllib = shader.shader(key);
5249 if (!mtllib.shader().isEmpty()) {
5250 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
5251 size_t(mtllib.shader().size()),
5252 dispatch_get_global_queue(0, 0),
5253 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
5255 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
5256 dispatch_release(data);
5258 *entryPoint = mtllib.entryPoint();
5262 const QString msg = QString::fromNSString(err.localizedDescription);
5263 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
5267 for (
const int &version : versions) {
5268 key = { QShader::Source::MslShader, version, shaderVariant };
5269 if (shaders.contains(key))
5273 QShaderCode mslSource = shader.shader(key);
5274 if (mslSource.shader().isEmpty()) {
5275 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
5279 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
5280 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
5281 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
5283 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
5291 const QString msg = QString::fromNSString(err.localizedDescription);
5296 *entryPoint = mslSource.entryPoint();
5303 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
5308 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
5312 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
5313 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
5314 rpDesc.colorAttachments[0].blendingEnabled =
false;
5316 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
5317 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
5319 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
5320 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
5321 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
5322 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
5323 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
5324 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
5325 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
5326 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
5327 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
5328 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
5329 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
5336 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
5337 rpDesc.depthAttachmentPixelFormat = fmt;
5338#if defined(Q_OS_MACOS)
5339 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
5341 if (fmt != MTLPixelFormatDepth32Float)
5343 rpDesc.stencilAttachmentPixelFormat = fmt;
5347 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
5352 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
5354 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
5355 dsDesc.depthWriteEnabled = m_depthWrite;
5356 if (m_stencilTest) {
5357 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
5358 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
5359 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
5360 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
5361 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
5362 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
5363 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
5365 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
5366 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
5367 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
5368 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
5369 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
5370 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
5371 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
5377 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
5378 d->cullMode = toMetalCullMode(m_cullMode);
5379 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
5380 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
5381 d->depthBias =
float(m_depthBias);
5382 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
5392 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5395 const uint loc = uint(it->location());
5396 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5397 desc.attributes[loc].offset = NSUInteger(it->offset());
5398 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5400 int bindingIndex = 0;
5401 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
5402 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5403 it != itEnd; ++it, ++bindingIndex)
5405 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5406 desc.layouts[layoutIdx].stepFunction =
5407 it->classification() == QRhiVertexInputBinding::PerInstance
5408 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
5409 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5410 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
5411 desc.layouts[layoutIdx].stepRate *= viewCount;
5412 desc.layouts[layoutIdx].stride = it->stride();
5423 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
5426 const uint loc = uint(it->location());
5427 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
5428 desc.attributes[loc].offset = NSUInteger(it->offset());
5429 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
5431 int bindingIndex = 0;
5432 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
5433 it != itEnd; ++it, ++bindingIndex)
5435 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
5436 if (desc.indexBufferIndex) {
5437 desc.layouts[layoutIdx].stepFunction =
5438 it->classification() == QRhiVertexInputBinding::PerInstance
5439 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
5441 desc.layouts[layoutIdx].stepFunction =
5442 it->classification() == QRhiVertexInputBinding::PerInstance
5443 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
5445 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
5446 desc.layouts[layoutIdx].stride = it->stride();
5453 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
5454 rpDesc.binaryArchives = binArchArray;
5462 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
5463 const QString msg = QString::fromNSString(err.localizedDescription);
5464 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
5473 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5474 d->setupVertexInputDescriptor(vertexDesc);
5476 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5477 rpDesc.vertexDescriptor = vertexDesc;
5485 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5486 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5487 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5488 switch (shaderStage.type()) {
5489 case QRhiShaderStage::Vertex:
5492 [d->vs.func retain];
5493 rpDesc.vertexFunction = d->vs.func;
5495 case QRhiShaderStage::Fragment:
5498 [d->fs.func retain];
5499 rpDesc.fragmentFunction = d->fs.func;
5505 const QShader shader = shaderStage.shader();
5507 QByteArray entryPoint;
5508 QShaderKey activeKey;
5509 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5510 &error, &entryPoint, &activeKey);
5512 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5515 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5517 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5521 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5523 for (QMetalShader &s : rhiD->d->shaderCache)
5525 rhiD->d->shaderCache.clear();
5527 switch (shaderStage.type()) {
5528 case QRhiShaderStage::Vertex:
5531 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5532 d->vs.desc = shader.description();
5533 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5534 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5536 [d->vs.func retain];
5537 rpDesc.vertexFunction = func;
5539 case QRhiShaderStage::Fragment:
5542 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5543 d->fs.desc = shader.description();
5544 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5545 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5547 [d->fs.func retain];
5548 rpDesc.fragmentFunction = func;
5561 if (m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers)
5562 rpDesc.supportIndirectCommandBuffers = YES;
5564 if (m_multiViewCount >= 2)
5565 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5567 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5569 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5570 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
5573 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5576 const QString msg = QString::fromNSString(err.localizedDescription);
5577 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5581 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5583 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5586 d->primitiveType = toMetalPrimitiveType(m_topology);
5594 switch (vertexCompVariant) {
5595 case QShader::NonIndexedVertexAsComputeShader:
5597 case QShader::UInt32IndexedVertexAsComputeShader:
5599 case QShader::UInt16IndexedVertexAsComputeShader:
5609 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
5610 if (varIndex >= 0 && vertexComputeState[varIndex])
5611 return vertexComputeState[varIndex];
5613 id<MTLFunction> func = nil;
5615 func = compVs[varIndex].func;
5618 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
5622 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
5623 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
5625 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5626 cpDesc.computeFunction = func;
5627 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
5628 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
5629 if (indexBufferBinding >= 0) {
5630 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
5631 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
5632 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5633 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
5634 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
5635 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
5638 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
5640 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5642 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5643 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5646 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5647 options: MTLPipelineOptionNone
5652 const QString msg = QString::fromNSString(err.localizedDescription);
5653 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5655 vertexComputeState[varIndex] = ps;
5663 if (tessControlComputeState)
5664 return tessControlComputeState;
5666 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
5667 cpDesc.computeFunction = compTesc.func;
5669 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
5671 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5672 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
5675 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
5676 options: MTLPipelineOptionNone
5681 const QString msg = QString::fromNSString(err.localizedDescription);
5682 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
5684 tessControlComputeState = ps;
5692 return (indices >> index) & 0x1;
5695static inline void takeIndex(quint32 index, quint64 &indices)
5697 indices |= 1 << index;
5706 static const int maxVertexAttributes = 31;
5708 for (
int index = 0; index < maxVertexAttributes; ++index) {
5709 if (!indexTaken(index, indices))
5713 Q_UNREACHABLE_RETURN(-1);
5716static inline int aligned(quint32 offset, quint32 alignment)
5718 return ((offset + alignment - 1) / alignment) * alignment;
5726 for (
const int dim : variable.arrayDims)
5729 if (variable.type == QShaderDescription::VariableType::Struct) {
5730 for (
int element = 0; element < elements; ++element) {
5731 for (
const auto &member : variable.structMembers) {
5732 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
5736 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5737 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5740 const quint32 alignment = size;
5741 vertexAlignment =
std::max(vertexAlignment, alignment);
5743 for (
int element = 0; element < elements; ++element) {
5745 offset = aligned(offset, alignment);
5752static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
5756 for (
const int dim : variable.arrayDims)
5759 if (variable.type == QShaderDescription::VariableType::Struct) {
5760 for (
int element = 0; element < elements; ++element) {
5761 for (
const auto &member : variable.structMembers) {
5762 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
5766 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
5767 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
5770 const quint32 alignment = size;
5771 vertexAlignment =
std::max(vertexAlignment, alignment);
5773 for (
int element = 0; element < elements; ++element) {
5774 Q_ASSERT(!indexTaken(index, indices));
5777 offset = aligned(offset, alignment);
5779 attributes[index].bufferIndex = binding;
5780 attributes[index].format = toMetalAttributeFormat(format);
5781 attributes[index].offset = offset;
5783 takeIndex(index, indices);
5785 if (indexTaken(index, indices))
5786 index = nextAttributeIndex(indices);
5793static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
5795 if (a.size() == b.size()) {
5797 for (
int i = 0; i < a.size() && match; ++i) {
5798 match &= a[i].type == b[i].type
5799 && a[i].arrayDims == b[i].arrayDims
5800 && matches(a[i].structMembers, b[i].structMembers);
5808static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
5810 return a.location == b.location
5812 && a.perPatch == b.perPatch
5813 && matches(a.structMembers, b.structMembers);
5862 if (pipeline
->d->ps)
5863 return pipeline
->d->ps;
5865 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5866 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5869 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5870 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5871 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5872 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5873 quint32 offsetInTescOutput = 0;
5874 quint32 offsetInTescPatchOutput = 0;
5875 quint32 offsetInTessFactorBuffer = 0;
5876 quint32 tescOutputAlignment = 0;
5877 quint32 tescPatchOutputAlignment = 0;
5878 quint32 tessFactorAlignment = 0;
5879 QSet<
int> usedBuffers;
5882 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5883 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5884 tescOutVars[tescOutVar.location] = tescOutVar;
5887 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5888 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5889 teseInVars[teseInVar.location] = teseInVar;
5892 quint64 indices = 0;
5894 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5896 int index = tescOutVar.location;
5898 quint32 *offset =
nullptr;
5899 quint32 *alignment =
nullptr;
5901 if (tescOutVar.perPatch) {
5902 binding = tescPatchOutputBufferBinding;
5903 offset = &offsetInTescPatchOutput;
5904 alignment = &tescPatchOutputAlignment;
5906 tescOutVar.arrayDims.removeLast();
5907 binding = tescOutputBufferBinding;
5908 offset = &offsetInTescOutput;
5909 alignment = &tescOutputAlignment;
5912 if (teseInVars.contains(index)) {
5914 if (!matches(teseInVars[index], tescOutVar)) {
5915 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5916 qWarning() <<
" tesc out:" << tescOutVar;
5917 qWarning() <<
" tese in:" << teseInVars[index];
5920 if (binding != -1) {
5921 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5922 usedBuffers << binding;
5924 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5925 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5929 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5930 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5933 teseInVars.remove(tescOutVar.location);
5936 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5937 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5940 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5941 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5942 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5945 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5946 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5947 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5949 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5950 bool tessLevelAdded =
false;
5952 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5954 QShaderDescription::InOutVariable variable;
5956 quint32 *offset =
nullptr;
5957 quint32 *alignment =
nullptr;
5959 switch (builtin.type) {
5960 case QShaderDescription::BuiltinType::PositionBuiltin:
5961 variable.type = QShaderDescription::VariableType::Vec4;
5962 binding = tescOutputBufferBinding;
5963 offset = &offsetInTescOutput;
5964 alignment = &tescOutputAlignment;
5966 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5967 variable.type = QShaderDescription::VariableType::Float;
5968 binding = tescOutputBufferBinding;
5969 offset = &offsetInTescOutput;
5970 alignment = &tescOutputAlignment;
5972 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5973 variable.type = QShaderDescription::VariableType::Float;
5974 variable.arrayDims = builtin.arrayDims;
5975 binding = tescOutputBufferBinding;
5976 offset = &offsetInTescOutput;
5977 alignment = &tescOutputAlignment;
5979 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5980 variable.type = QShaderDescription::VariableType::Half4;
5981 binding = tessFactorBufferBinding;
5982 offset = &offsetInTessFactorBuffer;
5983 tessLevelAdded = trianglesMode;
5984 alignment = &tessFactorAlignment;
5986 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5987 if (trianglesMode) {
5988 if (!tessLevelAdded) {
5989 variable.type = QShaderDescription::VariableType::Half4;
5990 binding = tessFactorBufferBinding;
5991 offsetInTessFactorBuffer = 0;
5992 offset = &offsetInTessFactorBuffer;
5993 alignment = &tessFactorAlignment;
5994 tessLevelAdded =
true;
5996 teseInBuiltins.remove(builtin.type);
6000 variable.type = QShaderDescription::VariableType::Half2;
6001 binding = tessFactorBufferBinding;
6002 offsetInTessFactorBuffer = 8;
6003 offset = &offsetInTessFactorBuffer;
6004 alignment = &tessFactorAlignment;
6012 if (teseInBuiltins.contains(builtin.type)) {
6013 if (binding != -1) {
6014 int index = nextAttributeIndex(indices);
6015 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
6016 usedBuffers << binding;
6018 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
6019 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6022 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
6025 teseInBuiltins.remove(builtin.type);
6028 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
6029 switch (builtin.type) {
6030 case QShaderDescription::BuiltinType::PositionBuiltin:
6031 case QShaderDescription::BuiltinType::PointSizeBuiltin:
6032 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
6033 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
6040 if (usedBuffers.contains(tescOutputBufferBinding)) {
6041 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
6042 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
6045 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
6046 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6047 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
6050 if (usedBuffers.contains(tessFactorBufferBinding)) {
6051 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
6052 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
6055 rpDesc.vertexDescriptor = vertexDesc;
6056 rpDesc.vertexFunction = vertTese.func;
6057 rpDesc.fragmentFunction = pipeline
->d->fs.func;
6063 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
6065 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
6070 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
6072 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6073 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
6076 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
6079 const QString msg = QString::fromNSString(err.localizedDescription);
6080 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
6084 pipeline->d->ps = ps;
6091 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
6094 for (QMetalBuffer *workBuf : *workBuffers) {
6095 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
6096 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6104 for (QMetalBuffer *workBuf : *workBuffers) {
6105 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
6106 workBuf->setSize(size);
6107 if (workBuf->create()) {
6108 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
6119 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6122 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
6126 workBuffers->append(buf);
6130 qWarning(
"Failed to acquire work buffer of size %u", size);
6138 QByteArray entryPoint;
6139 QShaderKey activeKey;
6141 const QShaderDescription tescDesc = tesc.description();
6142 const QShaderDescription teseDesc = tese.description();
6143 d->tess.inControlPointCount = uint(m_patchControlPointCount);
6144 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
6145 if (!
d->tess.outControlPointCount)
6146 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
6148 if (!
d->tess.outControlPointCount) {
6149 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
6150 d->tess.enabled =
false;
6151 d->tess.failed =
true;
6155 if (m_multiViewCount >= 2)
6156 qWarning(
"Multiview is not supported with tessellation");
6164 bool variantsPresent[3] = {};
6165 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
6166 for (
const QShaderKey &k : tessVertKeys) {
6167 switch (k.sourceVariant()) {
6168 case QShader::NonIndexedVertexAsComputeShader:
6169 variantsPresent[0] =
true;
6171 case QShader::UInt32IndexedVertexAsComputeShader:
6172 variantsPresent[1] =
true;
6174 case QShader::UInt16IndexedVertexAsComputeShader:
6175 variantsPresent[2] =
true;
6181 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
6182 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
6183 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
6184 "Try passing --msltess to qsb.");
6185 d->tess.enabled =
false;
6186 d->tess.failed =
true;
6191 for (QShader::Variant variant : {
6192 QShader::NonIndexedVertexAsComputeShader,
6193 QShader::UInt32IndexedVertexAsComputeShader,
6194 QShader::UInt16IndexedVertexAsComputeShader })
6196 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
6198 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
6199 d->tess.enabled =
false;
6200 d->tess.failed =
true;
6203 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
6205 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6207 d->tess.enabled =
false;
6208 d->tess.failed =
true;
6211 QMetalShader &compVs(d->tess.compVs[varIndex]);
6214 compVs.desc = tessVert.description();
6215 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
6216 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
6219 if (!d->tess.vsCompPipeline(rhiD, variant)) {
6220 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
6221 d->tess.enabled =
false;
6222 d->tess.failed =
true;
6230 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
6231 if (!tessControlLib) {
6232 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
6233 d->tess.enabled =
false;
6234 d->tess.failed =
true;
6237 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
6238 if (!tessControlFunc) {
6239 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6240 [tessControlLib release];
6241 d->tess.enabled =
false;
6242 d->tess.failed =
true;
6245 d->tess.compTesc.lib = tessControlLib;
6246 d->tess.compTesc.func = tessControlFunc;
6247 d->tess.compTesc.desc = tesc.description();
6248 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
6249 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
6250 if (!
d->tess.tescCompPipeline(rhiD)) {
6251 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
6252 d->tess.enabled =
false;
6253 d->tess.failed =
true;
6258 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
6260 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
6261 d->tess.enabled =
false;
6262 d->tess.failed =
true;
6265 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
6266 if (!tessEvalFunc) {
6267 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6268 [tessEvalLib release];
6269 d->tess.enabled =
false;
6270 d->tess.failed =
true;
6273 d->tess.vertTese.lib = tessEvalLib;
6274 d->tess.vertTese.func = tessEvalFunc;
6275 d->tess.vertTese.desc = tese.description();
6276 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
6277 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
6279 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
6281 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
6282 d->tess.enabled =
false;
6283 d->tess.failed =
true;
6286 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
6288 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6290 d->tess.enabled =
false;
6291 d->tess.failed =
true;
6294 d->fs.lib = fragLib;
6295 d->fs.func = fragFunc;
6296 d->fs.desc = tessFrag.description();
6297 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
6298 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
6300 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
6301 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
6302 d->tess.enabled =
false;
6303 d->tess.failed =
true;
6307 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
6309 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
6323 rhiD->pipelineCreationStart();
6324 if (!rhiD->sanityCheckGraphicsPipeline(
this))
6332 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
6333 switch (shaderStage.type()) {
6334 case QRhiShaderStage::Vertex:
6335 tessVert = shaderStage.shader();
6337 case QRhiShaderStage::TessellationControl:
6338 tesc = shaderStage.shader();
6340 case QRhiShaderStage::TessellationEvaluation:
6341 tese = shaderStage.shader();
6343 case QRhiShaderStage::Fragment:
6344 tessFrag = shaderStage.shader();
6350 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
6351 d->tess.failed =
false;
6353 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
6359 QVarLengthArray<QMetalShader *, 6> shaders;
6360 if (
d->tess.enabled) {
6361 shaders.append(&
d->tess.compVs[0]);
6362 shaders.append(&
d->tess.compVs[1]);
6363 shaders.append(&
d->tess.compVs[2]);
6364 shaders.append(&
d->tess.compTesc);
6365 shaders.append(&
d->tess.vertTese);
6367 shaders.append(&
d->vs);
6369 shaders.append(&
d->fs);
6371 for (QMetalShader *shader : shaders) {
6372 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6373 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6374 shader->nativeResourceBindingMap[binding] = {binding, -1};
6375 int maxNativeBinding = 0;
6376 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6377 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6381 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6386 if (!d->bufferSizeBuffer)
6387 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6393 rhiD->pipelineCreationEnd();
6396 rhiD->registerResource(
this);
6425 e.computePipeline.pipelineState =
d->ps;
6430 rhiD
->d->releaseQueue.append(e);
6431 rhiD->unregisterResource(
this);
6438 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6439 cpDesc.binaryArchives = binArchArray;
6447 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
6448 const QString msg = QString::fromNSString(err.localizedDescription);
6449 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
6460 rhiD->pipelineCreationStart();
6462 auto cacheIt = rhiD
->d->shaderCache.constFind(m_shaderStage);
6463 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
6466 const QShader shader = m_shaderStage.shader();
6468 QByteArray entryPoint;
6469 QShaderKey activeKey;
6470 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6471 &error, &entryPoint, &activeKey);
6473 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6476 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
6478 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6484 d->cs.localSize = shader.description().computeShaderLocalSize();
6485 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6486 d->cs.desc = shader.description();
6487 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6490 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6491 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6492 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
6495 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6496 for (QMetalShader &s : rhiD->d->shaderCache)
6498 rhiD
->d->shaderCache.clear();
6500 rhiD
->d->shaderCache.insert(m_shaderStage,
d->cs);
6504 [d->cs.func retain];
6506 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
6508 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6509 cpDesc.computeFunction =
d->cs.func;
6511 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6513 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6514 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
6517 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6518 options: MTLPipelineOptionNone
6523 const QString msg = QString::fromNSString(err.localizedDescription);
6524 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6529 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6531 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6532 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6536 if (!d->bufferSizeBuffer)
6537 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6543 rhiD->pipelineCreationEnd();
6546 rhiD->registerResource(
this);
6570 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
6571 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
6572 return &nativeHandlesStruct;
6578 d->currentRenderPassEncoder = nil;
6579 d->currentComputePassEncoder = nil;
6580 d->tessellationComputeEncoder = nil;
6581 d->currentPassRpDesc = nil;
6588 currentTarget =
nullptr;
6596 currentPipelineGeneration = 0;
6599 currentSrbGeneration = 0;
6602 currentIndexOffset = 0;
6603 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
6608 currentDepthBiasValues = { 0.0f, 0.0f };
6610 currentViewport = {};
6612 d->currentShaderResourceBindingState = {};
6613 d->currentDepthStencilState = nil;
6615 d->currentVertexInputsBuffers.clear();
6616 d->currentVertexInputOffsets.clear();
6626 d->sem[i] =
nullptr;
6627 d->msaaTex[i] = nil;
6647 dispatch_release(
d->sem[i]);
6648 d->sem[i] =
nullptr;
6653 [d->msaaTex[i] release];
6654 d->msaaTex[i] = nil;
6660 [d->curDrawable release];
6661 d->curDrawable = nil;
6665 rhiD->swapchains.remove(
this);
6666 rhiD->unregisterResource(
this);
6686 CALayer *layer =
nullptr;
6688 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
6689 layer = cocoaWindow->contentLayer();
6691 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
6694 return static_cast<CAMetalLayer *>(layer);
6703 d.reserved[0] = layerForWindow(window);
6710 CAMetalLayer *layer =
d->layer;
6712 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
6715 int height = (
int)layer.bounds.size.height;
6716 int width = (
int)layer.bounds.size.width;
6717 width *= layer.contentsScale;
6718 height *= layer.contentsScale;
6719 return QSize(width, height);
6724 if (f == HDRExtendedSrgbLinear) {
6726 }
else if (f == HDR10) {
6728 }
else if (f == HDRExtendedDisplayP3Linear) {
6742 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
6748 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
6749 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
6751 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
6754 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
6758 rhiD->registerResource(rpD,
false);
6765 samples = rhiD->effectiveSampleCount(m_sampleCount);
6767 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
6768 d->colorFormat = MTLPixelFormatRGBA16Float;
6769 d->rhiColorFormat = QRhiTexture::RGBA16F;
6772 if (m_format == HDR10) {
6773 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
6774 d->rhiColorFormat = QRhiTexture::RGB10A2;
6777 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
6778 d->rhiColorFormat = QRhiTexture::BGRA8;
6787 dispatch_semaphore_t sem =
d->sem[slot];
6788 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
6789 dispatch_semaphore_signal(sem);
6796 const bool needsRegistration = !window || window != m_window;
6798 if (window && window != m_window)
6803 if (needsRegistration || !rhiD->swapchains.contains(
this))
6804 rhiD->swapchains.insert(
this);
6808 if (window->surfaceType() != QSurface::MetalSurface) {
6809 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6813 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6817 if (
d->colorFormat !=
d->layer.pixelFormat)
6818 d->layer.pixelFormat =
d->colorFormat;
6820 if (m_format == HDRExtendedSrgbLinear) {
6821 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6822 d->layer.wantsExtendedDynamicRangeContent = YES;
6823 }
else if (m_format == HDR10) {
6824 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6825 d->layer.wantsExtendedDynamicRangeContent = YES;
6826 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6827 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6828 d->layer.wantsExtendedDynamicRangeContent = YES;
6831 if (m_flags.testFlag(UsedAsTransferSource))
6832 d->layer.framebufferOnly = NO;
6835 if (m_flags.testFlag(NoVSync))
6836 d->layer.displaySyncEnabled = NO;
6839 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6840 d->layer.opaque = NO;
6841 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6846 d->layer.opaque = NO;
6848 d->layer.opaque = YES;
6854 int width = (
int)
d->layer.bounds.size.width;
6855 int height = (
int)
d->layer.bounds.size.height;
6856 CGSize layerSize = CGSizeMake(width, height);
6857 const float scaleFactor =
d->layer.contentsScale;
6858 layerSize.width *= scaleFactor;
6859 layerSize.height *= scaleFactor;
6860 d->layer.drawableSize = layerSize;
6862 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6863 pixelSize = m_currentPixelSize;
6865 [d->layer setDevice: rhiD->d->dev];
6867 [d->curDrawable release];
6868 d->curDrawable = nil;
6879 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6880 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6881 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6882 m_depthStencil->sampleCount(), m_sampleCount);
6884 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6885 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6886 m_depthStencil->setPixelSize(pixelSize);
6887 if (!m_depthStencil->create())
6888 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6889 pixelSize.width(), pixelSize.height());
6891 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6892 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6893 pixelSize.width(), pixelSize.height());
6897 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6898 rtWrapper.d->pixelSize = pixelSize;
6904 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6905 pixelSize.width(), pixelSize.height(), scaleFactor);
6908 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6909 desc.textureType = MTLTextureType2DMultisample;
6910 desc.pixelFormat =
d->colorFormat;
6911 desc.width = NSUInteger(pixelSize.width());
6912 desc.height = NSUInteger(pixelSize.height());
6913 desc.sampleCount = NSUInteger(
samples);
6914 desc.resourceOptions = MTLResourceStorageModePrivate;
6915 desc.storageMode = MTLStorageModePrivate;
6916 desc.usage = MTLTextureUsageRenderTarget;
6918 if (
d->msaaTex[i]) {
6922 e.renderbuffer.texture =
d->msaaTex[i];
6923 rhiD
->d->releaseQueue.append(e);
6925 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6930 rhiD->registerResource(
this);
6946#if defined(Q_OS_MACOS)
6947 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
6948 NSScreen *screen = view.window.screen;
6949 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
6950 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
6951#elif defined(Q_OS_IOS)
6952 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
6953 UIScreen *screen = view.window.windowScene.screen;
6954 info.limits.colorComponentValue.maxColorComponentValue =
6955 view.window.windowScene.screen.currentEDRHeadroom;
6956 info.limits.colorComponentValue.maxPotentialColorComponentValue =
6957 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