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 QRhiRenderBuffer
|
||||||
\li QRhiTexture
|
\li QRhiTexture
|
||||||
\li QRhiSampler
|
\li QRhiSampler
|
||||||
|
\li QRhiShadingRateMap
|
||||||
\li QRhiTextureRenderTarget
|
\li QRhiTextureRenderTarget
|
||||||
\li QRhiShaderResourceBindings
|
\li QRhiShaderResourceBindings
|
||||||
\li QRhiGraphicsPipeline
|
\li QRhiGraphicsPipeline
|
||||||
|
@ -1036,6 +1036,38 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
|||||||
texture becomes necessary, for example when rendering into an
|
texture becomes necessary, for example when rendering into an
|
||||||
OpenXR-provided depth texture (XR_KHR_composition_layer_depth). This enum
|
OpenXR-provided depth texture (XR_KHR_composition_layer_depth). This enum
|
||||||
value has been introduced in Qt 6.8.
|
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
|
\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,
|
OpenGL ES 2.0, and 15 with OpenGL ES 3.0 and some Metal devices. Elsewhere,
|
||||||
a typical value is 32.
|
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()
|
\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
|
\class QRhiTextureSubresourceUploadDescription
|
||||||
\inmodule QtGui
|
\inmodule QtGui
|
||||||
@ -3450,6 +3519,7 @@ QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
|
|||||||
\value SwapChain
|
\value SwapChain
|
||||||
\value ComputePipeline
|
\value ComputePipeline
|
||||||
\value CommandBuffer
|
\value CommandBuffer
|
||||||
|
\value ShadingRateMap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -4324,6 +4394,8 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
|
|||||||
mipmap-based filtering may be unsupported. This is indicated by the
|
mipmap-based filtering may be unsupported. This is indicated by the
|
||||||
QRhi::OneDimensionalTextures and QRhi::OneDimensionalTextureMipmaps
|
QRhi::OneDimensionalTextures and QRhi::OneDimensionalTextureMipmaps
|
||||||
feature flags.
|
feature flags.
|
||||||
|
|
||||||
|
\value UsedAsShadingRateMap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -4403,6 +4475,8 @@ bool QRhiRenderBuffer::createFrom(NativeRenderBuffer src)
|
|||||||
\value ASTC_10x10
|
\value ASTC_10x10
|
||||||
\value ASTC_12x10
|
\value ASTC_12x10
|
||||||
\value ASTC_12x12
|
\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.
|
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
|
\class QRhiRenderPassDescriptor
|
||||||
\inmodule QtGui
|
\inmodule QtGui
|
||||||
@ -4954,6 +5136,34 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
|
|||||||
meant for storing on disk, reusing between processes, or using with multiple
|
meant for storing on disk, reusing between processes, or using with multiple
|
||||||
QRhi instances with potentially different backends.
|
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()
|
\sa isCompatible()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -6507,6 +6717,11 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
|
|||||||
into account. Debug information is relevant in particular with tools like
|
into account. Debug information is relevant in particular with tools like
|
||||||
RenderDoc since it allows seeing the original source code when investigating
|
RenderDoc since it allows seeing the original source code when investigating
|
||||||
the pipeline and when performing vertex or fragment shader debugging.
|
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()
|
\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
|
\struct QRhiSwapChainHdrInfo
|
||||||
\inmodule QtGui
|
\inmodule QtGui
|
||||||
@ -8032,6 +8278,8 @@ static const char *resourceTypeStr(const QRhiResource *res)
|
|||||||
return "ComputePipeline";
|
return "ComputePipeline";
|
||||||
case QRhiResource::CommandBuffer:
|
case QRhiResource::CommandBuffer:
|
||||||
return "CommandBuffer";
|
return "CommandBuffer";
|
||||||
|
case QRhiResource::ShadingRateMap:
|
||||||
|
return "ShadingRateMap";
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_UNREACHABLE_RETURN("");
|
Q_UNREACHABLE_RETURN("");
|
||||||
@ -8260,6 +8508,10 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
|
|||||||
bpc = 8;
|
bpc = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case QRhiTexture::R8UI:
|
||||||
|
bpc = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
break;
|
break;
|
||||||
@ -9692,6 +9944,29 @@ void QRhiCommandBuffer::setStencilRef(quint32 refValue)
|
|||||||
m_rhi->setStencilRef(this, 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.
|
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 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
|
\return a new texture render target with color and depth/stencil
|
||||||
attachments given in \a desc, and with the specified \a flags.
|
attachments given in \a desc, and with the specified \a flags.
|
||||||
@ -10992,6 +11277,18 @@ int QRhi::ubufAlignment() const
|
|||||||
return d->ubufAlignment();
|
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);
|
Q_CONSTINIT static QBasicAtomicInteger<QRhiGlobalObjectIdGenerator::Type> counter = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||||
|
|
||||||
QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId()
|
QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId()
|
||||||
|
@ -39,6 +39,7 @@ class QRhiCommandBuffer;
|
|||||||
class QRhiResourceUpdateBatch;
|
class QRhiResourceUpdateBatch;
|
||||||
class QRhiResourceUpdateBatchPrivate;
|
class QRhiResourceUpdateBatchPrivate;
|
||||||
class QRhiSwapChain;
|
class QRhiSwapChain;
|
||||||
|
class QRhiShadingRateMap;
|
||||||
|
|
||||||
class Q_GUI_EXPORT QRhiDepthStencilClearValue
|
class Q_GUI_EXPORT QRhiDepthStencilClearValue
|
||||||
{
|
{
|
||||||
@ -645,11 +646,15 @@ public:
|
|||||||
QRhiTexture *depthResolveTexture() const { return m_depthResolveTexture; }
|
QRhiTexture *depthResolveTexture() const { return m_depthResolveTexture; }
|
||||||
void setDepthResolveTexture(QRhiTexture *tex) { m_depthResolveTexture = tex; }
|
void setDepthResolveTexture(QRhiTexture *tex) { m_depthResolveTexture = tex; }
|
||||||
|
|
||||||
|
QRhiShadingRateMap *shadingRateMap() const { return m_shadingRateMap; }
|
||||||
|
void setShadingRateMap(QRhiShadingRateMap *map) { m_shadingRateMap = map; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
|
QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
|
||||||
QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
|
QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
|
||||||
QRhiTexture *m_depthTexture = nullptr;
|
QRhiTexture *m_depthTexture = nullptr;
|
||||||
QRhiTexture *m_depthResolveTexture = nullptr;
|
QRhiTexture *m_depthResolveTexture = nullptr;
|
||||||
|
QRhiShadingRateMap *m_shadingRateMap = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
|
class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
|
||||||
@ -815,7 +820,8 @@ public:
|
|||||||
GraphicsPipeline,
|
GraphicsPipeline,
|
||||||
SwapChain,
|
SwapChain,
|
||||||
ComputePipeline,
|
ComputePipeline,
|
||||||
CommandBuffer
|
CommandBuffer,
|
||||||
|
ShadingRateMap
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~QRhiResource();
|
virtual ~QRhiResource();
|
||||||
@ -908,7 +914,8 @@ public:
|
|||||||
ThreeDimensional = 1 << 10,
|
ThreeDimensional = 1 << 10,
|
||||||
TextureRectangleGL = 1 << 11,
|
TextureRectangleGL = 1 << 11,
|
||||||
TextureArray = 1 << 12,
|
TextureArray = 1 << 12,
|
||||||
OneDimensional = 1 << 13
|
OneDimensional = 1 << 13,
|
||||||
|
UsedAsShadingRateMap = 1 << 14
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Flags, Flag)
|
Q_DECLARE_FLAGS(Flags, Flag)
|
||||||
|
|
||||||
@ -930,6 +937,8 @@ public:
|
|||||||
|
|
||||||
RGB10A2,
|
RGB10A2,
|
||||||
|
|
||||||
|
R8UI,
|
||||||
|
|
||||||
D16,
|
D16,
|
||||||
D24,
|
D24,
|
||||||
D24S8,
|
D24S8,
|
||||||
@ -1140,6 +1149,22 @@ protected:
|
|||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags)
|
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
|
class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -1275,7 +1300,8 @@ public:
|
|||||||
UsesBlendConstants = 1 << 0,
|
UsesBlendConstants = 1 << 0,
|
||||||
UsesStencilRef = 1 << 1,
|
UsesStencilRef = 1 << 1,
|
||||||
UsesScissor = 1 << 2,
|
UsesScissor = 1 << 2,
|
||||||
CompileShadersWithDebugInfo = 1 << 3
|
CompileShadersWithDebugInfo = 1 << 3,
|
||||||
|
UsesShadingRate = 1 << 4
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Flags, Flag)
|
Q_DECLARE_FLAGS(Flags, Flag)
|
||||||
|
|
||||||
@ -1595,6 +1621,9 @@ public:
|
|||||||
QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
|
QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
|
||||||
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
|
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; }
|
QSize currentPixelSize() const { return m_currentPixelSize; }
|
||||||
|
|
||||||
virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0;
|
virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0;
|
||||||
@ -1616,6 +1645,7 @@ protected:
|
|||||||
QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
|
QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
|
||||||
QSize m_currentPixelSize;
|
QSize m_currentPixelSize;
|
||||||
QRhiSwapChainProxyData m_proxyData;
|
QRhiSwapChainProxyData m_proxyData;
|
||||||
|
QRhiShadingRateMap *m_shadingRateMap = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
|
||||||
@ -1688,6 +1718,7 @@ public:
|
|||||||
void setScissor(const QRhiScissor &scissor);
|
void setScissor(const QRhiScissor &scissor);
|
||||||
void setBlendConstants(const QColor &c);
|
void setBlendConstants(const QColor &c);
|
||||||
void setStencilRef(quint32 refValue);
|
void setStencilRef(quint32 refValue);
|
||||||
|
void setShadingRate(const QSize &coarsePixelSize);
|
||||||
|
|
||||||
void draw(quint32 vertexCount,
|
void draw(quint32 vertexCount,
|
||||||
quint32 instanceCount = 1,
|
quint32 instanceCount = 1,
|
||||||
@ -1873,7 +1904,10 @@ public:
|
|||||||
ThreeDimensionalTextureMipmaps,
|
ThreeDimensionalTextureMipmaps,
|
||||||
MultiView,
|
MultiView,
|
||||||
TextureViewFormat,
|
TextureViewFormat,
|
||||||
ResolveDepthStencil
|
ResolveDepthStencil,
|
||||||
|
VariableRateShading,
|
||||||
|
VariableRateShadingMap,
|
||||||
|
VariableRateShadingMapWithTexture
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BeginFrameFlag {
|
enum BeginFrameFlag {
|
||||||
@ -1899,7 +1933,8 @@ public:
|
|||||||
TextureArraySizeMax,
|
TextureArraySizeMax,
|
||||||
MaxUniformBufferRange,
|
MaxUniformBufferRange,
|
||||||
MaxVertexInputs,
|
MaxVertexInputs,
|
||||||
MaxVertexOutputs
|
MaxVertexOutputs,
|
||||||
|
ShadingRateImageTileSize
|
||||||
};
|
};
|
||||||
|
|
||||||
~QRhi();
|
~QRhi();
|
||||||
@ -1959,6 +1994,8 @@ public:
|
|||||||
QRhiSampler::AddressMode addressV,
|
QRhiSampler::AddressMode addressV,
|
||||||
QRhiSampler::AddressMode addressW = QRhiSampler::Repeat);
|
QRhiSampler::AddressMode addressW = QRhiSampler::Repeat);
|
||||||
|
|
||||||
|
QRhiShadingRateMap *newShadingRateMap();
|
||||||
|
|
||||||
QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags = {});
|
QRhiTextureRenderTarget::Flags flags = {});
|
||||||
|
|
||||||
@ -2009,6 +2046,8 @@ public:
|
|||||||
|
|
||||||
static QRhiSwapChainProxyData updateSwapChainProxyData(Implementation impl, QWindow *window);
|
static QRhiSwapChainProxyData updateSwapChainProxyData(Implementation impl, QWindow *window);
|
||||||
|
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QRhi();
|
QRhi();
|
||||||
|
|
||||||
|
@ -65,6 +65,8 @@ public:
|
|||||||
virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) = 0;
|
QRhiTextureRenderTarget::Flags flags) = 0;
|
||||||
|
|
||||||
|
virtual QRhiShadingRateMap *createShadingRateMap() = 0;
|
||||||
|
|
||||||
virtual QRhiSwapChain *createSwapChain() = 0;
|
virtual QRhiSwapChain *createSwapChain() = 0;
|
||||||
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
|
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
|
||||||
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
|
||||||
virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
|
virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
|
||||||
virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 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,
|
virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
|
||||||
@ -124,6 +127,7 @@ public:
|
|||||||
|
|
||||||
virtual QList<int> supportedSampleCounts() const = 0;
|
virtual QList<int> supportedSampleCounts() const = 0;
|
||||||
virtual int ubufAlignment() const = 0;
|
virtual int ubufAlignment() const = 0;
|
||||||
|
virtual QList<QSize> supportedShadingRates(int sampleCount) const = 0;
|
||||||
virtual bool isYUpInFramebuffer() const = 0;
|
virtual bool isYUpInFramebuffer() const = 0;
|
||||||
virtual bool isYUpInNDC() const = 0;
|
virtual bool isYUpInNDC() const = 0;
|
||||||
virtual bool isClipDepthZeroToOne() const = 0;
|
virtual bool isClipDepthZeroToOne() const = 0;
|
||||||
@ -658,7 +662,8 @@ public:
|
|||||||
TexDepthOutput,
|
TexDepthOutput,
|
||||||
TexStorageLoad,
|
TexStorageLoad,
|
||||||
TexStorageStore,
|
TexStorageStore,
|
||||||
TexStorageLoadStore
|
TexStorageLoadStore,
|
||||||
|
TexShadingRate
|
||||||
};
|
};
|
||||||
|
|
||||||
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
|
void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
|
||||||
|
@ -478,6 +478,12 @@ QList<int> QRhiD3D11::supportedSampleCounts() const
|
|||||||
return { 1, 2, 4, 8 };
|
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 QRhiD3D11::effectiveSampleDesc(int sampleCount) const
|
||||||
{
|
{
|
||||||
DXGI_SAMPLE_DESC desc;
|
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
|
return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing
|
||||||
case QRhi::ResolveDepthStencil:
|
case QRhi::ResolveDepthStencil:
|
||||||
return false;
|
return false;
|
||||||
|
case QRhi::VariableRateShading:
|
||||||
|
return false;
|
||||||
|
case QRhi::VariableRateShadingMap:
|
||||||
|
case QRhi::VariableRateShadingMapWithTexture:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
@ -680,6 +691,8 @@ int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
|
|||||||
return D3D11_VS_INPUT_REGISTER_COUNT;
|
return D3D11_VS_INPUT_REGISTER_COUNT;
|
||||||
case QRhi::MaxVertexOutputs:
|
case QRhi::MaxVertexOutputs:
|
||||||
return D3D11_VS_OUTPUT_REGISTER_COUNT;
|
return D3D11_VS_OUTPUT_REGISTER_COUNT;
|
||||||
|
case QRhi::ShadingRateImageTileSize:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return 0;
|
return 0;
|
||||||
@ -902,6 +915,11 @@ QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureR
|
|||||||
return new QD3D11TextureRenderTarget(this, desc, flags);
|
return new QD3D11TextureRenderTarget(this, desc, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRhiShadingRateMap *QRhiD3D11::createShadingRateMap()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline()
|
QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline()
|
||||||
{
|
{
|
||||||
return new QD3D11GraphicsPipeline(this);
|
return new QD3D11GraphicsPipeline(this);
|
||||||
@ -1237,6 +1255,12 @@ void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
|||||||
cmd.args.stencilRef.ref = 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,
|
void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
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;
|
return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||||
case QRhiTexture::R8:
|
case QRhiTexture::R8:
|
||||||
return DXGI_FORMAT_R8_UNORM;
|
return DXGI_FORMAT_R8_UNORM;
|
||||||
|
case QRhiTexture::R8UI:
|
||||||
|
return DXGI_FORMAT_R8_UINT;
|
||||||
case QRhiTexture::RG8:
|
case QRhiTexture::RG8:
|
||||||
return DXGI_FORMAT_R8G8_UNORM;
|
return DXGI_FORMAT_R8G8_UNORM;
|
||||||
case QRhiTexture::R16:
|
case QRhiTexture::R16:
|
||||||
|
@ -663,6 +663,8 @@ public:
|
|||||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) override;
|
QRhiTextureRenderTarget::Flags flags) override;
|
||||||
|
|
||||||
|
QRhiShadingRateMap *createShadingRateMap() override;
|
||||||
|
|
||||||
QRhiSwapChain *createSwapChain() override;
|
QRhiSwapChain *createSwapChain() override;
|
||||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||||
|
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||||
|
|
||||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||||
@ -722,6 +725,7 @@ public:
|
|||||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||||
|
|
||||||
QList<int> supportedSampleCounts() const override;
|
QList<int> supportedSampleCounts() const override;
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||||
int ubufAlignment() const override;
|
int ubufAlignment() const override;
|
||||||
bool isYUpInFramebuffer() const override;
|
bool isYUpInFramebuffer() const override;
|
||||||
bool isYUpInNDC() const override;
|
bool isYUpInNDC() const override;
|
||||||
|
@ -498,6 +498,20 @@ bool QRhiD3D12::create(QRhi::Flags flags)
|
|||||||
caps.textureViewFormat = options3.CastingFullyTypedFormatSupported;
|
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;
|
deviceLost = false;
|
||||||
offscreenActive = false;
|
offscreenActive = false;
|
||||||
|
|
||||||
@ -597,6 +611,40 @@ QList<int> QRhiD3D12::supportedSampleCounts() const
|
|||||||
return { 1, 2, 4, 8 };
|
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()
|
QRhiSwapChain *QRhiD3D12::createSwapChain()
|
||||||
{
|
{
|
||||||
return new QD3D12SwapChain(this);
|
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
|
// 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
|
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
|
||||||
return false;
|
return false;
|
||||||
|
case QRhi::VariableRateShading:
|
||||||
|
return caps.vrs;
|
||||||
|
case QRhi::VariableRateShadingMap:
|
||||||
|
case QRhi::VariableRateShadingMapWithTexture:
|
||||||
|
return caps.vrsMap;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -782,6 +835,8 @@ int QRhiD3D12::resourceLimit(QRhi::ResourceLimit limit) const
|
|||||||
return 32;
|
return 32;
|
||||||
case QRhi::MaxVertexOutputs:
|
case QRhi::MaxVertexOutputs:
|
||||||
return 32;
|
return 32;
|
||||||
|
case QRhi::ShadingRateImageTileSize:
|
||||||
|
return shadingRateImageTileSize;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -868,6 +923,11 @@ QRhiTextureRenderTarget *QRhiD3D12::createTextureRenderTarget(const QRhiTextureR
|
|||||||
return new QD3D12TextureRenderTarget(this, desc, flags);
|
return new QD3D12TextureRenderTarget(this, desc, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRhiShadingRateMap *QRhiD3D12::createShadingRateMap()
|
||||||
|
{
|
||||||
|
return new QD3D12ShadingRateMap(this);
|
||||||
|
}
|
||||||
|
|
||||||
QRhiGraphicsPipeline *QRhiD3D12::createGraphicsPipeline()
|
QRhiGraphicsPipeline *QRhiD3D12::createGraphicsPipeline()
|
||||||
{
|
{
|
||||||
return new QD3D12GraphicsPipeline(this);
|
return new QD3D12GraphicsPipeline(this);
|
||||||
@ -1404,6 +1464,44 @@ void QRhiD3D12::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
|||||||
cbD->cmdList->OMSetStencilRef(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,
|
void QRhiD3D12::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||||
{
|
{
|
||||||
@ -1633,7 +1731,7 @@ QRhi::FrameOpResult QRhiD3D12::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
|
|||||||
timestampPairStartIndex * sizeof(quint64));
|
timestampPairStartIndex * sizeof(quint64));
|
||||||
}
|
}
|
||||||
|
|
||||||
ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
|
D3D12GraphicsCommandList *cmdList = cbD->cmdList;
|
||||||
HRESULT hr = cmdList->Close();
|
HRESULT hr = cmdList->Close();
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to close command list: %s",
|
qWarning("Failed to close command list: %s",
|
||||||
@ -1753,7 +1851,7 @@ QRhi::FrameOpResult QRhiD3D12::endOffscreenFrame(QRhi::EndFrameFlags flags)
|
|||||||
timestampPairStartIndex * sizeof(quint64));
|
timestampPairStartIndex * sizeof(quint64));
|
||||||
}
|
}
|
||||||
|
|
||||||
ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
|
D3D12GraphicsCommandList *cmdList = cbD->cmdList;
|
||||||
HRESULT hr = cmdList->Close();
|
HRESULT hr = cmdList->Close();
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to close command list: %s",
|
qWarning("Failed to close command list: %s",
|
||||||
@ -1802,7 +1900,7 @@ QRhi::FrameOpResult QRhiD3D12::finish()
|
|||||||
|
|
||||||
Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
|
Q_ASSERT(cbD->recordingPass == QD3D12CommandBuffer::NoPass);
|
||||||
|
|
||||||
ID3D12GraphicsCommandList1 *cmdList = cbD->cmdList;
|
D3D12GraphicsCommandList *cmdList = cbD->cmdList;
|
||||||
HRESULT hr = cmdList->Close();
|
HRESULT hr = cmdList->Close();
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to close command list: %s",
|
qWarning("Failed to close command list: %s",
|
||||||
@ -1928,7 +2026,31 @@ void QRhiD3D12::beginPass(QRhiCommandBuffer *cb,
|
|||||||
cbD->recordingPass = QD3D12CommandBuffer::RenderPass;
|
cbD->recordingPass = QD3D12CommandBuffer::RenderPass;
|
||||||
cbD->currentTarget = rt;
|
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();
|
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)
|
void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
|
||||||
@ -3140,7 +3262,7 @@ DXGI_SAMPLE_DESC QRhiD3D12::effectiveSampleDesc(int sampleCount, DXGI_FORMAT for
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList)
|
bool QRhiD3D12::startCommandListForCurrentFrameSlot(D3D12GraphicsCommandList **cmdList)
|
||||||
{
|
{
|
||||||
ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot];
|
ID3D12CommandAllocator *cmdAlloc = cmdAllocators[currentFrameSlot];
|
||||||
if (!*cmdList) {
|
if (!*cmdList) {
|
||||||
@ -3148,7 +3270,7 @@ bool QRhiD3D12::startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 *
|
|||||||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||||
cmdAlloc,
|
cmdAlloc,
|
||||||
nullptr,
|
nullptr,
|
||||||
__uuidof(ID3D12GraphicsCommandList1),
|
__uuidof(D3D12GraphicsCommandList),
|
||||||
reinterpret_cast<void **>(cmdList));
|
reinterpret_cast<void **>(cmdList));
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
qWarning("Failed to create command list: %s", qPrintable(QSystemError::windowsComString(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;
|
return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||||
case QRhiTexture::R8:
|
case QRhiTexture::R8:
|
||||||
return DXGI_FORMAT_R8_UNORM;
|
return DXGI_FORMAT_R8_UNORM;
|
||||||
|
case QRhiTexture::R8UI:
|
||||||
|
return DXGI_FORMAT_R8_UINT;
|
||||||
case QRhiTexture::RG8:
|
case QRhiTexture::RG8:
|
||||||
return DXGI_FORMAT_R8G8_UNORM;
|
return DXGI_FORMAT_R8G8_UNORM;
|
||||||
case QRhiTexture::R16:
|
case QRhiTexture::R16:
|
||||||
@ -4595,6 +4719,34 @@ QD3D12Descriptor QD3D12Sampler::lookupOrCreateShaderVisibleDescriptor()
|
|||||||
return shaderVisibleDescriptor;
|
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,
|
QD3D12TextureRenderTarget::QD3D12TextureRenderTarget(QRhiImplementation *rhi,
|
||||||
const QRhiTextureRenderTargetDescription &desc,
|
const QRhiTextureRenderTargetDescription &desc,
|
||||||
Flags flags)
|
Flags flags)
|
||||||
@ -4659,6 +4811,8 @@ QRhiRenderPassDescriptor *QD3D12TextureRenderTarget::newCompatibleRenderPassDesc
|
|||||||
rpD->dsFormat = toD3DDepthTextureDSVFormat(depthTexD->format()); // cannot be a typeless format
|
rpD->dsFormat = toD3DDepthTextureDSVFormat(depthTexD->format()); // cannot be a typeless format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpD->hasShadingRateMap = m_desc.shadingRateMap() != nullptr;
|
||||||
|
|
||||||
rpD->updateSerializedFormat();
|
rpD->updateSerializedFormat();
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiD3D12);
|
QRHI_RES_RHI(QRhiD3D12);
|
||||||
@ -6003,6 +6157,9 @@ bool QD3D12RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasShadingRateMap != o->hasShadingRateMap)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6025,6 +6182,7 @@ QRhiRenderPassDescriptor *QD3D12RenderPassDescriptor::newCompatibleRenderPassDes
|
|||||||
rpD->hasDepthStencil = hasDepthStencil;
|
rpD->hasDepthStencil = hasDepthStencil;
|
||||||
memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
|
memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
|
||||||
rpD->dsFormat = dsFormat;
|
rpD->dsFormat = dsFormat;
|
||||||
|
rpD->hasShadingRateMap = hasShadingRateMap;
|
||||||
|
|
||||||
rpD->updateSerializedFormat();
|
rpD->updateSerializedFormat();
|
||||||
|
|
||||||
@ -6251,6 +6409,9 @@ QRhiRenderPassDescriptor *QD3D12SwapChain::newCompatibleRenderPassDescriptor()
|
|||||||
rpD->hasDepthStencil = m_depthStencil != nullptr;
|
rpD->hasDepthStencil = m_depthStencil != nullptr;
|
||||||
rpD->colorFormat[0] = int(srgbAdjustedColorFormat);
|
rpD->colorFormat[0] = int(srgbAdjustedColorFormat);
|
||||||
rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
|
rpD->dsFormat = QD3D12RenderBuffer::DS_FORMAT;
|
||||||
|
|
||||||
|
rpD->hasShadingRateMap = m_shadingRateMap != nullptr;
|
||||||
|
|
||||||
rpD->updateSerializedFormat();
|
rpD->updateSerializedFormat();
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiD3D12);
|
QRHI_RES_RHI(QRhiD3D12);
|
||||||
|
@ -38,6 +38,15 @@
|
|||||||
#ifdef __ID3D12Device2_INTERFACE_DEFINED__
|
#ifdef __ID3D12Device2_INTERFACE_DEFINED__
|
||||||
#define QRHI_D3D12_AVAILABLE
|
#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
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
static const int QD3D12_FRAMES_IN_FLIGHT = 2;
|
static const int QD3D12_FRAMES_IN_FLIGHT = 2;
|
||||||
@ -754,6 +763,17 @@ struct QD3D12Sampler : public QRhiSampler
|
|||||||
QD3D12Descriptor shaderVisibleDescriptor;
|
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
|
struct QD3D12RenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||||
{
|
{
|
||||||
QD3D12RenderPassDescriptor(QRhiImplementation *rhi);
|
QD3D12RenderPassDescriptor(QRhiImplementation *rhi);
|
||||||
@ -770,6 +790,7 @@ struct QD3D12RenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||||||
bool hasDepthStencil = false;
|
bool hasDepthStencil = false;
|
||||||
int colorFormat[MAX_COLOR_ATTACHMENTS];
|
int colorFormat[MAX_COLOR_ATTACHMENTS];
|
||||||
int dsFormat;
|
int dsFormat;
|
||||||
|
bool hasShadingRateMap = false;
|
||||||
QVector<quint32> serializedFormatData;
|
QVector<quint32> serializedFormatData;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -916,7 +937,7 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer
|
|||||||
|
|
||||||
const QRhiNativeHandles *nativeHandles();
|
const QRhiNativeHandles *nativeHandles();
|
||||||
|
|
||||||
ID3D12GraphicsCommandList1 *cmdList = nullptr; // not owned
|
D3D12GraphicsCommandList *cmdList = nullptr; // not owned
|
||||||
QRhiD3D12CommandBufferNativeHandles nativeHandlesStruct;
|
QRhiD3D12CommandBufferNativeHandles nativeHandlesStruct;
|
||||||
|
|
||||||
enum PassType {
|
enum PassType {
|
||||||
@ -946,6 +967,8 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer
|
|||||||
currentIndexFormat = DXGI_FORMAT_R16_UINT;
|
currentIndexFormat = DXGI_FORMAT_R16_UINT;
|
||||||
currentVertexBuffers = {};
|
currentVertexBuffers = {};
|
||||||
currentVertexOffsets = {};
|
currentVertexOffsets = {};
|
||||||
|
hasShadingRateSet = false;
|
||||||
|
hasShadingRateMapSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// per-frame
|
// per-frame
|
||||||
@ -964,6 +987,8 @@ struct QD3D12CommandBuffer : public QRhiCommandBuffer
|
|||||||
DXGI_FORMAT currentIndexFormat;
|
DXGI_FORMAT currentIndexFormat;
|
||||||
std::array<QD3D12ObjectHandle, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexBuffers;
|
std::array<QD3D12ObjectHandle, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexBuffers;
|
||||||
std::array<quint32, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexOffsets;
|
std::array<quint32, D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> currentVertexOffsets;
|
||||||
|
bool hasShadingRateSet;
|
||||||
|
bool hasShadingRateMapSet;
|
||||||
|
|
||||||
// global
|
// global
|
||||||
double lastGpuTime = 0;
|
double lastGpuTime = 0;
|
||||||
@ -1050,7 +1075,7 @@ struct QD3D12SwapChain : public QRhiSwapChain
|
|||||||
ID3D12Fence *fence = nullptr;
|
ID3D12Fence *fence = nullptr;
|
||||||
HANDLE fenceEvent = nullptr;
|
HANDLE fenceEvent = nullptr;
|
||||||
UINT64 fenceCounter = 0;
|
UINT64 fenceCounter = 0;
|
||||||
ID3D12GraphicsCommandList1 *cmdList = nullptr;
|
D3D12GraphicsCommandList *cmdList = nullptr;
|
||||||
} frameRes[QD3D12_FRAMES_IN_FLIGHT];
|
} frameRes[QD3D12_FRAMES_IN_FLIGHT];
|
||||||
|
|
||||||
int currentFrameSlot = 0; // index in frameRes
|
int currentFrameSlot = 0; // index in frameRes
|
||||||
@ -1104,6 +1129,8 @@ public:
|
|||||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) override;
|
QRhiTextureRenderTarget::Flags flags) override;
|
||||||
|
|
||||||
|
QRhiShadingRateMap *createShadingRateMap() override;
|
||||||
|
|
||||||
QRhiSwapChain *createSwapChain() override;
|
QRhiSwapChain *createSwapChain() override;
|
||||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||||
|
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||||
|
|
||||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||||
@ -1163,6 +1191,7 @@ public:
|
|||||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||||
|
|
||||||
QList<int> supportedSampleCounts() const override;
|
QList<int> supportedSampleCounts() const override;
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||||
int ubufAlignment() const override;
|
int ubufAlignment() const override;
|
||||||
bool isYUpInFramebuffer() const override;
|
bool isYUpInFramebuffer() const override;
|
||||||
bool isYUpInNDC() const override;
|
bool isYUpInNDC() const override;
|
||||||
@ -1184,7 +1213,7 @@ public:
|
|||||||
void waitGpu();
|
void waitGpu();
|
||||||
DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const;
|
DXGI_SAMPLE_DESC effectiveSampleDesc(int sampleCount, DXGI_FORMAT format) const;
|
||||||
bool ensureDirectCompositionDevice();
|
bool ensureDirectCompositionDevice();
|
||||||
bool startCommandListForCurrentFrameSlot(ID3D12GraphicsCommandList1 **cmdList);
|
bool startCommandListForCurrentFrameSlot(D3D12GraphicsCommandList **cmdList);
|
||||||
void enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
|
void enqueueResourceUpdates(QD3D12CommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
|
||||||
void finishActiveReadbacks(bool forced = false);
|
void finishActiveReadbacks(bool forced = false);
|
||||||
bool ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
|
bool ensureShaderVisibleDescriptorHeapCapacity(QD3D12ShaderVisibleDescriptorHeap *h,
|
||||||
@ -1236,10 +1265,14 @@ public:
|
|||||||
QVarLengthArray<QD3D12Readback, 4> activeReadbacks;
|
QVarLengthArray<QD3D12Readback, 4> activeReadbacks;
|
||||||
bool offscreenActive = false;
|
bool offscreenActive = false;
|
||||||
QD3D12CommandBuffer *offscreenCb[QD3D12_FRAMES_IN_FLIGHT] = {};
|
QD3D12CommandBuffer *offscreenCb[QD3D12_FRAMES_IN_FLIGHT] = {};
|
||||||
|
UINT shadingRateImageTileSize = 0;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool multiView = false;
|
bool multiView = false;
|
||||||
bool textureViewFormat = false;
|
bool textureViewFormat = false;
|
||||||
|
bool vrs = false;
|
||||||
|
bool vrsMap = false;
|
||||||
|
bool vrsAdditionalRates = false;
|
||||||
} caps;
|
} caps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -160,6 +160,10 @@ QT_BEGIN_NAMESPACE
|
|||||||
#define GL_R8 0x8229
|
#define GL_R8 0x8229
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef GL_R8UI
|
||||||
|
#define GL_R8UI 0x8232
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef GL_RG8
|
#ifndef GL_RG8
|
||||||
#define GL_RG8 0x822B
|
#define GL_RG8 0x822B
|
||||||
#endif
|
#endif
|
||||||
@ -1188,6 +1192,12 @@ QList<int> QRhiGles2::supportedSampleCounts() const
|
|||||||
return supportedSampleCountList;
|
return supportedSampleCountList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSize> QRhiGles2::supportedShadingRates(int sampleCount) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(sampleCount);
|
||||||
|
return { QSize(1, 1) };
|
||||||
|
}
|
||||||
|
|
||||||
QRhiSwapChain *QRhiGles2::createSwapChain()
|
QRhiSwapChain *QRhiGles2::createSwapChain()
|
||||||
{
|
{
|
||||||
return new QGles2SwapChain(this);
|
return new QGles2SwapChain(this);
|
||||||
@ -1260,6 +1270,12 @@ static inline void toGlTextureFormat(QRhiTexture::Format format, const QRhiGles2
|
|||||||
*glformat = GL_RED;
|
*glformat = GL_RED;
|
||||||
*gltype = GL_UNSIGNED_BYTE;
|
*gltype = GL_UNSIGNED_BYTE;
|
||||||
break;
|
break;
|
||||||
|
case QRhiTexture::R8UI:
|
||||||
|
*glintformat = GL_R8UI;
|
||||||
|
*glsizedintformat = *glintformat;
|
||||||
|
*glformat = GL_RED;
|
||||||
|
*gltype = GL_UNSIGNED_BYTE;
|
||||||
|
break;
|
||||||
case QRhiTexture::RG8:
|
case QRhiTexture::RG8:
|
||||||
*glintformat = GL_RG8;
|
*glintformat = GL_RG8;
|
||||||
*glsizedintformat = *glintformat;
|
*glsizedintformat = *glintformat;
|
||||||
@ -1363,6 +1379,7 @@ bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture
|
|||||||
return caps.bgraExternalFormat;
|
return caps.bgraExternalFormat;
|
||||||
|
|
||||||
case QRhiTexture::R8:
|
case QRhiTexture::R8:
|
||||||
|
case QRhiTexture::R8UI:
|
||||||
return caps.r8Format;
|
return caps.r8Format;
|
||||||
|
|
||||||
case QRhiTexture::RG8:
|
case QRhiTexture::RG8:
|
||||||
@ -1481,6 +1498,11 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return false;
|
return false;
|
||||||
case QRhi::ResolveDepthStencil:
|
case QRhi::ResolveDepthStencil:
|
||||||
return true;
|
return true;
|
||||||
|
case QRhi::VariableRateShading:
|
||||||
|
return false;
|
||||||
|
case QRhi::VariableRateShadingMap:
|
||||||
|
case QRhi::VariableRateShadingMapWithTexture:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE_RETURN(false);
|
Q_UNREACHABLE_RETURN(false);
|
||||||
}
|
}
|
||||||
@ -1519,6 +1541,8 @@ int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
|
|||||||
return caps.maxVertexInputs;
|
return caps.maxVertexInputs;
|
||||||
case QRhi::MaxVertexOutputs:
|
case QRhi::MaxVertexOutputs:
|
||||||
return caps.maxVertexOutputs;
|
return caps.maxVertexOutputs;
|
||||||
|
case QRhi::ShadingRateImageTileSize:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE_RETURN(0);
|
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);
|
return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRhiShadingRateMap *QRhiGles2::createShadingRateMap()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags)
|
QRhiTextureRenderTarget::Flags flags)
|
||||||
{
|
{
|
||||||
@ -1992,6 +2021,12 @@ void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
|||||||
cmd.args.stencilRef.ps = cbD->currentGraphicsPipeline;
|
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,
|
void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||||
{
|
{
|
||||||
|
@ -794,6 +794,8 @@ public:
|
|||||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) override;
|
QRhiTextureRenderTarget::Flags flags) override;
|
||||||
|
|
||||||
|
QRhiShadingRateMap *createShadingRateMap() override;
|
||||||
|
|
||||||
QRhiSwapChain *createSwapChain() override;
|
QRhiSwapChain *createSwapChain() override;
|
||||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||||
|
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||||
|
|
||||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||||
@ -853,6 +856,7 @@ public:
|
|||||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||||
|
|
||||||
QList<int> supportedSampleCounts() const override;
|
QList<int> supportedSampleCounts() const override;
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||||
int ubufAlignment() const override;
|
int ubufAlignment() const override;
|
||||||
bool isYUpInFramebuffer() const override;
|
bool isYUpInFramebuffer() const override;
|
||||||
bool isYUpInNDC() const override;
|
bool isYUpInNDC() const override;
|
||||||
|
@ -176,7 +176,8 @@ struct QRhiMetalData
|
|||||||
MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
|
MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
|
||||||
const QColor &colorClearValue,
|
const QColor &colorClearValue,
|
||||||
const QRhiDepthStencilClearValue &depthStencilClearValue,
|
const QRhiDepthStencilClearValue &depthStencilClearValue,
|
||||||
int colorAttCount);
|
int colorAttCount,
|
||||||
|
QRhiShadingRateMap *shadingRateMap);
|
||||||
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
|
id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
|
||||||
QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
|
QString *error, QByteArray *entryPoint, QShaderKey *activeKey);
|
||||||
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
|
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
|
||||||
@ -194,7 +195,8 @@ struct QRhiMetalData
|
|||||||
Sampler,
|
Sampler,
|
||||||
StagingBuffer,
|
StagingBuffer,
|
||||||
GraphicsPipeline,
|
GraphicsPipeline,
|
||||||
ComputePipeline
|
ComputePipeline,
|
||||||
|
ShadingRateMap
|
||||||
};
|
};
|
||||||
Type type;
|
Type type;
|
||||||
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
|
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
|
||||||
@ -225,6 +227,9 @@ struct QRhiMetalData
|
|||||||
struct {
|
struct {
|
||||||
id<MTLComputePipelineState> pipelineState;
|
id<MTLComputePipelineState> pipelineState;
|
||||||
} computePipeline;
|
} computePipeline;
|
||||||
|
struct {
|
||||||
|
id<MTLRasterizationRateMap> rateMap;
|
||||||
|
} shadingRateMap;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
QVector<DeferredReleaseEntry> releaseQueue;
|
QVector<DeferredReleaseEntry> releaseQueue;
|
||||||
@ -306,6 +311,11 @@ struct QMetalSamplerData
|
|||||||
id<MTLSamplerState> samplerState = nil;
|
id<MTLSamplerState> samplerState = nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct QMetalShadingRateMapData
|
||||||
|
{
|
||||||
|
id<MTLRasterizationRateMap> rateMap = nil;
|
||||||
|
};
|
||||||
|
|
||||||
struct QMetalShaderResourceBindingsData {
|
struct QMetalShaderResourceBindingsData {
|
||||||
struct Stage {
|
struct Stage {
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
@ -634,6 +644,10 @@ bool QRhiMetal::create(QRhi::Flags flags)
|
|||||||
caps.supportedSampleCounts.append(sampleCount);
|
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))
|
if (rhiFlags.testFlag(QRhi::EnablePipelineCacheDataSave))
|
||||||
d->setupBinaryArchive();
|
d->setupBinaryArchive();
|
||||||
|
|
||||||
@ -672,6 +686,12 @@ QVector<int> QRhiMetal::supportedSampleCounts() const
|
|||||||
return caps.supportedSampleCounts;
|
return caps.supportedSampleCounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QSize> QRhiMetal::supportedShadingRates(int sampleCount) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(sampleCount);
|
||||||
|
return { QSize(1, 1) };
|
||||||
|
}
|
||||||
|
|
||||||
QRhiSwapChain *QRhiMetal::createSwapChain()
|
QRhiSwapChain *QRhiMetal::createSwapChain()
|
||||||
{
|
{
|
||||||
return new QMetalSwapChain(this);
|
return new QMetalSwapChain(this);
|
||||||
@ -838,6 +858,12 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return false;
|
return false;
|
||||||
case QRhi::ResolveDepthStencil:
|
case QRhi::ResolveDepthStencil:
|
||||||
return true;
|
return true;
|
||||||
|
case QRhi::VariableRateShading:
|
||||||
|
return false;
|
||||||
|
case QRhi::VariableRateShadingMap:
|
||||||
|
return caps.shadingRateMap;
|
||||||
|
case QRhi::VariableRateShadingMapWithTexture:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
@ -875,6 +901,8 @@ int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
|
|||||||
return 31;
|
return 31;
|
||||||
case QRhi::MaxVertexOutputs:
|
case QRhi::MaxVertexOutputs:
|
||||||
return 15; // use the minimum from MTLGPUFamily1/2/3
|
return 15; // use the minimum from MTLGPUFamily1/2/3
|
||||||
|
case QRhi::ShadingRateImageTileSize:
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return 0;
|
return 0;
|
||||||
@ -1063,6 +1091,11 @@ QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler
|
|||||||
return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
|
return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRhiShadingRateMap *QRhiMetal::createShadingRateMap()
|
||||||
|
{
|
||||||
|
return new QMetalShadingRateMap(this);
|
||||||
|
}
|
||||||
|
|
||||||
QRhiTextureRenderTarget *QRhiMetal::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *QRhiMetal::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags)
|
QRhiTextureRenderTarget::Flags flags)
|
||||||
{
|
{
|
||||||
@ -1886,6 +1919,12 @@ void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
|||||||
[cbD->d->currentRenderPassEncoder setStencilReferenceValue: 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)
|
static id<MTLComputeCommandEncoder> tessellationComputeEncoder(QMetalCommandBuffer *cbD)
|
||||||
{
|
{
|
||||||
if (cbD->d->currentRenderPassEncoder) {
|
if (cbD->d->currentRenderPassEncoder) {
|
||||||
@ -2557,7 +2596,8 @@ QRhi::FrameOpResult QRhiMetal::finish()
|
|||||||
MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
|
MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
|
||||||
const QColor &colorClearValue,
|
const QColor &colorClearValue,
|
||||||
const QRhiDepthStencilClearValue &depthStencilClearValue,
|
const QRhiDepthStencilClearValue &depthStencilClearValue,
|
||||||
int colorAttCount)
|
int colorAttCount,
|
||||||
|
QRhiShadingRateMap *shadingRateMap)
|
||||||
{
|
{
|
||||||
MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
|
MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||||
MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
|
MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
|
||||||
@ -2578,6 +2618,9 @@ MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthSte
|
|||||||
rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
|
rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shadingRateMap)
|
||||||
|
rp.rasterizationRateMap = QRHI_RES(QMetalShadingRateMap, shadingRateMap)->d->rateMap;
|
||||||
|
|
||||||
return rp;
|
return rp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2958,8 +3001,15 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
|||||||
QMetalRenderTargetData *rtD = nullptr;
|
QMetalRenderTargetData *rtD = nullptr;
|
||||||
switch (rt->resourceType()) {
|
switch (rt->resourceType()) {
|
||||||
case QRhiResource::SwapChainRenderTarget:
|
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) {
|
if (rtD->colorAttCount) {
|
||||||
QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
|
QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
|
||||||
if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
|
if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
|
||||||
@ -2983,6 +3033,9 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (shadingRateMap)
|
||||||
|
QRHI_RES(QMetalShadingRateMap, shadingRateMap)->lastActiveFrameSlot = currentFrameSlot;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QRhiResource::TextureRenderTarget:
|
case QRhiResource::TextureRenderTarget:
|
||||||
{
|
{
|
||||||
@ -2990,7 +3043,11 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
|||||||
rtD = rtTex->d;
|
rtD = rtTex->d;
|
||||||
if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
|
if (!QRhiRenderTargetAttachmentTracker::isUpToDate<QMetalTexture, QMetalRenderBuffer>(rtTex->description(), rtD->currentResIdList))
|
||||||
rtTex->create();
|
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) {
|
if (rtD->fb.preserveColor) {
|
||||||
for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
|
for (uint i = 0; i < uint(rtD->colorAttCount); ++i)
|
||||||
cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
|
cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
|
||||||
@ -3024,6 +3081,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
|
|||||||
}
|
}
|
||||||
if (rtTex->m_desc.depthResolveTexture())
|
if (rtTex->m_desc.depthResolveTexture())
|
||||||
QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
|
QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
|
||||||
|
if (rtTex->m_desc.shadingRateMap())
|
||||||
|
QRHI_RES(QMetalShadingRateMap, rtTex->m_desc.shadingRateMap())->lastActiveFrameSlot = currentFrameSlot;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -3195,6 +3254,9 @@ void QRhiMetal::executeDeferredReleases(bool forced)
|
|||||||
case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
|
case QRhiMetalData::DeferredReleaseEntry::ComputePipeline:
|
||||||
[e.computePipeline.pipelineState release];
|
[e.computePipeline.pipelineState release];
|
||||||
break;
|
break;
|
||||||
|
case QRhiMetalData::DeferredReleaseEntry::ShadingRateMap:
|
||||||
|
[e.shadingRateMap.rateMap release];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -3393,6 +3455,8 @@ static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QR
|
|||||||
#else
|
#else
|
||||||
return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
|
return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
|
||||||
#endif
|
#endif
|
||||||
|
case QRhiTexture::R8UI:
|
||||||
|
return MTLPixelFormatR8Uint;
|
||||||
case QRhiTexture::RG8:
|
case QRhiTexture::RG8:
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
return MTLPixelFormatRG8Unorm;
|
return MTLPixelFormatRG8Unorm;
|
||||||
@ -4034,6 +4098,55 @@ bool QMetalSampler::create()
|
|||||||
return true;
|
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.
|
// 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.
|
// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass.
|
||||||
QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi)
|
QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi)
|
||||||
@ -4077,6 +4190,9 @@ bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasShadingRateMap != o->hasShadingRateMap)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4088,8 +4204,9 @@ void QMetalRenderPassDescriptor::updateSerializedFormat()
|
|||||||
*p++ = colorAttachmentCount;
|
*p++ = colorAttachmentCount;
|
||||||
*p++ = hasDepthStencil;
|
*p++ = hasDepthStencil;
|
||||||
for (int i = 0; i < colorAttachmentCount; ++i)
|
for (int i = 0; i < colorAttachmentCount; ++i)
|
||||||
*p++ = colorFormat[i];
|
*p++ = colorFormat[i];
|
||||||
*p++ = hasDepthStencil ? dsFormat : 0;
|
*p++ = hasDepthStencil ? dsFormat : 0;
|
||||||
|
*p++ = hasShadingRateMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
|
QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
|
||||||
@ -4099,6 +4216,7 @@ QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDes
|
|||||||
rpD->hasDepthStencil = hasDepthStencil;
|
rpD->hasDepthStencil = hasDepthStencil;
|
||||||
memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
|
memcpy(rpD->colorFormat, colorFormat, sizeof(colorFormat));
|
||||||
rpD->dsFormat = dsFormat;
|
rpD->dsFormat = dsFormat;
|
||||||
|
rpD->hasShadingRateMap = hasShadingRateMap;
|
||||||
|
|
||||||
rpD->updateSerializedFormat();
|
rpD->updateSerializedFormat();
|
||||||
|
|
||||||
@ -4184,6 +4302,8 @@ QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDesc
|
|||||||
else if (m_desc.depthStencilBuffer())
|
else if (m_desc.depthStencilBuffer())
|
||||||
rpD->dsFormat = int(QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
|
rpD->dsFormat = int(QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
|
||||||
|
|
||||||
|
rpD->hasShadingRateMap = m_desc.shadingRateMap() != nullptr;
|
||||||
|
|
||||||
rpD->updateSerializedFormat();
|
rpD->updateSerializedFormat();
|
||||||
|
|
||||||
QRHI_RES_RHI(QRhiMetal);
|
QRHI_RES_RHI(QRhiMetal);
|
||||||
@ -6223,6 +6343,8 @@ QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
|
|||||||
rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
|
rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
rpD->hasShadingRateMap = m_shadingRateMap != nullptr;
|
||||||
|
|
||||||
rpD->updateSerializedFormat();
|
rpD->updateSerializedFormat();
|
||||||
|
|
||||||
rhiD->registerResource(rpD, false);
|
rhiD->registerResource(rpD, false);
|
||||||
|
@ -105,6 +105,21 @@ struct QMetalSampler : public QRhiSampler
|
|||||||
friend struct QMetalShaderResourceBindings;
|
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
|
struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||||
{
|
{
|
||||||
QMetalRenderPassDescriptor(QRhiImplementation *rhi);
|
QMetalRenderPassDescriptor(QRhiImplementation *rhi);
|
||||||
@ -124,6 +139,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||||||
bool hasDepthStencil = false;
|
bool hasDepthStencil = false;
|
||||||
int colorFormat[MAX_COLOR_ATTACHMENTS];
|
int colorFormat[MAX_COLOR_ATTACHMENTS];
|
||||||
int dsFormat;
|
int dsFormat;
|
||||||
|
bool hasShadingRateMap = false;
|
||||||
QVector<quint32> serializedFormatData;
|
QVector<quint32> serializedFormatData;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -361,6 +377,8 @@ public:
|
|||||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) override;
|
QRhiTextureRenderTarget::Flags flags) override;
|
||||||
|
|
||||||
|
QRhiShadingRateMap *createShadingRateMap() override;
|
||||||
|
|
||||||
QRhiSwapChain *createSwapChain() override;
|
QRhiSwapChain *createSwapChain() override;
|
||||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||||
|
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||||
|
|
||||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||||
@ -420,6 +439,7 @@ public:
|
|||||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||||
|
|
||||||
QList<int> supportedSampleCounts() const override;
|
QList<int> supportedSampleCounts() const override;
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||||
int ubufAlignment() const override;
|
int ubufAlignment() const override;
|
||||||
bool isYUpInFramebuffer() const override;
|
bool isYUpInFramebuffer() const override;
|
||||||
bool isYUpInNDC() const override;
|
bool isYUpInNDC() const override;
|
||||||
@ -500,6 +520,7 @@ public:
|
|||||||
bool isAppleGPU = false;
|
bool isAppleGPU = false;
|
||||||
int maxThreadGroupSize = 512;
|
int maxThreadGroupSize = 512;
|
||||||
bool multiView = false;
|
bool multiView = false;
|
||||||
|
bool shadingRateMap = false;
|
||||||
} caps;
|
} caps;
|
||||||
|
|
||||||
QRhiMetalData *d = nullptr;
|
QRhiMetalData *d = nullptr;
|
||||||
|
@ -60,6 +60,12 @@ QList<int> QRhiNull::supportedSampleCounts() const
|
|||||||
return { 1 };
|
return { 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSize> QRhiNull::supportedShadingRates(int sampleCount) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(sampleCount);
|
||||||
|
return { QSize(1, 1) };
|
||||||
|
}
|
||||||
|
|
||||||
QRhiSwapChain *QRhiNull::createSwapChain()
|
QRhiSwapChain *QRhiNull::createSwapChain()
|
||||||
{
|
{
|
||||||
return new QNullSwapChain(this);
|
return new QNullSwapChain(this);
|
||||||
@ -139,6 +145,8 @@ int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
|
|||||||
return 32;
|
return 32;
|
||||||
case QRhi::MaxVertexOutputs:
|
case QRhi::MaxVertexOutputs:
|
||||||
return 32;
|
return 32;
|
||||||
|
case QRhi::ShadingRateImageTileSize:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_UNREACHABLE_RETURN(0);
|
Q_UNREACHABLE_RETURN(0);
|
||||||
@ -214,6 +222,11 @@ QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRe
|
|||||||
return new QNullTextureRenderTarget(this, desc, flags);
|
return new QNullTextureRenderTarget(this, desc, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRhiShadingRateMap *QRhiNull::createShadingRateMap()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
|
QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
|
||||||
{
|
{
|
||||||
return new QNullGraphicsPipeline(this);
|
return new QNullGraphicsPipeline(this);
|
||||||
@ -282,6 +295,12 @@ void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
|
|||||||
Q_UNUSED(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,
|
void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
|
||||||
{
|
{
|
||||||
|
@ -204,6 +204,8 @@ public:
|
|||||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) override;
|
QRhiTextureRenderTarget::Flags flags) override;
|
||||||
|
|
||||||
|
QRhiShadingRateMap *createShadingRateMap() override;
|
||||||
|
|
||||||
QRhiSwapChain *createSwapChain() override;
|
QRhiSwapChain *createSwapChain() override;
|
||||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||||
|
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||||
|
|
||||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||||
@ -263,6 +266,7 @@ public:
|
|||||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||||
|
|
||||||
QList<int> supportedSampleCounts() const override;
|
QList<int> supportedSampleCounts() const override;
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||||
int ubufAlignment() const override;
|
int ubufAlignment() const override;
|
||||||
bool isYUpInFramebuffer() const override;
|
bool isYUpInFramebuffer() const override;
|
||||||
bool isYUpInNDC() 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;
|
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
|
struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
|
||||||
{
|
{
|
||||||
QVkRenderPassDescriptor(QRhiImplementation *rhi);
|
QVkRenderPassDescriptor(QRhiImplementation *rhi);
|
||||||
@ -165,9 +176,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||||||
QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
|
QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
|
||||||
bool hasDepthStencil = false;
|
bool hasDepthStencil = false;
|
||||||
bool hasDepthStencilResolve = false;
|
bool hasDepthStencilResolve = false;
|
||||||
|
bool hasShadingRateMap = false;
|
||||||
uint32_t multiViewCount = 0;
|
uint32_t multiViewCount = 0;
|
||||||
VkAttachmentReference dsRef;
|
VkAttachmentReference dsRef;
|
||||||
VkAttachmentReference dsResolveRef;
|
VkAttachmentReference dsResolveRef;
|
||||||
|
VkAttachmentReference shadingRateRef;
|
||||||
QVector<quint32> serializedFormatData;
|
QVector<quint32> serializedFormatData;
|
||||||
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
|
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
|
||||||
int lastActiveFrameSlot = -1;
|
int lastActiveFrameSlot = -1;
|
||||||
@ -184,6 +197,7 @@ struct QVkRenderTargetData
|
|||||||
int dsAttCount = 0;
|
int dsAttCount = 0;
|
||||||
int resolveAttCount = 0;
|
int resolveAttCount = 0;
|
||||||
int dsResolveAttCount = 0;
|
int dsResolveAttCount = 0;
|
||||||
|
int shadingRateAttCount = 0;
|
||||||
int multiViewCount = 0;
|
int multiViewCount = 0;
|
||||||
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
|
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
|
||||||
static const int MAX_COLOR_ATTACHMENTS = 8;
|
static const int MAX_COLOR_ATTACHMENTS = 8;
|
||||||
@ -220,6 +234,7 @@ struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
|
|||||||
VkImageView dsv = VK_NULL_HANDLE;
|
VkImageView dsv = VK_NULL_HANDLE;
|
||||||
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
|
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
|
||||||
VkImageView resdsv = VK_NULL_HANDLE;
|
VkImageView resdsv = VK_NULL_HANDLE;
|
||||||
|
VkImageView shadingRateMapView = VK_NULL_HANDLE;
|
||||||
int lastActiveFrameSlot = -1;
|
int lastActiveFrameSlot = -1;
|
||||||
friend class QRhiVulkan;
|
friend class QRhiVulkan;
|
||||||
};
|
};
|
||||||
@ -332,10 +347,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
|||||||
currentTarget = nullptr;
|
currentTarget = nullptr;
|
||||||
activeSecondaryCbStack.clear();
|
activeSecondaryCbStack.clear();
|
||||||
resetCommands();
|
resetCommands();
|
||||||
resetCachedState();
|
resetPerPassState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetCachedState() {
|
void resetPerPassState() {
|
||||||
currentGraphicsPipeline = nullptr;
|
currentGraphicsPipeline = nullptr;
|
||||||
currentComputePipeline = nullptr;
|
currentComputePipeline = nullptr;
|
||||||
currentPipelineGeneration = 0;
|
currentPipelineGeneration = 0;
|
||||||
@ -349,6 +364,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
|||||||
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
|
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
|
||||||
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
|
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
|
||||||
inExternal = false;
|
inExternal = false;
|
||||||
|
hasShadingRateSet = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PassType recordingPass;
|
PassType recordingPass;
|
||||||
@ -370,6 +386,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
|||||||
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
|
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
|
||||||
QVarLengthArray<VkCommandBuffer, 4> activeSecondaryCbStack;
|
QVarLengthArray<VkCommandBuffer, 4> activeSecondaryCbStack;
|
||||||
bool inExternal;
|
bool inExternal;
|
||||||
|
bool hasShadingRateSet;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
|
QHash<QRhiResource *, QPair<VkAccessFlags, bool> > writtenResources;
|
||||||
@ -404,7 +421,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
|||||||
DebugMarkerInsert,
|
DebugMarkerInsert,
|
||||||
TransitionPassResources,
|
TransitionPassResources,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
ExecuteSecondary
|
ExecuteSecondary,
|
||||||
|
SetShadingRate
|
||||||
};
|
};
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
|
|
||||||
@ -531,6 +549,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
|
|||||||
struct {
|
struct {
|
||||||
VkCommandBuffer cb;
|
VkCommandBuffer cb;
|
||||||
} executeSecondary;
|
} executeSecondary;
|
||||||
|
struct {
|
||||||
|
uint32_t w;
|
||||||
|
uint32_t h;
|
||||||
|
} setShadingRate;
|
||||||
} args;
|
} args;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -608,6 +630,7 @@ struct QVkSwapChain : public QRhiSwapChain
|
|||||||
QVkSwapChainRenderTarget rtWrapper;
|
QVkSwapChainRenderTarget rtWrapper;
|
||||||
QVkSwapChainRenderTarget rtWrapperRight;
|
QVkSwapChainRenderTarget rtWrapperRight;
|
||||||
QVkCommandBuffer cbWrapper;
|
QVkCommandBuffer cbWrapper;
|
||||||
|
VkImageView shadingRateMapView = VK_NULL_HANDLE;
|
||||||
|
|
||||||
struct ImageResources {
|
struct ImageResources {
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
@ -679,6 +702,8 @@ public:
|
|||||||
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
|
||||||
QRhiTextureRenderTarget::Flags flags) override;
|
QRhiTextureRenderTarget::Flags flags) override;
|
||||||
|
|
||||||
|
QRhiShadingRateMap *createShadingRateMap() override;
|
||||||
|
|
||||||
QRhiSwapChain *createSwapChain() override;
|
QRhiSwapChain *createSwapChain() override;
|
||||||
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
|
||||||
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags 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 setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
|
||||||
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
|
||||||
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
|
||||||
|
void setShadingRate(QRhiCommandBuffer *cb, const QSize &coarsePixelSize) override;
|
||||||
|
|
||||||
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
|
||||||
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
|
||||||
@ -738,6 +764,7 @@ public:
|
|||||||
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
double lastCompletedGpuTime(QRhiCommandBuffer *cb) override;
|
||||||
|
|
||||||
QList<int> supportedSampleCounts() const override;
|
QList<int> supportedSampleCounts() const override;
|
||||||
|
QList<QSize> supportedShadingRates(int sampleCount) const override;
|
||||||
int ubufAlignment() const override;
|
int ubufAlignment() const override;
|
||||||
bool isYUpInFramebuffer() const override;
|
bool isYUpInFramebuffer() const override;
|
||||||
bool isYUpInNDC() const override;
|
bool isYUpInNDC() const override;
|
||||||
@ -771,7 +798,8 @@ public:
|
|||||||
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD,
|
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD,
|
||||||
bool hasDepthStencil,
|
bool hasDepthStencil,
|
||||||
VkSampleCountFlagBits samples,
|
VkSampleCountFlagBits samples,
|
||||||
VkFormat colorFormat);
|
VkFormat colorFormat,
|
||||||
|
QRhiShadingRateMap *shadingRateMap);
|
||||||
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
|
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
|
||||||
const QRhiColorAttachment *colorAttachmentsBegin,
|
const QRhiColorAttachment *colorAttachmentsBegin,
|
||||||
const QRhiColorAttachment *colorAttachmentsEnd,
|
const QRhiColorAttachment *colorAttachmentsEnd,
|
||||||
@ -780,7 +808,8 @@ public:
|
|||||||
bool storeDs,
|
bool storeDs,
|
||||||
QRhiRenderBuffer *depthStencilBuffer,
|
QRhiRenderBuffer *depthStencilBuffer,
|
||||||
QRhiTexture *depthTexture,
|
QRhiTexture *depthTexture,
|
||||||
QRhiTexture *depthResolveTexture);
|
QRhiTexture *depthResolveTexture,
|
||||||
|
QRhiShadingRateMap *shadingRateMap);
|
||||||
bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0);
|
bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0);
|
||||||
VkShaderModule createShader(const QByteArray &spirv);
|
VkShaderModule createShader(const QByteArray &spirv);
|
||||||
|
|
||||||
@ -897,7 +926,10 @@ public:
|
|||||||
bool multiView = false;
|
bool multiView = false;
|
||||||
bool renderPass2KHR = false;
|
bool renderPass2KHR = false;
|
||||||
bool depthStencilResolveKHR = false;
|
bool depthStencilResolveKHR = false;
|
||||||
|
bool perDrawShadingRate = false;
|
||||||
|
bool imageBasedShadingRate = false;
|
||||||
QVersionNumber apiVersion;
|
QVersionNumber apiVersion;
|
||||||
|
int imageBasedShadingRateTileSize = 0;
|
||||||
} caps;
|
} caps;
|
||||||
|
|
||||||
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
|
||||||
@ -1013,6 +1045,7 @@ public:
|
|||||||
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
|
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
|
||||||
VkImageView dsv;
|
VkImageView dsv;
|
||||||
VkImageView resdsv;
|
VkImageView resdsv;
|
||||||
|
VkImageView shadingRateMapView;
|
||||||
} textureRenderTarget;
|
} textureRenderTarget;
|
||||||
struct {
|
struct {
|
||||||
VkRenderPass rp;
|
VkRenderPass rp;
|
||||||
@ -1027,6 +1060,11 @@ public:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
QList<DeferredReleaseEntry> releaseQueue;
|
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);
|
Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_RELOCATABLE_TYPE);
|
||||||
|
@ -34,6 +34,7 @@ add_subdirectory(displacement)
|
|||||||
add_subdirectory(imguirenderer)
|
add_subdirectory(imguirenderer)
|
||||||
add_subdirectory(multiview)
|
add_subdirectory(multiview)
|
||||||
add_subdirectory(msaatextureresolve)
|
add_subdirectory(msaatextureresolve)
|
||||||
|
add_subdirectory(vrs)
|
||||||
if(QT_FEATURE_widgets)
|
if(QT_FEATURE_widgets)
|
||||||
add_subdirectory(rhiwidgetproto)
|
add_subdirectory(rhiwidgetproto)
|
||||||
endif()
|
endif()
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
#include <rhi/qrhi.h>
|
#include <rhi/qrhi.h>
|
||||||
|
|
||||||
#ifdef EXAMPLEFW_IMGUI
|
#ifdef EXAMPLEFW_IMGUI
|
||||||
#include "qrhiimgui_p.h"
|
#include "qrhiimgui.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -98,6 +98,9 @@ protected:
|
|||||||
|
|
||||||
void customInit();
|
void customInit();
|
||||||
void customRelease();
|
void customRelease();
|
||||||
|
#ifdef EXAMPLEFW_BEFORE_FRAME
|
||||||
|
void customBeforeFrame();
|
||||||
|
#endif
|
||||||
void customRender();
|
void customRender();
|
||||||
#ifdef EXAMPLEFW_IMGUI
|
#ifdef EXAMPLEFW_IMGUI
|
||||||
void customGui();
|
void customGui();
|
||||||
@ -360,6 +363,10 @@ void Window::render()
|
|||||||
if (!m_hasSwapChain || m_notExposed)
|
if (!m_hasSwapChain || m_notExposed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#ifdef EXAMPLEFW_BEFORE_FRAME
|
||||||
|
customBeforeFrame();
|
||||||
|
#endif
|
||||||
|
|
||||||
// If the window got resized or got newly exposed, resize the swapchain.
|
// If the window got resized or got newly exposed, resize the swapchain.
|
||||||
// (the newly-exposed case is not actually required by some
|
// (the newly-exposed case is not actually required by some
|
||||||
// platforms/backends, but f.ex. Vulkan on Windows seems to need it)
|
// 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_widgets.cpp
|
||||||
${imgui_base}/imgui/imgui_demo.cpp
|
${imgui_base}/imgui/imgui_demo.cpp
|
||||||
${imgui_base}/qrhiimgui.cpp
|
${imgui_base}/qrhiimgui.cpp
|
||||||
${imgui_base}/qrhiimgui_p.h
|
${imgui_base}/qrhiimgui.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${imgui_target} PRIVATE
|
target_sources(${imgui_target} PRIVATE
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
// 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 <QtCore/qfile.h>
|
||||||
#include <QtGui/qguiapplication.h>
|
#include <QtGui/qguiapplication.h>
|
||||||
#include <QtGui/qevent.h>
|
#include <QtGui/qevent.h>
|
||||||
@ -34,7 +34,8 @@ QRhiImguiRenderer::~QRhiImguiRenderer()
|
|||||||
void QRhiImguiRenderer::releaseResources()
|
void QRhiImguiRenderer::releaseResources()
|
||||||
{
|
{
|
||||||
for (Texture &t : m_textures) {
|
for (Texture &t : m_textures) {
|
||||||
delete t.tex;
|
if (t.ownTex)
|
||||||
|
delete t.tex;
|
||||||
delete t.srb;
|
delete t.srb;
|
||||||
}
|
}
|
||||||
m_textures.clear();
|
m_textures.clear();
|
||||||
@ -43,7 +44,8 @@ void QRhiImguiRenderer::releaseResources()
|
|||||||
m_ibuf.reset();
|
m_ibuf.reset();
|
||||||
m_ubuf.reset();
|
m_ubuf.reset();
|
||||||
m_ps.reset();
|
m_ps.reset();
|
||||||
m_sampler.reset();
|
m_linearSampler.reset();
|
||||||
|
m_nearestSampler.reset();
|
||||||
|
|
||||||
m_rhi = nullptr;
|
m_rhi = nullptr;
|
||||||
}
|
}
|
||||||
@ -100,41 +102,53 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_sampler) {
|
if (!m_linearSampler) {
|
||||||
m_sampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
m_linearSampler.reset(m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
||||||
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
QRhiSampler::Repeat, QRhiSampler::Repeat));
|
||||||
m_sampler->setName(QByteArrayLiteral("imgui sampler"));
|
m_linearSampler->setName(QByteArrayLiteral("imgui linear sampler"));
|
||||||
if (!m_sampler->create())
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_textures.isEmpty()) {
|
if (m_textures.isEmpty()) {
|
||||||
Texture fontTex;
|
Texture fontTex;
|
||||||
fontTex.image = sf.fontTextureData;
|
fontTex.image = sf.fontTextureData;
|
||||||
m_textures.append(fontTex);
|
m_textures.insert(nullptr, fontTex);
|
||||||
} else if (!sf.fontTextureData.isNull()) {
|
sf.reset();
|
||||||
|
} else if (sf.isValid()) {
|
||||||
Texture fontTex;
|
Texture fontTex;
|
||||||
fontTex.image = sf.fontTextureData;
|
fontTex.image = sf.fontTextureData;
|
||||||
delete m_textures[0].tex;
|
Texture &fontTexEntry(m_textures[nullptr]);
|
||||||
delete m_textures[0].srb;
|
delete fontTexEntry.tex;
|
||||||
m_textures[0] = fontTex;
|
delete fontTexEntry.srb;
|
||||||
|
fontTexEntry = fontTex;
|
||||||
|
sf.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVarLengthArray<int, 8> texturesNeedUpdate;
|
QVarLengthArray<void *, 8> texturesNeedUpdate;
|
||||||
for (int i = 0; i < m_textures.count(); ++i) {
|
for (auto it = m_textures.begin(), end = m_textures.end(); it != end; ++it) {
|
||||||
Texture &t(m_textures[i]);
|
Texture &t(*it);
|
||||||
if (!t.tex) {
|
if (!t.tex) {
|
||||||
t.tex = m_rhi->newTexture(QRhiTexture::RGBA8, t.image.size());
|
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())
|
if (!t.tex->create())
|
||||||
return;
|
return;
|
||||||
texturesNeedUpdate.append(i);
|
texturesNeedUpdate.append(it.key());
|
||||||
}
|
}
|
||||||
if (!t.srb) {
|
if (!t.srb) {
|
||||||
|
QRhiSampler *sampler = t.filter == QRhiSampler::Nearest ? m_nearestSampler.get() : m_linearSampler.get();
|
||||||
t.srb = m_rhi->newShaderResourceBindings();
|
t.srb = m_rhi->newShaderResourceBindings();
|
||||||
t.srb->setBindings({
|
t.srb->setBindings({
|
||||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, m_ubuf.get()),
|
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())
|
if (!t.srb->create())
|
||||||
return;
|
return;
|
||||||
@ -146,6 +160,9 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
|||||||
if (m_ps && m_rt->renderPassDescriptor()->serializedFormat() != m_renderPassFormat)
|
if (m_ps && m_rt->renderPassDescriptor()->serializedFormat() != m_renderPassFormat)
|
||||||
m_ps.reset();
|
m_ps.reset();
|
||||||
|
|
||||||
|
if (m_ps && m_rt->sampleCount() != m_ps->sampleCount())
|
||||||
|
m_ps.reset();
|
||||||
|
|
||||||
if (!m_ps) {
|
if (!m_ps) {
|
||||||
QShader vs = getShader(QLatin1String(":/imgui.vert.qsb"));
|
QShader vs = getShader(QLatin1String(":/imgui.vert.qsb"));
|
||||||
QShader fs = getShader(QLatin1String(":/imgui.frag.qsb"));
|
QShader fs = getShader(QLatin1String(":/imgui.frag.qsb"));
|
||||||
@ -170,7 +187,7 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
|||||||
m_ps->setDepthTest(true);
|
m_ps->setDepthTest(true);
|
||||||
m_ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
|
m_ps->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
|
||||||
m_ps->setDepthWrite(false);
|
m_ps->setDepthWrite(false);
|
||||||
m_ps->setFlags(QRhiGraphicsPipeline::UsesScissor);
|
m_ps->setFlags(QRhiGraphicsPipeline::UsesScissor | QRhiGraphicsPipeline::UsesShadingRate);
|
||||||
|
|
||||||
m_ps->setShaderStages({
|
m_ps->setShaderStages({
|
||||||
{ QRhiShaderStage::Vertex, vs },
|
{ QRhiShaderStage::Vertex, vs },
|
||||||
@ -186,8 +203,8 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
|||||||
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) },
|
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) },
|
||||||
{ 0, 2, QRhiVertexInputAttribute::UNormByte4, 4 * sizeof(float) }
|
{ 0, 2, QRhiVertexInputAttribute::UNormByte4, 4 * sizeof(float) }
|
||||||
});
|
});
|
||||||
|
|
||||||
m_ps->setVertexInputLayout(inputLayout);
|
m_ps->setVertexInputLayout(inputLayout);
|
||||||
|
m_ps->setSampleCount(rt->sampleCount());
|
||||||
m_ps->setShaderResourceBindings(m_textures[0].srb);
|
m_ps->setShaderResourceBindings(m_textures[0].srb);
|
||||||
m_ps->setRenderPassDescriptor(m_rt->renderPassDescriptor());
|
m_ps->setRenderPassDescriptor(m_rt->renderPassDescriptor());
|
||||||
m_renderPassFormat = m_rt->renderPassDescriptor()->serializedFormat();
|
m_renderPassFormat = m_rt->renderPassDescriptor()->serializedFormat();
|
||||||
@ -210,8 +227,10 @@ void QRhiImguiRenderer::prepare(QRhi *rhi,
|
|||||||
|
|
||||||
for (int i = 0; i < texturesNeedUpdate.count(); ++i) {
|
for (int i = 0; i < texturesNeedUpdate.count(); ++i) {
|
||||||
Texture &t(m_textures[texturesNeedUpdate[i]]);
|
Texture &t(m_textures[texturesNeedUpdate[i]]);
|
||||||
u->uploadTexture(t.tex, t.image);
|
if (!t.image.isNull()) {
|
||||||
t.image = QImage();
|
u->uploadTexture(t.tex, t.image);
|
||||||
|
t.image = QImage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cb->resourceUpdate(u);
|
m_cb->resourceUpdate(u);
|
||||||
@ -244,12 +263,31 @@ void QRhiImguiRenderer::render()
|
|||||||
scissorSize.setWidth(qMin(viewportSize.width(), scissorSize.width()));
|
scissorSize.setWidth(qMin(viewportSize.width(), scissorSize.width()));
|
||||||
scissorSize.setHeight(qMin(viewportSize.height(), scissorSize.height()));
|
scissorSize.setHeight(qMin(viewportSize.height(), scissorSize.height()));
|
||||||
m_cb->setScissor({ scissorPos.x(), scissorPos.y(), scissorSize.width(), 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->setVertexInput(0, 1, &vbufBinding, m_ibuf.get(), c.indexOffset, QRhiCommandBuffer::IndexUInt32);
|
||||||
m_cb->drawIndexed(c.elemCount);
|
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 const char *getClipboardText(void *)
|
||||||
{
|
{
|
||||||
static QByteArray contents;
|
static QByteArray contents;
|
||||||
@ -264,7 +302,8 @@ static void setClipboardText(void *, const char *text)
|
|||||||
|
|
||||||
QRhiImgui::QRhiImgui()
|
QRhiImgui::QRhiImgui()
|
||||||
{
|
{
|
||||||
ImGui::CreateContext();
|
context = ImGui::CreateContext();
|
||||||
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||||
rebuildFontAtlas();
|
rebuildFontAtlas();
|
||||||
ImGuiIO &io(ImGui::GetIO());
|
ImGuiIO &io(ImGui::GetIO());
|
||||||
io.GetClipboardTextFn = getClipboardText;
|
io.GetClipboardTextFn = getClipboardText;
|
||||||
@ -273,22 +312,40 @@ QRhiImgui::QRhiImgui()
|
|||||||
|
|
||||||
QRhiImgui::~QRhiImgui()
|
QRhiImgui::~QRhiImgui()
|
||||||
{
|
{
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext(static_cast<ImGuiContext *>(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QRhiImgui::rebuildFontAtlas()
|
void QRhiImgui::rebuildFontAtlas()
|
||||||
{
|
{
|
||||||
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||||
|
ImGuiIO &io(ImGui::GetIO());
|
||||||
unsigned char *pixels;
|
unsigned char *pixels;
|
||||||
int w, h;
|
int w, h;
|
||||||
ImGuiIO &io(ImGui::GetIO());
|
|
||||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &w, &h);
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &w, &h);
|
||||||
const QImage wrapperImg(const_cast<const uchar *>(pixels), w, h, QImage::Format_RGBA8888);
|
const QImage wrapperImg(const_cast<const uchar *>(pixels), w, h, QImage::Format_RGBA8888);
|
||||||
sf.fontTextureData = wrapperImg.copy();
|
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)
|
void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPointF &logicalOffset, FrameFunc frameFunc)
|
||||||
{
|
{
|
||||||
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||||
ImGuiIO &io(ImGui::GetIO());
|
ImGuiIO &io(ImGui::GetIO());
|
||||||
|
|
||||||
const QPointF itemPixelOffset = logicalOffset * dpr;
|
const QPointF itemPixelOffset = logicalOffset * dpr;
|
||||||
@ -332,7 +389,7 @@ void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPoi
|
|||||||
if (!cmd->UserCallback) {
|
if (!cmd->UserCallback) {
|
||||||
QRhiImguiRenderer::DrawCmd dc;
|
QRhiImguiRenderer::DrawCmd dc;
|
||||||
dc.cmdListBufferIdx = n;
|
dc.cmdListBufferIdx = n;
|
||||||
dc.textureIndex = int(reinterpret_cast<qintptr>(cmd->TextureId));
|
dc.textureId = cmd->TextureId;
|
||||||
dc.indexOffset = indexOffset;
|
dc.indexOffset = indexOffset;
|
||||||
dc.elemCount = cmd->ElemCount;
|
dc.elemCount = cmd->ElemCount;
|
||||||
dc.itemPixelOffset = itemPixelOffset;
|
dc.itemPixelOffset = itemPixelOffset;
|
||||||
@ -348,8 +405,10 @@ void QRhiImgui::nextFrame(const QSizeF &logicalOutputSize, float dpr, const QPoi
|
|||||||
|
|
||||||
void QRhiImgui::syncRenderer(QRhiImguiRenderer *renderer)
|
void QRhiImgui::syncRenderer(QRhiImguiRenderer *renderer)
|
||||||
{
|
{
|
||||||
renderer->sf = sf;
|
if (sf.isValid()) {
|
||||||
sf.fontTextureData = QImage();
|
renderer->sf = sf;
|
||||||
|
sf.reset();
|
||||||
|
}
|
||||||
renderer->f = std::move(f);
|
renderer->f = std::move(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,6 +599,7 @@ static ImGuiKey mapKey(int k)
|
|||||||
|
|
||||||
bool QRhiImgui::processEvent(QEvent *event)
|
bool QRhiImgui::processEvent(QEvent *event)
|
||||||
{
|
{
|
||||||
|
ImGui::SetCurrentContext(static_cast<ImGuiContext *>(context));
|
||||||
ImGuiIO &io(ImGui::GetIO());
|
ImGuiIO &io(ImGui::GetIO());
|
||||||
|
|
||||||
switch (event->type()) {
|
switch (event->type()) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Copyright (C) 2022 The Qt Company Ltd.
|
// 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
|
#ifndef QRHIIMGUI_H
|
||||||
#define QRHIIMGUI_P_H
|
#define QRHIIMGUI_H
|
||||||
|
|
||||||
#include <rhi/qrhi.h>
|
#include <rhi/qrhi.h>
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ public:
|
|||||||
|
|
||||||
struct DrawCmd {
|
struct DrawCmd {
|
||||||
int cmdListBufferIdx;
|
int cmdListBufferIdx;
|
||||||
int textureIndex;
|
void *textureId;
|
||||||
quint32 indexOffset;
|
quint32 indexOffset;
|
||||||
quint32 elemCount;
|
quint32 elemCount;
|
||||||
QPointF itemPixelOffset;
|
QPointF itemPixelOffset;
|
||||||
@ -31,6 +31,8 @@ public:
|
|||||||
|
|
||||||
struct StaticRenderData {
|
struct StaticRenderData {
|
||||||
QImage fontTextureData;
|
QImage fontTextureData;
|
||||||
|
bool isValid() const { return !fontTextureData.isNull(); }
|
||||||
|
void reset() { fontTextureData = QImage(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FrameRenderData {
|
struct FrameRenderData {
|
||||||
@ -54,6 +56,15 @@ public:
|
|||||||
void render();
|
void render();
|
||||||
void releaseResources();
|
void releaseResources();
|
||||||
|
|
||||||
|
enum CustomTextureOwnership {
|
||||||
|
TakeCustomTextureOwnership,
|
||||||
|
NoCustomTextureOwnership
|
||||||
|
};
|
||||||
|
void registerCustomTexture(void *id,
|
||||||
|
QRhiTexture *texture,
|
||||||
|
QRhiSampler::Filter filter,
|
||||||
|
CustomTextureOwnership ownership);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QRhi *m_rhi = nullptr;
|
QRhi *m_rhi = nullptr;
|
||||||
QRhiRenderTarget *m_rt = nullptr;
|
QRhiRenderTarget *m_rt = nullptr;
|
||||||
@ -64,14 +75,17 @@ private:
|
|||||||
std::unique_ptr<QRhiBuffer> m_ubuf;
|
std::unique_ptr<QRhiBuffer> m_ubuf;
|
||||||
std::unique_ptr<QRhiGraphicsPipeline> m_ps;
|
std::unique_ptr<QRhiGraphicsPipeline> m_ps;
|
||||||
QVector<quint32> m_renderPassFormat;
|
QVector<quint32> m_renderPassFormat;
|
||||||
std::unique_ptr<QRhiSampler> m_sampler;
|
std::unique_ptr<QRhiSampler> m_linearSampler;
|
||||||
|
std::unique_ptr<QRhiSampler> m_nearestSampler;
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
QImage image;
|
QImage image;
|
||||||
QRhiTexture *tex = nullptr;
|
QRhiTexture *tex = nullptr;
|
||||||
QRhiShaderResourceBindings *srb = nullptr;
|
QRhiShaderResourceBindings *srb = nullptr;
|
||||||
|
QRhiSampler::Filter filter = QRhiSampler::Linear;
|
||||||
|
bool ownTex = true;
|
||||||
};
|
};
|
||||||
QVector<Texture> m_textures;
|
QHash<void *, Texture> m_textures;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QRhiImgui
|
class QRhiImgui
|
||||||
@ -86,8 +100,10 @@ public:
|
|||||||
bool processEvent(QEvent *e);
|
bool processEvent(QEvent *e);
|
||||||
|
|
||||||
void rebuildFontAtlas();
|
void rebuildFontAtlas();
|
||||||
|
void rebuildFontAtlasWithFont(const QString &filename);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void *context;
|
||||||
QRhiImguiRenderer::StaticRenderData sf;
|
QRhiImguiRenderer::StaticRenderData sf;
|
||||||
QRhiImguiRenderer::FrameRenderData f;
|
QRhiImguiRenderer::FrameRenderData f;
|
||||||
Qt::MouseButtons pressedMouseButtons;
|
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