Add support for VRS
The basic Metal plumbing for letting an MTLRasterizationRateMap through is important for Vision Pro, that seems to rely heavily on VRS to reduce the GPU load via eye-tracked, dynamic foveation. Metal's approach is kind of different than everyone else. For a more complete, forward looking story, introduce support for some of the D3D12 and Vulkan VRS features. (these seem to be identical feature-wise, with Vulkan essentially copying D3D, just with a lot more convoluted API) A certain fragmentation is however inevitable. This way we support: - setting a per-draw rate (say I want to draw this mesh with 4x4 rate, i.e. 4x reduced resolution both horizontally and vertically). This is supported with D3D12/Vulkan, and could also be supported with the Qualcomm OpenGL ES extension, although that's not currently implemented. - setting a texture-based rate map, D3D12/Vulkan style. This implies starting to support R8_UINT textures since R8_UNORM, while the difference isn't necessarily relevant here, will be rejected by some APIs. This is done by setting a QRhiTexture on a QRhiShadingRateMap (a new lightweight, wrapper-style object that does not do much on its own, just contains either a texture or some other native object) - setting an MTLRasterizationRateMap on a QRhiShadingRateMap. This is only supported on Metal, naturally, and is realized by an overload of createFrom(). Conversely, setting a QRhiTexture (or attempting to set a per-draw rate) will not work with Metal. We do not offer ways to create a rate map directly via Qt, due to the underlying API differences. So for the user this involves either uploading a Grayscale8 or similar QImage into an R8UI QRhiTexture, or by setting up an MTLRasterizationRateMap directly with Metal and passing that in to the QRhiShadingRateMap instead of a QRhiTexture. We also do not care about the bizarre 'render to a texture, get a buffer with scale rates, and then do a scaling pass using the buffer and MSL intrinsics in the shader' approach of Metal. For now we assume that in case of the Vision Pro this is taken care of by the XR compositor, and is not something the applications (Qt) implements. Some D3D12/Vulkan VRS features are left as potential future enhancements. For example, when it comes to combining the two rates, we always do MAX, there is no API to control the combiners. The third mode of D3D12/Vulkan VRS, per-triangle (writing to a special variable in the vertex shader) is not planned to be supported now or in the future. The Vulkan backend gets quite a lot of changes, mainly due to the difficulty of dealing with extensions in a portable and robust manner in that API, and the API versioning design mistakes in Vulkan 1.1 and beyond. Now both swapchain and texture render pass objects go through vkCreateRenderPass2, when supported, but the KHR variant still, not the 1.2+ core functions, in order to play nice with Vulkan 1.1 and the Android ecosystem. Some QRhi docs are enhanced, e.g. around QRhiRenderPassDescriptor, since as the manual test demonstrates, dynamically taking VRS into use in a scene involves certain steps (new/compatible QRhiRenderPassDescriptor is needed, rebuilding the render target) that are not trivial. (these are just the usual consequences of Vulkan's VkRenderPass permeating everything in the rendering engines) The manual test can be used to exercise both per-draw and image-based VRS with D3D12 and Vulkan. It also includes a limited example of setting up a rate map with Metal (but the test app does not implement the special scaling) A number of things, such as operating with MSAA enabled or using VRS combined with multiview are not currently exercised by the test app, and are left as a future exercise to verify. Task-number: QTBUG-126297 Change-Id: I5210f4cff6b8360b6bb47cdbe8d9caee1c29b8a5 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
parent
e7ac6667f2
commit
07c0037c7e
@ -79,6 +79,7 @@
|
||||
\li QRhiRenderBuffer
|
||||
\li QRhiTexture
|
||||
\li QRhiSampler
|
||||
\li QRhiShadingRateMap
|
||||
\li QRhiTextureRenderTarget
|
||||
\li QRhiShaderResourceBindings
|
||||
\li QRhiGraphicsPipeline
|
||||
|
@ -1036,6 +1036,38 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
texture becomes necessary, for example when rendering into an
|
||||
OpenXR-provided depth texture (XR_KHR_composition_layer_depth). This enum
|
||||
value has been introduced in Qt 6.8.
|
||||
|
||||
\value VariableRateShading Indicates that per-draw (per-pipeline) variable
|
||||
rate shading is supported. When reported as supported, \l
|
||||
QRhiCommandBuffer::setShadingRate() is functional and has an effect for
|
||||
QRhiGraphicsPipeline objects that declared \l
|
||||
QRhiGraphicsPipeline::UsesShadingRate in their flags. Call \l
|
||||
QRhi::supportedShadingRates() to check which rates are supported. (1x1 is
|
||||
always supported, other typical values are 2x2, 1x2, 2x1, 2x4, 4x2, 4x4).
|
||||
This feature can be expected to be supported with Direct 3D 12 and Vulkan,
|
||||
assuming the implementation and GPU used at run time supports VRS. This enum
|
||||
value has been introduced in Qt 6.9.
|
||||
|
||||
\value VariableRateShadingMap Indicates that image-based specification of
|
||||
the shading rate is possible. The "image" is not necessarily a texture, it
|
||||
may be a native 3D API object, depending on the underlying backend and
|
||||
graphics API at run time. In practice this feature can be expected to be
|
||||
supported with Direct 3D 12, Vulkan, and Metal, assuming the GPU is modern
|
||||
enough to support VRS. To check if D3D12/Vulkan-style image-based VRS is
|
||||
suspported, use VariableRateShadingMapWithTexture instead. When this feature
|
||||
is reported as supported, there are two possibilities: when
|
||||
VariableRateShadingMapWithTexture is also true, then QRhiShadingRateMap
|
||||
consumes QRhiTexture objects via the createFrom() overload taking a
|
||||
QRhiTexture argument. When VariableRateShadingMapWithTexture is false, then
|
||||
QRhiShadingRateMap consumes some other type of native objects, for example
|
||||
an MTLRasterizationRateMap in case of Metal. Use the createFrom() overload
|
||||
taking a NativeShadingRateMap in this case. This enum value has been
|
||||
introduced in Qt 6.9.
|
||||
|
||||
\value VariableRateShadingMapWithTexture Indicates that image-based
|
||||
specification of the shading rate is supported via regular textures. In
|
||||
practice this may be supported with Direct 3D 12 and Vulkan. This enum value
|
||||
has been introduced in Qt 6.9.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -1143,6 +1175,12 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
\c out variables) from the vertex shader. The value may be as low as 8 with
|
||||
OpenGL ES 2.0, and 15 with OpenGL ES 3.0 and some Metal devices. Elsewhere,
|
||||
a typical value is 32.
|
||||
|
||||
\value ShadingRateImageTileSize The tile size for shading rate textures. 0
|
||||
if the QRhi::VariableRateShadingMapWithTexture feature is not supported.
|
||||
Otherwise a value such as 16, indicating, for example, a tile size of 16x16.
|
||||
Each byte in the (R8UI) shading rate texture defines then the shading rate
|
||||
for a tile of 16x16 pixels. See \l QRhiShadingRateMap for details.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -2739,6 +2777,37 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh
|
||||
\sa QRhiColorAttachment::setResolveTexture(), setDepthTexture()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRhiShadingRateMap *QRhiTextureRenderTargetDescription::shadingRateMap() const
|
||||
\return the currently set QRhiShadingRateMap. By default this is \nullptr.
|
||||
\since 6.9
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRhiTextureRenderTargetDescription::setShadingRateMap(QRhiShadingRateMap *map)
|
||||
|
||||
Associates with the specified QRhiShadingRateMap \a map. This is functional
|
||||
only when the \l QRhi::VariableRateShadingMap feature is reported as
|
||||
supported.
|
||||
|
||||
When QRhiCommandBuffer::setShadingRate() is also called, the higher of two
|
||||
the shading rates are used for each tile. There is currently no control
|
||||
offered over the combiner behavior.
|
||||
|
||||
\note When the render target had already been built (create() was called
|
||||
successfully), setting a shading rate map implies that a different, new
|
||||
QRhiRenderPassDescriptor is needed and thus a rebuild is needed. Call
|
||||
setRenderPassDescriptor() again (outside of a render pass) and then rebuild
|
||||
by calling create(). This has other rolling consequences as well, for
|
||||
example for graphics pipelines: those also need to be associated with the
|
||||
new QRhiRenderPassDescriptor and then rebuilt. See \l
|
||||
QRhiRenderPassDescriptor::serializedFormat() for some suggestions on how to
|
||||
deal with this. Remember to set the QRhiGraphicsPipeline::UsesShadingRate
|
||||
flag as well.
|
||||
|
||||
\since 6.9
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QRhiTextureSubresourceUploadDescription
|
||||
\inmodule QtGui
|
||||
@ -3450,6 +3519,7 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
|
||||
\value SwapChain
|
||||
\value ComputePipeline
|
||||
\value CommandBuffer
|
||||
\value ShadingRateMap
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -4324,6 +4394,8 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
|
||||
mipmap-based filtering may be unsupported. This is indicated by the
|
||||
QRhi::OneDimensionalTextures and QRhi::OneDimensionalTextureMipmaps
|
||||
feature flags.
|
||||
|
||||
\value UsedAsShadingRateMap
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -4403,6 +4475,8 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
|
||||
\value ASTC_10x10
|
||||
\value ASTC_12x10
|
||||
\value ASTC_12x12
|
||||
|
||||
\value R8UI One component, unsigned 8 bit.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -4853,6 +4927,114 @@ QRhiResource::Type QRhiSampler::resourceType() const
|
||||
Sets the texture comparison function \a op.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QRhiShadingRateMap
|
||||
\inmodule QtGui
|
||||
\since 6.9
|
||||
\brief An object that wraps a texture or another kind of native 3D API object.
|
||||
|
||||
\note This is a RHI API with limited compatibility guarantees, see \l QRhi
|
||||
for details.
|
||||
|
||||
For an introduction to Variable Rate Shading (VRS), see
|
||||
\l{https://learn.microsoft.com/en-us/windows/win32/direct3d12/vrs}. Qt
|
||||
supports a subset of the VRS features offered by Direct 3D 12 and Vulkan. In
|
||||
addition, Metal's somewhat different mechanism is supported by making it
|
||||
possible to set up a QRhiShadingRateMap with an existing
|
||||
MTLRasterizationRateMap object.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\struct QRhiShadingRateMap::NativeShadingRateMap
|
||||
\inmodule QtGui
|
||||
\since 6.9
|
||||
\brief Wraps a native shading rate map.
|
||||
|
||||
An example is MTLRasterizationRateMap with Metal. Other 3D APIs that use
|
||||
textures for image-based VRS do not use this struct since those can function
|
||||
via the QRhiTexture-based overload of QRhiShadingRate::createFrom().
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable QRhiShadingRateMap::NativeShadingRateMap::object
|
||||
\brief 64-bit integer containing the native object handle.
|
||||
|
||||
Used with QRhiShadingRateMap::createFrom(). For example, with Metal,
|
||||
\c object is expected to be an id<MTLRasterizationRateMap>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QRhiShadingRateMap::QRhiShadingRateMap(QRhiImplementation *rhi)
|
||||
: QRhiResource(rhi)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\return the resource type.
|
||||
*/
|
||||
QRhiResource::Type QRhiShadingRateMap::resourceType() const
|
||||
{
|
||||
return ShadingRateMap;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets up the shading rate map to use a native 3D API shading rate object
|
||||
\a src.
|
||||
|
||||
\return \c true when successful, \c false when not supported.
|
||||
|
||||
\note This is functional only when the QRhi::VariableRateShadingMap feature
|
||||
is reported as supported, while QRhi::VariableShadingRateMapWithTexture
|
||||
feature is not. Currently this is true for Metal, assuming variable rate
|
||||
shading is supported by the GPU.
|
||||
|
||||
\note With Metal, the \c object field of \a src is expected to contain an
|
||||
id<MTLRasterizationRateMap>. Note that Qt does not perform anything else
|
||||
apart from passing the MTLRasterizationRateMap on to the
|
||||
MTLRenderPassDescriptor. If any special scaling is required, it is up to the
|
||||
application (or the XR compositor) to perform that.
|
||||
*/
|
||||
bool QRhiShadingRateMap::createFrom(NativeShadingRateMap src)
|
||||
{
|
||||
Q_UNUSED(src);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets up the shading rate map to use the texture \a src as the
|
||||
image containing the per-tile shading rates.
|
||||
|
||||
\return \c true when successful, \c false when not supported.
|
||||
|
||||
The QRhiShadingRateMap does not take ownership of \a src.
|
||||
|
||||
\note This is functional only when the
|
||||
QRhi::VariableRateShadingMapWithTexture feature is reported as supported. In
|
||||
practice may be supported on Vulkan and Direct 3D 12 when using modern
|
||||
graphics cards. It will never be supported on OpenGL or Metal, for example.
|
||||
|
||||
\note \a src must have a format of QRhiTexture::R8UI.
|
||||
|
||||
\note \a src must have a width of \c{ceil(render_target_pixel_width /
|
||||
(float)tile_width)} and a height of \c{ceil(render_target_pixel_height /
|
||||
(float)tile_height)}. It is up to the application to ensure the size of the
|
||||
texture is as expected, using the above formula, at all times. The tile size
|
||||
can be queried via \l QRhi::resourceLimit() and
|
||||
QRhi::ShadingRateImageTileSize.
|
||||
|
||||
Each byte (texel) in the texture corresponds to the shading rate value for
|
||||
one tile. 0 indicates 1x1, while a value of 10 indicates 4x4. See
|
||||
\l{https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_shading_rate}{D3D12_SHADING_RATE}
|
||||
for other possible values.
|
||||
*/
|
||||
bool QRhiShadingRateMap::createFrom(QRhiTexture *src)
|
||||
{
|
||||
Q_UNUSED(src);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QRhiRenderPassDescriptor
|
||||
\inmodule QtGui
|
||||
@ -4954,6 +5136,34 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
|
||||
meant for storing on disk, reusing between processes, or using with multiple
|
||||
QRhi instances with potentially different backends.
|
||||
|
||||
\note Calling this function is expected to be a cheap operation since the
|
||||
backends are not supposed to calculate the data in this function, but rather
|
||||
return an already calculated series of data.
|
||||
|
||||
When creating reusable components as part of a library, where graphics
|
||||
pipelines are created and maintained while targeting a QRhiRenderTarget (be
|
||||
it a swapchain or a texture) managed by the client of the library, the
|
||||
components must be able to deal with a changing QRhiRenderPassDescriptor.
|
||||
For example, because the render target changes and so invalidates the
|
||||
previously QRhiRenderPassDescriptor (with regards to the new render target
|
||||
at least) due to having a potentially different color format and attachments
|
||||
now. Or because \l{QRhiShadingRateMap}{variable rate shading} is taken into
|
||||
use dynamically. A simple pattern that helps dealing with this is performing
|
||||
the following check on every frame, to recognize the case when the pipeline
|
||||
needs to be associated with a new QRhiRenderPassDescriptor, because
|
||||
something is different about the render target now, compared to earlier
|
||||
frames:
|
||||
|
||||
\code
|
||||
QRhiRenderPassDescriptor *rp = m_renderTarget->renderPassDescriptor();
|
||||
if (m_pipeline && rp->serializedFormat() != m_renderPassFormat) {
|
||||
m_pipeline->setRenderPassDescriptor(rp);
|
||||
m_renderPassFormat = rp->serializedFormat();
|
||||
m_pipeline->create();
|
||||
}
|
||||
// remember to store m_renderPassFormat also when creating m_pipeline the first time
|
||||
\endcode
|
||||
|
||||
\sa isCompatible()
|
||||
*/
|
||||
|
||||
@ -6507,6 +6717,11 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
|
||||
into account. Debug information is relevant in particular with tools like
|
||||
RenderDoc since it allows seeing the original source code when investigating
|
||||
the pipeline and when performing vertex or fragment shader debugging.
|
||||
|
||||
\value UsesShadingRate Indicates that a per-draw (per-pipeline) shading rate
|
||||
value will be set via QRhiCommandBuffer::setShadingRate(). Not specifying
|
||||
this flag and still calling setShadingRate() may lead to varying, unexpected
|
||||
results depending on the underlying graphics API.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -7608,6 +7823,37 @@ QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget(StereoTargetBuffer tar
|
||||
\sa createOrResize()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRhiShadingRateMap *QRhiSwapChain::shadingRateMap() const
|
||||
\return the currently set QRhiShadingRateMap. By default this is \nullptr.
|
||||
\since 6.9
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QRhiSwapChain::setShadingRateMap(QRhiShadingRateMap *map)
|
||||
|
||||
Associates with the specified QRhiShadingRateMap \a map. This is functional
|
||||
only when the \l QRhi::VariableRateShadingMap feature is reported as
|
||||
supported.
|
||||
|
||||
When QRhiCommandBuffer::setShadingRate() is also called, the higher of two
|
||||
the shading rates are used for each tile. There is currently no control
|
||||
offered over the combiner behavior.
|
||||
|
||||
\note Setting a shading rate map implies that a different, new
|
||||
QRhiRenderPassDescriptor is needed and some of the native swapchain objects
|
||||
must be rebuilt. Therefore, if the swapchain is already set up, call
|
||||
newCompatibleRenderPassDescriptor() and setRenderPassDescriptor() right
|
||||
after setShadingRateMap(). Then, createOrResize() must also be called again.
|
||||
This has rolling consequences, for example for graphics pipelines: those
|
||||
also need to be associated with the new QRhiRenderPassDescriptor and then
|
||||
rebuilt. See \l QRhiRenderPassDescriptor::serializedFormat() for some
|
||||
suggestions on how to deal with this. Remember to set the
|
||||
QRhiGraphicsPipeline::UsesShadingRate flag for them as well.
|
||||
|
||||
\since 6.9
|
||||
*/
|
||||
|
||||
/*!
|
||||
\struct QRhiSwapChainHdrInfo
|
||||
\inmodule QtGui
|
||||
@ -8032,6 +8278,8 @@ static const char *resourceTypeStr(const QRhiResource *res)
|
||||
return "ComputePipeline";
|
||||
case QRhiResource::CommandBuffer:
|
||||
return "CommandBuffer";
|
||||
case QRhiResource::ShadingRateMap:
|
||||
return "ShadingRateMap";
|
||||
}
|
||||
|
||||
Q_UNREACHABLE_RETURN("");
|
||||
@ -8260,6 +8508,10 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
|
||||
bpc = 8;
|
||||
break;
|
||||
|
||||
case QRhiTexture::R8UI:
|
||||
bpc = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
@ -9692,6 +9944,29 @@ void QRhiCommandBuffer::setStencilRef(quint32 refValue)
|
||||
m_rhi->setStencilRef(this, refValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the shading rate for the following draw calls to \a coarsePixelSize.
|
||||
|
||||
The default is 1x1.
|
||||
|
||||
Functional only when the \l QRhi::VariableRateShading feature is reported as
|
||||
supported and the QRhiGraphicsPipeline(s) bound on the command buffer were
|
||||
declaring \l QRhiGraphicsPipeline::UsesShadingRate when creating them.
|
||||
|
||||
Call \l QRhi::supportedShadingRates() to check what shading rates are
|
||||
supported for a given sample count.
|
||||
|
||||
When both a QRhiShadingRateMap and this function is in use, the higher of
|
||||
two the shading rates are used for each tile. There is currently no control
|
||||
offered over the combiner behavior.
|
||||
|
||||
\since 6.9
|
||||
*/
|
||||
void QRhiCommandBuffer::setShadingRate(const QSize &coarsePixelSize)
|
||||
{
|
||||
m_rhi->setShadingRate(this, coarsePixelSize);
|
||||
}
|
||||
|
||||
/*!
|
||||
Records a non-indexed draw.
|
||||
|
||||
@ -10694,6 +10969,16 @@ QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter,
|
||||
return d->createSampler(magFilter, minFilter, mipmapMode, addressU, addressV, addressW);
|
||||
}
|
||||
|
||||
/*!
|
||||
\return a new shading rate map object.
|
||||
|
||||
\since 6.9
|
||||
*/
|
||||
QRhiShadingRateMap *QRhi::newShadingRateMap()
|
||||
{
|
||||
return d->createShadingRateMap();
|
||||
}
|
||||
|
||||
/*!
|
||||
\return a new texture render target with color and depth/stencil
|
||||
attachments given in \a desc, and with the specified \a flags.
|
||||
@ -10992,6 +11277,18 @@ int QRhi::ubufAlignment() const
|
||||
return d->ubufAlignment();
|
||||
}
|
||||
|
||||
/*!
|
||||
\return The list of supported variable shading rates for the specified \a sampleCount.
|
||||
|
||||
1x1 is always supported.
|
||||
|
||||
\since 6.9
|
||||
*/
|
||||
QList<QSize> QRhi::supportedShadingRates(int sampleCount) const
|
||||
{
|
||||
return d->supportedShadingRates(sampleCount);
|
||||
}
|
||||
|
||||
Q_CONSTINIT static QBasicAtomicInteger<QRhiGlobalObjectIdGenerator::Type> counter = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||
|
||||
QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId()
|
||||
|
@ -39,6 +39,7 @@ class QRhiCommandBuffer;
|
||||
class QRhiResourceUpdateBatch;
|
||||
class QRhiResourceUpdateBatchPrivate;
|
||||
class QRhiSwapChain;
|
||||
class QRhiShadingRateMap;
|
||||
|
||||
class Q_GUI_EXPORT QRhiDepthStencilClearValue
|
||||
{
|
||||
@ -645,11 +646,15 @@ public:
|
||||
QRhiTexture *depthResolveTexture() const { return m_depthResolveTexture; }
|
||||
void setDepthResolveTexture(QRhiTexture *tex) { m_depthResolveTexture = tex; }
|
||||
|
||||
QRhiShadingRateMap *shadingRateMap() const { return m_shadingRateMap; }
|
||||
void setShadingRateMap(QRhiShadingRateMap *map) { m_shadingRateMap = map; }
|
||||
|
||||
private:
|
||||
QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
|
||||
QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
|
||||
QRhiTexture *m_depthTexture = nullptr;
|
||||
QRhiTexture *m_depthResolveTexture = nullptr;
|
||||
QRhiShadingRateMap *m_shadingRateMap = nullptr;
|
||||
};
|
||||
|
||||
class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
|
||||
@ -815,7 +820,8 @@ public:
|
||||
GraphicsPipeline,
|
||||
SwapChain,
|
||||
ComputePipeline,
|
||||
CommandBuffer
|
||||
CommandBuffer,
|
||||
ShadingRateMap
|
||||
};
|
||||
|
||||
virtual ~QRhiResource();
|
||||
@ -908,7 +914,8 @@ public:
|
||||
ThreeDimensional = 1 << 10,
|
||||
TextureRectangleGL = 1 << 11,
|
||||
TextureArray = 1 << 12,
|
||||
OneDimensional = 1 << 13
|
||||
OneDimensional = 1 << 13,
|
||||
UsedAsShadingRateMap = 1 << 14
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
@ -930,6 +937,8 @@ public:
|
||||
|
||||
RGB10A2,
|
||||
|
||||
R8UI,
|
||||
|
||||
D16,
|
||||
D24,
|
||||
D24S8,
|
||||
@ -1140,6 +1149,22 @@ protected:
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags)
|
||||
|
||||
class Q_GUI_EXPORT QRhiShadingRateMap : public QRhiResource
|
||||
{
|
||||
public:
|
||||
struct NativeShadingRateMap {
|
||||
quint64 object;
|
||||
};
|
||||
|
||||
QRhiResource::Type resourceType() const override;
|
||||
|
||||
virtual bool createFrom(NativeShadingRateMap src);
|
||||
virtual bool createFrom(QRhiTexture *src);
|
||||
|
||||
protected:
|
||||
QRhiShadingRateMap(QRhiImplementation *rhi);
|
||||
};
|
||||
|
||||
class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
|
||||
{
|
||||
public:
|
||||
@ -1275,7 +1300,8 @@ public:
|
||||
UsesBlendConstants = 1 << 0,
|
||||
UsesStencilRef = 1 << 1,
|
||||
UsesScissor = 1 << 2,
|
||||
CompileShadersWithDebugInfo = 1 << 3
|
||||
CompileShadersWithDebugInfo = 1 << 3,
|
||||
UsesShadingRate = 1 << 4
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
@ -1595,6 +1621,9 @@ public:
|
||||
QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
|
||||
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
|
||||
|
||||
QRhiShadingRateMap *shadingRateMap() const { return m_shadingRateMap; }
|
||||
void setShadingRateMap(QRhiShadingRateMap *map) { m_shadingRateMap = map; }
|
||||
|
||||
QSize currentPixelSize() const { return m_currentPixelSize; }
|
||||
|
||||
virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0;
|
||||
@ -1616,6 +1645,7 @@ protected:
|
||||
QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
|
||||
QSize m_currentPixelSize;
|
||||
QRhiSwapChainProxyData m_proxyData;
|
||||
QRhiShadingRateMap *m_shadingRateMap = nullptr;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
|
||||
@ -1688,6 +1718,7 @@ public:
|
||||
void setScissor(const QRhiScissor &scissor);
|
||||
void setBlendConstants(const QColor &c);
|
||||
void setStencilRef(quint32 refValue);
|
||||
void setShadingRate(const QSize &coarsePixelSize);
|
||||
|
||||
void draw(quint32 vertexCount,
|
||||
quint32 instanceCount = 1,
|
||||
@ -1873,7 +1904,10 @@ public:
|
||||
ThreeDimensionalTextureMipmaps,
|
||||
MultiView,
|
||||
TextureViewFormat,
|
||||
ResolveDepthStencil
|
||||
ResolveDepthStencil,
|
||||
VariableRateShading,
|
||||
VariableRateShadingMap,
|
||||
VariableRateShadingMapWithTexture
|
||||
};
|
||||
|
||||
enum BeginFrameFlag {
|
||||
@ -1899,7 +1933,8 @@ public:
|
||||
TextureArraySizeMax,
|
||||
MaxUniformBufferRange,
|
||||
MaxVertexInputs,
|
||||
MaxVertexOutputs
|
||||
MaxVertexOutputs,
|
||||
ShadingRateImageTileSize
|
||||
};
|
||||
|
||||
~QRhi();
|
||||
@ -1959,6 +1994,8 @@ public:
|
||||
QRhiSampler::AddressMode addressV,
|
||||
QRhiSampler::AddressMode addressW = QRhiSampler::Repeat);
|
||||
|
||||
QRhiShadingRateMap *newShadingRateMap();
|
||||
|
||||
QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags = {});
|
||||
|
||||
@ -2009,6 +2046,8 @@ public:
|
||||
|
||||
static QRhiSwapChainProxyData updateSwapChainProxyData(Implementation impl, QWindow *window);
|
||||
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const;
|
||||
|
||||
protected:
|
||||
QRhi();
|
||||
|
||||
|
@ -65,6 +65,8 @@ public:
|
||||
virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) = 0;
|
||||
|
||||
virtual QRhiShadingRateMap *createShadingRateMap() = 0;
|
||||
|
||||
virtual QRhiSwapChain *createSwapChain() = 0;
|
||||
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
|
||||
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
|
||||
@ -99,6 +101,7 @@ public:
|
||||
virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
|
||||
virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
|
||||
virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
|
||||
virtual void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) = 0;
|
||||
|
||||
virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
|
||||
@ -124,6 +127,7 @@ public:
|
||||
|
||||
virtual QList<int> supportedSampleCounts() const = 0;
|
||||
virtual int ubufAlignment() const = 0;
|
||||
virtual QList<QSize> supportedShadingRates(int sampleCount) const = 0;
|
||||
virtual bool isYUpInFramebuffer() const = 0;
|
||||
virtual bool isYUpInNDC() const = 0;
|
||||
virtual bool isClipDepthZeroToOne() const = 0;
|
||||
@ -658,7 +662,8 @@ public:
|
||||
TexDepthOutput,
|
||||
TexStorageLoad,
|
||||
TexStorageStore,
|
||||
TexStorageLoadStore
|
||||
TexStorageLoadStore,
|
||||
TexShadingRate
|
||||
};
|
||||
|
||||
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
|
||||
|
@ -478,6 +478,12 @@ QList<int> QRhiD3D11::supportedSampleCounts() const
|
||||
return { 1, 2, 4, 8 };
|
||||
}
|
||||
|
||||
QList<QSize> QRhiD3D11::supportedShadingRates(int sampleCount) const
|
||||
{
|
||||
Q_UNUSED(sampleCount);
|
||||
return { QSize(1, 1) };
|
||||
}
|
||||
|
||||
DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleDesc(int sampleCount) const
|
||||
{
|
||||
DXGI_SAMPLE_DESC desc;
|
||||
@ -639,6 +645,11 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
|
||||
return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing
|
||||
case QRhi::ResolveDepthStencil:
|
||||
return false;
|
||||
case QRhi::VariableRateShading:
|
||||
return false;
|
||||
case QRhi::VariableRateShadingMap:
|
||||
case QRhi::VariableRateShadingMapWithTexture:
|
||||
return false;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
@ -680,6 +691,8 @@ int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
|
||||
return D3D11_VS_INPUT_REGISTER_COUNT;
|
||||
case QRhi::MaxVertexOutputs:
|
||||
return D3D11_VS_OUTPUT_REGISTER_COUNT;
|
||||
case QRhi::ShadingRateImageTileSize:
|
||||
return 0;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return 0;
|
||||
@ -902,6 +915,11 @@ QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureR
|
||||
return new QD3D11TextureRenderTarget(this, desc, flags);
|
||||
}
|
||||
|
||||
QRhiShadingRateMap *QRhiD3D11::createShadingRateMap()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline()
|
||||
{
|
||||
return new QD3D11GraphicsPipeline(this);
|
||||
@ -1237,6 +1255,12 @@ void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
||||
cmd.args.stencilRef.ref = refValue;
|
||||
}
|
||||
|
||||
void QRhiD3D11::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
|
||||
{
|
||||
Q_UNUSED(cb);
|
||||
Q_UNUSED(coarsePixelSize);
|
||||
}
|
||||
|
||||
void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||
{
|
||||
@ -1542,6 +1566,8 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
|
||||
return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
case QRhiTexture::R8:
|
||||
return DXGI_FORMAT_R8_UNORM;
|
||||
case QRhiTexture::R8UI:
|
||||
return DXGI_FORMAT_R8_UINT;
|
||||
case QRhiTexture::RG8:
|
||||
return DXGI_FORMAT_R8G8_UNORM;
|
||||
case QRhiTexture::R16:
|
||||
|
@ -663,6 +663,8 @@ public:
|
||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) override;
|
||||
|
||||
QRhiShadingRateMap *createShadingRateMap() override;
|
||||
|
||||
QRhiSwapChain *createSwapChain() override;
|
||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
|
||||
@ -697,6 +699,7 @@ public:
|
||||
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||
|
||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||
@ -722,6 +725,7 @@ public:
|
||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||
|
||||
QList<int> supportedSampleCounts() const override;
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||
int ubufAlignment() const override;
|
||||
bool isYUpInFramebuffer() const override;
|
||||
bool isYUpInNDC() const override;
|
||||
|
@ -498,6 +498,20 @@ bool QRhiD3D12::create(QRhi::Flags flags)
|
||||
caps.textureViewFormat = options3.CastingFullyTypedFormatSupported;
|
||||
}
|
||||
|
||||
#ifdef QRHI_D3D12_CL5_AVAILABLE
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = {};
|
||||
if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, sizeof(options6)))) {
|
||||
caps.vrs = options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED;
|
||||
caps.vrsMap = options6.VariableShadingRateTier == D3D12_VARIABLE_SHADING_RATE_TIER_2;
|
||||
caps.vrsAdditionalRates = options6.AdditionalShadingRatesSupported;
|
||||
shadingRateImageTileSize = options6.ShadingRateImageTileSize;
|
||||
}
|
||||
#else
|
||||
caps.vrs = false;
|
||||
caps.vrsMap = false;
|
||||
caps.vrsAdditionalRates = false;
|
||||
#endif
|
||||
|
||||
deviceLost = false;
|
||||
offscreenActive = false;
|
||||
|
||||
@ -597,6 +611,40 @@ QList<int> QRhiD3D12::supportedSampleCounts() const
|
||||
return { 1, 2, 4, 8 };
|
||||
}
|
||||
|
||||
QList<QSize> QRhiD3D12::supportedShadingRates(int sampleCount) const
|
||||
{
|
||||
QList<QSize> sizes;
|
||||
switch (sampleCount) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (caps.vrsAdditionalRates) {
|
||||
sizes.append(QSize(4, 4));
|
||||
sizes.append(QSize(4, 2));
|
||||
sizes.append(QSize(2, 4));
|
||||
}
|
||||
sizes.append(QSize(2, 2));
|
||||
sizes.append(QSize(2, 1));
|
||||
sizes.append(QSize(1, 2));
|
||||
break;
|
||||
case 2:
|
||||
if (caps.vrsAdditionalRates)
|
||||
sizes.append(QSize(2, 4));
|
||||
sizes.append(QSize(2, 2));
|
||||
sizes.append(QSize(2, 1));
|
||||
sizes.append(QSize(1, 2));
|
||||
break;
|
||||
case 4:
|
||||
sizes.append(QSize(2, 2));
|
||||
sizes.append(QSize(2, 1));
|
||||
sizes.append(QSize(1, 2));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sizes.append(QSize(1, 1));
|
||||
return sizes;
|
||||
}
|
||||
|
||||
QRhiSwapChain *QRhiD3D12::createSwapChain()
|
||||
{
|
||||
return new QD3D12SwapChain(this);
|
||||
@ -747,6 +795,11 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
|
||||
// there is no Multisample Resolve support for depth/stencil formats
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
|
||||
return false;
|
||||
case QRhi::VariableRateShading:
|
||||
return caps.vrs;
|
||||
case QRhi::VariableRateShadingMap:
|
||||
case QRhi::VariableRateShadingMapWithTexture:
|
||||
return caps.vrsMap;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -782,6 +835,8 @@ int QRhiD3D12::resourceLimit(QRhi::ResourceLimit limit) const
|
||||
return 32;
|
||||
case QRhi::MaxVertexOutputs:
|
||||
return 32;
|
||||
case QRhi::ShadingRateImageTileSize:
|
||||
return shadingRateImageTileSize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -868,6 +923,11 @@ QRhiTextureRenderTarget *QRhiD3D12::createTextureRenderTarget(const QRhiTextureR
|
||||
return new QD3D12TextureRenderTarget(this, desc, flags);
|
||||
}
|
||||
|
||||
QRhiShadingRateMap *QRhiD3D12::createShadingRateMap()
|
||||
{
|
||||
return new QD3D12ShadingRateMap(this);
|
||||
}
|
||||
|
||||
QRhiGraphicsPipeline *QRhiD3D12::createGraphicsPipeline()
|
||||
{
|
||||
return new QD3D12GraphicsPipeline(this);
|
||||
@ -1404,6 +1464,44 @@ void QRhiD3D12::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
||||
cbD->cmdList->OMSetStencilRef(refValue);
|
||||
}
|
||||
|
||||
static inline D3D12_SHADING_RATE toD3DShadingRate(const QSize &coarsePixelSize)
|
||||
{
|
||||
if (coarsePixelSize == QSize(1, 2))
|
||||
return D3D12_SHADING_RATE_1X2;
|
||||
if (coarsePixelSize == QSize(2, 1))
|
||||
return D3D12_SHADING_RATE_2X1;
|
||||
if (coarsePixelSize == QSize(2, 2))
|
||||
return D3D12_SHADING_RATE_2X2;
|
||||
if (coarsePixelSize == QSize(2, 4))
|
||||
return D3D12_SHADING_RATE_2X4;
|
||||
if (coarsePixelSize == QSize(4, 2))
|
||||
return D3D12_SHADING_RATE_4X2;
|
||||
if (coarsePixelSize == QSize(4, 4))
|
||||
return D3D12_SHADING_RATE_4X4;
|
||||
return D3D12_SHADING_RATE_1X1;
|
||||
}
|
||||
|
||||
void QRhiD3D12::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
|
||||
{
|
||||
QD3D12CommandBuffer *cbD = QRHI_RES(QD3D12CommandBuffer, cb);
|
||||
cbD->hasShadingRateSet = false;
|
||||
|
||||
#ifdef QRHI_D3D12_CL5_AVAILABLE
|
||||
if (!caps.vrs)
|
||||
return;
|
||||
|
||||
Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::RenderPass);
|
||||
const D3D12_SHADING_RATE_COMBINER combiners[] = { D3D12_SHADING_RATE_COMBINER_MAX, D3D12_SHADING_RATE_COMBINER_MAX };
|
||||
cbD->cmdList->RSSetShadingRate(toD3DShadingRate(coarsePixelSize), combiners);
|
||||
if (coarsePixelSize.width() != 1 || coarsePixelSize.height() != 1)
|
||||
cbD->hasShadingRateSet = true;
|
||||
#else
|
||||
Q_UNUSED(cb);
|
||||
Q_UNUSED(coarsePixelSize);
|
||||
qWarning("Attempted to set ShadingRate without building Qt against a sufficiently new Windows SDK and d3d12.h. This cannot work.");
|
||||
#endif
|
||||
}
|
||||
|
||||
void QRhiD3D12::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||
{
|
||||
@ -1633,7 +1731,7 @@ QRhi::FrameOpResult QRhiD3D12::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
|
||||
timestampPairStartIndex * sizeof(quint64));
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
|
||||
D3D12GraphicsCommandList *cmdList = cbD->cmdList;
|
||||
HRESULT hr = cmdList->Close();
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to close command list: %s",
|
||||
@ -1753,7 +1851,7 @@ QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags)
|
||||
timestampPairStartIndex * sizeof(quint64));
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
|
||||
D3D12GraphicsCommandList *cmdList = cbD->cmdList;
|
||||
HRESULT hr = cmdList->Close();
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to close command list: %s",
|
||||
@ -1802,7 +1900,7 @@ QRhi::FrameOpResult QRhiD3D12::finish()
|
||||
|
||||
Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
|
||||
|
||||
ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
|
||||
D3D12GraphicsCommandList *cmdList = cbD->cmdList;
|
||||
HRESULT hr = cmdList->Close();
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to close command list: %s",
|
||||
@ -1928,7 +2026,31 @@ void QRhiD3D12::beginPass(QRhiCommandBuffer *cb,
|
||||
cbD->recordingPass = QD3D12CommandBuffer::RenderPass;
|
||||
cbD->currentTarget = rt;
|
||||
|
||||
bool hasShadingRateMapSet = false;
|
||||
#ifdef QRHI_D3D12_CL5_AVAILABLE
|
||||
if (rtD->rp->hasShadingRateMap) {
|
||||
cbD->setShadingRate(QSize(1, 1));
|
||||
QD3D12ShadingRateMap *rateMapD = rt->resourceType() == QRhiRenderTarget::TextureRenderTarget
|
||||
? QRHI_RES(QD3D12ShadingRateMap, QRHI_RES(QD3D12TextureRenderTarget, rt)->m_desc.shadingRateMap())
|
||||
: QRHI_RES(QD3D12ShadingRateMap, QRHI_RES(QD3D12SwapChainRenderTarget, rt)->swapChain()->shadingRateMap());
|
||||
if (QD3D12Resource *res = resourcePool.lookupRef(rateMapD->handle)) {
|
||||
barrierGen.addTransitionBarrier(rateMapD->handle, D3D12_RESOURCE_STATE_SHADING_RATE_SOURCE);
|
||||
barrierGen.enqueueBufferedTransitionBarriers(cbD);
|
||||
cbD->cmdList->RSSetShadingRateImage(res->resource);
|
||||
hasShadingRateMapSet = true;
|
||||
}
|
||||
} else if (cbD->hasShadingRateMapSet) {
|
||||
cbD->cmdList->RSSetShadingRateImage(nullptr);
|
||||
cbD->setShadingRate(QSize(1, 1));
|
||||
} else if (cbD->hasShadingRateSet) {
|
||||
cbD->setShadingRate(QSize(1, 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
cbD->resetPerPassState();
|
||||
|
||||
// shading rate tracking is reset in resetPerPassState(), sync what we did just above
|
||||
cbD->hasShadingRateMapSet = hasShadingRateMapSet;
|
||||
}
|
||||
|
||||
void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
|
||||
@ -3140,7 +3262,7 @@ DXGI_SAMPLE_DESC QRhiD3D12::effectiveSampleDesc(int sampleCount, DXGI_FORMAT for
|
||||
return desc;
|
||||
}
|
||||
|
||||
bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList)
|
||||
bool QRhiD3D12::startCommandListForCurrentFrameSlot(D3D12GraphicsCommandList **cmdList)
|
||||
{
|
||||
ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot];
|
||||
if (!*cmdList) {
|
||||
@ -3148,7 +3270,7 @@ bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 *
|
||||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
cmdAlloc,
|
||||
nullptr,
|
||||
__uuidof(ID3D12GraphicsCommandList1),
|
||||
__uuidof(D3D12GraphicsCommandList),
|
||||
reinterpret_cast<void **>(cmdList));
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to create command list: %s", qPrintable(QSystemError::windowsComString(hr)));
|
||||
@ -3873,6 +3995,8 @@ static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTex
|
||||
return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
case QRhiTexture::R8:
|
||||
return DXGI_FORMAT_R8_UNORM;
|
||||
case QRhiTexture::R8UI:
|
||||
return DXGI_FORMAT_R8_UINT;
|
||||
case QRhiTexture::RG8:
|
||||
return DXGI_FORMAT_R8G8_UNORM;
|
||||
case QRhiTexture::R16:
|
||||
@ -4595,6 +4719,34 @@ QD3D12Descriptor QD3D12Sampler::lookupOrCreateShaderVisibleDescriptor()
|
||||
return shaderVisibleDescriptor;
|
||||
}
|
||||
|
||||
QD3D12ShadingRateMap::QD3D12ShadingRateMap(QRhiImplementation *rhi)
|
||||
: QRhiShadingRateMap(rhi)
|
||||
{
|
||||
}
|
||||
|
||||
QD3D12ShadingRateMap::~QD3D12ShadingRateMap()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void QD3D12ShadingRateMap::destroy()
|
||||
{
|
||||
if (handle.isNull())
|
||||
return;
|
||||
|
||||
handle = {};
|
||||
}
|
||||
|
||||
bool QD3D12ShadingRateMap::createFrom(QRhiTexture *src)
|
||||
{
|
||||
if (!handle.isNull())
|
||||
destroy();
|
||||
|
||||
handle = QRHI_RES(QD3D12Texture, src)->handle;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QD3D12TextureRenderTarget::QD3D12TextureRenderTarget(QRhiImplementation *rhi,
|
||||
const QRhiTextureRenderTargetDescription &desc,
|
||||
Flags flags)
|
||||
@ -4659,6 +4811,8 @@ QRhiRenderPassDescriptor *QD3D12TextureRenderTarget::newCompatibleRenderPassDesc
|
||||
rpD->dsFormat = toD3DDepthTextureDSVFormat(depthTexD->format()); // cannot be a typeless format
|
||||
}
|
||||
|
||||
rpD->hasShadingRateMap = m_desc.shadingRateMap() != nullptr;
|
||||
|
||||
rpD->updateSerializedFormat();
|
||||
|
||||
QRHI_RES_RHI(QRhiD3D12);
|
||||
@ -6003,6 +6157,9 @@ bool QD3D12RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasShadingRateMap != o->hasShadingRateMap)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6025,6 +6182,7 @@ QRhiRenderPassDescriptor *QD3D12RenderPassDescriptor::newCompatibleRenderPassDes
|
||||
rpD->hasDepthStencil = hasDepthStencil;
|
||||
memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
|
||||
rpD->dsFormat = dsFormat;
|
||||
rpD->hasShadingRateMap = hasShadingRateMap;
|
||||
|
||||
rpD->updateSerializedFormat();
|
||||
|
||||
@ -6251,6 +6409,9 @@ QRhiRenderPassDescriptor *QD3D12SwapChain::newCompatibleRenderPassDescriptor()
|
||||
rpD->hasDepthStencil = m_depthStencil != nullptr;
|
||||
rpD->colorFormat[0] = int(srgbAdjustedColorFormat);
|
||||
rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
|
||||
|
||||
rpD->hasShadingRateMap = m_shadingRateMap != nullptr;
|
||||
|
||||
rpD->updateSerializedFormat();
|
||||
|
||||
QRHI_RES_RHI(QRhiD3D12);
|
||||
|
@ -38,6 +38,15 @@
|
||||
#ifdef __ID3D12Device2_INTERFACE_DEFINED__
|
||||
#define QRHI_D3D12_AVAILABLE
|
||||
|
||||
// Will use ID3D12GraphicsCommandList5 as long as the d3d12.h is new enough.
|
||||
// Otherwise, some features (VRS) will not be available.
|
||||
#ifdef __ID3D12GraphicsCommandList5_INTERFACE_DEFINED__
|
||||
#define QRHI_D3D12_CL5_AVAILABLE
|
||||
using D3D12GraphicsCommandList = ID3D12GraphicsCommandList5;
|
||||
#else
|
||||
using D3D12GraphicsCommandList = ID3D12GraphicsCommandList1;
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static const int QD3D12_FRAMES_IN_FLIGHT = 2;
|
||||
@ -754,6 +763,17 @@ struct QD3D12Sampler : public QRhiSampler
|
||||
QD3D12Descriptor shaderVisibleDescriptor;
|
||||
};
|
||||
|
||||
struct QD3D12ShadingRateMap : public QRhiShadingRateMap
|
||||
{
|
||||
QD3D12ShadingRateMap(QRhiImplementation *rhi);
|
||||
~QD3D12ShadingRateMap();
|
||||
void destroy() override;
|
||||
bool createFrom(QRhiTexture *src) override;
|
||||
|
||||
QD3D12ObjectHandle handle; // just copied from the texture
|
||||
friend class QRhiD3D12;
|
||||
};
|
||||
|
||||
struct QD3D12RenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||
{
|
||||
QD3D12RenderPassDescriptor(QRhiImplementation *rhi);
|
||||
@ -770,6 +790,7 @@ struct QD3D12RenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||
bool hasDepthStencil = false;
|
||||
int colorFormat[MAX_COLOR_ATTACHMENTS];
|
||||
int dsFormat;
|
||||
bool hasShadingRateMap = false;
|
||||
QVector<quint32> serializedFormatData;
|
||||
};
|
||||
|
||||
@ -916,7 +937,7 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer
|
||||
|
||||
const QRhiNativeHandles *nativeHandles();
|
||||
|
||||
ID3D12GraphicsCommandList1 *cmdList = nullptr; // not owned
|
||||
D3D12GraphicsCommandList *cmdList = nullptr; // not owned
|
||||
QRhiD3D12CommandBufferNativeHandles nativeHandlesStruct;
|
||||
|
||||
enum PassType {
|
||||
@ -946,6 +967,8 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer
|
||||
currentIndexFormat = DXGI_FORMAT_R16_UINT;
|
||||
currentVertexBuffers = {};
|
||||
currentVertexOffsets = {};
|
||||
hasShadingRateSet = false;
|
||||
hasShadingRateMapSet = false;
|
||||
}
|
||||
|
||||
// per-frame
|
||||
@ -964,6 +987,8 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer
|
||||
DXGI_FORMAT currentIndexFormat;
|
||||
std::array<QD3D12ObjectHandle, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexBuffers;
|
||||
std::array<quint32, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexOffsets;
|
||||
bool hasShadingRateSet;
|
||||
bool hasShadingRateMapSet;
|
||||
|
||||
// global
|
||||
double lastGpuTime = 0;
|
||||
@ -1050,7 +1075,7 @@ struct QD3D12SwapChain : public QRhiSwapChain
|
||||
ID3D12Fence *fence = nullptr;
|
||||
HANDLE fenceEvent = nullptr;
|
||||
UINT64 fenceCounter = 0;
|
||||
ID3D12GraphicsCommandList1 *cmdList = nullptr;
|
||||
D3D12GraphicsCommandList *cmdList = nullptr;
|
||||
} frameRes[QD3D12_FRAMES_IN_FLIGHT];
|
||||
|
||||
int currentFrameSlot = 0; // index in frameRes
|
||||
@ -1104,6 +1129,8 @@ public:
|
||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) override;
|
||||
|
||||
QRhiShadingRateMap *createShadingRateMap() override;
|
||||
|
||||
QRhiSwapChain *createSwapChain() override;
|
||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
|
||||
@ -1138,6 +1165,7 @@ public:
|
||||
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||
|
||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||
@ -1163,6 +1191,7 @@ public:
|
||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||
|
||||
QList<int> supportedSampleCounts() const override;
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||
int ubufAlignment() const override;
|
||||
bool isYUpInFramebuffer() const override;
|
||||
bool isYUpInNDC() const override;
|
||||
@ -1184,7 +1213,7 @@ public:
|
||||
void waitGpu();
|
||||
DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const;
|
||||
bool ensureDirectCompositionDevice();
|
||||
bool startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList);
|
||||
bool startCommandListForCurrentFrameSlot(D3D12GraphicsCommandList **cmdList);
|
||||
void enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
|
||||
void finishActiveReadbacks(bool forced = false);
|
||||
bool ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
|
||||
@ -1236,10 +1265,14 @@ public:
|
||||
QVarLengthArray<QD3D12Readback, 4> activeReadbacks;
|
||||
bool offscreenActive = false;
|
||||
QD3D12CommandBuffer *offscreenCb[QD3D12_FRAMES_IN_FLIGHT] = {};
|
||||
UINT shadingRateImageTileSize = 0;
|
||||
|
||||
struct {
|
||||
bool multiView = false;
|
||||
bool textureViewFormat = false;
|
||||
bool vrs = false;
|
||||
bool vrsMap = false;
|
||||
bool vrsAdditionalRates = false;
|
||||
} caps;
|
||||
};
|
||||
|
||||
|
@ -160,6 +160,10 @@ QT_BEGIN_NAMESPACE
|
||||
#define GL_R8 0x8229
|
||||
#endif
|
||||
|
||||
#ifndef GL_R8UI
|
||||
#define GL_R8UI 0x8232
|
||||
#endif
|
||||
|
||||
#ifndef GL_RG8
|
||||
#define GL_RG8 0x822B
|
||||
#endif
|
||||
@ -1188,6 +1192,12 @@ QList<int> QRhiGles2::supportedSampleCounts() const
|
||||
return supportedSampleCountList;
|
||||
}
|
||||
|
||||
QList<QSize> QRhiGles2::supportedShadingRates(int sampleCount) const
|
||||
{
|
||||
Q_UNUSED(sampleCount);
|
||||
return { QSize(1, 1) };
|
||||
}
|
||||
|
||||
QRhiSwapChain *QRhiGles2::createSwapChain()
|
||||
{
|
||||
return new QGles2SwapChain(this);
|
||||
@ -1260,6 +1270,12 @@ static inline void toGlTextureFormat(QRhiTexture::Format format, const QRhiGles2
|
||||
*glformat = GL_RED;
|
||||
*gltype = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case QRhiTexture::R8UI:
|
||||
*glintformat = GL_R8UI;
|
||||
*glsizedintformat = *glintformat;
|
||||
*glformat = GL_RED;
|
||||
*gltype = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case QRhiTexture::RG8:
|
||||
*glintformat = GL_RG8;
|
||||
*glsizedintformat = *glintformat;
|
||||
@ -1363,6 +1379,7 @@ bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture
|
||||
return caps.bgraExternalFormat;
|
||||
|
||||
case QRhiTexture::R8:
|
||||
case QRhiTexture::R8UI:
|
||||
return caps.r8Format;
|
||||
|
||||
case QRhiTexture::RG8:
|
||||
@ -1481,6 +1498,11 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
||||
return false;
|
||||
case QRhi::ResolveDepthStencil:
|
||||
return true;
|
||||
case QRhi::VariableRateShading:
|
||||
return false;
|
||||
case QRhi::VariableRateShadingMap:
|
||||
case QRhi::VariableRateShadingMapWithTexture:
|
||||
return false;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
@ -1519,6 +1541,8 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
|
||||
return caps.maxVertexInputs;
|
||||
case QRhi::MaxVertexOutputs:
|
||||
return caps.maxVertexOutputs;
|
||||
case QRhi::ShadingRateImageTileSize:
|
||||
return 0;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(0);
|
||||
}
|
||||
@ -1719,6 +1743,11 @@ QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler
|
||||
return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
|
||||
}
|
||||
|
||||
QRhiShadingRateMap *QRhiGles2::createShadingRateMap()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags)
|
||||
{
|
||||
@ -1992,6 +2021,12 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
||||
cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
|
||||
}
|
||||
|
||||
void QRhiGles2::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
|
||||
{
|
||||
Q_UNUSED(cb);
|
||||
Q_UNUSED(coarsePixelSize);
|
||||
}
|
||||
|
||||
void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||
{
|
||||
|
@ -794,6 +794,8 @@ public:
|
||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) override;
|
||||
|
||||
QRhiShadingRateMap *createShadingRateMap() override;
|
||||
|
||||
QRhiSwapChain *createSwapChain() override;
|
||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
|
||||
@ -828,6 +830,7 @@ public:
|
||||
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||
|
||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||
@ -853,6 +856,7 @@ public:
|
||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||
|
||||
QList<int> supportedSampleCounts() const override;
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||
int ubufAlignment() const override;
|
||||
bool isYUpInFramebuffer() const override;
|
||||
bool isYUpInNDC() const override;
|
||||
|
@ -176,7 +176,8 @@ struct QRhiMetalData
|
||||
MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
|
||||
const QColor &colorClearValue,
|
||||
const QRhiDepthStencilClearValue &depthStencilClearValue,
|
||||
int colorAttCount);
|
||||
int colorAttCount,
|
||||
QRhiShadingRateMap *shadingRateMap);
|
||||
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
|
||||
QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
|
||||
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
|
||||
@ -194,7 +195,8 @@ struct QRhiMetalData
|
||||
Sampler,
|
||||
StagingBuffer,
|
||||
GraphicsPipeline,
|
||||
ComputePipeline
|
||||
ComputePipeline,
|
||||
ShadingRateMap
|
||||
};
|
||||
Type type;
|
||||
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
|
||||
@ -225,6 +227,9 @@ struct QRhiMetalData
|
||||
struct {
|
||||
id<MTLComputePipelineState> pipelineState;
|
||||
} computePipeline;
|
||||
struct {
|
||||
id<MTLRasterizationRateMap> rateMap;
|
||||
} shadingRateMap;
|
||||
};
|
||||
};
|
||||
QVector<DeferredReleaseEntry> releaseQueue;
|
||||
@ -306,6 +311,11 @@ struct QMetalSamplerData
|
||||
id<MTLSamplerState> samplerState = nil;
|
||||
};
|
||||
|
||||
struct QMetalShadingRateMapData
|
||||
{
|
||||
id<MTLRasterizationRateMap> rateMap = nil;
|
||||
};
|
||||
|
||||
struct QMetalShaderResourceBindingsData {
|
||||
struct Stage {
|
||||
struct Buffer {
|
||||
@ -634,6 +644,10 @@ bool QRhiMetal::create(QRhi::Flags flags)
|
||||
caps.supportedSampleCounts.append(sampleCount);
|
||||
}
|
||||
|
||||
caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 1];
|
||||
if (caps.shadingRateMap && caps.multiView)
|
||||
caps.shadingRateMap = [d->dev supportsRasterizationRateMapWithLayerCount: 2];
|
||||
|
||||
if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
|
||||
d->setupBinaryArchive();
|
||||
|
||||
@ -672,6 +686,12 @@ QVector<int> QRhiMetal::supportedSampleCounts() const
|
||||
return caps.supportedSampleCounts;
|
||||
}
|
||||
|
||||
QVector<QSize> QRhiMetal::supportedShadingRates(int sampleCount) const
|
||||
{
|
||||
Q_UNUSED(sampleCount);
|
||||
return { QSize(1, 1) };
|
||||
}
|
||||
|
||||
QRhiSwapChain *QRhiMetal::createSwapChain()
|
||||
{
|
||||
return new QMetalSwapChain(this);
|
||||
@ -838,6 +858,12 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
||||
return false;
|
||||
case QRhi::ResolveDepthStencil:
|
||||
return true;
|
||||
case QRhi::VariableRateShading:
|
||||
return false;
|
||||
case QRhi::VariableRateShadingMap:
|
||||
return caps.shadingRateMap;
|
||||
case QRhi::VariableRateShadingMapWithTexture:
|
||||
return false;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
@ -875,6 +901,8 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
|
||||
return 31;
|
||||
case QRhi::MaxVertexOutputs:
|
||||
return 15; // use the minimum from MTLGPUFamily1/2/3
|
||||
case QRhi::ShadingRateImageTileSize:
|
||||
return 0;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return 0;
|
||||
@ -1063,6 +1091,11 @@ QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler
|
||||
return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
|
||||
}
|
||||
|
||||
QRhiShadingRateMap *QRhiMetal::createShadingRateMap()
|
||||
{
|
||||
return new QMetalShadingRateMap(this);
|
||||
}
|
||||
|
||||
QRhiTextureRenderTarget *QRhiMetal::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags)
|
||||
{
|
||||
@ -1886,6 +1919,12 @@ void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
||||
[cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
|
||||
}
|
||||
|
||||
void QRhiMetal::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
|
||||
{
|
||||
Q_UNUSED(cb);
|
||||
Q_UNUSED(coarsePixelSize);
|
||||
}
|
||||
|
||||
static id<MTLComputeCommandEncoder> tessellationComputeEncoder(QMetalCommandBuffer *cbD)
|
||||
{
|
||||
if (cbD->d->currentRenderPassEncoder) {
|
||||
@ -2557,7 +2596,8 @@ QRhi::FrameOpResult QRhiMetal::finish()
|
||||
MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
|
||||
const QColor &colorClearValue,
|
||||
const QRhiDepthStencilClearValue &depthStencilClearValue,
|
||||
int colorAttCount)
|
||||
int colorAttCount,
|
||||
QRhiShadingRateMap *shadingRateMap)
|
||||
{
|
||||
MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
|
||||
@ -2578,6 +2618,9 @@ MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthSte
|
||||
rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
|
||||
}
|
||||
|
||||
if (shadingRateMap)
|
||||
rp.rasterizationRateMap = QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
@ -2958,8 +3001,15 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
||||
QMetalRenderTargetData *rtD = nullptr;
|
||||
switch (rt->resourceType()) {
|
||||
case QRhiResource::SwapChainRenderTarget:
|
||||
rtD = QRHI_RES(QMetalSwapChainRenderTarget, rt)->d;
|
||||
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
|
||||
{
|
||||
QMetalSwapChainRenderTarget *rtSc = QRHI_RES(QMetalSwapChainRenderTarget, rt);
|
||||
rtD = rtSc->d;
|
||||
QRhiShadingRateMap *shadingRateMap = rtSc->swapChain()->shadingRateMap();
|
||||
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount,
|
||||
colorClearValue,
|
||||
depthStencilClearValue,
|
||||
rtD->colorAttCount,
|
||||
shadingRateMap);
|
||||
if (rtD->colorAttCount) {
|
||||
QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
|
||||
if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
|
||||
@ -2983,6 +3033,9 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shadingRateMap)
|
||||
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
|
||||
}
|
||||
break;
|
||||
case QRhiResource::TextureRenderTarget:
|
||||
{
|
||||
@ -2990,7 +3043,11 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
||||
rtD = rtTex->d;
|
||||
if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
|
||||
rtTex->create();
|
||||
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
|
||||
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount,
|
||||
colorClearValue,
|
||||
depthStencilClearValue,
|
||||
rtD->colorAttCount,
|
||||
rtTex->m_desc.shadingRateMap());
|
||||
if (rtD->fb.preserveColor) {
|
||||
for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
|
||||
cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
|
||||
@ -3024,6 +3081,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
||||
}
|
||||
if (rtTex->m_desc.depthResolveTexture())
|
||||
QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
|
||||
if (rtTex->m_desc.shadingRateMap())
|
||||
QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -3195,6 +3254,9 @@ void QRhiMetal::executeDeferredReleases(bool forced)
|
||||
case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
|
||||
[e.computePipeline.pipelineState release];
|
||||
break;
|
||||
case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
|
||||
[e.shadingRateMap.rateMap release];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -3393,6 +3455,8 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
|
||||
#else
|
||||
return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
|
||||
#endif
|
||||
case QRhiTexture::R8UI:
|
||||
return MTLPixelFormatR8Uint;
|
||||
case QRhiTexture::RG8:
|
||||
#ifdef Q_OS_MACOS
|
||||
return MTLPixelFormatRG8Unorm;
|
||||
@ -4034,6 +4098,55 @@ bool QMetalSampler::create()
|
||||
return true;
|
||||
}
|
||||
|
||||
QMetalShadingRateMap::QMetalShadingRateMap(QRhiImplementation *rhi)
|
||||
: QRhiShadingRateMap(rhi),
|
||||
d(new QMetalShadingRateMapData)
|
||||
{
|
||||
}
|
||||
|
||||
QMetalShadingRateMap::~QMetalShadingRateMap()
|
||||
{
|
||||
destroy();
|
||||
delete d;
|
||||
}
|
||||
|
||||
void QMetalShadingRateMap::destroy()
|
||||
{
|
||||
if (!d->rateMap)
|
||||
return;
|
||||
|
||||
QRhiMetalData::DeferredReleaseEntry e;
|
||||
e.type = QRhiMetalData::DeferredReleaseEntry::ShadingRateMap;
|
||||
e.lastActiveFrameSlot = lastActiveFrameSlot;
|
||||
|
||||
e.shadingRateMap.rateMap = d->rateMap;
|
||||
d->rateMap = nil;
|
||||
|
||||
QRHI_RES_RHI(QRhiMetal);
|
||||
if (rhiD) {
|
||||
rhiD->d->releaseQueue.append(e);
|
||||
rhiD->unregisterResource(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool QMetalShadingRateMap::createFrom(NativeShadingRateMap src)
|
||||
{
|
||||
if (d->rateMap)
|
||||
destroy();
|
||||
|
||||
d->rateMap = (id<MTLRasterizationRateMap>) (quintptr(src.object));
|
||||
if (!d->rateMap)
|
||||
return false;
|
||||
|
||||
[d->rateMap retain];
|
||||
|
||||
lastActiveFrameSlot = -1;
|
||||
generation += 1;
|
||||
QRHI_RES_RHI(QRhiMetal);
|
||||
rhiD->registerResource(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
// dummy, no Vulkan-style RenderPass+Framebuffer concept here.
|
||||
// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass.
|
||||
QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi)
|
||||
@ -4077,6 +4190,9 @@ bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasShadingRateMap != o->hasShadingRateMap)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4088,8 +4204,9 @@ void QMetalRenderPassDescriptor::updateSerializedFormat()
|
||||
*p++ = colorAttachmentCount;
|
||||
*p++ = hasDepthStencil;
|
||||
for (int i = 0; i < colorAttachmentCount; ++i)
|
||||
*p++ = colorFormat[i];
|
||||
*p++ = colorFormat[i];
|
||||
*p++ = hasDepthStencil ? dsFormat : 0;
|
||||
*p++ = hasShadingRateMap;
|
||||
}
|
||||
|
||||
QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
|
||||
@ -4099,6 +4216,7 @@ QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDes
|
||||
rpD->hasDepthStencil = hasDepthStencil;
|
||||
memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
|
||||
rpD->dsFormat = dsFormat;
|
||||
rpD->hasShadingRateMap = hasShadingRateMap;
|
||||
|
||||
rpD->updateSerializedFormat();
|
||||
|
||||
@ -4184,6 +4302,8 @@ QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDesc
|
||||
else if (m_desc.depthStencilBuffer())
|
||||
rpD->dsFormat = int(QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
|
||||
|
||||
rpD->hasShadingRateMap = m_desc.shadingRateMap() != nullptr;
|
||||
|
||||
rpD->updateSerializedFormat();
|
||||
|
||||
QRHI_RES_RHI(QRhiMetal);
|
||||
@ -6223,6 +6343,8 @@ QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
|
||||
rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
|
||||
#endif
|
||||
|
||||
rpD->hasShadingRateMap = m_shadingRateMap != nullptr;
|
||||
|
||||
rpD->updateSerializedFormat();
|
||||
|
||||
rhiD->registerResource(rpD, false);
|
||||
|
@ -105,6 +105,21 @@ struct QMetalSampler : public QRhiSampler
|
||||
friend struct QMetalShaderResourceBindings;
|
||||
};
|
||||
|
||||
struct QMetalShadingRateMapData;
|
||||
|
||||
struct QMetalShadingRateMap : public QRhiShadingRateMap
|
||||
{
|
||||
QMetalShadingRateMap(QRhiImplementation *rhi);
|
||||
~QMetalShadingRateMap();
|
||||
void destroy() override;
|
||||
bool createFrom(NativeShadingRateMap src) override;
|
||||
|
||||
QMetalShadingRateMapData *d;
|
||||
uint generation = 0;
|
||||
int lastActiveFrameSlot = -1;
|
||||
friend class QRhiMetal;
|
||||
};
|
||||
|
||||
struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||
{
|
||||
QMetalRenderPassDescriptor(QRhiImplementation *rhi);
|
||||
@ -124,6 +139,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||
bool hasDepthStencil = false;
|
||||
int colorFormat[MAX_COLOR_ATTACHMENTS];
|
||||
int dsFormat;
|
||||
bool hasShadingRateMap = false;
|
||||
QVector<quint32> serializedFormatData;
|
||||
};
|
||||
|
||||
@ -361,6 +377,8 @@ public:
|
||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) override;
|
||||
|
||||
QRhiShadingRateMap *createShadingRateMap() override;
|
||||
|
||||
QRhiSwapChain *createSwapChain() override;
|
||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
|
||||
@ -395,6 +413,7 @@ public:
|
||||
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||
|
||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||
@ -420,6 +439,7 @@ public:
|
||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||
|
||||
QList<int> supportedSampleCounts() const override;
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||
int ubufAlignment() const override;
|
||||
bool isYUpInFramebuffer() const override;
|
||||
bool isYUpInNDC() const override;
|
||||
@ -500,6 +520,7 @@ public:
|
||||
bool isAppleGPU = false;
|
||||
int maxThreadGroupSize = 512;
|
||||
bool multiView = false;
|
||||
bool shadingRateMap = false;
|
||||
} caps;
|
||||
|
||||
QRhiMetalData *d = nullptr;
|
||||
|
@ -60,6 +60,12 @@ QList<int> QRhiNull::supportedSampleCounts() const
|
||||
return { 1 };
|
||||
}
|
||||
|
||||
QList<QSize> QRhiNull::supportedShadingRates(int sampleCount) const
|
||||
{
|
||||
Q_UNUSED(sampleCount);
|
||||
return { QSize(1, 1) };
|
||||
}
|
||||
|
||||
QRhiSwapChain *QRhiNull::createSwapChain()
|
||||
{
|
||||
return new QNullSwapChain(this);
|
||||
@ -139,6 +145,8 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
|
||||
return 32;
|
||||
case QRhi::MaxVertexOutputs:
|
||||
return 32;
|
||||
case QRhi::ShadingRateImageTileSize:
|
||||
return 0;
|
||||
}
|
||||
|
||||
Q_UNREACHABLE_RETURN(0);
|
||||
@ -214,6 +222,11 @@ QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRe
|
||||
return new QNullTextureRenderTarget(this, desc, flags);
|
||||
}
|
||||
|
||||
QRhiShadingRateMap *QRhiNull::createShadingRateMap()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
|
||||
{
|
||||
return new QNullGraphicsPipeline(this);
|
||||
@ -282,6 +295,12 @@ void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
||||
Q_UNUSED(refValue);
|
||||
}
|
||||
|
||||
void QRhiNull::setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize)
|
||||
{
|
||||
Q_UNUSED(cb);
|
||||
Q_UNUSED(coarsePixelSize);
|
||||
}
|
||||
|
||||
void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||
{
|
||||
|
@ -204,6 +204,8 @@ public:
|
||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) override;
|
||||
|
||||
QRhiShadingRateMap *createShadingRateMap() override;
|
||||
|
||||
QRhiSwapChain *createSwapChain() override;
|
||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
|
||||
@ -238,6 +240,7 @@ public:
|
||||
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||
|
||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||
@ -263,6 +266,7 @@ public:
|
||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||
|
||||
QList<int> supportedSampleCounts() const override;
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||
int ubufAlignment() const override;
|
||||
bool isYUpInFramebuffer() const override;
|
||||
bool isYUpInNDC() const override;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -145,6 +145,17 @@ struct QVkSampler : public QRhiSampler
|
||||
friend class QRhiVulkan;
|
||||
};
|
||||
|
||||
struct QVkShadingRateMap : public QRhiShadingRateMap
|
||||
{
|
||||
QVkShadingRateMap(QRhiImplementation *rhi);
|
||||
~QVkShadingRateMap();
|
||||
void destroy() override;
|
||||
bool createFrom(QRhiTexture *src) override;
|
||||
|
||||
QVkTexture *texture = nullptr; // not owned
|
||||
friend class QRhiVulkan;
|
||||
};
|
||||
|
||||
struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||
{
|
||||
QVkRenderPassDescriptor(QRhiImplementation *rhi);
|
||||
@ -165,9 +176,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||
QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
|
||||
bool hasDepthStencil = false;
|
||||
bool hasDepthStencilResolve = false;
|
||||
bool hasShadingRateMap = false;
|
||||
uint32_t multiViewCount = 0;
|
||||
VkAttachmentReference dsRef;
|
||||
VkAttachmentReference dsResolveRef;
|
||||
VkAttachmentReference shadingRateRef;
|
||||
QVector<quint32> serializedFormatData;
|
||||
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
|
||||
int lastActiveFrameSlot = -1;
|
||||
@ -184,6 +197,7 @@ struct QVkRenderTargetData
|
||||
int dsAttCount = 0;
|
||||
int resolveAttCount = 0;
|
||||
int dsResolveAttCount = 0;
|
||||
int shadingRateAttCount = 0;
|
||||
int multiViewCount = 0;
|
||||
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
|
||||
static const int MAX_COLOR_ATTACHMENTS = 8;
|
||||
@ -220,6 +234,7 @@ struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
|
||||
VkImageView dsv = VK_NULL_HANDLE;
|
||||
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
|
||||
VkImageView resdsv = VK_NULL_HANDLE;
|
||||
VkImageView shadingRateMapView = VK_NULL_HANDLE;
|
||||
int lastActiveFrameSlot = -1;
|
||||
friend class QRhiVulkan;
|
||||
};
|
||||
@ -332,10 +347,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
||||
currentTarget = nullptr;
|
||||
activeSecondaryCbStack.clear();
|
||||
resetCommands();
|
||||
resetCachedState();
|
||||
resetPerPassState();
|
||||
}
|
||||
|
||||
void resetCachedState() {
|
||||
void resetPerPassState() {
|
||||
currentGraphicsPipeline = nullptr;
|
||||
currentComputePipeline = nullptr;
|
||||
currentPipelineGeneration = 0;
|
||||
@ -349,6 +364,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
||||
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
|
||||
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
|
||||
inExternal = false;
|
||||
hasShadingRateSet = false;
|
||||
}
|
||||
|
||||
PassType recordingPass;
|
||||
@ -370,6 +386,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
||||
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
|
||||
QVarLengthArray<VkCommandBuffer, 4> activeSecondaryCbStack;
|
||||
bool inExternal;
|
||||
bool hasShadingRateSet;
|
||||
|
||||
struct {
|
||||
QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
|
||||
@ -404,7 +421,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
||||
DebugMarkerInsert,
|
||||
TransitionPassResources,
|
||||
Dispatch,
|
||||
ExecuteSecondary
|
||||
ExecuteSecondary,
|
||||
SetShadingRate
|
||||
};
|
||||
Cmd cmd;
|
||||
|
||||
@ -531,6 +549,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
||||
struct {
|
||||
VkCommandBuffer cb;
|
||||
} executeSecondary;
|
||||
struct {
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
} setShadingRate;
|
||||
} args;
|
||||
};
|
||||
|
||||
@ -608,6 +630,7 @@ struct QVkSwapChain : public QRhiSwapChain
|
||||
QVkSwapChainRenderTarget rtWrapper;
|
||||
QVkSwapChainRenderTarget rtWrapperRight;
|
||||
QVkCommandBuffer cbWrapper;
|
||||
VkImageView shadingRateMapView = VK_NULL_HANDLE;
|
||||
|
||||
struct ImageResources {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
@ -679,6 +702,8 @@ public:
|
||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||
QRhiTextureRenderTarget::Flags flags) override;
|
||||
|
||||
QRhiShadingRateMap *createShadingRateMap() override;
|
||||
|
||||
QRhiSwapChain *createSwapChain() override;
|
||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
|
||||
@ -713,6 +738,7 @@ public:
|
||||
void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||
|
||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||
@ -738,6 +764,7 @@ public:
|
||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||
|
||||
QList<int> supportedSampleCounts() const override;
|
||||
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||
int ubufAlignment() const override;
|
||||
bool isYUpInFramebuffer() const override;
|
||||
bool isYUpInNDC() const override;
|
||||
@ -771,7 +798,8 @@ public:
|
||||
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD,
|
||||
bool hasDepthStencil,
|
||||
VkSampleCountFlagBits samples,
|
||||
VkFormat colorFormat);
|
||||
VkFormat colorFormat,
|
||||
QRhiShadingRateMap *shadingRateMap);
|
||||
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
|
||||
const QRhiColorAttachment *colorAttachmentsBegin,
|
||||
const QRhiColorAttachment *colorAttachmentsEnd,
|
||||
@ -780,7 +808,8 @@ public:
|
||||
bool storeDs,
|
||||
QRhiRenderBuffer *depthStencilBuffer,
|
||||
QRhiTexture *depthTexture,
|
||||
QRhiTexture *depthResolveTexture);
|
||||
QRhiTexture *depthResolveTexture,
|
||||
QRhiShadingRateMap *shadingRateMap);
|
||||
bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0);
|
||||
VkShaderModule createShader(const QByteArray &spirv);
|
||||
|
||||
@ -897,7 +926,10 @@ public:
|
||||
bool multiView = false;
|
||||
bool renderPass2KHR = false;
|
||||
bool depthStencilResolveKHR = false;
|
||||
bool perDrawShadingRate = false;
|
||||
bool imageBasedShadingRate = false;
|
||||
QVersionNumber apiVersion;
|
||||
int imageBasedShadingRateTileSize = 0;
|
||||
} caps;
|
||||
|
||||
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
||||
@ -1013,6 +1045,7 @@ public:
|
||||
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
|
||||
VkImageView dsv;
|
||||
VkImageView resdsv;
|
||||
VkImageView shadingRateMapView;
|
||||
} textureRenderTarget;
|
||||
struct {
|
||||
VkRenderPass rp;
|
||||
@ -1027,6 +1060,11 @@ public:
|
||||
};
|
||||
};
|
||||
QList<DeferredReleaseEntry> releaseQueue;
|
||||
|
||||
#ifdef VK_KHR_fragment_shading_rate
|
||||
QVarLengthArray<VkPhysicalDeviceFragmentShadingRateKHR, 8> fragmentShadingRates;
|
||||
PFN_vkCmdSetFragmentShadingRateKHR vkCmdSetFragmentShadingRateKHR = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_RELOCATABLE_TYPE);
|
||||
|
@ -34,6 +34,7 @@ add_subdirectory(displacement)
|
||||
add_subdirectory(imguirenderer)
|
||||
add_subdirectory(multiview)
|
||||
add_subdirectory(msaatextureresolve)
|
||||
add_subdirectory(vrs)
|
||||
if(QT_FEATURE_widgets)
|
||||
add_subdirectory(rhiwidgetproto)
|
||||
endif()
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <rhi/qrhi.h>
|
||||
|
||||
#ifdef EXAMPLEFW_IMGUI
|
||||
#include "qrhiimgui_p.h"
|
||||
#include "qrhiimgui.h"
|
||||
#include "imgui.h"
|
||||
#endif
|
||||
|
||||
@ -98,6 +98,9 @@ protected:
|
||||
|
||||
void customInit();
|
||||
void customRelease();
|
||||
#ifdef EXAMPLEFW_BEFORE_FRAME
|
||||
void customBeforeFrame();
|
||||
#endif
|
||||
void customRender();
|
||||
#ifdef EXAMPLEFW_IMGUI
|
||||
void customGui();
|
||||
@ -360,6 +363,10 @@ void Window::render()
|
||||
if (!m_hasSwapChain || m_notExposed)
|
||||
return;
|
||||
|
||||
#ifdef EXAMPLEFW_BEFORE_FRAME
|
||||
customBeforeFrame();
|
||||
#endif
|
||||
|
||||
// If the window got resized or got newly exposed, resize the swapchain.
|
||||
// (the newly-exposed case is not actually required by some
|
||||
// platforms/backends, but f.ex. Vulkan on Windows seems to need it)
|
||||
|
@ -5,7 +5,7 @@ set(imgui_sources
|
||||
${imgui_base}/imgui/imgui_widgets.cpp
|
||||
${imgui_base}/imgui/imgui_demo.cpp
|
||||
${imgui_base}/qrhiimgui.cpp
|
||||
${imgui_base}/qrhiimgui_p.h
|
||||
${imgui_base}/qrhiimgui.h
|
||||
)
|
||||
|
||||
target_sources(${imgui_target} PRIVATE
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "qrhiimgui_p.h"
|
||||
#include "qrhiimgui.h"
|
||||
#include <QtCore/qfile.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qevent.h>
|
||||
@ -34,7 +34,8 @@ QRhiImguiRenderer::~QRhiImguiRenderer()
|
||||
void QRhiImguiRenderer::releaseResources()
|
||||
{
|
||||
for (Texture &t : m_textures) {
|
||||
delete t.tex;
|
||||
if (t.ownTex)
|
||||
delete t.tex;
|
||||
delete t.srb;
|
||||
}
|
||||
m_textures.clear();
|
||||
@ -43,7 +44,8 @@ void QRhiImguiRenderer::releaseResources()
|
||||
m_ibuf.reset();
|
||||
m_ubuf.reset();
|
||||
m_ps.reset();
|
||||
m_sampler.reset();
|
||||
m_linearSampler.reset();
|
||||
m_nearestSampler.reset();
|
||||
|
||||
m_rhi = nullptr;
|
||||
}
|
||||
@ -100,41 +102,53 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_sampler) {
|
||||
m_sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
||||
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
||||
m_sampler->setName(QByteArrayLiteral("imgui sampler"));
|
||||
if (!m_sampler->create())
|
||||
if (!m_linearSampler) {
|
||||
m_linearSampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
||||
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
||||
m_linearSampler->setName(QByteArrayLiteral("imgui linear sampler"));
|
||||
if (!m_linearSampler->create())
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_nearestSampler) {
|
||||
m_nearestSampler.reset(m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
||||
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
||||
m_nearestSampler->setName(QByteArrayLiteral("imgui nearest sampler"));
|
||||
if (!m_nearestSampler->create())
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_textures.isEmpty()) {
|
||||
Texture fontTex;
|
||||
fontTex.image = sf.fontTextureData;
|
||||
m_textures.append(fontTex);
|
||||
} else if (!sf.fontTextureData.isNull()) {
|
||||
m_textures.insert(nullptr, fontTex);
|
||||
sf.reset();
|
||||
} else if (sf.isValid()) {
|
||||
Texture fontTex;
|
||||
fontTex.image = sf.fontTextureData;
|
||||
delete m_textures[0].tex;
|
||||
delete m_textures[0].srb;
|
||||
m_textures[0] = fontTex;
|
||||
Texture &fontTexEntry(m_textures[nullptr]);
|
||||
delete fontTexEntry.tex;
|
||||
delete fontTexEntry.srb;
|
||||
fontTexEntry = fontTex;
|
||||
sf.reset();
|
||||
}
|
||||
|
||||
QVarLengthArray<int, 8> texturesNeedUpdate;
|
||||
for (int i = 0; i < m_textures.count(); ++i) {
|
||||
Texture &t(m_textures[i]);
|
||||
QVarLengthArray<void *, 8> texturesNeedUpdate;
|
||||
for (auto it = m_textures.begin(), end = m_textures.end(); it != end; ++it) {
|
||||
Texture &t(*it);
|
||||
if (!t.tex) {
|
||||
t.tex = m_rhi->newTexture(QRhiTexture::RGBA8, t.image.size());
|
||||
t.tex->setName(QByteArrayLiteral("imgui texture ") + QByteArray::number(i));
|
||||
t.tex->setName(QByteArrayLiteral("imgui texture ") + QByteArray::number(qintptr(it.key())));
|
||||
if (!t.tex->create())
|
||||
return;
|
||||
texturesNeedUpdate.append(i);
|
||||
texturesNeedUpdate.append(it.key());
|
||||
}
|
||||
if (!t.srb) {
|
||||
QRhiSampler *sampler = t.filter == QRhiSampler::Nearest ? m_nearestSampler.get() : m_linearSampler.get();
|
||||
t.srb = m_rhi->newShaderResourceBindings();
|
||||
t.srb->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_ubuf.get()),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, t.tex, m_sampler.get())
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, t.tex, sampler)
|
||||
});
|
||||
if (!t.srb->create())
|
||||
return;
|
||||
@ -146,6 +160,9 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
||||
if (m_ps && m_rt->renderPassDescriptor()->serializedFormat() != m_renderPassFormat)
|
||||
m_ps.reset();
|
||||
|
||||
if (m_ps && m_rt->sampleCount() != m_ps->sampleCount())
|
||||
m_ps.reset();
|
||||
|
||||
if (!m_ps) {
|
||||
QShader vs = getShader(QLatin1String(":/imgui.vert.qsb"));
|
||||
QShader fs = getShader(QLatin1String(":/imgui.frag.qsb"));
|
||||
@ -170,7 +187,7 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
||||
m_ps->setDepthTest(true);
|
||||
m_ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
|
||||
m_ps->setDepthWrite(false);
|
||||
m_ps->setFlags(QRhiGraphicsPipeline::UsesScissor);
|
||||
m_ps->setFlags(QRhiGraphicsPipeline::UsesScissor | QRhiGraphicsPipeline::UsesShadingRate);
|
||||
|
||||
m_ps->setShaderStages({
|
||||
{ QRhiShaderStage::Vertex, vs },
|
||||
@ -186,8 +203,8 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
||||
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) },
|
||||
{ 0, 2, QRhiVertexInputAttribute::UNormByte4, 4 * sizeof(float) }
|
||||
});
|
||||
|
||||
m_ps->setVertexInputLayout(inputLayout);
|
||||
m_ps->setSampleCount(rt->sampleCount());
|
||||
m_ps->setShaderResourceBindings(m_textures[0].srb);
|
||||
m_ps->setRenderPassDescriptor(m_rt->renderPassDescriptor());
|
||||
m_renderPassFormat = m_rt->renderPassDescriptor()->serializedFormat();
|
||||
@ -210,8 +227,10 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
||||
|
||||
for (int i = 0; i < texturesNeedUpdate.count(); ++i) {
|
||||
Texture &t(m_textures[texturesNeedUpdate[i]]);
|
||||
u->uploadTexture(t.tex, t.image);
|
||||
t.image = QImage();
|
||||
if (!t.image.isNull()) {
|
||||
u->uploadTexture(t.tex, t.image);
|
||||
t.image = QImage();
|
||||
}
|
||||
}
|
||||
|
||||
m_cb->resourceUpdate(u);
|
||||
@ -244,12 +263,31 @@ void QRhiImguiRenderer::render()
|
||||
scissorSize.setWidth(qMin(viewportSize.width(), scissorSize.width()));
|
||||
scissorSize.setHeight(qMin(viewportSize.height(), scissorSize.height()));
|
||||
m_cb->setScissor({ scissorPos.x(), scissorPos.y(), scissorSize.width(), scissorSize.height() });
|
||||
m_cb->setShaderResources(m_textures[c.textureIndex].srb);
|
||||
m_cb->setShaderResources(m_textures[c.textureId].srb);
|
||||
m_cb->setVertexInput(0, 1, &vbufBinding, m_ibuf.get(), c.indexOffset, QRhiCommandBuffer::IndexUInt32);
|
||||
m_cb->drawIndexed(c.elemCount);
|
||||
}
|
||||
}
|
||||
|
||||
void QRhiImguiRenderer::registerCustomTexture(void *id,
|
||||
QRhiTexture *texture,
|
||||
QRhiSampler::Filter filter,
|
||||
CustomTextureOwnership ownership)
|
||||
{
|
||||
Q_ASSERT(id);
|
||||
auto it = m_textures.constFind(id);
|
||||
if (it != m_textures.cend()) {
|
||||
if (it->ownTex)
|
||||
delete it->tex;
|
||||
delete it->srb;
|
||||
}
|
||||
Texture t;
|
||||
t.tex = texture;
|
||||
t.filter = filter;
|
||||
t.ownTex = ownership == TakeCustomTextureOwnership;
|
||||
m_textures[id] = t;
|
||||
}
|
||||
|
||||
static const char *getClipboardText(void *)
|
||||
{
|
||||
static QByteArray contents;
|
||||
@ -264,7 +302,8 @@ static void setClipboardText(void *, const char *text)
|
||||
|
||||
QRhiImgui::QRhiImgui()
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
context = ImGui::CreateContext();
|
||||
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||
rebuildFontAtlas();
|
||||
ImGuiIO &io(ImGui::GetIO());
|
||||
io.GetClipboardTextFn = getClipboardText;
|
||||
@ -273,22 +312,40 @@ QRhiImgui::QRhiImgui()
|
||||
|
||||
QRhiImgui::~QRhiImgui()
|
||||
{
|
||||
ImGui::DestroyContext();
|
||||
ImGui::DestroyContext(static_cast<ImGuiContext *>(context));
|
||||
}
|
||||
|
||||
void QRhiImgui::rebuildFontAtlas()
|
||||
{
|
||||
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||
ImGuiIO &io(ImGui::GetIO());
|
||||
unsigned char *pixels;
|
||||
int w, h;
|
||||
ImGuiIO &io(ImGui::GetIO());
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &w, &h);
|
||||
const QImage wrapperImg(const_cast<const uchar *>(pixels), w, h, QImage::Format_RGBA8888);
|
||||
sf.fontTextureData = wrapperImg.copy();
|
||||
io.Fonts->SetTexID(reinterpret_cast<ImTextureID>(quintptr(0)));
|
||||
io.Fonts->SetTexID(nullptr);
|
||||
}
|
||||
|
||||
void QRhiImgui::rebuildFontAtlasWithFont(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Failed to open %s", qPrintable(filename));
|
||||
return;
|
||||
}
|
||||
QByteArray font = f.readAll();
|
||||
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||
ImFontConfig fontCfg;
|
||||
fontCfg.FontDataOwnedByAtlas = false;
|
||||
ImGui::GetIO().Fonts->Clear();
|
||||
ImGui::GetIO().Fonts->AddFontFromMemoryTTF(font.data(), font.size(), 20.0f, &fontCfg);
|
||||
rebuildFontAtlas();
|
||||
}
|
||||
|
||||
void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPointF &logicalOffset, FrameFunc frameFunc)
|
||||
{
|
||||
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||
ImGuiIO &io(ImGui::GetIO());
|
||||
|
||||
const QPointF itemPixelOffset = logicalOffset * dpr;
|
||||
@ -332,7 +389,7 @@ void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPoi
|
||||
if (!cmd->UserCallback) {
|
||||
QRhiImguiRenderer::DrawCmd dc;
|
||||
dc.cmdListBufferIdx = n;
|
||||
dc.textureIndex = int(reinterpret_cast<qintptr>(cmd->TextureId));
|
||||
dc.textureId = cmd->TextureId;
|
||||
dc.indexOffset = indexOffset;
|
||||
dc.elemCount = cmd->ElemCount;
|
||||
dc.itemPixelOffset = itemPixelOffset;
|
||||
@ -348,8 +405,10 @@ void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPoi
|
||||
|
||||
void QRhiImgui::syncRenderer(QRhiImguiRenderer *renderer)
|
||||
{
|
||||
renderer->sf = sf;
|
||||
sf.fontTextureData = QImage();
|
||||
if (sf.isValid()) {
|
||||
renderer->sf = sf;
|
||||
sf.reset();
|
||||
}
|
||||
renderer->f = std::move(f);
|
||||
}
|
||||
|
||||
@ -540,6 +599,7 @@ static ImGuiKey mapKey(int k)
|
||||
|
||||
bool QRhiImgui::processEvent(QEvent *event)
|
||||
{
|
||||
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||
ImGuiIO &io(ImGui::GetIO());
|
||||
|
||||
switch (event->type()) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef QRHIIMGUI_P_H
|
||||
#define QRHIIMGUI_P_H
|
||||
#ifndef QRHIIMGUI_H
|
||||
#define QRHIIMGUI_H
|
||||
|
||||
#include <rhi/qrhi.h>
|
||||
|
||||
@ -22,7 +22,7 @@ public:
|
||||
|
||||
struct DrawCmd {
|
||||
int cmdListBufferIdx;
|
||||
int textureIndex;
|
||||
void *textureId;
|
||||
quint32 indexOffset;
|
||||
quint32 elemCount;
|
||||
QPointF itemPixelOffset;
|
||||
@ -31,6 +31,8 @@ public:
|
||||
|
||||
struct StaticRenderData {
|
||||
QImage fontTextureData;
|
||||
bool isValid() const { return !fontTextureData.isNull(); }
|
||||
void reset() { fontTextureData = QImage(); }
|
||||
};
|
||||
|
||||
struct FrameRenderData {
|
||||
@ -54,6 +56,15 @@ public:
|
||||
void render();
|
||||
void releaseResources();
|
||||
|
||||
enum CustomTextureOwnership {
|
||||
TakeCustomTextureOwnership,
|
||||
NoCustomTextureOwnership
|
||||
};
|
||||
void registerCustomTexture(void *id,
|
||||
QRhiTexture *texture,
|
||||
QRhiSampler::Filter filter,
|
||||
CustomTextureOwnership ownership);
|
||||
|
||||
private:
|
||||
QRhi *m_rhi = nullptr;
|
||||
QRhiRenderTarget *m_rt = nullptr;
|
||||
@ -64,14 +75,17 @@ private:
|
||||
std::unique_ptr<QRhiBuffer> m_ubuf;
|
||||
std::unique_ptr<QRhiGraphicsPipeline> m_ps;
|
||||
QVector<quint32> m_renderPassFormat;
|
||||
std::unique_ptr<QRhiSampler> m_sampler;
|
||||
std::unique_ptr<QRhiSampler> m_linearSampler;
|
||||
std::unique_ptr<QRhiSampler> m_nearestSampler;
|
||||
|
||||
struct Texture {
|
||||
QImage image;
|
||||
QRhiTexture *tex = nullptr;
|
||||
QRhiShaderResourceBindings *srb = nullptr;
|
||||
QRhiSampler::Filter filter = QRhiSampler::Linear;
|
||||
bool ownTex = true;
|
||||
};
|
||||
QVector<Texture> m_textures;
|
||||
QHash<void *, Texture> m_textures;
|
||||
};
|
||||
|
||||
class QRhiImgui
|
||||
@ -86,8 +100,10 @@ public:
|
||||
bool processEvent(QEvent *e);
|
||||
|
||||
void rebuildFontAtlas();
|
||||
void rebuildFontAtlasWithFont(const QString &filename);
|
||||
|
||||
private:
|
||||
void *context;
|
||||
QRhiImguiRenderer::StaticRenderData sf;
|
||||
QRhiImguiRenderer::FrameRenderData f;
|
||||
Qt::MouseButtons pressedMouseButtons;
|
44
tests/manual/rhi/vrs/CMakeLists.txt
Normal file
44
tests/manual/rhi/vrs/CMakeLists.txt
Normal file
@ -0,0 +1,44 @@
|
||||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(vrs LANGUAGES CXX)
|
||||
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||
endif()
|
||||
|
||||
qt_internal_add_manual_test(vrs
|
||||
GUI
|
||||
SOURCES
|
||||
vrs.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
||||
|
||||
set_source_files_properties("../shared/texture.vert.qsb"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
|
||||
)
|
||||
set_source_files_properties("../shared/texture.frag.qsb"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
|
||||
)
|
||||
set_source_files_properties("../shared/qt256.png"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "qt256.png"
|
||||
)
|
||||
qt_internal_add_resource(vrs "vrs"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
"../shared/texture.vert.qsb"
|
||||
"../shared/texture.frag.qsb"
|
||||
"../shared/qt256.png"
|
||||
)
|
||||
|
||||
qt_internal_extend_target(vrs CONDITION QT_FEATURE_metal
|
||||
SOURCES
|
||||
vrs_metaltest.mm
|
||||
)
|
||||
|
||||
set(imgui_base ../shared/imgui)
|
||||
set(imgui_target vrs)
|
||||
include(${imgui_base}/imgui.cmakeinc)
|
428
tests/manual/rhi/vrs/vrs.cpp
Normal file
428
tests/manual/rhi/vrs/vrs.cpp
Normal file
@ -0,0 +1,428 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#define EXAMPLEFW_IMGUI
|
||||
#define EXAMPLEFW_BEFORE_FRAME
|
||||
#include "../shared/examplefw.h"
|
||||
|
||||
#include "../shared/cube.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#if QT_CONFIG(metal)
|
||||
void *makeRateMap(QRhi *rhi, const QSize &outputSizeInPixels);
|
||||
void releaseRateMap(void *map);
|
||||
#endif
|
||||
|
||||
const int CUBE_COUNT = 10;
|
||||
|
||||
struct {
|
||||
QMatrix4x4 winProj;
|
||||
QList<QRhiResource *> releasePool;
|
||||
QRhiResourceUpdateBatch *initialUpdates = nullptr;
|
||||
QRhiBuffer *vbuf = nullptr;
|
||||
QRhiBuffer *ubuf = nullptr;
|
||||
QRhiTexture *tex = nullptr;
|
||||
QRhiSampler *sampler = nullptr;
|
||||
QRhiShaderResourceBindings *srb = nullptr;
|
||||
QRhiGraphicsPipeline *ps = nullptr;
|
||||
bool showDemoWindow = true;
|
||||
float rotation = 35.0f;
|
||||
bool vrsSupported = false;
|
||||
bool vrsMapSupported = false;
|
||||
bool vrsMapImageSupported = false;
|
||||
QMap<int, QList<QSize>> supportedShadingRates;
|
||||
int cps[2] = {};
|
||||
bool applyRateMapWithImage = false;
|
||||
bool applyRateMapNative = false;
|
||||
bool applyRateMapPending = false;
|
||||
QRhiShadingRateMap *rateMap = nullptr;
|
||||
QRhiRenderPassDescriptor *scRpWithRateMap = nullptr;
|
||||
QRhiTexture *rateMapTexture = nullptr;
|
||||
QRhiTexture *rateMapTextureForVisualization = nullptr;
|
||||
void *nativeRateMap = nullptr;
|
||||
QSize nativeRateMapSize;
|
||||
QVector<float> tx;
|
||||
QVector<float> ty;
|
||||
QVector<float> scale;
|
||||
quint32 ubufAlignedSize;
|
||||
|
||||
bool textureBased = false;
|
||||
QRhiTexture *outTexture = nullptr;
|
||||
QRhiTextureRenderTarget *texRt = nullptr;
|
||||
QRhiRenderPassDescriptor *texRtRp = nullptr;
|
||||
QRhiRenderPassDescriptor *texRtRpWithRateMap = nullptr;
|
||||
} d;
|
||||
|
||||
void Window::customInit()
|
||||
{
|
||||
d.vrsSupported = m_r->isFeatureSupported(QRhi::VariableRateShading);
|
||||
d.vrsMapSupported = m_r->isFeatureSupported(QRhi::VariableRateShadingMap);
|
||||
d.vrsMapImageSupported = m_r->isFeatureSupported(QRhi::VariableRateShadingMapWithTexture);
|
||||
for (int sampleCount : { 1, 2, 4, 8, 16 })
|
||||
d.supportedShadingRates.insert(sampleCount, m_r->supportedShadingRates(sampleCount));
|
||||
|
||||
d.initialUpdates = m_r->nextResourceUpdateBatch();
|
||||
|
||||
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
|
||||
d.vbuf->create();
|
||||
d.releasePool << d.vbuf;
|
||||
|
||||
d.initialUpdates->uploadStaticBuffer(d.vbuf, cube);
|
||||
|
||||
d.ubufAlignedSize = m_r->ubufAligned(68);
|
||||
const quint32 ubufSize = d.ubufAlignedSize * CUBE_COUNT;
|
||||
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
|
||||
d.ubuf->create();
|
||||
d.releasePool << d.ubuf;
|
||||
|
||||
QImage image = QImage(QLatin1String(":/qt256.png")).convertToFormat(QImage::Format_RGBA8888).mirrored();
|
||||
d.tex = m_r->newTexture(QRhiTexture::RGBA8, QSize(image.width(), image.height()), 1, {});
|
||||
d.releasePool << d.tex;
|
||||
d.tex->create();
|
||||
d.initialUpdates->uploadTexture(d.tex, image);
|
||||
|
||||
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
||||
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
|
||||
d.releasePool << d.sampler;
|
||||
d.sampler->create();
|
||||
|
||||
d.srb = m_r->newShaderResourceBindings();
|
||||
d.releasePool << d.srb;
|
||||
d.srb->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf, 68),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
|
||||
});
|
||||
d.srb->create();
|
||||
|
||||
d.ps = m_r->newGraphicsPipeline();
|
||||
d.releasePool << d.ps;
|
||||
|
||||
d.ps->setFlags(QRhiGraphicsPipeline::UsesShadingRate);
|
||||
|
||||
d.ps->setCullMode(QRhiGraphicsPipeline::Back);
|
||||
const QRhiShaderStage stages[] = {
|
||||
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
|
||||
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
|
||||
};
|
||||
d.ps->setShaderStages(stages, stages + 2);
|
||||
QRhiVertexInputLayout inputLayout;
|
||||
inputLayout.setBindings({
|
||||
{ 3 * sizeof(float) },
|
||||
{ 2 * sizeof(float) }
|
||||
});
|
||||
inputLayout.setAttributes({
|
||||
{ 0, 0, QRhiVertexInputAttribute::Float3, 0 },
|
||||
{ 1, 1, QRhiVertexInputAttribute::Float2, 0 }
|
||||
});
|
||||
d.ps->setVertexInputLayout(inputLayout);
|
||||
d.ps->setShaderResourceBindings(d.srb);
|
||||
d.ps->setRenderPassDescriptor(m_rp);
|
||||
d.ps->create();
|
||||
|
||||
// resources for trying out rendering into a texture
|
||||
d.outTexture = m_r->newTexture(QRhiTexture::RGBA8, QSize(1024, 1024), 1, QRhiTexture::RenderTarget);
|
||||
d.releasePool << d.outTexture;
|
||||
d.outTexture->create();
|
||||
|
||||
d.texRt = m_r->newTextureRenderTarget({ d.outTexture });
|
||||
d.releasePool << d.texRt;
|
||||
d.texRtRp = d.texRt->newCompatibleRenderPassDescriptor();
|
||||
d.releasePool << d.texRtRp;
|
||||
d.texRt->setRenderPassDescriptor(d.texRtRp);
|
||||
d.texRt->create();
|
||||
|
||||
QRandomGenerator *rg = QRandomGenerator::global();
|
||||
for (int i = 0; i < CUBE_COUNT; i++) {
|
||||
d.tx.append(rg->bounded(-20, 20) / 10.0f);
|
||||
d.ty.append(rg->bounded(-20, 20) / 10.0f);
|
||||
d.scale.append(rg->bounded(0, 10) / 10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::customRelease()
|
||||
{
|
||||
qDeleteAll(d.releasePool);
|
||||
d.releasePool.clear();
|
||||
|
||||
#if QT_CONFIG(metal)
|
||||
if (d.nativeRateMap)
|
||||
releaseRateMap(d.nativeRateMap);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Window::customBeforeFrame()
|
||||
{
|
||||
// This function is invoked before calling rhi->beginFrame().
|
||||
// Thus it is suitable to do things that involve rebuilding render target related things.
|
||||
|
||||
if (d.applyRateMapPending) {
|
||||
d.applyRateMapPending = false;
|
||||
if (d.applyRateMapWithImage || d.applyRateMapNative) {
|
||||
if (d.textureBased) {
|
||||
QRhiTextureRenderTargetDescription desc = d.texRt->description();
|
||||
desc.setShadingRateMap(d.rateMap);
|
||||
d.texRt->setDescription(desc);
|
||||
if (!d.texRtRpWithRateMap) {
|
||||
d.texRtRpWithRateMap = d.texRt->newCompatibleRenderPassDescriptor();
|
||||
d.releasePool << d.texRtRpWithRateMap;
|
||||
}
|
||||
d.texRt->setRenderPassDescriptor(d.texRtRpWithRateMap);
|
||||
d.texRt->create();
|
||||
d.ps->setRenderPassDescriptor(d.texRtRpWithRateMap);
|
||||
d.ps->create();
|
||||
} else {
|
||||
m_sc->setShadingRateMap(d.rateMap);
|
||||
if (!d.scRpWithRateMap) {
|
||||
d.scRpWithRateMap = m_sc->newCompatibleRenderPassDescriptor();
|
||||
d.releasePool << d.scRpWithRateMap;
|
||||
}
|
||||
m_sc->setRenderPassDescriptor(d.scRpWithRateMap);
|
||||
m_sc->createOrResize();
|
||||
d.ps->setRenderPassDescriptor(d.scRpWithRateMap);
|
||||
d.ps->create();
|
||||
}
|
||||
} else {
|
||||
if (d.textureBased) {
|
||||
QRhiTextureRenderTargetDescription desc = d.texRt->description();
|
||||
desc.setShadingRateMap(nullptr);
|
||||
d.texRt->setDescription(desc);
|
||||
d.texRt->setRenderPassDescriptor(d.texRtRp);
|
||||
d.texRt->create();
|
||||
d.ps->setRenderPassDescriptor(d.texRtRp);
|
||||
d.ps->create();
|
||||
} else {
|
||||
m_sc->setShadingRateMap(nullptr);
|
||||
m_sc->setRenderPassDescriptor(m_rp);
|
||||
m_sc->createOrResize();
|
||||
d.ps->setRenderPassDescriptor(m_rp);
|
||||
d.ps->create();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void renderCube(QRhiCommandBuffer *cb, const QSize &outputSizeInPixels, quint32 ubufAlignedSize)
|
||||
{
|
||||
cb->setGraphicsPipeline(d.ps);
|
||||
cb->setViewport(QRhiViewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height()));
|
||||
if (d.vrsSupported) {
|
||||
int coarsePixelWidth = 1;
|
||||
if (d.cps[0] == 1)
|
||||
coarsePixelWidth = 2;
|
||||
if (d.cps[0] == 2)
|
||||
coarsePixelWidth = 4;
|
||||
int coarsePixelHeight = 1;
|
||||
if (d.cps[1] == 1)
|
||||
coarsePixelHeight = 2;
|
||||
if (d.cps[1] == 2)
|
||||
coarsePixelHeight = 4;
|
||||
const QSize shadingRate(coarsePixelWidth, coarsePixelHeight);
|
||||
cb->setShadingRate(shadingRate);
|
||||
}
|
||||
const QRhiCommandBuffer::VertexInput vbufBindings[] = {
|
||||
{ d.vbuf, 0 },
|
||||
{ d.vbuf, quint32(36 * 3 * sizeof(float)) }
|
||||
};
|
||||
cb->setVertexInput(0, 2, vbufBindings);
|
||||
for (int i = 0; i < CUBE_COUNT; ++i) {
|
||||
QRhiCommandBuffer::DynamicOffset dynOfs(0, i * ubufAlignedSize);
|
||||
cb->setShaderResources(d.srb, 1, &dynOfs);
|
||||
cb->draw(36);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::customRender()
|
||||
{
|
||||
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
|
||||
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
|
||||
if (d.initialUpdates) {
|
||||
u->merge(d.initialUpdates);
|
||||
d.initialUpdates->release();
|
||||
d.initialUpdates = nullptr;
|
||||
}
|
||||
|
||||
float opacity = 1.0f;
|
||||
quint32 uoffset = 0;
|
||||
for (int i = 0; i < CUBE_COUNT; ++i) {
|
||||
QMatrix4x4 mvp = m_proj;
|
||||
mvp.translate(d.tx[i], d.ty[i], 0.0f);
|
||||
mvp.rotate(d.rotation, 0, 1, 0);
|
||||
mvp.scale(d.scale[i], d.scale[i], d.scale[i]);
|
||||
u->updateDynamicBuffer(d.ubuf, uoffset, 64, mvp.constData());
|
||||
u->updateDynamicBuffer(d.ubuf, uoffset + 64, 4, &opacity);
|
||||
uoffset += d.ubufAlignedSize;
|
||||
}
|
||||
cb->resourceUpdate(u);
|
||||
|
||||
if (d.textureBased) {
|
||||
cb->beginPass(d.texRt, Qt::black, { 1.0f, 0 }, nullptr);
|
||||
renderCube(cb, d.texRt->pixelSize(), d.ubufAlignedSize);
|
||||
cb->endPass();
|
||||
}
|
||||
|
||||
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
|
||||
if (!d.textureBased)
|
||||
renderCube(cb, m_sc->currentPixelSize(), d.ubufAlignedSize);
|
||||
|
||||
m_imguiRenderer->render();
|
||||
|
||||
cb->endPass();
|
||||
|
||||
d.rotation += 0.1f;
|
||||
}
|
||||
|
||||
void Window::customGui()
|
||||
{
|
||||
ImGui::ShowDemoWindow(&d.showDemoWindow);
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(620, 500), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin("Variable Rate Shading Test");
|
||||
ImGui::Text("Per-draw VRS supported = %s", d.vrsSupported ? "true" : "false");
|
||||
ImGui::Text("Map-based VRS supported = %s", d.vrsMapSupported ? "true" : "false");
|
||||
ImGui::Text("Map/Image-based VRS supported = %s", d.vrsMapImageSupported ? "true" : "false");
|
||||
const int tileSize = m_r->resourceLimit(QRhi::ShadingRateImageTileSize);
|
||||
ImGui::Text("VRS image tile size: %dx%d", tileSize, tileSize);
|
||||
|
||||
if (ImGui::TreeNodeEx("Supported rates", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Columns(2, "ratecols");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Sample count"); ImGui::NextColumn();
|
||||
ImGui::Text("Rates"); ImGui::NextColumn();
|
||||
ImGui::Separator();
|
||||
for (int sampleCount : { 1, 2, 4, 8, 16 }) {
|
||||
ImGui::Text("%d", sampleCount);
|
||||
ImGui::NextColumn();
|
||||
QString rateStr;
|
||||
for (QSize coarsePixelSize : d.supportedShadingRates[sampleCount])
|
||||
rateStr += QString::asprintf(" %dx%d", coarsePixelSize.width(), coarsePixelSize.height());
|
||||
ImGui::Text("%s", qPrintable(rateStr));
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
ImGui::Columns(1);
|
||||
ImGui::Separator();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::Text("Sample count: %d", sampleCount);
|
||||
|
||||
const bool wasThisFrameTextureBased = d.textureBased;
|
||||
if (ImGui::Checkbox("Render cubes to texture and apply VRS to that", &d.textureBased)) {
|
||||
d.applyRateMapPending = true;
|
||||
// this imgui callback is made before customRender(), ensure the pipeline
|
||||
// and renderpasses are valid; customBeforeFrame() comes only before the next frame.
|
||||
if (d.textureBased) {
|
||||
d.ps->setRenderPassDescriptor(d.texRtRp);
|
||||
d.ps->create();
|
||||
} else {
|
||||
d.ps->setRenderPassDescriptor(m_rp);
|
||||
d.ps->create();
|
||||
}
|
||||
m_imguiRenderer->registerCustomTexture(d.outTexture, d.outTexture, QRhiSampler::Nearest, QRhiImguiRenderer::NoCustomTextureOwnership);
|
||||
}
|
||||
|
||||
if (d.vrsSupported) {
|
||||
ImGui::Text("Coarse pixel size");
|
||||
ImGui::PushID("cps_width");
|
||||
ImGui::Text("Width"); ImGui::SameLine(); ImGui::RadioButton("1", &d.cps[0], 0); ImGui::SameLine(); ImGui::RadioButton("2", &d.cps[0], 1); ImGui::SameLine(); ImGui::RadioButton("4", &d.cps[0], 2);
|
||||
ImGui::PopID();
|
||||
ImGui::PushID("cps_height");
|
||||
ImGui::Text("Height"); ImGui::SameLine(); ImGui::RadioButton("1", &d.cps[1], 0); ImGui::SameLine(); ImGui::RadioButton("2", &d.cps[1], 1); ImGui::SameLine(); ImGui::RadioButton("4", &d.cps[1], 2);
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (d.vrsMapImageSupported) {
|
||||
if (ImGui::Checkbox("Apply R8_UINT texture as shading rate image", &d.applyRateMapWithImage)) {
|
||||
// We are recording a frame already (between beginFrame..endFrame), it is too
|
||||
// late to attempt to change settings that involve recreating the render
|
||||
// targets, because the swapchain is involved here. It can only apply from the
|
||||
// next frame (the one after this one).
|
||||
d.applyRateMapPending = true;
|
||||
if (d.applyRateMapWithImage && tileSize > 0) {
|
||||
const QSize outputSizeInPixels = d.textureBased ? d.texRt->pixelSize() : m_sc->currentPixelSize();
|
||||
if (d.rateMap && d.rateMapTexture->pixelSize() != outputSizeInPixels)
|
||||
d.rateMap = nullptr;
|
||||
if (!d.rateMap) {
|
||||
const QSize rateImageSize(qCeil(outputSizeInPixels.width() / (float)tileSize),
|
||||
qCeil(outputSizeInPixels.height() / (float)tileSize));
|
||||
qDebug() << "Tile size" << tileSize << "Shading rate texture size" << rateImageSize;
|
||||
d.rateMapTexture = m_r->newTexture(QRhiTexture::R8UI, rateImageSize, 1, QRhiTexture::UsedAsShadingRateMap);
|
||||
d.releasePool << d.rateMapTexture;
|
||||
d.rateMapTexture->create();
|
||||
|
||||
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
|
||||
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
|
||||
// 1x1 in a certain area, but use 4x4 outside
|
||||
QImage img(rateImageSize, QImage::Format_Grayscale8);
|
||||
img.fill(0xA); // 4x4
|
||||
QPainter pnt(&img);
|
||||
// pnt.setPen(QColor::fromRgb(0, 0, 0)); // 1x1
|
||||
// pnt.setBrush(QColor::fromRgb(0, 0, 0));
|
||||
// pnt.drawEllipse(20, 20, rateImageSize.width() - 40, rateImageSize.height() - 40);
|
||||
pnt.fillRect(20, 20, rateImageSize.width() - 40, rateImageSize.height() - 40, QColor::fromRgb(0, 0, 0));
|
||||
pnt.end();
|
||||
u->uploadTexture(d.rateMapTexture, img);
|
||||
cb->resourceUpdate(u);
|
||||
|
||||
d.rateMap = m_r->newShadingRateMap();
|
||||
d.releasePool << d.rateMap;
|
||||
d.rateMap->createFrom(d.rateMapTexture);
|
||||
|
||||
d.rateMapTextureForVisualization = m_r->newTexture(QRhiTexture::RGBA8, rateImageSize, 1);
|
||||
d.releasePool << d.rateMapTextureForVisualization;
|
||||
d.rateMapTextureForVisualization->create();
|
||||
QImage rgbaImg = img.convertToFormat(QImage::Format_RGBA8888);
|
||||
u = m_r->nextResourceUpdateBatch();
|
||||
u->uploadTexture(d.rateMapTextureForVisualization, rgbaImg);
|
||||
cb->resourceUpdate(u);
|
||||
m_imguiRenderer->registerCustomTexture(d.rateMapTextureForVisualization, d.rateMapTextureForVisualization, QRhiSampler::Nearest, QRhiImguiRenderer::NoCustomTextureOwnership);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (d.vrsMapSupported) {
|
||||
#if QT_CONFIG(metal)
|
||||
if (ImGui::Checkbox("Apply a MTLRasterizationRateMap (no scaling, incomplete!)", &d.applyRateMapNative)) {
|
||||
d.applyRateMapPending = true;
|
||||
const QSize outputSizeInPixels = d.textureBased ? d.texRt->pixelSize() : m_sc->currentPixelSize();
|
||||
if (d.applyRateMapNative && d.nativeRateMap && d.nativeRateMapSize != outputSizeInPixels) {
|
||||
releaseRateMap(d.nativeRateMap);
|
||||
d.nativeRateMap = nullptr;
|
||||
}
|
||||
if (d.applyRateMapNative && !d.nativeRateMap) {
|
||||
d.nativeRateMap = makeRateMap(m_r, outputSizeInPixels);
|
||||
d.nativeRateMapSize = outputSizeInPixels;
|
||||
d.rateMap = m_r->newShadingRateMap();
|
||||
d.releasePool << d.rateMap;
|
||||
// rateMap will not own nativeRateMap as per cross-platform docs,
|
||||
// but it does actually do a retain/release in the Metal backend.
|
||||
// Regardless, we make sure nativeRateMap lives until the end.
|
||||
d.rateMap->createFrom({ quint64(d.nativeRateMap) });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
if (wasThisFrameTextureBased) {
|
||||
QSize s = d.outTexture->pixelSize();
|
||||
ImGui::SetNextWindowPos(ImVec2(500, 50), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(s.width() / 2, s.height() / 2), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin("Texture", nullptr, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGui::Image(d.outTexture, ImVec2(s.width(), s.height()));
|
||||
ImGui::End();
|
||||
|
||||
if (d.applyRateMapWithImage && !d.applyRateMapPending) {
|
||||
ImGui::SetNextWindowPos(ImVec2(500, 250), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(s.width() / 2, s.height() / 2), ImGuiCond_FirstUseEver);
|
||||
ImGui::Begin("Shading rate image", nullptr, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
s = d.rateMapTextureForVisualization->pixelSize();
|
||||
const int tileSize = m_r->resourceLimit(QRhi::ShadingRateImageTileSize);
|
||||
const float alpha = 0.4f;
|
||||
ImGui::Image(d.rateMapTextureForVisualization, ImVec2(s.width() * tileSize, s.height() * tileSize), ImVec2(0, 0), ImVec2(1, 1), ImVec4(1, 1, 1, alpha));
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
}
|
33
tests/manual/rhi/vrs/vrs_metaltest.mm
Normal file
33
tests/manual/rhi/vrs/vrs_metaltest.mm
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include <rhi/qrhi.h>
|
||||
|
||||
void *makeRateMap(QRhi *rhi, const QSize &outputSizeInPixels)
|
||||
{
|
||||
// note that multiview needs two layers, this example only uses one
|
||||
|
||||
MTLDevice *dev = static_cast<const QRhiMetalNativeHandles *>(rhi->nativeHandles())->dev;
|
||||
MTLRasterizationRateMapDescriptor *descriptor = [[MTLRasterizationRateMapDescriptor alloc] init];
|
||||
descriptor.screenSize = MTLSizeMake(outputSizeInPixels.width(), outputSizeInPixels.height(), 1);
|
||||
MTLSize zoneCounts = MTLSizeMake(8, 8, 1);
|
||||
MTLRasterizationRateLayerDescriptor *layerDescriptor = [[MTLRasterizationRateLayerDescriptor alloc] initWithSampleCount:zoneCounts];
|
||||
for (uint row = 0; row < zoneCounts.height; row++)
|
||||
layerDescriptor.verticalSampleStorage[row] = 1.0;
|
||||
for (uint column = 0; column < zoneCounts.width; column++)
|
||||
layerDescriptor.horizontalSampleStorage[column] = 1.0;
|
||||
layerDescriptor.horizontalSampleStorage[0] = 0.25;
|
||||
layerDescriptor.horizontalSampleStorage[7] = 0.25;
|
||||
layerDescriptor.verticalSampleStorage[0] = 0.25;
|
||||
layerDescriptor.verticalSampleStorage[7] = 0.25;
|
||||
[descriptor setLayer:layerDescriptor atIndex:0];
|
||||
id<MTLRasterizationRateMap> rateMap = [dev newRasterizationRateMapWithDescriptor: descriptor];
|
||||
return rateMap;
|
||||
}
|
||||
|
||||
void releaseRateMap(void *map)
|
||||
{
|
||||
id<MTLRasterizationRateMap> rateMap = (id<MTLRasterizationRateMap>) map;
|
||||
[rateMap release];
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user