562 d->dev = MTLCreateSystemDefaultDevice();
565 qWarning(
"No MTLDevice");
569 const QString deviceName = QString::fromNSString([d->dev name]);
570 qCDebug(QRHI_LOG_INFO,
"Metal device: %s", qPrintable(deviceName));
571 driverInfoStruct.deviceName = deviceName.toUtf8();
578 const MTLDeviceLocation deviceLocation = [d->dev location];
579 switch (deviceLocation) {
580 case MTLDeviceLocationBuiltIn:
581 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
583 case MTLDeviceLocationSlot:
584 driverInfoStruct.deviceType = QRhiDriverInfo::DiscreteDevice;
586 case MTLDeviceLocationExternal:
587 driverInfoStruct.deviceType = QRhiDriverInfo::ExternalDevice;
593 driverInfoStruct.deviceType = QRhiDriverInfo::IntegratedDevice;
596 const QOperatingSystemVersion ver = QOperatingSystemVersion::current();
597 osMajor = ver.majorVersion();
598 osMinor = ver.minorVersion();
600 if (importedCmdQueue)
601 [d->cmdQueue retain];
603 d->cmdQueue = [d->dev newCommandQueue];
605 d->captureMgr = [MTLCaptureManager sharedCaptureManager];
609 d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
610 const QString label = QString::asprintf(
"Qt capture scope for QRhi %p",
this);
611 d->captureScope.label = label.toNSString();
613#if defined(Q_OS_MACOS) || defined(Q_OS_VISIONOS)
614 caps.maxTextureSize = 16384;
615 caps.baseVertexAndInstance =
true;
616 caps.isAppleGPU = [d->dev supportsFamily:MTLGPUFamilyApple7];
617 caps.maxThreadGroupSize = 1024;
618 caps.multiView =
true;
619#elif defined(Q_OS_TVOS)
620 if ([d->dev supportsFamily:MTLGPUFamilyApple3])
621 caps.maxTextureSize = 16384;
623 caps.maxTextureSize = 8192;
624 caps.baseVertexAndInstance =
false;
625 caps.isAppleGPU =
true;
626#elif defined(Q_OS_IOS)
627 if ([d->dev supportsFamily:MTLGPUFamilyApple3]) {
628 caps.maxTextureSize = 16384;
629 caps.baseVertexAndInstance =
true;
630 }
else if ([d->dev supportsFamily:MTLGPUFamilyApple2]) {
631 caps.maxTextureSize = 8192;
632 caps.baseVertexAndInstance =
false;
634 caps.maxTextureSize = 4096;
635 caps.baseVertexAndInstance =
false;
637 caps.isAppleGPU =
true;
638 if ([d->dev supportsFamily:MTLGPUFamilyApple4])
639 caps.maxThreadGroupSize = 1024;
640 if ([d->dev supportsFamily:MTLGPUFamilyApple5])
641 caps.multiView =
true;
644 caps.supportedSampleCounts = { 1 };
645 for (
int sampleCount : { 2, 4, 8 }) {
646 if ([d->dev supportsTextureSampleCount: sampleCount])
647 caps.supportedSampleCounts.append(sampleCount);
650 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
651 if (caps.shadingRateMap && caps.multiView)
652 caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
654 if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
655 d->setupBinaryArchive();
657 nativeHandlesStruct.dev = (MTLDevice *) d->dev;
658 nativeHandlesStruct.cmdQueue = (MTLCommandQueue *) d->cmdQueue;
1285void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
1286 QMetalCommandBuffer *cbD,
1287 int dynamicOffsetCount,
1288 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
1289 bool offsetOnlyChange,
1290 const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
1292 QMetalShaderResourceBindingsData bindingData;
1294 for (
const QRhiShaderResourceBinding &binding : std::as_const(srbD->sortedBindings)) {
1295 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(binding);
1297 case QRhiShaderResourceBinding::UniformBuffer:
1299 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1300 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1301 quint32 offset = b->u.ubuf.offset;
1302 for (
int i = 0; i < dynamicOffsetCount; ++i) {
1303 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
1304 if (dynOfs.first == b->binding) {
1305 offset = dynOfs.second;
1310 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1311 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1312 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1313 if (nativeBinding >= 0)
1314 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1319 case QRhiShaderResourceBinding::SampledTexture:
1320 case QRhiShaderResourceBinding::Texture:
1321 case QRhiShaderResourceBinding::Sampler:
1323 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1324 for (
int elem = 0; elem < data->count; ++elem) {
1325 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex);
1326 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler);
1328 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1329 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1334 const int textureBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1335 const int samplerBinding = texD && samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Sampler)
1336 : (samplerD ? mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture) : -1);
1337 if (textureBinding >= 0 && texD)
1338 bindingData.res[stage].textures.append({ textureBinding + elem, texD->d->tex });
1339 if (samplerBinding >= 0)
1340 bindingData.res[stage].samplers.append({ samplerBinding + elem, samplerD->d->samplerState });
1346 case QRhiShaderResourceBinding::ImageLoad:
1347 case QRhiShaderResourceBinding::ImageStore:
1348 case QRhiShaderResourceBinding::ImageLoadStore:
1350 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1351 id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
1353 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1354 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1355 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Texture);
1356 if (nativeBinding >= 0)
1357 bindingData.res[stage].textures.append({ nativeBinding, t });
1362 case QRhiShaderResourceBinding::BufferLoad:
1363 case QRhiShaderResourceBinding::BufferStore:
1364 case QRhiShaderResourceBinding::BufferLoadStore:
1366 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1367 id<MTLBuffer> mtlbuf = bufD->d->buf[0];
1368 quint32 offset = b->u.sbuf.offset;
1369 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1370 if (b->stage.testFlag(toRhiSrbStage(stage))) {
1371 const int nativeBinding = mapBinding(b->binding, stage, nativeResourceBindingMaps, BindingType::Buffer);
1372 if (nativeBinding >= 0)
1373 bindingData.res[stage].buffers.append({ nativeBinding, mtlbuf, offset });
1384 for (
int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
1385 if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == QMetalShaderResourceBindingsData::VERTEX || stage == QMetalShaderResourceBindingsData::FRAGMENT
1386 || stage == QMetalShaderResourceBindingsData::TESSCTRL || stage == QMetalShaderResourceBindingsData::TESSEVAL))
1388 if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && (stage == QMetalShaderResourceBindingsData::COMPUTE))
1396 std::sort(bindingData.res[stage].buffers.begin(), bindingData.res[stage].buffers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Buffer &a,
const QMetalShaderResourceBindingsData::Stage::Buffer &b) {
1397 return a.nativeBinding < b.nativeBinding;
1400 for (
const QMetalShaderResourceBindingsData::Stage::Buffer &buf : std::as_const(bindingData.res[stage].buffers)) {
1401 bindingData.res[stage].bufferBatches.feed(buf.nativeBinding, buf.mtlbuf);
1402 bindingData.res[stage].bufferOffsetBatches.feed(buf.nativeBinding, buf.offset);
1405 bindingData.res[stage].bufferBatches.finish();
1406 bindingData.res[stage].bufferOffsetBatches.finish();
1408 for (
int i = 0, ie = bindingData.res[stage].bufferBatches.batches.count(); i != ie; ++i) {
1409 const auto &bufferBatch(bindingData.res[stage].bufferBatches.batches[i]);
1410 const auto &offsetBatch(bindingData.res[stage].bufferOffsetBatches.batches[i]);
1412 if (cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches.count() > i
1413 && cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches.count() > i
1414 && bufferBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferBatches.batches[i]
1415 && offsetBatch == cbD->d->currentShaderResourceBindingState.res[stage].bufferOffsetBatches.batches[i])
1419 bindStageBuffers(cbD, stage, bufferBatch, offsetBatch);
1422 if (offsetOnlyChange)
1425 std::sort(bindingData.res[stage].textures.begin(), bindingData.res[stage].textures.end(), [](
const QMetalShaderResourceBindingsData::Stage::Texture &a,
const QMetalShaderResourceBindingsData::Stage::Texture &b) {
1426 return a.nativeBinding < b.nativeBinding;
1429 std::sort(bindingData.res[stage].samplers.begin(), bindingData.res[stage].samplers.end(), [](
const QMetalShaderResourceBindingsData::Stage::Sampler &a,
const QMetalShaderResourceBindingsData::Stage::Sampler &b) {
1430 return a.nativeBinding < b.nativeBinding;
1433 for (
const QMetalShaderResourceBindingsData::Stage::Texture &t : std::as_const(bindingData.res[stage].textures))
1434 bindingData.res[stage].textureBatches.feed(t.nativeBinding, t.mtltex);
1436 for (
const QMetalShaderResourceBindingsData::Stage::Sampler &s : std::as_const(bindingData.res[stage].samplers))
1437 bindingData.res[stage].samplerBatches.feed(s.nativeBinding, s.mtlsampler);
1439 bindingData.res[stage].textureBatches.finish();
1440 bindingData.res[stage].samplerBatches.finish();
1442 for (
int i = 0, ie = bindingData.res[stage].textureBatches.batches.count(); i != ie; ++i) {
1443 const auto &batch(bindingData.res[stage].textureBatches.batches[i]);
1445 if (cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches.count() > i
1446 && batch == cbD->d->currentShaderResourceBindingState.res[stage].textureBatches.batches[i])
1450 bindStageTextures(cbD, stage, batch);
1453 for (
int i = 0, ie = bindingData.res[stage].samplerBatches.batches.count(); i != ie; ++i) {
1454 const auto &batch(bindingData.res[stage].samplerBatches.batches[i]);
1456 if (cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches.count() > i
1457 && batch == cbD->d->currentShaderResourceBindingState.res[stage].samplerBatches.batches[i])
1461 bindStageSamplers(cbD, stage, batch);
1465 cbD->d->currentShaderResourceBindingState = bindingData;
1529void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
1530 int dynamicOffsetCount,
1531 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
1533 QMetalCommandBuffer *cbD =
QRHI_RES(QMetalCommandBuffer, cb);
1534 Q_ASSERT(cbD->recordingPass != QMetalCommandBuffer::NoPass);
1535 QMetalGraphicsPipeline *gfxPsD = cbD->currentGraphicsPipeline;
1536 QMetalComputePipeline *compPsD = cbD->currentComputePipeline;
1540 srb = gfxPsD->m_shaderResourceBindings;
1542 srb = compPsD->m_shaderResourceBindings;
1545 QMetalShaderResourceBindings *srbD =
QRHI_RES(QMetalShaderResourceBindings, srb);
1546 bool hasSlottedResourceInSrb =
false;
1547 bool hasDynamicOffsetInSrb =
false;
1548 bool resNeedsRebind =
false;
1553 const bool needsBufferSizeBuffer = (compPsD && compPsD->d->bufferSizeBuffer) || (gfxPsD && gfxPsD->d->bufferSizeBuffer);
1554 QMap<QRhiShaderResourceBinding::StageFlag, QMap<
int, quint32>> storageBufferSizes;
1557 for (
int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
1558 const QRhiShaderResourceBinding::Data *b = shaderResourceBindingData(srbD->sortedBindings.at(i));
1559 QMetalShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
1561 case QRhiShaderResourceBinding::UniformBuffer:
1563 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
1564 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
1565 executeBufferHostWritesForCurrentFrame(bufD);
1566 if (bufD->d->slotted)
1567 hasSlottedResourceInSrb =
true;
1568 if (b->u.ubuf.hasDynamicOffset)
1569 hasDynamicOffsetInSrb =
true;
1570 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
1571 resNeedsRebind =
true;
1572 bd.ubuf.id = bufD->m_id;
1573 bd.ubuf.generation = bufD->generation;
1575 bufD->lastActiveFrameSlot = currentFrameSlot;
1578 case QRhiShaderResourceBinding::SampledTexture:
1579 case QRhiShaderResourceBinding::Texture:
1580 case QRhiShaderResourceBinding::Sampler:
1582 const QRhiShaderResourceBinding::Data::TextureAndOrSamplerData *data = &b->u.stex;
1583 if (bd.stex.count != data->count) {
1584 bd.stex.count = data->count;
1585 resNeedsRebind =
true;
1587 for (
int elem = 0; elem < data->count; ++elem) {
1588 QMetalTexture *texD =
QRHI_RES(QMetalTexture, data->texSamplers[elem].tex);
1589 QMetalSampler *samplerD =
QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler);
1590 Q_ASSERT(texD || samplerD);
1591 const quint64 texId = texD ? texD->m_id : 0;
1592 const uint texGen = texD ? texD->generation : 0;
1593 const quint64 samplerId = samplerD ? samplerD->m_id : 0;
1594 const uint samplerGen = samplerD ? samplerD->generation : 0;
1595 if (texGen != bd.stex.d[elem].texGeneration
1596 || texId != bd.stex.d[elem].texId
1597 || samplerGen != bd.stex.d[elem].samplerGeneration
1598 || samplerId != bd.stex.d[elem].samplerId)
1600 resNeedsRebind =
true;
1601 bd.stex.d[elem].texId = texId;
1602 bd.stex.d[elem].texGeneration = texGen;
1603 bd.stex.d[elem].samplerId = samplerId;
1604 bd.stex.d[elem].samplerGeneration = samplerGen;
1607 texD->lastActiveFrameSlot = currentFrameSlot;
1609 samplerD->lastActiveFrameSlot = currentFrameSlot;
1613 case QRhiShaderResourceBinding::ImageLoad:
1614 case QRhiShaderResourceBinding::ImageStore:
1615 case QRhiShaderResourceBinding::ImageLoadStore:
1617 QMetalTexture *texD =
QRHI_RES(QMetalTexture, b->u.simage.tex);
1618 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
1619 resNeedsRebind =
true;
1620 bd.simage.id = texD->m_id;
1621 bd.simage.generation = texD->generation;
1623 texD->lastActiveFrameSlot = currentFrameSlot;
1626 case QRhiShaderResourceBinding::BufferLoad:
1627 case QRhiShaderResourceBinding::BufferStore:
1628 case QRhiShaderResourceBinding::BufferLoadStore:
1630 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
1631 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
1633 if (needsBufferSizeBuffer) {
1634 for (
int i = 0; i < 6; ++i) {
1635 const QRhiShaderResourceBinding::StageFlag stage =
1636 QRhiShaderResourceBinding::StageFlag(1 << i);
1637 if (b->stage.testFlag(stage)) {
1638 storageBufferSizes[stage][b->binding] = b->u.sbuf.maybeSize ? b->u.sbuf.maybeSize : bufD->size();
1643 executeBufferHostWritesForCurrentFrame(bufD);
1644 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
1645 resNeedsRebind =
true;
1646 bd.sbuf.id = bufD->m_id;
1647 bd.sbuf.generation = bufD->generation;
1649 bufD->lastActiveFrameSlot = currentFrameSlot;
1658 if (needsBufferSizeBuffer) {
1659 QMetalBuffer *bufD =
nullptr;
1660 QVarLengthArray<QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders;
1663 bufD = compPsD->d->bufferSizeBuffer;
1664 Q_ASSERT(compPsD->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1665 shaders.append(qMakePair(&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage));
1667 bufD = gfxPsD->d->bufferSizeBuffer;
1668 if (gfxPsD->d->tess.enabled) {
1678 Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[1].desc.storageBlocks());
1679 Q_ASSERT(gfxPsD->d->tess.compVs[0].desc.storageBlocks() == gfxPsD->d->tess.compVs[2].desc.storageBlocks());
1680 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[1].nativeResourceBindingMap);
1681 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[2].nativeResourceBindingMap);
1682 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1683 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1684 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)
1685 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding));
1686 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1687 == gfxPsD->d->tess.compVs[1].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1688 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]
1689 == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]);
1691 if (gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1692 shaders.append(qMakePair(&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage));
1694 if (gfxPsD->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1695 shaders.append(qMakePair(&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage));
1697 if (gfxPsD->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1698 shaders.append(qMakePair(&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage));
1701 if (gfxPsD->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1702 shaders.append(qMakePair(&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage));
1704 if (gfxPsD->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding))
1705 shaders.append(qMakePair(&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage));
1709 for (
const QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag> &shader : shaders) {
1711 const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
1714 if (!(storageBufferSizes.contains(shader.second) && storageBufferSizes[shader.second].contains(binding))) {
1716 int maxNativeBinding = 0;
1717 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks())
1718 maxNativeBinding = qMax(maxNativeBinding, shader.first->nativeResourceBindingMap[block.binding].first);
1720 const int size = (maxNativeBinding + 1) *
sizeof(
int);
1722 Q_ASSERT(offset + size <= bufD->size());
1723 srbD->sortedBindings.append(QRhiShaderResourceBinding::bufferLoad(binding, shader.second, bufD, offset, size));
1725 QMetalShaderResourceBindings::BoundResourceData bd;
1726 bd.sbuf.id = bufD->m_id;
1727 bd.sbuf.generation = bufD->generation;
1728 srbD->boundResourceData.append(bd);
1732 QVarLengthArray<
int, 8> bufferSizeBufferData;
1733 Q_ASSERT(storageBufferSizes.contains(shader.second));
1734 const QMap<
int, quint32> &sizes(storageBufferSizes[shader.second]);
1735 for (
const QShaderDescription::StorageBlock &block : shader.first->desc.storageBlocks()) {
1736 const int index = shader.first->nativeResourceBindingMap[block.binding].first;
1742 if (bufferSizeBufferData.size() <= index)
1743 bufferSizeBufferData.resize(index + 1);
1745 Q_ASSERT(sizes.contains(block.binding));
1746 bufferSizeBufferData[index] = sizes[block.binding];
1749 QRhiBufferData data;
1750 const quint32 size = bufferSizeBufferData.size() *
sizeof(
int);
1751 data.assign(
reinterpret_cast<
const char *>(bufferSizeBufferData.constData()), size);
1752 Q_ASSERT(offset + size <= bufD->size());
1753 bufD->d->pendingUpdates[bufD->d->slotted ? currentFrameSlot : 0].append({ offset, data });
1756 offset += ((size + 31) / 32) * 32;
1759 executeBufferHostWritesForCurrentFrame(bufD);
1760 bufD->lastActiveFrameSlot = currentFrameSlot;
1764 const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
1765 if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot)
1766 resNeedsRebind =
true;
1768 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srbD) : (cbD->currentComputeSrb != srbD);
1769 const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
1772 if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
1773 const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = {
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
1775 cbD->currentGraphicsSrb = srbD;
1776 cbD->currentComputeSrb =
nullptr;
1777 if (gfxPsD->d->tess.enabled) {
1780 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[1].nativeResourceBindingMap);
1781 Q_ASSERT(gfxPsD->d->tess.compVs[0].nativeResourceBindingMap == gfxPsD->d->tess.compVs[2].nativeResourceBindingMap);
1782 resBindMaps[QMetalShaderResourceBindingsData::VERTEX] = &gfxPsD->d->tess.compVs[0].nativeResourceBindingMap;
1783 resBindMaps[QMetalShaderResourceBindingsData::TESSCTRL] = &gfxPsD->d->tess.compTesc.nativeResourceBindingMap;
1784 resBindMaps[QMetalShaderResourceBindingsData::TESSEVAL] = &gfxPsD->d->tess.vertTese.nativeResourceBindingMap;
1786 resBindMaps[QMetalShaderResourceBindingsData::VERTEX] = &gfxPsD->d->vs.nativeResourceBindingMap;
1788 resBindMaps[QMetalShaderResourceBindingsData::FRAGMENT] = &gfxPsD->d->fs.nativeResourceBindingMap;
1790 cbD->currentGraphicsSrb =
nullptr;
1791 cbD->currentComputeSrb = srbD;
1792 resBindMaps[QMetalShaderResourceBindingsData::COMPUTE] = &compPsD->d->cs.nativeResourceBindingMap;
1794 cbD->currentSrbGeneration = srbD->generation;
1795 cbD->currentResSlot = resSlot;
1797 const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
1798 enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
1802void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb,
1803 int startBinding,
int bindingCount,
const QRhiCommandBuffer::VertexInput *bindings,
1804 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
1806 QMetalCommandBuffer *cbD =
QRHI_RES(QMetalCommandBuffer, cb);
1807 Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
1809 QRhiBatchedBindings<id<MTLBuffer> > buffers;
1810 QRhiBatchedBindings<NSUInteger> offsets;
1811 for (
int i = 0; i < bindingCount; ++i) {
1812 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, bindings[i].first);
1813 executeBufferHostWritesForCurrentFrame(bufD);
1814 bufD->lastActiveFrameSlot = currentFrameSlot;
1815 id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
1816 buffers.feed(startBinding + i, mtlbuf);
1817 offsets.feed(startBinding + i, bindings[i].second);
1823 QMetalShaderResourceBindings *srbD = cbD->currentGraphicsSrb;
1828 srbD =
QRHI_RES(QMetalShaderResourceBindings, cbD->currentGraphicsPipeline->shaderResourceBindings());
1829 const int firstVertexBinding = srbD->maxBinding + 1;
1831 if (firstVertexBinding != cbD->d->currentFirstVertexBinding
1832 || buffers != cbD->d->currentVertexInputsBuffers
1833 || offsets != cbD->d->currentVertexInputOffsets)
1835 cbD->d->currentFirstVertexBinding = firstVertexBinding;
1836 cbD->d->currentVertexInputsBuffers = buffers;
1837 cbD->d->currentVertexInputOffsets = offsets;
1839 for (
int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
1840 const auto &bufferBatch(buffers.batches[i]);
1841 const auto &offsetBatch(offsets.batches[i]);
1842 [cbD->d->currentRenderPassEncoder setVertexBuffers:
1843 bufferBatch.resources.constData()
1844 offsets: offsetBatch.resources.constData()
1845 withRange: NSMakeRange(uint(firstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
1850 QMetalBuffer *ibufD =
QRHI_RES(QMetalBuffer, indexBuf);
1851 executeBufferHostWritesForCurrentFrame(ibufD);
1852 ibufD->lastActiveFrameSlot = currentFrameSlot;
1853 cbD->currentIndexBuffer = ibufD;
1854 cbD->currentIndexOffset = indexOffset;
1855 cbD->currentIndexFormat = indexFormat;
1857 cbD->currentIndexBuffer =
nullptr;
2020void QRhiMetal::tessellatedDraw(
const TessDrawArgs &args)
2022 QMetalCommandBuffer *cbD = args.cbD;
2023 QMetalGraphicsPipeline *graphicsPipeline = cbD->currentGraphicsPipeline;
2024 if (graphicsPipeline->d->tess.failed)
2027 const bool indexed = args.type != TessDrawArgs::NonIndexed;
2028 const quint32 instanceCount = indexed ? args.drawIndexed.instanceCount : args.draw.instanceCount;
2029 const quint32 vertexOrIndexCount = indexed ? args.drawIndexed.indexCount : args.draw.vertexCount;
2031 QMetalGraphicsPipelineData::Tessellation &tess(graphicsPipeline->d->tess);
2032 QMetalGraphicsPipelineData::ExtraBufferManager &extraBufMgr(graphicsPipeline->d->extraBufMgr);
2033 const quint32 patchCount = tess.patchCountForDrawCall(vertexOrIndexCount, instanceCount);
2034 QMetalBuffer *vertOutBuf =
nullptr;
2035 QMetalBuffer *tescOutBuf =
nullptr;
2036 QMetalBuffer *tescPatchOutBuf =
nullptr;
2037 QMetalBuffer *tescFactorBuf =
nullptr;
2038 QMetalBuffer *tescParamsBuf =
nullptr;
2039 id<MTLComputeCommandEncoder> vertTescComputeEncoder = tessellationComputeEncoder(cbD);
2043 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2044 QShader::Variant shaderVariant = QShader::NonIndexedVertexAsComputeShader;
2045 if (args.type == TessDrawArgs::U16Indexed)
2046 shaderVariant = QShader::UInt16IndexedVertexAsComputeShader;
2047 else if (args.type == TessDrawArgs::U32Indexed)
2048 shaderVariant = QShader::UInt32IndexedVertexAsComputeShader;
2049 const int varIndex = QMetalGraphicsPipelineData::Tessellation::vsCompVariantToIndex(shaderVariant);
2050 id<MTLComputePipelineState> computePipelineState = tess.vsCompPipeline(
this, shaderVariant);
2051 [computeEncoder setComputePipelineState: computePipelineState];
2056 cbD->d->currentComputePassEncoder = computeEncoder;
2057 rebindShaderResources(cbD, QMetalShaderResourceBindingsData::VERTEX, QMetalShaderResourceBindingsData::COMPUTE);
2058 cbD->d->currentComputePassEncoder = nil;
2060 const QMap<
int,
int> &ebb(tess.compVs[varIndex].nativeShaderInfo.extraBufferBindings);
2061 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2062 const int indexBufferBinding = ebb.value(QShaderPrivate::MslTessVertIndicesBufferBinding, -1);
2064 if (outputBufferBinding >= 0) {
2065 const quint32 workBufSize = tess.vsCompOutputBufferSize(vertexOrIndexCount, instanceCount);
2066 vertOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2069 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2072 if (indexBufferBinding >= 0)
2073 [computeEncoder setBuffer: (id<MTLBuffer>) args.drawIndexed.indexBuffer offset: 0 atIndex: indexBufferBinding];
2075 for (
int i = 0, ie = cbD->d->currentVertexInputsBuffers.batches.count(); i != ie; ++i) {
2076 const auto &bufferBatch(cbD->d->currentVertexInputsBuffers.batches[i]);
2077 const auto &offsetBatch(cbD->d->currentVertexInputOffsets.batches[i]);
2078 [computeEncoder setBuffers: bufferBatch.resources.constData()
2079 offsets: offsetBatch.resources.constData()
2080 withRange: NSMakeRange(uint(cbD->d->currentFirstVertexBinding) + bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
2084 [computeEncoder setStageInRegion: MTLRegionMake2D(args.drawIndexed.vertexOffset, args.drawIndexed.firstInstance,
2085 args.drawIndexed.indexCount, args.drawIndexed.instanceCount)];
2087 [computeEncoder setStageInRegion: MTLRegionMake2D(args.draw.firstVertex, args.draw.firstInstance,
2088 args.draw.vertexCount, args.draw.instanceCount)];
2091 [computeEncoder dispatchThreads: MTLSizeMake(vertexOrIndexCount, instanceCount, 1)
2092 threadsPerThreadgroup: MTLSizeMake(computePipelineState.threadExecutionWidth, 1, 1)];
2097 id<MTLComputeCommandEncoder> computeEncoder = vertTescComputeEncoder;
2098 id<MTLComputePipelineState> computePipelineState = tess.tescCompPipeline(
this);
2099 [computeEncoder setComputePipelineState: computePipelineState];
2101 cbD->d->currentComputePassEncoder = computeEncoder;
2102 rebindShaderResources(cbD, QMetalShaderResourceBindingsData::TESSCTRL, QMetalShaderResourceBindingsData::COMPUTE);
2103 cbD->d->currentComputePassEncoder = nil;
2105 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2106 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2107 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2108 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2109 const int paramsBufferBinding = ebb.value(QShaderPrivate::MslTessTescParamsBufferBinding, -1);
2110 const int inputBufferBinding = ebb.value(QShaderPrivate::MslTessTescInputBufferBinding, -1);
2112 if (outputBufferBinding >= 0) {
2113 const quint32 workBufSize = tess.tescCompOutputBufferSize(patchCount);
2114 tescOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2117 [computeEncoder setBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2120 if (patchOutputBufferBinding >= 0) {
2121 const quint32 workBufSize = tess.tescCompPatchOutputBufferSize(patchCount);
2122 tescPatchOutBuf = extraBufMgr.acquireWorkBuffer(
this, workBufSize);
2123 if (!tescPatchOutBuf)
2125 [computeEncoder setBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2128 if (tessFactorBufferBinding >= 0) {
2129 tescFactorBuf = extraBufMgr.acquireWorkBuffer(
this, patchCount *
sizeof(MTLQuadTessellationFactorsHalf));
2130 [computeEncoder setBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2133 if (paramsBufferBinding >= 0) {
2135 quint32 inControlPointCount;
2138 tescParamsBuf = extraBufMgr.acquireWorkBuffer(
this,
sizeof(params), QMetalGraphicsPipelineData::ExtraBufferManager::WorkBufType::HostVisible);
2141 params.inControlPointCount = tess.inControlPointCount;
2142 params.patchCount = patchCount;
2143 id<MTLBuffer> paramsBuf = tescParamsBuf->d->buf[0];
2144 char *p =
reinterpret_cast<
char *>([paramsBuf contents]);
2145 memcpy(p, ¶ms,
sizeof(params));
2146 [computeEncoder setBuffer: paramsBuf offset: 0 atIndex: paramsBufferBinding];
2149 if (vertOutBuf && inputBufferBinding >= 0)
2150 [computeEncoder setBuffer: vertOutBuf->d->buf[0] offset: 0 atIndex: inputBufferBinding];
2152 int sgSize =
int(computePipelineState.threadExecutionWidth);
2153 int wgSize = std::lcm(tess.outControlPointCount, sgSize);
2154 while (wgSize > caps.maxThreadGroupSize) {
2156 wgSize = std::lcm(tess.outControlPointCount, sgSize);
2158 [computeEncoder dispatchThreads: MTLSizeMake(patchCount * tess.outControlPointCount, 1, 1)
2159 threadsPerThreadgroup: MTLSizeMake(wgSize, 1, 1)];
2165 const QMetalShaderResourceBindingsData resourceBindings = cbD->d->currentShaderResourceBindingState;
2167 endTessellationComputeEncoding(cbD);
2175 graphicsPipeline->makeActiveForCurrentRenderPassEncoder(cbD);
2176 id<MTLRenderCommandEncoder> renderEncoder = cbD->d->currentRenderPassEncoder;
2178 rebindShaderResources(cbD, QMetalShaderResourceBindingsData::TESSEVAL, QMetalShaderResourceBindingsData::VERTEX, &resourceBindings);
2179 rebindShaderResources(cbD, QMetalShaderResourceBindingsData::FRAGMENT, QMetalShaderResourceBindingsData::FRAGMENT, &resourceBindings);
2181 const QMap<
int,
int> &ebb(tess.compTesc.nativeShaderInfo.extraBufferBindings);
2182 const int outputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
2183 const int patchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
2184 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
2186 if (outputBufferBinding >= 0 && tescOutBuf)
2187 [renderEncoder setVertexBuffer: tescOutBuf->d->buf[0] offset: 0 atIndex: outputBufferBinding];
2189 if (patchOutputBufferBinding >= 0 && tescPatchOutBuf)
2190 [renderEncoder setVertexBuffer: tescPatchOutBuf->d->buf[0] offset: 0 atIndex: patchOutputBufferBinding];
2192 if (tessFactorBufferBinding >= 0 && tescFactorBuf) {
2193 [renderEncoder setTessellationFactorBuffer: tescFactorBuf->d->buf[0] offset: 0 instanceStride: 0];
2194 [renderEncoder setVertexBuffer: tescFactorBuf->d->buf[0] offset: 0 atIndex: tessFactorBufferBinding];
2197 [cbD->d->currentRenderPassEncoder drawPatches: tess.outControlPointCount
2199 patchCount: patchCount
2200 patchIndexBuffer: nil
2201 patchIndexBufferOffset: 0
2269void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
2270 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
2272 QMetalCommandBuffer *cbD =
QRHI_RES(QMetalCommandBuffer, cb);
2273 Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
2275 if (!cbD->currentIndexBuffer)
2278 const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
2279 Q_ASSERT(indexOffset == aligned(indexOffset, 4u));
2281 QMetalBuffer *ibufD = cbD->currentIndexBuffer;
2282 id<MTLBuffer> mtlibuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
2284 if (cbD->currentGraphicsPipeline->d->tess.enabled) {
2287 a.type = cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? TessDrawArgs::U16Indexed : TessDrawArgs::U32Indexed;
2288 a.drawIndexed.indexCount = indexCount;
2289 a.drawIndexed.instanceCount = instanceCount;
2290 a.drawIndexed.firstIndex = firstIndex;
2291 a.drawIndexed.vertexOffset = vertexOffset;
2292 a.drawIndexed.firstInstance = firstInstance;
2293 a.drawIndexed.indexBuffer = mtlibuf;
2298 adjustForMultiViewDraw(&instanceCount, cb);
2300 if (caps.baseVertexAndInstance) {
2301 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2302 indexCount: indexCount
2303 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2304 indexBuffer: mtlibuf
2305 indexBufferOffset: indexOffset
2306 instanceCount: instanceCount
2307 baseVertex: vertexOffset
2308 baseInstance: firstInstance];
2310 [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: cbD->currentGraphicsPipeline->d->primitiveType
2311 indexCount: indexCount
2312 indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
2313 indexBuffer: mtlibuf
2314 indexBufferOffset: indexOffset
2315 instanceCount: instanceCount];
2429QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
2431 QMetalSwapChain *swapChainD =
QRHI_RES(QMetalSwapChain, swapChain);
2432 Q_ASSERT(currentSwapChain == swapChainD);
2435 id<MTLCommandBuffer> commandBuffer = swapChainD->cbWrapper.d->cb;
2437 __block
int thisFrameSlot = currentFrameSlot;
2438 [commandBuffer addCompletedHandler: ^(id<MTLCommandBuffer> cb) {
2439 swapChainD->d->lastGpuTime[thisFrameSlot] += cb.GPUEndTime - cb.GPUStartTime;
2440 dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
2447 id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
2448 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>) {
2449 [drawableTexture release];
2453 if (flags.testFlag(QRhi::SkipPresent)) {
2455 [commandBuffer commit];
2457 if (id<CAMetalDrawable> drawable = swapChainD->d->curDrawable) {
2459 if (swapChainD->d->layer.presentsWithTransaction) {
2460 [commandBuffer commit];
2462 auto *metalLayer = swapChainD->d->layer;
2463 auto presentWithTransaction = ^{
2464 [commandBuffer waitUntilScheduled];
2471 const auto surfaceSize = QSizeF::fromCGSize(metalLayer.bounds.size) * metalLayer.contentsScale;
2472 const auto textureSize = QSizeF(drawable.texture.width, drawable.texture.height);
2473 if (textureSize == surfaceSize) {
2476 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable <<
"due to texture size"
2477 << textureSize <<
"not matching surface size" << surfaceSize;
2481 if (NSThread.currentThread == NSThread.mainThread) {
2482 presentWithTransaction();
2484 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2485 Q_ASSERT(qtMetalLayer);
2487 qtMetalLayer.mainThreadPresentation = presentWithTransaction;
2491 auto *qtMetalLayer = qt_objc_cast<QMetalLayer*>(swapChainD->d->layer);
2492 [commandBuffer addScheduledHandler:^(id<MTLCommandBuffer>) {
2498 if (qtMetalLayer.displayLock.tryLockForRead()) {
2500 qtMetalLayer.displayLock.unlock();
2502 qCDebug(QRHI_LOG_INFO) <<
"Skipping" << drawable
2503 <<
"due to" << qtMetalLayer <<
"needing display";
2509 [commandBuffer commit];
2513 [commandBuffer commit];
2516 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
2520 [swapChainD->d->curDrawable release];
2521 swapChainD->d->curDrawable = nil;
2523 [d->captureScope endScope];
2525 swapChainD->frameCount += 1;
2526 currentSwapChain =
nullptr;
2527 return QRhi::FrameOpSuccess;
2661void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD,
void *mp,
void *blitEncPtr,
2662 int layer,
int level,
const QRhiTextureSubresourceUploadDescription &subresDesc,
2665 const QPoint dp = subresDesc.destinationTopLeft();
2666 const QByteArray rawData = subresDesc.data();
2667 QImage img = subresDesc.image();
2668 const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2669 id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
2671 if (!img.isNull()) {
2672 const qsizetype fullImageSizeBytes = img.sizeInBytes();
2673 int w = img.width();
2674 int h = img.height();
2675 int bpl = img.bytesPerLine();
2677 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2678 const int sx = subresDesc.sourceTopLeft().x();
2679 const int sy = subresDesc.sourceTopLeft().y();
2680 if (!subresDesc.sourceSize().isEmpty()) {
2681 w = subresDesc.sourceSize().width();
2682 h = subresDesc.sourceSize().height();
2684 if (w == img.width()) {
2685 const int bpc = qMax(1, img.depth() / 8);
2686 Q_ASSERT(h * img.bytesPerLine() <= fullImageSizeBytes);
2687 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs,
2688 img.constBits() + sy * img.bytesPerLine() + sx * bpc,
2689 h * img.bytesPerLine());
2691 img = img.copy(sx, sy, w, h);
2692 bpl = img.bytesPerLine();
2693 Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
2694 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(img.sizeInBytes()));
2697 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
2700 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2701 sourceOffset: NSUInteger(*curOfs)
2702 sourceBytesPerRow: NSUInteger(bpl)
2703 sourceBytesPerImage: 0
2704 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2705 toTexture: texD->d->tex
2706 destinationSlice: NSUInteger(is3D ? 0 : layer)
2707 destinationLevel: NSUInteger(level)
2708 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2709 options: MTLBlitOptionNone];
2711 *curOfs += aligned<qsizetype>(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
2712 }
else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2713 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2714 const int subresw = subresSize.width();
2715 const int subresh = subresSize.height();
2717 if (subresDesc.sourceSize().isEmpty()) {
2721 w = subresDesc.sourceSize().width();
2722 h = subresDesc.sourceSize().height();
2727 compressedFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr, &blockDim);
2729 const int dx = aligned(dp.x(), blockDim.width());
2730 const int dy = aligned(dp.y(), blockDim.height());
2731 if (dx + w != subresw)
2732 w = aligned(w, blockDim.width());
2733 if (dy + h != subresh)
2734 h = aligned(h, blockDim.height());
2736 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2738 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2739 sourceOffset: NSUInteger(*curOfs)
2740 sourceBytesPerRow: bpl
2741 sourceBytesPerImage: 0
2742 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2743 toTexture: texD->d->tex
2744 destinationSlice: NSUInteger(is3D ? 0 : layer)
2745 destinationLevel: NSUInteger(level)
2746 destinationOrigin: MTLOriginMake(NSUInteger(dx), NSUInteger(dy), NSUInteger(is3D ? layer : 0))
2747 options: MTLBlitOptionNone];
2749 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2750 }
else if (!rawData.isEmpty()) {
2751 const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
2752 const int subresw = subresSize.width();
2753 const int subresh = subresSize.height();
2755 if (subresDesc.sourceSize().isEmpty()) {
2759 w = subresDesc.sourceSize().width();
2760 h = subresDesc.sourceSize().height();
2764 if (subresDesc.dataStride())
2765 bpl = subresDesc.dataStride();
2767 textureFormatInfo(texD->m_format, QSize(w, h), &bpl,
nullptr,
nullptr);
2769 memcpy(
reinterpret_cast<
char *>(mp) + *curOfs, rawData.constData(), size_t(rawData.size()));
2771 [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
2772 sourceOffset: NSUInteger(*curOfs)
2773 sourceBytesPerRow: bpl
2774 sourceBytesPerImage: 0
2775 sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
2776 toTexture: texD->d->tex
2777 destinationSlice: NSUInteger(is3D ? 0 : layer)
2778 destinationLevel: NSUInteger(level)
2779 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(is3D ? layer : 0))
2780 options: MTLBlitOptionNone];
2782 *curOfs += aligned<qsizetype>(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
2784 qWarning(
"Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2788void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2790 QMetalCommandBuffer *cbD =
QRHI_RES(QMetalCommandBuffer, cb);
2791 QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
2793 id<MTLBlitCommandEncoder> blitEnc = nil;
2794 auto ensureBlit = [&blitEnc, cbD,
this]() {
2796 blitEnc = [cbD->d->cb blitCommandEncoder];
2798 [blitEnc pushDebugGroup: @
"Texture upload/copy"];
2802 for (
int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
2803 const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
2804 if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::DynamicUpdate) {
2805 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, u.buf);
2806 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2807 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
2808 if (u.offset == 0 && u.data.size() == bufD->m_size)
2809 bufD->d->pendingUpdates[i].clear();
2810 bufD->d->pendingUpdates[i].append({ u.offset, u.data });
2812 }
else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::StaticUpload) {
2815 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, u.buf);
2816 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2817 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
2818 for (
int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i)
2819 bufD->d->pendingUpdates[i].append({ u.offset, u.data });
2820 }
else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
2821 QMetalBuffer *bufD =
QRHI_RES(QMetalBuffer, u.buf);
2822 executeBufferHostWritesForCurrentFrame(bufD);
2823 const int idx = bufD->d->slotted ? currentFrameSlot : 0;
2824 if (bufD->m_type == QRhiBuffer::Dynamic) {
2825 char *p =
reinterpret_cast<
char *>([bufD->d->buf[idx] contents]);
2827 u.result->data.resize(u.readSize);
2828 memcpy(u.result->data.data(), p + u.offset, size_t(u.readSize));
2830 if (u.result->completed)
2831 u.result->completed();
2833 QRhiMetalData::BufferReadback readback;
2834 readback.activeFrameSlot = idx;
2835 readback.buf = bufD->d->buf[idx];
2836 readback.offset = u.offset;
2837 readback.readSize = u.readSize;
2838 readback.result = u.result;
2839 d->activeBufferReadbacks.append(readback);
2841 if (bufD->d->managed) {
2844 [blitEnc synchronizeResource:readback.buf];
2851 for (
int opIdx = 0; opIdx < ud->activeTextureOpCount; ++opIdx) {
2852 const QRhiResourceUpdateBatchPrivate::TextureOp &u(ud->textureOps[opIdx]);
2853 if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
2854 QMetalTexture *utexD =
QRHI_RES(QMetalTexture, u.dst);
2855 qsizetype stagingSize = 0;
2856 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2857 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2858 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2859 stagingSize += subresUploadByteSize(subresDesc);
2864 Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
2865 utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
2866 options: MTLResourceStorageModeShared];
2868 void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
2869 qsizetype curOfs = 0;
2870 for (
int layer = 0, maxLayer = u.subresDesc.count(); layer < maxLayer; ++layer) {
2871 for (
int level = 0; level < QRhi::MAX_MIP_LEVELS; ++level) {
2872 for (
const QRhiTextureSubresourceUploadDescription &subresDesc : std::as_const(u.subresDesc[layer][level]))
2873 enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
2877 utexD->lastActiveFrameSlot = currentFrameSlot;
2879 QRhiMetalData::DeferredReleaseEntry e;
2880 e.type = QRhiMetalData::DeferredReleaseEntry::StagingBuffer;
2881 e.lastActiveFrameSlot = currentFrameSlot;
2882 e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
2883 utexD->d->stagingBuf[currentFrameSlot] = nil;
2884 d->releaseQueue.append(e);
2885 }
else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
2886 Q_ASSERT(u.src && u.dst);
2887 QMetalTexture *srcD =
QRHI_RES(QMetalTexture, u.src);
2888 QMetalTexture *dstD =
QRHI_RES(QMetalTexture, u.dst);
2889 const bool srcIs3D = srcD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2890 const bool dstIs3D = dstD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2891 const QPoint dp = u.desc.destinationTopLeft();
2892 const QSize mipSize = q->sizeForMipLevel(u.desc.sourceLevel(), srcD->m_pixelSize);
2893 const QSize copySize = u.desc.pixelSize().isEmpty() ? mipSize : u.desc.pixelSize();
2894 const QPoint sp = u.desc.sourceTopLeft();
2897 [blitEnc copyFromTexture: srcD->d->tex
2898 sourceSlice: NSUInteger(srcIs3D ? 0 : u.desc.sourceLayer())
2899 sourceLevel: NSUInteger(u.desc.sourceLevel())
2900 sourceOrigin: MTLOriginMake(NSUInteger(sp.x()), NSUInteger(sp.y()), NSUInteger(srcIs3D ? u.desc.sourceLayer() : 0))
2901 sourceSize: MTLSizeMake(NSUInteger(copySize.width()), NSUInteger(copySize.height()), 1)
2902 toTexture: dstD->d->tex
2903 destinationSlice: NSUInteger(dstIs3D ? 0 : u.desc.destinationLayer())
2904 destinationLevel: NSUInteger(u.desc.destinationLevel())
2905 destinationOrigin: MTLOriginMake(NSUInteger(dp.x()), NSUInteger(dp.y()), NSUInteger(dstIs3D ? u.desc.destinationLayer() : 0))];
2907 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2908 }
else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
2909 QRhiMetalData::TextureReadback readback;
2910 readback.activeFrameSlot = currentFrameSlot;
2911 readback.desc = u.rb;
2912 readback.result = u.result;
2914 QMetalTexture *texD =
QRHI_RES(QMetalTexture, u.rb.texture());
2915 QMetalSwapChain *swapChainD =
nullptr;
2920 if (texD->samples > 1) {
2921 qWarning(
"Multisample texture cannot be read back");
2924 is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
2925 readback.pixelSize = q->sizeForMipLevel(u.rb.level(), texD->m_pixelSize);
2926 readback.format = texD->m_format;
2928 srcSize = readback.pixelSize;
2929 texD->lastActiveFrameSlot = currentFrameSlot;
2931 Q_ASSERT(currentSwapChain);
2932 swapChainD =
QRHI_RES(QMetalSwapChain, currentSwapChain);
2933 readback.pixelSize = swapChainD->pixelSize;
2934 readback.format = swapChainD->d->rhiColorFormat;
2937 const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]);
2938 src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
2939 srcSize = swapChainD->rtWrapper.d->pixelSize;
2943 textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize,
nullptr);
2944 readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
2947 [blitEnc copyFromTexture: src
2948 sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
2949 sourceLevel: NSUInteger(u.rb.level())
2950 sourceOrigin: MTLOriginMake(0, 0, is3D ? u.rb.layer() : 0)
2951 sourceSize: MTLSizeMake(NSUInteger(srcSize.width()), NSUInteger(srcSize.height()), 1)
2952 toBuffer: readback.buf
2953 destinationOffset: 0
2954 destinationBytesPerRow: bpl
2955 destinationBytesPerImage: 0
2956 options: MTLBlitOptionNone];
2958 d->activeTextureReadbacks.append(readback);
2959 }
else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::GenMips) {
2960 QMetalTexture *utexD =
QRHI_RES(QMetalTexture, u.dst);
2962 [blitEnc generateMipmapsForTexture: utexD->d->tex];
2963 utexD->lastActiveFrameSlot = currentFrameSlot;
2969 [blitEnc popDebugGroup];
2970 [blitEnc endEncoding];
3012void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
3013 QRhiRenderTarget *rt,
3014 const QColor &colorClearValue,
3015 const QRhiDepthStencilClearValue &depthStencilClearValue,
3016 QRhiResourceUpdateBatch *resourceUpdates,
3017 QRhiCommandBuffer::BeginPassFlags)
3019 QMetalCommandBuffer *cbD =
QRHI_RES(QMetalCommandBuffer, cb);
3020 Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::NoPass);
3022 if (resourceUpdates)
3023 enqueueResourceUpdates(cb, resourceUpdates);
3025 QMetalRenderTargetData *rtD =
nullptr;
3026 switch (rt->resourceType()) {
3027 case QRhiResource::SwapChainRenderTarget:
3029 QMetalSwapChainRenderTarget *rtSc =
QRHI_RES(QMetalSwapChainRenderTarget, rt);
3031 QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
3032 cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount,
3034 depthStencilClearValue,
3037 if (rtD->colorAttCount) {
3038 QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
3039 if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
3040 Q_ASSERT(currentSwapChain);
3041 QMetalSwapChain *swapChainD =
QRHI_RES(QMetalSwapChain, currentSwapChain);
3042 if (!swapChainD->d->curDrawable) {
3043 QMacAutoReleasePool pool;
3044 swapChainD->d->curDrawable = [[swapChainD->d->layer nextDrawable] retain];
3046 if (!swapChainD->d->curDrawable) {
3047 qWarning(
"No drawable");
3050 id<MTLTexture> scTex = swapChainD->d->curDrawable.texture;
3051 if (color0.needsDrawableForTex) {
3053 color0.needsDrawableForTex =
false;
3055 color0.resolveTex = scTex;
3056 color0.needsDrawableForResolveTex =
false;
3061 QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
3064 case QRhiResource::TextureRenderTarget:
3066 QMetalTextureRenderTarget *rtTex =
QRHI_RES(QMetalTextureRenderTarget, rt);
3068 if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
3070 cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount,
3072 depthStencilClearValue,
3074 rtTex->m_desc.shadingRateMap());
3075 if (rtD->fb.preserveColor) {
3076 for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
3077 cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
3079 if (rtD->dsAttCount && rtD->fb.preserveDs) {
3080 cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
3081 cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
3083 int colorAttCount = 0;
3084 for (
auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
3088 if (it->texture()) {
3089 QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
3090 if (it->multiViewCount() >= 2)
3091 cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(it->multiViewCount());
3092 }
else if (it->renderBuffer()) {
3093 QRHI_RES(QMetalRenderBuffer, it->renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
3095 if (it->resolveTexture())
3096 QRHI_RES(QMetalTexture, it->resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3098 if (rtTex->m_desc.depthStencilBuffer())
3099 QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
3100 if (rtTex->m_desc.depthTexture()) {
3101 QMetalTexture *depthTexture =
QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture());
3102 depthTexture->lastActiveFrameSlot = currentFrameSlot;
3103 if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
3104 cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
3106 if (rtTex->m_desc.depthResolveTexture())
3107 QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
3108 if (rtTex->m_desc.shadingRateMap())
3109 QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
3117 for (uint i = 0; i < uint(rtD->colorAttCount); ++i) {
3118 cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
3119 cbD->d->currentPassRpDesc.colorAttachments[i].slice = NSUInteger(rtD->fb.colorAtt[i].arrayLayer);
3120 cbD->d->currentPassRpDesc.colorAttachments[i].depthPlane = NSUInteger(rtD->fb.colorAtt[i].slice);
3121 cbD->d->currentPassRpDesc.colorAttachments[i].level = NSUInteger(rtD->fb.colorAtt[i].level);
3122 if (rtD->fb.colorAtt[i].resolveTex) {
3123 cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = rtD->fb.preserveColor ? MTLStoreActionStoreAndMultisampleResolve
3124 : MTLStoreActionMultisampleResolve;
3125 cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
3126 cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = NSUInteger(rtD->fb.colorAtt[i].resolveLayer);
3127 cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = NSUInteger(rtD->fb.colorAtt[i].resolveLevel);
3131 if (rtD->dsAttCount) {
3132 Q_ASSERT(rtD->fb.dsTex);
3133 cbD->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
3134 cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
3135 if (rtD->fb.depthNeedsStore)
3136 cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
3137 if (rtD->fb.dsResolveTex) {
3138 cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
3139 : MTLStoreActionMultisampleResolve;
3140 cbD->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
3141 if (rtD->fb.hasStencil) {
3142 cbD->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
3143 cbD->d->currentPassRpDesc.stencilAttachment.storeAction = cbD->d->currentPassRpDesc.depthAttachment.storeAction;
3148 cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
3150 cbD->resetPerPassState();
3152 cbD->recordingPass = QMetalCommandBuffer::RenderPass;
3153 cbD->currentTarget = rt;
3464static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags,
const QRhiMetal *d)
3470 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
3472 case QRhiTexture::RGBA8:
3473 return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
3474 case QRhiTexture::BGRA8:
3475 return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
3476 case QRhiTexture::R8:
3478 return MTLPixelFormatR8Unorm;
3480 return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
3482 case QRhiTexture::R8SI:
3483 return MTLPixelFormatR8Sint;
3484 case QRhiTexture::R8UI:
3485 return MTLPixelFormatR8Uint;
3486 case QRhiTexture::RG8:
3488 return MTLPixelFormatRG8Unorm;
3490 return srgb ? MTLPixelFormatRG8Unorm_sRGB : MTLPixelFormatRG8Unorm;
3492 case QRhiTexture::R16:
3493 return MTLPixelFormatR16Unorm;
3494 case QRhiTexture::RG16:
3495 return MTLPixelFormatRG16Unorm;
3496 case QRhiTexture::RED_OR_ALPHA8:
3497 return MTLPixelFormatR8Unorm;
3499 case QRhiTexture::RGBA16F:
3500 return MTLPixelFormatRGBA16Float;
3501 case QRhiTexture::RGBA32F:
3502 return MTLPixelFormatRGBA32Float;
3503 case QRhiTexture::R16F:
3504 return MTLPixelFormatR16Float;
3505 case QRhiTexture::R32F:
3506 return MTLPixelFormatR32Float;
3508 case QRhiTexture::RGB10A2:
3509 return MTLPixelFormatRGB10A2Unorm;
3511 case QRhiTexture::R32SI:
3512 return MTLPixelFormatR32Sint;
3513 case QRhiTexture::R32UI:
3514 return MTLPixelFormatR32Uint;
3515 case QRhiTexture::RG32SI:
3516 return MTLPixelFormatRG32Sint;
3517 case QRhiTexture::RG32UI:
3518 return MTLPixelFormatRG32Uint;
3519 case QRhiTexture::RGBA32SI:
3520 return MTLPixelFormatRGBA32Sint;
3521 case QRhiTexture::RGBA32UI:
3522 return MTLPixelFormatRGBA32Uint;
3525 case QRhiTexture::D16:
3526 return MTLPixelFormatDepth16Unorm;
3527 case QRhiTexture::D24:
3528 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float;
3529 case QRhiTexture::D24S8:
3530 return [d->d->dev isDepth24Stencil8PixelFormatSupported] ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
3532 case QRhiTexture::D16:
3533 return MTLPixelFormatDepth32Float;
3534 case QRhiTexture::D24:
3535 return MTLPixelFormatDepth32Float;
3536 case QRhiTexture::D24S8:
3537 return MTLPixelFormatDepth32Float_Stencil8;
3539 case QRhiTexture::D32F:
3540 return MTLPixelFormatDepth32Float;
3541 case QRhiTexture::D32FS8:
3542 return MTLPixelFormatDepth32Float_Stencil8;
3545 case QRhiTexture::BC1:
3546 return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
3547 case QRhiTexture::BC2:
3548 return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
3549 case QRhiTexture::BC3:
3550 return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
3551 case QRhiTexture::BC4:
3552 return MTLPixelFormatBC4_RUnorm;
3553 case QRhiTexture::BC5:
3554 qWarning(
"QRhiMetal does not support BC5");
3555 return MTLPixelFormatInvalid;
3556 case QRhiTexture::BC6H:
3557 return MTLPixelFormatBC6H_RGBUfloat;
3558 case QRhiTexture::BC7:
3559 return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
3561 case QRhiTexture::BC1:
3562 case QRhiTexture::BC2:
3563 case QRhiTexture::BC3:
3564 case QRhiTexture::BC4:
3565 case QRhiTexture::BC5:
3566 case QRhiTexture::BC6H:
3567 case QRhiTexture::BC7:
3568 qWarning(
"QRhiMetal: BCx compression not supported on this platform");
3569 return MTLPixelFormatInvalid;
3573 case QRhiTexture::ETC2_RGB8:
3574 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3575 case QRhiTexture::ETC2_RGB8A1:
3576 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3577 case QRhiTexture::ETC2_RGBA8:
3578 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3580 case QRhiTexture::ASTC_4x4:
3581 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3582 case QRhiTexture::ASTC_5x4:
3583 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3584 case QRhiTexture::ASTC_5x5:
3585 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3586 case QRhiTexture::ASTC_6x5:
3587 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3588 case QRhiTexture::ASTC_6x6:
3589 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3590 case QRhiTexture::ASTC_8x5:
3591 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3592 case QRhiTexture::ASTC_8x6:
3593 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3594 case QRhiTexture::ASTC_8x8:
3595 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3596 case QRhiTexture::ASTC_10x5:
3597 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3598 case QRhiTexture::ASTC_10x6:
3599 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3600 case QRhiTexture::ASTC_10x8:
3601 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3602 case QRhiTexture::ASTC_10x10:
3603 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3604 case QRhiTexture::ASTC_12x10:
3605 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3606 case QRhiTexture::ASTC_12x12:
3607 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3609 case QRhiTexture::ETC2_RGB8:
3610 if (d->caps.isAppleGPU)
3611 return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
3612 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3613 return MTLPixelFormatInvalid;
3614 case QRhiTexture::ETC2_RGB8A1:
3615 if (d->caps.isAppleGPU)
3616 return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
3617 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3618 return MTLPixelFormatInvalid;
3619 case QRhiTexture::ETC2_RGBA8:
3620 if (d->caps.isAppleGPU)
3621 return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
3622 qWarning(
"QRhiMetal: ETC2 compression not supported on this platform");
3623 return MTLPixelFormatInvalid;
3624 case QRhiTexture::ASTC_4x4:
3625 if (d->caps.isAppleGPU)
3626 return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
3627 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3628 return MTLPixelFormatInvalid;
3629 case QRhiTexture::ASTC_5x4:
3630 if (d->caps.isAppleGPU)
3631 return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
3632 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3633 return MTLPixelFormatInvalid;
3634 case QRhiTexture::ASTC_5x5:
3635 if (d->caps.isAppleGPU)
3636 return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
3637 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3638 return MTLPixelFormatInvalid;
3639 case QRhiTexture::ASTC_6x5:
3640 if (d->caps.isAppleGPU)
3641 return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
3642 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3643 return MTLPixelFormatInvalid;
3644 case QRhiTexture::ASTC_6x6:
3645 if (d->caps.isAppleGPU)
3646 return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
3647 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3648 return MTLPixelFormatInvalid;
3649 case QRhiTexture::ASTC_8x5:
3650 if (d->caps.isAppleGPU)
3651 return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
3652 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3653 return MTLPixelFormatInvalid;
3654 case QRhiTexture::ASTC_8x6:
3655 if (d->caps.isAppleGPU)
3656 return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
3657 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3658 return MTLPixelFormatInvalid;
3659 case QRhiTexture::ASTC_8x8:
3660 if (d->caps.isAppleGPU)
3661 return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
3662 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3663 return MTLPixelFormatInvalid;
3664 case QRhiTexture::ASTC_10x5:
3665 if (d->caps.isAppleGPU)
3666 return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
3667 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3668 return MTLPixelFormatInvalid;
3669 case QRhiTexture::ASTC_10x6:
3670 if (d->caps.isAppleGPU)
3671 return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
3672 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3673 return MTLPixelFormatInvalid;
3674 case QRhiTexture::ASTC_10x8:
3675 if (d->caps.isAppleGPU)
3676 return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
3677 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3678 return MTLPixelFormatInvalid;
3679 case QRhiTexture::ASTC_10x10:
3680 if (d->caps.isAppleGPU)
3681 return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
3682 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3683 return MTLPixelFormatInvalid;
3684 case QRhiTexture::ASTC_12x10:
3685 if (d->caps.isAppleGPU)
3686 return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
3687 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3688 return MTLPixelFormatInvalid;
3689 case QRhiTexture::ASTC_12x12:
3690 if (d->caps.isAppleGPU)
3691 return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
3692 qWarning(
"QRhiMetal: ASTC compression not supported on this platform");
3693 return MTLPixelFormatInvalid;
3698 return MTLPixelFormatInvalid;
4353bool QMetalTextureRenderTarget::create()
4356 Q_ASSERT(m_desc.colorAttachmentCount() > 0 || m_desc.depthTexture());
4357 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
4358 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
4360 d->colorAttCount = 0;
4362 for (
auto it = m_desc.cbeginColorAttachments(), itEnd = m_desc.cendColorAttachments(); it != itEnd; ++it, ++attIndex) {
4363 d->colorAttCount += 1;
4364 QMetalTexture *texD =
QRHI_RES(QMetalTexture, it->texture());
4365 QMetalRenderBuffer *rbD =
QRHI_RES(QMetalRenderBuffer, it->renderBuffer());
4366 Q_ASSERT(texD || rbD);
4367 id<MTLTexture> dst = nil;
4371 if (attIndex == 0) {
4372 d->pixelSize = rhiD->q->sizeForMipLevel(it->level(), texD->pixelSize());
4373 d->sampleCount = texD->samples;
4375 is3D = texD->flags().testFlag(QRhiTexture::ThreeDimensional);
4378 if (attIndex == 0) {
4379 d->pixelSize = rbD->pixelSize();
4380 d->sampleCount = rbD->samples;
4383 QMetalRenderTargetData::ColorAtt colorAtt;
4385 colorAtt.arrayLayer = is3D ? 0 : it->layer();
4386 colorAtt.slice = is3D ? it->layer() : 0;
4387 colorAtt.level = it->level();
4388 QMetalTexture *resTexD =
QRHI_RES(QMetalTexture, it->resolveTexture());
4389 colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
4390 colorAtt.resolveLayer = it->resolveLayer();
4391 colorAtt.resolveLevel = it->resolveLevel();
4392 d->fb.colorAtt[attIndex] = colorAtt;
4396 if (hasDepthStencil) {
4397 if (m_desc.depthTexture()) {
4398 QMetalTexture *depthTexD =
QRHI_RES(QMetalTexture, m_desc.depthTexture());
4399 d->fb.dsTex = depthTexD->d->tex;
4400 d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
4401 d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
4402 d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
4403 if (d->colorAttCount == 0) {
4404 d->pixelSize = depthTexD->pixelSize();
4405 d->sampleCount = depthTexD->samples;
4408 QMetalRenderBuffer *depthRbD =
QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer());
4409 d->fb.dsTex = depthRbD->d->tex;
4410 d->fb.hasStencil =
true;
4411 d->fb.depthNeedsStore =
false;
4412 d->fb.preserveDs =
false;
4413 if (d->colorAttCount == 0) {
4414 d->pixelSize = depthRbD->pixelSize();
4415 d->sampleCount = depthRbD->samples;
4418 if (m_desc.depthResolveTexture()) {
4419 QMetalTexture *depthResolveTexD =
QRHI_RES(QMetalTexture, m_desc.depthResolveTexture());
4420 d->fb.dsResolveTex = depthResolveTexD->d->tex;
4427 if (d->colorAttCount > 0)
4428 d->fb.preserveColor = m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
4430 QRhiRenderTargetAttachmentTracker::updateResIdList<QMetalTexture, QMetalRenderBuffer>(m_desc, &d->currentResIdList);
4432 rhiD->registerResource(
this,
false);
4638static inline MTLBlendFactor toMetalBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
4641 case QRhiGraphicsPipeline::Zero:
4642 return MTLBlendFactorZero;
4643 case QRhiGraphicsPipeline::One:
4644 return MTLBlendFactorOne;
4645 case QRhiGraphicsPipeline::SrcColor:
4646 return MTLBlendFactorSourceColor;
4647 case QRhiGraphicsPipeline::OneMinusSrcColor:
4648 return MTLBlendFactorOneMinusSourceColor;
4649 case QRhiGraphicsPipeline::DstColor:
4650 return MTLBlendFactorDestinationColor;
4651 case QRhiGraphicsPipeline::OneMinusDstColor:
4652 return MTLBlendFactorOneMinusDestinationColor;
4653 case QRhiGraphicsPipeline::SrcAlpha:
4654 return MTLBlendFactorSourceAlpha;
4655 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4656 return MTLBlendFactorOneMinusSourceAlpha;
4657 case QRhiGraphicsPipeline::DstAlpha:
4658 return MTLBlendFactorDestinationAlpha;
4659 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4660 return MTLBlendFactorOneMinusDestinationAlpha;
4661 case QRhiGraphicsPipeline::ConstantColor:
4662 return MTLBlendFactorBlendColor;
4663 case QRhiGraphicsPipeline::ConstantAlpha:
4664 return MTLBlendFactorBlendAlpha;
4665 case QRhiGraphicsPipeline::OneMinusConstantColor:
4666 return MTLBlendFactorOneMinusBlendColor;
4667 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4668 return MTLBlendFactorOneMinusBlendAlpha;
4669 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4670 return MTLBlendFactorSourceAlphaSaturated;
4671 case QRhiGraphicsPipeline::Src1Color:
4672 return MTLBlendFactorSource1Color;
4673 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4674 return MTLBlendFactorOneMinusSource1Color;
4675 case QRhiGraphicsPipeline::Src1Alpha:
4676 return MTLBlendFactorSource1Alpha;
4677 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4678 return MTLBlendFactorOneMinusSource1Alpha;
4681 return MTLBlendFactorZero;
4867id<MTLLibrary> QRhiMetalData::createMetalLib(
const QShader &shader, QShader::Variant shaderVariant,
4868 QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
4870 QVarLengthArray<
int, 8> versions;
4871 if (@available(macOS 13, iOS 16, *))
4873 if (@available(macOS 12, iOS 15, *))
4875 versions << 23 << 22 << 21 << 20 << 12;
4877 const QList<QShaderKey> shaders = shader.availableShaders();
4881 for (
const int &version : versions) {
4882 key = { QShader::Source::MetalLibShader, version, shaderVariant };
4883 if (shaders.contains(key))
4887 QShaderCode mtllib = shader.shader(key);
4888 if (!mtllib.shader().isEmpty()) {
4889 dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
4890 size_t(mtllib.shader().size()),
4891 dispatch_get_global_queue(0, 0),
4892 DISPATCH_DATA_DESTRUCTOR_DEFAULT);
4894 id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
4895 dispatch_release(data);
4897 *entryPoint = mtllib.entryPoint();
4901 const QString msg = QString::fromNSString(err.localizedDescription);
4902 qWarning(
"Failed to load metallib from baked shader: %s", qPrintable(msg));
4906 for (
const int &version : versions) {
4907 key = { QShader::Source::MslShader, version, shaderVariant };
4908 if (shaders.contains(key))
4912 QShaderCode mslSource = shader.shader(key);
4913 if (mslSource.shader().isEmpty()) {
4914 qWarning() <<
"No MSL 2.0 or 1.2 code found in baked shader" << shader;
4918 NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
4919 MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
4920 opts.languageVersion = toMetalLanguageVersion(key.sourceVersion());
4922 id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
4930 const QString msg = QString::fromNSString(err.localizedDescription);
4935 *entryPoint = mslSource.entryPoint();
5107bool QMetalGraphicsPipeline::createVertexFragmentPipeline()
5111 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5112 d->setupVertexInputDescriptor(vertexDesc);
5114 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5115 rpDesc.vertexDescriptor = vertexDesc;
5123 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5124 auto cacheIt = rhiD->d->shaderCache.constFind(shaderStage);
5125 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
5126 switch (shaderStage.type()) {
5127 case QRhiShaderStage::Vertex:
5130 [d->vs.func retain];
5131 rpDesc.vertexFunction = d->vs.func;
5133 case QRhiShaderStage::Fragment:
5136 [d->fs.func retain];
5137 rpDesc.fragmentFunction = d->fs.func;
5143 const QShader shader = shaderStage.shader();
5145 QByteArray entryPoint;
5146 QShaderKey activeKey;
5147 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
5148 &error, &entryPoint, &activeKey);
5150 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
5153 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5155 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5159 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
5161 for (QMetalShader &s : rhiD->d->shaderCache)
5163 rhiD->d->shaderCache.clear();
5165 switch (shaderStage.type()) {
5166 case QRhiShaderStage::Vertex:
5169 d->vs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5170 d->vs.desc = shader.description();
5171 d->vs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5172 rhiD->d->shaderCache.insert(shaderStage, d->vs);
5174 [d->vs.func retain];
5175 rpDesc.vertexFunction = func;
5177 case QRhiShaderStage::Fragment:
5180 d->fs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
5181 d->fs.desc = shader.description();
5182 d->fs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
5183 rhiD->d->shaderCache.insert(shaderStage, d->fs);
5185 [d->fs.func retain];
5186 rpDesc.fragmentFunction = func;
5196 QMetalRenderPassDescriptor *rpD =
QRHI_RES(QMetalRenderPassDescriptor, m_renderPassDesc);
5197 setupAttachmentsInMetalRenderPassDescriptor(rpDesc, rpD);
5199 if (m_multiViewCount >= 2)
5200 rpDesc.inputPrimitiveTopology = toMetalPrimitiveTopologyClass(m_topology);
5202 rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5204 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5205 rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
5208 d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5211 const QString msg = QString::fromNSString(err.localizedDescription);
5212 qWarning(
"Failed to create render pipeline state: %s", qPrintable(msg));
5216 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5217 setupMetalDepthStencilDescriptor(dsDesc);
5218 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5221 d->primitiveType = toMetalPrimitiveType(m_topology);
5495id<MTLRenderPipelineState> QMetalGraphicsPipelineData::Tessellation::teseFragRenderPipeline(QRhiMetal *rhiD, QMetalGraphicsPipeline *pipeline)
5497 if (pipeline->d->ps)
5498 return pipeline->d->ps;
5500 MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
5501 MTLVertexDescriptor *vertexDesc = [MTLVertexDescriptor vertexDescriptor];
5504 const QMap<
int,
int> &ebb(compTesc.nativeShaderInfo.extraBufferBindings);
5505 const int tescOutputBufferBinding = ebb.value(QShaderPrivate::MslTessVertTescOutputBufferBinding, -1);
5506 const int tescPatchOutputBufferBinding = ebb.value(QShaderPrivate::MslTessTescPatchOutputBufferBinding, -1);
5507 const int tessFactorBufferBinding = ebb.value(QShaderPrivate::MslTessTescTessLevelBufferBinding, -1);
5508 quint32 offsetInTescOutput = 0;
5509 quint32 offsetInTescPatchOutput = 0;
5510 quint32 offsetInTessFactorBuffer = 0;
5511 quint32 tescOutputAlignment = 0;
5512 quint32 tescPatchOutputAlignment = 0;
5513 quint32 tessFactorAlignment = 0;
5514 QSet<
int> usedBuffers;
5517 QMap<
int, QShaderDescription::InOutVariable> tescOutVars;
5518 for (
const auto &tescOutVar : compTesc.desc.outputVariables())
5519 tescOutVars[tescOutVar.location] = tescOutVar;
5522 QMap<
int, QShaderDescription::InOutVariable> teseInVars;
5523 for (
const auto &teseInVar : vertTese.desc.inputVariables())
5524 teseInVars[teseInVar.location] = teseInVar;
5527 quint64 indices = 0;
5529 for (QShaderDescription::InOutVariable &tescOutVar : tescOutVars) {
5531 int index = tescOutVar.location;
5533 quint32 *offset =
nullptr;
5534 quint32 *alignment =
nullptr;
5536 if (tescOutVar.perPatch) {
5537 binding = tescPatchOutputBufferBinding;
5538 offset = &offsetInTescPatchOutput;
5539 alignment = &tescPatchOutputAlignment;
5541 tescOutVar.arrayDims.removeLast();
5542 binding = tescOutputBufferBinding;
5543 offset = &offsetInTescOutput;
5544 alignment = &tescOutputAlignment;
5547 if (teseInVars.contains(index)) {
5549 if (!matches(teseInVars[index], tescOutVar)) {
5550 qWarning() <<
"mismatched tessellation control output -> tesssellation evaluation input at location" << index;
5551 qWarning() <<
" tesc out:" << tescOutVar;
5552 qWarning() <<
" tese in:" << teseInVars[index];
5555 if (binding != -1) {
5556 addVertexAttribute(tescOutVar, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5557 usedBuffers << binding;
5559 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5560 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5564 qWarning() <<
"missing tessellation evaluation input for tessellation control output:" << tescOutVar;
5565 addUnusedVertexAttribute(tescOutVar, rhiD, *offset, *alignment);
5568 teseInVars.remove(tescOutVar.location);
5571 for (
const QShaderDescription::InOutVariable &teseInVar : teseInVars)
5572 qWarning() <<
"missing tessellation control output for tessellation evaluation input:" << teseInVar;
5575 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> tescOutBuiltins;
5576 for (
const auto &tescOutBuiltin : compTesc.desc.outputBuiltinVariables())
5577 tescOutBuiltins[tescOutBuiltin.type] = tescOutBuiltin;
5580 QMap<QShaderDescription::BuiltinType, QShaderDescription::BuiltinVariable> teseInBuiltins;
5581 for (
const auto &teseInBuiltin : vertTese.desc.inputBuiltinVariables())
5582 teseInBuiltins[teseInBuiltin.type] = teseInBuiltin;
5584 const bool trianglesMode = vertTese.desc.tessellationMode() == QShaderDescription::TrianglesTessellationMode;
5585 bool tessLevelAdded =
false;
5587 for (
const QShaderDescription::BuiltinVariable &builtin : tescOutBuiltins) {
5589 QShaderDescription::InOutVariable variable;
5591 quint32 *offset =
nullptr;
5592 quint32 *alignment =
nullptr;
5594 switch (builtin.type) {
5595 case QShaderDescription::BuiltinType::PositionBuiltin:
5596 variable.type = QShaderDescription::VariableType::Vec4;
5597 binding = tescOutputBufferBinding;
5598 offset = &offsetInTescOutput;
5599 alignment = &tescOutputAlignment;
5601 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5602 variable.type = QShaderDescription::VariableType::Float;
5603 binding = tescOutputBufferBinding;
5604 offset = &offsetInTescOutput;
5605 alignment = &tescOutputAlignment;
5607 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5608 variable.type = QShaderDescription::VariableType::Float;
5609 variable.arrayDims = builtin.arrayDims;
5610 binding = tescOutputBufferBinding;
5611 offset = &offsetInTescOutput;
5612 alignment = &tescOutputAlignment;
5614 case QShaderDescription::BuiltinType::TessLevelOuterBuiltin:
5615 variable.type = QShaderDescription::VariableType::Half4;
5616 binding = tessFactorBufferBinding;
5617 offset = &offsetInTessFactorBuffer;
5618 tessLevelAdded = trianglesMode;
5619 alignment = &tessFactorAlignment;
5621 case QShaderDescription::BuiltinType::TessLevelInnerBuiltin:
5622 if (trianglesMode) {
5623 if (!tessLevelAdded) {
5624 variable.type = QShaderDescription::VariableType::Half4;
5625 binding = tessFactorBufferBinding;
5626 offsetInTessFactorBuffer = 0;
5627 offset = &offsetInTessFactorBuffer;
5628 alignment = &tessFactorAlignment;
5629 tessLevelAdded =
true;
5631 teseInBuiltins.remove(builtin.type);
5635 variable.type = QShaderDescription::VariableType::Half2;
5636 binding = tessFactorBufferBinding;
5637 offsetInTessFactorBuffer = 8;
5638 offset = &offsetInTessFactorBuffer;
5639 alignment = &tessFactorAlignment;
5647 if (teseInBuiltins.contains(builtin.type)) {
5648 if (binding != -1) {
5649 int index = nextAttributeIndex(indices);
5650 addVertexAttribute(variable, binding, rhiD, index, *offset, vertexDesc.attributes, indices, *alignment);
5651 usedBuffers << binding;
5653 qWarning() <<
"baked tessellation control shader missing output buffer binding information";
5654 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5657 addUnusedVertexAttribute(variable, rhiD, *offset, *alignment);
5660 teseInBuiltins.remove(builtin.type);
5663 for (
const QShaderDescription::BuiltinVariable &builtin : teseInBuiltins) {
5664 switch (builtin.type) {
5665 case QShaderDescription::BuiltinType::PositionBuiltin:
5666 case QShaderDescription::BuiltinType::PointSizeBuiltin:
5667 case QShaderDescription::BuiltinType::ClipDistanceBuiltin:
5668 qWarning() <<
"missing tessellation control output for tessellation evaluation builtin input:" << builtin;
5675 if (usedBuffers.contains(tescOutputBufferBinding)) {
5676 vertexDesc.layouts[tescOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatchControlPoint;
5677 vertexDesc.layouts[tescOutputBufferBinding].stride = aligned(offsetInTescOutput, tescOutputAlignment);
5680 if (usedBuffers.contains(tescPatchOutputBufferBinding)) {
5681 vertexDesc.layouts[tescPatchOutputBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5682 vertexDesc.layouts[tescPatchOutputBufferBinding].stride = aligned(offsetInTescPatchOutput, tescPatchOutputAlignment);
5685 if (usedBuffers.contains(tessFactorBufferBinding)) {
5686 vertexDesc.layouts[tessFactorBufferBinding].stepFunction = MTLVertexStepFunctionPerPatch;
5687 vertexDesc.layouts[tessFactorBufferBinding].stride = trianglesMode ?
sizeof(MTLTriangleTessellationFactorsHalf) :
sizeof(MTLQuadTessellationFactorsHalf);
5690 rpDesc.vertexDescriptor = vertexDesc;
5691 rpDesc.vertexFunction = vertTese.func;
5692 rpDesc.fragmentFunction = pipeline->d->fs.func;
5698 rpDesc.tessellationOutputWindingOrder = toMetalTessellationWindingOrder(vertTese.desc.tessellationWindingOrder());
5700 rpDesc.tessellationPartitionMode = toMetalTessellationPartitionMode(vertTese.desc.tessellationPartitioning());
5702 QMetalRenderPassDescriptor *rpD =
QRHI_RES(QMetalRenderPassDescriptor, pipeline->renderPassDescriptor());
5703 pipeline->setupAttachmentsInMetalRenderPassDescriptor(rpDesc, rpD);
5705 rhiD->d->trySeedingRenderPipelineFromBinaryArchive(rpDesc);
5707 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
5708 rhiD->d->addRenderPipelineToBinaryArchive(rpDesc);
5711 id<MTLRenderPipelineState> ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
5714 const QString msg = QString::fromNSString(err.localizedDescription);
5715 qWarning(
"Failed to create render pipeline state for tessellation: %s", qPrintable(msg));
5719 pipeline->d->ps = ps;
5769bool QMetalGraphicsPipeline::createTessellationPipelines(
const QShader &tessVert,
const QShader &tesc,
const QShader &tese,
const QShader &tessFrag)
5773 QByteArray entryPoint;
5774 QShaderKey activeKey;
5776 const QShaderDescription tescDesc = tesc.description();
5777 const QShaderDescription teseDesc = tese.description();
5778 d->tess.inControlPointCount = uint(m_patchControlPointCount);
5779 d->tess.outControlPointCount = tescDesc.tessellationOutputVertexCount();
5780 if (!d->tess.outControlPointCount)
5781 d->tess.outControlPointCount = teseDesc.tessellationOutputVertexCount();
5783 if (!d->tess.outControlPointCount) {
5784 qWarning(
"Failed to determine output vertex count from the tessellation control or evaluation shader, cannot tessellate");
5785 d->tess.enabled =
false;
5786 d->tess.failed =
true;
5790 if (m_multiViewCount >= 2)
5791 qWarning(
"Multiview is not supported with tessellation");
5799 bool variantsPresent[3] = {};
5800 const QVector<QShaderKey> tessVertKeys = tessVert.availableShaders();
5801 for (
const QShaderKey &k : tessVertKeys) {
5802 switch (k.sourceVariant()) {
5803 case QShader::NonIndexedVertexAsComputeShader:
5804 variantsPresent[0] =
true;
5806 case QShader::UInt32IndexedVertexAsComputeShader:
5807 variantsPresent[1] =
true;
5809 case QShader::UInt16IndexedVertexAsComputeShader:
5810 variantsPresent[2] =
true;
5816 if (!(variantsPresent[0] && variantsPresent[1] && variantsPresent[2])) {
5817 qWarning(
"Vertex shader is not prepared for Metal tessellation. Cannot tessellate. "
5818 "Perhaps the relevant variants (UInt32IndexedVertexAsComputeShader et al) were not generated? "
5819 "Try passing --msltess to qsb.");
5820 d->tess.enabled =
false;
5821 d->tess.failed =
true;
5826 for (QShader::Variant variant : {
5827 QShader::NonIndexedVertexAsComputeShader,
5828 QShader::UInt32IndexedVertexAsComputeShader,
5829 QShader::UInt16IndexedVertexAsComputeShader })
5831 id<MTLLibrary> lib = rhiD->d->createMetalLib(tessVert, variant, &error, &entryPoint, &activeKey);
5833 qWarning(
"MSL shader compilation failed for vertex-as-compute shader %d: %s",
int(variant), qPrintable(error));
5834 d->tess.enabled =
false;
5835 d->tess.failed =
true;
5838 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
5840 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5842 d->tess.enabled =
false;
5843 d->tess.failed =
true;
5846 QMetalShader &compVs(d->tess.compVs[varIndex]);
5849 compVs.desc = tessVert.description();
5850 compVs.nativeResourceBindingMap = tessVert.nativeResourceBindingMap(activeKey);
5851 compVs.nativeShaderInfo = tessVert.nativeShaderInfo(activeKey);
5854 if (!d->tess.vsCompPipeline(rhiD, variant)) {
5855 qWarning(
"Failed to pre-generate compute pipeline for vertex compute shader (tessellation variant %d)",
int(variant));
5856 d->tess.enabled =
false;
5857 d->tess.failed =
true;
5865 id<MTLLibrary> tessControlLib = rhiD->d->createMetalLib(tesc, QShader::StandardShader, &error, &entryPoint, &activeKey);
5866 if (!tessControlLib) {
5867 qWarning(
"MSL shader compilation failed for tessellation control compute shader: %s", qPrintable(error));
5868 d->tess.enabled =
false;
5869 d->tess.failed =
true;
5872 id<MTLFunction> tessControlFunc = rhiD->d->createMSLShaderFunction(tessControlLib, entryPoint);
5873 if (!tessControlFunc) {
5874 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5875 [tessControlLib release];
5876 d->tess.enabled =
false;
5877 d->tess.failed =
true;
5880 d->tess.compTesc.lib = tessControlLib;
5881 d->tess.compTesc.func = tessControlFunc;
5882 d->tess.compTesc.desc = tesc.description();
5883 d->tess.compTesc.nativeResourceBindingMap = tesc.nativeResourceBindingMap(activeKey);
5884 d->tess.compTesc.nativeShaderInfo = tesc.nativeShaderInfo(activeKey);
5885 if (!d->tess.tescCompPipeline(rhiD)) {
5886 qWarning(
"Failed to pre-generate compute pipeline for tessellation control shader");
5887 d->tess.enabled =
false;
5888 d->tess.failed =
true;
5893 id<MTLLibrary> tessEvalLib = rhiD->d->createMetalLib(tese, QShader::StandardShader, &error, &entryPoint, &activeKey);
5895 qWarning(
"MSL shader compilation failed for tessellation evaluation vertex shader: %s", qPrintable(error));
5896 d->tess.enabled =
false;
5897 d->tess.failed =
true;
5900 id<MTLFunction> tessEvalFunc = rhiD->d->createMSLShaderFunction(tessEvalLib, entryPoint);
5901 if (!tessEvalFunc) {
5902 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5903 [tessEvalLib release];
5904 d->tess.enabled =
false;
5905 d->tess.failed =
true;
5908 d->tess.vertTese.lib = tessEvalLib;
5909 d->tess.vertTese.func = tessEvalFunc;
5910 d->tess.vertTese.desc = tese.description();
5911 d->tess.vertTese.nativeResourceBindingMap = tese.nativeResourceBindingMap(activeKey);
5912 d->tess.vertTese.nativeShaderInfo = tese.nativeShaderInfo(activeKey);
5914 id<MTLLibrary> fragLib = rhiD->d->createMetalLib(tessFrag, QShader::StandardShader, &error, &entryPoint, &activeKey);
5916 qWarning(
"MSL shader compilation failed for fragment shader: %s", qPrintable(error));
5917 d->tess.enabled =
false;
5918 d->tess.failed =
true;
5921 id<MTLFunction> fragFunc = rhiD->d->createMSLShaderFunction(fragLib, entryPoint);
5923 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
5925 d->tess.enabled =
false;
5926 d->tess.failed =
true;
5929 d->fs.lib = fragLib;
5930 d->fs.func = fragFunc;
5931 d->fs.desc = tessFrag.description();
5932 d->fs.nativeShaderInfo = tessFrag.nativeShaderInfo(activeKey);
5933 d->fs.nativeResourceBindingMap = tessFrag.nativeResourceBindingMap(activeKey);
5935 if (!d->tess.teseFragRenderPipeline(rhiD,
this)) {
5936 qWarning(
"Failed to pre-generate render pipeline for tessellation evaluation + fragment shader");
5937 d->tess.enabled =
false;
5938 d->tess.failed =
true;
5942 MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
5943 setupMetalDepthStencilDescriptor(dsDesc);
5944 d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
5953bool QMetalGraphicsPipeline::create()
5958 rhiD->pipelineCreationStart();
5959 if (!rhiD->sanityCheckGraphicsPipeline(
this))
5967 for (
const QRhiShaderStage &shaderStage : std::as_const(m_shaderStages)) {
5968 switch (shaderStage.type()) {
5969 case QRhiShaderStage::Vertex:
5970 tessVert = shaderStage.shader();
5972 case QRhiShaderStage::TessellationControl:
5973 tesc = shaderStage.shader();
5975 case QRhiShaderStage::TessellationEvaluation:
5976 tese = shaderStage.shader();
5978 case QRhiShaderStage::Fragment:
5979 tessFrag = shaderStage.shader();
5985 d->tess.enabled = tesc.isValid() && tese.isValid() && m_topology == Patches && m_patchControlPointCount > 0;
5986 d->tess.failed =
false;
5988 bool ok = d->tess.enabled ? createTessellationPipelines(tessVert, tesc, tese, tessFrag) : createVertexFragmentPipeline();
5994 QVarLengthArray<QMetalShader *, 6> shaders;
5995 if (d->tess.enabled) {
5996 shaders.append(&d->tess.compVs[0]);
5997 shaders.append(&d->tess.compVs[1]);
5998 shaders.append(&d->tess.compVs[2]);
5999 shaders.append(&d->tess.compTesc);
6000 shaders.append(&d->tess.vertTese);
6002 shaders.append(&d->vs);
6004 shaders.append(&d->fs);
6006 for (QMetalShader *shader : shaders) {
6007 if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6008 const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6009 shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6010 int maxNativeBinding = 0;
6011 for (
const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks())
6012 maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first);
6016 buffers += ((maxNativeBinding + 1 + 7) / 8) * 8;
6021 if (!d->bufferSizeBuffer)
6022 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6024 d->bufferSizeBuffer->setSize(buffers *
sizeof(
int));
6025 d->bufferSizeBuffer->create();
6028 rhiD->pipelineCreationEnd();
6029 lastActiveFrameSlot = -1;
6031 rhiD->registerResource(
this);
6089bool QMetalComputePipeline::create()
6095 rhiD->pipelineCreationStart();
6097 auto cacheIt = rhiD->d->shaderCache.constFind(m_shaderStage);
6098 if (cacheIt != rhiD->d->shaderCache.constEnd()) {
6101 const QShader shader = m_shaderStage.shader();
6103 QByteArray entryPoint;
6104 QShaderKey activeKey;
6105 id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
6106 &error, &entryPoint, &activeKey);
6108 qWarning(
"MSL shader compilation failed: %s", qPrintable(error));
6111 id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
6113 qWarning(
"MSL function for entry point %s not found", entryPoint.constData());
6119 d->cs.localSize = shader.description().computeShaderLocalSize();
6120 d->cs.nativeResourceBindingMap = shader.nativeResourceBindingMap(activeKey);
6121 d->cs.desc = shader.description();
6122 d->cs.nativeShaderInfo = shader.nativeShaderInfo(activeKey);
6125 if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6126 const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding];
6127 d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1);
6130 if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
6131 for (QMetalShader &s : rhiD->d->shaderCache)
6133 rhiD->d->shaderCache.clear();
6135 rhiD->d->shaderCache.insert(m_shaderStage, d->cs);
6139 [d->cs.func retain];
6141 d->localSize = MTLSizeMake(d->cs.localSize[0], d->cs.localSize[1], d->cs.localSize[2]);
6143 MTLComputePipelineDescriptor *cpDesc = [MTLComputePipelineDescriptor
new];
6144 cpDesc.computeFunction = d->cs.func;
6146 rhiD->d->trySeedingComputePipelineFromBinaryArchive(cpDesc);
6148 if (rhiD->rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
6149 rhiD->d->addComputePipelineToBinaryArchive(cpDesc);
6152 d->ps = [rhiD->d->dev newComputePipelineStateWithDescriptor: cpDesc
6153 options: MTLPipelineOptionNone
6158 const QString msg = QString::fromNSString(err.localizedDescription);
6159 qWarning(
"Failed to create compute pipeline state: %s", qPrintable(msg));
6164 if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) {
6166 for (
const QShaderDescription::StorageBlock &block : d->cs.desc.storageBlocks())
6167 buffers = qMax(buffers, d->cs.nativeResourceBindingMap[block.binding].first);
6171 if (!d->bufferSizeBuffer)
6172 d->bufferSizeBuffer =
new QMetalBuffer(rhiD, QRhiBuffer::Static, QRhiBuffer::StorageBuffer, buffers *
sizeof(
int));
6174 d->bufferSizeBuffer->setSize(buffers *
sizeof(
int));
6175 d->bufferSizeBuffer->create();
6178 rhiD->pipelineCreationEnd();
6179 lastActiveFrameSlot = -1;
6181 rhiD->registerResource(
this);
6428bool QMetalSwapChain::createOrResize()
6432 const bool needsRegistration = !window || window != m_window;
6434 if (window && window != m_window)
6439 if (needsRegistration || !rhiD->swapchains.contains(
this))
6440 rhiD->swapchains.insert(
this);
6444 if (window->surfaceType() != QSurface::MetalSurface) {
6445 qWarning(
"QMetalSwapChain only supports MetalSurface windows");
6449 d->layer = qrhi_objectFromProxyData<CAMetalLayer>(&m_proxyData, window, QRhi::Metal, 0);
6453 if (d->colorFormat != d->layer.pixelFormat)
6454 d->layer.pixelFormat = d->colorFormat;
6456 if (m_format == HDRExtendedSrgbLinear) {
6457 if (@available(iOS 16.0, *)) {
6458 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
6459 d->layer.wantsExtendedDynamicRangeContent = YES;
6461 }
else if (m_format == HDR10) {
6462 if (@available(iOS 16.0, *)) {
6463 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2100_PQ);
6464 d->layer.wantsExtendedDynamicRangeContent = YES;
6466 }
else if (m_format == HDRExtendedDisplayP3Linear) {
6467 if (@available(iOS 16.0, *)) {
6468 d->layer.colorspace = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearDisplayP3);
6469 d->layer.wantsExtendedDynamicRangeContent = YES;
6473 if (m_flags.testFlag(UsedAsTransferSource))
6474 d->layer.framebufferOnly = NO;
6477 if (m_flags.testFlag(NoVSync))
6478 d->layer.displaySyncEnabled = NO;
6481 if (m_flags.testFlag(SurfaceHasPreMulAlpha)) {
6482 d->layer.opaque = NO;
6483 }
else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha)) {
6488 d->layer.opaque = NO;
6490 d->layer.opaque = YES;
6496 int width = (
int)d->layer.bounds.size.width;
6497 int height = (
int)d->layer.bounds.size.height;
6498 CGSize layerSize = CGSizeMake(width, height);
6499 const float scaleFactor = d->layer.contentsScale;
6500 layerSize.width *= scaleFactor;
6501 layerSize.height *= scaleFactor;
6502 d->layer.drawableSize = layerSize;
6504 m_currentPixelSize = QSizeF::fromCGSize(layerSize).toSize();
6505 pixelSize = m_currentPixelSize;
6507 [d->layer setDevice: rhiD->d->dev];
6509 [d->curDrawable release];
6510 d->curDrawable = nil;
6512 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6513 d->lastGpuTime[i] = 0;
6515 d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
6518 currentFrameSlot = 0;
6521 ds = m_depthStencil ?
QRHI_RES(QMetalRenderBuffer, m_depthStencil) :
nullptr;
6522 if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
6523 qWarning(
"Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
6524 m_depthStencil->sampleCount(), m_sampleCount);
6526 if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
6527 if (m_depthStencil->flags().testFlag(QRhiRenderBuffer::UsedWithSwapChainOnly)) {
6528 m_depthStencil->setPixelSize(pixelSize);
6529 if (!m_depthStencil->create())
6530 qWarning(
"Failed to rebuild swapchain's associated depth-stencil buffer for size %dx%d",
6531 pixelSize.width(), pixelSize.height());
6533 qWarning(
"Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
6534 m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
6535 pixelSize.width(), pixelSize.height());
6539 rtWrapper.setRenderPassDescriptor(m_renderPassDesc);
6540 rtWrapper.d->pixelSize = pixelSize;
6541 rtWrapper.d->dpr = scaleFactor;
6542 rtWrapper.d->sampleCount = samples;
6543 rtWrapper.d->colorAttCount = 1;
6544 rtWrapper.d->dsAttCount = ds ? 1 : 0;
6546 qCDebug(QRHI_LOG_INFO,
"got CAMetalLayer, pixel size %dx%d (scale %.2f)",
6547 pixelSize.width(), pixelSize.height(), scaleFactor);
6550 MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
6551 desc.textureType = MTLTextureType2DMultisample;
6552 desc.pixelFormat = d->colorFormat;
6553 desc.width = NSUInteger(pixelSize.width());
6554 desc.height = NSUInteger(pixelSize.height());
6555 desc.sampleCount = NSUInteger(samples);
6556 desc.resourceOptions = MTLResourceStorageModePrivate;
6557 desc.storageMode = MTLStorageModePrivate;
6558 desc.usage = MTLTextureUsageRenderTarget;
6559 for (
int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
6560 [d->msaaTex[i] release];
6561 d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
6566 rhiD->registerResource(
this);