rhi: Introduce multiview starting with OpenGL (ES)
Fixes: QTBUG-114770 Change-Id: Ibb1ced7f19d15a5116c60e95fd3e6b86ace63155 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
parent
2b9ef2eb44
commit
f9d90c6fba
@ -59,7 +59,9 @@ public:
|
|||||||
StandardDerivatives = 0x02000000,
|
StandardDerivatives = 0x02000000,
|
||||||
ASTCTextureCompression = 0x04000000,
|
ASTCTextureCompression = 0x04000000,
|
||||||
ETC2TextureCompression = 0x08000000,
|
ETC2TextureCompression = 0x08000000,
|
||||||
HalfFloatVertex = 0x10000000
|
HalfFloatVertex = 0x10000000,
|
||||||
|
MultiView = 0x20000000,
|
||||||
|
MultiViewExtended = 0x40000000
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
|
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
|
||||||
|
|
||||||
|
@ -348,6 +348,10 @@ static int qt_gl_resolve_extensions()
|
|||||||
extensions |= QOpenGLExtensions::StandardDerivatives;
|
extensions |= QOpenGLExtensions::StandardDerivatives;
|
||||||
if (extensionMatcher.match("GL_ARB_half_float_vertex"))
|
if (extensionMatcher.match("GL_ARB_half_float_vertex"))
|
||||||
extensions |= QOpenGLExtensions::HalfFloatVertex;
|
extensions |= QOpenGLExtensions::HalfFloatVertex;
|
||||||
|
if (extensionMatcher.match("GL_OVR_multiview"))
|
||||||
|
extensions |= QOpenGLExtensions::MultiView;
|
||||||
|
if (extensionMatcher.match("GL_OVR_multiview2"))
|
||||||
|
extensions |= QOpenGLExtensions::MultiViewExtended;
|
||||||
|
|
||||||
if (ctx->isOpenGLES()) {
|
if (ctx->isOpenGLES()) {
|
||||||
if (format.majorVersion() >= 2)
|
if (format.majorVersion() >= 2)
|
||||||
|
@ -967,6 +967,22 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
|||||||
\value ThreeDimensionalTextureMipmaps Indicates that generating 3D texture
|
\value ThreeDimensionalTextureMipmaps Indicates that generating 3D texture
|
||||||
mipmaps are supported. In practice this feature will be unsupported with
|
mipmaps are supported. In practice this feature will be unsupported with
|
||||||
Direct 3D 12.
|
Direct 3D 12.
|
||||||
|
|
||||||
|
\value MultiView Indicates that multiview, see e.g.
|
||||||
|
\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_multiview.html}{VK_KHR_multiview}
|
||||||
|
is supported. With OpenGL ES 2.0, Direct 3D 11, and OpenGL (ES)
|
||||||
|
implementations without \c{GL_OVR_multiview2} this feature will not be
|
||||||
|
supported. With Vulkan 1.1 and newer, and Direct 3D 12 multiview is
|
||||||
|
typically supported. When reported as supported, creating a
|
||||||
|
QRhiTextureRenderTarget with a QRhiColorAttachment that references a texture
|
||||||
|
array and has \l{QRhiColorAttachment::setMultiViewCount()}{multiViewCount}
|
||||||
|
set enables recording a render pass that uses multiview rendering. Note that
|
||||||
|
multiview is only available in combination with 2D texture arrays. It cannot
|
||||||
|
be used to optimize the rendering into individual textures (e.g. two, for
|
||||||
|
the left and right eyes). Rather, the target of a multiview render pass is
|
||||||
|
always a texture array, automatically rendering to the layer (array element)
|
||||||
|
corresponding to each view. Therefore this feature implies \l TextureArrays
|
||||||
|
as well. This enum value has been introduced in Qt 6.7.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -2296,6 +2312,70 @@ QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
|
|||||||
Sets the resolve texture mip \a level to use.
|
Sets the resolve texture mip \a level to use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn int QRhiColorAttachment::multiViewCount() const
|
||||||
|
|
||||||
|
\return the currently set number of views. Defaults to 0 which indicates
|
||||||
|
the render target with this color attachment is not going to be used with
|
||||||
|
multiview rendering.
|
||||||
|
|
||||||
|
\since 6.7
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void QRhiColorAttachment::setMultiViewCount(int count)
|
||||||
|
|
||||||
|
Sets the view \a count. Setting a value larger than 1 indicates that the
|
||||||
|
render target with this color attachment is going to be used with multiview
|
||||||
|
rendering. The default value is 0. Values smaller than 2 indicate no
|
||||||
|
multiview rendering.
|
||||||
|
|
||||||
|
When \a count is set to \c 2 or greater, the color attachment must be
|
||||||
|
associated with a 2D texture array. layer() and multiViewCount() together
|
||||||
|
define the range of texture array elements that are targeted during
|
||||||
|
multiview rendering.
|
||||||
|
|
||||||
|
For example, if \c layer is \c 0 and \c multiViewCount is \c 2, the texture
|
||||||
|
array must have 2 (or more) elements, and the multiview rendering will
|
||||||
|
target elements 0 and 1. The \c{gl_ViewIndex} variable in the shaders has a
|
||||||
|
value of \c 0 or \c 1 then, where view \c 0 corresponds to the texture array
|
||||||
|
element \c 0, and view \c 1 to the array element \c 1.
|
||||||
|
|
||||||
|
\note Setting a \a count larger than 1, using a texture array as texture(),
|
||||||
|
and calling \l{QRhiCommandBuffer::beginPass()}{beginPass()} on a
|
||||||
|
QRhiTextureRenderTarget with this color attachment implies multiview
|
||||||
|
rendering for the entire render pass. multiViewCount() should not be set
|
||||||
|
unless multiview rendering is wanted. Multiview cannot be used with texture
|
||||||
|
types other than 2D texture arrays. (although 3D textures may work,
|
||||||
|
depending on the graphics API and backend; applications are nonetheless
|
||||||
|
advised not to rely on that and only use 2D texture arrays as the render
|
||||||
|
targets of multiview rendering)
|
||||||
|
|
||||||
|
See
|
||||||
|
\l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview}
|
||||||
|
for more details regarding multiview rendering. Do note that Qt requires
|
||||||
|
\l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview2.txt}{GL_OVR_multiview2}
|
||||||
|
as well, when running on OpenGL (ES).
|
||||||
|
|
||||||
|
Multiview rendering is available only when the
|
||||||
|
\l{QRhi::MultiView}{MultiView} feature is reported as supported from
|
||||||
|
\l{QRhi::isFeatureSupported()}{isFeatureSupported()}.
|
||||||
|
|
||||||
|
\note For portability, be aware of limitations that exist for multiview
|
||||||
|
rendering with some of the graphics APIs. For example, OpenGL disallows
|
||||||
|
tessellation or geometry shaders with multiview. With other APIs, e.g.
|
||||||
|
Vulkan, some of these are optional features, the actual support depending
|
||||||
|
on the implementation. It is therefore recommended that multiview render
|
||||||
|
passes do not rely on any of the features that
|
||||||
|
\l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview}
|
||||||
|
declares as unsupported. The one exception is shader stage outputs other
|
||||||
|
than \c{gl_Position} depending on \c{gl_ViewIndex}: that can be relied on
|
||||||
|
(even with OpenGL) because QRhi never reports multiview as supported without
|
||||||
|
\c{GL_OVR_multiview2} also being present.
|
||||||
|
|
||||||
|
\since 6.7
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QRhiTextureRenderTargetDescription
|
\class QRhiTextureRenderTargetDescription
|
||||||
\inmodule QtGui
|
\inmodule QtGui
|
||||||
|
@ -592,6 +592,9 @@ public:
|
|||||||
int resolveLevel() const { return m_resolveLevel; }
|
int resolveLevel() const { return m_resolveLevel; }
|
||||||
void setResolveLevel(int level) { m_resolveLevel = level; }
|
void setResolveLevel(int level) { m_resolveLevel = level; }
|
||||||
|
|
||||||
|
int multiViewCount() const { return m_multiViewCount; }
|
||||||
|
void setMultiViewCount(int count) { m_multiViewCount = count; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QRhiTexture *m_texture = nullptr;
|
QRhiTexture *m_texture = nullptr;
|
||||||
QRhiRenderBuffer *m_renderBuffer = nullptr;
|
QRhiRenderBuffer *m_renderBuffer = nullptr;
|
||||||
@ -600,6 +603,7 @@ private:
|
|||||||
QRhiTexture *m_resolveTexture = nullptr;
|
QRhiTexture *m_resolveTexture = nullptr;
|
||||||
int m_resolveLayer = 0;
|
int m_resolveLayer = 0;
|
||||||
int m_resolveLevel = 0;
|
int m_resolveLevel = 0;
|
||||||
|
int m_multiViewCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_RELOCATABLE_TYPE);
|
Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_RELOCATABLE_TYPE);
|
||||||
@ -1822,7 +1826,8 @@ public:
|
|||||||
OneDimensionalTextureMipmaps,
|
OneDimensionalTextureMipmaps,
|
||||||
HalfAttributes,
|
HalfAttributes,
|
||||||
RenderToOneDimensionalTexture,
|
RenderToOneDimensionalTexture,
|
||||||
ThreeDimensionalTextureMipmaps
|
ThreeDimensionalTextureMipmaps,
|
||||||
|
MultiView
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BeginFrameFlag {
|
enum BeginFrameFlag {
|
||||||
|
@ -588,6 +588,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return true;
|
return true;
|
||||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||||
return true;
|
return true;
|
||||||
|
case QRhi::MultiView:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
|
@ -638,6 +638,8 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return true;
|
return true;
|
||||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||||
return false; // we generate mipmaps ourselves with compute and this is not implemented
|
return false; // we generate mipmaps ourselves with compute and this is not implemented
|
||||||
|
case QRhi::MultiView:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1009,6 +1009,15 @@ bool QRhiGles2::create(QRhi::Flags flags)
|
|||||||
|
|
||||||
caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
|
caps.halfAttributes = f->hasOpenGLExtension(QOpenGLExtensions::HalfFloatVertex);
|
||||||
|
|
||||||
|
// We always require GL_OVR_multiview2 for symmetry with other backends.
|
||||||
|
caps.multiView = f->hasOpenGLExtension(QOpenGLExtensions::MultiView)
|
||||||
|
&& f->hasOpenGLExtension(QOpenGLExtensions::MultiViewExtended);
|
||||||
|
if (caps.multiView) {
|
||||||
|
glFramebufferTextureMultiviewOVR =
|
||||||
|
reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei)>(
|
||||||
|
ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
|
||||||
|
}
|
||||||
|
|
||||||
nativeHandlesStruct.context = ctx;
|
nativeHandlesStruct.context = ctx;
|
||||||
|
|
||||||
contextLost = false;
|
contextLost = false;
|
||||||
@ -1371,6 +1380,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return caps.texture1D;
|
return caps.texture1D;
|
||||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||||
return caps.texture3D;
|
return caps.texture3D;
|
||||||
|
case QRhi::MultiView:
|
||||||
|
return caps.multiView && caps.maxTextureArraySize > 0;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE_RETURN(false);
|
Q_UNREACHABLE_RETURN(false);
|
||||||
}
|
}
|
||||||
@ -5554,8 +5565,13 @@ bool QGles2TextureRenderTarget::create()
|
|||||||
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
|
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
|
||||||
Q_ASSERT(texD->texture && texD->specified);
|
Q_ASSERT(texD->texture && texD->specified);
|
||||||
if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
|
if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
|
||||||
rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
|
if (it->multiViewCount() < 2) {
|
||||||
colorAtt.level(), colorAtt.layer());
|
rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
|
||||||
|
colorAtt.level(), colorAtt.layer());
|
||||||
|
} else {
|
||||||
|
rhiD->glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
|
||||||
|
colorAtt.level(), colorAtt.layer(), colorAtt.multiViewCount());
|
||||||
|
}
|
||||||
} else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
|
} else if (texD->flags().testFlag(QRhiTexture::OneDimensional)) {
|
||||||
rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
|
rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
|
||||||
texD->target + uint(colorAtt.layer()), texD->texture,
|
texD->target + uint(colorAtt.layer()), texD->texture,
|
||||||
|
@ -909,6 +909,8 @@ public:
|
|||||||
GLsizei, const GLvoid *) = nullptr;
|
GLsizei, const GLvoid *) = nullptr;
|
||||||
void(QOPENGLF_APIENTRYP glFramebufferTexture1D)(GLenum, GLenum, GLenum, GLuint,
|
void(QOPENGLF_APIENTRYP glFramebufferTexture1D)(GLenum, GLenum, GLenum, GLuint,
|
||||||
GLint) = nullptr;
|
GLint) = nullptr;
|
||||||
|
void(QOPENGLF_APIENTRYP glFramebufferTextureMultiviewOVR)(GLenum, GLenum, GLuint, GLint,
|
||||||
|
GLint, GLsizei) = nullptr;
|
||||||
|
|
||||||
uint vao = 0;
|
uint vao = 0;
|
||||||
struct Caps {
|
struct Caps {
|
||||||
@ -962,7 +964,8 @@ public:
|
|||||||
geometryShader(false),
|
geometryShader(false),
|
||||||
texture1D(false),
|
texture1D(false),
|
||||||
hasDrawBuffersFunc(false),
|
hasDrawBuffersFunc(false),
|
||||||
halfAttributes(false)
|
halfAttributes(false),
|
||||||
|
multiView(false)
|
||||||
{ }
|
{ }
|
||||||
int ctxMajor;
|
int ctxMajor;
|
||||||
int ctxMinor;
|
int ctxMinor;
|
||||||
@ -1016,6 +1019,7 @@ public:
|
|||||||
uint texture1D : 1;
|
uint texture1D : 1;
|
||||||
uint hasDrawBuffersFunc : 1;
|
uint hasDrawBuffersFunc : 1;
|
||||||
uint halfAttributes : 1;
|
uint halfAttributes : 1;
|
||||||
|
uint multiView : 1;
|
||||||
} caps;
|
} caps;
|
||||||
QGles2SwapChain *currentSwapChain = nullptr;
|
QGles2SwapChain *currentSwapChain = nullptr;
|
||||||
QSet<GLint> supportedCompressedFormats;
|
QSet<GLint> supportedCompressedFormats;
|
||||||
|
@ -833,6 +833,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return false;
|
return false;
|
||||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||||
return true;
|
return true;
|
||||||
|
case QRhi::MultiView:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
return false;
|
return false;
|
||||||
|
@ -4437,6 +4437,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
|||||||
return true;
|
return true;
|
||||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||||
return true;
|
return true;
|
||||||
|
case QRhi::MultiView:
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
Q_UNREACHABLE_RETURN(false);
|
Q_UNREACHABLE_RETURN(false);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ add_subdirectory(stereo)
|
|||||||
add_subdirectory(tex1d)
|
add_subdirectory(tex1d)
|
||||||
add_subdirectory(displacement)
|
add_subdirectory(displacement)
|
||||||
add_subdirectory(imguirenderer)
|
add_subdirectory(imguirenderer)
|
||||||
|
add_subdirectory(multiview)
|
||||||
if(QT_FEATURE_widgets)
|
if(QT_FEATURE_widgets)
|
||||||
add_subdirectory(rhiwidget)
|
add_subdirectory(rhiwidget)
|
||||||
endif()
|
endif()
|
||||||
|
21
tests/manual/rhi/multiview/CMakeLists.txt
Normal file
21
tests/manual/rhi/multiview/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
qt_internal_add_manual_test(multiview
|
||||||
|
GUI
|
||||||
|
SOURCES
|
||||||
|
multiview.cpp
|
||||||
|
LIBRARIES
|
||||||
|
Qt::Gui
|
||||||
|
Qt::GuiPrivate
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_internal_add_resource(multiview "multiview"
|
||||||
|
PREFIX
|
||||||
|
"/"
|
||||||
|
FILES
|
||||||
|
"multiview.vert.qsb"
|
||||||
|
"multiview.frag.qsb"
|
||||||
|
"texture.vert.qsb"
|
||||||
|
"texture.frag.qsb"
|
||||||
|
)
|
4
tests/manual/rhi/multiview/buildshaders.bat
Normal file
4
tests/manual/rhi/multiview/buildshaders.bat
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
qsb --view-count 2 --glsl "300 es,330" --hlsl 61 multiview.vert -o multiview.vert.qsb
|
||||||
|
qsb --glsl "300 es,330" --hlsl 61 multiview.frag -o multiview.frag.qsb
|
||||||
|
qsb --glsl "300 es,330" --hlsl 61 texture.vert -o texture.vert.qsb
|
||||||
|
qsb --glsl "300 es,330" --hlsl 61 texture.frag -o texture.frag.qsb
|
239
tests/manual/rhi/multiview/multiview.cpp
Normal file
239
tests/manual/rhi/multiview/multiview.cpp
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
#include "../shared/examplefw.h"
|
||||||
|
|
||||||
|
// Multiview rendering. Renders the same geometry (a triangle) with two
|
||||||
|
// different transforms into two layers of a texture array object in a *single*
|
||||||
|
// draw call. (NB under the hood it is at the hardware/driver's discretion what
|
||||||
|
// happens; it may very well map to some simple looping and still drawing
|
||||||
|
// twice, whereas with modern hardware it can be expected to be implemented
|
||||||
|
// more efficiently, but that's hidden from us)
|
||||||
|
|
||||||
|
static float quadVertexData[] =
|
||||||
|
{ // Y up, CCW
|
||||||
|
-0.5f, 0.5f, 0.0f, 0.0f,
|
||||||
|
-0.5f, -0.5f, 0.0f, 1.0f,
|
||||||
|
0.5f, -0.5f, 1.0f, 1.0f,
|
||||||
|
0.5f, 0.5f, 1.0f, 0.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
static quint16 quadIndexData[] =
|
||||||
|
{
|
||||||
|
0, 1, 2, 0, 2, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
static float triangleData[] =
|
||||||
|
{ // Y up, CCW
|
||||||
|
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
|
||||||
|
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
||||||
|
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
QList<QRhiResource *> releasePool;
|
||||||
|
QRhiBuffer *vbuf = nullptr;
|
||||||
|
QRhiBuffer *ibuf = nullptr;
|
||||||
|
QRhiBuffer *ubuf = nullptr;
|
||||||
|
QRhiTextureRenderTarget *rt = nullptr;
|
||||||
|
QRhiRenderPassDescriptor *rtRp = nullptr;
|
||||||
|
QRhiSampler *sampler = nullptr;
|
||||||
|
QRhiGraphicsPipeline *ps = nullptr;
|
||||||
|
QRhiResourceUpdateBatch *initialUpdates = nullptr;
|
||||||
|
QMatrix4x4 winProj;
|
||||||
|
QRhiTexture *tex = nullptr;
|
||||||
|
QRhiShaderResourceBindings *srb[2] = {};
|
||||||
|
|
||||||
|
QRhiBuffer *triUbuf = nullptr;
|
||||||
|
QRhiShaderResourceBindings *triSrb = nullptr;
|
||||||
|
QRhiGraphicsPipeline *triPs = nullptr;
|
||||||
|
QMatrix4x4 triBaseMvp;
|
||||||
|
} d;
|
||||||
|
|
||||||
|
void Window::customInit()
|
||||||
|
{
|
||||||
|
if (!m_r->isFeatureSupported(QRhi::MultiView))
|
||||||
|
qFatal("Multiview is not supported");
|
||||||
|
|
||||||
|
// texture array with 2 elements, e.g. 0 is left eye, 1 is right
|
||||||
|
d.tex = m_r->newTextureArray(QRhiTexture::RGBA8, 2, QSize(512, 512), 1, QRhiTexture::RenderTarget);
|
||||||
|
d.releasePool << d.tex;
|
||||||
|
d.tex->create();
|
||||||
|
|
||||||
|
// set up the multiview render target
|
||||||
|
QRhiColorAttachment multiViewAtt(d.tex);
|
||||||
|
// using array elements 0 and 1
|
||||||
|
multiViewAtt.setLayer(0);
|
||||||
|
multiViewAtt.setMultiViewCount(2);
|
||||||
|
QRhiTextureRenderTargetDescription rtDesc(multiViewAtt);
|
||||||
|
d.rt = m_r->newTextureRenderTarget(rtDesc);
|
||||||
|
d.releasePool << d.rt;
|
||||||
|
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
|
||||||
|
d.releasePool << d.rtRp;
|
||||||
|
d.rt->setRenderPassDescriptor(d.rtRp);
|
||||||
|
d.rt->create();
|
||||||
|
|
||||||
|
// vertex buffer used by both passes
|
||||||
|
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(quadVertexData) + sizeof(triangleData));
|
||||||
|
d.vbuf->create();
|
||||||
|
d.releasePool << d.vbuf;
|
||||||
|
|
||||||
|
// resources for the on-screen visualizer
|
||||||
|
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(quadIndexData));
|
||||||
|
d.ibuf->create();
|
||||||
|
d.releasePool << d.ibuf;
|
||||||
|
|
||||||
|
const int oneRoundedUniformBlockSize = m_r->ubufAligned(72);
|
||||||
|
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, oneRoundedUniformBlockSize * 2);
|
||||||
|
d.ubuf->create();
|
||||||
|
d.releasePool << d.ubuf;
|
||||||
|
|
||||||
|
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
||||||
|
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
|
||||||
|
d.releasePool << d.sampler;
|
||||||
|
d.sampler->create();
|
||||||
|
|
||||||
|
// two srbs, just for the quad positioning on-screen
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
QRhiShaderResourceBindings *srb = m_r->newShaderResourceBindings();
|
||||||
|
d.releasePool << srb;
|
||||||
|
srb->setBindings({
|
||||||
|
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
|
||||||
|
d.ubuf, i * oneRoundedUniformBlockSize, 72),
|
||||||
|
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
|
||||||
|
});
|
||||||
|
srb->create();
|
||||||
|
d.srb[i] = srb;
|
||||||
|
}
|
||||||
|
|
||||||
|
d.ps = m_r->newGraphicsPipeline();
|
||||||
|
d.releasePool << d.ps;
|
||||||
|
d.ps->setShaderStages({
|
||||||
|
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
|
||||||
|
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
|
||||||
|
});
|
||||||
|
QRhiVertexInputLayout inputLayout;
|
||||||
|
inputLayout.setBindings({
|
||||||
|
{ 4 * sizeof(float) }
|
||||||
|
});
|
||||||
|
inputLayout.setAttributes({
|
||||||
|
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
|
||||||
|
{ 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) }
|
||||||
|
});
|
||||||
|
d.ps->setVertexInputLayout(inputLayout);
|
||||||
|
d.ps->setShaderResourceBindings(d.srb[0]); // all of them are layout-compatible
|
||||||
|
d.ps->setRenderPassDescriptor(m_rp);
|
||||||
|
d.ps->create();
|
||||||
|
|
||||||
|
d.initialUpdates = m_r->nextResourceUpdateBatch();
|
||||||
|
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(quadVertexData), quadVertexData);
|
||||||
|
d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(quadVertexData), sizeof(triangleData), triangleData);
|
||||||
|
d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
|
||||||
|
|
||||||
|
qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 64, 4, &flip);
|
||||||
|
float layer = i;
|
||||||
|
d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 68, 4, &layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create resources for the multiview render pass
|
||||||
|
d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 128); // mat4 mvp[2]
|
||||||
|
d.releasePool << d.triUbuf;
|
||||||
|
d.triUbuf->create();
|
||||||
|
|
||||||
|
d.triSrb = m_r->newShaderResourceBindings();
|
||||||
|
d.releasePool << d.triSrb;
|
||||||
|
d.triSrb->setBindings({
|
||||||
|
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
|
||||||
|
});
|
||||||
|
d.triSrb->create();
|
||||||
|
|
||||||
|
d.triPs = m_r->newGraphicsPipeline();
|
||||||
|
d.releasePool << d.triPs;
|
||||||
|
d.triPs->setShaderStages({
|
||||||
|
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/multiview.vert.qsb")) },
|
||||||
|
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/multiview.frag.qsb")) }
|
||||||
|
});
|
||||||
|
|
||||||
|
inputLayout.setBindings({
|
||||||
|
{ 5 * sizeof(float) }
|
||||||
|
});
|
||||||
|
inputLayout.setAttributes({
|
||||||
|
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
|
||||||
|
{ 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) }
|
||||||
|
});
|
||||||
|
d.triPs->setVertexInputLayout(inputLayout);
|
||||||
|
d.triPs->setShaderResourceBindings(d.triSrb);
|
||||||
|
d.triPs->setRenderPassDescriptor(d.rtRp);
|
||||||
|
d.triPs->create();
|
||||||
|
|
||||||
|
d.triBaseMvp = m_r->clipSpaceCorrMatrix();
|
||||||
|
d.triBaseMvp.perspective(45.0f, d.rt->pixelSize().width() / float(d.rt->pixelSize().height()), 0.01f, 1000.0f);
|
||||||
|
d.triBaseMvp.translate(0, 0, -2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::customRelease()
|
||||||
|
{
|
||||||
|
qDeleteAll(d.releasePool);
|
||||||
|
d.releasePool.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, quint32(sizeof(quadVertexData)));
|
||||||
|
|
||||||
|
QMatrix4x4 triMvp = d.triBaseMvp;
|
||||||
|
// let's say this is the left eye, make the triangle point left for now
|
||||||
|
triMvp.rotate(90, 0, 0, 1);
|
||||||
|
u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
|
||||||
|
triMvp = d.triBaseMvp;
|
||||||
|
// right for the right eye
|
||||||
|
triMvp.rotate(270, 0, 0, 1);
|
||||||
|
u->updateDynamicBuffer(d.triUbuf, 64, 64, triMvp.constData());
|
||||||
|
|
||||||
|
cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u);
|
||||||
|
cb->setGraphicsPipeline(d.triPs);
|
||||||
|
cb->setViewport({ 0, 0, float(d.rt->pixelSize().width()), float(d.rt->pixelSize().height()) });
|
||||||
|
cb->setShaderResources();
|
||||||
|
cb->setVertexInput(0, 1, &vbufBinding);
|
||||||
|
cb->draw(3);
|
||||||
|
cb->endPass();
|
||||||
|
|
||||||
|
cb->resourceUpdate(u);
|
||||||
|
|
||||||
|
// "blit" the two texture layers on-screen just to visualize the contents
|
||||||
|
u = m_r->nextResourceUpdateBatch();
|
||||||
|
if (d.winProj != m_proj) {
|
||||||
|
d.winProj = m_proj;
|
||||||
|
const int oneRoundedUniformBlockSize = m_r->ubufAligned(72);
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
QMatrix4x4 mvp = m_proj;
|
||||||
|
mvp.translate(0, 0, 1);
|
||||||
|
if (i == 0)
|
||||||
|
mvp.translate(-1.0f, 0, 0);
|
||||||
|
else
|
||||||
|
mvp.translate(1.0f, 0, 0);
|
||||||
|
u->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize, 64, mvp.constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QSize outputSizeInPixels = m_sc->currentPixelSize();
|
||||||
|
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u);
|
||||||
|
cb->setGraphicsPipeline(d.ps);
|
||||||
|
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
|
||||||
|
vbufBinding.second = 0;
|
||||||
|
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
cb->setShaderResources(d.srb[i]);
|
||||||
|
cb->drawIndexed(6);
|
||||||
|
}
|
||||||
|
cb->endPass();
|
||||||
|
}
|
10
tests/manual/rhi/multiview/multiview.frag
Normal file
10
tests/manual/rhi/multiview/multiview.frag
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 v_color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fragColor = vec4(v_color, 1.0);
|
||||||
|
}
|
BIN
tests/manual/rhi/multiview/multiview.frag.qsb
Normal file
BIN
tests/manual/rhi/multiview/multiview.frag.qsb
Normal file
Binary file not shown.
8
tests/manual/rhi/multiview/multiview.pro
Normal file
8
tests/manual/rhi/multiview/multiview.pro
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
QT += gui-private
|
||||||
|
|
||||||
|
SOURCES = \
|
||||||
|
multiview.cpp
|
||||||
|
|
||||||
|
RESOURCES = multiview.qrc
|
8
tests/manual/rhi/multiview/multiview.qrc
Normal file
8
tests/manual/rhi/multiview/multiview.qrc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file>multiview.vert.qsb</file>
|
||||||
|
<file>multiview.frag.qsb</file>
|
||||||
|
<file>texture.vert.qsb</file>
|
||||||
|
<file>texture.frag.qsb</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
18
tests/manual/rhi/multiview/multiview.vert
Normal file
18
tests/manual/rhi/multiview/multiview.vert
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#version 440
|
||||||
|
#extension GL_EXT_multiview : require
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 pos;
|
||||||
|
layout(location = 1) in vec3 color;
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 v_color;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf
|
||||||
|
{
|
||||||
|
mat4 mvp[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_color = color;
|
||||||
|
gl_Position = mvp[gl_ViewIndex] * pos;
|
||||||
|
}
|
BIN
tests/manual/rhi/multiview/multiview.vert.qsb
Normal file
BIN
tests/manual/rhi/multiview/multiview.vert.qsb
Normal file
Binary file not shown.
19
tests/manual/rhi/multiview/texture.frag
Normal file
19
tests/manual/rhi/multiview/texture.frag
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 v_texcoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2DArray tex;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 mvp;
|
||||||
|
int flip;
|
||||||
|
float layer;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 c = texture(tex, vec3(v_texcoord, layer));
|
||||||
|
fragColor = vec4(c.rgb * c.a, c.a);
|
||||||
|
}
|
BIN
tests/manual/rhi/multiview/texture.frag.qsb
Normal file
BIN
tests/manual/rhi/multiview/texture.frag.qsb
Normal file
Binary file not shown.
20
tests/manual/rhi/multiview/texture.vert
Normal file
20
tests/manual/rhi/multiview/texture.vert
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 position;
|
||||||
|
layout(location = 1) in vec2 texcoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 v_texcoord;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 mvp;
|
||||||
|
int flip;
|
||||||
|
float layer;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_texcoord = vec2(texcoord.x, texcoord.y);
|
||||||
|
if (flip != 0)
|
||||||
|
v_texcoord.y = 1.0 - v_texcoord.y;
|
||||||
|
gl_Position = mvp * position;
|
||||||
|
}
|
BIN
tests/manual/rhi/multiview/texture.vert.qsb
Normal file
BIN
tests/manual/rhi/multiview/texture.vert.qsb
Normal file
Binary file not shown.
@ -22,7 +22,17 @@ SUBDIRS += \
|
|||||||
computeimage \
|
computeimage \
|
||||||
instancing \
|
instancing \
|
||||||
noninstanced \
|
noninstanced \
|
||||||
tex3d
|
tex3d \
|
||||||
|
texturearray \
|
||||||
|
polygonmode \
|
||||||
|
tessellation \
|
||||||
|
geometryshader \
|
||||||
|
stenciloutline \
|
||||||
|
stereo \
|
||||||
|
tex1d \
|
||||||
|
displacement \
|
||||||
|
imguirenderer \
|
||||||
|
multiview
|
||||||
|
|
||||||
qtConfig(widgets) {
|
qtConfig(widgets) {
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
|
Loading…
Reference in New Issue
Block a user