rhi: metal: Add support for tessellation
Change-Id: Ie8d226a6a959aa5e78284ea72505fd26aec1e671 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
parent
855a9ca217
commit
c681c7c23f
@ -677,16 +677,17 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
can be set via
|
||||
\l{QRhiGraphicsPipeline::setPatchControlPointCount()}{setPatchControlPointCount()},
|
||||
and shaders for tessellation control and evaluation can be specified in the
|
||||
QRhiShaderStage list. Tessellation is considered an experimental feature in
|
||||
QRhi and can only be expected to be supported with Vulkan, OpenGL (ES), and
|
||||
Direct 3D, assuming the implementation reports it as supported at run time.
|
||||
Tessellation shaders have portability issues between APIs (for example,
|
||||
translating GLSL/SPIR-V to HLSL is problematic due to the way hull shaders
|
||||
are structured, whereas Metal uses a somewhat different tessellation
|
||||
pipeline than others), and therefore no guarantees can be given for a
|
||||
universal solution for now. (for Direct 3D in particular, handwritten HLSL
|
||||
hull and domain shaders must be injected into each QShader since qsb cannot
|
||||
generate these from SPIR-V)
|
||||
QRhiShaderStage list. Tessellation shaders have portability issues between
|
||||
APIs (for example, translating GLSL/SPIR-V to HLSL is problematic due to
|
||||
the way hull shaders are structured, whereas Metal uses a somewhat
|
||||
different tessellation pipeline than others), and therefore unexpected
|
||||
issues may still arise, even though basic functionality is implemented
|
||||
across all the underlying APIs. For Direct 3D in particular, handwritten
|
||||
HLSL hull and domain shaders must be injected into each QShader for the
|
||||
tessellation control and evaluation stages, respectively, since qsb cannot
|
||||
generate these from SPIR-V. Note that isoline tessellation should be
|
||||
avoided as it will not be supported by all backends. The maximum patch
|
||||
control point count portable between backends is 32.
|
||||
|
||||
\value GeometryShader Indicates that the geometry shader stage is
|
||||
supported. When supported, a geometry shader can be specified in the
|
||||
@ -695,9 +696,9 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
Direct 3D, OpenGL (3.2+) and OpenGL ES (3.2+), assuming the implementation
|
||||
reports it as supported at run time. Geometry shaders have portability
|
||||
issues between APIs, and therefore no guarantees can be given for a
|
||||
universal solution for now. (for Direct 3D in particular, a handwritten
|
||||
HLSL geometry shader must be injected into each QShader since qsb cannot
|
||||
generate this from SPIR-V)
|
||||
universal solution. They will never be supported with Metal. Whereas with
|
||||
Direct 3D a handwritten HLSL geometry shader must be injected into each
|
||||
QShader for the geometry stage since qsb cannot generate this from SPIR-V.
|
||||
|
||||
\value TextureArrayRange Indicates that for
|
||||
\l{QRhi::newTextureArray()}{texture arrays} it is possible to specify a
|
||||
@ -1403,6 +1404,85 @@ QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
|
||||
}
|
||||
#endif
|
||||
|
||||
QRhiVertexInputAttribute::Format QRhiImplementation::shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case QShaderDescription::Vec4:
|
||||
return QRhiVertexInputAttribute::Float4;
|
||||
case QShaderDescription::Vec3:
|
||||
return QRhiVertexInputAttribute::Float3;
|
||||
case QShaderDescription::Vec2:
|
||||
return QRhiVertexInputAttribute::Float2;
|
||||
case QShaderDescription::Float:
|
||||
return QRhiVertexInputAttribute::Float;
|
||||
|
||||
case QShaderDescription::Int4:
|
||||
return QRhiVertexInputAttribute::SInt4;
|
||||
case QShaderDescription::Int3:
|
||||
return QRhiVertexInputAttribute::SInt3;
|
||||
case QShaderDescription::Int2:
|
||||
return QRhiVertexInputAttribute::SInt2;
|
||||
case QShaderDescription::Int:
|
||||
return QRhiVertexInputAttribute::SInt;
|
||||
|
||||
case QShaderDescription::Uint4:
|
||||
return QRhiVertexInputAttribute::UInt4;
|
||||
case QShaderDescription::Uint3:
|
||||
return QRhiVertexInputAttribute::UInt3;
|
||||
case QShaderDescription::Uint2:
|
||||
return QRhiVertexInputAttribute::UInt2;
|
||||
case QShaderDescription::Uint:
|
||||
return QRhiVertexInputAttribute::UInt;
|
||||
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return QRhiVertexInputAttribute::Float;
|
||||
}
|
||||
}
|
||||
|
||||
quint32 QRhiImplementation::byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const
|
||||
{
|
||||
switch (format) {
|
||||
case QRhiVertexInputAttribute::Float4:
|
||||
return 4 * sizeof(float);
|
||||
case QRhiVertexInputAttribute::Float3:
|
||||
return 4 * sizeof(float); // vec3 still takes 16 bytes
|
||||
case QRhiVertexInputAttribute::Float2:
|
||||
return 2 * sizeof(float);
|
||||
case QRhiVertexInputAttribute::Float:
|
||||
return sizeof(float);
|
||||
|
||||
case QRhiVertexInputAttribute::UNormByte4:
|
||||
return 4 * sizeof(quint8);
|
||||
case QRhiVertexInputAttribute::UNormByte2:
|
||||
return 2 * sizeof(quint8);
|
||||
case QRhiVertexInputAttribute::UNormByte:
|
||||
return sizeof(quint8);
|
||||
|
||||
case QRhiVertexInputAttribute::UInt4:
|
||||
return 4 * sizeof(quint32);
|
||||
case QRhiVertexInputAttribute::UInt3:
|
||||
return 4 * sizeof(quint32); // ivec3 still takes 16 bytes
|
||||
case QRhiVertexInputAttribute::UInt2:
|
||||
return 2 * sizeof(quint32);
|
||||
case QRhiVertexInputAttribute::UInt:
|
||||
return sizeof(quint32);
|
||||
|
||||
case QRhiVertexInputAttribute::SInt4:
|
||||
return 4 * sizeof(qint32);
|
||||
case QRhiVertexInputAttribute::SInt3:
|
||||
return 4 * sizeof(qint32); // uvec3 still takes 16 bytes
|
||||
case QRhiVertexInputAttribute::SInt2:
|
||||
return 2 * sizeof(qint32);
|
||||
case QRhiVertexInputAttribute::SInt:
|
||||
return sizeof(qint32);
|
||||
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QRhiVertexInputLayout
|
||||
\internal
|
||||
|
@ -217,6 +217,9 @@ public:
|
||||
return accumulatedPipelineCreationTime;
|
||||
}
|
||||
|
||||
QRhiVertexInputAttribute::Format shaderDescVariableFormatToVertexInputFormat(QShaderDescription::VariableType type) const;
|
||||
quint32 byteSizePerVertexForVertexInputFormat(QRhiVertexInputAttribute::Format format) const;
|
||||
|
||||
QRhi *q;
|
||||
|
||||
static const int MAX_SHADER_CACHE_ENTRIES = 128;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -41,6 +41,9 @@ struct QMetalBuffer : public QRhiBuffer
|
||||
int lastActiveFrameSlot = -1;
|
||||
friend class QRhiMetal;
|
||||
friend struct QMetalShaderResourceBindings;
|
||||
|
||||
static constexpr int WorkBufPoolUsage = 1 << 8;
|
||||
static_assert(WorkBufPoolUsage > QRhiBuffer::StorageBuffer);
|
||||
};
|
||||
|
||||
struct QMetalRenderBufferData;
|
||||
@ -204,6 +207,7 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
|
||||
};
|
||||
|
||||
struct QMetalGraphicsPipelineData;
|
||||
struct QMetalCommandBuffer;
|
||||
|
||||
struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
|
||||
{
|
||||
@ -212,6 +216,13 @@ struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
|
||||
void destroy() override;
|
||||
bool create() override;
|
||||
|
||||
void makeActiveForCurrentRenderPassEncoder(QMetalCommandBuffer *cbD);
|
||||
void setupAttachmentsInMetalRenderPassDescriptor(void *metalRpDesc, QMetalRenderPassDescriptor *rpD);
|
||||
void setupMetalDepthStencilDescriptor(void *metalDsDesc);
|
||||
void mapStates();
|
||||
bool createVertexFragmentPipeline();
|
||||
bool createTessellationPipelines(const QShader &tessVert, const QShader &tesc, const QShader &tese, const QShader &tessFrag);
|
||||
|
||||
QMetalGraphicsPipelineData *d;
|
||||
uint generation = 0;
|
||||
int lastActiveFrameSlot = -1;
|
||||
@ -256,14 +267,14 @@ struct QMetalCommandBuffer : public QRhiCommandBuffer
|
||||
QRhiRenderTarget *currentTarget;
|
||||
|
||||
// per-pass (render or compute command encoder) volatile (cached) state
|
||||
QRhiGraphicsPipeline *currentGraphicsPipeline;
|
||||
QRhiComputePipeline *currentComputePipeline;
|
||||
QMetalGraphicsPipeline *currentGraphicsPipeline;
|
||||
QMetalComputePipeline *currentComputePipeline;
|
||||
uint currentPipelineGeneration;
|
||||
QRhiShaderResourceBindings *currentGraphicsSrb;
|
||||
QRhiShaderResourceBindings *currentComputeSrb;
|
||||
QMetalShaderResourceBindings *currentGraphicsSrb;
|
||||
QMetalShaderResourceBindings *currentComputeSrb;
|
||||
uint currentSrbGeneration;
|
||||
int currentResSlot;
|
||||
QRhiBuffer *currentIndexBuffer;
|
||||
QMetalBuffer *currentIndexBuffer;
|
||||
quint32 currentIndexOffset;
|
||||
QRhiCommandBuffer::IndexFormat currentIndexFormat;
|
||||
int currentCullMode;
|
||||
@ -442,6 +453,33 @@ public:
|
||||
bool offsetOnlyChange,
|
||||
const QShader::NativeResourceBindingMap *nativeResourceBindingMaps[SUPPORTED_STAGES]);
|
||||
int effectiveSampleCount(int sampleCount) const;
|
||||
struct TessDrawArgs {
|
||||
QMetalCommandBuffer *cbD;
|
||||
enum {
|
||||
NonIndexed,
|
||||
U16Indexed,
|
||||
U32Indexed
|
||||
} type;
|
||||
struct NonIndexedArgs {
|
||||
quint32 vertexCount;
|
||||
quint32 instanceCount;
|
||||
quint32 firstVertex;
|
||||
quint32 firstInstance;
|
||||
};
|
||||
struct IndexedArgs {
|
||||
quint32 indexCount;
|
||||
quint32 instanceCount;
|
||||
quint32 firstIndex;
|
||||
qint32 vertexOffset;
|
||||
quint32 firstInstance;
|
||||
void *indexBuffer;
|
||||
};
|
||||
union {
|
||||
NonIndexedArgs draw;
|
||||
IndexedArgs drawIndexed;
|
||||
};
|
||||
};
|
||||
void tessellatedDraw(const TessDrawArgs &args);
|
||||
|
||||
bool importedDevice = false;
|
||||
bool importedCmdQueue = false;
|
||||
|
@ -167,7 +167,31 @@ QT_BEGIN_NAMESPACE
|
||||
Describes what kind of shader code an entry contains.
|
||||
|
||||
\value StandardShader The normal, unmodified version of the shader code.
|
||||
|
||||
\value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
|
||||
|
||||
\value UInt16IndexedVertexAsComputeShader A vertex shader meant to be used
|
||||
in a Metal pipeline with tessellation in combination with indexed draw
|
||||
calls sourcing index data from a uint16 index buffer. To support the Metal
|
||||
tessellation pipeline, the vertex shader is translated to a compute shader
|
||||
that may be dependent on the index buffer usage in the draw calls (e.g. if
|
||||
the shader is using gl_VertexIndex), hence the need for three dedicated
|
||||
variants.
|
||||
|
||||
\value UInt32IndexedVertexAsComputeShader A vertex shader meant to be used
|
||||
in a Metal pipeline with tessellation in combination with indexed draw
|
||||
calls sourcing index data from a uint32 index buffer. To support the Metal
|
||||
tessellation pipeline, the vertex shader is translated to a compute shader
|
||||
that may be dependent on the index buffer usage in the draw calls (e.g. if
|
||||
the shader is using gl_VertexIndex), hence the need for three dedicated
|
||||
variants.
|
||||
|
||||
\value NonIndexedVertexAsComputeShader A vertex shader meant to be used in
|
||||
a Metal pipeline with tessellation in combination with non-indexed draw
|
||||
calls. To support the Metal tessellation pipeline, the vertex shader is
|
||||
translated to a compute shader that may be dependent on the index buffer
|
||||
usage in the draw calls (e.g. if the shader is using gl_VertexIndex), hence
|
||||
the need for three dedicated variants.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -367,6 +391,19 @@ QByteArray QShader::serialized() const
|
||||
ds << listIt->samplerBinding;
|
||||
}
|
||||
}
|
||||
ds << int(d->nativeShaderInfoMap.count());
|
||||
for (auto it = d->nativeShaderInfoMap.cbegin(), itEnd = d->nativeShaderInfoMap.cend(); it != itEnd; ++it) {
|
||||
const QShaderKey &k(it.key());
|
||||
writeShaderKey(&ds, k);
|
||||
ds << it->flags;
|
||||
ds << int(it->extraBufferBindings.count());
|
||||
for (auto mapIt = it->extraBufferBindings.cbegin(), mapItEnd = it->extraBufferBindings.cend();
|
||||
mapIt != mapItEnd; ++mapIt)
|
||||
{
|
||||
ds << mapIt.key();
|
||||
ds << mapIt.value();
|
||||
}
|
||||
}
|
||||
|
||||
return qCompress(buf.buffer());
|
||||
}
|
||||
@ -407,6 +444,7 @@ QShader QShader::fromSerialized(const QByteArray &data)
|
||||
ds >> intVal;
|
||||
d->qsbVersion = intVal;
|
||||
if (d->qsbVersion != QShaderPrivate::QSB_VERSION
|
||||
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO
|
||||
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS
|
||||
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_VAR_ARRAYDIMS
|
||||
&& d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
|
||||
@ -484,6 +522,26 @@ QShader QShader::fromSerialized(const QByteArray &data)
|
||||
}
|
||||
}
|
||||
|
||||
if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
|
||||
ds >> count;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
QShaderKey k;
|
||||
readShaderKey(&ds, &k);
|
||||
int flags;
|
||||
ds >> flags;
|
||||
QMap<int, int> extraBufferBindings;
|
||||
int mapSize;
|
||||
ds >> mapSize;
|
||||
for (int b = 0; b < mapSize; ++b) {
|
||||
int k, v;
|
||||
ds >> k;
|
||||
ds >> v;
|
||||
extraBufferBindings.insert(k, v);
|
||||
}
|
||||
d->nativeShaderInfoMap.insert(k, { flags, extraBufferBindings });
|
||||
}
|
||||
}
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
@ -711,7 +769,7 @@ QDebug operator<<(QDebug dbg, const QShaderVersion &v)
|
||||
/*!
|
||||
\typedef QShader::NativeResourceBindingMap
|
||||
|
||||
Synonym for QHash<int, QPair<int, int>>.
|
||||
Synonym for QMap<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
|
||||
@ -839,4 +897,62 @@ void QShader::removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &
|
||||
d->combinedImageMap.erase(it);
|
||||
}
|
||||
|
||||
/*!
|
||||
\struct QShader::NativeShaderInfo
|
||||
|
||||
Describes information about the native shader code, if applicable. This
|
||||
becomes relevant with certain shader languages for certain shader stages,
|
||||
in case the translation from SPIR-V involves the introduction of
|
||||
additional, "magic" inputs, outputs, or resources in the generated shader.
|
||||
Such additions may be dependent on the original source code (i.e. the usage
|
||||
of various GLSL language constructs or built-ins), and therefore it needs
|
||||
to be indicated in a dynamic manner if certain features got added to the
|
||||
generated shader code.
|
||||
|
||||
As an example, consider a tessellation control shader with a per-patch (not
|
||||
per-vertex) output variable. This is translated to a Metal compute shader
|
||||
outputting (among others) into an spvPatchOut buffer. But this buffer would
|
||||
not be present at all if per-patch output variables were not used. The fact
|
||||
that the shader code relies on such a buffer present can be indicated by
|
||||
the data in this struct.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\return the native shader info struct for \a key, or an empty object if
|
||||
there is no data available for \a key, for example because such a mapping
|
||||
is not applicable for the shading language or the shader stage.
|
||||
*/
|
||||
QShader::NativeShaderInfo QShader::nativeShaderInfo(const QShaderKey &key) const
|
||||
{
|
||||
auto it = d->nativeShaderInfoMap.constFind(key);
|
||||
if (it == d->nativeShaderInfoMap.cend())
|
||||
return {};
|
||||
|
||||
return it.value();
|
||||
}
|
||||
|
||||
/*!
|
||||
Stores the given native shader \a info associated with \a key.
|
||||
|
||||
\sa nativeShaderInfo()
|
||||
*/
|
||||
void QShader::setNativeShaderInfo(const QShaderKey &key, const NativeShaderInfo &info)
|
||||
{
|
||||
detach();
|
||||
d->nativeShaderInfoMap[key] = info;
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes the native shader information for \a key.
|
||||
*/
|
||||
void QShader::removeNativeShaderInfo(const QShaderKey &key)
|
||||
{
|
||||
auto it = d->nativeShaderInfoMap.find(key);
|
||||
if (it == d->nativeShaderInfoMap.end())
|
||||
return;
|
||||
|
||||
detach();
|
||||
d->nativeShaderInfoMap.erase(it);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <QtGui/qtguiglobal.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qmap.h>
|
||||
#include <private/qshaderdescription_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -102,7 +103,10 @@ public:
|
||||
|
||||
enum Variant {
|
||||
StandardShader = 0,
|
||||
BatchableVertexShader
|
||||
BatchableVertexShader,
|
||||
UInt16IndexedVertexAsComputeShader,
|
||||
UInt32IndexedVertexAsComputeShader,
|
||||
NonIndexedVertexAsComputeShader
|
||||
};
|
||||
|
||||
QShader();
|
||||
@ -127,7 +131,7 @@ public:
|
||||
QByteArray serialized() const;
|
||||
static QShader fromSerialized(const QByteArray &data);
|
||||
|
||||
using NativeResourceBindingMap = QHash<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
|
||||
using NativeResourceBindingMap = QMap<int, QPair<int, int> >; // binding -> native_binding[, native_binding]
|
||||
NativeResourceBindingMap nativeResourceBindingMap(const QShaderKey &key) const;
|
||||
void setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map);
|
||||
void removeResourceBindingMap(const QShaderKey &key);
|
||||
@ -143,6 +147,14 @@ public:
|
||||
const SeparateToCombinedImageSamplerMappingList &list);
|
||||
void removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key);
|
||||
|
||||
struct NativeShaderInfo {
|
||||
int flags = 0;
|
||||
QMap<int, int> extraBufferBindings;
|
||||
};
|
||||
NativeShaderInfo nativeShaderInfo(const QShaderKey &key) const;
|
||||
void setNativeShaderInfo(const QShaderKey &key, const NativeShaderInfo &info);
|
||||
void removeNativeShaderInfo(const QShaderKey &key);
|
||||
|
||||
private:
|
||||
QShaderPrivate *d;
|
||||
friend struct QShaderPrivate;
|
||||
|
@ -24,13 +24,23 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
struct Q_GUI_EXPORT QShaderPrivate
|
||||
{
|
||||
static const int QSB_VERSION = 6;
|
||||
static const int QSB_VERSION = 7;
|
||||
static const int QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO = 6;
|
||||
static const int QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS = 5;
|
||||
static const int QSB_VERSION_WITHOUT_VAR_ARRAYDIMS = 4;
|
||||
static const int QSB_VERSION_WITH_CBOR = 3;
|
||||
static const int QSB_VERSION_WITH_BINARY_JSON = 2;
|
||||
static const int QSB_VERSION_WITHOUT_BINDINGS = 1;
|
||||
|
||||
enum MslNativeShaderInfoExtraBufferBindings {
|
||||
MslTessVertIndicesBufferBinding = 0,
|
||||
MslTessVertTescOutputBufferBinding,
|
||||
MslTessTescTessLevelBufferBinding,
|
||||
MslTessTescPatchOutputBufferBinding,
|
||||
MslTessTescParamsBufferBinding,
|
||||
MslTessTescInputBufferBinding
|
||||
};
|
||||
|
||||
QShaderPrivate()
|
||||
: ref(1)
|
||||
{
|
||||
@ -43,7 +53,8 @@ struct Q_GUI_EXPORT QShaderPrivate
|
||||
desc(other.desc),
|
||||
shaders(other.shaders),
|
||||
bindings(other.bindings),
|
||||
combinedImageMap(other.combinedImageMap)
|
||||
combinedImageMap(other.combinedImageMap),
|
||||
nativeShaderInfoMap(other.nativeShaderInfoMap)
|
||||
{
|
||||
}
|
||||
|
||||
@ -58,6 +69,7 @@ struct Q_GUI_EXPORT QShaderPrivate
|
||||
QMap<QShaderKey, QShaderCode> shaders;
|
||||
QMap<QShaderKey, QShader::NativeResourceBindingMap> bindings;
|
||||
QMap<QShaderKey, QShader::SeparateToCombinedImageSamplerMappingList> combinedImageMap;
|
||||
QMap<QShaderKey, QShader::NativeShaderInfo> nativeShaderInfoMap;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -302,7 +302,8 @@ bool QShaderDescription::isValid() const
|
||||
return !d->inVars.isEmpty() || !d->outVars.isEmpty()
|
||||
|| !d->uniformBlocks.isEmpty() || !d->pushConstantBlocks.isEmpty() || !d->storageBlocks.isEmpty()
|
||||
|| !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty()
|
||||
|| !d->separateImages.isEmpty() || !d->separateSamplers.isEmpty();
|
||||
|| !d->separateImages.isEmpty() || !d->separateSamplers.isEmpty()
|
||||
|| !d->inBuiltins.isEmpty() || !d->outBuiltins.isEmpty();
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -514,7 +515,26 @@ QList<QShaderDescription::InOutVariable> QShaderDescription::storageImages() con
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the local size of a compute shader.
|
||||
\return the list of active builtins used as input. For example, a
|
||||
tessellation evaluation shader reading the value of gl_TessCoord and
|
||||
gl_Position will have TessCoordBuiltin and PositionBuiltin listed here.
|
||||
*/
|
||||
QVector<QShaderDescription::BuiltinVariable> QShaderDescription::inputBuiltinVariables() const
|
||||
{
|
||||
return d->inBuiltins;
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the list of active built-in variables used as input. For example, a
|
||||
vertex shader will very often have PositionBuiltin as an output built-in.
|
||||
*/
|
||||
QVector<QShaderDescription::BuiltinVariable> QShaderDescription::outputBuiltinVariables() const
|
||||
{
|
||||
return d->outBuiltins;
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the local size of a compute shader.
|
||||
|
||||
For example, for a compute shader with the following declaration the
|
||||
function returns { 256, 16, 1}.
|
||||
@ -528,6 +548,101 @@ std::array<uint, 3> QShaderDescription::computeShaderLocalSize() const
|
||||
return d->localSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the number of output vertices.
|
||||
|
||||
For example, for a tessellation control shader with the following
|
||||
declaration the function returns 3.
|
||||
|
||||
\badcode
|
||||
layout(vertices = 3) out;
|
||||
\endcode
|
||||
*/
|
||||
uint QShaderDescription::tessellationOutputVertexCount() const
|
||||
{
|
||||
return d->tessOutVertCount;
|
||||
}
|
||||
|
||||
/*!
|
||||
\enum QShaderDescription::TessellationMode
|
||||
|
||||
\value UnknownTessellationMode
|
||||
\value TrianglesTessellationMode
|
||||
\value QuadTessellationMode
|
||||
\value IsolinesTessellationMode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\return the tessellation execution mode for a tessellation control or
|
||||
evaluation shader.
|
||||
|
||||
When not set, the returned value is UnknownTessellationMode.
|
||||
|
||||
For example, for a tessellation evaluation shader with the following
|
||||
declaration the function returns TrianglesTessellationMode.
|
||||
|
||||
\badcode
|
||||
layout(triangles) in;
|
||||
\endcode
|
||||
*/
|
||||
QShaderDescription::TessellationMode QShaderDescription::tessellationMode() const
|
||||
{
|
||||
return d->tessMode;
|
||||
}
|
||||
|
||||
/*!
|
||||
\enum QShaderDescription::TessellationWindingOrder
|
||||
|
||||
\value UnknownTessellationWindingOrder
|
||||
\value CwTessellationWindingOrder
|
||||
\value CcwTessellationWindingOrder
|
||||
*/
|
||||
|
||||
/*!
|
||||
\return the tessellation winding order for a tessellation control or
|
||||
evaluation shader.
|
||||
|
||||
When not set, the returned value is UnknownTessellationWindingOrder.
|
||||
|
||||
For example, for a tessellation evaluation shader with the following
|
||||
declaration the function returns CcwTessellationWindingOrder.
|
||||
|
||||
\badcode
|
||||
layout(triangles, fractional_odd_spacing, ccw) in;
|
||||
\endcode
|
||||
*/
|
||||
QShaderDescription::TessellationWindingOrder QShaderDescription::tessellationWindingOrder() const
|
||||
{
|
||||
return d->tessWind;
|
||||
}
|
||||
|
||||
/*!
|
||||
\enum QShaderDescription::TessellationPartitioning
|
||||
|
||||
\value UnknownTessellationPartitioning
|
||||
\value EqualTessellationPartitioning
|
||||
\value FractionalEvenTessellationPartitioning
|
||||
\value FractionalOddTessellationPartitioning
|
||||
*/
|
||||
|
||||
/*!
|
||||
\return the tessellation partitioning mode for a tessellation control or
|
||||
evaluation shader.
|
||||
|
||||
When not set, the returned value is UnknownTessellationPartitioning.
|
||||
|
||||
For example, for a tessellation evaluation shader with the following
|
||||
declaration the function returns FractionalOddTessellationPartitioning.
|
||||
|
||||
\badcode
|
||||
layout(triangles, fractional_odd_spacing, ccw) in;
|
||||
\endcode
|
||||
*/
|
||||
QShaderDescription::TessellationPartitioning QShaderDescription::tessellationPartitioning() const
|
||||
{
|
||||
return d->tessPart;
|
||||
}
|
||||
|
||||
static const struct TypeTab {
|
||||
const char k[20];
|
||||
QShaderDescription::VariableType v;
|
||||
@ -607,7 +722,7 @@ static const struct TypeTab {
|
||||
{ "imageBuffer", QShaderDescription::ImageBuffer }
|
||||
};
|
||||
|
||||
static QLatin1StringView typeStr(const QShaderDescription::VariableType &t)
|
||||
static QLatin1StringView typeStr(QShaderDescription::VariableType t)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
|
||||
if (typeTab[i].v == t)
|
||||
@ -662,7 +777,7 @@ static const struct ImageFormatTab {
|
||||
{ "r8ui", QShaderDescription::ImageFormatR8ui }
|
||||
};
|
||||
|
||||
static QLatin1StringView imageFormatStr(const QShaderDescription::ImageFormat &f)
|
||||
static QLatin1StringView imageFormatStr(QShaderDescription::ImageFormat f)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
|
||||
if (imageFormatTab[i].v == f)
|
||||
@ -671,6 +786,106 @@ static QLatin1StringView imageFormatStr(const QShaderDescription::ImageFormat &f
|
||||
return {};
|
||||
}
|
||||
|
||||
static const struct BuiltinTypeTab {
|
||||
const char k[21];
|
||||
QShaderDescription::BuiltinType v;
|
||||
} builtinTypeTab[] = {
|
||||
{ "Position", QShaderDescription::PositionBuiltin },
|
||||
{ "PointSize", QShaderDescription::PointSizeBuiltin },
|
||||
{ "ClipDistance", QShaderDescription::ClipDistanceBuiltin },
|
||||
{ "CullDistance", QShaderDescription::CullDistanceBuiltin },
|
||||
{ "VertexId", QShaderDescription::VertexIdBuiltin },
|
||||
{ "InstanceId", QShaderDescription::InstanceIdBuiltin },
|
||||
{ "PrimitiveId", QShaderDescription::PrimitiveIdBuiltin },
|
||||
{ "InvocationId", QShaderDescription::InvocationIdBuiltin },
|
||||
{ "Layer", QShaderDescription::LayerBuiltin },
|
||||
{ "ViewportIndex", QShaderDescription::ViewportIndexBuiltin },
|
||||
{ "TessLevelOuter", QShaderDescription::TessLevelOuterBuiltin },
|
||||
{ "TessLevelInner", QShaderDescription::TessLevelInnerBuiltin },
|
||||
{ "TessCoord", QShaderDescription::TessCoordBuiltin },
|
||||
{ "PatchVertices", QShaderDescription::PatchVerticesBuiltin },
|
||||
{ "FragCoord", QShaderDescription::FragCoordBuiltin },
|
||||
{ "PointCoord", QShaderDescription::PointCoordBuiltin },
|
||||
{ "FrontFacing", QShaderDescription::FrontFacingBuiltin },
|
||||
{ "SampleId", QShaderDescription::SampleIdBuiltin },
|
||||
{ "SamplePosition", QShaderDescription::SamplePositionBuiltin },
|
||||
{ "SampleMask", QShaderDescription::SampleMaskBuiltin },
|
||||
{ "FragDepth", QShaderDescription::FragDepthBuiltin },
|
||||
{ "NumWorkGroups", QShaderDescription::NumWorkGroupsBuiltin },
|
||||
{ "WorkgroupSize", QShaderDescription::WorkgroupSizeBuiltin },
|
||||
{ "WorkgroupId", QShaderDescription::WorkgroupIdBuiltin },
|
||||
{ "LocalInvocationId", QShaderDescription::LocalInvocationIdBuiltin },
|
||||
{ "GlobalInvocationId", QShaderDescription::GlobalInvocationIdBuiltin },
|
||||
{ "LocalInvocationIndex", QShaderDescription::LocalInvocationIndexBuiltin },
|
||||
{ "VertexIndex", QShaderDescription::VertexIndexBuiltin },
|
||||
{ "InstanceIndex", QShaderDescription::InstanceIndexBuiltin }
|
||||
};
|
||||
|
||||
static QLatin1StringView builtinTypeStr(QShaderDescription::BuiltinType t)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(builtinTypeTab) / sizeof(BuiltinTypeTab); ++i) {
|
||||
if (builtinTypeTab[i].v == t)
|
||||
return QLatin1StringView(builtinTypeTab[i].k);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static const struct TessellationModeTab {
|
||||
const char k[10];
|
||||
QShaderDescription::TessellationMode v;
|
||||
} tessellationModeTab[] {
|
||||
{ "unknown", QShaderDescription::UnknownTessellationMode },
|
||||
{ "triangles", QShaderDescription::TrianglesTessellationMode },
|
||||
{ "quad", QShaderDescription::QuadTessellationMode },
|
||||
{ "isoline", QShaderDescription::IsolineTessellationMode }
|
||||
};
|
||||
|
||||
static QLatin1StringView tessModeStr(QShaderDescription::TessellationMode mode)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(tessellationModeTab) / sizeof(TessellationModeTab); ++i) {
|
||||
if (tessellationModeTab[i].v == mode)
|
||||
return QLatin1StringView(tessellationModeTab[i].k);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static const struct TessellationWindingOrderTab {
|
||||
const char k[8];
|
||||
QShaderDescription::TessellationWindingOrder v;
|
||||
} tessellationWindingOrderTab[] {
|
||||
{ "unknown", QShaderDescription::UnknownTessellationWindingOrder },
|
||||
{ "cw", QShaderDescription::CwTessellationWindingOrder },
|
||||
{ "ccw", QShaderDescription::CcwTessellationWindingOrder }
|
||||
};
|
||||
|
||||
static QLatin1StringView tessWindStr(QShaderDescription::TessellationWindingOrder w)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(tessellationWindingOrderTab) / sizeof(TessellationWindingOrderTab); ++i) {
|
||||
if (tessellationWindingOrderTab[i].v == w)
|
||||
return QLatin1StringView(tessellationWindingOrderTab[i].k);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static const struct TessellationPartitioningTab {
|
||||
const char k[24];
|
||||
QShaderDescription::TessellationPartitioning v;
|
||||
} tessellationPartitioningTab[] {
|
||||
{ "unknown", QShaderDescription::UnknownTessellationPartitioning },
|
||||
{ "equal_spacing", QShaderDescription::EqualTessellationPartitioning },
|
||||
{ "fractional_even_spacing", QShaderDescription::FractionalEvenTessellationPartitioning },
|
||||
{ "fractional_odd_spacing", QShaderDescription::FractionalOddTessellationPartitioning }
|
||||
};
|
||||
|
||||
static QLatin1StringView tessPartStr(QShaderDescription::TessellationPartitioning p)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(tessellationPartitioningTab) / sizeof(TessellationPartitioningTab); ++i) {
|
||||
if (tessellationPartitioningTab[i].v == p)
|
||||
return QLatin1StringView(tessellationPartitioningTab[i].k);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
|
||||
{
|
||||
@ -688,6 +903,8 @@ QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
|
||||
<< " storageImages " << d->storageImages
|
||||
<< " separateImages " << d->separateImages
|
||||
<< " separateSamplers " << d->separateSamplers
|
||||
<< " inBuiltins " << d->inBuiltins
|
||||
<< " outBuiltins " << d->outBuiltins
|
||||
<< ')';
|
||||
} else {
|
||||
dbg.nospace() << "QShaderDescription(null)";
|
||||
@ -700,6 +917,8 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
|
||||
{
|
||||
QDebugStateSaver saver(dbg);
|
||||
dbg.nospace() << "InOutVariable(" << typeStr(var.type) << ' ' << var.name;
|
||||
if (var.perPatch)
|
||||
dbg.nospace() << " per-patch";
|
||||
if (var.location >= 0)
|
||||
dbg.nospace() << " location=" << var.location;
|
||||
if (var.binding >= 0)
|
||||
@ -768,6 +987,13 @@ QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk)
|
||||
dbg.nospace() << ' ' << blk.members << ')';
|
||||
return dbg;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const QShaderDescription::BuiltinVariable &builtin)
|
||||
{
|
||||
QDebugStateSaver saver(dbg);
|
||||
dbg.nospace() << "BuiltinVariable(type=" << builtinTypeStr(builtin.type) << ")";
|
||||
return dbg;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define JSON_KEY(key) static constexpr QLatin1StringView key ## Key() noexcept { return QLatin1StringView( #key ); }
|
||||
@ -776,6 +1002,7 @@ JSON_KEY(type)
|
||||
JSON_KEY(location)
|
||||
JSON_KEY(binding)
|
||||
JSON_KEY(set)
|
||||
JSON_KEY(perPatch)
|
||||
JSON_KEY(imageFormat)
|
||||
JSON_KEY(imageFlags)
|
||||
JSON_KEY(offset)
|
||||
@ -797,7 +1024,13 @@ JSON_KEY(pushConstantBlocks)
|
||||
JSON_KEY(storageBlocks)
|
||||
JSON_KEY(combinedImageSamplers)
|
||||
JSON_KEY(storageImages)
|
||||
JSON_KEY(localSize)
|
||||
JSON_KEY(inBuiltins)
|
||||
JSON_KEY(outBuiltins)
|
||||
JSON_KEY(computeLocalSize)
|
||||
JSON_KEY(tessellationOutputVertexCount)
|
||||
JSON_KEY(tessellationMode)
|
||||
JSON_KEY(tessellationWindingOrder)
|
||||
JSON_KEY(tessellationPartitioning)
|
||||
JSON_KEY(separateImages)
|
||||
JSON_KEY(separateSamplers)
|
||||
#undef JSON_KEY
|
||||
@ -810,6 +1043,8 @@ static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v
|
||||
(*obj)[bindingKey()] = v.binding;
|
||||
if (v.descriptorSet >= 0)
|
||||
(*obj)[setKey()] = v.descriptorSet;
|
||||
if (v.perPatch)
|
||||
(*obj)[perPatchKey()] = v.perPatch;
|
||||
if (v.imageFormat != QShaderDescription::ImageFormatUnknown)
|
||||
(*obj)[imageFormatKey()] = imageFormatStr(v.imageFormat);
|
||||
if (v.imageFlags)
|
||||
@ -832,6 +1067,7 @@ static void serializeDecorations(QDataStream *stream, const QShaderDescription::
|
||||
(*stream) << int(v.arrayDims.count());
|
||||
for (int dim : v.arrayDims)
|
||||
(*stream) << dim;
|
||||
(*stream) << quint8(v.perPatch);
|
||||
}
|
||||
|
||||
static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
|
||||
@ -985,10 +1221,42 @@ QJsonDocument QShaderDescriptionPrivate::makeDoc()
|
||||
if (!jstorageImages.isEmpty())
|
||||
root[storageImagesKey()] = jstorageImages;
|
||||
|
||||
QJsonArray jlocalSize;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
jlocalSize.append(QJsonValue(int(localSize[i])));
|
||||
root[localSizeKey()] = jlocalSize;
|
||||
QJsonArray jinBuiltins;
|
||||
for (const QShaderDescription::BuiltinVariable &v : qAsConst(inBuiltins)) {
|
||||
QJsonObject builtin;
|
||||
builtin[typeKey()] = builtinTypeStr(v.type);
|
||||
jinBuiltins.append(builtin);
|
||||
}
|
||||
if (!jinBuiltins.isEmpty())
|
||||
root[inBuiltinsKey()] = jinBuiltins;
|
||||
|
||||
QJsonArray joutBuiltins;
|
||||
for (const QShaderDescription::BuiltinVariable &v : qAsConst(outBuiltins)) {
|
||||
QJsonObject builtin;
|
||||
builtin[typeKey()] = builtinTypeStr(v.type);
|
||||
joutBuiltins.append(builtin);
|
||||
}
|
||||
if (!joutBuiltins.isEmpty())
|
||||
root[outBuiltinsKey()] = joutBuiltins;
|
||||
|
||||
if (localSize[0] || localSize[1] || localSize[2]) {
|
||||
QJsonArray jlocalSize;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
jlocalSize.append(QJsonValue(int(localSize[i])));
|
||||
root[computeLocalSizeKey()] = jlocalSize;
|
||||
}
|
||||
|
||||
if (tessOutVertCount)
|
||||
root[tessellationOutputVertexCountKey()] = int(tessOutVertCount);
|
||||
|
||||
if (tessMode != QShaderDescription::UnknownTessellationMode)
|
||||
root[tessellationModeKey()] = tessModeStr(tessMode);
|
||||
|
||||
if (tessWind != QShaderDescription::UnknownTessellationWindingOrder)
|
||||
root[tessellationWindingOrderKey()] = tessWindStr(tessWind);
|
||||
|
||||
if (tessPart != QShaderDescription::UnknownTessellationPartitioning)
|
||||
root[tessellationPartitioningKey()] = tessPartStr(tessPart);
|
||||
|
||||
QJsonArray jseparateImages;
|
||||
for (const QShaderDescription::InOutVariable &v : qAsConst(separateImages)) {
|
||||
@ -1073,7 +1341,7 @@ void QShaderDescriptionPrivate::writeToStream(QDataStream *stream)
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
(*stream) << localSize[i];
|
||||
(*stream) << quint32(localSize[i]);
|
||||
|
||||
(*stream) << int(separateImages.count());
|
||||
for (const QShaderDescription::InOutVariable &v : qAsConst(separateImages)) {
|
||||
@ -1088,6 +1356,19 @@ void QShaderDescriptionPrivate::writeToStream(QDataStream *stream)
|
||||
(*stream) << int(v.type);
|
||||
serializeDecorations(stream, v);
|
||||
}
|
||||
|
||||
(*stream) << quint32(tessOutVertCount);
|
||||
(*stream) << quint32(tessMode);
|
||||
(*stream) << quint32(tessWind);
|
||||
(*stream) << quint32(tessPart);
|
||||
|
||||
(*stream) << int(inBuiltins.count());
|
||||
for (const QShaderDescription::BuiltinVariable &v : qAsConst(inBuiltins))
|
||||
(*stream) << int(v.type);
|
||||
|
||||
(*stream) << int(outBuiltins.count());
|
||||
for (const QShaderDescription::BuiltinVariable &v : qAsConst(outBuiltins))
|
||||
(*stream) << int(v.type);
|
||||
}
|
||||
|
||||
static void deserializeDecorations(QDataStream *stream, int version, QShaderDescription::InOutVariable *v)
|
||||
@ -1107,6 +1388,12 @@ static void deserializeDecorations(QDataStream *stream, int version, QShaderDesc
|
||||
for (int i = 0; i < f; ++i)
|
||||
(*stream) >> v->arrayDims[i];
|
||||
}
|
||||
|
||||
if (version > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
|
||||
quint8 b;
|
||||
(*stream) >> b;
|
||||
v->perPatch = b;
|
||||
}
|
||||
}
|
||||
|
||||
static QShaderDescription::InOutVariable deserializeInOutVar(QDataStream *stream, int version)
|
||||
@ -1237,8 +1524,11 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
|
||||
deserializeDecorations(stream, version, &storageImages[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
(*stream) >> localSize[i];
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
quint32 v;
|
||||
(*stream) >> v;
|
||||
localSize[i] = v;
|
||||
}
|
||||
|
||||
if (version > QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS) {
|
||||
(*stream) >> count;
|
||||
@ -1265,6 +1555,34 @@ void QShaderDescriptionPrivate::loadFromStream(QDataStream *stream, int version)
|
||||
deserializeDecorations(stream, version, &separateSamplers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (version > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
|
||||
quint32 v;
|
||||
(*stream) >> v;
|
||||
tessOutVertCount = v;
|
||||
(*stream) >> v;
|
||||
tessMode = QShaderDescription::TessellationMode(v);
|
||||
(*stream) >> v;
|
||||
tessWind = QShaderDescription::TessellationWindingOrder(v);
|
||||
(*stream) >> v;
|
||||
tessPart = QShaderDescription::TessellationPartitioning(v);
|
||||
|
||||
(*stream) >> count;
|
||||
inBuiltins.resize(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int t;
|
||||
(*stream) >> t;
|
||||
inBuiltins[i].type = QShaderDescription::BuiltinType(t);
|
||||
}
|
||||
|
||||
(*stream) >> count;
|
||||
outBuiltins.resize(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
int t;
|
||||
(*stream) >> t;
|
||||
outBuiltins[i].type = QShaderDescription::BuiltinType(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1287,7 +1605,13 @@ bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) no
|
||||
&& lhs.d->separateImages == rhs.d->separateImages
|
||||
&& lhs.d->separateSamplers == rhs.d->separateSamplers
|
||||
&& lhs.d->storageImages == rhs.d->storageImages
|
||||
&& lhs.d->localSize == rhs.d->localSize;
|
||||
&& lhs.d->inBuiltins == rhs.d->inBuiltins
|
||||
&& lhs.d->outBuiltins == rhs.d->outBuiltins
|
||||
&& lhs.d->localSize == rhs.d->localSize
|
||||
&& lhs.d->tessOutVertCount == rhs.d->tessOutVertCount
|
||||
&& lhs.d->tessMode == rhs.d->tessMode
|
||||
&& lhs.d->tessWind == rhs.d->tessWind
|
||||
&& lhs.d->tessPart == rhs.d->tessPart;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1305,7 +1629,8 @@ bool operator==(const QShaderDescription::InOutVariable &lhs, const QShaderDescr
|
||||
&& lhs.descriptorSet == rhs.descriptorSet
|
||||
&& lhs.imageFormat == rhs.imageFormat
|
||||
&& lhs.imageFlags == rhs.imageFlags
|
||||
&& lhs.arrayDims == rhs.arrayDims;
|
||||
&& lhs.arrayDims == rhs.arrayDims
|
||||
&& lhs.perPatch == rhs.perPatch;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1372,4 +1697,15 @@ bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescri
|
||||
&& lhs.members == rhs.members;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if the two BuiltinVariable objects \a lhs and \a rhs are
|
||||
equal.
|
||||
|
||||
\relates QShaderDescription::BuiltinVariable
|
||||
*/
|
||||
bool operator==(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept
|
||||
{
|
||||
return lhs.type == rhs.type;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -181,6 +181,7 @@ public:
|
||||
ImageFormat imageFormat = ImageFormatUnknown;
|
||||
ImageFlags imageFlags;
|
||||
QList<int> arrayDims;
|
||||
bool perPatch = false;
|
||||
};
|
||||
|
||||
struct BlockVariable {
|
||||
@ -229,8 +230,76 @@ public:
|
||||
QList<InOutVariable> separateSamplers() const;
|
||||
QList<InOutVariable> storageImages() const;
|
||||
|
||||
enum BuiltinType {
|
||||
// must match SpvBuiltIn
|
||||
PositionBuiltin = 0,
|
||||
PointSizeBuiltin = 1,
|
||||
ClipDistanceBuiltin = 3,
|
||||
CullDistanceBuiltin = 4,
|
||||
VertexIdBuiltin = 5,
|
||||
InstanceIdBuiltin = 6,
|
||||
PrimitiveIdBuiltin = 7,
|
||||
InvocationIdBuiltin = 8,
|
||||
LayerBuiltin = 9,
|
||||
ViewportIndexBuiltin = 10,
|
||||
TessLevelOuterBuiltin = 11,
|
||||
TessLevelInnerBuiltin = 12,
|
||||
TessCoordBuiltin = 13,
|
||||
PatchVerticesBuiltin = 14,
|
||||
FragCoordBuiltin = 15,
|
||||
PointCoordBuiltin = 16,
|
||||
FrontFacingBuiltin = 17,
|
||||
SampleIdBuiltin = 18,
|
||||
SamplePositionBuiltin = 19,
|
||||
SampleMaskBuiltin = 20,
|
||||
FragDepthBuiltin = 22,
|
||||
NumWorkGroupsBuiltin = 24,
|
||||
WorkgroupSizeBuiltin = 25,
|
||||
WorkgroupIdBuiltin = 26,
|
||||
LocalInvocationIdBuiltin = 27,
|
||||
GlobalInvocationIdBuiltin = 28,
|
||||
LocalInvocationIndexBuiltin = 29,
|
||||
VertexIndexBuiltin = 42,
|
||||
InstanceIndexBuiltin = 43
|
||||
};
|
||||
|
||||
struct BuiltinVariable {
|
||||
BuiltinType type;
|
||||
};
|
||||
|
||||
QList<BuiltinVariable> inputBuiltinVariables() const;
|
||||
QList<BuiltinVariable> outputBuiltinVariables() const;
|
||||
|
||||
std::array<uint, 3> computeShaderLocalSize() const;
|
||||
|
||||
uint tessellationOutputVertexCount() const;
|
||||
|
||||
enum TessellationMode {
|
||||
UnknownTessellationMode,
|
||||
TrianglesTessellationMode,
|
||||
QuadTessellationMode,
|
||||
IsolineTessellationMode
|
||||
};
|
||||
|
||||
TessellationMode tessellationMode() const;
|
||||
|
||||
enum TessellationWindingOrder {
|
||||
UnknownTessellationWindingOrder,
|
||||
CwTessellationWindingOrder,
|
||||
CcwTessellationWindingOrder
|
||||
};
|
||||
|
||||
TessellationWindingOrder tessellationWindingOrder() const;
|
||||
|
||||
enum TessellationPartitioning {
|
||||
UnknownTessellationPartitioning,
|
||||
EqualTessellationPartitioning,
|
||||
FractionalEvenTessellationPartitioning,
|
||||
FractionalOddTessellationPartitioning
|
||||
};
|
||||
|
||||
TessellationPartitioning tessellationPartitioning() const;
|
||||
|
||||
private:
|
||||
QShaderDescriptionPrivate *d;
|
||||
friend struct QShaderDescriptionPrivate;
|
||||
@ -249,6 +318,7 @@ Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BlockVariable &
|
||||
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::UniformBlock &);
|
||||
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlock &);
|
||||
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &);
|
||||
Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BuiltinVariable &);
|
||||
#endif
|
||||
|
||||
Q_GUI_EXPORT bool operator==(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept;
|
||||
@ -257,6 +327,7 @@ Q_GUI_EXPORT bool operator==(const QShaderDescription::BlockVariable &lhs, const
|
||||
Q_GUI_EXPORT bool operator==(const QShaderDescription::UniformBlock &lhs, const QShaderDescription::UniformBlock &rhs) noexcept;
|
||||
Q_GUI_EXPORT bool operator==(const QShaderDescription::PushConstantBlock &lhs, const QShaderDescription::PushConstantBlock &rhs) noexcept;
|
||||
Q_GUI_EXPORT bool operator==(const QShaderDescription::StorageBlock &lhs, const QShaderDescription::StorageBlock &rhs) noexcept;
|
||||
Q_GUI_EXPORT bool operator==(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept;
|
||||
|
||||
inline bool operator!=(const QShaderDescription &lhs, const QShaderDescription &rhs) noexcept
|
||||
{
|
||||
@ -288,6 +359,11 @@ inline bool operator!=(const QShaderDescription::StorageBlock &lhs, const QShade
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline bool operator!=(const QShaderDescription::BuiltinVariable &lhs, const QShaderDescription::BuiltinVariable &rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
@ -27,7 +27,6 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
|
||||
QShaderDescriptionPrivate()
|
||||
: ref(1)
|
||||
{
|
||||
localSize[0] = localSize[1] = localSize[2] = 0;
|
||||
}
|
||||
|
||||
QShaderDescriptionPrivate(const QShaderDescriptionPrivate &other)
|
||||
@ -41,7 +40,13 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
|
||||
separateImages(other.separateImages),
|
||||
separateSamplers(other.separateSamplers),
|
||||
storageImages(other.storageImages),
|
||||
localSize(other.localSize)
|
||||
inBuiltins(other.inBuiltins),
|
||||
outBuiltins(other.outBuiltins),
|
||||
localSize(other.localSize),
|
||||
tessOutVertCount(other.tessOutVertCount),
|
||||
tessMode(other.tessMode),
|
||||
tessWind(other.tessWind),
|
||||
tessPart(other.tessPart)
|
||||
{
|
||||
}
|
||||
|
||||
@ -62,7 +67,13 @@ struct Q_GUI_EXPORT QShaderDescriptionPrivate
|
||||
QList<QShaderDescription::InOutVariable> separateImages;
|
||||
QList<QShaderDescription::InOutVariable> separateSamplers;
|
||||
QList<QShaderDescription::InOutVariable> storageImages;
|
||||
std::array<uint, 3> localSize;
|
||||
QList<QShaderDescription::BuiltinVariable> inBuiltins;
|
||||
QList<QShaderDescription::BuiltinVariable> outBuiltins;
|
||||
std::array<uint, 3> localSize = {};
|
||||
uint tessOutVertCount = 0;
|
||||
QShaderDescription::TessellationMode tessMode = QShaderDescription::UnknownTessellationMode;
|
||||
QShaderDescription::TessellationWindingOrder tessWind = QShaderDescription::UnknownTessellationWindingOrder;
|
||||
QShaderDescription::TessellationPartitioning tessPart = QShaderDescription::UnknownTessellationPartitioning;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -11,3 +11,7 @@ qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.vert.qsb textured.
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured.frag.qsb textured.frag
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured_multiubuf.vert.qsb textured_multiubuf.vert
|
||||
qsb --glsl "150,120,100 es" --hlsl 50 -c --msl 12 -o textured_multiubuf.frag.qsb textured_multiubuf.frag
|
||||
qsb --glsl 320es,410 --msl 12 --msltess simpletess.vert -o simpletess.vert.qsb
|
||||
qsb --glsl 320es,410 --msl 12 --tess-mode triangles simpletess.tesc -o simpletess.tesc.qsb
|
||||
qsb --glsl 320es,410 --msl 12 --tess-vertex-count 3 simpletess.tese -o simpletess.tese.qsb
|
||||
qsb --glsl 320es,410 --msl 12 simpletess.frag -o simpletess.frag.qsb
|
||||
|
10
tests/auto/gui/rhi/qrhi/data/simpletess.frag
Normal file
10
tests/auto/gui/rhi/qrhi/data/simpletess.frag
Normal file
@ -0,0 +1,10 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec3 v_color;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = vec4(v_color, 1.0);
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.frag.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.frag.qsb
Normal file
Binary file not shown.
22
tests/auto/gui/rhi/qrhi/data/simpletess.tesc
Normal file
22
tests/auto/gui/rhi/qrhi/data/simpletess.tesc
Normal file
@ -0,0 +1,22 @@
|
||||
#version 440
|
||||
|
||||
layout(vertices = 3) out;
|
||||
|
||||
layout(location = 0) in vec3 inColor[];
|
||||
layout(location = 0) out vec3 outColor[];
|
||||
layout(location = 1) patch out float a_per_patch_output_variable;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (gl_InvocationID == 0) {
|
||||
gl_TessLevelOuter[0] = 4.0;
|
||||
gl_TessLevelOuter[1] = 4.0;
|
||||
gl_TessLevelOuter[2] = 4.0;
|
||||
|
||||
gl_TessLevelInner[0] = 4.0;
|
||||
}
|
||||
|
||||
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||
outColor[gl_InvocationID] = inColor[gl_InvocationID];
|
||||
a_per_patch_output_variable = 1.0;
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.tesc.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.tesc.qsb
Normal file
Binary file not shown.
17
tests/auto/gui/rhi/qrhi/data/simpletess.tese
Normal file
17
tests/auto/gui/rhi/qrhi/data/simpletess.tese
Normal file
@ -0,0 +1,17 @@
|
||||
#version 440
|
||||
|
||||
layout(triangles, fractional_odd_spacing, ccw) in;
|
||||
|
||||
layout(location = 0) in vec3 inColor[];
|
||||
layout(location = 0) out vec3 outColor;
|
||||
layout(location = 1) patch in float a_per_patch_output_variable;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 mvp;
|
||||
};
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = mvp * ((gl_TessCoord.x * gl_in[0].gl_Position) + (gl_TessCoord.y * gl_in[1].gl_Position) + (gl_TessCoord.z * gl_in[2].gl_Position));
|
||||
outColor = gl_TessCoord.x * inColor[0] + gl_TessCoord.y * inColor[1] + gl_TessCoord.z * inColor[2] * a_per_patch_output_variable;
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.tese.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.tese.qsb
Normal file
Binary file not shown.
12
tests/auto/gui/rhi/qrhi/data/simpletess.vert
Normal file
12
tests/auto/gui/rhi/qrhi/data/simpletess.vert
Normal file
@ -0,0 +1,12 @@
|
||||
#version 440
|
||||
|
||||
layout(location = 0) in vec3 position;
|
||||
layout(location = 1) in vec3 color;
|
||||
|
||||
layout(location = 0) out vec3 v_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(position, 1.0);
|
||||
v_color = color;
|
||||
}
|
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.vert.qsb
Normal file
BIN
tests/auto/gui/rhi/qrhi/data/simpletess.vert.qsb
Normal file
Binary file not shown.
@ -136,6 +136,9 @@ private slots:
|
||||
void renderToRgb10Texture_data();
|
||||
void renderToRgb10Texture();
|
||||
|
||||
void tessellation_data();
|
||||
void tessellation();
|
||||
|
||||
private:
|
||||
void setWindowType(QWindow *window, QRhi::Implementation impl);
|
||||
|
||||
@ -4899,5 +4902,165 @@ void tst_QRhi::renderToRgb10Texture()
|
||||
QVERIFY(redCount > blueCount); // 1742 > 178
|
||||
}
|
||||
|
||||
void tst_QRhi::tessellation_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::tessellation()
|
||||
{
|
||||
QFETCH(QRhi::Implementation, impl);
|
||||
QFETCH(QRhiInitParams *, initParams);
|
||||
|
||||
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
|
||||
if (!rhi)
|
||||
QSKIP("QRhi could not be created, skipping testing rendering");
|
||||
|
||||
if (!rhi->isFeatureSupported(QRhi::Tessellation)) {
|
||||
// From a Vulkan or Metal implementation we expect tessellation to work,
|
||||
// even though it is optional (as per spec) for Vulkan.
|
||||
QVERIFY(rhi->backend() != QRhi::Vulkan);
|
||||
QVERIFY(rhi->backend() != QRhi::Metal);
|
||||
QSKIP("Tessellation is not supported with this graphics API, skipping test");
|
||||
}
|
||||
|
||||
if (rhi->backend() == QRhi::D3D11)
|
||||
QSKIP("Skipping tessellation test on D3D for now, test assets not prepared for HLSL yet");
|
||||
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, QSize(1280, 720), 1,
|
||||
QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
|
||||
QVERIFY(texture->create());
|
||||
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ texture.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->create());
|
||||
|
||||
static const float triangleVertices[] = {
|
||||
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
};
|
||||
|
||||
QRhiResourceUpdateBatch *u = rhi->nextResourceUpdateBatch();
|
||||
QScopedPointer<QRhiBuffer> vbuf(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangleVertices)));
|
||||
QVERIFY(vbuf->create());
|
||||
u->uploadStaticBuffer(vbuf.data(), triangleVertices);
|
||||
|
||||
QScopedPointer<QRhiBuffer> ubuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64));
|
||||
QVERIFY(ubuf->create());
|
||||
|
||||
// Use the 3D API specific correction matrix that flips Y, so we can use
|
||||
// the OpenGL-targeted vertex data and the tessellation winding order of
|
||||
// counter-clockwise to get uniform results.
|
||||
QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
|
||||
u->updateDynamicBuffer(ubuf.data(), 0, 64, mvp.constData());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb(rhi->newShaderResourceBindings());
|
||||
srb->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::TessellationEvaluationStage, ubuf.data()),
|
||||
});
|
||||
QVERIFY(srb->create());
|
||||
|
||||
QScopedPointer<QRhiGraphicsPipeline> pipeline(rhi->newGraphicsPipeline());
|
||||
|
||||
pipeline->setTopology(QRhiGraphicsPipeline::Patches);
|
||||
pipeline->setPatchControlPointCount(3);
|
||||
|
||||
pipeline->setShaderStages({
|
||||
{ QRhiShaderStage::Vertex, loadShader(":/data/simpletess.vert.qsb") },
|
||||
{ QRhiShaderStage::TessellationControl, loadShader(":/data/simpletess.tesc.qsb") },
|
||||
{ QRhiShaderStage::TessellationEvaluation, loadShader(":/data/simpletess.tese.qsb") },
|
||||
{ QRhiShaderStage::Fragment, loadShader(":/data/simpletess.frag.qsb") }
|
||||
});
|
||||
|
||||
pipeline->setCullMode(QRhiGraphicsPipeline::Back); // to ensure the winding order is correct
|
||||
|
||||
// won't get the wireframe with OpenGL ES
|
||||
if (rhi->isFeatureSupported(QRhi::NonFillPolygonMode))
|
||||
pipeline->setPolygonMode(QRhiGraphicsPipeline::Line);
|
||||
|
||||
QRhiVertexInputLayout inputLayout;
|
||||
inputLayout.setBindings({
|
||||
{ 6 * sizeof(float) }
|
||||
});
|
||||
inputLayout.setAttributes({
|
||||
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
|
||||
{ 0, 1, QRhiVertexInputAttribute::Float3, 3 * sizeof(float) }
|
||||
});
|
||||
|
||||
pipeline->setVertexInputLayout(inputLayout);
|
||||
pipeline->setShaderResourceBindings(srb.data());
|
||||
pipeline->setRenderPassDescriptor(rpDesc.data());
|
||||
|
||||
QVERIFY(pipeline->create());
|
||||
|
||||
QRhiCommandBuffer *cb = nullptr;
|
||||
QCOMPARE(rhi->beginOffscreenFrame(&cb), QRhi::FrameOpSuccess);
|
||||
|
||||
cb->beginPass(rt.data(), Qt::black, { 1.0f, 0 }, u);
|
||||
cb->setGraphicsPipeline(pipeline.data());
|
||||
cb->setViewport({ 0, 0, float(rt->pixelSize().width()), float(rt->pixelSize().height()) });
|
||||
cb->setShaderResources();
|
||||
QRhiCommandBuffer::VertexInput vbufBinding(vbuf.data(), 0);
|
||||
cb->setVertexInput(0, 1, &vbufBinding);
|
||||
cb->draw(3);
|
||||
|
||||
QRhiReadbackResult readResult;
|
||||
QImage result;
|
||||
readResult.completed = [&readResult, &result] {
|
||||
result = QImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
|
||||
readResult.pixelSize.width(), readResult.pixelSize.height(),
|
||||
QImage::Format_RGBA8888);
|
||||
};
|
||||
QRhiResourceUpdateBatch *readbackBatch = rhi->nextResourceUpdateBatch();
|
||||
readbackBatch->readBackTexture({ texture.data() }, &readResult);
|
||||
cb->endPass(readbackBatch);
|
||||
|
||||
rhi->endOffscreenFrame();
|
||||
|
||||
if (rhi->isYUpInFramebuffer()) // we used clipSpaceCorrMatrix so this is different from many other tests
|
||||
result = std::move(result).mirrored();
|
||||
|
||||
QCOMPARE(result.size(), rt->pixelSize());
|
||||
|
||||
// cannot check rendering results with Null, because there is no rendering there
|
||||
if (impl == QRhi::Null)
|
||||
return;
|
||||
|
||||
int redCount = 0, greenCount = 0, blueCount = 0;
|
||||
for (int y = 0; y < result.height(); ++y) {
|
||||
const quint32 *p = reinterpret_cast<const quint32 *>(result.constScanLine(y));
|
||||
int x = result.width() - 1;
|
||||
while (x-- >= 0) {
|
||||
const QRgb c(*p++);
|
||||
const int red = qRed(c);
|
||||
const int green = qGreen(c);
|
||||
const int blue = qBlue(c);
|
||||
// just count the color components that are above a certain threshold
|
||||
if (red > 240)
|
||||
++redCount;
|
||||
if (green > 240)
|
||||
++greenCount;
|
||||
if (blue > 240)
|
||||
++blueCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Line drawing can be different between the 3D APIs. What we will check if
|
||||
// the number of strong-enough r/g/b components above a certain threshold.
|
||||
// That is good enough to ensure that something got rendered, i.e. that
|
||||
// tessellation is not completely broken.
|
||||
//
|
||||
// For the record the actual values are something like:
|
||||
// OpenGL (NVIDIA, Windows) 59 82 82
|
||||
// Metal (Intel, macOS 12.5) 59 79 79
|
||||
// Vulkan (NVIDIA, Windows) 71 85 85
|
||||
|
||||
QVERIFY(redCount > 50);
|
||||
QVERIFY(blueCount > 50);
|
||||
QVERIFY(greenCount > 50);
|
||||
}
|
||||
|
||||
#include <tst_qrhi.moc>
|
||||
QTEST_MAIN(tst_QRhi)
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -25,6 +25,7 @@ private slots:
|
||||
void loadV4();
|
||||
void manualShaderPackCreation();
|
||||
void loadV6WithSeparateImagesAndSamplers();
|
||||
void loadV7();
|
||||
};
|
||||
|
||||
static QShader getShader(const QString &name)
|
||||
@ -590,5 +591,87 @@ void tst_QShader::loadV6WithSeparateImagesAndSamplers()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QShader::loadV7()
|
||||
{
|
||||
QShader vert = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.vert.qsb"));
|
||||
QVERIFY(vert.isValid());
|
||||
QCOMPARE(QShaderPrivate::get(&vert)->qsbVersion, 7);
|
||||
QCOMPARE(vert.availableShaders().count(), 8);
|
||||
|
||||
QCOMPARE(vert.description().inputVariables().count(), 2);
|
||||
QCOMPARE(vert.description().outputBuiltinVariables().count(), 1);
|
||||
QCOMPARE(vert.description().outputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin);
|
||||
QCOMPARE(vert.description().outputVariables().count(), 1);
|
||||
QCOMPARE(vert.description().outputVariables()[0].name, QByteArrayLiteral("v_color"));
|
||||
|
||||
QVERIFY(vert.availableShaders().contains(QShaderKey(QShader::MslShader, QShaderVersion(12))));
|
||||
QVERIFY(!vert.shader(QShaderKey(QShader::MslShader, QShaderVersion(12), QShader::NonIndexedVertexAsComputeShader)).shader().isEmpty());
|
||||
QVERIFY(!vert.shader(QShaderKey(QShader::MslShader, QShaderVersion(12), QShader::UInt16IndexedVertexAsComputeShader)).shader().isEmpty());
|
||||
QVERIFY(!vert.shader(QShaderKey(QShader::MslShader, QShaderVersion(12), QShader::UInt32IndexedVertexAsComputeShader)).shader().isEmpty());
|
||||
|
||||
QShader tesc = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.tesc.qsb"));
|
||||
QVERIFY(tesc.isValid());
|
||||
QCOMPARE(QShaderPrivate::get(&tesc)->qsbVersion, 7);
|
||||
QCOMPARE(tesc.availableShaders().count(), 5);
|
||||
QCOMPARE(tesc.description().tessellationOutputVertexCount(), 3);
|
||||
|
||||
QCOMPARE(tesc.description().inputBuiltinVariables().count(), 2);
|
||||
QCOMPARE(tesc.description().outputBuiltinVariables().count(), 3);
|
||||
// builtins must be sorted based on the type
|
||||
QCOMPARE(tesc.description().inputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin);
|
||||
QCOMPARE(tesc.description().inputBuiltinVariables()[1].type, QShaderDescription::InvocationIdBuiltin);
|
||||
QCOMPARE(tesc.description().outputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin);
|
||||
QCOMPARE(tesc.description().outputBuiltinVariables()[1].type, QShaderDescription::TessLevelOuterBuiltin);
|
||||
QCOMPARE(tesc.description().outputBuiltinVariables()[2].type, QShaderDescription::TessLevelInnerBuiltin);
|
||||
|
||||
QCOMPARE(tesc.description().outputVariables().count(), 3);
|
||||
for (const QShaderDescription::InOutVariable &v : tesc.description().outputVariables()) {
|
||||
switch (v.location) {
|
||||
case 0:
|
||||
QCOMPARE(v.name, QByteArrayLiteral("outColor"));
|
||||
QCOMPARE(v.type, QShaderDescription::Vec3);
|
||||
QCOMPARE(v.perPatch, false);
|
||||
break;
|
||||
case 1:
|
||||
QCOMPARE(v.name, QByteArrayLiteral("stuff"));
|
||||
QCOMPARE(v.type, QShaderDescription::Vec3);
|
||||
QCOMPARE(v.perPatch, true);
|
||||
break;
|
||||
case 2:
|
||||
QCOMPARE(v.name, QByteArrayLiteral("more_stuff"));
|
||||
QCOMPARE(v.type, QShaderDescription::Float);
|
||||
QCOMPARE(v.perPatch, true);
|
||||
break;
|
||||
default:
|
||||
QFAIL(qPrintable(QStringLiteral("Bad location: %1").arg(v.location)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY(!tesc.shader(QShaderKey(QShader::MslShader, QShaderVersion(12))).shader().isEmpty());
|
||||
QCOMPARE(tesc.nativeShaderInfo(QShaderKey(QShader::SpirvShader, QShaderVersion(100))).extraBufferBindings.count(), 0);
|
||||
QCOMPARE(tesc.nativeShaderInfo(QShaderKey(QShader::MslShader, QShaderVersion(12))).extraBufferBindings.count(), 5);
|
||||
|
||||
QShader tese = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.tese.qsb"));
|
||||
QVERIFY(tese.isValid());
|
||||
QCOMPARE(QShaderPrivate::get(&tese)->qsbVersion, 7);
|
||||
QCOMPARE(tese.availableShaders().count(), 5);
|
||||
QCOMPARE(tese.description().tessellationMode(), QShaderDescription::TrianglesTessellationMode);
|
||||
QCOMPARE(tese.description().tessellationWindingOrder(), QShaderDescription::CcwTessellationWindingOrder);
|
||||
QCOMPARE(tese.description().tessellationPartitioning(), QShaderDescription::FractionalOddTessellationPartitioning);
|
||||
|
||||
QCOMPARE(tese.description().inputBuiltinVariables()[0].type, QShaderDescription::PositionBuiltin);
|
||||
QCOMPARE(tese.description().inputBuiltinVariables()[1].type, QShaderDescription::TessLevelOuterBuiltin);
|
||||
QCOMPARE(tese.description().inputBuiltinVariables()[2].type, QShaderDescription::TessLevelInnerBuiltin);
|
||||
QCOMPARE(tese.description().inputBuiltinVariables()[3].type, QShaderDescription::TessCoordBuiltin);
|
||||
|
||||
QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).count(), 1);
|
||||
QCOMPARE(tese.nativeResourceBindingMap(QShaderKey(QShader::MslShader, QShaderVersion(12))).value(0), qMakePair(0, -1));
|
||||
|
||||
QShader frag = getShader(QLatin1String(":/data/metal_enabled_tessellation_v7.frag.qsb"));
|
||||
QVERIFY(frag.isValid());
|
||||
QCOMPARE(QShaderPrivate::get(&frag)->qsbVersion, 7);
|
||||
}
|
||||
|
||||
#include <tst_qshader.moc>
|
||||
QTEST_MAIN(tst_QShader)
|
||||
|
@ -1,6 +1,6 @@
|
||||
qsb --glsl 320es,410 --hlsl 50 test.vert -o test.vert.qsb
|
||||
qsb --glsl 320es,410 test.tesc -o test.tesc.qsb
|
||||
qsb --glsl 320es,410 --hlsl 50 --msl 12 --msltess test.vert -o test.vert.qsb
|
||||
qsb --glsl 320es,410 --msl 12 --tess-mode triangles test.tesc -o test.tesc.qsb
|
||||
qsb -r hlsl,50,test_hull.hlsl test.tesc.qsb
|
||||
qsb --glsl 320es,410 test.tese -o test.tese.qsb
|
||||
qsb --glsl 320es,410 --msl 12 --tess-vertex-count 3 test.tese -o test.tese.qsb
|
||||
qsb -r hlsl,50,test_domain.hlsl test.tese.qsb
|
||||
qsb --glsl 320es,410 --hlsl 50 test.frag -o test.frag.qsb
|
||||
qsb --glsl 320es,410 --hlsl 50 --msl 12 test.frag -o test.frag.qsb
|
||||
|
@ -9,9 +9,13 @@ static const float tri[] = {
|
||||
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
};
|
||||
|
||||
static const bool INDEXED = false;
|
||||
static const quint32 indices[] = { 0, 1, 2 };
|
||||
|
||||
struct {
|
||||
QVector<QRhiResource *> releasePool;
|
||||
QRhiBuffer *vbuf = nullptr;
|
||||
QRhiBuffer *ibuf = nullptr;
|
||||
QRhiBuffer *ubuf = nullptr;
|
||||
QRhiShaderResourceBindings *srb = nullptr;
|
||||
QRhiGraphicsPipeline *ps = nullptr;
|
||||
@ -29,6 +33,12 @@ void Window::customInit()
|
||||
d.vbuf->create();
|
||||
d.releasePool << d.vbuf;
|
||||
|
||||
if (INDEXED) {
|
||||
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indices));
|
||||
d.ibuf->create();
|
||||
d.releasePool << d.ibuf;
|
||||
}
|
||||
|
||||
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64 + 4 + 4);
|
||||
d.ubuf->create();
|
||||
d.releasePool << d.ubuf;
|
||||
@ -71,8 +81,12 @@ void Window::customInit()
|
||||
|
||||
d.initialUpdates = m_r->nextResourceUpdateBatch();
|
||||
d.initialUpdates->uploadStaticBuffer(d.vbuf, tri);
|
||||
|
||||
const float amplitude = 0.5f;
|
||||
d.initialUpdates->updateDynamicBuffer(d.ubuf, 68, 4, &litude);
|
||||
|
||||
if (INDEXED)
|
||||
d.initialUpdates->uploadStaticBuffer(d.ibuf, indices);
|
||||
}
|
||||
|
||||
void Window::customRelease()
|
||||
@ -96,14 +110,20 @@ void Window::customRender()
|
||||
u->updateDynamicBuffer(d.ubuf, 0, 64, d.winProj.constData());
|
||||
}
|
||||
u->updateDynamicBuffer(d.ubuf, 64, 4, &d.time);
|
||||
d.time += 0.1f;
|
||||
d.time += 0.01f;
|
||||
|
||||
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
|
||||
cb->setGraphicsPipeline(d.ps);
|
||||
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
|
||||
cb->setShaderResources();
|
||||
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, 0);
|
||||
cb->setVertexInput(0, 1, &vbufBinding);
|
||||
cb->draw(3);
|
||||
if (INDEXED) {
|
||||
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt32);
|
||||
cb->drawIndexed(3);
|
||||
} else {
|
||||
cb->setVertexInput(0, 1, &vbufBinding);
|
||||
cb->draw(3);
|
||||
}
|
||||
|
||||
cb->endPass();
|
||||
}
|
||||
|
Binary file not shown.
@ -6,6 +6,10 @@ layout(location = 0) in vec3 inColor[];
|
||||
|
||||
layout(location = 0) out vec3 outColor[];
|
||||
|
||||
// these serve no purpose, just exist to test per-patch outputs
|
||||
layout(location = 1) patch out vec3 stuff;
|
||||
layout(location = 2) patch out float more_stuff;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (gl_InvocationID == 0) {
|
||||
@ -18,4 +22,6 @@ void main()
|
||||
|
||||
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
||||
outColor[gl_InvocationID] = inColor[gl_InvocationID];
|
||||
stuff = vec3(1.0);
|
||||
more_stuff = 1.0;
|
||||
}
|
||||
|
Binary file not shown.
@ -6,6 +6,10 @@ layout(location = 0) in vec3 inColor[];
|
||||
|
||||
layout(location = 0) out vec3 outColor;
|
||||
|
||||
// these serve no purpose, just exist to test per-patch outputs
|
||||
layout(location = 1) patch in vec3 stuff;
|
||||
layout(location = 2) patch in float more_stuff;
|
||||
|
||||
layout(std140, binding = 0) uniform buf {
|
||||
mat4 mvp;
|
||||
float time;
|
||||
@ -14,7 +18,10 @@ layout(std140, binding = 0) uniform buf {
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = mvp * ((gl_TessCoord.x * gl_in[0].gl_Position) + (gl_TessCoord.y * gl_in[1].gl_Position) + (gl_TessCoord.z * gl_in[2].gl_Position));
|
||||
gl_Position.x += sin(time + gl_Position.y) * amplitude;
|
||||
outColor = gl_TessCoord.x * inColor[0] + gl_TessCoord.y * inColor[1] + gl_TessCoord.z * inColor[2];
|
||||
vec4 pos = (gl_TessCoord.x * gl_in[0].gl_Position) + (gl_TessCoord.y * gl_in[1].gl_Position) + (gl_TessCoord.z * gl_in[2].gl_Position);
|
||||
gl_Position = mvp * pos;
|
||||
gl_Position.x += sin(time + pos.y) * amplitude;
|
||||
outColor = gl_TessCoord.x * inColor[0] + gl_TessCoord.y * inColor[1] + gl_TessCoord.z * inColor[2]
|
||||
// these are all 1.0, just here to exercise the shader generation and the runtime pipeline setup
|
||||
* stuff.x * more_stuff * (gl_TessLevelOuter[0] / 4.0) * (gl_TessLevelInner[0] / 4.0);
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
@ -30,7 +30,7 @@ PixelInput main(Input input, float3 uvwCoord : SV_DomainLocation, const OutputPa
|
||||
|
||||
float3 vertexPosition = uvwCoord.x * patch[0].position + uvwCoord.y * patch[1].position + uvwCoord.z * patch[2].position;
|
||||
output.position = mul(float4(vertexPosition, 1.0f), mvp);
|
||||
output.position.x += sin(time + output.position.y) * amplitude;
|
||||
output.position.x += sin(time + vertexPosition.y) * amplitude;
|
||||
|
||||
output.color = uvwCoord.x * patch[0].color + uvwCoord.y * patch[1].color + uvwCoord.z * patch[2].color;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user