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,
|
||||
ASTCTextureCompression = 0x04000000,
|
||||
ETC2TextureCompression = 0x08000000,
|
||||
HalfFloatVertex = 0x10000000
|
||||
HalfFloatVertex = 0x10000000,
|
||||
MultiView = 0x20000000,
|
||||
MultiViewExtended = 0x40000000
|
||||
};
|
||||
Q_DECLARE_FLAGS(OpenGLExtensions, OpenGLExtension)
|
||||
|
||||
|
@ -348,6 +348,10 @@ static int qt_gl_resolve_extensions()
|
||||
extensions |= QOpenGLExtensions::StandardDerivatives;
|
||||
if (extensionMatcher.match("GL_ARB_half_float_vertex"))
|
||||
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 (format.majorVersion() >= 2)
|
||||
|
@ -967,6 +967,22 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
\value ThreeDimensionalTextureMipmaps Indicates that generating 3D texture
|
||||
mipmaps are supported. In practice this feature will be unsupported with
|
||||
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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\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
|
||||
\inmodule QtGui
|
||||
|
@ -592,6 +592,9 @@ public:
|
||||
int resolveLevel() const { return m_resolveLevel; }
|
||||
void setResolveLevel(int level) { m_resolveLevel = level; }
|
||||
|
||||
int multiViewCount() const { return m_multiViewCount; }
|
||||
void setMultiViewCount(int count) { m_multiViewCount = count; }
|
||||
|
||||
private:
|
||||
QRhiTexture *m_texture = nullptr;
|
||||
QRhiRenderBuffer *m_renderBuffer = nullptr;
|
||||
@ -600,6 +603,7 @@ private:
|
||||
QRhiTexture *m_resolveTexture = nullptr;
|
||||
int m_resolveLayer = 0;
|
||||
int m_resolveLevel = 0;
|
||||
int m_multiViewCount = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_RELOCATABLE_TYPE);
|
||||
@ -1822,7 +1826,8 @@ public:
|
||||
OneDimensionalTextureMipmaps,
|
||||
HalfAttributes,
|
||||
RenderToOneDimensionalTexture,
|
||||
ThreeDimensionalTextureMipmaps
|
||||
ThreeDimensionalTextureMipmaps,
|
||||
MultiView
|
||||
};
|
||||
|
||||
enum BeginFrameFlag {
|
||||
|
@ -588,6 +588,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||
return true;
|
||||
case QRhi::MultiView:
|
||||
return false;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
|
@ -638,6 +638,8 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||
return false; // we generate mipmaps ourselves with compute and this is not implemented
|
||||
case QRhi::MultiView:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1009,6 +1009,15 @@ bool QRhiGles2::create(QRhi::Flags flags)
|
||||
|
||||
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;
|
||||
|
||||
contextLost = false;
|
||||
@ -1371,6 +1380,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
||||
return caps.texture1D;
|
||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||
return caps.texture3D;
|
||||
case QRhi::MultiView:
|
||||
return caps.multiView && caps.maxTextureArraySize > 0;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
@ -5554,8 +5565,13 @@ bool QGles2TextureRenderTarget::create()
|
||||
QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
|
||||
Q_ASSERT(texD->texture && texD->specified);
|
||||
if (texD->flags().testFlag(QRhiTexture::ThreeDimensional) || texD->flags().testFlag(QRhiTexture::TextureArray)) {
|
||||
rhiD->f->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), texD->texture,
|
||||
colorAtt.level(), colorAtt.layer());
|
||||
if (it->multiViewCount() < 2) {
|
||||
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)) {
|
||||
rhiD->glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex),
|
||||
texD->target + uint(colorAtt.layer()), texD->texture,
|
||||
|
@ -909,6 +909,8 @@ public:
|
||||
GLsizei, const GLvoid *) = nullptr;
|
||||
void(QOPENGLF_APIENTRYP glFramebufferTexture1D)(GLenum, GLenum, GLenum, GLuint,
|
||||
GLint) = nullptr;
|
||||
void(QOPENGLF_APIENTRYP glFramebufferTextureMultiviewOVR)(GLenum, GLenum, GLuint, GLint,
|
||||
GLint, GLsizei) = nullptr;
|
||||
|
||||
uint vao = 0;
|
||||
struct Caps {
|
||||
@ -962,7 +964,8 @@ public:
|
||||
geometryShader(false),
|
||||
texture1D(false),
|
||||
hasDrawBuffersFunc(false),
|
||||
halfAttributes(false)
|
||||
halfAttributes(false),
|
||||
multiView(false)
|
||||
{ }
|
||||
int ctxMajor;
|
||||
int ctxMinor;
|
||||
@ -1016,6 +1019,7 @@ public:
|
||||
uint texture1D : 1;
|
||||
uint hasDrawBuffersFunc : 1;
|
||||
uint halfAttributes : 1;
|
||||
uint multiView : 1;
|
||||
} caps;
|
||||
QGles2SwapChain *currentSwapChain = nullptr;
|
||||
QSet<GLint> supportedCompressedFormats;
|
||||
|
@ -833,6 +833,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
|
||||
return false;
|
||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||
return true;
|
||||
case QRhi::MultiView:
|
||||
return false;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return false;
|
||||
|
@ -4437,6 +4437,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
|
||||
return true;
|
||||
case QRhi::ThreeDimensionalTextureMipmaps:
|
||||
return true;
|
||||
case QRhi::MultiView:
|
||||
return false;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ add_subdirectory(stereo)
|
||||
add_subdirectory(tex1d)
|
||||
add_subdirectory(displacement)
|
||||
add_subdirectory(imguirenderer)
|
||||
add_subdirectory(multiview)
|
||||
if(QT_FEATURE_widgets)
|
||||
add_subdirectory(rhiwidget)
|
||||
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 \
|
||||
instancing \
|
||||
noninstanced \
|
||||
tex3d
|
||||
tex3d \
|
||||
texturearray \
|
||||
polygonmode \
|
||||
tessellation \
|
||||
geometryshader \
|
||||
stenciloutline \
|
||||
stereo \
|
||||
tex1d \
|
||||
displacement \
|
||||
imguirenderer \
|
||||
multiview
|
||||
|
||||
qtConfig(widgets) {
|
||||
SUBDIRS += \
|
||||
|
Loading…
Reference in New Issue
Block a user