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
168 nativeResourceBindingMap.clear();
173 [argumentEncoder release];
174 argumentEncoder = nil;
190 const QColor &colorClearValue,
191 const QRhiDepthStencilClearValue &depthStencilClearValue,
193 QRhiShadingRateMap *shadingRateMap);
195 bool preferArgumentBuffers,
196 QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
226 id<MTLTexture> texture;
321 int frameSlot, quint32 minBlockSize, quint32 *offset);
517 return vertexOrIndexCount * instanceCount *
sizeof(
float) * 60;
526 return patchCount *
sizeof(
float) * 128;
574 if (importDevice->dev) {
575 d->dev = (id<MTLDevice>) importDevice->dev;
577 if (importedCmdQueue)
578 d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
580 qWarning(
"No MTLDevice given, cannot import");
594 return (v + byteAlign - 1) & ~(byteAlign - 1);
599 QMacAutoReleasePool pool;
602 id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
616 return [cmdQueue commandBufferWithUnretainedReferences];
618 return [cmdQueue commandBuffer];
629 MTLBinaryArchiveDescriptor *binArchDesc = [MTLBinaryArchiveDescriptor
new];
630 binArchDesc.url = sourceFileUrl;
632 binArch = [dev newBinaryArchiveWithDescriptor: binArchDesc error: &err];
633 [binArchDesc release];
635 const QString msg = QString::fromNSString(err.localizedDescription);
636 qWarning(
"newBinaryArchiveWithDescriptor failed: %s", qPrintable(msg));
649 d->dev = MTLCreateSystemDefaultDevice();
652 qWarning(
"No MTLDevice");
656 const QString deviceName = QString::fromNSString([d->dev name]);
657 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
658 driverInfoStruct.deviceName = deviceName.toUtf8();
665 const MTLDeviceLocation deviceLocation = [d->dev location];
666 switch (deviceLocation) {
667 case MTLDeviceLocationBuiltIn:
668 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
670 case MTLDeviceLocationSlot:
671 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
673 case MTLDeviceLocationExternal:
674 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
680 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
683 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
684 osMajor = ver.majorVersion();
685 osMinor = ver.minorVersion();
687 if (importedCmdQueue)
688 [d->cmdQueue retain];
690 d->cmdQueue = [d->dev newCommandQueue];
692 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
696 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
697 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
698 d->captureScope.label = label.toNSString();
700#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
701 caps.maxTextureSize = 16384;
702 caps.baseVertexAndInstance =
true;
703 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
704 caps.maxThreadGroupSize = 1024;
705 caps.multiView =
true;
706#elif defined(Q_OS_TVOS)
707 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
708 caps.maxTextureSize = 16384;
710 caps.maxTextureSize = 8192;
711 caps.baseVertexAndInstance =
false;
712 caps.isAppleGPU =
true;
713#elif defined(Q_OS_IOS)
714 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
715 caps.maxTextureSize = 16384;
716 caps.baseVertexAndInstance =
true;
717 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
718 caps.maxTextureSize = 8192;
719 caps.baseVertexAndInstance =
false;
721 caps.maxTextureSize = 4096;
722 caps.baseVertexAndInstance =
false;
724 caps.isAppleGPU =
true;
725 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
726 caps.maxThreadGroupSize = 1024;
727 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
728 caps.multiView =
true;
734 caps.usePrivateStaticBuffers = caps.isAppleGPU;
735 if (qEnvironmentVariableIntValue(
"QT_METAL_NO_PRIVATE_STATIC_BUFFERS"))
736 caps.usePrivateStaticBuffers =
false;
738 caps.supportedSampleCounts = { 1 };
739 for (
int sampleCount : { 2, 4, 8 }) {
740 if ([d->dev supportsTextureSampleCount: sampleCount])
741 caps.supportedSampleCounts.append(sampleCount);
744 caps.indirectCommandBuffers = ([d->dev supportsFamily:MTLGPUFamilyApple5]
745 || [d->dev supportsFamily:MTLGPUFamilyMac2])
746 && [d->dev supportsFamily:MTLGPUFamilyMetal3];
748 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
749 if (caps.shadingRateMap && caps.multiView)
750 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
753 caps.depthClamp = [d->dev supportsFamily:MTLGPUFamilyApple3];
755 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
756 d->setupBinaryArchive();
758 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
759 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
769 for (QMetalShader &s : d->shaderCache)
771 d->shaderCache.clear();
773 [d->captureScope release];
774 d->captureScope = nil;
776 for (
auto *pools : { d->argBufPool, d->bufStagingPool }) {
777 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
778 [pools[i].buf release];
780 pools[i].capacity = 0;
785 [d->icbArgumentBuffer release];
786 d->icbArgumentBuffer = nil;
788 [d->icbRangeBuffer release];
789 d->icbRangeBuffer = nil;
791 [d->icbNoCountBuffer release];
792 d->icbNoCountBuffer = nil;
794 [d->icbEncodeFunction release];
795 d->icbEncodeFunction = nil;
797 [d->icbEncodeFunctionU32 release];
798 d->icbEncodeFunctionU32 = nil;
800 [d->icbEncodeFunctionU16 release];
801 d->icbEncodeFunctionU16 = nil;
803 [d->icbEncodePipeline release];
804 d->icbEncodePipeline = nil;
806 [d->icbEncodePipelineU32 release];
807 d->icbEncodePipelineU32 = nil;
809 [d->icbEncodePipelineU16 release];
810 d->icbEncodePipelineU16 = nil;
818 [d->binArch release];
821 [d->cmdQueue release];
822 if (!importedCmdQueue)
832 return caps.supportedSampleCounts;
837 Q_UNUSED(sampleCount);
838 return { QSize(1, 1) };
843 return new QMetalSwapChain(
this);
846QRhiBuffer *
QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
848 return new QMetalBuffer(
this, type, usage, size);
875 static constexpr QMatrix4x4 m(1.0f, 0.0f, 0.0f, 0.0f,
876 0.0f, 1.0f, 0.0f, 0.0f,
877 0.0f, 0.0f, 0.5f, 0.5f,
878 0.0f, 0.0f, 0.0f, 1.0f);
886 bool supportsFamilyMac2 =
false;
887 bool supportsFamilyApple3 =
false;
890 supportsFamilyMac2 =
true;
892 supportsFamilyApple3 =
true;
894 supportsFamilyApple3 =
true;
898 if (format == QRhiTexture::BC5)
901 if (!supportsFamilyApple3) {
902 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
904 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
908 if (!supportsFamilyMac2)
909 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
918 case QRhi::MultisampleTexture:
920 case QRhi::MultisampleRenderBuffer:
922 case QRhi::DebugMarkers:
924 case QRhi::Timestamps:
926 case QRhi::Instancing:
928 case QRhi::CustomInstanceStepRate:
930 case QRhi::PrimitiveRestart:
932 case QRhi::NonDynamicUniformBuffers:
934 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
936 case QRhi::NPOTTextureRepeat:
938 case QRhi::RedOrAlpha8IsRed:
940 case QRhi::ElementIndexUint:
944 case QRhi::WideLines:
946 case QRhi::VertexShaderPointSize:
948 case QRhi::BaseVertex:
949 return caps.baseVertexAndInstance;
950 case QRhi::BaseInstance:
951 return caps.baseVertexAndInstance;
952 case QRhi::TriangleFanTopology:
954 case QRhi::ReadBackNonUniformBuffer:
956 case QRhi::ReadBackNonBaseMipLevel:
958 case QRhi::TexelFetch:
960 case QRhi::RenderToNonBaseMipLevel:
962 case QRhi::IntAttributes:
964 case QRhi::ScreenSpaceDerivatives:
966 case QRhi::ReadBackAnyTextureFormat:
968 case QRhi::PipelineCacheDataLoadSave:
974 case QRhi::ImageDataStride:
976 case QRhi::RenderBufferImport:
978 case QRhi::ThreeDimensionalTextures:
980 case QRhi::RenderTo3DTextureSlice:
982 case QRhi::TextureArrays:
984 case QRhi::Tessellation:
986 case QRhi::GeometryShader:
988 case QRhi::TextureArrayRange:
990 case QRhi::NonFillPolygonMode:
992 case QRhi::OneDimensionalTextures:
994 case QRhi::OneDimensionalTextureMipmaps:
996 case QRhi::HalfAttributes:
998 case QRhi::RenderToOneDimensionalTexture:
1000 case QRhi::ThreeDimensionalTextureMipmaps:
1002 case QRhi::MultiView:
1003 return caps.multiView;
1004 case QRhi::TextureViewFormat:
1006 case QRhi::ResolveDepthStencil:
1008 case QRhi::VariableRateShading:
1010 case QRhi::VariableRateShadingMap:
1011 return caps.shadingRateMap;
1012 case QRhi::VariableRateShadingMapWithTexture:
1014 case QRhi::PerRenderTargetBlending:
1015 case QRhi::SampleVariables:
1017 case QRhi::InstanceIndexIncludesBaseInstance:
1019 case QRhi::DepthClamp:
1020 return caps.depthClamp;
1021 case QRhi::DrawIndirect:
1023 case QRhi::DrawIndirectMulti:
1024 return caps.indirectCommandBuffers;
1025 case QRhi::ShaderDrawParameters:
1027 case QRhi::PushConstants:
1029 case QRhi::DrawIndirectCount:
1030 return caps.indirectCommandBuffers;
1031 case QRhi::DispatchIndirect:
1033 case QRhi::BufferToBufferCopy:
1035 case QRhi::StaticBuffersOnGpuTimeline:
1036 return caps.usePrivateStaticBuffers;
1046 case QRhi::TextureSizeMin:
1048 case QRhi::TextureSizeMax:
1049 return caps.maxTextureSize;
1050 case QRhi::MaxColorAttachments:
1052 case QRhi::FramesInFlight:
1054 case QRhi::MaxAsyncReadbackFrames:
1056 case QRhi::MaxThreadGroupsPerDimension:
1058 case QRhi::MaxThreadsPerThreadGroup:
1060 case QRhi::MaxThreadGroupX:
1062 case QRhi::MaxThreadGroupY:
1064 case QRhi::MaxThreadGroupZ:
1065 return caps.maxThreadGroupSize;
1066 case QRhi::TextureArraySizeMax:
1068 case QRhi::MaxUniformBufferRange:
1070 case QRhi::MaxVertexInputs:
1072 case QRhi::MaxVertexOutputs:
1074 case QRhi::MaxPushConstantsSize:
1076 case QRhi::MaxVertexStorageBuffers:
1077 case QRhi::MaxFragmentStorageBuffers:
1079 case QRhi::ShadingRateImageTileSize:
1089 return &nativeHandlesStruct;
1094 return driverInfoStruct;
1100 result.totalPipelineCreationTime = totalPipelineCreationTime();
1117 for (QMetalShader &s : d->shaderCache)
1120 d->shaderCache.clear();
1142 if (!d->binArch || !rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
1147 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1152 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1153 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1155 if (![d->binArch serializeToURL: url error: &err]) {
1156 const QString msg = QString::fromNSString(err.localizedDescription);
1158 qCDebug(QRHI_LOG_INFO,
"Failed to serialize MTLBinaryArchive: %s", qPrintable(msg));
1163 if (!f.open(QIODevice::ReadOnly)) {
1164 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to reopen temporary file");
1167 const QByteArray blob = f.readAll();
1171 const quint32 dataSize = quint32(blob.size());
1173 data.resize(headerSize + dataSize);
1176 header.rhiId = pipelineCacheRhiId();
1177 header.arch = quint32(
sizeof(
void*));
1178 header.dataSize = quint32(dataSize);
1179 header.osMajor = osMajor;
1180 header.osMinor = osMinor;
1181 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1183 memcpy(header.driver, driverInfoStruct.deviceName.constData(), driverStrLen);
1184 header.driver[driverStrLen] =
'\0';
1186 memcpy(data.data(), &header, headerSize);
1187 memcpy(data.data() + headerSize, blob.constData(), dataSize);
1197 if (data.size() < qsizetype(headerSize)) {
1198 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (header incomplete)");
1202 const size_t dataOffset = headerSize;
1204 memcpy(&header, data.constData(), headerSize);
1206 const quint32 rhiId = pipelineCacheRhiId();
1207 if (header.rhiId != rhiId) {
1208 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: The data is for a different QRhi version or backend (%u, %u)",
1209 rhiId, header.rhiId);
1213 const quint32 arch = quint32(
sizeof(
void*));
1214 if (header.arch != arch) {
1215 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Architecture does not match (%u, %u)",
1220 if (header.osMajor != osMajor || header.osMinor != osMinor) {
1221 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: OS version does not match (%u.%u, %u.%u)",
1222 osMajor, osMinor, header.osMajor, header.osMinor);
1226 const size_t driverStrLen = qMin(
sizeof(header
.driver) - 1, size_t(driverInfoStruct.deviceName.length()));
1227 if (strncmp(header
.driver, driverInfoStruct.deviceName.constData(), driverStrLen)) {
1228 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Metal device name does not match");
1232 if (quint64(data.size()) < quint64(dataOffset) + header.dataSize) {
1233 qCDebug(QRHI_LOG_INFO,
"setPipelineCacheData: Invalid blob size (data incomplete)");
1237 const char *p = data.constData() + dataOffset;
1241 qCDebug(QRHI_LOG_INFO,
"pipelineCacheData: Failed to create temporary file for Metal");
1244 tmp.write(p, header.dataSize);
1247 const QString fn = QFileInfo(tmp.fileName()).absoluteFilePath();
1248 NSURL *url = QUrl::fromLocalFile(fn).toNSURL();
1249 if (
d->setupBinaryArchive(url))
1250 qCDebug(QRHI_LOG_INFO,
"Created MTLBinaryArchive with initial data of %u bytes", header.dataSize);
1253QRhiRenderBuffer *
QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type,
const QSize &pixelSize,
1254 int sampleCount, QRhiRenderBuffer::Flags flags,
1255 QRhiTexture::Format backingFormatHint)
1257 return new QMetalRenderBuffer(
this, type, pixelSize, sampleCount, flags, backingFormatHint);
1261 const QSize &pixelSize,
int depth,
int arraySize,
1262 int sampleCount, QRhiTexture::Flags flags)
1264 return new QMetalTexture(
this, format, pixelSize, depth, arraySize, sampleCount, flags);
1268 QRhiSampler::Filter mipmapMode,
1269 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v, QRhiSampler::AddressMode w)
1271 return new QMetalSampler(
this, magFilter, minFilter, mipmapMode, u, v, w);
1276 return new QMetalShadingRateMap(
this);
1280 QRhiTextureRenderTarget::Flags flags)
1287 return new QMetalGraphicsPipeline(
this);
1292 return new QMetalComputePipeline(
this);
1297 return new QMetalShaderResourceBindings(
this);
1308 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
1311 const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
1312 if (!map || map->isEmpty())
1315 auto it = map->constFind(binding);
1316 if (it != map->cend())
1328 case QRhiShaderResourceBinding::ImageLoad:
1329 return MTLResourceUsageRead;
1330 case QRhiShaderResourceBinding::ImageStore:
1331 return MTLResourceUsageWrite;
1333 return MTLResourceUsageRead | MTLResourceUsageWrite;
1341 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : res.textures) {
1342 switch (encoderStage) {
1343 case QMetalShaderResourceBindingsData::VERTEX:
1344 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageVertex];
1346 case QMetalShaderResourceBindingsData::FRAGMENT:
1347 [cbD->d->currentRenderPassEncoder useResource: t.mtltex usage: t.usage stages: MTLRenderStageFragment];
1349 case QMetalShaderResourceBindingsData::COMPUTE:
1350 [cbD->d->currentComputePassEncoder useResource: t.mtltex usage: t.usage];
1360 const QRhiBatchedBindings<id<MTLBuffer>>::Batch &bufferBatch,
1361 const QRhiBatchedBindings<NSUInteger>::Batch &offsetBatch)
1364 case QMetalShaderResourceBindingsData::VERTEX:
1365 [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
1366 offsets: offsetBatch.resources.constData()
1367 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1369 case QMetalShaderResourceBindingsData::FRAGMENT:
1370 [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
1371 offsets: offsetBatch.resources.constData()
1372 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1374 case QMetalShaderResourceBindingsData::COMPUTE:
1375 [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
1376 offsets: offsetBatch.resources.constData()
1377 withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1391 const QRhiBatchedBindings<id<MTLTexture>>::Batch &textureBatch)
1394 case QMetalShaderResourceBindingsData::VERTEX:
1395 [cbD->d->currentRenderPassEncoder setVertexTextures: textureBatch.resources.constData()
1396 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1398 case QMetalShaderResourceBindingsData::FRAGMENT:
1399 [cbD->d->currentRenderPassEncoder setFragmentTextures: textureBatch.resources.constData()
1400 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1402 case QMetalShaderResourceBindingsData::COMPUTE:
1403 [cbD->d->currentComputePassEncoder setTextures: textureBatch.resources.constData()
1404 withRange: NSMakeRange(textureBatch.startBinding, NSUInteger(textureBatch.resources.count()))];
1418 const QRhiBatchedBindings<id<MTLSamplerState>>::Batch &samplerBatch)
1420 switch (encoderStage) {
1421 case QMetalShaderResourceBindingsData::VERTEX:
1422 [cbD->d->currentRenderPassEncoder setVertexSamplerStates: samplerBatch.resources.constData()
1423 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1425 case QMetalShaderResourceBindingsData::FRAGMENT:
1426 [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: samplerBatch.resources.constData()
1427 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1429 case QMetalShaderResourceBindingsData::COMPUTE:
1430 [cbD->d->currentComputePassEncoder setSamplerStates: samplerBatch.resources.constData()
1431 withRange: NSMakeRange(samplerBatch.startBinding, NSUInteger(samplerBatch.resources.count()))];
1453 for (
int i = 0, ie = bindingData->res[resourceStage].bufferBatches.batches.count(); i != ie; ++i) {
1454 const auto &bufferBatch(bindingData->res[resourceStage].bufferBatches.batches[i]);
1455 const auto &offsetBatch(bindingData->res[resourceStage].bufferOffsetBatches.batches[i]);
1456 bindStageBuffers(cbD, encoderStage, bufferBatch, offsetBatch);
1459 for (
int i = 0, ie = bindingData->res[resourceStage].textureBatches.batches.count(); i != ie; ++i) {
1460 const auto &batch(bindingData->res[resourceStage].textureBatches.batches[i]);
1461 bindStageTextures(cbD, encoderStage, batch);
1464 for (
int i = 0, ie = bindingData->res[resourceStage].samplerBatches.batches.count(); i != ie; ++i) {
1465 const auto &batch(bindingData->res[resourceStage].samplerBatches.batches[i]);
1466 bindStageSamplers(cbD, encoderStage, batch);
1469 if (bindingData->res[resourceStage].usesArgumentBuffer)
1470 declareStageArgumentBufferResources(cbD, encoderStage, bindingData->res[resourceStage]);
1476 case QMetalShaderResourceBindingsData::VERTEX:
1477 return QRhiShaderResourceBinding::StageFlag::VertexStage;
1478 case QMetalShaderResourceBindingsData::TESSCTRL:
1479 return QRhiShaderResourceBinding::StageFlag::TessellationControlStage;
1480 case QMetalShaderResourceBindingsData::TESSEVAL:
1481 return QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage;
1482 case QMetalShaderResourceBindingsData::FRAGMENT:
1483 return QRhiShaderResourceBinding::StageFlag::FragmentStage;
1484 case QMetalShaderResourceBindingsData::COMPUTE:
1485 return QRhiShaderResourceBinding::StageFlag::ComputeStage;
1488 Q_UNREACHABLE_RETURN(QRhiShaderResourceBinding::StageFlag::VertexStage);
1493 int dynamicOffsetCount,
1494 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1495 bool offsetOnlyChange,
1496 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[
SUPPORTED_STAGES],
1501 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1502 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1504 case QRhiShaderResourceBinding::UniformBuffer:
1506 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1507 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1508 quint32 offset = b->u.ubuf.offset;
1509 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1510 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1511 if (dynOfs.first == b->binding) {
1512 offset = dynOfs.second;
1517 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1518 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1519 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1520 if (nativeBinding >= 0)
1521 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1526 case QRhiShaderResourceBinding::SampledTexture:
1527 case QRhiShaderResourceBinding::Texture:
1528 case QRhiShaderResourceBinding::Sampler:
1530 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->stex;
1531 for (
int elem = 0; elem < data->count(); ++elem) {
1532 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->stex.texSamplers[elem].tex);
1533 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->stex.texSamplers[elem].sampler);
1535 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1536 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1541 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1542 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1543 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1544 if (textureBinding >= 0 && texD)
1545 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->textureForSampling(), MTLResourceUsageRead });
1546 if (samplerBinding >= 0)
1547 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1553 case QRhiShaderResourceBinding::ImageLoad:
1554 case QRhiShaderResourceBinding::ImageStore:
1555 case QRhiShaderResourceBinding::ImageLoadStore:
1557 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1558 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1560 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1561 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1562 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1563 if (nativeBinding >= 0)
1564 bindingData.res[stage].textures.append({ nativeBinding, t, storageImageUsage(b->type) });
1569 case QRhiShaderResourceBinding::BufferLoad:
1570 case QRhiShaderResourceBinding::BufferStore:
1571 case QRhiShaderResourceBinding::BufferLoadStore:
1573 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1574 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1575 quint32 offset = b->u.sbuf.offset;
1576 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1577 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1578 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1579 if (nativeBinding >= 0)
1580 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1596 if (!shader || !shader->argumentEncoder)
1603 if (offsetOnlyChange) {
1605 cbD
->d->currentShaderResourceBindingState.res[stage]);
1607 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &b : prev.buffers) {
1608 if (b.nativeBinding == shader->argumentBufferIndex) {
1609 res.samplers.clear();
1610 res.buffers.append(b);
1611 res.usesArgumentBuffer =
true;
1624 const quint32 argBufAlignment = qMax(quint32(shader->argumentEncoder.alignment),
1626 quint32 argBufOffset = 0;
1627 id<MTLBuffer> argBuf =
d->allocArgumentBuffer(quint32(shader->argumentEncoder.encodedLength),
1628 argBufAlignment, currentFrameSlot, &argBufOffset);
1634 qWarning(
"Failed to allocate Metal argument buffer");
1635 res.textures.clear();
1636 res.samplers.clear();
1639 [shader->argumentEncoder setArgumentBuffer: argBuf offset: argBufOffset];
1640 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(res.textures))
1641 [shader->argumentEncoder setTexture: t.mtltex atIndex: NSUInteger(t.nativeBinding)];
1642 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &sm : std::as_const(res.samplers))
1643 [shader->argumentEncoder setSamplerState: sm.mtlsampler atIndex: NSUInteger(sm.nativeBinding)];
1644 res.samplers.clear();
1665 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1666 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1667 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1670 bindingData.res[stage].bufferBatches.finish();
1671 bindingData.res[stage].bufferOffsetBatches.finish();
1673 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1674 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1675 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1677 if (cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1678 && cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1679 && bufferBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1680 && offsetBatch == cbD
->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1684 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1687 if (offsetOnlyChange)
1690 if (bindingData.res[stage].usesArgumentBuffer) {
1691 declareStageArgumentBufferResources(cbD, stage, bindingData.res[stage]);
1703 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1704 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1706 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1707 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1709 bindingData.res[stage].textureBatches.finish();
1710 bindingData.res[stage].samplerBatches.finish();
1712 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1713 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1715 if (cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1716 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1720 bindStageTextures(cbD, stage, batch);
1723 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1724 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1726 if (cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1727 && batch == cbD
->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1731 bindStageSamplers(cbD, stage, batch);
1735 cbD
->d->currentShaderResourceBindingState = bindingData;
1740 const QList<QShaderDescription::PushConstantBlock> blocks = s.desc.pushConstantBlocks();
1741 return blocks.isEmpty() ? 0 : quint32(blocks.first().size);
1746 return s.nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslPushConstantBufferBinding, -1);
1759 [cbD->d->currentRenderPassEncoder setRenderPipelineState: d->ps];
1761 if (cbD
->d->currentDepthStencilState !=
d->ds) {
1762 [cbD->d->currentRenderPassEncoder setDepthStencilState: d->ds];
1763 cbD
->d->currentDepthStencilState =
d->ds;
1766 [cbD->d->currentRenderPassEncoder setCullMode: d->cullMode];
1770 [cbD->d->currentRenderPassEncoder setTriangleFillMode: d->triangleFillMode];
1773 if (rhiD->caps.depthClamp) {
1775 [cbD->d->currentRenderPassEncoder setDepthClipMode: d->depthClipMode];
1780 [cbD->d->currentRenderPassEncoder setFrontFacingWinding: d->winding];
1783 if (!qFuzzyCompare(
d->depthBias, cbD->currentDepthBiasValues.first)
1786 [cbD->d->currentRenderPassEncoder setDepthBias: d->depthBias
1787 slopeScale: d->slopeScaledDepthBias
1793 const int vsIdx = mtlPushConstantBufferIndex(
d->vs);
1794 const int fsIdx = mtlPushConstantBufferIndex(
d->fs);
1795 if (vsIdx >= 0 || fsIdx >= 0) {
1796 const quint32 blockSize = qMax(mtlPushConstantBlockSize(d->vs), mtlPushConstantBlockSize(d->fs));
1797 if (quint32(cbD->pushConstantData.size()) < blockSize)
1798 cbD->pushConstantData.resize(
int(blockSize), 0);
1799 const NSUInteger total = NSUInteger(cbD->pushConstantData.size());
1801 [cbD->d->currentRenderPassEncoder setVertexBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(vsIdx)];
1803 [cbD->d->currentRenderPassEncoder setFragmentBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(fsIdx)];
1825 if (!psD
->d->tess.enabled && !psD
->d->tess.failed)
1830 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.deviceLocalWorkBuffers) {
1831 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1832 workBuf->lastActiveFrameSlot = -1;
1834 for (QMetalBuffer *workBuf : psD->d->extraBufMgr.hostVisibleWorkBuffers) {
1835 if (workBuf && workBuf->lastActiveFrameSlot == currentFrameSlot)
1836 workBuf->lastActiveFrameSlot = -1;
1839 psD->lastActiveFrameSlot = currentFrameSlot;
1843 int dynamicOffsetCount,
1844 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1853 srb = gfxPsD->m_shaderResourceBindings;
1855 srb = compPsD->m_shaderResourceBindings;
1859 bool hasSlottedResourceInSrb =
false;
1860 bool hasDynamicOffsetInSrb =
false;
1861 bool resNeedsRebind =
false;
1863 bool pipelineChanged =
false;
1876 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1879 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1880 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1883 case QRhiShaderResourceBinding::UniformBuffer:
1886 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1887 sanityCheckResourceOwnership(bufD);
1890 hasSlottedResourceInSrb =
true;
1891 if (b->u.ubuf.hasDynamicOffset)
1892 hasDynamicOffsetInSrb =
true;
1893 if (bufD
->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1894 resNeedsRebind =
true;
1895 bd.ubuf.id = bufD->m_id;
1898 bufD->lastActiveFrameSlot = currentFrameSlot;
1901 case QRhiShaderResourceBinding::SampledTexture:
1902 case QRhiShaderResourceBinding::Texture:
1903 case QRhiShaderResourceBinding::Sampler:
1905 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->stex;
1906 if (bd.stex.d.size() != data->count()) {
1907 bd.stex.d.resize(data->count());
1908 resNeedsRebind =
true;
1910 for (
int elem = 0; elem < data->count(); ++elem) {
1913 Q_ASSERT(texD || samplerD);
1914 sanityCheckResourceOwnership(texD);
1915 sanityCheckResourceOwnership(samplerD);
1916 const quint64 texId = texD ? texD->m_id : 0;
1918 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1919 const uint samplerGen = samplerD ? samplerD
->generation : 0;
1920 if (texGen != bd.stex.d[elem].texGeneration
1921 || texId != bd.stex.d[elem].texId
1922 || samplerGen != bd.stex.d[elem].samplerGeneration
1923 || samplerId != bd.stex.d[elem].samplerId)
1925 resNeedsRebind =
true;
1926 bd.stex.d[elem].texId = texId;
1927 bd.stex.d[elem].texGeneration = texGen;
1928 bd.stex.d[elem].samplerId = samplerId;
1929 bd.stex.d[elem].samplerGeneration = samplerGen;
1932 texD->lastActiveFrameSlot = currentFrameSlot;
1934 samplerD->lastActiveFrameSlot = currentFrameSlot;
1938 case QRhiShaderResourceBinding::ImageLoad:
1939 case QRhiShaderResourceBinding::ImageStore:
1940 case QRhiShaderResourceBinding::ImageLoadStore:
1943 sanityCheckResourceOwnership(texD);
1944 if (texD
->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1945 resNeedsRebind =
true;
1946 bd.simage.id = texD->m_id;
1949 texD->lastActiveFrameSlot = currentFrameSlot;
1952 case QRhiShaderResourceBinding::BufferLoad:
1953 case QRhiShaderResourceBinding::BufferStore:
1954 case QRhiShaderResourceBinding::BufferLoadStore:
1957 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1958 sanityCheckResourceOwnership(bufD);
1960 if (needsBufferSizeBuffer) {
1961 for (
int i = 0; i < 6; ++i) {
1962 const QRhiShaderResourceBinding::StageFlag stage =
1963 QRhiShaderResourceBinding::StageFlag(1 << i);
1964 if (b->stage.testFlag(stage)) {
1965 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1971 if (bufD
->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1972 resNeedsRebind =
true;
1973 bd.sbuf.id = bufD->m_id;
1976 bufD->lastActiveFrameSlot = currentFrameSlot;
1985 if (needsBufferSizeBuffer) {
1987 QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1991 Q_ASSERT(compPsD
->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1992 shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage});
1995 if (gfxPsD
->d->tess.enabled) {
2005 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[1].desc.storageBlocks());
2006 Q_ASSERT(gfxPsD
->d->tess.compVs[0].desc.storageBlocks() == gfxPsD
->d->tess.compVs[2].desc.storageBlocks());
2007 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2008 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2009 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
2010 == gfxPsD
->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2011 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
2012 == gfxPsD
->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
2013 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
2014 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
2015 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
2016 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
2018 if (gfxPsD
->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2019 shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage});
2021 if (gfxPsD
->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2022 shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage});
2024 if (gfxPsD
->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2025 shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage});
2028 if (gfxPsD
->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2029 shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage});
2031 if (gfxPsD
->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
2032 shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage});
2036 for (
const auto &shader : shaders) {
2038 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
2041 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
2043 int maxNativeBinding = 0;
2044 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
2045 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
2047 const int size = (maxNativeBinding + 1) *
sizeof(
int);
2049 Q_ASSERT(offset + size <= bufD->size());
2050 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
2052 QMetalShaderResourceBindings::BoundResourceData bd;
2053 bd.sbuf.id = bufD->m_id;
2054 bd.sbuf.generation = bufD->generation;
2055 srbD->boundResourceData.append(bd);
2059 QVarLengthArray<
int, 8> bufferSizeBufferData;
2060 Q_ASSERT(storageBufferSizes.contains(shader.second));
2061 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
2062 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
2063 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
2069 if (bufferSizeBufferData.size() <= index)
2070 bufferSizeBufferData.resize(index + 1);
2072 Q_ASSERT(sizes.contains(block.binding));
2073 bufferSizeBufferData[index] = sizes[block.binding];
2076 QRhiBufferData data;
2077 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
2078 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
2079 Q_ASSERT(offset + size <= bufD->size());
2080 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
2083 offset += ((size + 31) / 32) * 32;
2087 bufD->lastActiveFrameSlot = currentFrameSlot;
2091 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
2093 resNeedsRebind =
true;
2099 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt || pipelineChanged) {
2100 const QShader::NativeResourceBindingMap *resBindMaps[
SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
2105 if (gfxPsD
->d->tess.enabled) {
2108 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[1].nativeResourceBindingMap);
2109 Q_ASSERT(gfxPsD
->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD
->d->tess.compVs[2].nativeResourceBindingMap);
2128 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind
2129 && !srbChanged && !srbRebuilt && !pipelineChanged;
2130 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange,
2131 resBindMaps, shaders);
2136 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
2137 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
2142 QRhiBatchedBindings<id<MTLBuffer> > buffers;
2143 QRhiBatchedBindings<NSUInteger> offsets;
2144 for (
int i = 0; i < bindingCount; ++i) {
2147 bufD->lastActiveFrameSlot = currentFrameSlot;
2148 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
2149 buffers.feed(startBinding + i, mtlbuf);
2150 offsets.feed(startBinding + i, bindings[i].second);
2165 || buffers != cbD
->d->currentVertexInputsBuffers
2166 || offsets != cbD
->d->currentVertexInputOffsets)
2169 cbD
->d->currentVertexInputsBuffers = buffers;
2170 cbD
->d->currentVertexInputOffsets = offsets;
2172 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
2173 const auto &bufferBatch(buffers.batches[i]);
2174 const auto &offsetBatch(offsets.batches[i]);
2175 [cbD->d->currentRenderPassEncoder setVertexBuffers:
2176 bufferBatch.resources.constData()
2177 offsets: offsetBatch.resources.constData()
2178 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2185 ibufD->lastActiveFrameSlot = currentFrameSlot;
2187 cbD->currentIndexOffset = indexOffset;
2188 cbD->currentIndexFormat = indexFormat;
2199QSize
QRhiMetal::outputSizeForTarget(QRhiRenderTarget *target)
2201 QRhiShadingRateMap *srm =
nullptr;
2202 switch (target->resourceType()) {
2203 case QRhiResource::TextureRenderTarget:
2204 srm =
QRHI_RES(QMetalTextureRenderTarget, target)->m_desc.shadingRateMap();
2206 case QRhiResource::SwapChainRenderTarget:
2207 srm =
QRHI_RES(QMetalSwapChainRenderTarget, target)->swapChain()->shadingRateMap();
2214 const QSize logicalSize = srm->logicalSize();
2215 if (logicalSize.isValid())
2219 return target->pixelSize();
2226 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2227 std::array<
float, 4> vp = cbD->currentViewport.viewport();
2228 float x = 0, y = 0, w = 0, h = 0;
2230 if (qFuzzyIsNull(vp[2]) && qFuzzyIsNull(vp[3])) {
2233 w = outputSize.width();
2234 h = outputSize.height();
2237 qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, vp, &x, &y, &w, &h);
2241 s.x = NSUInteger(x);
2242 s.y = NSUInteger(y);
2243 s.width = NSUInteger(w);
2244 s.height = NSUInteger(h);
2245 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2252 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2256 if (!qrhi_toTopLeftRenderTargetRect<
UnBounded>(outputSize, viewport.viewport(), &x, &y, &w, &h))
2260 vp.originX =
double(x);
2261 vp.originY =
double(y);
2262 vp.width =
double(w);
2263 vp.height =
double(h);
2264 vp.znear =
double(viewport.minDepth());
2265 vp.zfar =
double(viewport.maxDepth());
2267 [cbD->d->currentRenderPassEncoder setViewport: vp];
2269 cbD->currentViewport = viewport;
2283 const QSize outputSize = outputSizeForTarget(cbD->currentTarget);
2287 if (!qrhi_toTopLeftRenderTargetRect<
Bounded>(outputSize, scissor.scissor(), &x, &y, &w, &h))
2291 s.x = NSUInteger(x);
2292 s.y = NSUInteger(y);
2293 s.width = NSUInteger(w);
2294 s.height = NSUInteger(h);
2296 [cbD->d->currentRenderPassEncoder setScissorRect: s];
2299 cbD->currentScissor = scissor;
2307 [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF()
2308 green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
2311 cbD->currentBlendConstants = c;
2319 [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
2322 cbD->currentStencilRef = refValue;
2332 auto patch = [cbD, offset, size, data](quint32 blockSize) {
2333 const quint32 total = qMax(blockSize, offset + size);
2334 if (quint32(cbD->pushConstantData.size()) < total)
2335 cbD->pushConstantData.resize(
int(total), 0);
2336 memcpy(cbD->pushConstantData.data() + offset, data, size);
2344 const int idx = mtlPushConstantBufferIndex(psD
->d->cs);
2346 qWarning(
"No pipeline with a push constant block is active; setPushConstants ignored");
2349 const quint32 total = patch(mtlPushConstantBlockSize(psD->d->cs));
2350 [cbD->d->currentComputePassEncoder setBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(idx)];
2355 if (psD
->d->tess.enabled) {
2356 qWarning(
"Push constants are not supported with tessellation on Metal");
2359 const int vsIdx = mtlPushConstantBufferIndex(psD
->d->vs);
2360 const int fsIdx = mtlPushConstantBufferIndex(psD
->d->fs);
2361 if (vsIdx < 0 && fsIdx < 0) {
2362 qWarning(
"No pipeline with a push constant block is active; setPushConstants ignored");
2365 const quint32 total = patch(qMax(mtlPushConstantBlockSize(psD->d->vs), mtlPushConstantBlockSize(psD->d->fs)));
2367 [cbD->d->currentRenderPassEncoder setVertexBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(vsIdx)];
2369 [cbD->d->currentRenderPassEncoder setFragmentBytes: cbD->pushConstantData.constData() length: total atIndex: NSUInteger(fsIdx)];
2376 Q_UNUSED(coarsePixelSize);
2381 switch (cbD->currentTarget->resourceType()) {
2382 case QRhiResource::SwapChainRenderTarget:
2384 case QRhiResource::TextureRenderTarget:
2395 return tex && tex.storageMode != MTLStorageModeMemoryless;
2405 return finalAction == MTLStoreActionDontCare ? MTLStoreActionStore
2406 : MTLStoreActionStoreAndMultisampleResolve;
2416 for (
const auto &[index, finalAction] : std::as_const(cbD->d->deferredColorStoreActions))
2417 [cbD->d->currentRenderPassEncoder setColorStoreAction:
2418 interruptionStoreAction(finalAction, passIsEnding) atIndex: index];
2420 if (cbD->d->deferredDepthStoreAction != MTLStoreActionUnknown) {
2421 [cbD->d->currentRenderPassEncoder setDepthStoreAction:
2422 interruptionStoreAction(cbD->d->deferredDepthStoreAction, passIsEnding)];
2424 if (cbD->d->deferredStencilStoreAction != MTLStoreActionUnknown) {
2425 [cbD->d->currentRenderPassEncoder setStencilStoreAction:
2426 interruptionStoreAction(cbD->d->deferredStencilStoreAction, passIsEnding)];
2435 for (qsizetype i = 0; i < cbD->d->openDebugGroups.size(); ++i)
2436 [cbD->d->currentRenderPassEncoder popDebugGroup];
2437 [cbD->d->currentRenderPassEncoder endEncoding];
2438 cbD->d->currentRenderPassEncoder = nil;
2443 id<MTLComputeCommandEncoder> maybeComputeEncoder)
2445 if (cbD
->d->currentRenderPassEncoder)
2448 if (!maybeComputeEncoder)
2449 maybeComputeEncoder = [cbD->d->cb computeCommandEncoder];
2451 return maybeComputeEncoder;
2455 id<MTLComputeCommandEncoder> computeEncoder)
2457 if (computeEncoder) {
2458 [computeEncoder endEncoding];
2459 computeEncoder = nil;
2468 const auto canLoad = [](MTLRenderPassAttachmentDescriptor *att) {
2469 return att.storeAction != MTLStoreActionDontCare && canStoreAttachment(att.texture);
2472 QVarLengthArray<MTLLoadAction, 4> oldColorLoad;
2474 oldColorLoad.append(cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction);
2475 if (canLoad(cbD->d->currentPassRpDesc.colorAttachments[i]))
2476 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
2479 MTLLoadAction oldDepthLoad;
2480 MTLLoadAction oldStencilLoad;
2482 oldDepthLoad = cbD
->d->currentPassRpDesc.depthAttachment.loadAction;
2483 if (canLoad(cbD->d->currentPassRpDesc.depthAttachment))
2484 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
2486 oldStencilLoad = cbD
->d->currentPassRpDesc.stencilAttachment.loadAction;
2487 if (canLoad(cbD->d->currentPassRpDesc.stencilAttachment))
2488 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
2495 const QRhiViewport prevViewport = cbD->currentViewport;
2497 const QRhiScissor prevScissor = cbD->currentScissor;
2499 const QColor prevBlendConstants = cbD->currentBlendConstants;
2501 const quint32 prevStencilRef = cbD->currentStencilRef;
2509 const QVarLengthArray<
char, 128> prevPushConstantData = cbD->pushConstantData;
2511 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
2514 for (
const QByteArray &name : std::as_const(cbD->d->openDebugGroups))
2515 [cbD->d->currentRenderPassEncoder pushDebugGroup: [NSString stringWithUTF8String: name.constData()]];
2517 cbD->pushConstantData = prevPushConstantData;
2523 if (!qFuzzyIsNull(prevViewport.viewport()[2]) || !qFuzzyIsNull(prevViewport.viewport()[3]))
2524 rhiD->setViewport(cbD, prevViewport);
2526 rhiD->setScissor(cbD, prevScissor);
2527 else if (prevHasDefaultScissor)
2529 if (prevHasBlendConstants)
2530 rhiD->setBlendConstants(cbD, prevBlendConstants);
2531 if (prevHasStencilRef)
2532 rhiD->setStencilRef(cbD, prevStencilRef);
2535 cbD
->d->currentPassRpDesc.colorAttachments[i].loadAction = oldColorLoad[i];
2539 cbD
->d->currentPassRpDesc.depthAttachment.loadAction = oldDepthLoad;
2540 cbD
->d->currentPassRpDesc.stencilAttachment.loadAction = oldStencilLoad;
2549 if (graphicsPipeline
->d->tess.failed)
2553 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2554 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2558 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2564 id<MTLComputeCommandEncoder> vertTescComputeEncoder
2565 = tempComputeEncoder(
this, cbD, cbD->d->tessellationComputeEncoder);
2566 cbD
->d->tessellationComputeEncoder = vertTescComputeEncoder;
2570 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2571 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2572 if (args.type == TessDrawArgs::U16Indexed)
2573 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2574 else if (args.type == TessDrawArgs::U32Indexed)
2575 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2576 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2577 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2578 [computeEncoder setComputePipelineState: computePipelineState];
2583 cbD
->d->currentComputePassEncoder = computeEncoder;
2585 cbD->d->currentComputePassEncoder = nil;
2587 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2588 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2589 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2591 if (outputBufferBinding >= 0) {
2592 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2593 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2596 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2599 if (indexBufferBinding >= 0)
2600 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2602 for (
int i = 0, ie = cbD
->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2603 const auto &bufferBatch(cbD
->d->currentVertexInputsBuffers.batches[i]);
2604 const auto &offsetBatch(cbD
->d->currentVertexInputOffsets.batches[i]);
2605 [computeEncoder setBuffers: bufferBatch.resources.constData()
2606 offsets: offsetBatch.resources.constData()
2607 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2611 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2612 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2614 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2615 args.draw.vertexCount, args.draw.instanceCount)];
2618 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2619 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2624 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2625 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2626 [computeEncoder setComputePipelineState: computePipelineState];
2628 cbD
->d->currentComputePassEncoder = computeEncoder;
2630 cbD->d->currentComputePassEncoder = nil;
2632 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2633 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2634 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2635 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2636 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2637 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2639 if (outputBufferBinding >= 0) {
2640 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2641 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2644 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2647 if (patchOutputBufferBinding >= 0) {
2648 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2649 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2650 if (!tescPatchOutBuf)
2652 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2655 if (tessFactorBufferBinding >= 0) {
2656 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2657 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2660 if (paramsBufferBinding >= 0) {
2662 quint32 inControlPointCount;
2669 params.patchCount = patchCount;
2670 id<MTLBuffer> paramsBuf = tescParamsBuf
->d->buf[0];
2671 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2672 memcpy(p, ¶ms,
sizeof(params));
2673 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2676 if (vertOutBuf && inputBufferBinding >= 0)
2677 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2679 int sgSize =
int(computePipelineState.threadExecutionWidth);
2680 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2681 while (wgSize > caps.maxThreadGroupSize) {
2683 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2685 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2686 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2694 endTempComputeEncoding(
this, cbD, cbD
->d->tessellationComputeEncoder);
2695 cbD->d->tessellationComputeEncoder = nil;
2704 id<MTLRenderCommandEncoder> renderEncoder = cbD
->d->currentRenderPassEncoder;
2709 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2710 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2711 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2712 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2714 if (outputBufferBinding >= 0 && tescOutBuf)
2715 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2717 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2718 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2720 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2721 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2722 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2725 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2727 patchCount: patchCount
2728 patchIndexBuffer: nil
2729 patchIndexBufferOffset: 0
2739 if (multiViewCount <= 1)
2743 const int viewMaskBufBinding = ebb.value(QShaderPrivate::MslMultiViewMaskBufferBinding, -1);
2744 if (viewMaskBufBinding == -1) {
2745 qWarning(
"No extra buffer for multiview in the vertex shader; was it built with --view-count specified?");
2752 multiViewInfo.viewOffset = 0;
2753 multiViewInfo.viewCount = quint32(multiViewCount);
2757 id<MTLBuffer> mtlbuf = buf
->d->buf[0];
2758 char *p =
reinterpret_cast<
char *>([mtlbuf contents]);
2759 memcpy(p, &multiViewInfo,
sizeof(multiViewInfo));
2760 [cbD->d->currentRenderPassEncoder setVertexBuffer: mtlbuf offset: 0 atIndex: viewMaskBufBinding];
2764 *instanceCount *= multiViewCount;
2769 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
2778 a.draw.vertexCount = vertexCount;
2779 a.draw.instanceCount = instanceCount;
2780 a.draw.firstVertex = firstVertex;
2781 a.draw.firstInstance = firstInstance;
2786 adjustForMultiViewDraw(&instanceCount, cb);
2788 if (caps.baseVertexAndInstance) {
2789 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2790 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
2792 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2793 vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount];
2798 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2806 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2807 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2810 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2815 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2816 a.drawIndexed.indexCount = indexCount;
2817 a.drawIndexed.instanceCount = instanceCount;
2818 a.drawIndexed.firstIndex = firstIndex;
2819 a.drawIndexed.vertexOffset = vertexOffset;
2820 a.drawIndexed.firstInstance = firstInstance;
2821 a.drawIndexed.indexBuffer = mtlibuf;
2826 adjustForMultiViewDraw(&instanceCount, cb);
2828 if (caps.baseVertexAndInstance) {
2829 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2830 indexCount: indexCount
2831 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2832 indexBuffer: mtlibuf
2833 indexBufferOffset: indexOffset
2834 instanceCount: instanceCount
2835 baseVertex: vertexOffset
2836 baseInstance: firstInstance];
2838 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2839 indexCount: indexCount
2840 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2841 indexBuffer: mtlibuf
2842 indexBufferOffset: indexOffset
2843 instanceCount: instanceCount];
2850 if (!caps.indirectCommandBuffers)
2851 return "indirect command buffers are not supported on this device";
2855 return "the current graphics pipeline was not created with UsesIndirectDraws";
2858 return "the current graphics pipeline uses tessellation";
2860 return "the shaders of the current graphics pipeline sample textures but have no "
2861 "argument buffer variant, which Metal requires for a pipeline that supports "
2862 "indirect command buffers; rebuild them with qsb --msl-argument-buffers "
2863 "(or use MSLARGUMENTBUFFERS with qt_add_shaders)";
2873 if (!
d->icbEncodePipeline) {
2875 NSString *src = [NSString stringWithUTF8String:s_icbEncodeMsl];
2876 MTLCompileOptions *opts = [MTLCompileOptions
new];
2877 opts.languageVersion = MTLLanguageVersion2_1;
2878 id<MTLLibrary> lib = [d->dev newLibraryWithSource:src options:opts error:&err];
2881 qWarning(
"Failed to compile ICB encode kernel: %s",
2882 qPrintable(QString::fromNSString(err.localizedDescription)));
2886 d->icbEncodeFunction = [lib newFunctionWithName:@
"encode_icb"];
2887 d->icbEncodeFunctionU32 = [lib newFunctionWithName:@
"encode_icb_indexed_u32"];
2888 d->icbEncodeFunctionU16 = [lib newFunctionWithName:@
"encode_icb_indexed_u16"];
2890 if (!
d->icbEncodeFunction || !
d->icbEncodeFunctionU32 || !
d->icbEncodeFunctionU16) {
2891 qWarning(
"ICB encode kernel functions not found");
2895 NSError *errU32 = nil;
2896 NSError *errU16 = nil;
2897 d->icbEncodePipeline = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunction error:&err];
2898 d->icbEncodePipelineU32 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU32 error:&errU32];
2899 d->icbEncodePipelineU16 = [d->dev newComputePipelineStateWithFunction:d->icbEncodeFunctionU16 error:&errU16];
2900 if (!
d->icbEncodePipeline || !
d->icbEncodePipelineU32 || !
d->icbEncodePipelineU16) {
2901 NSError *firstErr = !
d->icbEncodePipeline ? err
2902 : (!
d->icbEncodePipelineU32 ? errU32 : errU16);
2903 qWarning(
"Failed to create ICB encode compute pipeline: %s",
2904 qPrintable(QString::fromNSString(firstErr.localizedDescription)));
2910 if (!
d->icbRangeBuffer) {
2911 d->icbRangeBuffer = [d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
2912 options:MTLResourceStorageModePrivate];
2913 static constexpr quint32 noCount = 0xFFFFFFFFu;
2914 d->icbNoCountBuffer = [d->dev newBufferWithBytes:&noCount
2915 length:
sizeof(noCount)
2916 options:MTLResourceStorageModeShared];
2917 if (!
d->icbRangeBuffer || !
d->icbNoCountBuffer) {
2918 qWarning(
"Failed to create ICB helper buffers");
2932 if (!
d->icb ||
d->icbCapacity < maxDrawCount) {
2936 e.lastActiveFrameSlot = currentFrameSlot;
2937 e.stagingIcbBuffer.icb =
d->icb;
2938 e.stagingIcbBuffer.argBuffer =
d->icbArgumentBuffer;
2939 d->releaseQueue.append(e);
2942 d->icbArgumentBuffer = nil;
2944 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
2945 icbDesc.commandTypes = MTLIndirectCommandTypeDraw | MTLIndirectCommandTypeDrawIndexed;
2946 icbDesc.inheritPipelineState = YES;
2947 icbDesc.inheritBuffers = YES;
2948 icbDesc.maxVertexBufferBindCount = 0;
2949 icbDesc.maxFragmentBufferBindCount = 0;
2950 d->icb = [d->dev newIndirectCommandBufferWithDescriptor:icbDesc
2951 maxCommandCount:maxDrawCount
2952 options:MTLResourceStorageModePrivate];
2955 qWarning(
"Failed to create MTLIndirectCommandBuffer");
2959 d->icbCapacity = maxDrawCount;
2961 id<MTLArgumentEncoder> argEnc = [d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
2962 d->icbArgumentBuffer = [d->dev newBufferWithLength:argEnc.encodedLength
2963 options:MTLResourceStorageModeShared];
2964 [argEnc setArgumentBuffer:d->icbArgumentBuffer offset:0];
2965 [argEnc setIndirectCommandBuffer:d->icb atIndex:0];
2976 id<MTLComputeCommandEncoder> computeEncoder,
2977 id<MTLIndirectCommandBuffer> targetIcb,
2978 id<MTLBuffer> targetArgBuffer,
2979 id<MTLBuffer> targetRangeBuffer,
2981 QRhiCommandBuffer::IndexFormat indexFormat,
2982 MTLPrimitiveType primitiveType,
2983 id<MTLBuffer> indirectBufMtl, quint32 indirectBufferOffset,
2984 id<MTLBuffer> indexBufMtl, quint32 indexBufferOffset,
2985 id<MTLBuffer> countBufMtl, quint32 countBufferOffset,
2986 quint32 maxDrawCount, quint32 stride)
2988 id<MTLComputePipelineState> computePipeline = d->icbEncodePipeline;
2990 computePipeline = indexFormat == QRhiCommandBuffer::IndexUInt16
2991 ? d->icbEncodePipelineU16 : d->icbEncodePipelineU32;
2993 uint32_t maxDrawCountVal = maxDrawCount;
2994 uint32_t metalPrimType = uint32_t(primitiveType);
2995 uint32_t strideVal = stride;
2997 [computeEncoder setComputePipelineState:computePipeline];
2998 [computeEncoder setBuffer:indirectBufMtl offset:indirectBufferOffset atIndex:0];
2999 [computeEncoder setBuffer:targetArgBuffer offset:0 atIndex:1];
3000 [computeEncoder setBytes:&maxDrawCountVal length:
sizeof(uint32_t) atIndex:2];
3002 [computeEncoder setBuffer:indexBufMtl offset:indexBufferOffset atIndex:3];
3003 [computeEncoder setBytes:&metalPrimType length:
sizeof(uint32_t) atIndex:4];
3004 [computeEncoder setBytes:&strideVal length:
sizeof(uint32_t) atIndex:5];
3005 [computeEncoder setBuffer:countBufMtl ? countBufMtl : d->icbNoCountBuffer
3006 offset:countBufMtl ? countBufferOffset : 0
3008 [computeEncoder setBuffer:targetRangeBuffer offset:0 atIndex:7];
3009 [computeEncoder useResource:targetIcb usage:MTLResourceUsageWrite];
3010 [computeEncoder useResource:indirectBufMtl usage:MTLResourceUsageRead];
3012 [computeEncoder useResource:indexBufMtl usage:MTLResourceUsageRead];
3014 NSUInteger tw = computePipeline.threadExecutionWidth;
3015 [computeEncoder dispatchThreads:MTLSizeMake(maxDrawCount, 1, 1)
3016 threadsPerThreadgroup:MTLSizeMake(tw, 1, 1)];
3026 QMetalBuffer *indirectBufD, quint32 indirectBufferOffset,
3028 quint32 maxDrawCount, quint32 stride)
3034 if (indexed && !indexBufD)
3037 if (!prepareIcb(maxDrawCount))
3041 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3042 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3044 id<MTLBuffer> countBufMtl = nil;
3047 countBufD->lastActiveFrameSlot = currentFrameSlot;
3048 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
3055 const auto savedVertexBuffers = cbD
->d->currentVertexInputsBuffers;
3056 const auto savedVertexOffsets = cbD
->d->currentVertexInputOffsets;
3057 const quint32 savedIndexOffset = cbD->currentIndexOffset;
3058 const QRhiCommandBuffer::IndexFormat savedIndexFormat = cbD->currentIndexFormat;
3059 id<MTLBuffer> indexBufMtl = indexed
3060 ? indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0] : nil;
3065 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
3066 encodeIcbWithCompute(
d, computeEncoder,
d->icb,
d->icbArgumentBuffer,
d->icbRangeBuffer,
3067 indexed, savedIndexFormat, savedPipeline
->d->primitiveType,
3068 indirectBufMtl, indirectBufferOffset,
3069 indexBufMtl, savedIndexOffset,
3070 countBufMtl, countBufferOffset,
3071 maxDrawCount, stride);
3074 endTempComputeEncoding(
this, cbD, computeEncoder);
3083 if (savedFirstVertexBinding >= 0) {
3085 cbD
->d->currentVertexInputsBuffers = savedVertexBuffers;
3086 cbD
->d->currentVertexInputOffsets = savedVertexOffsets;
3087 for (
int i = 0, ie = savedVertexBuffers.batches.count(); i != ie; ++i) {
3088 const auto &bufferBatch(savedVertexBuffers.batches[i]);
3089 const auto &offsetBatch(savedVertexOffsets.batches[i]);
3090 [cbD->d->currentRenderPassEncoder setVertexBuffers:
3091 bufferBatch.resources.constData()
3092 offsets: offsetBatch.resources.constData()
3093 withRange: NSMakeRange(uint(savedFirstVertexBinding) + bufferBatch.startBinding,
3094 NSUInteger(bufferBatch.resources.count()))];
3099 cbD->currentIndexOffset = savedIndexOffset;
3100 cbD->currentIndexFormat = savedIndexFormat;
3104 [cbD->d->currentRenderPassEncoder useResource:indirectBufMtl
3105 usage:MTLResourceUsageRead
3106 stages:MTLRenderStageVertex | MTLRenderStageFragment];
3108 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
3109 usage:MTLResourceUsageRead
3110 stages:MTLRenderStageVertex | MTLRenderStageFragment];
3112 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:d->icb
3113 indirectBuffer:d->icbRangeBuffer
3114 indirectBufferOffset:0];
3127 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3134 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3135 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3137 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3138 && icbDraw(cbD,
false, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3144 NSUInteger offset = indirectBufferOffset;
3145 for (quint32 i = 0; i < drawCount; ++i) {
3146 [cbD->d->currentRenderPassEncoder drawPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3147 indirectBuffer: indirectBufMtl
3148 indirectBufferOffset: offset];
3154 quint32 indirectBufferOffset, quint32 drawCount, quint32 stride)
3163 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
3167 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
3168 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
3170 if (drawCount > ICB_DRAW_COUNT_THRESHOLD && !icbUnavailableReason(cbD)
3171 && icbDraw(cbD,
true, indirectBufD, indirectBufferOffset,
nullptr, 0, drawCount, stride))
3177 NSUInteger offset = indirectBufferOffset;
3178 for (quint32 i = 0; i < drawCount; ++i) {
3179 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
3180 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
3181 indexBuffer: indexBufMtl
3182 indexBufferOffset: cbD->currentIndexOffset
3183 indirectBuffer: indirectBufMtl
3184 indirectBufferOffset: offset];
3194 NSString *str = [NSString stringWithUTF8String: name.constData()];
3197 case QMetalCommandBuffer::RenderPass:
3198 [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
3199 cbD
->d->openDebugGroups.append(name);
3201 case QMetalCommandBuffer::ComputePass:
3202 [cbD->d->currentComputePassEncoder pushDebugGroup: str];
3205 [cbD->d->cb pushDebugGroup: str];
3217 case QMetalCommandBuffer::RenderPass:
3218 [cbD->d->currentRenderPassEncoder popDebugGroup];
3219 if (!cbD
->d->openDebugGroups.isEmpty())
3220 cbD
->d->openDebugGroups.removeLast();
3222 case QMetalCommandBuffer::ComputePass:
3223 [cbD->d->currentComputePassEncoder popDebugGroup];
3226 [cbD->d->cb popDebugGroup];
3237 NSString *str = [NSString stringWithUTF8String: msg.constData()];
3239 case QMetalCommandBuffer::RenderPass:
3240 [cbD->d->currentRenderPassEncoder insertDebugSignpost: str];
3242 case QMetalCommandBuffer::ComputePass:
3243 [cbD->d->currentComputePassEncoder insertDebugSignpost: str];
3252 return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
3278 currentFrameSlot = swapChainD->currentFrameSlot;
3283 dispatch_semaphore_wait(swapChainD->d->sem[currentFrameSlot], DISPATCH_TIME_FOREVER);
3291 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3292 if (sc != swapChainD)
3293 sc->waitUntilCompleted(currentFrameSlot);
3296 [d->captureScope beginScope];
3298 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3302 colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
3309 swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
3310 swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
3315 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
3317 d->argBufPool[currentFrameSlot].offset = 0;
3319 d->globalFrameId += 1;
3322 swapChainD->cbWrapper.resetState(swapChainD->d->lastGpuTime[currentFrameSlot]);
3323 swapChainD->d->lastGpuTime[currentFrameSlot] = 0;
3326 return QRhi::FrameOpSuccess;
3335 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
3337 __block
int thisFrameSlot = currentFrameSlot;
3338 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
3339 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3340 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
3347 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
3348 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
3349 [drawableTexture release];
3353 if (flags.testFlag(QRhi::SkipPresent)) {
3355 [commandBuffer commit];
3357 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
3359 if (swapChainD
->d->layer.presentsWithTransaction) {
3360 [commandBuffer commit];
3362 auto *metalLayer = swapChainD
->d->layer;
3363 auto presentWithTransaction = ^{
3364 [commandBuffer waitUntilScheduled];
3371 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
3372 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
3373 if (textureSize == surfaceSize) {
3376 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
3377 << textureSize <<
"not matching surface size" << surfaceSize;
3381 if (NSThread.currentThread == NSThread.mainThread) {
3382 presentWithTransaction();
3384 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3385 Q_ASSERT(qtMetalLayer);
3387 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
3391 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
3392 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
3398 if (qtMetalLayer.displayLock.tryLockForRead()) {
3400 qtMetalLayer.displayLock.unlock();
3402 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
3403 <<
"due to" << qtMetalLayer <<
"needing display";
3409 [commandBuffer commit];
3413 [commandBuffer commit];
3420 [swapChainD->d->curDrawable release];
3421 swapChainD->d->curDrawable = nil;
3423 [d->captureScope endScope];
3427 return QRhi::FrameOpSuccess;
3434 currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
3436 for (QMetalSwapChain *sc : std::as_const(swapchains))
3437 sc->waitUntilCompleted(currentFrameSlot);
3439 d->ofr.active =
true;
3440 *cb = &
d->ofr.cbWrapper;
3441 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3443 d->argBufPool[currentFrameSlot].offset = 0;
3445 d->globalFrameId += 1;
3448 d->ofr.cbWrapper.resetState(
d->ofr.lastGpuTime);
3449 d->ofr.lastGpuTime = 0;
3452 return QRhi::FrameOpSuccess;
3458 Q_ASSERT(
d->ofr.active);
3459 d->ofr.active =
false;
3461 id<MTLCommandBuffer> cb =
d->ofr.cbWrapper.d->cb;
3465 [cb waitUntilCompleted];
3467 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3471 return QRhi::FrameOpSuccess;
3476 id<MTLCommandBuffer> cb = nil;
3479 if (
d->ofr.active) {
3482 cb =
d->ofr.cbWrapper.d->cb;
3487 cb = swapChainD->cbWrapper.d->cb;
3491 for (QMetalSwapChain *sc : std::as_const(swapchains)) {
3492 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
3493 if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
3498 sc->waitUntilCompleted(i);
3504 [cb waitUntilCompleted];
3508 if (
d->ofr.active) {
3509 d->ofr.lastGpuTime += cb.GPUEndTime - cb.GPUStartTime;
3510 d->ofr.cbWrapper.d->cb =
d->newCommandBuffer();
3512 swapChainD->d->lastGpuTime[currentFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
3513 swapChainD->cbWrapper.d->cb =
d->newCommandBuffer();
3521 return QRhi::FrameOpSuccess;
3525 const QColor &colorClearValue,
3526 const QRhiDepthStencilClearValue &depthStencilClearValue,
3528 QRhiShadingRateMap *shadingRateMap)
3530 MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
3531 MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
3532 colorClearValue.alphaF());
3534 for (uint i = 0; i < uint(colorAttCount); ++i) {
3535 rp.colorAttachments[i].loadAction = MTLLoadActionClear;
3536 rp.colorAttachments[i].storeAction = MTLStoreActionStore;
3537 rp.colorAttachments[i].clearColor = c;
3540 if (hasDepthStencil) {
3541 rp.depthAttachment.loadAction = MTLLoadActionClear;
3542 rp.depthAttachment.storeAction = MTLStoreActionDontCare;
3543 rp.stencilAttachment.loadAction = MTLLoadActionClear;
3544 rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
3545 rp.depthAttachment.clearDepth =
double(depthStencilClearValue.depthClearValue());
3546 rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
3550 rp.rasterizationRateMap =
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
3558 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
3559 subresDesc.data().size() : subresDesc.image().sizeInBytes();
3560 if (imageSizeBytes > 0)
3561 size += aligned<qsizetype>(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3566 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
3569 const QPoint dp = subresDesc.destinationTopLeft();
3570 const QByteArray rawData = subresDesc.data();
3571 QImage img = subresDesc.image();
3572 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3573 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
3575 if (!img.isNull()) {
3576 const qsizetype fullImageSizeBytes = img.sizeInBytes();
3577 QSize size = img.size();
3578 int bpl = img.bytesPerLine();
3580 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
3581 const int sx = subresDesc.sourceTopLeft().x();
3582 const int sy = subresDesc.sourceTopLeft().y();
3583 if (!subresDesc.sourceSize().isEmpty())
3584 size = subresDesc.sourceSize();
3585 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3586 if (size.width() == img.width()) {
3587 const int bpc = qMax(1, img.depth() / 8);
3588 Q_ASSERT(size.height() * img.bytesPerLine() <= fullImageSizeBytes);
3589 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
3590 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
3591 size.height() * img.bytesPerLine());
3593 img = img.copy(sx, sy, size.width(), size.height());
3594 bpl = img.bytesPerLine();
3595 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
3596 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
3599 size = clampedSubResourceUploadSize(size, dp, level, texD->m_pixelSize);
3600 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
3603 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3604 sourceOffset: NSUInteger(*curOfs)
3605 sourceBytesPerRow: NSUInteger(bpl)
3606 sourceBytesPerImage: 0
3607 sourceSize: MTLSizeMake(NSUInteger(size.width()), NSUInteger(size.height()), 1)
3608 toTexture: texD->d->tex
3609 destinationSlice: NSUInteger(is3D ? 0 : layer)
3610 destinationLevel: NSUInteger(level)
3611 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3612 options: MTLBlitOptionNone];
3614 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
3615 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
3616 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3617 const int subresw = subresSize.width();
3618 const int subresh = subresSize.height();
3620 if (subresDesc.sourceSize().isEmpty()) {
3624 w = subresDesc.sourceSize().width();
3625 h = subresDesc.sourceSize().height();
3630 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
3632 const int dx = aligned(dp.x(), blockDim.width());
3633 const int dy = aligned(dp.y(), blockDim.height());
3634 if (dx + w != subresw)
3635 w = aligned(w, blockDim.width());
3636 if (dy + h != subresh)
3637 h = aligned(h, blockDim.height());
3639 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3641 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3642 sourceOffset: NSUInteger(*curOfs)
3643 sourceBytesPerRow: bpl
3644 sourceBytesPerImage: 0
3645 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3646 toTexture: texD->d->tex
3647 destinationSlice: NSUInteger(is3D ? 0 : layer)
3648 destinationLevel: NSUInteger(level)
3649 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
3650 options: MTLBlitOptionNone];
3652 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3653 }
else if (!rawData.isEmpty()) {
3654 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
3655 const int subresw = subresSize.width();
3656 const int subresh = subresSize.height();
3658 if (subresDesc.sourceSize().isEmpty()) {
3662 w = subresDesc.sourceSize().width();
3663 h = subresDesc.sourceSize().height();
3666 QSize size = clampedSubResourceUploadSize(QSize(w, h), dp, level, texD->m_pixelSize);
3667 quint32 bytesPerPixel = 0;
3668 textureFormatInfo(texD->m_format, size,
nullptr,
nullptr, &bytesPerPixel);
3669 size = clampedSubResourceUploadSizeForSourceData(size, subresDesc.dataStride(),
3670 bytesPerPixel, rawData.size());
3675 if (subresDesc.dataStride())
3676 bpl = subresDesc.dataStride();
3678 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
3680 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
3682 if (!size.isEmpty()) {
3683 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
3684 sourceOffset: NSUInteger(*curOfs)
3685 sourceBytesPerRow: bpl
3686 sourceBytesPerImage: 0
3687 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
3688 toTexture: texD->d->tex
3689 destinationSlice: NSUInteger(is3D ? 0 : layer)
3690 destinationLevel: NSUInteger(level)
3691 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
3692 options: MTLBlitOptionNone];
3695 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
3697 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
3706 id<MTLBlitCommandEncoder> blitEnc = nil;
3707 auto ensureBlit = [&blitEnc, cbD,
this]() {
3709 blitEnc = [cbD->d->cb blitCommandEncoder];
3711 [blitEnc pushDebugGroup: @
"Resource updates"];
3719 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
3721 if (u.offset == 0 && u
.data.size() == bufD->m_size)
3722 bufD
->d->pendingUpdates[i].clear();
3723 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3727 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
3728 Q_ASSERT(u.offset + u
.data.size() <= bufD->m_size);
3730 const quint32 size = quint32(u
.data.size());
3732 quint32 stagingOffset = 0;
3733 id<MTLBuffer> stagingBuf =
d->allocBufferStaging(size, currentFrameSlot, &stagingOffset);
3735 char *p =
reinterpret_cast<
char *>([stagingBuf contents]);
3738 [blitEnc copyFromBuffer: stagingBuf
3739 sourceOffset: stagingOffset
3740 toBuffer: bufD->d->buf[0]
3741 destinationOffset: u.offset
3744 qWarning(
"Failed to allocate staging buffer of size %u for buffer upload", size);
3747 bufD->lastActiveFrameSlot = currentFrameSlot;
3750 bufD
->d->pendingUpdates[i].append({ u.offset, u
.data });
3755 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
3756 if (bufD->m_type == QRhiBuffer::Dynamic) {
3757 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
3759 u.result->data.resize(u.readSize);
3760 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
3762 if (u.result->completed)
3763 u.result->completed();
3773 readback.activeFrameSlot = currentFrameSlot;
3774 readback.readSize = u.readSize;
3775 readback.result = u.result;
3776 readback.buf = [d->dev newBufferWithLength: u.readSize
3777 options: MTLResourceStorageModeShared];
3780 [blitEnc copyFromBuffer: bufD->d->buf[idx]
3781 sourceOffset: u.offset
3782 toBuffer: readback.buf
3783 destinationOffset: 0
3786 d->activeBufferReadbacks.append(readback);
3787 bufD->lastActiveFrameSlot = currentFrameSlot;
3792 Q_ASSERT(dstD->m_type != QRhiBuffer::Dynamic && srcD->m_type != QRhiBuffer::Dynamic);
3795 const int srcIdx = srcD->d->slotted ? currentFrameSlot : 0;
3806 for (
int i = 0; i != dstSlotCount; ++i)
3810 for (
int i = 0; i != dstSlotCount; ++i) {
3811 [blitEnc copyFromBuffer: srcD->d->buf[srcIdx]
3812 sourceOffset: u.srcOffset
3813 toBuffer: dstD->d->buf[i]
3814 destinationOffset: u.offset
3818 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3821 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic && !bufD->d->slotted);
3824 [blitEnc fillBuffer: bufD->d->buf[0]
3825 range: NSMakeRange(u.offset, u.readSize)
3826 value: u.fillValue];
3827 bufD->lastActiveFrameSlot = currentFrameSlot;
3835 qsizetype stagingSize = 0;
3836 for (
const auto &subres : u.subresDesc)
3837 stagingSize += subresUploadByteSize(subres.desc);
3840 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
3841 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
3842 options: MTLResourceStorageModeShared];
3844 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
3845 qsizetype curOfs = 0;
3846 for (
const auto &subres : u.subresDesc)
3847 enqueueSubresUpload(utexD, mp, blitEnc, subres.layer, subres.level, subres.desc, &curOfs);
3849 utexD->lastActiveFrameSlot = currentFrameSlot;
3853 e.lastActiveFrameSlot = currentFrameSlot;
3854 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
3855 utexD->d->stagingBuf[currentFrameSlot] = nil;
3856 d->releaseQueue.append(e);
3861 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3862 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3863 const QPoint dp = u.desc.destinationTopLeft();
3864 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
3865 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
3866 const QPoint sp = u.desc.sourceTopLeft();
3869 [blitEnc copyFromTexture: srcD->d->tex
3870 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
3871 sourceLevel: NSUInteger(u.desc.sourceLevel())
3872 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
3873 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
3874 toTexture: dstD->d->tex
3875 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
3876 destinationLevel: NSUInteger(u.desc.destinationLevel())
3877 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
3879 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
3882 readback.activeFrameSlot = currentFrameSlot;
3883 readback.desc = u.rb;
3884 readback.result = u.result;
3893 qWarning(
"Multisample texture cannot be read back");
3896 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
3897 if (u.rb.rect().isValid())
3900 rect = QRect({0, 0}, q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize));
3901 readback.format = texD->m_format;
3903 texD->lastActiveFrameSlot = currentFrameSlot;
3907 if (u.rb.rect().isValid())
3910 rect = QRect({0, 0}, swapChainD->pixelSize);
3911 readback.format = swapChainD
->d->rhiColorFormat;
3915 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
3917 readback.pixelSize = rect.size();
3920 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
3921 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
3924 [blitEnc copyFromTexture: src
3925 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
3926 sourceLevel: NSUInteger(u.rb.level())
3927 sourceOrigin: MTLOriginMake(NSUInteger(rect.x()), NSUInteger(rect.y()), NSUInteger(is3D ? u.rb.layer() : 0))
3928 sourceSize: MTLSizeMake(NSUInteger(rect.width()), NSUInteger(rect.height()), 1)
3929 toBuffer: readback.buf
3930 destinationOffset: 0
3931 destinationBytesPerRow: bpl
3932 destinationBytesPerImage: 0
3933 options: MTLBlitOptionNone];
3935 d->activeTextureReadbacks.append(readback);
3939 [blitEnc generateMipmapsForTexture: utexD->d->tex];
3940 utexD->lastActiveFrameSlot = currentFrameSlot;
3946 [blitEnc popDebugGroup];
3947 [blitEnc endEncoding];
3956 if (bufD
->d->pendingUpdates[slot].isEmpty())
3961 void *p = [bufD->d->buf[slot] contents];
3962 quint32 changeBegin = UINT32_MAX;
3963 quint32 changeEnd = 0;
3964 for (
const QMetalBufferData::BufferUpdate &u : std::as_const(bufD->d->pendingUpdates[slot])) {
3965 memcpy(
static_cast<
char *>(p) + u.offset, u.data.constData(), size_t(u.data.size()));
3966 if (u.offset < changeBegin)
3967 changeBegin = u.offset;
3968 if (u.offset + u.data.size() > changeEnd)
3969 changeEnd = u.offset + u.data.size();
3972 if (changeBegin < UINT32_MAX && changeBegin < changeEnd && bufD->d->managed)
3973 [bufD->d->buf[slot] didModifyRange: NSMakeRange(NSUInteger(changeBegin), NSUInteger(changeEnd - changeBegin))];
3976 bufD
->d->pendingUpdates[slot].clear();
3986 Q_ASSERT(
QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
3992 QRhiRenderTarget *rt,
3993 const QColor &colorClearValue,
3994 const QRhiDepthStencilClearValue &depthStencilClearValue,
3995 QRhiResourceUpdateBatch *resourceUpdates,
4001 if (resourceUpdates)
4005 switch (rt->resourceType()) {
4006 case QRhiResource::SwapChainRenderTarget:
4010 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
4013 depthStencilClearValue,
4021 if (!swapChainD
->d->curDrawable) {
4022 QMacAutoReleasePool pool;
4023 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
4025 if (!swapChainD
->d->curDrawable) {
4026 qWarning(
"No drawable");
4029 id<MTLTexture> scTex = swapChainD
->d->curDrawable.texture;
4034 color0.resolveTex = scTex;
4040 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
4043 case QRhiResource::TextureRenderTarget:
4047 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
4051 depthStencilClearValue,
4053 rtTex->m_desc.shadingRateMap());
4054 if (rtD->fb.preserveColor) {
4055 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
4056 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
4059 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
4060 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
4062 int colorAttCount = 0;
4063 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
4067 if (it->texture()) {
4068 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
4069 if (it->multiViewCount() >= 2)
4070 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
4071 }
else if (it->renderBuffer()) {
4072 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
4074 if (it->resolveTexture())
4075 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
4077 if (rtTex->m_desc.depthStencilBuffer())
4078 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
4079 if (rtTex->m_desc.depthTexture()) {
4081 depthTexture->lastActiveFrameSlot = currentFrameSlot;
4082 if (depthTexture->arraySize() >= 2) {
4083 const int depthLayer = rtTex->m_desc.depthLayer();
4084 if (depthLayer >= 0) {
4085 cbD
->d->currentPassRpDesc.depthAttachment.slice = NSUInteger(depthLayer);
4086 cbD
->d->currentPassRpDesc.stencilAttachment.slice = NSUInteger(depthLayer);
4087 if (colorAttCount == 0)
4088 cbD
->d->currentPassRpDesc.renderTargetArrayLength = 1;
4089 }
else if (colorAttCount == 0) {
4090 cbD
->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
4094 if (rtTex->m_desc.depthResolveTexture())
4095 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
4096 if (rtTex->m_desc.shadingRateMap())
4097 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
4105 cbD
->d->deferredColorStoreActions.clear();
4106 cbD->d->deferredDepthStoreAction = MTLStoreActionUnknown;
4107 cbD->d->deferredStencilStoreAction = MTLStoreActionUnknown;
4109 cbD
->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
4110 cbD
->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
4111 cbD
->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
4112 cbD
->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
4113 if (rtD->fb.colorAtt[i].resolveTex) {
4114 const MTLStoreAction storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
4115 : MTLStoreActionMultisampleResolve;
4119 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionUnknown;
4120 cbD
->d->deferredColorStoreActions.append({ i, storeAction });
4121 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
4122 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
4123 cbD
->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
4128 Q_ASSERT(rtD->fb.dsTex);
4129 cbD
->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
4130 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
4131 if (rtD->fb.depthNeedsStore) {
4132 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
4133 }
else if (canStoreAttachment(rtD->fb.dsTex)) {
4137 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionUnknown;
4138 cbD->d->deferredDepthStoreAction = MTLStoreActionDontCare;
4139 if (rtD->fb.hasStencil) {
4140 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = MTLStoreActionUnknown;
4141 cbD->d->deferredStencilStoreAction = MTLStoreActionDontCare;
4144 if (rtD->fb.dsResolveTex) {
4145 const MTLStoreAction dsStoreAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
4146 : MTLStoreActionMultisampleResolve;
4152 const bool deferrable = canStoreAttachment(rtD->fb.dsTex);
4153 cbD->d->currentPassRpDesc.depthAttachment.storeAction = deferrable ? MTLStoreActionUnknown
4156 cbD
->d->deferredDepthStoreAction = dsStoreAction;
4157 cbD
->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
4158 if (rtD->fb.hasStencil) {
4159 cbD
->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
4160 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = deferrable ? MTLStoreActionUnknown
4163 cbD
->d->deferredStencilStoreAction = dsStoreAction;
4168 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
4173 cbD->currentTarget = rt;
4182 [cbD->d->currentRenderPassEncoder endEncoding];
4185 cbD->currentTarget =
nullptr;
4187 if (resourceUpdates)
4192 QRhiResourceUpdateBatch *resourceUpdates,
4198 if (resourceUpdates)
4201 cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
4211 [cbD->d->currentComputePassEncoder endEncoding];
4214 if (resourceUpdates)
4229 [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
4232 psD->lastActiveFrameSlot = currentFrameSlot;
4241 [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(NSUInteger(x), NSUInteger(y), NSUInteger(z))
4242 threadsPerThreadgroup: psD->d->localSize];
4246 quint32 indirectBufferOffset)
4254 indirectBufD->lastActiveFrameSlot = currentFrameSlot;
4255 id<MTLBuffer> indirectBufMtl = indirectBufD->d->buf[indirectBufD->d->slotted ? currentFrameSlot : 0];
4262 [cbD->d->currentComputePassEncoder
4263 dispatchThreadgroupsWithIndirectBuffer: indirectBufMtl
4264 indirectBufferOffset: indirectBufferOffset
4265 threadsPerThreadgroup: psD->d->localSize];
4269 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4270 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4271 quint32 maxDrawCount, quint32 stride)
4279 qWarning(
"drawIndirectCount is not available because %s; skipping", reason);
4283 icbDraw(cbD,
false,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4284 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4288 QRhiBuffer *indirectBuffer, quint32 indirectBufferOffset,
4289 QRhiBuffer *countBuffer, quint32 countBufferOffset,
4290 quint32 maxDrawCount, quint32 stride)
4298 qWarning(
"drawIndexedIndirectCount is not available because %s; skipping", reason);
4303 qWarning(
"drawIndexedIndirectCount called without an index buffer bound; skipping");
4307 icbDraw(cbD,
true,
QRHI_RES(QMetalBuffer, indirectBuffer), indirectBufferOffset,
4308 QRHI_RES(QMetalBuffer, countBuffer), countBufferOffset, maxDrawCount, stride);
4352 e.stagingIcbBuffer.icb = slot.icb;
4353 e.stagingIcbBuffer.argBuffer = slot.argBuffer;
4354 rhiD
->d->releaseQueue.append(e);
4356 if (slot.rangeBuffer) {
4360 e.stagingBuffer.buffer = slot.rangeBuffer;
4361 rhiD
->d->releaseQueue.append(e);
4376 if (icbD->d->fill == fill)
4377 return icbD->d->frameSlots[0].icb != nil;
4385 if (!rhiD->caps.indirectCommandBuffers)
4392 MTLIndirectCommandBufferDescriptor *icbDesc = [MTLIndirectCommandBufferDescriptor
new];
4393 icbDesc.commandTypes = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws
4394 ? MTLIndirectCommandTypeDrawIndexed : MTLIndirectCommandTypeDraw;
4396 icbDesc.inheritPipelineState = YES;
4397 icbDesc.inheritBuffers = YES;
4398 icbDesc.maxVertexBufferBindCount = 0;
4399 icbDesc.maxFragmentBufferBindCount = 0;
4404 slot.icb = [rhiD->d->dev newIndirectCommandBufferWithDescriptor:icbDesc
4405 maxCommandCount:icbD->maxCommandCount()
4406 options:gpu ? MTLResourceStorageModePrivate
4407 : MTLResourceStorageModeShared];
4409 qWarning(
"Failed to create MTLIndirectCommandBuffer");
4414 slot.rangeBuffer = [rhiD->d->dev newBufferWithLength:
sizeof(MTLIndirectCommandBufferExecutionRange)
4415 options:MTLResourceStorageModePrivate];
4416 id<MTLArgumentEncoder> argEnc = [rhiD->d->icbEncodeFunction newArgumentEncoderWithBufferIndex:1];
4417 slot.argBuffer = [rhiD->d->dev newBufferWithLength:argEnc.encodedLength
4418 options:MTLResourceStorageModeShared];
4419 if (slot.rangeBuffer && slot.argBuffer) {
4420 [argEnc setArgumentBuffer:slot.argBuffer offset:0];
4421 [argEnc setIndirectCommandBuffer:slot.icb atIndex:0];
4423 qWarning(
"Failed to create MTLIndirectCommandBuffer helper buffers");
4435 [icbD->d->frameSlots[i].icb release];
4436 [icbD->d->frameSlots[i].rangeBuffer release];
4437 [icbD->d->frameSlots[i].argBuffer release];
4438 icbD
->d->frameSlots[i] = {};
4449 quint32 maxCommandCount)
4475 m_gpuBuiltCommandCount = 0;
4480 rhiD->unregisterResource(
this);
4491 if (!m_maxCommandCount) {
4492 qWarning(
"QRhiIndirectCommandBuffer: maxCommandCount is 0");
4499 rhiD->registerResource(
this);
4503QRhiIndirectCommandBuffer *
QRhiMetal::createIndirectCommandBuffer(QRhiIndirectCommandBuffer::Type type,
4504 quint32 maxCommandCount)
4506 return new QMetalIndirectCommandBuffer(
this, type, maxCommandCount);
4510 QRhiIndirectCommandBuffer *icb)
4522 MTLPrimitiveType primitiveType,
4523 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4524 QRhiCommandBuffer::IndexFormat indexFormat)
4526 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4529 && slot.generation == icbD->contentsGeneration()
4530 && slot.primitiveType == primitiveType
4531 && (!indexed || (slot.indexBuf == indexBufMtl
4532 && slot.indexOffset == indexOffset
4533 && slot.indexFormat == indexFormat));
4539 MTLPrimitiveType primitiveType,
4540 id<MTLBuffer> indexBufMtl, quint32 indexOffset,
4541 QRhiCommandBuffer::IndexFormat indexFormat)
4543 const bool indexed = icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4545 if (qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset, indexFormat))
4548 const quint32 count = icbD->recordedCommandCount();
4550 const MTLIndexType indexType = indexFormat == QRhiCommandBuffer::IndexUInt16
4551 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4552 const quint32 indexSize = indexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4554 for (quint32 i = 0; i < count; ++i) {
4556 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4557 [rc drawIndexedPrimitives:primitiveType
4558 indexCount:c.indexCount
4560 indexBuffer:indexBufMtl
4561 indexBufferOffset:indexOffset + c.firstIndex * indexSize
4562 instanceCount:c.instanceCount
4563 baseVertex:c.vertexOffset
4564 baseInstance:c.firstInstance];
4567 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4568 for (quint32 i = 0; i < count; ++i) {
4569 const QRhiIndirectDrawCommand &c(cmds[i]);
4570 id<MTLIndirectRenderCommand> rc = [slot.icb indirectRenderCommandAtIndex:i];
4571 [rc drawPrimitives:primitiveType
4572 vertexStart:c.firstVertex
4573 vertexCount:c.vertexCount
4574 instanceCount:c.instanceCount
4575 baseInstance:c.firstInstance];
4580 if (count < icbD->maxCommandCount())
4581 [slot.icb resetWithRange:NSMakeRange(count, icbD->maxCommandCount() - count)];
4583 slot.generation = icbD->contentsGeneration();
4584 slot.primitiveType = primitiveType;
4585 slot.indexBuf = indexBufMtl;
4586 slot.indexOffset = indexOffset;
4587 slot.indexFormat = indexFormat;
4593 quint32 firstCommand, quint32 count,
4594 int currentFrameSlot)
4598 if (icbD->type() == QRhiIndirectCommandBuffer::IndexedDraws) {
4602 id<MTLBuffer> indexBufMtl = indexBufD
->d->buf[indexBufD
->d->slotted ? currentFrameSlot : 0];
4603 const MTLIndexType indexType = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16
4604 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
4605 const quint32 indexSize = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4;
4607 for (quint32 i = 0; i < count; ++i) {
4609 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: primitiveType
4610 indexCount: c.indexCount
4611 indexType: indexType
4612 indexBuffer: indexBufMtl
4613 indexBufferOffset: cbD->currentIndexOffset + c.firstIndex * indexSize
4614 instanceCount: c.instanceCount
4615 baseVertex: c.vertexOffset
4616 baseInstance: c.firstInstance];
4619 const QRhiIndirectDrawCommand *cmds = icbD->drawCommands();
4620 for (quint32 i = 0; i < count; ++i) {
4621 const QRhiIndirectDrawCommand &c(cmds[firstCommand + i]);
4622 [cbD->d->currentRenderPassEncoder drawPrimitives: primitiveType
4623 vertexStart: c.firstVertex
4624 vertexCount: c.vertexCount
4625 instanceCount: c.instanceCount
4626 baseInstance: c.firstInstance];
4632 quint32 firstCommand, quint32 commandCount)
4638 icbD->lastActiveFrameSlot = currentFrameSlot;
4640 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4644 qWarning(
"executeIndirect: the indirect command buffer was built for a different "
4645 "topology than the current graphics pipeline uses; skipping");
4650 const quint32 total = icbD->commandCount();
4651 const bool deviceCount = icbD
->d->buildInfo.countBuffer !=
nullptr;
4652 NSRange range = NSMakeRange(0, total);
4658 if (firstCommand != 0 || commandCount < total) {
4659 qWarning(
"executeIndirect: firstCommand and commandCount cannot be honoured "
4660 "together with a device-side count; executing all %u command(s)", total);
4663 if (firstCommand >= total)
4665 const quint32 count = qMin(commandCount, total - firstCommand);
4668 range = NSMakeRange(firstCommand, count);
4671 if (indexed && icbD
->d->buildInfo.indexBuffer) {
4673 id<MTLBuffer> indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4674 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4675 usage:MTLResourceUsageRead
4676 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4679 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4680 indirectBuffer:slot.rangeBuffer
4681 indirectBufferOffset:0];
4683 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb withRange:range];
4689 const QRhiIndirectCommandBufferBuildInfo &info(icbD
->d->buildInfo);
4690 if (info.countBuffer) {
4691 qWarning(
"executeIndirect: a device-side count needs an indirect command buffer, "
4692 "which is not available here; skipping");
4695 const quint32 canonicalStride = indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4696 :
sizeof(QRhiIndirectDrawCommand);
4697 const quint32 stride = info.stride ? info.stride : canonicalStride;
4698 const quint32 total = icbD->commandCount();
4699 if (firstCommand >= total)
4701 const quint32 count = qMin(commandCount, total - firstCommand);
4704 const quint32 offset = info.sourceBufferOffset + firstCommand * stride;
4706 drawIndexedIndirect(cb, info.sourceBuffer, offset, count, stride);
4708 drawIndirect(cb, info.sourceBuffer, offset, count, stride);
4712 const quint32 total = icbD->recordedCommandCount();
4713 if (firstCommand >= total)
4715 const quint32 count = qMin(commandCount, total - firstCommand);
4722 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4727 id<MTLBuffer> indexBufMtl = nil;
4728 quint32 indexOffset = 0;
4733 indexBufD->lastActiveFrameSlot = currentFrameSlot;
4734 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4735 indexOffset = cbD->currentIndexOffset;
4739 if (slot.usedInFrameId == d->globalFrameId
4740 && !qrhimtl_icbSlotMatches(icbD, slot, primitiveType, indexBufMtl, indexOffset,
4741 cbD->currentIndexFormat))
4746 qWarning(
"executeIndirect: the same indirect command buffer is executed more than once "
4747 "in a frame, with different contents, topology or index buffer state; "
4748 "falling back to individual draw calls");
4749 qrhimtl_replayIcbOnCpu(cbD, icbD, firstCommand, count, currentFrameSlot);
4753 qrhimtl_encodeIcbFromCpu(icbD, slot, primitiveType,
4754 indexBufMtl, indexOffset, cbD->currentIndexFormat);
4759 [cbD->d->currentRenderPassEncoder useResource:indexBufMtl
4760 usage:MTLResourceUsageRead
4761 stages:MTLRenderStageVertex | MTLRenderStageFragment];
4763 [cbD->d->currentRenderPassEncoder executeCommandsInBuffer:slot.icb
4764 withRange:NSMakeRange(firstCommand, count)];
4765 slot.usedInFrameId =
d->globalFrameId;
4769 const QRhiIndirectCommandBufferBuildInfo &info)
4775 icbD->lastActiveFrameSlot = currentFrameSlot;
4777 const bool indexed = icb->type() == QRhiIndirectCommandBuffer::IndexedDraws;
4779 if (indexed && !info.indexBuffer) {
4780 qWarning(
"buildIndirect: an IndexedDraws indirect command buffer needs an "
4781 "index buffer in QRhiIndirectCommandBufferBuildInfo; skipping");
4785 quint32 count = info.commandCount ? info.commandCount : icbD->m_maxCommandCount;
4786 if (count > icbD->m_maxCommandCount) {
4787 qWarning(
"QRhiIndirectCommandBuffer: buildIndirect() with commandCount %u exceeds "
4788 "maxCommandCount %u; clamping", count, icbD->m_maxCommandCount);
4789 count = icbD->m_maxCommandCount;
4791 icbD->m_gpuBuilt =
true;
4792 icbD->m_gpuBuiltCommandCount = count;
4797 icbD
->d->buildInfo = info;
4806 srcBufD->lastActiveFrameSlot = currentFrameSlot;
4807 id<MTLBuffer> srcBufMtl = srcBufD->d->buf[srcBufD->d->slotted ? currentFrameSlot : 0];
4809 id<MTLBuffer> indexBufMtl = nil;
4812 indexBufD->lastActiveFrameSlot = currentFrameSlot;
4813 indexBufMtl = indexBufD->d->buf[indexBufD->d->slotted ? currentFrameSlot : 0];
4816 id<MTLBuffer> countBufMtl = nil;
4817 if (info.countBuffer) {
4820 countBufD->lastActiveFrameSlot = currentFrameSlot;
4821 countBufMtl = countBufD->d->buf[countBufD->d->slotted ? currentFrameSlot : 0];
4824 const quint32 stride = info.stride ? info.stride
4825 : (indexed ?
sizeof(QRhiIndexedIndirectDrawCommand)
4826 :
sizeof(QRhiIndirectDrawCommand));
4830 id<MTLComputeCommandEncoder> computeEncoder = [cbD->d->cb computeCommandEncoder];
4831 encodeIcbWithCompute(
d, computeEncoder, slot.icb, slot.argBuffer, slot.rangeBuffer,
4832 indexed, info.indexFormat,
4833 toMetalPrimitiveType(info.topology),
4834 srcBufMtl, info.sourceBufferOffset,
4835 indexBufMtl, info.indexBufferOffset,
4836 countBufMtl, info.countBufferOffset,
4837 icbD->commandCount(), stride);
4838 [computeEncoder endEncoding];
4842 icbD
->d->buildInfo = info;
4843 icbD->d->builtSlot = currentFrameSlot;
4850 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4851 [e.buffer.buffers[i] release];
4856 [e.renderbuffer.texture release];
4861 [e.texture.texture release];
4862 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4863 [e.texture.stagingBuffers[i] release];
4864 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
4865 [e.texture.views[i] release];
4866 [e.texture.samplingView release];
4867 [e.texture.writeView release];
4872 [e.sampler.samplerState release];
4877 qsizetype keepBegin =
d->releaseQueue.size();
4878 for (qsizetype i = keepBegin - 1; i >= 0; --i) {
4880 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
4894 case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
4895 [e.stagingBuffer.buffer release];
4897 case QRhiMetalData::DeferredReleaseEntry::GraphicsPipeline:
4898 [e.graphicsPipeline.pipelineState release];
4899 [e.graphicsPipeline.depthStencilState release];
4900 [e.graphicsPipeline.tessVertexComputeState[0] release];
4901 [e.graphicsPipeline.tessVertexComputeState[1] release];
4902 [e.graphicsPipeline.tessVertexComputeState[2] release];
4903 [e.graphicsPipeline.tessTessControlComputeState release];
4905 case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
4906 [e.computePipeline.pipelineState release];
4908 case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
4909 [e.shadingRateMap.rateMap release];
4911 case QRhiMetalData::DeferredReleaseEntry::StagingIcbBuffer:
4912 [e.stagingIcbBuffer.icb release];
4913 [e.stagingIcbBuffer.argBuffer release];
4918 }
else if (--keepBegin != i) {
4919 d->releaseQueue[keepBegin] =
std::move(
d->releaseQueue[i]);
4923 d->releaseQueue.remove(0, keepBegin);
4928 QVarLengthArray<std::function<
void()>, 4> completedCallbacks;
4930 for (
int i =
d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
4932 if (forced || currentFrameSlot == readback.activeFrameSlot || readback.activeFrameSlot < 0) {
4933 readback.result->format = readback.format;
4934 readback.result->pixelSize = readback.pixelSize;
4935 readback.result->data.resize(
int(readback.bufSize));
4936 void *p = [readback.buf contents];
4937 memcpy(readback.result->data.data(), p, readback.bufSize);
4938 [readback.buf release];
4940 if (readback.result->completed)
4941 completedCallbacks.append(readback.result->completed);
4943 d->activeTextureReadbacks.remove(i);
4947 for (
int i =
d->activeBufferReadbacks.count() - 1; i >= 0; --i) {
4949 if (forced || currentFrameSlot == readback.activeFrameSlot
4950 || readback.activeFrameSlot < 0) {
4951 readback.result->data.resize(readback.readSize);
4952 char *p =
reinterpret_cast<
char *>([readback.buf contents]);
4954 memcpy(readback.result->data.data(), p, size_t(readback.readSize));
4955 [readback.buf release];
4957 if (readback.result->completed)
4958 completedCallbacks.append(readback.result->completed);
4960 d->activeBufferReadbacks.remove(i);
4964 for (
auto f : completedCallbacks)
4972 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
4992 e.buffer.buffers[i] =
d->buf[i];
4994 d->pendingUpdates[i].clear();
4999 rhiD
->d->releaseQueue.append(e);
5000 rhiD->unregisterResource(
this);
5009 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
5010 qWarning(
"StorageBuffer cannot be combined with Dynamic");
5014 const quint32 nonZeroSize = m_size <= 0 ? 256 : m_size;
5015 const quint32 roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256u) : nonZeroSize;
5019 MTLResourceOptions opts = MTLResourceStorageModeShared;
5023 const bool internalHostWritable = (
int(m_usage) & (WorkBufPoolUsage | InternalHostWritable)) != 0;
5025 if (m_type != Dynamic && !internalHostWritable && rhiD->caps.usePrivateStaticBuffers) {
5026 opts = MTLResourceStorageModePrivate;
5031 if (!rhiD->caps.isAppleGPU && m_type != Dynamic) {
5032 opts = MTLResourceStorageModeManaged;
5039 d->slotted = !m_usage.testFlag(QRhiBuffer::StorageBuffer)
5040 && !internalHostWritable;
5045 d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
5046 if (!m_objectName.isEmpty()) {
5048 d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
5050 const QByteArray name = m_objectName +
'/' + QByteArray::number(i);
5051 d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
5059 rhiD->registerResource(
this);
5071 b.objects[i] = &
d->buf[i];
5080 return { { &
d->buf[0] }, 1 };
5090 Q_ASSERT(m_type == Dynamic);
5093 Q_ASSERT(rhiD->inFrame);
5094 const int slot =
d->slotted ? rhiD->currentFrameSlot : 0;
5095 void *p = [d->buf[slot] contents];
5096 return static_cast<
char *>(p);
5111 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
5113 case QRhiTexture::RGBA8:
5114 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
5115 case QRhiTexture::BGRA8:
5116 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
5117 case QRhiTexture::R8:
5119 return MTLPixelFormatR8Unorm;
5121 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
5123 case QRhiTexture::R8SI:
5124 return MTLPixelFormatR8Sint;
5125 case QRhiTexture::R8UI:
5126 return MTLPixelFormatR8Uint;
5127 case QRhiTexture::RG8:
5129 return MTLPixelFormatRG8Unorm;
5131 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
5133 case QRhiTexture::R16:
5134 return MTLPixelFormatR16Unorm;
5135 case QRhiTexture::RG16:
5136 return MTLPixelFormatRG16Unorm;
5137 case QRhiTexture::RED_OR_ALPHA8:
5138 return MTLPixelFormatR8Unorm;
5140 case QRhiTexture::RGBA16F:
5141 return MTLPixelFormatRGBA16Float;
5142 case QRhiTexture::RGBA32F:
5143 return MTLPixelFormatRGBA32Float;
5144 case QRhiTexture::R16F:
5145 return MTLPixelFormatR16Float;
5146 case QRhiTexture::R32F:
5147 return MTLPixelFormatR32Float;
5149 case QRhiTexture::RGB10A2:
5150 return MTLPixelFormatRGB10A2Unorm;
5152 case QRhiTexture::R32SI:
5153 return MTLPixelFormatR32Sint;
5154 case QRhiTexture::R32UI:
5155 return MTLPixelFormatR32Uint;
5156 case QRhiTexture::RG32SI:
5157 return MTLPixelFormatRG32Sint;
5158 case QRhiTexture::RG32UI:
5159 return MTLPixelFormatRG32Uint;
5160 case QRhiTexture::RGBA32SI:
5161 return MTLPixelFormatRGBA32Sint;
5162 case QRhiTexture::RGBA32UI:
5163 return MTLPixelFormatRGBA32Uint;
5166 case QRhiTexture::D16:
5167 return MTLPixelFormatDepth16Unorm;
5168 case QRhiTexture::D24:
5169 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
5170 case QRhiTexture::D24S8:
5171 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5173 case QRhiTexture::D16:
5174 return MTLPixelFormatDepth32Float;
5175 case QRhiTexture::D24:
5176 return MTLPixelFormatDepth32Float;
5177 case QRhiTexture::D24S8:
5178 return MTLPixelFormatDepth32Float_Stencil8;
5180 case QRhiTexture::D32F:
5181 return MTLPixelFormatDepth32Float;
5182 case QRhiTexture::D32FS8:
5183 return MTLPixelFormatDepth32Float_Stencil8;
5186 case QRhiTexture::BC1:
5187 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
5188 case QRhiTexture::BC2:
5189 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
5190 case QRhiTexture::BC3:
5191 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
5192 case QRhiTexture::BC4:
5193 return MTLPixelFormatBC4_RUnorm;
5194 case QRhiTexture::BC5:
5195 qWarning(
"QRhiMetal does not support BC5");
5196 return MTLPixelFormatInvalid;
5197 case QRhiTexture::BC6H:
5198 return MTLPixelFormatBC6H_RGBUfloat;
5199 case QRhiTexture::BC7:
5200 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
5202 case QRhiTexture::BC1:
5203 case QRhiTexture::BC2:
5204 case QRhiTexture::BC3:
5205 case QRhiTexture::BC4:
5206 case QRhiTexture::BC5:
5207 case QRhiTexture::BC6H:
5208 case QRhiTexture::BC7:
5209 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
5210 return MTLPixelFormatInvalid;
5214 case QRhiTexture::ETC2_RGB8:
5215 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5216 case QRhiTexture::ETC2_RGB8A1:
5217 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5218 case QRhiTexture::ETC2_RGBA8:
5219 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5221 case QRhiTexture::ASTC_4x4:
5222 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5223 case QRhiTexture::ASTC_5x4:
5224 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5225 case QRhiTexture::ASTC_5x5:
5226 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5227 case QRhiTexture::ASTC_6x5:
5228 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5229 case QRhiTexture::ASTC_6x6:
5230 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5231 case QRhiTexture::ASTC_8x5:
5232 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5233 case QRhiTexture::ASTC_8x6:
5234 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5235 case QRhiTexture::ASTC_8x8:
5236 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5237 case QRhiTexture::ASTC_10x5:
5238 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5239 case QRhiTexture::ASTC_10x6:
5240 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5241 case QRhiTexture::ASTC_10x8:
5242 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5243 case QRhiTexture::ASTC_10x10:
5244 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5245 case QRhiTexture::ASTC_12x10:
5246 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5247 case QRhiTexture::ASTC_12x12:
5248 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5250 case QRhiTexture::ETC2_RGB8:
5251 if (d->caps.isAppleGPU)
5252 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
5253 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5254 return MTLPixelFormatInvalid;
5255 case QRhiTexture::ETC2_RGB8A1:
5256 if (d->caps.isAppleGPU)
5257 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
5258 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5259 return MTLPixelFormatInvalid;
5260 case QRhiTexture::ETC2_RGBA8:
5261 if (d->caps.isAppleGPU)
5262 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
5263 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
5264 return MTLPixelFormatInvalid;
5265 case QRhiTexture::ASTC_4x4:
5266 if (d->caps.isAppleGPU)
5267 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
5268 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5269 return MTLPixelFormatInvalid;
5270 case QRhiTexture::ASTC_5x4:
5271 if (d->caps.isAppleGPU)
5272 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
5273 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5274 return MTLPixelFormatInvalid;
5275 case QRhiTexture::ASTC_5x5:
5276 if (d->caps.isAppleGPU)
5277 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
5278 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5279 return MTLPixelFormatInvalid;
5280 case QRhiTexture::ASTC_6x5:
5281 if (d->caps.isAppleGPU)
5282 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
5283 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5284 return MTLPixelFormatInvalid;
5285 case QRhiTexture::ASTC_6x6:
5286 if (d->caps.isAppleGPU)
5287 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
5288 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5289 return MTLPixelFormatInvalid;
5290 case QRhiTexture::ASTC_8x5:
5291 if (d->caps.isAppleGPU)
5292 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
5293 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5294 return MTLPixelFormatInvalid;
5295 case QRhiTexture::ASTC_8x6:
5296 if (d->caps.isAppleGPU)
5297 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
5298 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5299 return MTLPixelFormatInvalid;
5300 case QRhiTexture::ASTC_8x8:
5301 if (d->caps.isAppleGPU)
5302 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
5303 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5304 return MTLPixelFormatInvalid;
5305 case QRhiTexture::ASTC_10x5:
5306 if (d->caps.isAppleGPU)
5307 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
5308 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5309 return MTLPixelFormatInvalid;
5310 case QRhiTexture::ASTC_10x6:
5311 if (d->caps.isAppleGPU)
5312 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
5313 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5314 return MTLPixelFormatInvalid;
5315 case QRhiTexture::ASTC_10x8:
5316 if (d->caps.isAppleGPU)
5317 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
5318 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5319 return MTLPixelFormatInvalid;
5320 case QRhiTexture::ASTC_10x10:
5321 if (d->caps.isAppleGPU)
5322 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
5323 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5324 return MTLPixelFormatInvalid;
5325 case QRhiTexture::ASTC_12x10:
5326 if (d->caps.isAppleGPU)
5327 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
5328 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5329 return MTLPixelFormatInvalid;
5330 case QRhiTexture::ASTC_12x12:
5331 if (d->caps.isAppleGPU)
5332 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
5333 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
5334 return MTLPixelFormatInvalid;
5339 return MTLPixelFormatInvalid;
5344 int sampleCount, QRhiRenderBuffer::Flags flags,
5345 QRhiTexture::Format backingFormatHint)
5366 e.renderbuffer.texture =
d->tex;
5371 rhiD
->d->releaseQueue.append(e);
5372 rhiD->unregisterResource(
this);
5381 if (m_pixelSize.isEmpty())
5385 samples = rhiD->effectiveSampleCount(m_sampleCount);
5387 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5388 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5389 desc.width = NSUInteger(m_pixelSize.width());
5390 desc.height = NSUInteger(m_pixelSize.height());
5392 desc.sampleCount = NSUInteger(
samples);
5393 desc.resourceOptions = MTLResourceStorageModePrivate;
5394 desc.usage = MTLTextureUsageRenderTarget;
5399 const bool canBeMemoryless = !m_flags.testFlag(QRhiRenderBuffer::NoTransientBacking);
5404 if (rhiD->caps.isAppleGPU && canBeMemoryless) {
5405 desc.storageMode = MTLStorageModeMemoryless;
5406 d->format = MTLPixelFormatDepth32Float_Stencil8;
5408 desc.storageMode = MTLStorageModePrivate;
5409 d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
5410 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
5413 desc.storageMode = canBeMemoryless ? MTLStorageModeMemoryless : MTLStorageModePrivate;
5414 d->format = MTLPixelFormatDepth32Float_Stencil8;
5416 desc.pixelFormat =
d->format;
5419 desc.storageMode = MTLStorageModePrivate;
5420 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5421 d->format = toMetalTextureFormat(m_backingFormatHint, {}, rhiD);
5423 d->format = MTLPixelFormatRGBA8Unorm;
5424 desc.pixelFormat =
d->format;
5431 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5434 if (!m_objectName.isEmpty())
5435 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5439 rhiD->registerResource(
this);
5445 if (m_backingFormatHint != QRhiTexture::UnknownFormat)
5446 return m_backingFormatHint;
5448 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
5452 int arraySize,
int sampleCount, Flags flags)
5456 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
5457 d->stagingBuf[i] = nil;
5459 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i)
5460 d->perLevelViews[i] = nil;
5478 e.texture.texture = d->owns ? d->tex : nil;
5482 e.texture.stagingBuffers[i] =
d->stagingBuf[i];
5483 d->stagingBuf[i] = nil;
5486 for (
int i = 0; i < QRhi::MAX_MIP_LEVELS; ++i) {
5487 e.texture.views[i] =
d->perLevelViews[i];
5488 d->perLevelViews[i] = nil;
5491 e.texture.samplingView =
d->samplingView;
5492 d->samplingView = nil;
5493 e.texture.writeView =
d->writeView;
5498 rhiD
->d->releaseQueue.append(e);
5499 rhiD->unregisterResource(
this);
5508 const bool isCube = m_flags.testFlag(CubeMap);
5509 const bool is3D = m_flags.testFlag(ThreeDimensional);
5510 const bool isArray = m_flags.testFlag(TextureArray);
5511 const bool hasMipMaps = m_flags.testFlag(MipMapped);
5512 const bool is1D = m_flags.testFlag(OneDimensional);
5514 const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
5515 : (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
5518 d->format = toMetalTextureFormat(m_format, m_flags, rhiD);
5519 if (m_writeViewFormat.format != UnknownFormat) {
5520 d->viewFormat = toMetalTextureFormat(m_writeViewFormat.format,
5521 m_writeViewFormat.srgb ? sRGB : Flags(), rhiD);
5523 d->viewFormat =
d->format;
5525 if (m_readViewFormat.format != UnknownFormat) {
5526 d->viewFormatForSampling = toMetalTextureFormat(m_readViewFormat.format,
5527 m_readViewFormat.srgb ? sRGB : Flags(), rhiD);
5529 d->viewFormatForSampling =
d->format;
5531 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
5532 samples = rhiD->effectiveSampleCount(m_sampleCount);
5535 qWarning(
"Cubemap texture cannot be multisample");
5539 qWarning(
"3D texture cannot be multisample");
5543 qWarning(
"Multisample texture cannot have mipmaps");
5547 if (isCube && is3D) {
5548 qWarning(
"Texture cannot be both cube and 3D");
5551 if (isArray && is3D) {
5552 qWarning(
"Texture cannot be both array and 3D");
5556 qWarning(
"Texture cannot be both 1D and 3D");
5559 if (is1D && isCube) {
5560 qWarning(
"Texture cannot be both 1D and cube");
5563 if (m_depth > 1 && !is3D) {
5564 qWarning(
"Texture cannot have a depth of %d when it is not 3D", m_depth);
5567 if (m_arraySize > 0 && !isArray) {
5568 qWarning(
"Texture cannot have an array size of %d when it is not an array", m_arraySize);
5571 if (m_arraySize < 1 && isArray) {
5572 qWarning(
"Texture is an array but array size is %d", m_arraySize);
5576 if (!rhiD->textureFormatInfo(m_format, size,
nullptr,
nullptr,
nullptr))
5580 *adjustedSize = size;
5588 if (!prepareCreate(&size))
5591 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
5593 const bool isCube = m_flags.testFlag(CubeMap);
5594 const bool is3D = m_flags.testFlag(ThreeDimensional);
5595 const bool isArray = m_flags.testFlag(TextureArray);
5596 const bool is1D = m_flags.testFlag(OneDimensional);
5598 desc.textureType = MTLTextureTypeCube;
5600 desc.textureType = MTLTextureType3D;
5602 desc.textureType = isArray ? MTLTextureType1DArray : MTLTextureType1D;
5603 }
else if (isArray) {
5604 desc.textureType = samples > 1 ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
5606 desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
5608 desc.pixelFormat =
d->format;
5609 desc.width = NSUInteger(size.width());
5610 desc.height = NSUInteger(size.height());
5611 desc.depth = is3D ? qMax(1, m_depth) : 1;
5614 desc.sampleCount = NSUInteger(
samples);
5616 desc.arrayLength = NSUInteger(qMax(0, m_arraySize));
5617 desc.resourceOptions = MTLResourceStorageModePrivate;
5618 desc.storageMode = MTLStorageModePrivate;
5619 desc.usage = MTLTextureUsageShaderRead;
5620 if (m_flags.testFlag(RenderTarget))
5621 desc.usage |= MTLTextureUsageRenderTarget;
5622 if (m_flags.testFlag(UsedWithLoadStore))
5623 desc.usage |= MTLTextureUsageShaderWrite;
5629 const bool writeViewChangesFormat = m_writeViewFormat.format != UnknownFormat
5630 && m_writeViewFormat.format != m_format;
5631 const bool readViewChangesFormat = m_readViewFormat.format != UnknownFormat
5632 && m_readViewFormat.format != m_format;
5633 if (writeViewChangesFormat || readViewChangesFormat)
5634 desc.usage |= MTLTextureUsagePixelFormatView;
5637 d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
5640 if (!m_objectName.isEmpty())
5641 d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
5650 rhiD->registerResource(
this);
5656 id<MTLTexture> tex = id<MTLTexture>(src.object);
5660 if (!prepareCreate())
5673 rhiD->registerResource(
this);
5679 return {quint64(
d->tex), 0};
5684 Q_ASSERT(!samplingView && !writeView);
5686 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5687 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5688 const bool hasArrayRange = isArray &&
q->m_arrayRangeStart >= 0 &&
q->m_arrayRangeLength >= 0;
5689 const NSUInteger sliceCount = isCube ? 6 : (isArray ? NSUInteger(qMax(0,
q->m_arraySize)) : 1);
5691 const MTLTextureType type = [tex textureType];
5693 id<MTLTexture> newSamplingView = nil;
5694 id<MTLTexture> newWriteView = nil;
5696 if (viewFormatForSampling != format || hasArrayRange) {
5697 const NSRange slices = hasArrayRange
5698 ? NSMakeRange(NSUInteger(
q->m_arrayRangeStart), NSUInteger(
q->m_arrayRangeLength))
5699 : NSMakeRange(0, sliceCount);
5700 newSamplingView = [tex newTextureViewWithPixelFormat: viewFormatForSampling
5701 textureType: type levels: levels slices: slices];
5702 if (!newSamplingView) {
5703 qWarning(
"QRhiMetal: Failed to create texture view used for sampling");
5708 if (viewFormat != format) {
5709 newWriteView = [tex newTextureViewWithPixelFormat: viewFormat
5710 textureType: type levels: levels
5711 slices: NSMakeRange(0, sliceCount)];
5712 if (!newWriteView) {
5713 qWarning(
"QRhiMetal: Failed to create texture view used for rendering");
5714 [newSamplingView release];
5719 samplingView = newSamplingView;
5720 writeView = newWriteView;
5727 if (perLevelViews[level])
5728 return perLevelViews[level];
5730 const MTLTextureType type = [tex textureType];
5731 const bool isCube =
q->m_flags.testFlag(QRhiTexture::CubeMap);
5732 const bool isArray =
q->m_flags.testFlag(QRhiTexture::TextureArray);
5733 id<MTLTexture> view = [tex newTextureViewWithPixelFormat: viewFormat textureType: type
5734 levels: NSMakeRange(NSUInteger(level), 1)
5735 slices: NSMakeRange(0, isCube ? 6 : (isArray ? qMax(0, q->m_arraySize) : 1))];
5737 perLevelViews[level] = view;
5742 AddressMode u, AddressMode v, AddressMode w)
5756 if (!
d->samplerState)
5763 e.sampler.samplerState =
d->samplerState;
5764 d->samplerState = nil;
5768 rhiD
->d->releaseQueue.append(e);
5769 rhiD->unregisterResource(
this);
5776 case QRhiSampler::Nearest:
5777 return MTLSamplerMinMagFilterNearest;
5778 case QRhiSampler::Linear:
5779 return MTLSamplerMinMagFilterLinear;
5782 return MTLSamplerMinMagFilterNearest;
5789 case QRhiSampler::None:
5790 return MTLSamplerMipFilterNotMipmapped;
5791 case QRhiSampler::Nearest:
5792 return MTLSamplerMipFilterNearest;
5793 case QRhiSampler::Linear:
5794 return MTLSamplerMipFilterLinear;
5797 return MTLSamplerMipFilterNotMipmapped;
5804 case QRhiSampler::Repeat:
5805 return MTLSamplerAddressModeRepeat;
5806 case QRhiSampler::ClampToEdge:
5807 return MTLSamplerAddressModeClampToEdge;
5808 case QRhiSampler::Mirror:
5809 return MTLSamplerAddressModeMirrorRepeat;
5812 return MTLSamplerAddressModeClampToEdge;
5819 case QRhiSampler::Never:
5820 return MTLCompareFunctionNever;
5821 case QRhiSampler::Less:
5822 return MTLCompareFunctionLess;
5823 case QRhiSampler::Equal:
5824 return MTLCompareFunctionEqual;
5825 case QRhiSampler::LessOrEqual:
5826 return MTLCompareFunctionLessEqual;
5827 case QRhiSampler::Greater:
5828 return MTLCompareFunctionGreater;
5829 case QRhiSampler::NotEqual:
5830 return MTLCompareFunctionNotEqual;
5831 case QRhiSampler::GreaterOrEqual:
5832 return MTLCompareFunctionGreaterEqual;
5833 case QRhiSampler::Always:
5834 return MTLCompareFunctionAlways;
5837 return MTLCompareFunctionNever;
5843 if (
d->samplerState)
5846 MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
5847 desc.minFilter = toMetalFilter(m_minFilter);
5848 desc.magFilter = toMetalFilter(m_magFilter);
5849 desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
5850 desc.sAddressMode = toMetalAddressMode(m_addressU);
5851 desc.tAddressMode = toMetalAddressMode(m_addressV);
5852 desc.rAddressMode = toMetalAddressMode(m_addressW);
5853 desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
5859 desc.supportArgumentBuffers = rhiD->caps.indirectCommandBuffers ? YES : NO;
5860 d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
5862 if (!
d->samplerState) {
5866 qWarning(
"Failed to create Metal sampler state. The number of unique sampler "
5867 "states with argument buffer support may have exceeded the limit of %u.",
5868 uint(rhiD
->d->dev.maxArgumentBufferSamplerCount));
5874 rhiD->registerResource(
this);
5899 e.shadingRateMap.rateMap =
d->rateMap;
5904 rhiD
->d->releaseQueue.append(e);
5905 rhiD->unregisterResource(
this);
5914 d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
5918 [d->rateMap retain];
5923 rhiD->registerResource(
this);
5932 const MTLSize screenSize = [d->rateMap screenSize];
5933 return QSize(
int(screenSize.width),
int(screenSize.height));
5941 serializedFormatData.reserve(16);
5953 rhiD->unregisterResource(
this);
5987 serializedFormatData.clear();
5988 auto p =
std::back_inserter(serializedFormatData);
6010 rhiD->registerResource(rpD,
false);
6016 return serializedFormatData;
6038 return d->pixelSize;
6052 const QRhiTextureRenderTargetDescription &desc,
6069 rhiD->unregisterResource(
this);
6074 const int colorAttachmentCount =
int(m_desc.colorAttachmentCount());
6077 rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
6079 for (
int i = 0; i < colorAttachmentCount; ++i) {
6080 const QRhiColorAttachment *colorAtt = m_desc.colorAttachmentAt(i);
6086 if (m_desc.depthTexture())
6087 rpD->dsFormat =
int(
QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->viewFormat);
6088 else if (m_desc.depthStencilBuffer())
6089 rpD->dsFormat =
int(
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
6091 rpD->hasShadingRateMap = m_desc.shadingRateMap() !=
nullptr;
6096 rhiD->registerResource(rpD,
false);
6103 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
6104 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
6105 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
6109 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
6113 Q_ASSERT(texD || rbD);
6114 id<MTLTexture> dst = nil;
6117 dst = texD
->d->textureForWrite();
6118 if (attIndex == 0) {
6119 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
6122 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
6125 if (attIndex == 0) {
6126 d->pixelSize = rbD->pixelSize();
6133 colorAtt
.slice = is3D ? it->layer() : 0;
6134 colorAtt
.level = it->level();
6136 colorAtt.resolveTex = resTexD ? resTexD->d->textureForWrite() : nil;
6139 d->fb.colorAtt[attIndex] = colorAtt;
6143 if (hasDepthStencil) {
6144 if (m_desc.depthTexture()) {
6146 d->fb.dsTex = depthTexD
->d->textureForWrite();
6147 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
6148 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
6149 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
6151 d->pixelSize = depthTexD->pixelSize();
6156 d->fb.dsTex = depthRbD
->d->tex;
6157 d->fb.hasStencil =
true;
6158 d->fb.depthNeedsStore =
false;
6159 d->fb.preserveDs =
false;
6161 d->pixelSize = depthRbD->pixelSize();
6165 if (m_desc.depthResolveTexture()) {
6167 d->fb.dsResolveTex = depthResolveTexD
->d->textureForWrite();
6174 if (d->colorAttCount > 0)
6175 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
6177 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
6179 rhiD->registerResource(
this,
false);
6185 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(m_desc, d->currentResIdList))
6188 return d->pixelSize;
6213 sortedBindings.clear();
6218 rhiD->unregisterResource(
this);
6223 if (!sortedBindings.isEmpty())
6227 if (!rhiD->sanityCheckShaderResourceBindings(
this))
6230 rhiD->updateLayoutDesc(
this);
6232 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
6233 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
6234 if (!sortedBindings.isEmpty())
6235 maxBinding = QRhiImplementation::shaderResourceBindingData(sortedBindings.last())->binding;
6239 boundResourceData.resize(sortedBindings.count());
6241 for (BoundResourceData &bd : boundResourceData)
6245 rhiD->registerResource(
this,
false);
6251 sortedBindings.clear();
6252 std::copy(m_bindings.cbegin(), m_bindings.cend(),
std::back_inserter(sortedBindings));
6253 if (!flags.testFlag(BindingsAreSorted))
6254 std::sort(sortedBindings.begin(), sortedBindings.end(), QRhiImplementation::sortedBindingLessThan);
6256 for (BoundResourceData &bd : boundResourceData)
6283 d->tess.compVs[0].destroy();
6284 d->tess.compVs[1].destroy();
6285 d->tess.compVs[2].destroy();
6287 d->tess.compTesc.destroy();
6288 d->tess.vertTese.destroy();
6290 qDeleteAll(
d->extraBufMgr.deviceLocalWorkBuffers);
6291 d->extraBufMgr.deviceLocalWorkBuffers.clear();
6292 qDeleteAll(
d->extraBufMgr.hostVisibleWorkBuffers);
6293 d->extraBufMgr.hostVisibleWorkBuffers.clear();
6298 if (!
d->ps && !
d->ds
6299 && !
d->tess.vertexComputeState[0] && !
d->tess.vertexComputeState[1] && !
d->tess.vertexComputeState[2]
6300 && !
d->tess.tessControlComputeState)
6308 e.graphicsPipeline.pipelineState =
d->ps;
6309 e.graphicsPipeline.depthStencilState =
d->ds;
6310 e.graphicsPipeline.tessVertexComputeState =
d->tess.vertexComputeState;
6311 e.graphicsPipeline.tessTessControlComputeState =
d->tess.tessControlComputeState;
6314 d->tess.vertexComputeState = {};
6315 d->tess.tessControlComputeState = nil;
6319 rhiD
->d->releaseQueue.append(e);
6320 rhiD->unregisterResource(
this);
6327 case QRhiVertexInputAttribute::Float4:
6328 return MTLVertexFormatFloat4;
6329 case QRhiVertexInputAttribute::Float3:
6330 return MTLVertexFormatFloat3;
6331 case QRhiVertexInputAttribute::Float2:
6332 return MTLVertexFormatFloat2;
6333 case QRhiVertexInputAttribute::Float:
6334 return MTLVertexFormatFloat;
6335 case QRhiVertexInputAttribute::UNormByte4:
6336 return MTLVertexFormatUChar4Normalized;
6337 case QRhiVertexInputAttribute::UNormByte2:
6338 return MTLVertexFormatUChar2Normalized;
6339 case QRhiVertexInputAttribute::UNormByte:
6340 return MTLVertexFormatUCharNormalized;
6341 case QRhiVertexInputAttribute::UInt4:
6342 return MTLVertexFormatUInt4;
6343 case QRhiVertexInputAttribute::UInt3:
6344 return MTLVertexFormatUInt3;
6345 case QRhiVertexInputAttribute::UInt2:
6346 return MTLVertexFormatUInt2;
6347 case QRhiVertexInputAttribute::UInt:
6348 return MTLVertexFormatUInt;
6349 case QRhiVertexInputAttribute::SInt4:
6350 return MTLVertexFormatInt4;
6351 case QRhiVertexInputAttribute::SInt3:
6352 return MTLVertexFormatInt3;
6353 case QRhiVertexInputAttribute::SInt2:
6354 return MTLVertexFormatInt2;
6355 case QRhiVertexInputAttribute::SInt:
6356 return MTLVertexFormatInt;
6357 case QRhiVertexInputAttribute::Half4:
6358 return MTLVertexFormatHalf4;
6359 case QRhiVertexInputAttribute::Half3:
6360 return MTLVertexFormatHalf3;
6361 case QRhiVertexInputAttribute::Half2:
6362 return MTLVertexFormatHalf2;
6363 case QRhiVertexInputAttribute::Half:
6364 return MTLVertexFormatHalf;
6365 case QRhiVertexInputAttribute::UShort4:
6366 return MTLVertexFormatUShort4;
6367 case QRhiVertexInputAttribute::UShort3:
6368 return MTLVertexFormatUShort3;
6369 case QRhiVertexInputAttribute::UShort2:
6370 return MTLVertexFormatUShort2;
6371 case QRhiVertexInputAttribute::UShort:
6372 return MTLVertexFormatUShort;
6373 case QRhiVertexInputAttribute::SShort4:
6374 return MTLVertexFormatShort4;
6375 case QRhiVertexInputAttribute::SShort3:
6376 return MTLVertexFormatShort3;
6377 case QRhiVertexInputAttribute::SShort2:
6378 return MTLVertexFormatShort2;
6379 case QRhiVertexInputAttribute::SShort:
6380 return MTLVertexFormatShort;
6383 return MTLVertexFormatFloat4;
6390 case QRhiGraphicsPipeline::Zero:
6391 return MTLBlendFactorZero;
6392 case QRhiGraphicsPipeline::One:
6393 return MTLBlendFactorOne;
6394 case QRhiGraphicsPipeline::SrcColor:
6395 return MTLBlendFactorSourceColor;
6396 case QRhiGraphicsPipeline::OneMinusSrcColor:
6397 return MTLBlendFactorOneMinusSourceColor;
6398 case QRhiGraphicsPipeline::DstColor:
6399 return MTLBlendFactorDestinationColor;
6400 case QRhiGraphicsPipeline::OneMinusDstColor:
6401 return MTLBlendFactorOneMinusDestinationColor;
6402 case QRhiGraphicsPipeline::SrcAlpha:
6403 return MTLBlendFactorSourceAlpha;
6404 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
6405 return MTLBlendFactorOneMinusSourceAlpha;
6406 case QRhiGraphicsPipeline::DstAlpha:
6407 return MTLBlendFactorDestinationAlpha;
6408 case QRhiGraphicsPipeline::OneMinusDstAlpha:
6409 return MTLBlendFactorOneMinusDestinationAlpha;
6410 case QRhiGraphicsPipeline::ConstantColor:
6411 return MTLBlendFactorBlendColor;
6412 case QRhiGraphicsPipeline::ConstantAlpha:
6413 return MTLBlendFactorBlendAlpha;
6414 case QRhiGraphicsPipeline::OneMinusConstantColor:
6415 return MTLBlendFactorOneMinusBlendColor;
6416 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
6417 return MTLBlendFactorOneMinusBlendAlpha;
6418 case QRhiGraphicsPipeline::SrcAlphaSaturate:
6419 return MTLBlendFactorSourceAlphaSaturated;
6420 case QRhiGraphicsPipeline::Src1Color:
6421 return MTLBlendFactorSource1Color;
6422 case QRhiGraphicsPipeline::OneMinusSrc1Color:
6423 return MTLBlendFactorOneMinusSource1Color;
6424 case QRhiGraphicsPipeline::Src1Alpha:
6425 return MTLBlendFactorSource1Alpha;
6426 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
6427 return MTLBlendFactorOneMinusSource1Alpha;
6430 return MTLBlendFactorZero;
6437 case QRhiGraphicsPipeline::Add:
6438 return MTLBlendOperationAdd;
6439 case QRhiGraphicsPipeline::Subtract:
6440 return MTLBlendOperationSubtract;
6441 case QRhiGraphicsPipeline::ReverseSubtract:
6442 return MTLBlendOperationReverseSubtract;
6443 case QRhiGraphicsPipeline::Min:
6444 return MTLBlendOperationMin;
6445 case QRhiGraphicsPipeline::Max:
6446 return MTLBlendOperationMax;
6449 return MTLBlendOperationAdd;
6456 if (c.testFlag(QRhiGraphicsPipeline::R))
6457 f |= MTLColorWriteMaskRed;
6458 if (c.testFlag(QRhiGraphicsPipeline::G))
6459 f |= MTLColorWriteMaskGreen;
6460 if (c.testFlag(QRhiGraphicsPipeline::B))
6461 f |= MTLColorWriteMaskBlue;
6462 if (c.testFlag(QRhiGraphicsPipeline::A))
6463 f |= MTLColorWriteMaskAlpha;
6470 case QRhiGraphicsPipeline::Never:
6471 return MTLCompareFunctionNever;
6472 case QRhiGraphicsPipeline::Less:
6473 return MTLCompareFunctionLess;
6474 case QRhiGraphicsPipeline::Equal:
6475 return MTLCompareFunctionEqual;
6476 case QRhiGraphicsPipeline::LessOrEqual:
6477 return MTLCompareFunctionLessEqual;
6478 case QRhiGraphicsPipeline::Greater:
6479 return MTLCompareFunctionGreater;
6480 case QRhiGraphicsPipeline::NotEqual:
6481 return MTLCompareFunctionNotEqual;
6482 case QRhiGraphicsPipeline::GreaterOrEqual:
6483 return MTLCompareFunctionGreaterEqual;
6484 case QRhiGraphicsPipeline::Always:
6485 return MTLCompareFunctionAlways;
6488 return MTLCompareFunctionAlways;
6495 case QRhiGraphicsPipeline::StencilZero:
6496 return MTLStencilOperationZero;
6497 case QRhiGraphicsPipeline::Keep:
6498 return MTLStencilOperationKeep;
6499 case QRhiGraphicsPipeline::Replace:
6500 return MTLStencilOperationReplace;
6501 case QRhiGraphicsPipeline::IncrementAndClamp:
6502 return MTLStencilOperationIncrementClamp;
6503 case QRhiGraphicsPipeline::DecrementAndClamp:
6504 return MTLStencilOperationDecrementClamp;
6505 case QRhiGraphicsPipeline::Invert:
6506 return MTLStencilOperationInvert;
6507 case QRhiGraphicsPipeline::IncrementAndWrap:
6508 return MTLStencilOperationIncrementWrap;
6509 case QRhiGraphicsPipeline::DecrementAndWrap:
6510 return MTLStencilOperationDecrementWrap;
6513 return MTLStencilOperationKeep;
6520 case QRhiGraphicsPipeline::Triangles:
6521 return MTLPrimitiveTypeTriangle;
6522 case QRhiGraphicsPipeline::TriangleStrip:
6523 return MTLPrimitiveTypeTriangleStrip;
6524 case QRhiGraphicsPipeline::Lines:
6525 return MTLPrimitiveTypeLine;
6526 case QRhiGraphicsPipeline::LineStrip:
6527 return MTLPrimitiveTypeLineStrip;
6528 case QRhiGraphicsPipeline::Points:
6529 return MTLPrimitiveTypePoint;
6532 return MTLPrimitiveTypeTriangle;
6539 case QRhiGraphicsPipeline::Triangles:
6540 case QRhiGraphicsPipeline::TriangleStrip:
6541 case QRhiGraphicsPipeline::TriangleFan:
6542 return MTLPrimitiveTopologyClassTriangle;
6543 case QRhiGraphicsPipeline::Lines:
6544 case QRhiGraphicsPipeline::LineStrip:
6545 return MTLPrimitiveTopologyClassLine;
6546 case QRhiGraphicsPipeline::Points:
6547 return MTLPrimitiveTopologyClassPoint;
6550 return MTLPrimitiveTopologyClassTriangle;
6557 case QRhiGraphicsPipeline::None:
6558 return MTLCullModeNone;
6559 case QRhiGraphicsPipeline::Front:
6560 return MTLCullModeFront;
6561 case QRhiGraphicsPipeline::Back:
6562 return MTLCullModeBack;
6565 return MTLCullModeNone;
6572 case QRhiGraphicsPipeline::Fill:
6573 return MTLTriangleFillModeFill;
6574 case QRhiGraphicsPipeline::Line:
6575 return MTLTriangleFillModeLines;
6578 return MTLTriangleFillModeFill;
6585 case QShaderDescription::CwTessellationWindingOrder:
6586 return MTLWindingClockwise;
6587 case QShaderDescription::CcwTessellationWindingOrder:
6588 return MTLWindingCounterClockwise;
6591 return MTLWindingCounterClockwise;
6598 case QShaderDescription::EqualTessellationPartitioning:
6599 return MTLTessellationPartitionModePow2;
6600 case QShaderDescription::FractionalEvenTessellationPartitioning:
6601 return MTLTessellationPartitionModeFractionalEven;
6602 case QShaderDescription::FractionalOddTessellationPartitioning:
6603 return MTLTessellationPartitionModeFractionalOdd;
6606 return MTLTessellationPartitionModePow2;
6612 int v = version.version();
6613 return MTLLanguageVersion(((v / 10) << 16) + (v % 10));
6617 int frameSlot, quint32 minBlockSize, quint32 *offset)
6620 const quint32 alignedSize = aligned<quint32>(size, alignment);
6621 if (pool.offset + alignedSize > pool.capacity) {
6631 e.stagingBuffer.buffer = pool.buf;
6632 releaseQueue.append(e);
6634 pool.capacity = qMax(pool.capacity * 2, qMax(alignedSize, minBlockSize));
6635 pool.buf = [dev newBufferWithLength: pool.capacity options: MTLResourceStorageModeShared];
6642 *offset = pool.offset;
6643 pool.offset += alignedSize;
6649 return allocFromStagingArea(&argBufPool[frameSlot], size, alignment, frameSlot, 16384, offset);
6654 id<MTLBuffer> buf = [dev newBufferWithLength: size options: MTLResourceStorageModeShared];
6660 e.stagingBuffer.buffer = buf;
6661 releaseQueue.append(e);
6667 if (size > LARGE_STAGING_ALLOC) {
6669 return newOneShotStagingBuffer(size, frameSlot);
6674 const quint32 alignedSize = aligned<quint32>(size, 4u);
6675 area.bytesNeeded += alignedSize;
6677 if (area.offset + alignedSize > area.capacity) {
6679 return newOneShotStagingBuffer(size, frameSlot);
6682 *offset = area.offset;
6683 area.offset += alignedSize;
6690 const quint32 needed = area.bytesNeeded;
6691 area.bytesNeeded = 0;
6694 bool resize =
false;
6695 quint32 newCapacity = 0;
6696 if (needed > area.capacity) {
6703 newCapacity = qMin(qNextPowerOfTwo(needed), STAGING_AREA_MAX);
6706 }
else if (needed <= area.capacity / 4 && area.capacity > 0) {
6712 newCapacity = needed ? qMax(area.capacity / 2, STAGING_AREA_MIN) : 0;
6720 if (resize && newCapacity != area.capacity) {
6725 e.stagingBuffer.buffer = area.buf;
6726 releaseQueue.append(e);
6731 area.buf = [dev newBufferWithLength: newCapacity options: MTLResourceStorageModeShared];
6733 area.capacity = newCapacity;
6739 bool preferArgumentBuffers,
6740 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
6742 QVarLengthArray<
int, 8> versions;
6743 versions << 30 << 24 << 23 << 22 << 21 << 20 << 12;
6753 QVarLengthArray<QShader::Variant, 2> variants;
6754 if (preferArgumentBuffers)
6755 variants << QShader::ArgumentBufferShader;
6756 variants << shaderVariant;
6758 const QList<QShaderKey> shaders = shader.availableShaders();
6760 auto findKey = [&shaders, &versions, &variants](QShader::Source source, QShaderKey *result) {
6761 for (
const QShader::Variant &variant : variants) {
6762 for (
const int &version : versions) {
6763 const QShaderKey key = { source, version, variant };
6764 if (shaders.contains(key)) {
6775 if (findKey(QShader::Source::MetalLibShader, &key)) {
6776 QShaderCode mtllib = shader.shader(key);
6777 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
6778 size_t(mtllib.shader().size()),
6779 dispatch_get_global_queue(0, 0),
6780 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
6782 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
6783 dispatch_release(data);
6785 *entryPoint = mtllib.entryPoint();
6789 const QString msg = QString::fromNSString(err.localizedDescription);
6790 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
6794 if (!findKey(QShader::Source::MslShader, &key)) {
6795 qWarning() <<
"No MSL code found in baked shader" << shader;
6799 QShaderCode mslSource = shader.shader(key);
6801 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
6802 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
6803 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
6805 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
6813 const QString msg = QString::fromNSString(err.localizedDescription);
6818 *entryPoint = mslSource.entryPoint();
6825 return [lib newFunctionWithName:[NSString stringWithUTF8String:entryPoint.constData()]];
6830 MTLRenderPipelineDescriptor *rpDesc =
reinterpret_cast<MTLRenderPipelineDescriptor *>(metalRpDesc);
6834 rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD
->colorFormat[0]);
6835 rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
6836 rpDesc.colorAttachments[0].blendingEnabled =
false;
6838 Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
6839 || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
6841 for (uint i = 0, ie = uint(m_targetBlends.count()); i != ie; ++i) {
6842 const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[
int(i)]);
6843 rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD
->colorFormat[i]);
6844 rpDesc.colorAttachments[i].blendingEnabled = b.enable;
6845 rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
6846 rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
6847 rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
6848 rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
6849 rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
6850 rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
6851 rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
6858 MTLPixelFormat fmt = MTLPixelFormat(rpD
->dsFormat);
6859 rpDesc.depthAttachmentPixelFormat = fmt;
6860#if defined(Q_OS_MACOS)
6861 if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
6863 if (fmt != MTLPixelFormatDepth32Float)
6865 rpDesc.stencilAttachmentPixelFormat = fmt;
6869 rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
6874 MTLDepthStencilDescriptor *dsDesc =
reinterpret_cast<MTLDepthStencilDescriptor *>(metalDsDesc);
6876 dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
6877 dsDesc.depthWriteEnabled = m_depthWrite;
6878 if (m_stencilTest) {
6879 dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
6880 dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
6881 dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
6882 dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
6883 dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
6884 dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
6885 dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
6887 dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
6888 dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
6889 dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
6890 dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
6891 dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
6892 dsDesc.backFaceStencil.readMask = m_stencilReadMask;
6893 dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
6899 d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
6900 d->cullMode = toMetalCullMode(m_cullMode);
6901 d->triangleFillMode = toMetalTriangleFillMode(m_polygonMode);
6902 d->depthClipMode = m_depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
6903 d->depthBias =
float(m_depthBias);
6904 d->slopeScaledDepthBias = m_slopeScaledDepthBias;
6914 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
6917 const uint loc = uint(it->location());
6918 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
6919 desc.attributes[loc].offset = NSUInteger(it->offset());
6920 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
6922 int bindingIndex = 0;
6923 const NSUInteger viewCount = qMax<NSUInteger>(1, q->multiViewCount());
6924 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
6925 it != itEnd; ++it, ++bindingIndex)
6927 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
6928 desc.layouts[layoutIdx].stepFunction =
6929 it->classification() == QRhiVertexInputBinding::PerInstance
6930 ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
6931 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
6932 if (desc.layouts[layoutIdx].stepFunction == MTLVertexStepFunctionPerInstance)
6933 desc.layouts[layoutIdx].stepRate *= viewCount;
6934 desc.layouts[layoutIdx].stride = it->stride();
6945 for (
auto it = vertexInputLayout.cbeginAttributes(), itEnd = vertexInputLayout.cendAttributes();
6948 const uint loc = uint(it->location());
6949 desc.attributes[loc].format =
decltype(desc.attributes[loc].format)(toMetalAttributeFormat(it->format()));
6950 desc.attributes[loc].offset = NSUInteger(it->offset());
6951 desc.attributes[loc].bufferIndex = NSUInteger(firstVertexBinding + it->binding());
6953 int bindingIndex = 0;
6954 for (
auto it = vertexInputLayout.cbeginBindings(), itEnd = vertexInputLayout.cendBindings();
6955 it != itEnd; ++it, ++bindingIndex)
6957 const uint layoutIdx = uint(firstVertexBinding + bindingIndex);
6958 if (desc.indexBufferIndex) {
6959 desc.layouts[layoutIdx].stepFunction =
6960 it->classification() == QRhiVertexInputBinding::PerInstance
6961 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridXIndexed;
6963 desc.layouts[layoutIdx].stepFunction =
6964 it->classification() == QRhiVertexInputBinding::PerInstance
6965 ? MTLStepFunctionThreadPositionInGridY : MTLStepFunctionThreadPositionInGridX;
6967 desc.layouts[layoutIdx].stepRate = NSUInteger(it->instanceStepRate());
6968 desc.layouts[layoutIdx].stride = it->stride();
6975 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
6976 rpDesc.binaryArchives = binArchArray;
6984 if (![binArch addRenderPipelineFunctionsWithDescriptor: rpDesc error: &err]) {
6985 const QString msg = QString::fromNSString(err.localizedDescription);
6986 qWarning(
"Failed to collect render pipeline functions to binary archive: %s", qPrintable(msg));
6993 return !desc.combinedImageSamplers().isEmpty()
6994 || !desc.separateImages().isEmpty()
6995 || !desc.storageImages().isEmpty();
7000 for (
const QShaderKey &k : shader.availableShaders()) {
7001 if (k.sourceVariant() == QShader::ArgumentBufferShader)
7009 const int index = shader->nativeShaderInfo.extraBufferBindings.value(QShaderPrivate::MslArgumentBufferBinding, -1);
7012 shader->argumentEncoder = [shader->func newArgumentEncoderWithBufferIndex: NSUInteger(index)];
7023 const bool wantArgumentBuffers = m_flags.testFlag(UsesIndirectDraws) && rhiD->caps.indirectCommandBuffers;
7025 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7026 d->setupVertexInputDescriptor(vertexDesc);
7028 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7029 rpDesc.vertexDescriptor = vertexDesc;
7037 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7038 const QShader shader = shaderStage.shader();
7039 const bool argumentBufferBuild = wantArgumentBuffers && hasArgumentBufferVariant(shader);
7040 auto cacheIt = rhiD->d->shaderCache.constFind({ shaderStage, argumentBufferBuild });
7041 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
7042 switch (shaderStage.type()) {
7043 case QRhiShaderStage::Vertex:
7046 [d->vs.func retain];
7047 [d->vs.argumentEncoder retain];
7048 rpDesc.vertexFunction = d->vs.func;
7050 case QRhiShaderStage::Fragment:
7053 [d->fs.func retain];
7054 [d->fs.argumentEncoder retain];
7055 rpDesc.fragmentFunction = d->fs.func;
7062 QByteArray entryPoint;
7063 QShaderKey activeKey;
7064 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
7065 argumentBufferBuild,
7066 &error, &entryPoint, &activeKey);
7068 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
7071 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7073 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7077 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
7079 for (QMetalShader &s : rhiD->d->shaderCache)
7081 rhiD->d->shaderCache.clear();
7083 switch (shaderStage.type()) {
7084 case QRhiShaderStage::Vertex:
7087 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7088 d->vs.desc = shader.description();
7089 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7090 setupArgumentBufferEncoder(&d->vs);
7091 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->vs);
7093 [d->vs.func retain];
7094 [d->vs.argumentEncoder retain];
7095 rpDesc.vertexFunction = func;
7097 case QRhiShaderStage::Fragment:
7100 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
7101 d->fs.desc = shader.description();
7102 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
7103 setupArgumentBufferEncoder(&d->fs);
7104 rhiD->d->shaderCache.insert({ shaderStage, argumentBufferBuild }, d->fs);
7106 [d->fs.func retain];
7107 [d->fs.argumentEncoder retain];
7108 rpDesc.fragmentFunction = func;
7124 return !usesTextures(s.desc) || s.argumentBufferIndex >= 0;
7126 d->icbCapable = wantArgumentBuffers && icbSafe(
d->vs) && icbSafe(
d->fs);
7128 rpDesc.supportIndirectCommandBuffers = YES;
7130 if (m_multiViewCount >= 2)
7131 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
7133 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7135 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7136 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7139 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7142 const QString msg = QString::fromNSString(err.localizedDescription);
7143 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
7147 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7149 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
7152 d->primitiveType = toMetalPrimitiveType(m_topology);
7160 switch (vertexCompVariant) {
7161 case QShader::NonIndexedVertexAsComputeShader:
7163 case QShader::UInt32IndexedVertexAsComputeShader:
7165 case QShader::UInt16IndexedVertexAsComputeShader:
7175 const int varIndex = vsCompVariantToIndex(vertexCompVariant);
7176 if (varIndex >= 0 && vertexComputeState[varIndex])
7177 return vertexComputeState[varIndex];
7179 id<MTLFunction> func = nil;
7181 func = compVs[varIndex].func;
7184 qWarning(
"No compute function found for vertex shader translated for tessellation, this should not happen");
7188 const QMap<
int,
int> &ebb(compVs[varIndex].nativeShaderInfo.extraBufferBindings);
7189 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
7191 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7192 cpDesc.computeFunction = func;
7193 cpDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES;
7194 cpDesc.stageInputDescriptor = [MTLStageInputOutputDescriptor stageInputOutputDescriptor];
7195 if (indexBufferBinding >= 0) {
7196 if (vertexCompVariant == QShader::UInt32IndexedVertexAsComputeShader) {
7197 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt32;
7198 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
7199 }
else if (vertexCompVariant == QShader::UInt16IndexedVertexAsComputeShader) {
7200 cpDesc.stageInputDescriptor.indexType = MTLIndexTypeUInt16;
7201 cpDesc.stageInputDescriptor.indexBufferIndex = indexBufferBinding;
7204 q->setupStageInputDescriptor(cpDesc.stageInputDescriptor);
7206 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7208 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7209 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7212 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7213 options: MTLPipelineOptionNone
7218 const QString msg = QString::fromNSString(err.localizedDescription);
7219 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7221 vertexComputeState[varIndex] = ps;
7229 if (tessControlComputeState)
7230 return tessControlComputeState;
7232 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
7233 cpDesc.computeFunction = compTesc.func;
7235 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
7237 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7238 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
7241 id<MTLComputePipelineState> ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
7242 options: MTLPipelineOptionNone
7247 const QString msg = QString::fromNSString(err.localizedDescription);
7248 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
7250 tessControlComputeState = ps;
7258 return (indices >> index) & 0x1;
7261static inline void takeIndex(quint32 index, quint64 &indices)
7263 indices |= 1 << index;
7272 static const int maxVertexAttributes = 31;
7274 for (
int index = 0; index < maxVertexAttributes; ++index) {
7275 if (!indexTaken(index, indices))
7279 Q_UNREACHABLE_RETURN(-1);
7282static inline int aligned(quint32 offset, quint32 alignment)
7284 return ((offset + alignment - 1) / alignment) * alignment;
7292 for (
const int dim : variable.arrayDims)
7295 if (variable.type == QShaderDescription::VariableType::Struct) {
7296 for (
int element = 0; element < elements; ++element) {
7297 for (
const auto &member : variable.structMembers) {
7298 addUnusedVertexAttribute(member, rhiD, offset, vertexAlignment);
7302 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
7303 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
7306 const quint32 alignment = size;
7307 vertexAlignment =
std::max(vertexAlignment, alignment);
7309 for (
int element = 0; element < elements; ++element) {
7311 offset = aligned(offset, alignment);
7318static void addVertexAttribute(
const T &variable,
int binding,
QRhiMetal *rhiD,
int &index, quint32 &offset, MTLVertexAttributeDescriptorArray *attributes, quint64 &indices, quint32 &vertexAlignment)
7322 for (
const int dim : variable.arrayDims)
7325 if (variable.type == QShaderDescription::VariableType::Struct) {
7326 for (
int element = 0; element < elements; ++element) {
7327 for (
const auto &member : variable.structMembers) {
7328 addVertexAttribute(member, binding, rhiD, index, offset, attributes, indices, vertexAlignment);
7332 const QRhiVertexInputAttribute::Format format = rhiD->shaderDescVariableFormatToVertexInputFormat(variable.type);
7333 const quint32 size = rhiD->byteSizePerVertexForVertexInputFormat(format);
7336 const quint32 alignment = size;
7337 vertexAlignment =
std::max(vertexAlignment, alignment);
7339 for (
int element = 0; element < elements; ++element) {
7340 Q_ASSERT(!indexTaken(index, indices));
7343 offset = aligned(offset, alignment);
7345 attributes[index].bufferIndex = binding;
7346 attributes[index].format = toMetalAttributeFormat(format);
7347 attributes[index].offset = offset;
7349 takeIndex(index, indices);
7351 if (indexTaken(index, indices))
7352 index = nextAttributeIndex(indices);
7359static inline bool matches(
const QList<QShaderDescription::BlockVariable> &a,
const QList<QShaderDescription::BlockVariable> &b)
7361 if (a.size() == b.size()) {
7363 for (
int i = 0; i < a.size() && match; ++i) {
7364 match &= a[i].type == b[i].type
7365 && a[i].arrayDims == b[i].arrayDims
7366 && matches(a[i].structMembers, b[i].structMembers);
7374static inline bool matches(
const QShaderDescription::InOutVariable &a,
const QShaderDescription::InOutVariable &b)
7376 return a.location == b.location
7378 && a.perPatch == b.perPatch
7379 && matches(a.structMembers, b.structMembers);
7428 if (pipeline
->d->ps)
7429 return pipeline
->d->ps;
7431 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
7432 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
7435 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
7436 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
7437 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
7438 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
7439 quint32 offsetInTescOutput = 0;
7440 quint32 offsetInTescPatchOutput = 0;
7441 quint32 offsetInTessFactorBuffer = 0;
7442 quint32 tescOutputAlignment = 0;
7443 quint32 tescPatchOutputAlignment = 0;
7444 quint32 tessFactorAlignment = 0;
7445 QSet<
int> usedBuffers;
7448 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
7449 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
7450 tescOutVars[tescOutVar.location] = tescOutVar;
7453 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
7454 for (
const auto &teseInVar : vertTese.desc.inputVariables())
7455 teseInVars[teseInVar.location] = teseInVar;
7458 quint64 indices = 0;
7460 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
7462 int index = tescOutVar.location;
7464 quint32 *offset =
nullptr;
7465 quint32 *alignment =
nullptr;
7467 if (tescOutVar.perPatch) {
7468 binding = tescPatchOutputBufferBinding;
7469 offset = &offsetInTescPatchOutput;
7470 alignment = &tescPatchOutputAlignment;
7472 tescOutVar.arrayDims.removeLast();
7473 binding = tescOutputBufferBinding;
7474 offset = &offsetInTescOutput;
7475 alignment = &tescOutputAlignment;
7478 if (teseInVars.contains(index)) {
7480 if (!matches(teseInVars[index], tescOutVar)) {
7481 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
7482 qWarning() <<
" tesc out:" << tescOutVar;
7483 qWarning() <<
" tese in:" << teseInVars[index];
7486 if (binding != -1) {
7487 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7488 usedBuffers << binding;
7490 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7491 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7495 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
7496 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
7499 teseInVars.remove(tescOutVar.location);
7502 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
7503 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
7506 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
7507 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
7508 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
7511 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
7512 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
7513 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
7515 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
7516 bool tessLevelAdded =
false;
7518 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
7520 QShaderDescription::InOutVariable variable;
7522 quint32 *offset =
nullptr;
7523 quint32 *alignment =
nullptr;
7525 switch (builtin.type) {
7526 case QShaderDescription::BuiltinType::PositionBuiltin:
7527 variable.type = QShaderDescription::VariableType::Vec4;
7528 binding = tescOutputBufferBinding;
7529 offset = &offsetInTescOutput;
7530 alignment = &tescOutputAlignment;
7532 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7533 variable.type = QShaderDescription::VariableType::Float;
7534 binding = tescOutputBufferBinding;
7535 offset = &offsetInTescOutput;
7536 alignment = &tescOutputAlignment;
7538 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7539 variable.type = QShaderDescription::VariableType::Float;
7540 variable.arrayDims = builtin.arrayDims;
7541 binding = tescOutputBufferBinding;
7542 offset = &offsetInTescOutput;
7543 alignment = &tescOutputAlignment;
7545 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
7546 variable.type = QShaderDescription::VariableType::Half4;
7547 binding = tessFactorBufferBinding;
7548 offset = &offsetInTessFactorBuffer;
7549 tessLevelAdded = trianglesMode;
7550 alignment = &tessFactorAlignment;
7552 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
7553 if (trianglesMode) {
7554 if (!tessLevelAdded) {
7555 variable.type = QShaderDescription::VariableType::Half4;
7556 binding = tessFactorBufferBinding;
7557 offsetInTessFactorBuffer = 0;
7558 offset = &offsetInTessFactorBuffer;
7559 alignment = &tessFactorAlignment;
7560 tessLevelAdded =
true;
7562 teseInBuiltins.remove(builtin.type);
7566 variable.type = QShaderDescription::VariableType::Half2;
7567 binding = tessFactorBufferBinding;
7568 offsetInTessFactorBuffer = 8;
7569 offset = &offsetInTessFactorBuffer;
7570 alignment = &tessFactorAlignment;
7578 if (teseInBuiltins.contains(builtin.type)) {
7579 if (binding != -1) {
7580 int index = nextAttributeIndex(indices);
7581 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
7582 usedBuffers << binding;
7584 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
7585 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7588 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
7591 teseInBuiltins.remove(builtin.type);
7594 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
7595 switch (builtin.type) {
7596 case QShaderDescription::BuiltinType::PositionBuiltin:
7597 case QShaderDescription::BuiltinType::PointSizeBuiltin:
7598 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
7599 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
7606 if (usedBuffers.contains(tescOutputBufferBinding)) {
7607 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
7608 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
7611 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
7612 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7613 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
7616 if (usedBuffers.contains(tessFactorBufferBinding)) {
7617 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
7618 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
7621 rpDesc.vertexDescriptor = vertexDesc;
7622 rpDesc.vertexFunction = vertTese.func;
7623 rpDesc.fragmentFunction = pipeline
->d->fs.func;
7629 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
7631 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
7636 rhiD
->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
7638 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
7639 rhiD
->d->addRenderPipelineToBinaryArchive(rpDesc);
7642 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
7645 const QString msg = QString::fromNSString(err.localizedDescription);
7646 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
7650 pipeline->d->ps = ps;
7657 QVector<QMetalBuffer *> *workBuffers = type == WorkBufType::DeviceLocal ? &deviceLocalWorkBuffers : &hostVisibleWorkBuffers;
7660 for (QMetalBuffer *workBuf : *workBuffers) {
7661 if (workBuf && workBuf->lastActiveFrameSlot == -1 && workBuf->size() >= size) {
7662 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7670 for (QMetalBuffer *workBuf : *workBuffers) {
7671 if (workBuf && workBuf->lastActiveFrameSlot == -1) {
7672 workBuf->setSize(size);
7673 if (workBuf->create()) {
7674 workBuf->lastActiveFrameSlot = rhiD->currentFrameSlot;
7685 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7688 buf =
new QMetalBuffer(rhiD, QRhiBuffer::Dynamic, QRhiBuffer::UsageFlags(QMetalBuffer::WorkBufPoolUsage), size);
7692 workBuffers->append(buf);
7696 qWarning(
"Failed to acquire work buffer of size %u", size);
7704 QByteArray entryPoint;
7705 QShaderKey activeKey;
7707 const QShaderDescription tescDesc = tesc.description();
7708 const QShaderDescription teseDesc = tese.description();
7709 d->tess.inControlPointCount = uint(m_patchControlPointCount);
7710 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
7711 if (!
d->tess.outControlPointCount)
7712 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
7714 if (!
d->tess.outControlPointCount) {
7715 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
7716 d->tess.enabled =
false;
7717 d->tess.failed =
true;
7721 if (m_multiViewCount >= 2)
7722 qWarning(
"Multiview is not supported with tessellation");
7730 bool variantsPresent[3] = {};
7731 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
7732 for (
const QShaderKey &k : tessVertKeys) {
7733 switch (k.sourceVariant()) {
7734 case QShader::NonIndexedVertexAsComputeShader:
7735 variantsPresent[0] =
true;
7737 case QShader::UInt32IndexedVertexAsComputeShader:
7738 variantsPresent[1] =
true;
7740 case QShader::UInt16IndexedVertexAsComputeShader:
7741 variantsPresent[2] =
true;
7747 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
7748 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
7749 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
7750 "Try passing --msltess to qsb.");
7751 d->tess.enabled =
false;
7752 d->tess.failed =
true;
7757 for (QShader::Variant variant : {
7758 QShader::NonIndexedVertexAsComputeShader,
7759 QShader::UInt32IndexedVertexAsComputeShader,
7760 QShader::UInt16IndexedVertexAsComputeShader })
7762 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant,
false, &error, &entryPoint, &activeKey);
7764 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
7765 d->tess.enabled =
false;
7766 d->tess.failed =
true;
7769 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
7771 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7773 d->tess.enabled =
false;
7774 d->tess.failed =
true;
7777 QMetalShader &compVs(d->tess.compVs[varIndex]);
7780 compVs.desc = tessVert.description();
7781 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
7782 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
7785 if (!d->tess.vsCompPipeline(rhiD, variant)) {
7786 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
7787 d->tess.enabled =
false;
7788 d->tess.failed =
true;
7796 id<MTLLibrary> tessControlLib = rhiD
->d->createMetalLib(tesc, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7797 if (!tessControlLib) {
7798 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
7799 d->tess.enabled =
false;
7800 d->tess.failed =
true;
7803 id<MTLFunction> tessControlFunc = rhiD
->d->createMSLShaderFunction(tessControlLib, entryPoint);
7804 if (!tessControlFunc) {
7805 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7806 [tessControlLib release];
7807 d->tess.enabled =
false;
7808 d->tess.failed =
true;
7811 d->tess.compTesc.lib = tessControlLib;
7812 d->tess.compTesc.func = tessControlFunc;
7813 d->tess.compTesc.desc = tesc.description();
7814 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
7815 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
7816 if (!
d->tess.tescCompPipeline(rhiD)) {
7817 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
7818 d->tess.enabled =
false;
7819 d->tess.failed =
true;
7824 id<MTLLibrary> tessEvalLib = rhiD
->d->createMetalLib(tese, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7826 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
7827 d->tess.enabled =
false;
7828 d->tess.failed =
true;
7831 id<MTLFunction> tessEvalFunc = rhiD
->d->createMSLShaderFunction(tessEvalLib, entryPoint);
7832 if (!tessEvalFunc) {
7833 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7834 [tessEvalLib release];
7835 d->tess.enabled =
false;
7836 d->tess.failed =
true;
7839 d->tess.vertTese.lib = tessEvalLib;
7840 d->tess.vertTese.func = tessEvalFunc;
7841 d->tess.vertTese.desc = tese.description();
7842 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
7843 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
7845 id<MTLLibrary> fragLib = rhiD
->d->createMetalLib(tessFrag, QShader::StandardShader,
false, &error, &entryPoint, &activeKey);
7847 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
7848 d->tess.enabled =
false;
7849 d->tess.failed =
true;
7852 id<MTLFunction> fragFunc = rhiD
->d->createMSLShaderFunction(fragLib, entryPoint);
7854 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
7856 d->tess.enabled =
false;
7857 d->tess.failed =
true;
7860 d->fs.lib = fragLib;
7861 d->fs.func = fragFunc;
7862 d->fs.desc = tessFrag.description();
7863 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
7864 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
7866 if (!
d->tess.teseFragRenderPipeline(rhiD,
this)) {
7867 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
7868 d->tess.enabled =
false;
7869 d->tess.failed =
true;
7873 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
7875 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
7889 rhiD->pipelineCreationStart();
7890 if (!rhiD->sanityCheckGraphicsPipeline(
this))
7898 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
7899 switch (shaderStage.type()) {
7900 case QRhiShaderStage::Vertex:
7901 tessVert = shaderStage.shader();
7903 case QRhiShaderStage::TessellationControl:
7904 tesc = shaderStage.shader();
7906 case QRhiShaderStage::TessellationEvaluation:
7907 tese = shaderStage.shader();
7909 case QRhiShaderStage::Fragment:
7910 tessFrag = shaderStage.shader();
7916 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
7917 d->tess.failed =
false;
7919 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
7925 QVarLengthArray<QMetalShader *, 6> shaders;
7926 if (
d->tess.enabled) {
7927 shaders.append(&
d->tess.compVs[0]);
7928 shaders.append(&
d->tess.compVs[1]);
7929 shaders.append(&
d->tess.compVs[2]);
7930 shaders.append(&
d->tess.compTesc);
7931 shaders.append(&
d->tess.vertTese);
7933 shaders.append(&
d->vs);
7935 shaders.append(&
d->fs);
7937 for (QMetalShader *shader : shaders) {
7938 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
7939 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
7940 shader->nativeResourceBindingMap[binding] = {binding, -1};
7941 int maxNativeBinding = 0;
7942 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
7943 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
7947 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
7952 if (!d->bufferSizeBuffer)
7953 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static,
7954 QRhiBuffer::UsageFlags(
int(QRhiBuffer::StorageBuffer)
7955 | QMetalBuffer::InternalHostWritable),
7956 buffers *
sizeof(
int));
7962 rhiD->pipelineCreationEnd();
7965 rhiD->registerResource(
this);
7994 e.computePipeline.pipelineState =
d->ps;
7999 rhiD
->d->releaseQueue.append(e);
8000 rhiD->unregisterResource(
this);
8007 NSArray *binArchArray = [NSArray arrayWithObjects: binArch, nil];
8008 cpDesc.binaryArchives = binArchArray;
8016 if (![binArch addComputePipelineFunctionsWithDescriptor: cpDesc error: &err]) {
8017 const QString msg = QString::fromNSString(err.localizedDescription);
8018 qWarning(
"Failed to collect compute pipeline functions to binary archive: %s", qPrintable(msg));
8029 rhiD->pipelineCreationStart();
8031 auto cacheIt = rhiD
->d->shaderCache.constFind({ m_shaderStage,
false });
8032 if (cacheIt != rhiD
->d->shaderCache.constEnd()) {
8035 const QShader shader = m_shaderStage.shader();
8037 QByteArray entryPoint;
8038 QShaderKey activeKey;
8039 id<MTLLibrary> lib = rhiD
->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
false,
8040 &error, &entryPoint, &activeKey);
8042 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
8045 id<MTLFunction> func = rhiD
->d->createMSLShaderFunction(lib, entryPoint);
8047 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
8053 d->cs.localSize = shader.description().computeShaderLocalSize();
8054 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
8055 d->cs.desc = shader.description();
8056 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
8063 setupArgumentBufferEncoder(&
d->cs);
8066 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8067 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
8068 d->cs.nativeResourceBindingMap[binding] = {binding, -1};
8071 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
8072 for (QMetalShader &s : rhiD->d->shaderCache)
8074 rhiD
->d->shaderCache.clear();
8076 rhiD
->d->shaderCache.insert({ m_shaderStage,
false },
d->cs);
8080 [d->cs.func retain];
8081 [d->cs.argumentEncoder retain];
8083 if (
d->cs.argumentBufferIndex >= 0 && !rhiD->caps.indirectCommandBuffers) {
8087 qWarning(
"The ArgumentBufferShader variant of a compute shader cannot be used on this device");
8091 d->localSize = MTLSizeMake(
d->cs.localSize[0],
d->cs.localSize[1],
d->cs.localSize[2]);
8093 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
8094 cpDesc.computeFunction =
d->cs.func;
8096 rhiD
->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
8098 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
8099 rhiD
->d->addComputePipelineToBinaryArchive(cpDesc);
8102 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
8103 options: MTLPipelineOptionNone
8108 const QString msg = QString::fromNSString(err.localizedDescription);
8109 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
8114 if (
d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
8116 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
8117 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
8121 if (!d->bufferSizeBuffer)
8122 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static,
8123 QRhiBuffer::UsageFlags(
int(QRhiBuffer::StorageBuffer)
8124 | QMetalBuffer::InternalHostWritable),
8125 buffers *
sizeof(
int));
8131 rhiD->pipelineCreationEnd();
8134 rhiD->registerResource(
this);
8158 nativeHandlesStruct.commandBuffer = (MTLCommandBuffer *) d->cb;
8159 nativeHandlesStruct.encoder = (MTLRenderCommandEncoder *) d->currentRenderPassEncoder;
8160 return &nativeHandlesStruct;
8166 d->currentRenderPassEncoder = nil;
8167 d->currentComputePassEncoder = nil;
8168 d->tessellationComputeEncoder = nil;
8169 d->currentPassRpDesc = nil;
8176 currentTarget =
nullptr;
8177 d->openDebugGroups.clear();
8186 pushConstantData.clear();
8193 currentIndexOffset = 0;
8194 currentIndexFormat = QRhiCommandBuffer::IndexUInt16;
8199 currentDepthBiasValues = { 0.0f, 0.0f };
8201 currentScissor = {};
8202 currentViewport = {};
8204 currentBlendConstants = {};
8206 currentStencilRef = 0;
8208 d->currentShaderResourceBindingState = {};
8209 d->currentDepthStencilState = nil;
8211 d->currentVertexInputsBuffers.clear();
8212 d->currentVertexInputOffsets.clear();
8222 d->sem[i] =
nullptr;
8223 d->msaaTex[i] = nil;
8243 dispatch_release(
d->sem[i]);
8244 d->sem[i] =
nullptr;
8249 [d->msaaTex[i] release];
8250 d->msaaTex[i] = nil;
8256 [d->curDrawable release];
8257 d->curDrawable = nil;
8261 rhiD->swapchains.remove(
this);
8262 rhiD->unregisterResource(
this);
8282 CALayer *layer =
nullptr;
8284 if (
auto *cocoaWindow = window->nativeInterface<QNativeInterface::Private::QCocoaWindow>())
8285 layer = cocoaWindow->contentLayer();
8287 layer =
reinterpret_cast<UIView *>(window->winId()).layer;
8290 return static_cast<CAMetalLayer *>(layer);
8299 d.reserved[0] = layerForWindow(window);
8306 CAMetalLayer *layer =
d->layer;
8308 layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, m_window, QRhi::Metal, 0);
8311 int height = (
int)layer.bounds.size.height;
8312 int width = (
int)layer.bounds.size.width;
8313 width *= layer.contentsScale;
8314 height *= layer.contentsScale;
8315 return QSize(width, height);
8320 if (f == HDRExtendedSrgbLinear) {
8322 }
else if (f == HDR10) {
8324 }
else if (f == HDRExtendedDisplayP3Linear) {
8338 rpD->hasDepthStencil = m_depthStencil !=
nullptr;
8344 rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
8345 ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
8347 rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
8350 rpD->hasShadingRateMap = m_shadingRateMap !=
nullptr;
8354 rhiD->registerResource(rpD,
false);
8361 samples = rhiD->effectiveSampleCount(m_sampleCount);
8363 if (m_format == HDRExtendedSrgbLinear || m_format == HDRExtendedDisplayP3Linear) {
8364 d->colorFormat = MTLPixelFormatRGBA16Float;
8365 d->rhiColorFormat = QRhiTexture::RGBA16F;
8368 if (m_format == HDR10) {
8369 d->colorFormat = MTLPixelFormatRGB10A2Unorm;
8370 d->rhiColorFormat = QRhiTexture::RGB10A2;
8373 d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
8374 d->rhiColorFormat = QRhiTexture::BGRA8;
8383 dispatch_semaphore_t sem =
d->sem[slot];
8384 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
8385 dispatch_semaphore_signal(sem);
8392 const bool needsRegistration = !window || window != m_window;
8394 if (window && window != m_window)
8399 if (needsRegistration || !rhiD->swapchains.contains(
this))
8400 rhiD->swapchains.insert(
this);
8404 if (window->surfaceType() != QSurface::MetalSurface) {
8405 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
8409 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
8413 if (
d->colorFormat !=
d->layer.pixelFormat)
8414 d->layer.pixelFormat =
d->colorFormat;
8416 if (m_format == HDRExtendedSrgbLinear) {
8417 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
8418 d->layer.wantsExtendedDynamicRangeContent = YES;
8419 }
else if (m_format == HDR10) {
8420 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
8421 d->layer.wantsExtendedDynamicRangeContent = YES;
8422 }
else if (m_format == HDRExtendedDisplayP3Linear) {
8423 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
8424 d->layer.wantsExtendedDynamicRangeContent = YES;
8427 if (m_flags.testFlag(UsedAsTransferSource))
8428 d->layer.framebufferOnly = NO;
8431 if (m_flags.testFlag(NoVSync))
8432 d->layer.displaySyncEnabled = NO;
8435 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
8436 d->layer.opaque = NO;
8437 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
8442 d->layer.opaque = NO;
8444 d->layer.opaque = YES;
8450 int width = (
int)
d->layer.bounds.size.width;
8451 int height = (
int)
d->layer.bounds.size.height;
8452 CGSize layerSize = CGSizeMake(width, height);
8453 const float scaleFactor =
d->layer.contentsScale;
8454 layerSize.width *= scaleFactor;
8455 layerSize.height *= scaleFactor;
8456 d->layer.drawableSize = layerSize;
8458 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
8459 pixelSize = m_currentPixelSize;
8461 [d->layer setDevice: rhiD->d->dev];
8463 [d->curDrawable release];
8464 d->curDrawable = nil;
8475 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
8476 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
8477 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
8478 m_depthStencil->sampleCount(), m_sampleCount);
8480 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
8481 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
8482 m_depthStencil->setPixelSize(pixelSize);
8483 if (!m_depthStencil->create())
8484 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
8485 pixelSize.width(), pixelSize.height());
8487 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
8488 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
8489 pixelSize.width(), pixelSize.height());
8493 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
8494 rtWrapper.d->pixelSize = pixelSize;
8500 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
8501 pixelSize.width(), pixelSize.height(), scaleFactor);
8504 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
8505 desc.textureType = MTLTextureType2DMultisample;
8506 desc.pixelFormat =
d->colorFormat;
8507 desc.width = NSUInteger(pixelSize.width());
8508 desc.height = NSUInteger(pixelSize.height());
8509 desc.sampleCount = NSUInteger(
samples);
8510 desc.resourceOptions = MTLResourceStorageModePrivate;
8511 desc.storageMode = MTLStorageModePrivate;
8512 desc.usage = MTLTextureUsageRenderTarget;
8514 if (
d->msaaTex[i]) {
8518 e.renderbuffer.texture =
d->msaaTex[i];
8519 rhiD
->d->releaseQueue.append(e);
8521 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
8526 rhiD->registerResource(
this);
8542#if defined(Q_OS_MACOS)
8543 NSView *view =
reinterpret_cast<NSView *>(m_window->winId());
8544 NSScreen *screen = view.window.screen;
8545 info.limits.colorComponentValue.maxColorComponentValue = screen.maximumExtendedDynamicRangeColorComponentValue;
8546 info.limits.colorComponentValue.maxPotentialColorComponentValue = screen.maximumPotentialExtendedDynamicRangeColorComponentValue;
8547#elif defined(Q_OS_IOS)
8548 UIView *view =
reinterpret_cast<UIView *>(m_window->winId());
8549 UIScreen *screen = view.window.windowScene.screen;
8550 info.limits.colorComponentValue.maxColorComponentValue =
8551 view.window.windowScene.screen.currentEDRHeadroom;
8552 info.limits.colorComponentValue.maxPotentialColorComponentValue =
8553 screen.potentialEDRHeadroom;
const char * constData() const
static QRhiResourceUpdateBatchPrivate * get(QRhiResourceUpdateBatch *b)
Combined button and popup list for selecting options.
Int aligned(Int v, Int byteAlign)
\variable QRhiVulkanQueueSubmitParams::waitSemaphoreCount
MTLPixelFormat viewFormat
MTLPixelFormat viewFormatForSampling
id< MTLTexture > viewForLevel(int level)
id< MTLTexture > perLevelViews[QRhi::MAX_MIP_LEVELS]
id< MTLBuffer > stagingBuf[QMTL_FRAMES_IN_FLIGHT]
id< MTLTexture > writeView
QMetalTextureData(QMetalTexture *t)
id< MTLTexture > samplingView
id< MTLTexture > textureForWrite() const
id< MTLTexture > textureForSampling() const
~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.
\variable QRhiIndirectDrawCommand::vertexCount
id< MTLComputePipelineState > pipelineState
id< MTLTexture > samplingView
id< MTLDepthStencilState > depthStencilState
std::array< id< MTLComputePipelineState >, 3 > tessVertexComputeState
id< MTLTexture > writeView
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