summaryrefslogtreecommitdiffstats
path: root/src/gui/rhi
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/rhi')
-rw-r--r--src/gui/rhi/qrhi.cpp26
-rw-r--r--src/gui/rhi/qrhigles2.cpp6
-rw-r--r--src/gui/rhi/qrhimetal.mm161
-rw-r--r--src/gui/rhi/qrhimetal_p_p.h7
-rw-r--r--src/gui/rhi/qshader.cpp155
-rw-r--r--src/gui/rhi/qshader_p.h5
-rw-r--r--src/gui/rhi/qshader_p_p.h10
-rw-r--r--src/gui/rhi/qshaderdescription.cpp152
-rw-r--r--src/gui/rhi/qshaderdescription_p.h40
9 files changed, 473 insertions, 89 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index dabad35688..8ef98d2e42 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -3518,14 +3518,36 @@ QRhiResource::Type QRhiSwapChain::resourceType() const
\c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be
resized.
+ \note Typical rendering logic will call this function to get the output
+ size when starting to prepare a new frame, and base dependent calculations
+ (such as, the viewport) on the size returned from this function.
+
+ While in many cases the value is the same as \c{QWindow::size() *
+ QWindow::devicePixelRatio()}, relying on the QWindow-reported size is not
+ guaranteed to be correct on all platforms and graphics API implementations.
+ Using this function is therefore strongly recommended whenever there is a
+ need to identify the dimensions, in pixels, of the output layer or surface.
+
+ This also has the added benefit of avoiding potential data races when QRhi
+ is used on a dedicated rendering thread, because the need to call QWindow
+ functions, that may then access data updated on the main thread, is
+ avoided.
+
\sa surfacePixelSize()
*/
/*!
\fn QSize QRhiSwapChain::surfacePixelSize()
- \return The size of the window's associated surface or layer. Do not assume
- this is the same as QWindow::size() * QWindow::devicePixelRatio().
+ \return The size of the window's associated surface or layer.
+
+ \warning Do not assume this is the same as \c{QWindow::size() *
+ QWindow::devicePixelRatio()}. With some graphics APIs and windowing system
+ interfaces (for example, Vulkan) there is a theoretical possibility for a
+ surface to assume a size different from the associated window. To support
+ these cases, rendering logic must always base size-derived calculations
+ (such as, viewports) on the size reported from QRhiSwapChain, and never on
+ the size queried from QWindow.
\note Can also be called before buildOrResize(), if at least window() is
already set) This in combination with currentPixelSize() allows to detect
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index dec28cac9b..abee843a74 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -2961,7 +2961,7 @@ bool QRhiGles2::isProgramBinaryDiskCacheEnabled() const
return checker.get(ctx)->isSupported();
}
-static QOpenGLProgramBinaryCache qrhi_programBinaryCache;
+Q_GLOBAL_STATIC(QOpenGLProgramBinaryCache, qrhi_programBinaryCache);
static inline QShader::Stage toShaderStage(QRhiShaderStage::Type type)
{
@@ -2995,7 +2995,7 @@ QRhiGles2::DiskCacheResult QRhiGles2::tryLoadFromDiskCache(const QRhiShaderStage
}
diskCacheKey = binaryProgram.cacheKey();
- if (qrhi_programBinaryCache.load(diskCacheKey, program)) {
+ if (qrhi_programBinaryCache()->load(diskCacheKey, program)) {
qCDebug(lcOpenGLProgramDiskCache, "Program binary received from cache, program %u, key %s",
program, diskCacheKey.constData());
result = QRhiGles2::DiskCacheHit;
@@ -3013,7 +3013,7 @@ void QRhiGles2::trySaveToDiskCache(GLuint program, const QByteArray &cacheKey)
if (isProgramBinaryDiskCacheEnabled()) {
qCDebug(lcOpenGLProgramDiskCache, "Saving program binary, program %u, key %s",
program, cacheKey.constData());
- qrhi_programBinaryCache.save(cacheKey, program);
+ qrhi_programBinaryCache()->save(cacheKey, program);
}
}
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 5f14d917b8..131b2da802 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -35,8 +35,6 @@
****************************************************************************/
#include "qrhimetal_p_p.h"
-#include "qshader_p.h"
-#include "qshaderdescription_p.h"
#include <QGuiApplication>
#include <QWindow>
#include <qmath.h>
@@ -143,8 +141,10 @@ struct QMetalShader
id<MTLLibrary> lib = nil;
id<MTLFunction> func = nil;
std::array<uint, 3> localSize;
+ QShader::NativeResourceBindingMap nativeResourceBindingMap;
void release() {
+ nativeResourceBindingMap.clear();
[lib release];
lib = nil;
[func release];
@@ -164,7 +164,7 @@ struct QRhiMetalData
const QRhiDepthStencilClearValue &depthStencilClearValue,
int colorAttCount);
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
- QString *error, QByteArray *entryPoint);
+ QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
struct DeferredReleaseEntry {
@@ -653,18 +653,40 @@ QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings()
return new QMetalShaderResourceBindings(this);
}
-void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+enum class BindingType {
+ Buffer,
+ Texture,
+ Sampler
+};
+
+static inline int mapBinding(int binding,
+ int stageIndex,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[],
+ BindingType type)
+{
+ const QShader::NativeResourceBindingMap *map = nativeResourceBindingMaps[stageIndex];
+ if (map) {
+ auto it = map->constFind(binding);
+ if (it != map->cend())
+ return type == BindingType::Sampler ? it->second : it->first;
+ }
+ return binding;
+}
+
+void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
+ QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
- bool offsetOnlyChange)
+ bool offsetOnlyChange,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES])
{
- static const int KNOWN_STAGES = 3;
struct {
QRhiBatchedBindings<id<MTLBuffer> > buffers;
QRhiBatchedBindings<NSUInteger> bufferOffsets;
QRhiBatchedBindings<id<MTLTexture> > textures;
QRhiBatchedBindings<id<MTLSamplerState> > samplers;
- } res[KNOWN_STAGES];
+ } res[SUPPORTED_STAGES];
+ enum { VERTEX = 0, FRAGMENT = 1, COMPUTE = 2 };
for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
const QRhiShaderResourceBinding::Data *b = binding.data();
@@ -682,16 +704,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[0].buffers.feed(b->binding, mtlbuf);
- res[0].bufferOffsets.feed(b->binding, offset);
+ res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
+ res[VERTEX].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[1].buffers.feed(b->binding, mtlbuf);
- res[1].bufferOffsets.feed(b->binding, offset);
+ res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
+ res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[2].buffers.feed(b->binding, mtlbuf);
- res[2].bufferOffsets.feed(b->binding, offset);
+ res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
+ res[COMPUTE].bufferOffsets.feed(b->binding, offset);
}
}
break;
@@ -700,16 +722,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[0].textures.feed(b->binding, texD->d->tex);
- res[0].samplers.feed(b->binding, samplerD->d->samplerState);
+ res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
+ res[VERTEX].samplers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[1].textures.feed(b->binding, texD->d->tex);
- res[1].samplers.feed(b->binding, samplerD->d->samplerState);
+ res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
+ res[FRAGMENT].samplers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[2].textures.feed(b->binding, texD->d->tex);
- res[2].samplers.feed(b->binding, samplerD->d->samplerState);
+ res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), texD->d->tex);
+ res[COMPUTE].samplers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler), samplerD->d->samplerState);
}
}
break;
@@ -722,11 +744,11 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage))
- res[0].textures.feed(b->binding, t);
+ res[VERTEX].textures.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture), t);
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
- res[1].textures.feed(b->binding, t);
+ res[FRAGMENT].textures.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture), t);
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
- res[2].textures.feed(b->binding, t);
+ res[COMPUTE].textures.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture), t);
}
break;
case QRhiShaderResourceBinding::BufferLoad:
@@ -739,16 +761,16 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
id<MTLBuffer> mtlbuf = bufD->d->buf[0];
uint offset = uint(b->u.sbuf.offset);
if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
- res[0].buffers.feed(b->binding, mtlbuf);
- res[0].bufferOffsets.feed(b->binding, offset);
+ res[VERTEX].buffers.feed(mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
+ res[VERTEX].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
- res[1].buffers.feed(b->binding, mtlbuf);
- res[1].bufferOffsets.feed(b->binding, offset);
+ res[FRAGMENT].buffers.feed(mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
+ res[FRAGMENT].bufferOffsets.feed(b->binding, offset);
}
if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
- res[2].buffers.feed(b->binding, mtlbuf);
- res[2].bufferOffsets.feed(b->binding, offset);
+ res[COMPUTE].buffers.feed(mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Buffer), mtlbuf);
+ res[COMPUTE].bufferOffsets.feed(b->binding, offset);
}
}
break;
@@ -758,25 +780,30 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
}
}
- for (int idx = 0; idx < KNOWN_STAGES; ++idx) {
- res[idx].buffers.finish();
- res[idx].bufferOffsets.finish();
+ for (int stage = 0; stage < SUPPORTED_STAGES; ++stage) {
+ if (cbD->recordingPass != QMetalCommandBuffer::RenderPass && (stage == VERTEX || stage == FRAGMENT))
+ continue;
+ if (cbD->recordingPass != QMetalCommandBuffer::ComputePass && stage == COMPUTE)
+ continue;
+
+ res[stage].buffers.finish();
+ res[stage].bufferOffsets.finish();
- for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) {
- const auto &bufferBatch(res[idx].buffers.batches[i]);
- const auto &offsetBatch(res[idx].bufferOffsets.batches[i]);
- switch (idx) {
- case 0:
+ for (int i = 0, ie = res[stage].buffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(res[stage].buffers.batches[i]);
+ const auto &offsetBatch(res[stage].bufferOffsets.batches[i]);
+ switch (stage) {
+ case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break;
- case 1:
+ case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
break;
- case 2:
+ case COMPUTE:
[cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
offsets: offsetBatch.resources.constData()
withRange: NSMakeRange(bufferBatch.startBinding, NSUInteger(bufferBatch.resources.count()))];
@@ -790,21 +817,21 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
if (offsetOnlyChange)
continue;
- res[idx].textures.finish();
- res[idx].samplers.finish();
+ res[stage].textures.finish();
+ res[stage].samplers.finish();
- for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) {
- const auto &batch(res[idx].textures.batches[i]);
- switch (idx) {
- case 0:
+ for (int i = 0, ie = res[stage].textures.batches.count(); i != ie; ++i) {
+ const auto &batch(res[stage].textures.batches[i]);
+ switch (stage) {
+ case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 1:
+ case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 2:
+ case COMPUTE:
[cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
@@ -813,18 +840,18 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD
break;
}
}
- for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) {
- const auto &batch(res[idx].samplers.batches[i]);
- switch (idx) {
- case 0:
+ for (int i = 0, ie = res[stage].samplers.batches.count(); i != ie; ++i) {
+ const auto &batch(res[stage].samplers.batches[i]);
+ switch (stage) {
+ case VERTEX:
[cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 1:
+ case FRAGMENT:
[cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
- case 2:
+ case COMPUTE:
[cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
withRange: NSMakeRange(batch.startBinding, NSUInteger(batch.resources.count()))];
break;
@@ -973,18 +1000,22 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind
// dynamic uniform buffer offsets always trigger a rebind
if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
+ const QShader::NativeResourceBindingMap *resBindMaps[SUPPORTED_STAGES] = { nullptr, nullptr, nullptr };
if (gfxPsD) {
cbD->currentGraphicsSrb = srb;
cbD->currentComputeSrb = nullptr;
+ resBindMaps[0] = &gfxPsD->d->vs.nativeResourceBindingMap;
+ resBindMaps[1] = &gfxPsD->d->fs.nativeResourceBindingMap;
} else {
cbD->currentGraphicsSrb = nullptr;
cbD->currentComputeSrb = srb;
+ resBindMaps[2] = &compPsD->d->cs.nativeResourceBindingMap;
}
cbD->currentSrbGeneration = srbD->generation;
cbD->currentResSlot = resSlot;
const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
- enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange);
+ enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange, resBindMaps);
}
}
@@ -3081,9 +3112,10 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
}
id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
- QString *error, QByteArray *entryPoint)
+ QString *error, QByteArray *entryPoint, QShaderKey *activeKey)
{
- QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant });
+ QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant };
+ QShaderCode mtllib = shader.shader(key);
if (!mtllib.shader().isEmpty()) {
dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
size_t(mtllib.shader().size()),
@@ -3094,6 +3126,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
dispatch_release(data);
if (!err) {
*entryPoint = mtllib.entryPoint();
+ *activeKey = key;
return lib;
} else {
const QString msg = QString::fromNSString(err.localizedDescription);
@@ -3101,7 +3134,8 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
}
- QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant });
+ key = { QShader::MslShader, 12, shaderVariant };
+ QShaderCode mslSource = shader.shader(key);
if (mslSource.shader().isEmpty()) {
qWarning() << "No MSL 1.2 code found in baked shader" << shader;
return nil;
@@ -3122,6 +3156,7 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var
}
*entryPoint = mslSource.entryPoint();
+ *activeKey = key;
return lib;
}
@@ -3195,9 +3230,12 @@ bool QMetalGraphicsPipeline::build()
break;
}
} else {
+ const QShader shader = shaderStage.shader();
QString error;
QByteArray entryPoint;
- id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
+ QShaderKey activeKey;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, shaderStage.shaderVariant(),
+ &error, &entryPoint, &activeKey);
if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false;
@@ -3218,6 +3256,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Vertex:
d->vs.lib = lib;
d->vs.func = func;
+ if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
+ d->vs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->vs);
[d->vs.lib retain];
[d->vs.func retain];
@@ -3226,6 +3266,8 @@ bool QMetalGraphicsPipeline::build()
case QRhiShaderStage::Fragment:
d->fs.lib = lib;
d->fs.func = func;
+ if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
+ d->fs.nativeResourceBindingMap = *map;
rhiD->d->shaderCache.insert(shaderStage, d->fs);
[d->fs.lib retain];
[d->fs.func retain];
@@ -3360,8 +3402,9 @@ bool QMetalComputePipeline::build()
const QShader shader = m_shaderStage.shader();
QString error;
QByteArray entryPoint;
+ QShaderKey activeKey;
id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
- &error, &entryPoint);
+ &error, &entryPoint, &activeKey);
if (!lib) {
qWarning("MSL shader compilation failed: %s", qPrintable(error));
return false;
@@ -3375,6 +3418,8 @@ bool QMetalComputePipeline::build()
d->cs.lib = lib;
d->cs.func = func;
d->cs.localSize = shader.description().computeShaderLocalSize();
+ if (const QShader::NativeResourceBindingMap *map = shader.nativeResourceBindingMap(activeKey))
+ d->cs.nativeResourceBindingMap = *map;
if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) {
for (QMetalShader &s : rhiD->d->shaderCache)
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
index 688fec8147..2be86db5c8 100644
--- a/src/gui/rhi/qrhimetal_p_p.h
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -433,10 +433,13 @@ public:
qsizetype *curOfs);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
- void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+ static const int SUPPORTED_STAGES = 3;
+ void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD,
+ QMetalCommandBuffer *cbD,
int dynamicOffsetCount,
const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
- bool offsetOnlyChange);
+ bool offsetOnlyChange,
+ const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
int effectiveSampleCount(int sampleCount) const;
bool importedDevice = false;
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
index 6a2c596557..14d780b4e4 100644
--- a/src/gui/rhi/qshader.cpp
+++ b/src/gui/rhi/qshader.cpp
@@ -214,8 +214,6 @@ QT_BEGIN_NAMESPACE
QShader, it indicates no shader code was found for the requested key.
*/
-static const int QSB_VERSION = 1;
-
/*!
Constructs a new, empty (and thus invalid) QShader instance.
*/
@@ -345,6 +343,14 @@ void QShader::removeShader(const QShaderKey &key)
d->shaders.erase(it);
}
+static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
+{
+ *ds << k.source();
+ *ds << k.sourceVersion().version();
+ *ds << k.sourceVersion().flags();
+ *ds << k.sourceVariant();
+}
+
/*!
\return a serialized binary version of all the data held by the
QShader, suitable for writing to files or other I/O devices.
@@ -359,24 +365,48 @@ QByteArray QShader::serialized() const
if (!buf.open(QIODevice::WriteOnly))
return QByteArray();
- ds << QSB_VERSION;
+ ds << QShaderPrivate::QSB_VERSION;
ds << d->stage;
- ds << d->desc.toBinaryJson();
+ ds << d->desc.toCbor();
ds << d->shaders.count();
for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
const QShaderKey &k(it.key());
- ds << k.source();
- ds << k.sourceVersion().version();
- ds << k.sourceVersion().flags();
- ds << k.sourceVariant();
+ writeShaderKey(&ds, k);
const QShaderCode &shader(d->shaders.value(k));
ds << shader.shader();
ds << shader.entryPoint();
}
+ ds << d->bindings.count();
+ for (auto it = d->bindings.cbegin(), itEnd = d->bindings.cend(); it != itEnd; ++it) {
+ const QShaderKey &k(it.key());
+ writeShaderKey(&ds, k);
+ const NativeResourceBindingMap &map(it.value());
+ ds << map.count();
+ for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
+ ds << mapIt.key();
+ ds << mapIt.value().first;
+ ds << mapIt.value().second;
+ }
+ }
return qCompress(buf.buffer());
}
+static void readShaderKey(QDataStream *ds, QShaderKey *k)
+{
+ int intVal;
+ *ds >> intVal;
+ k->setSource(QShader::Source(intVal));
+ QShaderVersion ver;
+ *ds >> intVal;
+ ver.setVersion(intVal);
+ *ds >> intVal;
+ ver.setFlags(QShaderVersion::Flags(intVal));
+ k->setSourceVersion(ver);
+ *ds >> intVal;
+ k->setSourceVariant(QShader::Variant(intVal));
+}
+
/*!
Creates a new QShader instance from the given \a data.
@@ -396,28 +426,28 @@ QShader QShader::fromSerialized(const QByteArray &data)
Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
int intVal;
ds >> intVal;
- if (intVal != QSB_VERSION)
+ d->qsbVersion = intVal;
+ if (d->qsbVersion != QShaderPrivate::QSB_VERSION
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
+ && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS)
+ {
+ qWarning("Attempted to deserialize QShader with unknown version %d.", d->qsbVersion);
return QShader();
+ }
ds >> intVal;
d->stage = Stage(intVal);
QByteArray descBin;
ds >> descBin;
- d->desc = QShaderDescription::fromBinaryJson(descBin);
+ if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON)
+ d->desc = QShaderDescription::fromCbor(descBin);
+ else
+ d->desc = QShaderDescription::fromBinaryJson(descBin);
int count;
ds >> count;
for (int i = 0; i < count; ++i) {
QShaderKey k;
- ds >> intVal;
- k.setSource(Source(intVal));
- QShaderVersion ver;
- ds >> intVal;
- ver.setVersion(intVal);
- ds >> intVal;
- ver.setFlags(QShaderVersion::Flags(intVal));
- k.setSourceVersion(ver);
- ds >> intVal;
- k.setSourceVariant(Variant(intVal));
+ readShaderKey(&ds, &k);
QShaderCode shader;
QByteArray s;
ds >> s;
@@ -427,6 +457,27 @@ QShader QShader::fromSerialized(const QByteArray &data)
d->shaders[k] = shader;
}
+ if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) {
+ ds >> count;
+ for (int i = 0; i < count; ++i) {
+ QShaderKey k;
+ readShaderKey(&ds, &k);
+ NativeResourceBindingMap map;
+ int mapSize;
+ ds >> mapSize;
+ for (int b = 0; b < mapSize; ++b) {
+ int binding;
+ ds >> binding;
+ int firstNativeBinding;
+ ds >> firstNativeBinding;
+ int secondNativeBinding;
+ ds >> secondNativeBinding;
+ map.insert(binding, { firstNativeBinding, secondNativeBinding });
+ }
+ d->bindings.insert(k, map);
+ }
+ }
+
return bs;
}
@@ -460,7 +511,7 @@ bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
{
return lhs.d->stage == rhs.d->stage
&& lhs.d->shaders == rhs.d->shaders;
- // do not bother with desc, if the shader code is the same, the description must match too
+ // do not bother with desc and bindings, if the shader code is the same, the description must match too
}
/*!
@@ -586,4 +637,66 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
}
#endif // QT_NO_DEBUG_STREAM
+/*!
+ \typedef QShader::NativeResourceBindingMap
+
+ Synonym for QHash<int, QPair<int, int>>.
+
+ The resource binding model QRhi assumes is based on SPIR-V. This means that
+ uniform buffers, storage buffers, combined image samplers, and storage
+ images share a common binding point space. The binding numbers in
+ QShaderDescription and QRhiShaderResourceBinding are expected to match the
+ \c binding layout qualifier in the Vulkan-compatible GLSL shader.
+
+ Graphics APIs other than Vulkan may use a resource binding model that is
+ not fully compatible with this. In addition, the generator of the shader
+ code translated from SPIR-V may choose not to take the SPIR-V binding
+ qualifiers into account, for various reasons. (this is the case with the
+ Metal backend of SPIRV-Cross, for example).
+
+ Therefore, a QShader may expose an additional map that describes what the
+ native binding point for a given SPIR-V binding is. The QRhi backends are
+ expected to use this map automatically, as appropriate. The value is a
+ pair, because combined image samplers may map to two native resources (a
+ texture and a sampler) in some shading languages. In that case the second
+ value refers to the sampler.
+*/
+
+/*!
+ \return the native binding map for \a key or null if no extra mapping is
+ available, or is not applicable.
+ */
+const QShader::NativeResourceBindingMap *QShader::nativeResourceBindingMap(const QShaderKey &key) const
+{
+ auto it = d->bindings.constFind(key);
+ if (it == d->bindings.cend())
+ return nullptr;
+
+ return &it.value();
+}
+
+/*!
+ Stores the given native resource binding \a map associated with \a key.
+
+ \sa nativeResourceBindingMap()
+ */
+void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
+{
+ detach();
+ d->bindings[key] = map;
+}
+
+/*!
+ Removes the native resource binding map for \a key.
+ */
+void QShader::removeResourceBindingMap(const QShaderKey &key)
+{
+ auto it = d->bindings.find(key);
+ if (it == d->bindings.end())
+ return;
+
+ detach();
+ d->bindings.erase(it);
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h
index 243842a95a..4b561b6fa9 100644
--- a/src/gui/rhi/qshader_p.h
+++ b/src/gui/rhi/qshader_p.h
@@ -149,6 +149,11 @@ public:
QByteArray serialized() const;
static QShader fromSerialized(const QByteArray &data);
+ using NativeResourceBindingMap = QHash<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
+ const NativeResourceBindingMap *nativeResourceBindingMap(const QShaderKey &key) const;
+ void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
+ void removeResourceBindingMap(const QShaderKey &key);
+
private:
QShaderPrivate *d;
friend struct QShaderPrivate;
diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h
index 6473590e95..8c89f2b45f 100644
--- a/src/gui/rhi/qshader_p_p.h
+++ b/src/gui/rhi/qshader_p_p.h
@@ -57,6 +57,10 @@ QT_BEGIN_NAMESPACE
struct Q_GUI_EXPORT QShaderPrivate
{
+ static const int QSB_VERSION = 3;
+ static const int QSB_VERSION_WITH_BINARY_JSON = 2;
+ static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
+
QShaderPrivate()
: ref(1)
{
@@ -64,9 +68,11 @@ struct Q_GUI_EXPORT QShaderPrivate
QShaderPrivate(const QShaderPrivate *other)
: ref(1),
+ qsbVersion(other->qsbVersion),
stage(other->stage),
desc(other->desc),
- shaders(other->shaders)
+ shaders(other->shaders),
+ bindings(other->bindings)
{
}
@@ -74,9 +80,11 @@ struct Q_GUI_EXPORT QShaderPrivate
static const QShaderPrivate *get(const QShader *s) { return s->d; }
QAtomicInt ref;
+ int qsbVersion = QSB_VERSION;
QShader::Stage stage = QShader::VertexStage;
QShaderDescription desc;
QHash<QShaderKey, QShaderCode> shaders;
+ QHash<QShaderKey, QShader::NativeResourceBindingMap> bindings;
};
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
index 179d5f3a07..d0f73f6aa7 100644
--- a/src/gui/rhi/qshaderdescription.cpp
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -38,6 +38,9 @@
#include <QDebug>
#include <QJsonObject>
#include <QJsonArray>
+#include <QCborValue>
+#include <QCborMap>
+#include <QCborArray>
QT_BEGIN_NAMESPACE
@@ -335,11 +338,27 @@ bool QShaderDescription::isValid() const
/*!
\return a serialized binary version of the data.
- \sa toJson()
+ \sa toJson(), toCbor()
*/
QByteArray QShaderDescription::toBinaryJson() const
{
+#if QT_CONFIG(binaryjson)
return d->makeDoc().toBinaryData();
+#else
+ qWarning("Cannot generate binary JSON from QShaderDescription due to disabled binaryjson feature");
+ return QByteArray();
+#endif
+}
+
+/*!
+ \return a serialized binary version of the data in CBOR (Concise Binary
+ Object Representation) format.
+
+ \sa QCborValue, toBinaryJson(), toJson()
+ */
+QByteArray QShaderDescription::toCbor() const
+{
+ return QCborValue::fromJsonValue(d->makeDoc().object()).toCbor();
}
/*!
@@ -347,7 +366,7 @@ QByteArray QShaderDescription::toBinaryJson() const
\note There is no deserialization method provided for JSON text.
- \sa toBinaryJson()
+ \sa toBinaryJson(), toCbor()
*/
QByteArray QShaderDescription::toJson() const
{
@@ -357,11 +376,38 @@ QByteArray QShaderDescription::toJson() const
/*!
Deserializes the given binary JSON \a data and returns a new
QShaderDescription.
+
+ \sa fromCbor()
*/
QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data)
{
QShaderDescription desc;
+#if QT_CONFIG(binaryjson)
QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data));
+#else
+ Q_UNUSED(data);
+ qWarning("Cannot load QShaderDescription from binary JSON due to disabled binaryjson feature");
+#endif
+ return desc;
+}
+
+/*!
+ Deserializes the given CBOR \a data and returns a new QShaderDescription.
+
+ \sa fromBinaryJson()
+ */
+QShaderDescription QShaderDescription::fromCbor(const QByteArray &data)
+{
+ QShaderDescription desc;
+ const QCborValue cbor = QCborValue::fromCbor(data);
+ if (cbor.isMap()) {
+ const QJsonDocument doc(cbor.toMap().toJsonObject());
+ QShaderDescriptionPrivate::get(&desc)->loadDoc(doc);
+ }
+ if (cbor.isArray()) {
+ const QJsonDocument doc(cbor.toArray().toJsonArray());
+ QShaderDescriptionPrivate::get(&desc)->loadDoc(doc);
+ }
return desc;
}
@@ -1119,4 +1165,106 @@ void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
}
}
+/*!
+ Returns \c true if the two QShaderDescription objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription
+ */
+bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW
+{
+ if (lhs.d == rhs.d)
+ return true;
+
+ return lhs.d->inVars == rhs.d->inVars
+ && lhs.d->outVars == rhs.d->outVars
+ && lhs.d->uniformBlocks == rhs.d->uniformBlocks
+ && lhs.d->pushConstantBlocks == rhs.d->pushConstantBlocks
+ && lhs.d->storageBlocks == rhs.d->storageBlocks
+ && lhs.d->combinedImageSamplers == rhs.d->combinedImageSamplers
+ && lhs.d->storageImages == rhs.d->storageImages
+ && lhs.d->localSize == rhs.d->localSize;
+}
+
+/*!
+ Returns \c true if the two InOutVariable objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::InOutVariable
+ */
+bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW
+{
+ return lhs.name == rhs.name
+ && lhs.type == rhs.type
+ && lhs.location == rhs.location
+ && lhs.binding == rhs.binding
+ && lhs.descriptorSet == rhs.descriptorSet
+ && lhs.imageFormat == rhs.imageFormat
+ && lhs.imageFlags == rhs.imageFlags;
+}
+
+/*!
+ Returns \c true if the two BlockVariable objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::BlockVariable
+ */
+bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW
+{
+ return lhs.name == rhs.name
+ && lhs.type == rhs.type
+ && lhs.offset == rhs.offset
+ && lhs.size == rhs.size
+ && lhs.arrayDims == rhs.arrayDims
+ && lhs.arrayStride == rhs.arrayStride
+ && lhs.matrixStride == rhs.matrixStride
+ && lhs.matrixIsRowMajor == rhs.matrixIsRowMajor
+ && lhs.structMembers == rhs.structMembers;
+}
+
+/*!
+ Returns \c true if the two UniformBlock objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::UniformBlock
+ */
+bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW
+{
+ return lhs.blockName == rhs.blockName
+ && lhs.structName == rhs.structName
+ && lhs.size == rhs.size
+ && lhs.binding == rhs.binding
+ && lhs.descriptorSet == rhs.descriptorSet
+ && lhs.members == rhs.members;
+}
+
+/*!
+ Returns \c true if the two PushConstantBlock objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::PushConstantBlock
+ */
+bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW
+{
+ return lhs.name == rhs.name
+ && lhs.size == rhs.size
+ && lhs.members == rhs.members;
+}
+
+/*!
+ Returns \c true if the two StorageBlock objects \a lhs and \a rhs are
+ equal.
+
+ \relates QShaderDescription::StorageBlock
+ */
+bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW
+{
+ return lhs.blockName == rhs.blockName
+ && lhs.instanceName == rhs.instanceName
+ && lhs.knownSize == rhs.knownSize
+ && lhs.binding == rhs.binding
+ && lhs.descriptorSet == rhs.descriptorSet
+ && lhs.members == rhs.members;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
index 5a63b998cd..e02a53dcb5 100644
--- a/src/gui/rhi/qshaderdescription_p.h
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -69,9 +69,11 @@ public:
bool isValid() const;
QByteArray toBinaryJson() const;
+ QByteArray toCbor() const;
QByteArray toJson() const;
static QShaderDescription fromBinaryJson(const QByteArray &data);
+ static QShaderDescription fromCbor(const QByteArray &data);
enum VariableType {
Unknown = 0,
@@ -263,6 +265,7 @@ private:
#ifndef QT_NO_DEBUG_STREAM
friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
#endif
+ friend Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags)
@@ -276,6 +279,43 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlo
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &);
#endif
+Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::InOutVariable &lhs, const QShaderDescription::InOutVariable &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::BlockVariable &lhs, const QShaderDescription::BlockVariable &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
QT_END_NAMESPACE
#endif