From c9ad5ad3b73ff60e9a40173b1ae49e67800a4b72 Mon Sep 17 00:00:00 2001 From: Kristoffer Skau Date: Tue, 25 Oct 2022 22:30:15 +0200 Subject: Add support for stereoscopic content in QRhi::OpenGLES2 Setting the flag QSurfaceFormat::StereoBuffers does not actually do anything, because we do not utilize the extra buffers provided. We need to expose setting the correct buffers using glDrawBuffers between draw calls. Change-Id: I6a5110405e621030ac3a2886fa83df0cfe928723 Reviewed-by: Laszlo Agocs --- src/gui/rhi/qrhi.cpp | 29 ++++++++++++++++++++ src/gui/rhi/qrhi_p.h | 6 ++++ src/gui/rhi/qrhigles2.cpp | 67 +++++++++++++++++++++++++++++++++++++-------- src/gui/rhi/qrhigles2_p_p.h | 11 ++++++++ 4 files changed, 101 insertions(+), 12 deletions(-) (limited to 'src/gui') diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 701cecb3d1..20f4b4beae 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -4859,6 +4859,35 @@ QRhiResource::Type QRhiSwapChain::resourceType() const \note the value must not be cached and reused between frames */ +/*! + \enum QRhiSwapChain::StereoTargetBuffer + Selects the backbuffer to use with a stereoscopic swapchain. + + \value LeftBuffer + \value RightBuffer + */ + +/*! + \return a render target that can be used with beginPass() in order to + render to the swapchain's left or right backbuffer. This overload should be + used only with stereoscopic rendering, that is, when the associated QWindow + is backed by two color buffers, one for each eye, instead of just one. + + When stereoscopic rendering is not supported, the return value will be + null. For the time being the only backend and 3D API where traditional + stereoscopic rendering is supported is OpenGL (excluding OpenGL ES), in + combination with \l QSurfaceFormat::StereoBuffers, assuming it is supported + by the graphics and display driver stack at run time. All other backends + are going to return null from this overload. + + \note the value must not be cached and reused between frames + */ +QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer) +{ + Q_UNUSED(targetBuffer); + return nullptr; +} + /*! \fn bool QRhiSwapChain::createOrResize() diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 1d32d08e84..5ee0d6e1fd 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -1379,6 +1379,11 @@ public: HDR10 }; + enum StereoTargetBuffer { + LeftBuffer, + RightBuffer + }; + QRhiResource::Type resourceType() const override; QWindow *window() const { return m_window; } @@ -1403,6 +1408,7 @@ public: virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0; virtual QRhiRenderTarget *currentFrameRenderTarget() = 0; + virtual QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer); virtual QSize surfacePixelSize() = 0; virtual bool isFormatSupported(Format f) = 0; virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 1a7be9eb49..180bb7e360 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -435,6 +435,14 @@ QT_BEGIN_NAMESPACE #define GL_GEOMETRY_SHADER 0x8DD9 #endif +#ifndef GL_BACK_LEFT +#define GL_BACK_LEFT 0x0402 +#endif + +#ifndef GL_BACK_RIGHT +#define GL_BACK_RIGHT 0x0403 +#endif + /*! Constructs a new QRhiGles2InitParams. @@ -2997,24 +3005,31 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) cmd.args.bindShaderResources.dynamicOffsetCount); break; case QGles2CommandBuffer::Command::BindFramebuffer: + { + QVarLengthArray bufs; if (cmd.args.bindFramebuffer.fbo) { f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo); + const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount; + bufs.append(colorAttCount > 0 ? GL_COLOR_ATTACHMENT0 : GL_NONE); if (caps.maxDrawBuffers > 1) { - const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount; - QVarLengthArray bufs; - for (int i = 0; i < colorAttCount; ++i) + for (int i = 1; i < colorAttCount; ++i) bufs.append(GL_COLOR_ATTACHMENT0 + uint(i)); - f->glDrawBuffers(colorAttCount, bufs.constData()); } } else { f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); + if (cmd.args.bindFramebuffer.stereo && cmd.args.bindFramebuffer.stereoTarget == QRhiSwapChain::RightBuffer) + bufs.append(GL_BACK_RIGHT); + else + bufs.append(caps.gles ? GL_BACK : GL_BACK_LEFT); } + f->glDrawBuffers(bufs.count(), bufs.constData()); if (caps.srgbCapableDefaultFramebuffer) { if (cmd.args.bindFramebuffer.srgb) f->glEnable(GL_FRAMEBUFFER_SRGB); else f->glDisable(GL_FRAMEBUFFER_SRGB); } + } break; case QGles2CommandBuffer::Command::Clear: f->glDisable(GL_SCISSOR_TEST); @@ -3980,6 +3995,9 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, *wantsDsClear = doClearBuffers; fbCmd.args.bindFramebuffer.fbo = 0; fbCmd.args.bindFramebuffer.colorAttCount = 1; + fbCmd.args.bindFramebuffer.stereo = rtD->stereoTarget.has_value(); + if (fbCmd.args.bindFramebuffer.stereo) + fbCmd.args.bindFramebuffer.stereoTarget = rtD->stereoTarget.value(); break; case QRhiResource::TextureRenderTarget: { @@ -3991,6 +4009,7 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents); fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer; fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount; + fbCmd.args.bindFramebuffer.stereo = false; for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments(); it != itEnd; ++it) @@ -5753,6 +5772,8 @@ void QGles2CommandBuffer::destroy() QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi) : QRhiSwapChain(rhi), rt(rhi, this), + rtLeft(rhi, this), + rtRight(rhi, this), cb(rhi) { } @@ -5779,6 +5800,16 @@ QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget() return &rt; } +QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget(StereoTargetBuffer targetBuffer) +{ + if (targetBuffer == LeftBuffer) + return rtLeft.d.isValid() ? &rtLeft : &rt; + else if (targetBuffer == RightBuffer) + return rtRight.d.isValid() ? &rtRight : &rt; + else + Q_UNREACHABLE_RETURN(nullptr); +} + QSize QGles2SwapChain::surfacePixelSize() { Q_ASSERT(m_window); @@ -5795,6 +5826,18 @@ QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor() return new QGles2RenderPassDescriptor(m_rhi); } +void QGles2SwapChain::initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt) +{ + rt->setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget + rt->d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc); + rt->d.pixelSize = pixelSize; + rt->d.dpr = float(m_window->devicePixelRatio()); + rt->d.sampleCount = qBound(1, m_sampleCount, 64); + rt->d.colorAttCount = 1; + rt->d.dsAttCount = m_depthStencil ? 1 : 0; + rt->d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB); +} + bool QGles2SwapChain::createOrResize() { // can be called multiple times due to window resizes @@ -5813,14 +5856,14 @@ bool QGles2SwapChain::createOrResize() m_depthStencil->create(); } - rt.setRenderPassDescriptor(m_renderPassDesc); // for the public getter in QRhiRenderTarget - rt.d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc); - rt.d.pixelSize = pixelSize; - rt.d.dpr = float(m_window->devicePixelRatio()); - rt.d.sampleCount = qBound(1, m_sampleCount, 64); - rt.d.colorAttCount = 1; - rt.d.dsAttCount = m_depthStencil ? 1 : 0; - rt.d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB); + initSwapChainRenderTarget(&rt); + + if (m_window->format().stereo()) { + initSwapChainRenderTarget(&rtLeft); + rtLeft.d.stereoTarget = QRhiSwapChain::LeftBuffer; + initSwapChainRenderTarget(&rtRight); + rtRight.d.stereoTarget = QRhiSwapChain::RightBuffer; + } frameCount = 0; diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index 69164e5ebf..41854f931d 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -23,6 +23,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -173,6 +174,8 @@ struct QGles2RenderTargetData { QGles2RenderTargetData(QRhiImplementation *) { } + bool isValid() const { return rp != nullptr; } + QGles2RenderPassDescriptor *rp = nullptr; QSize pixelSize; float dpr = 1; @@ -181,6 +184,7 @@ struct QGles2RenderTargetData int dsAttCount = 0; bool srgbUpdateAndBlend = false; QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList; + std::optional stereoTarget; }; struct QGles2SwapChainRenderTarget : public QRhiSwapChainRenderTarget @@ -399,6 +403,8 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer GLuint fbo; bool srgb; int colorAttCount; + bool stereo; + QRhiSwapChain::StereoTargetBuffer stereoTarget; } bindFramebuffer; struct { GLenum target; @@ -690,6 +696,7 @@ struct QGles2SwapChain : public QRhiSwapChain QRhiCommandBuffer *currentFrameCommandBuffer() override; QRhiRenderTarget *currentFrameRenderTarget() override; + QRhiRenderTarget *currentFrameRenderTarget(StereoTargetBuffer targetBuffer) override; QSize surfacePixelSize() override; bool isFormatSupported(Format f) override; @@ -697,9 +704,13 @@ struct QGles2SwapChain : public QRhiSwapChain QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; + void initSwapChainRenderTarget(QGles2SwapChainRenderTarget *rt); + QSurface *surface = nullptr; QSize pixelSize; QGles2SwapChainRenderTarget rt; + QGles2SwapChainRenderTarget rtLeft; + QGles2SwapChainRenderTarget rtRight; QGles2CommandBuffer cb; int frameCount = 0; }; -- cgit v1.2.3