From 274321de8ade98434019d56c328c95ab4d36c01d Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Wed, 27 Jul 2016 13:55:24 -0700 Subject: [PATCH] First pass at threaded rendering --- interface/resources/shaders/hmd_ui_glow.frag | 28 +- interface/resources/shaders/hmd_ui_glow.vert | 17 +- interface/src/Application.cpp | 104 ++-- interface/src/Application.h | 3 - interface/src/ui/ApplicationOverlay.cpp | 39 +- interface/src/ui/ApplicationOverlay.h | 3 +- .../Basic2DWindowOpenGLDisplayPlugin.cpp | 4 +- .../Basic2DWindowOpenGLDisplayPlugin.h | 2 +- .../src/display-plugins/NullDisplayPlugin.cpp | 8 +- .../src/display-plugins/NullDisplayPlugin.h | 16 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 457 ++++++++---------- .../src/display-plugins/OpenGLDisplayPlugin.h | 59 +-- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 365 ++++++++------ .../display-plugins/hmd/HmdDisplayPlugin.h | 77 ++- .../stereo/InterleavedStereoDisplayPlugin.cpp | 73 +-- .../stereo/InterleavedStereoDisplayPlugin.h | 3 +- .../stereo/SideBySideStereoDisplayPlugin.cpp | 7 +- .../stereo/StereoDisplayPlugin.cpp | 1 - libraries/gl/src/gl/GLEscrow.h | 2 + libraries/gl/src/gl/OffscreenGLCanvas.cpp | 5 +- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 15 - libraries/gpu-gl/src/gpu/gl/GLBackend.h | 6 +- libraries/gpu-gl/src/gpu/gl/GLBuffer.h | 2 +- .../gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp | 6 +- .../gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp | 6 +- libraries/gpu/src/gpu/Context.cpp | 51 +- libraries/gpu/src/gpu/Context.h | 16 +- libraries/gpu/src/gpu/Forward.h | 7 +- libraries/gpu/src/gpu/Frame.cpp | 53 ++ libraries/gpu/src/gpu/Frame.h | 44 +- libraries/gpu/src/gpu/Resource.cpp | 105 ++-- libraries/gpu/src/gpu/Resource.h | 309 ++++++++---- libraries/gpu/src/gpu/Texture.cpp | 8 - libraries/gpu/src/gpu/Texture.h | 2 - libraries/plugins/CMakeLists.txt | 1 + libraries/plugins/src/plugins/DisplayPlugin.h | 21 +- libraries/render/src/render/Engine.cpp | 3 - libraries/shared/src/shared/NsightHelpers.cpp | 14 + .../src/ui-plugins/PluginContainer.cpp | 6 + .../src/ui-plugins/PluginContainer.h | 2 - plugins/oculus/CMakeLists.txt | 2 + plugins/oculusLegacy/CMakeLists.txt | 3 + plugins/openvr/CMakeLists.txt | 2 + tests/controllers/src/main.cpp | 2 - tests/gpu-test/src/TestWindow.cpp | 1 - tests/render-perf/src/main.cpp | 269 +++++++---- 46 files changed, 1216 insertions(+), 1013 deletions(-) create mode 100644 libraries/gpu/src/gpu/Frame.cpp diff --git a/interface/resources/shaders/hmd_ui_glow.frag b/interface/resources/shaders/hmd_ui_glow.frag index 733f32d718..9270842092 100644 --- a/interface/resources/shaders/hmd_ui_glow.frag +++ b/interface/resources/shaders/hmd_ui_glow.frag @@ -6,18 +6,27 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#version 410 core - uniform sampler2D sampler; -uniform float alpha = 1.0; -uniform vec4 glowPoints = vec4(-1); -uniform vec4 glowColors[2]; -uniform vec2 resolution = vec2(3960.0, 1188.0); -uniform float radius = 0.005; + +struct OverlayData { + mat4 mvp; + vec4 glowPoints; + vec4 glowColors[2]; + vec4 resolutionRadiusAlpha; +}; + +layout(std140) uniform overlayBuffer { + OverlayData overlay; +}; + +vec2 resolution = overlay.resolutionRadiusAlpha.xy; +float radius = overlay.resolutionRadiusAlpha.z; +float alpha = overlay.resolutionRadiusAlpha.w; +vec4 glowPoints = overlay.glowPoints; +vec4 glowColors[2] = overlay.glowColors; in vec3 vPosition; in vec2 vTexCoord; -in vec4 vGlowPoints; out vec4 FragColor; @@ -31,9 +40,10 @@ float easeInOutCubic(float f) { } void main() { + FragColor = texture(sampler, vTexCoord); + vec2 aspect = resolution; aspect /= resolution.x; - FragColor = texture(sampler, vTexCoord); float glowIntensity = 0.0; float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect); diff --git a/interface/resources/shaders/hmd_ui_glow.vert b/interface/resources/shaders/hmd_ui_glow.vert index 5defec085f..54eb062590 100644 --- a/interface/resources/shaders/hmd_ui_glow.vert +++ b/interface/resources/shaders/hmd_ui_glow.vert @@ -6,12 +6,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#version 410 core +struct OverlayData { + mat4 mvp; + vec4 glowPoints; + vec4 glowColors[2]; + vec4 resolutionRadiusAlpha; +}; -uniform mat4 mvp = mat4(1); +layout(std140) uniform overlayBuffer { + OverlayData overlay; +}; -in vec3 Position; -in vec2 TexCoord; +mat4 mvp = overlay.mvp; + +layout(location = 0) in vec3 Position; +layout(location = 3) in vec2 TexCoord; out vec3 vPosition; out vec2 vTexCoord; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 76649739be..901d28c0d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -779,16 +779,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _glWidget->makeCurrent(); _glWidget->initializeGL(); - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->create(_glWidget->context()->contextHandle()); - _chromiumShareContext->makeCurrent(); - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); - - _offscreenContext = new OffscreenGLCanvas(); - _offscreenContext->create(_glWidget->context()->contextHandle()); - _offscreenContext->makeCurrent(); initializeGL(); - _offscreenContext->makeCurrent(); // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -1498,11 +1489,18 @@ void Application::initializeGL() { _isGLInitialized = true; } + _glWidget->makeCurrent(); + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->create(_glWidget->context()->contextHandle()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + + _glWidget->makeCurrent(); gpu::Context::init(); _gpuContext = std::make_shared(); // The gpu context can make child contexts for transfers, so // we need to restore primary rendering context - _offscreenContext->makeCurrent(); + _glWidget->makeCurrent(); initDisplay(); qCDebug(interfaceapp, "Initialized Display."); @@ -1521,7 +1519,8 @@ void Application::initializeGL() { // Needs to happen AFTER the render engine initialization to access its configuration initializeUi(); qCDebug(interfaceapp, "Initialized Offscreen UI."); - _offscreenContext->makeCurrent(); + _glWidget->makeCurrent(); + // call Menu getInstance static method to set up the menu // Needs to happen AFTER the QML UI initialization @@ -1537,8 +1536,13 @@ void Application::initializeGL() { _idleLoopStdev.reset(); + _offscreenContext = new OffscreenGLCanvas(); + _offscreenContext->create(_glWidget->context()->contextHandle()); + _offscreenContext->makeCurrent(); + // update before the first render update(0); + } FrameTimingsScriptingInterface _frameTimingsScriptingInterface; @@ -1555,7 +1559,7 @@ void Application::initializeUi() { auto offscreenUi = DependencyManager::get(); - offscreenUi->create(_offscreenContext->getContext()); + offscreenUi->create(_glWidget->context()->contextHandle()); auto rootContext = offscreenUi->getRootContext(); @@ -1726,17 +1730,7 @@ void Application::paintGL() { PerformanceWarning warn(showWarnings, "Application::paintGL()"); resizeGL(); - // Before anything else, let's sync up the gpuContext with the true glcontext used in case anything happened - { - PerformanceTimer perfTimer("syncCache"); - renderArgs._context->syncCache(); - } - - auto framebufferCache = DependencyManager::get(); - // Final framebuffer that will be handled to the display-plugin - auto finalFramebuffer = framebufferCache->getFramebuffer(); - - _gpuContext->beginFrame(finalFramebuffer, getHMDSensorPose()); + _gpuContext->beginFrame(getHMDSensorPose()); // Reset the gpu::Context Stages // Back to the default framebuffer; gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { @@ -1866,7 +1860,10 @@ void Application::paintGL() { getApplicationCompositor().setFrameInfo(_frameCount, _myCamera.getTransform()); // Primary rendering pass + auto framebufferCache = DependencyManager::get(); const QSize size = framebufferCache->getFrameBufferSize(); + // Final framebuffer that will be handled to the display-plugin + auto finalFramebuffer = framebufferCache->getFramebuffer(); { PROFILE_RANGE(__FUNCTION__ "/mainRender"); @@ -1907,13 +1904,6 @@ void Application::paintGL() { // Apply IPD scaling mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale); eyeOffsets[eye] = eyeOffsetTransform; - - // Tell the plugin what pose we're using to render. In this case we're just using the - // unmodified head pose because the only plugin that cares (the Oculus plugin) uses it - // for rotational timewarp. If we move to support positonal timewarp, we need to - // ensure this contains the full pose composed with the eye offsets. - displayPlugin->setEyeRenderPose(_frameCount, eye, headPose * glm::inverse(eyeOffsetTransform)); - eyeProjections[eye] = displayPlugin->getEyeProjection(eye, baseProjection); }); renderArgs._context->setStereoProjections(eyeProjections); @@ -1921,36 +1911,26 @@ void Application::paintGL() { } renderArgs._blitFramebuffer = finalFramebuffer; displaySide(&renderArgs, _myCamera); - - renderArgs._blitFramebuffer.reset(); - renderArgs._context->enableStereo(false); } - _gpuContext->endFrame(); - - gpu::TexturePointer overlayTexture = _applicationOverlay.acquireOverlay(); - if (overlayTexture) { - displayPlugin->submitOverlayTexture(overlayTexture); - } - - // deliver final composited scene to the display plugin + auto frame = _gpuContext->endFrame(); + frame->frameIndex = _frameCount; + frame->framebuffer = finalFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + frame->overlay = _applicationOverlay.getOverlayTexture(); + // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(__FUNCTION__ "/pluginOutput"); PerformanceTimer perfTimer("pluginOutput"); - - auto finalTexture = finalFramebuffer->getRenderBuffer(0); - Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture)); - _lockedFramebufferMap[finalTexture] = finalFramebuffer; - - Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); - { - PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene"); - PerformanceTimer perfTimer("pluginSubmitScene"); - displayPlugin->submitSceneTexture(_frameCount, finalTexture); - } - Q_ASSERT(isCurrentContext(_offscreenContext->getContext())); + displayPlugin->submitFrame(frame); } + // Reset the framebuffer and stereo state + renderArgs._blitFramebuffer.reset(); + renderArgs._context->enableStereo(false); + { Stats::getInstance()->setRenderDetails(renderArgs._details); } @@ -5405,6 +5385,7 @@ void Application::updateDisplayMode() { DisplayPluginList advanced; DisplayPluginList developer; foreach(auto displayPlugin, displayPlugins) { + displayPlugin->setBackend(_gpuContext->getBackend()); auto grouping = displayPlugin->getGrouping(); switch (grouping) { case Plugin::ADVANCED: @@ -5474,9 +5455,6 @@ void Application::updateDisplayMode() { _displayPlugin->deactivate(); } - // FIXME probably excessive and useless context switching - _offscreenContext->makeCurrent(); - bool active = newDisplayPlugin->activate(); if (!active) { @@ -5621,20 +5599,6 @@ bool Application::makeRenderingContextCurrent() { return _offscreenContext->makeCurrent(); } -void Application::releaseSceneTexture(const gpu::TexturePointer& texture) { - Q_ASSERT(QThread::currentThread() == thread()); - auto& framebufferMap = _lockedFramebufferMap; - Q_ASSERT(framebufferMap.contains(texture)); - auto framebufferPointer = framebufferMap[texture]; - framebufferMap.remove(texture); - auto framebufferCache = DependencyManager::get(); - framebufferCache->releaseFramebuffer(framebufferPointer); -} - -void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) { - _applicationOverlay.releaseOverlay(texture); -} - bool Application::isForeground() const { return _isForeground && !_window->isMinimized(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 8936206790..2d1927b0c6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -112,8 +112,6 @@ public: virtual MainWindow* getPrimaryWindow() override; virtual QOpenGLContext* getPrimaryContext() override; virtual bool makeRenderingContextCurrent() override; - virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override; - virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override; virtual bool isForeground() const override; virtual DisplayPluginPointer getActiveDisplayPlugin() const override; @@ -434,7 +432,6 @@ private: InputPluginList _activeInputPlugins; bool _activatingDisplayPlugin { false }; - QMap _lockedFramebufferMap; QUndoStack _undoStack; UndoStackScriptingInterface _undoStackScriptingInterface; diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 888529da5c..bd25de394c 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -67,7 +67,9 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { // Execute the batch into our framebuffer doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { + PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender"); renderArgs->_batch = &batch; + batch.enableStereo(false); int width = _overlayFramebuffer->getWidth(); int height = _overlayFramebuffer->getHeight(); @@ -246,10 +248,6 @@ static const auto COLOR_FORMAT = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA) static const auto DEFAULT_SAMPLER = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); static const auto DEPTH_FORMAT = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH); -std::mutex _textureGuard; -using Lock = std::unique_lock; -std::queue _availableTextures; - void ApplicationOverlay::buildFramebufferObject() { PROFILE_RANGE(__FUNCTION__); @@ -265,22 +263,6 @@ void ApplicationOverlay::buildFramebufferObject() { _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); } - if (!_overlayFramebuffer->getRenderBuffer(0)) { - gpu::TexturePointer newColorAttachment; - { - Lock lock(_textureGuard); - if (!_availableTextures.empty()) { - newColorAttachment = _availableTextures.front(); - _availableTextures.pop(); - } - } - if (newColorAttachment) { - newColorAttachment->resize2D(width, height, newColorAttachment->getNumSamples()); - _overlayFramebuffer->setRenderBuffer(0, newColorAttachment); - } - } - - // If the overlay framebuffer still has no color attachment, no textures were available for rendering, so build a new one if (!_overlayFramebuffer->getRenderBuffer(0)) { const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP); auto colorBuffer = gpu::TexturePointer(gpu::Texture::create2D(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); @@ -288,20 +270,9 @@ void ApplicationOverlay::buildFramebufferObject() { } } -gpu::TexturePointer ApplicationOverlay::acquireOverlay() { +gpu::TexturePointer ApplicationOverlay::getOverlayTexture() { if (!_overlayFramebuffer) { return gpu::TexturePointer(); } - auto result = _overlayFramebuffer->getRenderBuffer(0); - _overlayFramebuffer->setRenderBuffer(0, gpu::TexturePointer()); - return result; -} - -void ApplicationOverlay::releaseOverlay(gpu::TexturePointer texture) { - if (texture) { - Lock lock(_textureGuard); - _availableTextures.push(texture); - } else { - qWarning() << "Attempted to release null texture"; - } -} + return _overlayFramebuffer->getRenderBuffer(0); +} \ No newline at end of file diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index b77fcc6f89..b7a0529f92 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -26,8 +26,7 @@ public: void renderOverlay(RenderArgs* renderArgs); - gpu::TexturePointer acquireOverlay(); - void releaseOverlay(gpu::TexturePointer pointer); + gpu::TexturePointer getOverlayTexture(); private: void renderStatsAndLogs(RenderArgs* renderArgs); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index f488a805c6..eb8c275123 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -33,9 +33,9 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { +void Basic2DWindowOpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { _wantVsync = true; // always - Parent::submitSceneTexture(frameIndex, sceneTexture); + Parent::submitFrame(newFrame); } void Basic2DWindowOpenGLDisplayPlugin::internalPresent() { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index 6375425243..6321bb6d79 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -24,7 +24,7 @@ public: virtual bool internalActivate() override; - virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; + void submitFrame(const gpu::FramePointer& newFrame) override; virtual void internalPresent() override; diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 4fadbdb94b..05dacea385 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -11,6 +11,7 @@ #include #include +#include const QString NullDisplayPlugin::NAME("NullDisplayPlugin"); @@ -22,12 +23,7 @@ bool NullDisplayPlugin::hasFocus() const { return false; } -void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { - _container->releaseSceneTexture(sceneTexture); -} - -void NullDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) { - _container->releaseOverlayTexture(overlayTexture); +void NullDisplayPlugin::submitFrame(const gpu::FramePointer& resultFramebuffer) { } QImage NullDisplayPlugin::getScreenshot() const { diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index dfa4232a86..198c89ae78 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -11,16 +11,14 @@ class NullDisplayPlugin : public DisplayPlugin { public: + ~NullDisplayPlugin() final {} + const QString& getName() const override { return NAME; } + grouping getGrouping() const override { return DEVELOPER; } - virtual ~NullDisplayPlugin() final {} - virtual const QString& getName() const override { return NAME; } - virtual grouping getGrouping() const override { return DEVELOPER; } - - virtual glm::uvec2 getRecommendedRenderSize() const override; - virtual bool hasFocus() const override; - virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; - virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; - virtual QImage getScreenshot() const override; + glm::uvec2 getRecommendedRenderSize() const override; + bool hasFocus() const override; + void submitFrame(const gpu::FramePointer& newFrame) override; + QImage getScreenshot() const override; private: static const QString NAME; }; diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e0c87fbbed..8968b1e80b 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -8,6 +8,7 @@ #include "OpenGLDisplayPlugin.h" #include +#include #include #include @@ -19,26 +20,43 @@ #if defined(Q_OS_MAC) #include #endif -#include -#include -#include + #include #include -#include -#include +#include + +#include +#include #include #include -#include -#include -#include "CompositorHelper.h" + +#include +#include +#include +#include + +#include +#include +#include #include +#include +#include "CompositorHelper.h" -#if THREADED_PRESENT +const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE( -// FIXME, for display plugins that don't block on something like vsync, just -// cap the present rate at 200 -// const static unsigned int MAX_PRESENT_RATE = 200; +uniform sampler2D colorMap; + +in vec2 varTexCoord0; + +out vec4 outFragColor; + +void main(void) { + outFragColor = vec4(pow(texture(colorMap, varTexCoord0).rgb, vec3(2.2)), 1.0); +} +)SCRIBE"; + +QOpenGLContext* mainContext; class PresentThread : public QThread, public Dependency { using Mutex = std::mutex; @@ -87,8 +105,8 @@ public: virtual void run() override { OpenGLDisplayPlugin* currentPlugin{ nullptr }; - thread()->setPriority(QThread::HighestPriority); Q_ASSERT(_context); + mainContext = _context->contextHandle(); while (!_shutdown) { if (_pendingMainThreadOperation) { { @@ -118,19 +136,13 @@ public: if (newPlugin != currentPlugin) { // Deactivate the old plugin if (currentPlugin != nullptr) { - try { - currentPlugin->uncustomizeContext(); - } catch (const oglplus::Error& error) { - qWarning() << "OpenGL error in uncustomizeContext: " << error.what(); - } + currentPlugin->uncustomizeContext(); + CHECK_GL_ERROR(); } if (newPlugin) { - try { - newPlugin->customizeContext(); - } catch (const oglplus::Error& error) { - qWarning() << "OpenGL error in customizeContext: " << error.what(); - } + newPlugin->customizeContext(); + CHECK_GL_ERROR(); } currentPlugin = newPlugin; _newPluginQueue.pop(); @@ -150,11 +162,8 @@ public: // take the latest texture and present it _context->makeCurrent(); if (isCurrentContext(_context->contextHandle())) { - try { - currentPlugin->present(); - } catch (const oglplus::Error& error) { - qWarning() << "OpenGL error in presentation: " << error.what(); - } + currentPlugin->present(); + CHECK_GL_ERROR(); _context->doneCurrent(); } else { qWarning() << "Makecurrent failed"; @@ -204,27 +213,13 @@ private: QGLContext* _context { nullptr }; }; -#endif - +bool OpenGLDisplayPlugin::isRenderThread() const { + return QThread::currentThread() == DependencyManager::get()->thread(); +} OpenGLDisplayPlugin::OpenGLDisplayPlugin() { - _sceneTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture){ - cleanupForSceneTexture(texture); - _container->releaseSceneTexture(texture); - }); - _overlayTextureEscrow.setRecycler([this](const gpu::TexturePointer& texture) { - _container->releaseOverlayTexture(texture); - }); } -void OpenGLDisplayPlugin::cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture) { - withRenderThreadLock([&] { - Q_ASSERT(_sceneTextureToFrameIndexMap.contains(sceneTexture)); - _sceneTextureToFrameIndexMap.remove(sceneTexture); - }); -} - - bool OpenGLDisplayPlugin::activate() { if (!_cursorsData.size()) { auto& cursorManager = Cursor::Manager::instance(); @@ -244,7 +239,6 @@ bool OpenGLDisplayPlugin::activate() { } _vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported(); -#if THREADED_PRESENT // Start the present thread if necessary QSharedPointer presentThread; if (DependencyManager::isSet()) { @@ -259,7 +253,6 @@ bool OpenGLDisplayPlugin::activate() { presentThread->start(); } _presentThread = presentThread.data(); -#endif // Child classes may override this in order to do things like initialize // libraries, etc @@ -267,17 +260,10 @@ bool OpenGLDisplayPlugin::activate() { return false; } -#if THREADED_PRESENT // This should not return until the new context has been customized // and the old context (if any) has been uncustomized presentThread->setNewDisplayPlugin(this); -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - customizeContext(); - _container->makeRenderingContextCurrent(); -#endif auto compositorHelper = DependencyManager::get(); connect(compositorHelper.data(), &CompositorHelper::alphaChanged, [this] { @@ -300,16 +286,9 @@ void OpenGLDisplayPlugin::deactivate() { auto compositorHelper = DependencyManager::get(); disconnect(compositorHelper.data()); -#if THREADED_PRESENT auto presentThread = DependencyManager::get(); // Does not return until the GL transition has completeed presentThread->setNewDisplayPlugin(nullptr); -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - uncustomizeContext(); - _container->makeRenderingContextCurrent(); -#endif internalDeactivate(); _container->showDisplayPluginsTools(false); @@ -325,56 +304,74 @@ void OpenGLDisplayPlugin::deactivate() { void OpenGLDisplayPlugin::customizeContext() { -#if THREADED_PRESENT auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); -#endif enableVsync(); for (auto& cursorValue : _cursorsData) { auto& cursorData = cursorValue.second; if (!cursorData.texture) { - const auto& image = cursorData.image; - glGenTextures(1, &cursorData.texture); - glBindTexture(GL_TEXTURE_2D, cursorData.texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glGenerateMipmap(GL_TEXTURE_2D); + auto image = cursorData.image; + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + if ((image.width() > 0) && (image.height() > 0)) { + + cursorData.texture.reset( + gpu::Texture::create2D( + gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), + image.width(), image.height(), + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); + cursorData.texture->setUsage(usage.build()); + cursorData.texture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.byteCount(), image.constBits()); + cursorData.texture->autoGenerateMips(-1); + } } - glBindTexture(GL_TEXTURE_2D, 0); } - using namespace oglplus; - Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); - Context::Disable(Capability::Blend); - Context::Disable(Capability::DepthTest); - Context::Disable(Capability::CullFace); - - _program = loadDefaultShader(); - - auto uniforms = _program->ActiveUniforms(); - while (!uniforms.Empty()) { - auto uniform = uniforms.Front(); - if (uniform.Name() == "mvp") { - _mvpUniform = uniform.Index(); + if (!_presentPipeline) { + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + _presentPipeline = gpu::Pipeline::create(program, state); } - if (uniform.Name() == "alpha") { - _alphaUniform = uniform.Index(); + + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _overlayPipeline = gpu::Pipeline::create(program, state); + } + + { + auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS(); + auto ps = gpu::StandardShaderLib::getDrawTexturePS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + _cursorPipeline = gpu::Pipeline::create(program, state); } - uniforms.Next(); } - - _plane = loadPlane(_program); - _compositeFramebuffer = std::make_shared(); - _compositeFramebuffer->Init(getRecommendedRenderSize()); } void OpenGLDisplayPlugin::uncustomizeContext() { - _compositeFramebuffer.reset(); - _program.reset(); - _plane.reset(); + _presentPipeline.reset(); } @@ -420,172 +417,142 @@ bool OpenGLDisplayPlugin::eventFilter(QObject* receiver, QEvent* event) { return false; } -void OpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) { + +void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) { if (_lockCurrentTexture) { - _container->releaseSceneTexture(sceneTexture); return; } - withRenderThreadLock([&] { - _sceneTextureToFrameIndexMap[sceneTexture] = frameIndex; + withNonPresentThreadLock([&] { + _newFrameQueue.push(newFrame); }); - - // Submit it to the presentation thread via escrow - _sceneTextureEscrow.submit(sceneTexture); - -#if THREADED_PRESENT -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - present(); - _container->makeRenderingContextCurrent(); -#endif -} - -void OpenGLDisplayPlugin::submitOverlayTexture(const gpu::TexturePointer& overlayTexture) { - // Submit it to the presentation thread via escrow - _overlayTextureEscrow.submit(overlayTexture); -} - -void OpenGLDisplayPlugin::updateTextures() { - // FIXME intrduce a GPU wait instead of a CPU/GPU sync point? -#if THREADED_PRESENT - if (_sceneTextureEscrow.fetchSignaledAndRelease(_currentSceneTexture)) { -#else - if (_sceneTextureEscrow.fetchAndReleaseWithGpuWait(_currentSceneTexture)) { -#endif - updateFrameData(); - _newFrameRate.increment(); - } - - _overlayTextureEscrow.fetchSignaledAndRelease(_currentOverlayTexture); } void OpenGLDisplayPlugin::updateFrameData() { withPresentThreadLock([&] { - auto previousFrameIndex = _currentPresentFrameIndex; - _currentPresentFrameIndex = _sceneTextureToFrameIndexMap[_currentSceneTexture]; - auto skippedCount = (_currentPresentFrameIndex - previousFrameIndex) - 1; + gpu::FramePointer oldFrame = _currentFrame; + uint32_t skippedCount = 0; + while (!_newFrameQueue.empty()) { + _currentFrame = _newFrameQueue.front(); + _currentFrame->preRender(); + _newFrameQueue.pop(); + + _newFrameQueue = std::queue(); + if (_currentFrame && oldFrame) { + skippedCount = (_currentFrame->frameIndex - oldFrame->frameIndex) - 1; + } + } _droppedFrameRate.increment(skippedCount); }); } void OpenGLDisplayPlugin::compositeOverlay() { - using namespace oglplus; - - auto compositorHelper = DependencyManager::get(); - - useProgram(_program); - // set the alpha - Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha); - // check the alpha - // Overlay draw + gpu::Batch batch; + batch.enableStereo(false); + batch.setFramebuffer(_currentFrame->framebuffer); + batch.setPipeline(_overlayPipeline); + batch.setResourceTexture(0, _currentFrame->overlay); if (isStereo()) { - Uniform(*_program, _mvpUniform).Set(mat4()); for_each_eye([&](Eye eye) { - eyeViewport(eye); - drawUnitQuad(); + batch.setViewportTransform(eyeViewport(eye)); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); } else { - // Overlay draw - Uniform(*_program, _mvpUniform).Set(mat4()); - drawUnitQuad(); + batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize())); + batch.draw(gpu::TRIANGLE_STRIP, 4); } - // restore the alpha - Uniform(*_program, _alphaUniform).Set(1.0); + _backend->render(batch); } void OpenGLDisplayPlugin::compositePointer() { - using namespace oglplus; - auto compositorHelper = DependencyManager::get(); - - useProgram(_program); - // set the alpha - Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha); - Uniform(*_program, _mvpUniform).Set(compositorHelper->getReticleTransform(glm::mat4())); + auto& cursorManager = Cursor::Manager::instance(); + const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; + auto cursorTransform = DependencyManager::get()->getReticleTransform(glm::mat4()); + gpu::Batch batch; + batch.enableStereo(false); + batch.setProjectionTransform(mat4()); + batch.setFramebuffer(_currentFrame->framebuffer); + batch.setPipeline(_cursorPipeline); + batch.setResourceTexture(0, cursorData.texture); + batch.setViewTransform(Transform()); + batch.setModelTransform(cursorTransform); if (isStereo()) { for_each_eye([&](Eye eye) { - eyeViewport(eye); - drawUnitQuad(); + batch.setViewportTransform(eyeViewport(eye)); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); } else { - drawUnitQuad(); + batch.setViewportTransform(ivec4(uvec2(0), _currentFrame->framebuffer->getSize())); + batch.draw(gpu::TRIANGLE_STRIP, 4); } - Uniform(*_program, _mvpUniform).Set(mat4()); - // restore the alpha - Uniform(*_program, _alphaUniform).Set(1.0); + _backend->render(batch); } void OpenGLDisplayPlugin::compositeScene() { - using namespace oglplus; - useProgram(_program); - Uniform(*_program, _mvpUniform).Set(mat4()); - drawUnitQuad(); } void OpenGLDisplayPlugin::compositeLayers() { - using namespace oglplus; - auto targetRenderSize = getRecommendedRenderSize(); - if (!_compositeFramebuffer || _compositeFramebuffer->size != targetRenderSize) { - _compositeFramebuffer = std::make_shared(); - _compositeFramebuffer->Init(targetRenderSize); - } - _compositeFramebuffer->Bound(Framebuffer::Target::Draw, [&] { - Context::Viewport(targetRenderSize.x, targetRenderSize.y); - auto sceneTextureId = getSceneTextureId(); - auto overlayTextureId = getOverlayTextureId(); - glBindTexture(GL_TEXTURE_2D, sceneTextureId); + { + PROFILE_RANGE_EX("compositeScene", 0xff0077ff, (uint64_t)presentCount()) compositeScene(); - if (overlayTextureId) { - glBindTexture(GL_TEXTURE_2D, overlayTextureId); - Context::Enable(Capability::Blend); - Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha); - compositeOverlay(); - - auto compositorHelper = DependencyManager::get(); - if (compositorHelper->getReticleVisible()) { - auto& cursorManager = Cursor::Manager::instance(); - const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; - glBindTexture(GL_TEXTURE_2D, cursorData.texture); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, overlayTextureId); - compositePointer(); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - } - glBindTexture(GL_TEXTURE_2D, 0); - Context::Disable(Capability::Blend); - } + } + { + PROFILE_RANGE_EX("compositeOverlay", 0xff0077ff, (uint64_t)presentCount()) + compositeOverlay(); + } + auto compositorHelper = DependencyManager::get(); + if (compositorHelper->getReticleVisible()) { + PROFILE_RANGE_EX("compositePointer", 0xff0077ff, (uint64_t)presentCount()) + compositePointer(); + } + { + PROFILE_RANGE_EX("compositeExtra", 0xff0077ff, (uint64_t)presentCount()) compositeExtra(); - }); + } } void OpenGLDisplayPlugin::internalPresent() { - using namespace oglplus; - const uvec2& srcSize = _compositeFramebuffer->size; - uvec2 dstSize = getSurfacePixels(); - _compositeFramebuffer->Bound(FramebufferTarget::Read, [&] { - Context::BlitFramebuffer( - 0, 0, srcSize.x, srcSize.y, - 0, 0, dstSize.x, dstSize.y, - BufferSelectBit::ColorBuffer, BlitFilter::Nearest); - }); + gpu::Batch presentBatch; + presentBatch.enableStereo(false); + presentBatch.setViewTransform(Transform()); + presentBatch.setFramebuffer(gpu::FramebufferPointer()); + presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); + presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + presentBatch.setPipeline(_presentPipeline); + presentBatch.draw(gpu::TRIANGLE_STRIP, 4); + _backend->render(presentBatch); swapBuffers(); } void OpenGLDisplayPlugin::present() { + PROFILE_RANGE_EX(__FUNCTION__, 0xffffff00, (uint64_t)presentCount()) incrementPresentCount(); - PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) + updateFrameData(); + if (_currentFrame) { + _backend->syncCache(); + _backend->setStereoState(_currentFrame->stereoState); + { + PROFILE_RANGE_EX("execute", 0xff00ff00, (uint64_t)presentCount()) + // Execute the frame rendering commands + for (auto& batch : _currentFrame->batches) { + _backend->render(batch); + } + + } - updateTextures(); - if (_currentSceneTexture) { // Write all layers to a local framebuffer - compositeLayers(); + { + PROFILE_RANGE_EX("composite", 0xff00ffff, (uint64_t)presentCount()) + compositeLayers(); + } + // Take the composite framebuffer and send it to the output device - internalPresent(); + { + PROFILE_RANGE_EX("internalPresent", 0xff00ffff, (uint64_t)presentCount()) + internalPresent(); + } _presentRate.increment(); - _activeProgram.reset(); } } @@ -595,7 +562,7 @@ float OpenGLDisplayPlugin::newFramePresentRate() const { float OpenGLDisplayPlugin::droppedFrameRate() const { float result; - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { result = _droppedFrameRate.rate(); }); return result; @@ -605,11 +572,6 @@ float OpenGLDisplayPlugin::presentRate() const { return _presentRate.rate(); } -void OpenGLDisplayPlugin::drawUnitQuad() { - useProgram(_program); - _plane->Use(); - _plane->Draw(); -} void OpenGLDisplayPlugin::enableVsync(bool enable) { if (!_vsyncSupported) { @@ -626,6 +588,7 @@ void OpenGLDisplayPlugin::enableVsync(bool enable) { #endif } + bool OpenGLDisplayPlugin::isVsyncEnabled() { if (!_vsyncSupported) { return true; @@ -648,19 +611,13 @@ void OpenGLDisplayPlugin::swapBuffers() { } void OpenGLDisplayPlugin::withMainThreadContext(std::function f) const { -#if THREADED_PRESENT static auto presentThread = DependencyManager::get(); presentThread->withMainThreadContext(f); _container->makeRenderingContextCurrent(); -#else - static auto widget = _container->getPrimaryWidget(); - widget->makeCurrent(); - f(); - _container->makeRenderingContextCurrent(); -#endif } QImage OpenGLDisplayPlugin::getScreenshot() const { +#if 0 using namespace oglplus; QImage screenshot(_compositeFramebuffer->size.x, _compositeFramebuffer->size.y, QImage::Format_RGBA8888); withMainThreadContext([&] { @@ -668,32 +625,9 @@ QImage OpenGLDisplayPlugin::getScreenshot() const { Context::ReadPixels(0, 0, _compositeFramebuffer->size.x, _compositeFramebuffer->size.y, enums::PixelDataFormat::RGBA, enums::PixelDataType::UnsignedByte, screenshot.bits()); }); return screenshot.mirrored(false, true); -} - -uint32_t OpenGLDisplayPlugin::getSceneTextureId() const { - if (!_currentSceneTexture) { - return 0; - } - - return _currentSceneTexture->getHardwareId(); -} - -uint32_t OpenGLDisplayPlugin::getOverlayTextureId() const { - if (!_currentOverlayTexture) { - return 0; - } - return _currentOverlayTexture->getHardwareId(); -} - -void OpenGLDisplayPlugin::eyeViewport(Eye eye) const { - using namespace oglplus; - uvec2 vpSize = _compositeFramebuffer->size; - vpSize.x /= 2; - uvec2 vpPos; - if (eye == Eye::Right) { - vpPos.x = vpSize.x; - } - Context::Viewport(vpPos.x, vpPos.y, vpSize.x, vpSize.y); +#else + return QImage(); +#endif } glm::uvec2 OpenGLDisplayPlugin::getSurfacePixels() const { @@ -719,14 +653,7 @@ bool OpenGLDisplayPlugin::hasFocus() const { return window ? window->hasFocus() : false; } -void OpenGLDisplayPlugin::useProgram(const ProgramPtr& program) { - if (_activeProgram != program) { - program->Bind(); - _activeProgram = program; - } -} - -void OpenGLDisplayPlugin::assertIsRenderThread() const { +void OpenGLDisplayPlugin::assertNotPresentThread() const { Q_ASSERT(QThread::currentThread() != _presentThread); } @@ -735,8 +662,18 @@ void OpenGLDisplayPlugin::assertIsPresentThread() const { } bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) { - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { _compositeOverlayAlpha = _overlayAlpha; }); return Parent::beginFrameRender(frameIndex); } + +ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const { + uvec2 vpSize = _currentFrame->framebuffer->getSize(); + vpSize.x /= 2; + uvec2 vpPos; + if (eye == Eye::Right) { + vpPos.x = vpSize.x; + } + return ivec4(vpPos, vpSize); +} diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 068b236289..5d8f55ebbd 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -11,18 +11,16 @@ #include #include +#include #include #include #include #include -#include #include #include -#define THREADED_PRESENT 1 - class OpenGLDisplayPlugin : public DisplayPlugin { Q_OBJECT Q_PROPERTY(float overlayAlpha MEMBER _overlayAlpha) @@ -39,13 +37,12 @@ public: // between the main thread and the presentation thread bool activate() override final; void deactivate() override final; + bool isRenderThread() const override final; bool eventFilter(QObject* receiver, QEvent* event) override; bool isDisplayVisible() const override { return true; } - - void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) override; - void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) override; + void submitFrame(const gpu::FramePointer& newFrame) override; glm::uvec2 getRecommendedRenderSize() const override { return getSurfacePixels(); @@ -65,11 +62,7 @@ public: bool beginFrameRender(uint32_t frameIndex) override; protected: -#if THREADED_PRESENT friend class PresentThread; -#endif - uint32_t getSceneTextureId() const; - uint32_t getOverlayTextureId() const; glm::uvec2 getSurfaceSize() const; glm::uvec2 getSurfacePixels() const; @@ -93,39 +86,29 @@ protected: // Returns true on successful activation virtual bool internalActivate() { return true; } virtual void internalDeactivate() {} - virtual void cleanupForSceneTexture(const gpu::TexturePointer& sceneTexture); + // Plugin specific functionality to send the composed scene to the output window or device virtual void internalPresent(); - void withMainThreadContext(std::function f) const; - - void useProgram(const ProgramPtr& program); - void present(); - void updateTextures(); - void drawUnitQuad(); - void swapBuffers(); - void eyeViewport(Eye eye) const; - virtual void updateFrameData(); - QThread* _presentThread{ nullptr }; - ProgramPtr _program; - int32_t _mvpUniform { -1 }; - int32_t _alphaUniform { -1 }; - ShapeWrapperPtr _plane; + void withMainThreadContext(std::function f) const; + void present(); + void swapBuffers(); + ivec4 eyeViewport(Eye eye) const; + + QThread* _presentThread{ nullptr }; + std::queue _newFrameQueue; RateCounter<> _droppedFrameRate; RateCounter<> _newFrameRate; RateCounter<> _presentRate; - QMap _sceneTextureToFrameIndexMap; - uint32_t _currentPresentFrameIndex { 0 }; - float _compositeOverlayAlpha{ 1.0f }; + gpu::FramePointer _currentFrame; + gpu::PipelinePointer _overlayPipeline; + gpu::PipelinePointer _presentPipeline; + gpu::PipelinePointer _cursorPipeline; + float _compositeOverlayAlpha { 1.0f }; - gpu::TexturePointer _currentSceneTexture; - gpu::TexturePointer _currentOverlayTexture; - - TextureEscrow _sceneTextureEscrow; - TextureEscrow _overlayTextureEscrow; bool _vsyncSupported { false }; @@ -133,14 +116,13 @@ protected: QImage image; vec2 hotSpot; uvec2 size; - uint32_t texture { 0 }; + gpu::TexturePointer texture; }; std::map _cursorsData; - BasicFramebufferWrapperPtr _compositeFramebuffer; bool _lockCurrentTexture { false }; - void assertIsRenderThread() const; + void assertNotPresentThread() const; void assertIsPresentThread() const; template @@ -151,8 +133,8 @@ protected: } template - void withRenderThreadLock(F f) const { - assertIsRenderThread(); + void withNonPresentThreadLock(F f) const { + assertNotPresentThread(); Lock lock(_presentMutex); f(); } @@ -161,7 +143,6 @@ private: // Any resource shared by the main thread and the presentation thread must // be serialized through this mutex mutable Mutex _presentMutex; - ProgramPtr _activeProgram; float _overlayAlpha{ 1.0f }; }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 306bc26a17..ddd392d945 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -22,9 +22,10 @@ #include #include #include +#include -#include -#include +#include +#include #include @@ -39,6 +40,15 @@ static const bool DEFAULT_MONO_VIEW = true; static const int NUMBER_OF_HANDS = 2; static const glm::mat4 IDENTITY_MATRIX; +//#define LIVE_SHADER_RELOAD 1 + +static QString readFile(const QString& filename) { + QFile file(filename); + file.open(QFile::Text | QFile::ReadOnly); + QString result; + result.append(QTextStream(&file).readAll()); + return result; +} glm::uvec2 HmdDisplayPlugin::getRecommendedUiSize() const { return CompositorHelper::VIRTUAL_SCREEN_SIZE; @@ -68,6 +78,7 @@ bool HmdDisplayPlugin::internalActivate() { _eyeInverseProjections[eye] = glm::inverse(_eyeProjections[eye]); }); +#if 0 if (_previewTextureID == 0) { QImage previewTexture(PathUtils::resourcesPath() + "images/preview.png"); if (!previewTexture.isNull()) { @@ -83,18 +94,138 @@ bool HmdDisplayPlugin::internalActivate() { _firstPreview = true; } } +#endif return Parent::internalActivate(); } void HmdDisplayPlugin::internalDeactivate() { - if (_previewTextureID != 0) { - glDeleteTextures(1, &_previewTextureID); - _previewTextureID = 0; - } Parent::internalDeactivate(); } +extern glm::vec3 getPoint(float yaw, float pitch); + +void HmdDisplayPlugin::OverlayRender::build() { + auto geometryCache = DependencyManager::get(); + vertices = std::make_shared(); + indices = std::make_shared(); + + //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm + + static const float fov = CompositorHelper::VIRTUAL_UI_TARGET_FOV.y; + static const float aspectRatio = CompositorHelper::VIRTUAL_UI_ASPECT_RATIO; + static const uint16_t stacks = 128; + static const uint16_t slices = 64; + + Vertex vertex; + + // Compute vertices positions and texture UV coordinate + // Create and write to buffer + for (int i = 0; i < stacks; i++) { + vertex.uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f + // abs(theta) <= fov / 2.0f + float pitch = -fov * (vertex.uv.y - 0.5f); + for (int j = 0; j < slices; j++) { + vertex.uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f + // abs(phi) <= fov * aspectRatio / 2.0f + float yaw = -fov * aspectRatio * (vertex.uv.x - 0.5f); + vertex.pos = getPoint(yaw, pitch); + vertices->append(sizeof(Vertex), (gpu::Byte*)&vertex); + } + } + + // Compute number of indices needed + static const int VERTEX_PER_TRANGLE = 3; + static const int TRIANGLE_PER_RECTANGLE = 2; + int numberOfRectangles = (slices - 1) * (stacks - 1); + indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; + + // Compute indices order + std::vector indices; + for (int i = 0; i < stacks - 1; i++) { + for (int j = 0; j < slices - 1; j++) { + GLushort bottomLeftIndex = i * slices + j; + GLushort bottomRightIndex = bottomLeftIndex + 1; + GLushort topLeftIndex = bottomLeftIndex + slices; + GLushort topRightIndex = topLeftIndex + 1; + // FIXME make a z-order curve for better vertex cache locality + indices.push_back(topLeftIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(topRightIndex); + + indices.push_back(topRightIndex); + indices.push_back(bottomLeftIndex); + indices.push_back(bottomRightIndex); + } + } + this->indices->append(indices); + format = std::make_shared(); // 1 for everyone + format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0); + format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)); + uniformBuffers[0] = std::make_shared(sizeof(Uniforms), nullptr); + uniformBuffers[1] = std::make_shared(sizeof(Uniforms), nullptr); + updatePipeline(); +} + +void HmdDisplayPlugin::OverlayRender::updatePipeline() { + static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert"; + static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag"; + +#if LIVE_SHADER_RELOAD + static qint64 vsBuiltAge = 0; + static qint64 fsBuiltAge = 0; + QFileInfo vsInfo(vsFile); + QFileInfo fsInfo(fsFile); + auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); + auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); + if (!pipeline || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { + vsBuiltAge = vsAge; + fsBuiltAge = fsAge; +#else + if (!pipeline) { +#endif + QString vsSource = readFile(vsFile); + QString fsSource = readFile(fsFile); + auto vs = gpu::Shader::createVertex(vsSource.toLocal8Bit().toStdString()); + auto ps = gpu::Shader::createPixel(fsSource.toLocal8Bit().toStdString()); + auto program = gpu::Shader::createProgram(vs, ps); + gpu::gl::GLBackend::makeProgram(*program, gpu::Shader::BindingSet()); + this->uniformsLocation = program->getBuffers().findLocation("overlayBuffer"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + pipeline = gpu::Pipeline::create(program, state); + } + } + +void HmdDisplayPlugin::OverlayRender::render() { + for_each_eye([&](Eye eye){ + uniforms.mvp = mvps[eye]; + uniformBuffers[eye]->setSubData(0, uniforms); + }); + gpu::Batch batch; + batch.enableStereo(false); + batch.setResourceTexture(0, plugin._currentFrame->overlay); + batch.setPipeline(pipeline); + batch.setInputFormat(format); + gpu::BufferView posView(vertices, VERTEX_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::POSITION)._element); + gpu::BufferView uvView(vertices, TEXTURE_OFFSET, vertices->getSize(), VERTEX_STRIDE, format->getAttributes().at(gpu::Stream::TEXCOORD)._element); + batch.setInputBuffer(gpu::Stream::POSITION, posView); + batch.setInputBuffer(gpu::Stream::TEXCOORD, uvView); + batch.setIndexBuffer(gpu::UINT16, indices, 0); + for_each_eye([&](Eye eye){ + batch.setUniformBuffer(uniformsLocation, uniformBuffers[eye]); + batch.setViewportTransform(plugin.eyeViewport(eye)); + batch.drawIndexed(gpu::TRIANGLES, indexCount); + }); + // FIXME use stereo information input to set both MVPs in the uniforms + plugin._backend->render(batch); +} + void HmdDisplayPlugin::customizeContext() { Parent::customizeContext(); // Only enable mirroring if we know vsync is disabled @@ -103,32 +234,15 @@ void HmdDisplayPlugin::customizeContext() { enableVsync(false); #endif _enablePreview = !isVsyncEnabled(); - _sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO); - using namespace oglplus; - if (!_enablePreview) { - const std::string version("#version 410 core\n"); - compileProgram(_previewProgram, version + DrawUnitQuadTexcoord_vert, version + DrawTexture_frag); - _previewUniforms.previewTexture = Uniform(*_previewProgram, "colorMap").Location(); - } - + _overlay.build(); +#if 0 updateReprojectionProgram(); - updateOverlayProgram(); -#ifdef HMD_HAND_LASER_SUPPORT updateLaserProgram(); _laserGeometry = loadLaser(_laserProgram); #endif } -//#define LIVE_SHADER_RELOAD 1 - -static QString readFile(const QString& filename) { - QFile file(filename); - file.open(QFile::Text | QFile::ReadOnly); - QString result; - result.append(QTextStream(&file).readAll()); - return result; -} - +#if 0 void HmdDisplayPlugin::updateReprojectionProgram() { static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.vert"; static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_reproject.frag"; @@ -161,11 +275,11 @@ void HmdDisplayPlugin::updateReprojectionProgram() { qWarning() << "Error building reprojection shader " << error.what(); } } - } +#endif -#ifdef HMD_HAND_LASER_SUPPORT void HmdDisplayPlugin::updateLaserProgram() { +#if 0 static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.vert"; static const QString gsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.geom"; static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_hand_lasers.frag"; @@ -204,56 +318,16 @@ void HmdDisplayPlugin::updateLaserProgram() { qWarning() << "Error building hand laser composite shader " << error.what(); } } -} #endif - -void HmdDisplayPlugin::updateOverlayProgram() { - static const QString vsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.vert"; - static const QString fsFile = PathUtils::resourcesPath() + "/shaders/hmd_ui_glow.frag"; - -#if LIVE_SHADER_RELOAD - static qint64 vsBuiltAge = 0; - static qint64 fsBuiltAge = 0; - QFileInfo vsInfo(vsFile); - QFileInfo fsInfo(fsFile); - auto vsAge = vsInfo.lastModified().toMSecsSinceEpoch(); - auto fsAge = fsInfo.lastModified().toMSecsSinceEpoch(); - if (!_overlayProgram || vsAge > vsBuiltAge || fsAge > fsBuiltAge) { - vsBuiltAge = vsAge; - fsBuiltAge = fsAge; -#else - if (!_overlayProgram) { -#endif - QString vsSource = readFile(vsFile); - QString fsSource = readFile(fsFile); - ProgramPtr program; - try { - compileProgram(program, vsSource.toLocal8Bit().toStdString(), fsSource.toLocal8Bit().toStdString()); - if (program) { - using namespace oglplus; - _overlayUniforms.mvp = Uniform(*program, "mvp").Location(); - _overlayUniforms.alpha = Uniform(*program, "alpha").Location(); - _overlayUniforms.glowColors = Uniform(*program, "glowColors").Location(); - _overlayUniforms.glowPoints = Uniform(*program, "glowPoints").Location(); - _overlayUniforms.resolution = Uniform(*program, "resolution").Location(); - _overlayUniforms.radius = Uniform(*program, "radius").Location(); - _overlayProgram = program; - useProgram(_overlayProgram); - Uniform(*_overlayProgram, _overlayUniforms.resolution).Set(CompositorHelper::VIRTUAL_SCREEN_SIZE); - } - } catch (std::runtime_error& error) { - qWarning() << "Error building overlay composite shader " << error.what(); - } - } } void HmdDisplayPlugin::uncustomizeContext() { +#if 0 _overlayProgram.reset(); _sphereSection.reset(); _compositeFramebuffer.reset(); _previewProgram.reset(); _reprojectionProgram.reset(); -#ifdef HMD_HAND_LASER_SUPPORT _laserProgram.reset(); _laserGeometry.reset(); #endif @@ -277,6 +351,7 @@ void HmdDisplayPlugin::compositeScene() { #ifdef DEBUG_REPROJECTION_SHADER _reprojectionProgram = getReprojectionProgram(); #endif +#if 0 useProgram(_reprojectionProgram); using namespace oglplus; @@ -290,20 +365,23 @@ void HmdDisplayPlugin::compositeScene() { glUniformMatrix4fv(_reprojectionUniforms.projectionMatrix, 2, GL_FALSE, &(_eyeProjections[0][0][0])); _plane->UseInProgram(*_reprojectionProgram); _plane->Draw(); +#endif } void HmdDisplayPlugin::compositeOverlay() { - using namespace oglplus; + if (!_currentFrame) { + return; + } + auto compositorHelper = DependencyManager::get(); glm::mat4 modelMat = compositorHelper->getModelTransform().getMatrix(); - withPresentThreadLock([&] { _presentHandLasers = _handLasers; _presentHandPoses = _handPoses; _presentUiModelTransform = _uiModelTransform; }); - std::array handGlowPoints { { vec2(-1), vec2(-1) } }; + std::array handGlowPoints{ { vec2(-1), vec2(-1) } }; // compute the glow point interesections for (int i = 0; i < NUMBER_OF_HANDS; ++i) { if (_presentHandPoses[i] == IDENTITY_MATRIX) { @@ -353,65 +431,49 @@ void HmdDisplayPlugin::compositeOverlay() { handGlowPoints[i] = yawPitch; } - updateOverlayProgram(); - if (!_overlayProgram) { + if (!_currentFrame->overlay) { return; } - useProgram(_overlayProgram); + for_each_eye([&](Eye eye){ + auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat; + _overlay.mvps[eye] = _eyeProjections[eye] * modelView; + }); + // Setup the uniforms { - if (_overlayUniforms.alpha >= 0) { - Uniform(*_overlayProgram, _overlayUniforms.alpha).Set(_compositeOverlayAlpha); - } - if (_overlayUniforms.glowPoints >= 0) { - vec4 glowPoints(handGlowPoints[0], handGlowPoints[1]); - Uniform(*_overlayProgram, _overlayUniforms.glowPoints).Set(glowPoints); - } - if (_overlayUniforms.glowColors >= 0) { - std::array glowColors; - glowColors[0] = _presentHandLasers[0].color; - glowColors[1] = _presentHandLasers[1].color; - glProgramUniform4fv(GetName(*_overlayProgram), _overlayUniforms.glowColors, 2, &glowColors[0].r); - } + _overlay.uniforms.alpha = _compositeOverlayAlpha; + _overlay.uniforms.glowPoints = vec4(handGlowPoints[0], handGlowPoints[1]); + _overlay.uniforms.glowColors[0] = _presentHandLasers[0].color; + _overlay.uniforms.glowColors[1] = _presentHandLasers[1].color; } - - _sphereSection->Use(); - for_each_eye([&](Eye eye) { - eyeViewport(eye); - auto modelView = glm::inverse(_currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye)) * modelMat; - auto mvp = _eyeProjections[eye] * modelView; - Uniform(*_overlayProgram, _overlayUniforms.mvp).Set(mvp); - _sphereSection->Draw(); - }); + _overlay.render(); } void HmdDisplayPlugin::compositePointer() { - using namespace oglplus; - + auto& cursorManager = Cursor::Manager::instance(); + const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()]; auto compositorHelper = DependencyManager::get(); - - useProgram(_program); - // set the alpha - Uniform(*_program, _alphaUniform).Set(_compositeOverlayAlpha); - - // Mouse pointer - _plane->Use(); // Reconstruct the headpose from the eye poses auto headPosition = vec3(_currentPresentFrameInfo.presentPose[3]); + gpu::Batch batch; + batch.enableStereo(false); + batch.setProjectionTransform(mat4()); + batch.setFramebuffer(_currentFrame->framebuffer); + batch.setPipeline(_cursorPipeline); + batch.setResourceTexture(0, cursorData.texture); + batch.setViewTransform(Transform()); for_each_eye([&](Eye eye) { - eyeViewport(eye); auto eyePose = _currentPresentFrameInfo.presentPose * getEyeToHeadTransform(eye); auto reticleTransform = compositorHelper->getReticleTransform(eyePose, headPosition); - auto mvp = _eyeProjections[eye] * reticleTransform; - Uniform(*_program, _mvpUniform).Set(mvp); - _plane->Draw(); + batch.setViewportTransform(eyeViewport(eye)); + batch.setModelTransform(reticleTransform); + batch.setProjectionTransform(_eyeProjections[eye]); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); - // restore the alpha - Uniform(*_program, _alphaUniform).Set(1.0); + _backend->render(batch); } - void HmdDisplayPlugin::internalPresent() { PROFILE_RANGE_EX(__FUNCTION__, 0xff00ff00, (uint64_t)presentCount()) @@ -445,58 +507,45 @@ void HmdDisplayPlugin::internalPresent() { targetViewportPosition.y = (windowSize.y - targetViewportSize.y) / 2; } + if (_enablePreview) { - using namespace oglplus; - Context::Clear().ColorBuffer(); - auto sourceSize = _compositeFramebuffer->size; - if (_monoPreview) { - sourceSize.x /= 2; - } - _compositeFramebuffer->Bound(Framebuffer::Target::Read, [&] { - Context::BlitFramebuffer( - 0, 0, sourceSize.x, sourceSize.y, - targetViewportPosition.x, targetViewportPosition.y, - targetViewportPosition.x + targetViewportSize.x, targetViewportPosition.y + targetViewportSize.y, - BufferSelectBit::ColorBuffer, BlitFilter::Nearest); - }); - swapBuffers(); - } else if (_firstPreview || windowSize != _prevWindowSize || devicePixelRatio != _prevDevicePixelRatio) { - useProgram(_previewProgram); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(targetViewportPosition.x, targetViewportPosition.y, targetViewportSize.x, targetViewportSize.y); - glUniform1i(_previewUniforms.previewTexture, 0); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, _previewTextureID); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - swapBuffers(); - _firstPreview = false; - _prevWindowSize = windowSize; - _prevDevicePixelRatio = devicePixelRatio; - } + Parent::internalPresent(); + //gpu::Batch presentBatch; + //presentBatch.enableStereo(false); + //presentBatch.setViewTransform(Transform()); + //presentBatch.setFramebuffer(gpu::FramebufferPointer()); + //presentBatch.setViewportTransform(ivec4(targetViewportPosition, targetViewportSize)); + //presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + //presentBatch.setPipeline(_presentPipeline); + //presentBatch.draw(gpu::TRIANGLE_STRIP, 4); + //_backend->render(presentBatch); + //swapBuffers(); + } postPreview(); } -void HmdDisplayPlugin::setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { -} - void HmdDisplayPlugin::updateFrameData() { // Check if we have old frame data to discard - withPresentThreadLock([&] { - auto itr = _frameInfos.find(_currentPresentFrameIndex); - if (itr != _frameInfos.end()) { - _frameInfos.erase(itr); - } - }); + static const uint32_t INVALID_FRAME = (uint32_t)(~0); + uint32_t oldFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME; Parent::updateFrameData(); + uint32_t newFrameIndex = _currentFrame ? _currentFrame->frameIndex : INVALID_FRAME; - withPresentThreadLock([&] { - _currentPresentFrameInfo = _frameInfos[_currentPresentFrameIndex]; - }); + if (oldFrameIndex != newFrameIndex) { + withPresentThreadLock([&] { + if (oldFrameIndex != INVALID_FRAME) { + auto itr = _frameInfos.find(oldFrameIndex); + if (itr != _frameInfos.end()) { + _frameInfos.erase(itr); + } + } + if (newFrameIndex != INVALID_FRAME) { + _currentPresentFrameInfo = _frameInfos[newFrameIndex]; + } + }); + } } glm::mat4 HmdDisplayPlugin::getHeadPose() const { @@ -508,7 +557,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve info.mode = mode; info.color = color; info.direction = direction; - withRenderThreadLock([&] { + withNonPresentThreadLock([&] { if (hands & Hand::LeftHand) { _handLasers[0] = info; } @@ -522,7 +571,7 @@ bool HmdDisplayPlugin::setHandLaser(uint32_t hands, HandLaserMode mode, const ve } void HmdDisplayPlugin::compositeExtra() { -#ifdef HMD_HAND_LASER_SUPPORT +#if 0 // If neither hand laser is activated, exit if (!_presentHandLasers[0].valid() && !_presentHandLasers[1].valid()) { return; @@ -592,4 +641,6 @@ void HmdDisplayPlugin::compositeExtra() { }); glDisable(GL_BLEND); #endif + } + diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 79e52f1406..aad1fa061e 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -9,18 +9,21 @@ #include +#include + #include #include -#include "../OpenGLDisplayPlugin.h" +#include +#include -#ifdef Q_OS_WIN -#define HMD_HAND_LASER_SUPPORT -#endif +#include "../CompositorHelper.h" +#include "../OpenGLDisplayPlugin.h" class HmdDisplayPlugin : public OpenGLDisplayPlugin { using Parent = OpenGLDisplayPlugin; public: + HmdDisplayPlugin() : _overlay( *this ) {} bool isHmd() const override final { return true; } float getIPD() const override final { return _ipd; } glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; } @@ -28,7 +31,6 @@ public: glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override final { return _cullingProjection; } glm::uvec2 getRecommendedUiSize() const override final; glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; } - void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; bool isDisplayVisible() const override { return isHmdMounted(); } QRect getRecommendedOverlayRect() const override final; @@ -65,6 +67,9 @@ protected: } }; + + + Transform _uiModelTransform; std::array _handLasers; std::array _handPoses; @@ -96,10 +101,7 @@ protected: FrameInfo _currentRenderFrameInfo; private: - void updateOverlayProgram(); -#ifdef HMD_HAND_LASER_SUPPORT void updateLaserProgram(); -#endif void updateReprojectionProgram(); bool _enablePreview { false }; @@ -107,26 +109,53 @@ private: bool _enableReprojection { true }; bool _firstPreview { true }; - ProgramPtr _overlayProgram; - struct OverlayUniforms { - int32_t mvp { -1 }; - int32_t alpha { -1 }; - int32_t glowColors { -1 }; - int32_t glowPoints { -1 }; - int32_t resolution { -1 }; - int32_t radius { -1 }; - } _overlayUniforms; + float _previewAspect { 0 }; + glm::uvec2 _prevWindowSize { 0, 0 }; + qreal _prevDevicePixelRatio { 0 }; + + struct OverlayRender { + OverlayRender(HmdDisplayPlugin& plugin) : plugin(plugin) {}; + HmdDisplayPlugin& plugin; + gpu::Stream::FormatPointer format; + gpu::BufferPointer vertices; + gpu::BufferPointer indices; + uint32_t indexCount { 0 }; + gpu::PipelinePointer pipeline; + int32_t uniformsLocation { -1 }; + + // FIXME this is stupid, use the built in transformation pipeline + std::array uniformBuffers; + std::array mvps; + + struct Uniforms { + mat4 mvp; + vec4 glowPoints { -1 }; + vec4 glowColors[2]; + vec2 resolution { CompositorHelper::VIRTUAL_SCREEN_SIZE }; + float radius { 0.005f }; + float alpha { 1.0f }; + } uniforms; + + struct Vertex { + vec3 pos; + vec2 uv; + } vertex; + + static const size_t VERTEX_OFFSET { offsetof(Vertex, pos) }; + static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) }; + static const int VERTEX_STRIDE { sizeof(Vertex) }; + + void build(); + void updatePipeline(); + void render(); + } _overlay; +#if 0 ProgramPtr _previewProgram; struct PreviewUniforms { int32_t previewTexture { -1 }; } _previewUniforms; - float _previewAspect { 0 }; - GLuint _previewTextureID { 0 }; - glm::uvec2 _prevWindowSize { 0, 0 }; - qreal _prevDevicePixelRatio { 0 }; - ProgramPtr _reprojectionProgram; struct ReprojectionUniforms { int32_t reprojectionMatrix { -1 }; @@ -134,9 +163,6 @@ private: int32_t projectionMatrix { -1 }; } _reprojectionUniforms; - ShapeWrapperPtr _sphereSection; - -#ifdef HMD_HAND_LASER_SUPPORT ProgramPtr _laserProgram; struct LaserUniforms { int32_t mvp { -1 }; @@ -145,4 +171,3 @@ private: ShapeWrapperPtr _laserGeometry; #endif }; - diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index 62268afb47..fe51e92fea 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -8,52 +8,56 @@ #include "InterleavedStereoDisplayPlugin.h" -static const char * INTERLEAVED_TEXTURED_VS = R"VS(#version 410 core -#pragma line __LINE__ +#include +#include +#include +#include -in vec3 Position; -in vec2 TexCoord; +static const char* INTERLEAVED_SRGB_TO_LINEAR_FRAG = R"SCRIBE( -out vec2 vTexCoord; +struct TextureData { + ivec2 textureSize; +}; -void main() { - gl_Position = vec4(Position, 1); - vTexCoord = TexCoord; -} +layout(std140) uniform textureDataBuffer { + TextureData textureData; +}; -)VS"; +uniform sampler2D colorMap; -static const char * INTERLEAVED_TEXTURED_FS = R"FS(#version 410 core -#pragma line __LINE__ +in vec2 varTexCoord0; -uniform sampler2D sampler; -uniform ivec2 textureSize; +out vec4 outFragColor; -in vec2 vTexCoord; -out vec4 FragColor; - -void main() { - ivec2 texCoord = ivec2(floor(vTexCoord * textureSize)); +void main(void) { + ivec2 texCoord = ivec2(floor(varTexCoord0 * textureData.textureSize)); texCoord.x /= 2; int row = int(floor(gl_FragCoord.y)); if (row % 2 > 0) { - texCoord.x += (textureSize.x / 2); + texCoord.x += (textureData.textureSize.x / 2); } - FragColor = texelFetch(sampler, texCoord, 0); //texture(sampler, texCoord); + outFragColor = vec4(pow(texelFetch(colorMap, texCoord, 0).rgb, vec3(2.2)), 1.0); } -)FS"; +)SCRIBE"; const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved"); void InterleavedStereoDisplayPlugin::customizeContext() { StereoDisplayPlugin::customizeContext(); - // Set up the stencil buffers? Or use a custom shader? - compileProgram(_interleavedProgram, INTERLEAVED_TEXTURED_VS, INTERLEAVED_TEXTURED_FS); + if (!_interleavedPresentPipeline) { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(INTERLEAVED_SRGB_TO_LINEAR_FRAG)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::makeProgram(*program); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(false)); + _interleavedPresentPipeline = gpu::Pipeline::create(program, state); + } } void InterleavedStereoDisplayPlugin::uncustomizeContext() { - _interleavedProgram.reset(); + _interleavedPresentPipeline.reset(); StereoDisplayPlugin::uncustomizeContext(); } @@ -65,15 +69,14 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const { } void InterleavedStereoDisplayPlugin::internalPresent() { - using namespace oglplus; - auto sceneSize = getRecommendedRenderSize(); - _interleavedProgram->Bind(); - Uniform(*_interleavedProgram, "textureSize").SetValue(sceneSize); - auto surfaceSize = getSurfacePixels(); - Context::Viewport(0, 0, surfaceSize.x, surfaceSize.y); - glBindTexture(GL_TEXTURE_2D, GetName(_compositeFramebuffer->color)); - _plane->Use(); - _plane->Draw(); + gpu::Batch presentBatch; + presentBatch.enableStereo(false); + presentBatch.setViewTransform(Transform()); + presentBatch.setFramebuffer(gpu::FramebufferPointer()); + presentBatch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels())); + presentBatch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0)); + presentBatch.setPipeline(_interleavedPresentPipeline); + presentBatch.draw(gpu::TRIANGLE_STRIP, 4); + _backend->render(presentBatch); swapBuffers(); } - diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index 5eeda951e5..8c3ebcaa6d 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -24,6 +24,7 @@ protected: void internalPresent() override; private: - ProgramPtr _interleavedProgram; static const QString NAME; + gpu::PipelinePointer _interleavedPresentPipeline; + gpu::BufferPointer _textureDataBuffer; }; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 5d9f812edf..104c8ecc75 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -7,16 +7,11 @@ // #include "SideBySideStereoDisplayPlugin.h" -#include -#include -#include -#include -#include "../CompositorHelper.h" const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); glm::uvec2 SideBySideStereoDisplayPlugin::getRecommendedRenderSize() const { uvec2 result = Parent::getRecommendedRenderSize(); - result.x *= 2; + //result.x *= 2; return result; } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index cfdfb1fc21..ae8f9ec039 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -101,4 +101,3 @@ void StereoDisplayPlugin::internalDeactivate() { float StereoDisplayPlugin::getRecommendedAspectRatio() const { return aspect(Parent::getRecommendedRenderSize()); } - diff --git a/libraries/gl/src/gl/GLEscrow.h b/libraries/gl/src/gl/GLEscrow.h index 357398c79b..9482f88683 100644 --- a/libraries/gl/src/gl/GLEscrow.h +++ b/libraries/gl/src/gl/GLEscrow.h @@ -20,6 +20,8 @@ #include #include +#include "Config.h" + // The GLEscrow class provides a simple mechanism for producer GL contexts to provide // content to a consumer where the consumer is assumed to be connected to a display and // therefore must never be blocked. diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index a6b5a03ff6..672d481d4e 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -18,6 +18,7 @@ #include #include "GLHelpers.h" +#include "QOpenGLDebugLoggerWrapper.h" #ifdef DEBUG static bool enableDebugLogger = true; @@ -80,7 +81,7 @@ bool OffscreenGLCanvas::makeCurrent() { _logger = new QOpenGLDebugLogger(this); if (_logger->initialize()) { connect(_logger, &QOpenGLDebugLogger::messageLogged, [](const QOpenGLDebugMessage& message) { - qDebug() << message; + OpenGLDebug::log(message); }); _logger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::NotificationSeverity); _logger->startLogging(QOpenGLDebugLogger::LoggingMode::SynchronousLogging); @@ -101,4 +102,4 @@ QObject* OffscreenGLCanvas::getContextObject() { void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) { moveToThread(thread); _context->moveToThread(thread); -} \ No newline at end of file +} diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index ce2f4c8d66..06e13dc093 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -120,24 +120,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_popProfileRange), }; -extern std::function TEXTURE_ID_RESOLVER; - void GLBackend::init() { static std::once_flag once; std::call_once(once, [] { - - TEXTURE_ID_RESOLVER = [](const Texture& texture)->uint32 { - auto object = Backend::getGPUObject(texture); - if (!object) { - return 0; - } - - if (object->getSyncState() != GLSyncState::Idle) { - return object->_downsampleSource._texture; - } - return object->_texture; - }; - QString vendor{ (const char*)glGetString(GL_VENDOR) }; QString renderer{ (const char*)glGetString(GL_RENDERER) }; qCDebug(gpugllogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION)); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index d27ec3808b..2f77425f1e 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -35,12 +35,13 @@ class GLBackend : public Backend { friend class gpu::Context; static void init(); static Backend* createBackend(); - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings); protected: explicit GLBackend(bool syncCache); GLBackend(); public: + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); + ~GLBackend(); void render(Batch& batch) final; @@ -159,9 +160,10 @@ public: virtual void do_setStateBlendFactor(Batch& batch, size_t paramOffset) final; virtual void do_setStateScissorRect(Batch& batch, size_t paramOffset) final; + virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; + protected: - virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; virtual GLuint getBufferID(const Buffer& buffer) = 0; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBuffer.h b/libraries/gpu-gl/src/gpu/gl/GLBuffer.h index 4783541b11..ecf80c312d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBuffer.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBuffer.h @@ -23,7 +23,7 @@ public: object = new GLBufferType(buffer, object); } - if (0 != (buffer._flags & Buffer::DIRTY)) { + if (0 != (buffer._renderPages._flags & PageManager::DIRTY)) { object->transfer(); } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp index ac337550ca..a065b3094a 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp @@ -42,14 +42,14 @@ public: Size offset; Size size; Size currentPage { 0 }; - auto data = _gpuObject.getSysmem().readData(); - while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) { + auto data = _gpuObject._renderSysmem.readData(); + while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { glBufferSubData(GL_ARRAY_BUFFER, offset, size, data + offset); (void)CHECK_GL_ERROR(); } glBindBuffer(GL_ARRAY_BUFFER, 0); (void)CHECK_GL_ERROR(); - _gpuObject._flags &= ~Buffer::DIRTY; + _gpuObject._renderPages._flags &= ~PageManager::DIRTY; } }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp index 1676b0ce1c..4a025939b9 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendBuffer.cpp @@ -32,12 +32,12 @@ public: Size offset; Size size; Size currentPage { 0 }; - auto data = _gpuObject.getSysmem().readData(); - while (_gpuObject.getNextTransferBlock(offset, size, currentPage)) { + auto data = _gpuObject._renderSysmem.readData(); + while (_gpuObject._renderPages.getNextTransferBlock(offset, size, currentPage)) { glNamedBufferSubData(_buffer, (GLintptr)offset, (GLsizeiptr)size, data + offset); } (void)CHECK_GL_ERROR(); - _gpuObject._flags &= ~Buffer::DIRTY; + _gpuObject._renderPages._flags &= ~PageManager::DIRTY; } }; diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index ff43491133..dbd76d034a 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -20,13 +20,6 @@ Context::Context() { if (_createBackendCallback) { _backend.reset(_createBackendCallback()); } - - _frameHandler = [this](Frame& frame){ - for (size_t i = 0; i < frame.batches.size(); ++i) { - _backend->_stereo = frame.stereoStates[i]; - _backend->render(frame.batches[i]); - } - }; } Context::Context(const Context& context) { @@ -35,40 +28,30 @@ Context::Context(const Context& context) { Context::~Context() { } -void Context::setFrameHandler(FrameHandler handler) { - _frameHandler = handler; -} - -#define DEFERRED_RENDERING - -void Context::beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose) { - _currentFrame = Frame(); - _currentFrame.framebuffer = outputFramebuffer; - _currentFrame.pose = renderPose; +void Context::beginFrame(const glm::mat4& renderPose) { + assert(!_frameActive); _frameActive = true; + _currentFrame = std::make_shared(); + _currentFrame->pose = renderPose; } void Context::append(Batch& batch) { if (!_frameActive) { qWarning() << "Batch executed outside of frame boundaries"; + return; } -#ifdef DEFERRED_RENDERING - _currentFrame.batches.emplace_back(batch); - _currentFrame.stereoStates.emplace_back(_stereo); -#else - _backend->_stereo = _stereo; - _backend->render(batch); -#endif + _currentFrame->batches.push_back(batch); } -void Context::endFrame() { -#ifdef DEFERRED_RENDERING - if (_frameHandler) { - _frameHandler(_currentFrame); - } -#endif - _currentFrame = Frame(); +FramePointer Context::endFrame() { + assert(_frameActive); + auto result = _currentFrame; + _currentFrame.reset(); _frameActive = false; + + result->stereoState = _stereo; + result->finish(); + return result; } @@ -111,16 +94,10 @@ void Context::getStereoViews(mat4* eyeViews) const { } } -void Context::syncCache() { - PROFILE_RANGE(__FUNCTION__); - _backend->syncCache(); -} - void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { _backend->downloadFramebuffer(srcFramebuffer, region, destImage); } - void Context::getStats(ContextStats& stats) const { _backend->getStats(stats); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 5f894318f2..d967c7a977 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -51,8 +51,9 @@ class Backend { public: virtual~ Backend() {}; - virtual void render(Batch& batch) = 0; + void setStereoState(const StereoState& stereo) { _stereo = stereo; } + virtual void render(Batch& batch) = 0; virtual void syncCache() = 0; virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0; @@ -139,10 +140,11 @@ public: Context(); ~Context(); - void setFrameHandler(FrameHandler handler); - void beginFrame(const FramebufferPointer& outputFramebuffer, const glm::mat4& renderPose = glm::mat4()); + void beginFrame(const glm::mat4& renderPose = glm::mat4()); void append(Batch& batch); - void endFrame(); + FramePointer endFrame(); + + const BackendPointer& getBackend() const { return _backend; } void enableStereo(bool enable = true); bool isStereo(); @@ -150,7 +152,6 @@ public: void setStereoViews(const mat4 eyeViews[2]); void getStereoProjections(mat4* eyeProjections) const; void getStereoViews(mat4* eyeViews) const; - void syncCache(); // Downloading the Framebuffer is a synchronous action that is not efficient. // It s here for convenience to easily capture a snapshot @@ -171,10 +172,9 @@ public: protected: Context(const Context& context); - std::unique_ptr _backend; + std::shared_ptr _backend; bool _frameActive { false }; - Frame _currentFrame; - FrameHandler _frameHandler; + FramePointer _currentFrame; StereoState _stereo; // This function can only be called by "static Shader::makeProgram()" diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index 3b04b17d87..be8c9a4040 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -11,20 +11,23 @@ #include #include +#include #include -#include #include namespace gpu { + using Mutex = std::mutex; + using Lock = std::unique_lock; + class Batch; class Backend; + using BackendPointer = std::shared_ptr; class Context; using ContextPointer = std::shared_ptr; class GPUObject; class Frame; using FramePointer = std::shared_ptr; - using FrameHandler = std::function; using Stamp = int; using uint32 = uint32_t; diff --git a/libraries/gpu/src/gpu/Frame.cpp b/libraries/gpu/src/gpu/Frame.cpp new file mode 100644 index 0000000000..3570c96007 --- /dev/null +++ b/libraries/gpu/src/gpu/Frame.cpp @@ -0,0 +1,53 @@ +// +// Created by Bradley Austin Davis on 2016/07/26 +// Copyright 2013-2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Frame.h" +#include + +using namespace gpu; + +Frame::~Frame() { + if (framebuffer && framebufferRecycler) { + framebufferRecycler(framebuffer); + framebuffer.reset(); + } + + if (overlay && overlayRecycler) { + overlayRecycler(overlay); + overlay.reset(); + } +} + +void Frame::finish() { + std::unordered_set seenBuffers; + for (Batch& batch : batches) { + for (auto& bufferCacheItem : batch._buffers._items) { + const BufferPointer& buffer = bufferCacheItem._data; + if (!buffer) { + continue; + } + if (!buffer->isDirty()) { + continue; + } + if (seenBuffers.count(buffer.get())) { + continue; + } + seenBuffers.insert(buffer.get()); + bufferUpdates.push_back({ buffer, buffer->getUpdate() }); + } + } +} + +void Frame::preRender() { + for (auto& bufferUpdate : bufferUpdates) { + const BufferPointer& buffer = bufferUpdate.first; + const Buffer::Update& update = bufferUpdate.second; + buffer->applyUpdate(update); + } + bufferUpdates.clear(); +} diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index ed5e2ea179..658484c8dc 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -8,20 +8,44 @@ #ifndef hifi_gpu_Frame_h #define hifi_gpu_Frame_h +#include + #include "Forward.h" +#include "Batch.h" +#include "Resource.h" namespace gpu { -class Frame { -public: - /// The sensor pose used for rendering the frame, only applicable for HMDs - glm::mat4 pose; - /// The collection of batches which make up the frame - std::vector batches; - std::vector stereoStates; - /// The destination framebuffer in which the results will be placed - FramebufferPointer framebuffer; -}; + class Frame { + public: + using Batches = std::vector; + using FramebufferRecycler = std::function; + using OverlayRecycler = std::function; + using BufferUpdate = std::pair; + using BufferUpdates = std::vector; + + virtual ~Frame(); + void finish(); + void preRender(); + + StereoState stereoState; + uint32_t frameIndex{ 0 }; + /// The sensor pose used for rendering the frame, only applicable for HMDs + Mat4 pose; + /// The collection of batches which make up the frame + Batches batches; + /// The destination framebuffer in which the results will be placed + FramebufferPointer framebuffer; + /// The destination texture containing the 2D overlay + TexturePointer overlay; + + /// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE + FramebufferRecycler framebufferRecycler; + /// How to process the overlay texture when the frame dies. MUST BE THREAD SAFE + OverlayRecycler overlayRecycler; + BufferUpdates bufferUpdates; + + }; }; diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index 7dbe662cbc..4b33badeb8 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -78,7 +78,7 @@ const float AllocationDebugger::K = 1024.0f; static AllocationDebugger allocationDebugger; -Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { +Size Sysmem::allocateMemory(Byte** dataAllocated, Size size) { allocationDebugger += size; if ( !dataAllocated ) { qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; @@ -102,40 +102,40 @@ Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) return newSize; } -void Resource::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { +void Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { allocationDebugger -= size; if (dataAllocated) { delete[] dataAllocated; } } -Resource::Sysmem::Sysmem() {} +Sysmem::Sysmem() {} -Resource::Sysmem::Sysmem(Size size, const Byte* bytes) { +Sysmem::Sysmem(Size size, const Byte* bytes) { if (size > 0 && bytes) { setData(_size, bytes); } } -Resource::Sysmem::Sysmem(const Sysmem& sysmem) { +Sysmem::Sysmem(const Sysmem& sysmem) { if (sysmem.getSize() > 0) { allocate(sysmem._size); setData(_size, sysmem._data); } } -Resource::Sysmem& Resource::Sysmem::operator=(const Sysmem& sysmem) { +Sysmem& Sysmem::operator=(const Sysmem& sysmem) { setData(sysmem.getSize(), sysmem.readData()); return (*this); } -Resource::Sysmem::~Sysmem() { +Sysmem::~Sysmem() { deallocateMemory( _data, _size ); _data = NULL; _size = 0; } -Resource::Size Resource::Sysmem::allocate(Size size) { +Size Sysmem::allocate(Size size) { if (size != _size) { Byte* newData = NULL; Size newSize = 0; @@ -156,7 +156,7 @@ Resource::Size Resource::Sysmem::allocate(Size size) { return _size; } -Resource::Size Resource::Sysmem::resize(Size size) { +Size Sysmem::resize(Size size) { if (size != _size) { Byte* newData = NULL; Size newSize = 0; @@ -182,7 +182,7 @@ Resource::Size Resource::Sysmem::resize(Size size) { return _size; } -Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { +Size Sysmem::setData( Size size, const Byte* bytes ) { if (allocate(size) == size) { if (size && bytes) { memcpy( _data, bytes, _size ); @@ -191,7 +191,7 @@ Resource::Size Resource::Sysmem::setData( Size size, const Byte* bytes ) { return _size; } -Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { +Size Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { if (size && ((offset + size) <= getSize()) && bytes) { memcpy( _data + offset, bytes, size ); return size; @@ -199,7 +199,7 @@ Resource::Size Resource::Sysmem::setSubData( Size offset, Size size, const Byte* return 0; } -Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) { +Size Sysmem::append(Size size, const Byte* bytes) { if (size > 0) { Size oldSize = getSize(); Size totalSize = oldSize + size; @@ -241,7 +241,7 @@ Buffer::Size Buffer::getBufferGPUMemoryUsage() { } Buffer::Buffer(Size pageSize) : - _pageSize(pageSize) { + _pages(pageSize) { _bufferCPUCount++; } @@ -249,12 +249,12 @@ Buffer::Buffer(Size size, const Byte* bytes, Size pageSize) : Buffer(pageSize) { setData(size, bytes); } -Buffer::Buffer(const Buffer& buf) : Buffer(buf._pageSize) { +Buffer::Buffer(const Buffer& buf) : Buffer(buf._pages._pageSize) { setData(buf.getSize(), buf.getData()); } Buffer& Buffer::operator=(const Buffer& buf) { - const_cast(_pageSize) = buf._pageSize; + const_cast(_pages._pageSize) = buf._pages._pageSize; setData(buf.getSize(), buf.getData()); return (*this); } @@ -266,14 +266,10 @@ Buffer::~Buffer() { Buffer::Size Buffer::resize(Size size) { _end = size; - auto prevSize = editSysmem().getSize(); + auto prevSize = _sysmem.getSize(); if (prevSize < size) { - auto newPages = getRequiredPageCount(); - auto newSize = newPages * _pageSize; - editSysmem().resize(newSize); - // All new pages start off as clean, because they haven't been populated by data - _pages.resize(newPages, 0); - Buffer::updateBufferCPUMemoryUsage(prevSize, newSize); + _sysmem.resize(_pages.accommodate(_end)); + Buffer::updateBufferCPUMemoryUsage(prevSize, _sysmem.getSize()); } return _end; } @@ -282,28 +278,45 @@ void Buffer::markDirty(Size offset, Size bytes) { if (!bytes) { return; } - _flags |= DIRTY; - // Find the starting page - Size startPage = (offset / _pageSize); - // Non-zero byte count, so at least one page is dirty - Size pageCount = 1; - // How much of the page is after the offset? - Size remainder = _pageSize - (offset % _pageSize); - // If there are more bytes than page space remaining, we need to increase the page count - if (bytes > remainder) { - // Get rid of the amount that will fit in the current page - bytes -= remainder; - pageCount += (bytes / _pageSize); - if (bytes % _pageSize) { - ++pageCount; + _pages.markRegion(offset, bytes); +} + +void Buffer::applyUpdate(const Update& update) { + _renderSysmem.resize(update.size); + _renderPages = update.pages; + update.updateOperator(_renderSysmem); + } + +Buffer::Update Buffer::getUpdate() const { + static Update EMPTY_UPDATE; + if (!_pages) { + return EMPTY_UPDATE; + } + + Update result; + result.pages = _pages; + result.size = _sysmem.getSize(); + Size pageSize = _pages._pageSize; + PageManager::Pages dirtyPages = _pages.getMarkedPages(); + std::vector dirtyPageData; + dirtyPageData.resize(dirtyPages.size() * pageSize); + for (Size i = 0; i < dirtyPages.size(); ++i) { + Size page = dirtyPages[i]; + Size sourceOffset = page * pageSize; + Size destOffset = i * pageSize; + memcpy(dirtyPageData.data() + destOffset, _sysmem.readData() + sourceOffset, pageSize); + } + + result.updateOperator = [pageSize, dirtyPages, dirtyPageData](Sysmem& dest){ + for (Size i = 0; i < dirtyPages.size(); ++i) { + Size page = dirtyPages[i]; + Size sourceOffset = i * pageSize; + Size destOffset = page * pageSize; + memcpy(dest.editData() + destOffset, dirtyPageData.data() + sourceOffset, pageSize); } - } - - // Mark the pages dirty - for (Size i = 0; i < pageCount; ++i) { - _pages[i + startPage] |= DIRTY; - } + }; + return result; } @@ -333,14 +346,6 @@ Buffer::Size Buffer::getSize() const { return _end; } -Buffer::Size Buffer::getRequiredPageCount() const { - Size result = _end / _pageSize; - if (_end % _pageSize) { - ++result; - } - return result; -} - const Element BufferView::DEFAULT_ELEMENT = Element( gpu::SCALAR, gpu::UINT8, gpu::RAW ); BufferView::BufferView() : diff --git a/libraries/gpu/src/gpu/Resource.h b/libraries/gpu/src/gpu/Resource.h index 10c83dfb0e..e02ff04d05 100644 --- a/libraries/gpu/src/gpu/Resource.h +++ b/libraries/gpu/src/gpu/Resource.h @@ -25,11 +25,69 @@ namespace gpu { +// Sysmem is the underneath cache for the data in ram of a resource. +class Sysmem { +public: + static const Size NOT_ALLOCATED = (Size)-1; + + Sysmem(); + Sysmem(Size size, const Byte* bytes); + Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer + Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* bytes); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData(Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + const Byte* readData() const { return _data; } + Byte* editData() { return _data; } + + template< typename T > const T* read() const { return reinterpret_cast< T* > (_data); } + template< typename T > T* edit() { return reinterpret_cast< T* > (_data); } + + // Access the current version of the sysmem, used to compare if copies are in sync + Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + bool isAvailable() const { return (_data != 0); } + + using Operator = std::function; +private: + Stamp _stamp{ 0 }; + Size _size{ 0 }; + Byte* _data{ nullptr }; +}; // Sysmem + class Resource { public: typedef size_t Size; - static const Size NOT_ALLOCATED = (Size)-1; + static const Size NOT_ALLOCATED = Sysmem::NOT_ALLOCATED; // The size in bytes of data stored in the resource virtual Size getSize() const = 0; @@ -47,88 +105,178 @@ public: }; protected: + using Sysmem = gpu::Sysmem; Resource() {} virtual ~Resource() {} - // Sysmem is the underneath cache for the data in ram of a resource. - class Sysmem { - public: +}; // Resource - Sysmem(); - Sysmem(Size size, const Byte* bytes); - Sysmem(const Sysmem& sysmem); // deep copy of the sysmem buffer - Sysmem& operator=(const Sysmem& sysmem); // deep copy of the sysmem buffer - ~Sysmem(); - Size getSize() const { return _size; } +struct PageManager { + static const Size DEFAULT_PAGE_SIZE = 4096; - // Allocate the byte array - // \param pSize The nb of bytes to allocate, if already exist, content is lost. - // \return The nb of bytes allocated, nothing if allready the appropriate size. - Size allocate(Size pSize); - - // Resize the byte array - // Keep previous data [0 to min(pSize, mSize)] - Size resize(Size pSize); - - // Assign data bytes and size (allocate for size, then copy bytes if exists) - Size setData(Size size, const Byte* bytes ); - - // Update Sub data, - // doesn't allocate and only copy size * bytes at the offset location - // only if all fits in the existing allocated buffer - Size setSubData(Size offset, Size size, const Byte* bytes); - - // Append new data at the end of the current buffer - // do a resize( size + getSIze) and copy the new data - // \return the number of bytes copied - Size append(Size size, const Byte* data); - - // Access the byte array. - // The edit version allow to map data. - const Byte* readData() const { return _data; } - Byte* editData() { return _data; } - - template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); } - template< typename T > T* edit() { return reinterpret_cast< T* > ( _data ); } - - // Access the current version of the sysmem, used to compare if copies are in sync - Stamp getStamp() const { return _stamp; } - - static Size allocateMemory(Byte** memAllocated, Size size); - static void deallocateMemory(Byte* memDeallocated, Size size); - - bool isAvailable() const { return (_data != 0); } - - private: - Stamp _stamp { 0 }; - Size _size { 0 }; - Byte* _data { nullptr }; + enum Flag { + DIRTY = 0x01, }; + PageManager(Size pageSize = DEFAULT_PAGE_SIZE) : _pageSize(pageSize) {} + PageManager& operator=(const PageManager& other) { + assert(other._pageSize == _pageSize); + _pages = other._pages; + _flags = other._flags; + return *this; + } + + using Vector = std::vector; + using Pages = std::vector; + Vector _pages; + + uint8 _flags{ 0 }; + const Size _pageSize; + + operator bool const() { + return (*this)(DIRTY); + } + + bool operator()(uint8 desiredFlags) const { + return (desiredFlags == (_flags & desiredFlags)); + } + + void markPage(Size index, uint8 markFlags = DIRTY) { + assert(_pages.size() > index); + _pages[index] |= markFlags; + _flags |= markFlags; + } + + void markRegion(Size offset, Size bytes, uint8 markFlags = DIRTY) { + if (!bytes) { + return; + } + _flags |= markFlags; + // Find the starting page + Size startPage = (offset / _pageSize); + // Non-zero byte count, so at least one page is dirty + Size pageCount = 1; + // How much of the page is after the offset? + Size remainder = _pageSize - (offset % _pageSize); + // If there are more bytes than page space remaining, we need to increase the page count + if (bytes > remainder) { + // Get rid of the amount that will fit in the current page + bytes -= remainder; + + pageCount += (bytes / _pageSize); + if (bytes % _pageSize) { + ++pageCount; + } + } + + // Mark the pages dirty + for (Size i = 0; i < pageCount; ++i) { + _pages[i + startPage] |= DIRTY; + } + } + + Size getPageCount(uint8_t desiredFlags = DIRTY) const { + Size result = 0; + for (auto pageFlags : _pages) { + if (desiredFlags == (pageFlags & desiredFlags)) { + ++result; + } + } + return result; + } + + Size getSize(uint8_t desiredFlags = DIRTY) const { + return getPageCount(desiredFlags) * _pageSize; + } + + void setPageCount(Size count) { + _pages.resize(count); + } + + Size getRequiredPageCount(Size size) const { + Size result = size / _pageSize; + if (size % _pageSize) { + ++result; + } + return result; + } + + Size getRequiredSize(Size size) const { + return getRequiredPageCount(size) * _pageSize; + } + + Size accommodate(Size size) { + Size newPageCount = getRequiredPageCount(size); + Size newSize = newPageCount * _pageSize; + _pages.resize(newPageCount, 0); + return newSize; + } + + // Get pages with the specified flags, optionally clearing the flags as we go + Pages getMarkedPages(uint8_t desiredFlags = DIRTY, bool clear = true) { + Pages result; + if (desiredFlags == (_flags & desiredFlags)) { + _flags &= ~desiredFlags; + result.reserve(_pages.size()); + for (Size i = 0; i < _pages.size(); ++i) { + if (desiredFlags == (_pages[i] & desiredFlags)) { + result.push_back(i); + if (clear) { + _pages[i] &= ~desiredFlags; + } + } + } + } + return result; + } + + bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) { + Size pageCount = _pages.size(); + // Advance to the first dirty page + while (currentPage < pageCount && (0 == (DIRTY & _pages[currentPage]))) { + ++currentPage; + } + + // If we got to the end, we're done + if (currentPage >= pageCount) { + return false; + } + + // Advance to the next clean page + outOffset = static_cast(currentPage * _pageSize); + while (currentPage < pageCount && (0 != (DIRTY & _pages[currentPage]))) { + _pages[currentPage] &= ~DIRTY; + ++currentPage; + } + outSize = static_cast((currentPage * _pageSize) - outOffset); + return true; + } }; + class Buffer : public Resource { static std::atomic _bufferCPUCount; static std::atomic _bufferCPUMemoryUsage; static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize); public: - enum Flag { - DIRTY = 0x01, + using Flag = PageManager::Flag; + struct Update { + Size size; + PageManager pages; + Sysmem::Operator updateOperator; }; // Currently only one flag... 'dirty' - using PageFlags = std::vector; - static const Size DEFAULT_PAGE_SIZE = 4096; static uint32_t getBufferCPUCount(); static Size getBufferCPUMemoryUsage(); static uint32_t getBufferGPUCount(); static Size getBufferGPUMemoryUsage(); - Buffer(Size pageSize = DEFAULT_PAGE_SIZE); - Buffer(Size size, const Byte* bytes, Size pageSize = DEFAULT_PAGE_SIZE); + Buffer(Size pageSize = PageManager::DEFAULT_PAGE_SIZE); + Buffer(Size size, const Byte* bytes, Size pageSize = PageManager::DEFAULT_PAGE_SIZE); Buffer(const Buffer& buf); // deep copy of the sysmem buffer Buffer& operator=(const Buffer& buf); // deep copy of the sysmem buffer ~Buffer(); @@ -184,34 +332,24 @@ public: return append(sizeof(T) * t.size(), reinterpret_cast(&t[0])); } - bool getNextTransferBlock(Size& outOffset, Size& outSize, Size& currentPage) const { - Size pageCount = _pages.size(); - // Advance to the first dirty page - while (currentPage < pageCount && (0 == (Buffer::DIRTY & _pages[currentPage]))) { - ++currentPage; - } - - // If we got to the end, we're done - if (currentPage >= pageCount) { - return false; - } - - // Advance to the next clean page - outOffset = static_cast(currentPage * _pageSize); - while (currentPage < pageCount && (0 != (Buffer::DIRTY & _pages[currentPage]))) { - _pages[currentPage] &= ~Buffer::DIRTY; - ++currentPage; - } - outSize = static_cast((currentPage * _pageSize) - outOffset); - return true; - } const GPUObjectPointer gpuObject {}; // Access the sysmem object, limited to ourselves and GPUObject derived classes const Sysmem& getSysmem() const { return _sysmem; } - // FIXME find a better access mechanism for clearing this - mutable uint8_t _flags; + + bool isDirty() const { + return _pages(PageManager::DIRTY); + } + + void applyUpdate(const Update& update); + + // Main thread operation to say that the buffer is ready to be used as a frame + Update getUpdate() const; + + mutable PageManager _renderPages; + Sysmem _renderSysmem; + protected: void markDirty(Size offset, Size bytes); @@ -223,16 +361,15 @@ protected: Sysmem& editSysmem() { return _sysmem; } Byte* editData() { return editSysmem().editData(); } - Size getRequiredPageCount() const; - - Size _end { 0 }; - mutable PageFlags _pages; - const Size _pageSize; + mutable PageManager _pages; + Size _end{ 0 }; Sysmem _sysmem; + // FIXME find a more generic way to do this. friend class gl::GLBuffer; friend class BufferView; + friend class Frame; }; typedef std::shared_ptr BufferPointer; diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index f1a8960fa1..429cc5aeba 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -880,11 +880,3 @@ Vec3u Texture::evalMipDimensions(uint16 level) const { return glm::max(dimensions, Vec3u(1)); } -std::function TEXTURE_ID_RESOLVER; - -uint32 Texture::getHardwareId() const { - if (TEXTURE_ID_RESOLVER) { - return TEXTURE_ID_RESOLVER(*this); - } - return 0; -} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index bceb4b196a..8f075d906b 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -449,8 +449,6 @@ public: const GPUObjectPointer gpuObject {}; - uint32 getHardwareId() const; - protected: std::unique_ptr< Storage > _storage; diff --git a/libraries/plugins/CMakeLists.txt b/libraries/plugins/CMakeLists.txt index f32650df94..e7798e9f3d 100644 --- a/libraries/plugins/CMakeLists.txt +++ b/libraries/plugins/CMakeLists.txt @@ -1,3 +1,4 @@ set(TARGET_NAME plugins) setup_hifi_library(OpenGL) link_hifi_libraries(shared) +include_hifi_library_headers(gpu) diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index f0ba762ecb..004407c821 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Plugin.h" @@ -91,11 +92,6 @@ public: return glm::mat4(); } - // Needed for timewarp style features - virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) { - // NOOP - } - virtual void abandonCalibration() {} virtual void resetSensors() {} @@ -132,6 +128,7 @@ public: Present = QEvent::User + 1 }; + virtual bool isRenderThread() const { return false; } virtual bool isHmd() const { return false; } virtual int getHmdScreen() const { return -1; } /// By default, all HMDs are stereo @@ -149,16 +146,8 @@ public: virtual QString getPreferredAudioOutDevice() const { return QString(); } // Rendering support - - /** - * Sends the scene texture to the display plugin. - */ - virtual void submitSceneTexture(uint32_t frameIndex, const gpu::TexturePointer& sceneTexture) = 0; - - /** - * Sends the scene texture to the display plugin. - */ - virtual void submitOverlayTexture(const gpu::TexturePointer& overlayTexture) = 0; + virtual void setBackend(const gpu::BackendPointer& backend) final { _backend = backend; } + virtual void submitFrame(const gpu::FramePointer& newFrame) = 0; // Does the rendering surface have current focus? virtual bool hasFocus() const = 0; @@ -212,6 +201,8 @@ signals: protected: void incrementPresentCount(); + gpu::BackendPointer _backend; + private: std::atomic _presentedFrameIndex; mutable std::mutex _paintDelayMutex; diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index b0329faa3e..b955205a1a 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -53,9 +53,6 @@ void Engine::load() { } void Engine::run() { - // Sync GPU state before beginning to render - _renderContext->args->_context->syncCache(); - for (auto job : _jobs) { job.run(_sceneContext, _renderContext); } diff --git a/libraries/shared/src/shared/NsightHelpers.cpp b/libraries/shared/src/shared/NsightHelpers.cpp index 2539ff8864..3292205c1f 100644 --- a/libraries/shared/src/shared/NsightHelpers.cpp +++ b/libraries/shared/src/shared/NsightHelpers.cpp @@ -11,12 +11,23 @@ #ifdef _WIN32 #if defined(NSIGHT_FOUND) #include "nvToolsExt.h" +#include +#include + +extern bool isRenderThread(); ProfileRange::ProfileRange(const char *name) { + if (!isRenderThread()) { + return; + } + nvtxRangePush(name); } ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payload) { + if (!isRenderThread()) { + return; + } nvtxEventAttributes_t eventAttrib = {0}; eventAttrib.version = NVTX_VERSION; @@ -32,6 +43,9 @@ ProfileRange::ProfileRange(const char *name, uint32_t argbColor, uint64_t payloa } ProfileRange::~ProfileRange() { + if (!isRenderThread()) { + return; + } nvtxRangePop(); } diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp index 2581389424..188af56ca2 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp @@ -14,6 +14,7 @@ #include #include +#include static PluginContainer* INSTANCE{ nullptr }; @@ -159,3 +160,8 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) { Setting::Handle settingValue(settingName, value); return settingValue.set(value); } + +bool isRenderThread() { + auto displayPlugin = PluginContainer::getInstance().getActiveDisplayPlugin(); + return displayPlugin && displayPlugin->isRenderThread(); +} \ No newline at end of file diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h index 74ac834057..167af100b3 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h @@ -58,8 +58,6 @@ public: virtual void showDisplayPluginsTools(bool show = true) = 0; virtual void requestReset() = 0; virtual bool makeRenderingContextCurrent() = 0; - virtual void releaseSceneTexture(const gpu::TexturePointer& texture) = 0; - virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) = 0; virtual GLWidget* getPrimaryWidget() = 0; virtual MainWindow* getPrimaryWindow() = 0; virtual QOpenGLContext* getPrimaryContext() = 0; diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index 778be08dcf..1edf902b02 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -6,6 +6,7 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # +if (FALSE) if (WIN32) # we're using static GLEW, so define GLEW_STATIC @@ -23,4 +24,5 @@ if (WIN32) target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) target_link_libraries(${TARGET_NAME} Winmm.lib) +endif() endif() \ No newline at end of file diff --git a/plugins/oculusLegacy/CMakeLists.txt b/plugins/oculusLegacy/CMakeLists.txt index c1f2c6249f..b3d2394f9c 100644 --- a/plugins/oculusLegacy/CMakeLists.txt +++ b/plugins/oculusLegacy/CMakeLists.txt @@ -8,6 +8,8 @@ # Windows doesn't need this, and building it currently make Linux unstable. # if (NOT WIN32) + +if (FALSE) if (APPLE) set(TARGET_NAME oculusLegacy) @@ -26,3 +28,4 @@ if (APPLE) endif() +endif() \ No newline at end of file diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index 8263e87767..8b0230d850 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -6,6 +6,7 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # +if (FALSE) if (WIN32) # we're using static GLEW, so define GLEW_STATIC add_definitions(-DGLEW_STATIC) @@ -22,3 +23,4 @@ if (WIN32) target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) endif() +endif() \ No newline at end of file diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index 1a4f8742e9..2c8f361fac 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -85,8 +85,6 @@ public: virtual void showDisplayPluginsTools(bool show) override {} virtual void requestReset() override {} virtual bool makeRenderingContextCurrent() override { return true; } - virtual void releaseSceneTexture(const gpu::TexturePointer& texture) override {} - virtual void releaseOverlayTexture(const gpu::TexturePointer& texture) override {} virtual GLWidget* getPrimaryWidget() override { return nullptr; } virtual MainWindow* getPrimaryWindow() override { return nullptr; } virtual QOpenGLContext* getPrimaryContext() override { return nullptr; } diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests/gpu-test/src/TestWindow.cpp index f31eb6f7bb..5566fa92a4 100644 --- a/tests/gpu-test/src/TestWindow.cpp +++ b/tests/gpu-test/src/TestWindow.cpp @@ -93,7 +93,6 @@ void TestWindow::resizeWindow(const QSize& size) { } void TestWindow::beginFrame() { - _renderArgs->_context->syncCache(); #ifdef DEFERRED_LIGHTING diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index 22dd96b83b..b5b008b308 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -38,6 +37,7 @@ #include #include #include +#include #include #include @@ -157,7 +157,118 @@ static QString toHumanSize(size_t size, size_t maxUnit = std::numeric_limits { + using Parent = GenericQueueThread; +public: + QOpenGLContextWrapper* _displayContext{ nullptr }; + QSurface* _displaySurface{ nullptr }; + gpu::PipelinePointer _presentPipeline; + gpu::ContextPointer _gpuContext; // initialized during window creation + std::atomic _presentCount; + QElapsedTimer _elapsed; + std::atomic _fps; + RateCounter<200> _fpsCounter; + std::mutex _mutex; + std::shared_ptr _backend; + + + void initialize(QOpenGLContextWrapper* displayContext, QWindow* surface) { + setObjectName("RenderThread"); + _displayContext = displayContext; + _displaySurface = surface; + _displayContext->makeCurrent(_displaySurface); + // GPU library init + gpu::Context::init(); + _gpuContext = std::make_shared(); + _backend = _gpuContext->getBackend(); + _displayContext->makeCurrent(_displaySurface); + DependencyManager::get()->init(); + _displayContext->doneCurrent(); + Parent::initialize(); + if (isThreaded()) { + _displayContext->moveToThread(thread()); + } + } + + void setup() override { + _displayContext->makeCurrent(_displaySurface); + glewExperimental = true; + glewInit(); + glGetError(); + + { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(SRGB_TO_LINEAR_FRAG)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + _presentPipeline = gpu::Pipeline::create(program, state); + } + + //_textOverlay = new TextOverlay(glm::uvec2(800, 600)); + glViewport(0, 0, 800, 600); + _elapsed.start(); + } + + void shutdown() override { + } + + void renderFrame(gpu::FramePointer& frame) { + ++_presentCount; + _displayContext->makeCurrent(_displaySurface); + + if (frame && !frame->batches.empty()) { + _backend->syncCache(); + _backend->setStereoState(frame->stereoState); + for (auto& batch : frame->batches) { + _backend->render(batch); + } + { + auto geometryCache = DependencyManager::get(); + gpu::Batch presentBatch; + presentBatch.setViewTransform(Transform()); + presentBatch.setFramebuffer(gpu::FramebufferPointer()); + presentBatch.setResourceTexture(0, frame->framebuffer->getRenderBuffer(0)); + presentBatch.setPipeline(_presentPipeline); + presentBatch.draw(gpu::TRIANGLE_STRIP, 4); + _backend->render(presentBatch); + } + } + { + //_textOverlay->render(); + } + _displayContext->swapBuffers(_displaySurface); + _fpsCounter.increment(); + static size_t _frameCount{ 0 }; + ++_frameCount; + if (_elapsed.elapsed() >= 500) { + _fps = _fpsCounter.rate(); + _frameCount = 0; + _elapsed.restart(); + } + } + + bool processQueueItems(const Queue& items) override { + auto frame = items.last(); + renderFrame(frame); + return true; + } +}; // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow, public AbstractViewStateInterface { @@ -185,6 +296,7 @@ protected: } void postLambdaEvent(std::function f) override {} + qreal getDevicePixelRatio() override { return 1.0f; } @@ -192,6 +304,7 @@ protected: render::ScenePointer getMain3DScene() override { return _main3DScene; } + render::EnginePointer getRenderEngine() override { return _renderEngine; } @@ -221,12 +334,13 @@ public: } QTestWindow() { + _camera.movementSpeed = 50.0f; QThread::currentThread()->setPriority(QThread::HighestPriority); AbstractViewStateInterface::setInstance(this); _octree = DependencyManager::set(false, this, nullptr); _octree->init(); // Prevent web entities from rendering - REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory) + REGISTER_ENTITY_TYPE_WITH_FACTORY(Web, WebEntityItem::factory); DependencyManager::set(_octree->getTree()); getEntities()->setViewFrustum(_viewFrustum); @@ -241,11 +355,12 @@ public: format.setOption(QSurfaceFormat::DebugContext); setFormat(format); - _context.setFormat(format); - _context.create(); resize(QSize(800, 600)); show(); - makeCurrent(); + + _context.setFormat(format); + _context.create(); + _context.makeCurrent(this); glewExperimental = true; glewInit(); glGetError(); @@ -253,40 +368,24 @@ public: #ifdef Q_OS_WIN wglSwapIntervalEXT(0); #endif - { - makeCurrent(); - _quadProgram = loadDefaultShader(); - _plane = loadPlane(_quadProgram); - _textOverlay = new TextOverlay(glm::uvec2(800, 600)); - glViewport(0, 0, 800, 600); - } + _context.doneCurrent(); - _camera.movementSpeed = 50.0f; - - - // GPU library init - { - _offscreenContext = new OffscreenGLCanvas(); - _offscreenContext->create(_context.getContext()); - _offscreenContext->makeCurrent(); - gpu::Context::init(); - _gpuContext = std::make_shared(); - } + _initContext.create(_context.getContext()); + _renderThread.initialize(&_context, this); + // FIXME use a wait condition + QThread::msleep(1000); + _renderThread.queueItem(gpu::FramePointer()); + _initContext.makeCurrent(); + // Render engine init + _renderEngine->addJob("RenderShadowTask", _cullFunctor); + _renderEngine->addJob("RenderDeferredTask", _cullFunctor); + _renderEngine->load(); + _renderEngine->registerScene(_main3DScene); // Render engine library init - { - _offscreenContext->makeCurrent(); - DependencyManager::get()->init(); - _renderEngine->addJob("RenderShadowTask", _cullFunctor); - _renderEngine->addJob("RenderDeferredTask", _cullFunctor); - _renderEngine->load(); - _renderEngine->registerScene(_main3DScene); - } - reloadScene(); restorePosition(); - _elapsed.start(); QTimer* timer = new QTimer(this); timer->setInterval(0); connect(timer, &QTimer::timeout, this, [this] { @@ -298,8 +397,6 @@ public: virtual ~QTestWindow() { ResourceManager::cleanup(); - try { _quadProgram.reset(); } catch (std::runtime_error&) {} - try { _plane.reset(); } catch (std::runtime_error&) {} } protected: @@ -363,6 +460,7 @@ private: return (renderAccuracy > 0.0f); } + uint16_t _fps; void draw() { if (!_ready) { return; @@ -370,17 +468,21 @@ private: if (!isVisible()) { return; } + if (_renderCount.load() >= _renderThread._presentCount.load()) { + return; + } + _renderCount = _renderThread._presentCount.load(); update(); - _offscreenContext->makeCurrent(); - - RenderArgs renderArgs(_gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE, + RenderArgs renderArgs(_renderThread._gpuContext, _octree.data(), DEFAULT_OCTREE_SIZE_SCALE, 0, RenderArgs::DEFAULT_RENDER_MODE, RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE); auto framebufferCache = DependencyManager::get(); QSize windowSize = size(); framebufferCache->setFrameBufferSize(windowSize); + + renderArgs._blitFramebuffer = framebufferCache->getFramebuffer(); // Viewport is assigned to the size of the framebuffer renderArgs._viewport = ivec4(0, 0, windowSize.width(), windowSize.height()); @@ -398,49 +500,12 @@ private: } // Final framebuffer that will be handled to the display-plugin - { - auto finalFramebuffer = framebufferCache->getFramebuffer(); - renderArgs._blitFramebuffer = finalFramebuffer; - } - _gpuContext->beginFrame(renderArgs._blitFramebuffer); - gpu::doInBatch(renderArgs._context, [&](gpu::Batch& batch) { - batch.resetStages(); - }); render(&renderArgs); - _gpuContext->endFrame(); - GLuint glTex; - { - auto gpuTex = renderArgs._blitFramebuffer->getRenderBuffer(0); - glTex = gpu::Backend::getGPUObject(*gpuTex)->_id; - } - makeCurrent(); - { - glBindTexture(GL_TEXTURE_2D, glTex); - _quadProgram->Use(); - _plane->Use(); - _plane->Draw(); - glBindVertexArray(0); - } - - { - //_textOverlay->render(); - } - - _context.swapBuffers(this); - - _offscreenContext->makeCurrent(); - framebufferCache->releaseFramebuffer(renderArgs._blitFramebuffer); - renderArgs._blitFramebuffer.reset(); - _fpsCounter.increment(); - static size_t _frameCount { 0 }; - ++_frameCount; - if (_elapsed.elapsed() >= 500) { - _fps = _fpsCounter.rate(); + if (_fps != _renderThread._fps) { + _fps = _renderThread._fps; updateText(); - _frameCount = 0; - _elapsed.restart(); } } @@ -467,7 +532,12 @@ private: void updateText() { - //qDebug() << "FPS " << fps.rate(); + setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") + .arg(_fps).arg(_cullingEnabled) + .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) + .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2))); + +#if 0 { _textBlocks.erase(TextBlock::Info); auto& infoTextBlock = _textBlocks[TextBlock::Info]; @@ -475,13 +545,10 @@ private: infoTextBlock.push_back({ vec2(100, 10), std::to_string((uint32_t)_fps), TextOverlay::alignLeft }); infoTextBlock.push_back({ vec2(98, 30), "Culling: ", TextOverlay::alignRight }); infoTextBlock.push_back({ vec2(100, 30), _cullingEnabled ? "Enabled" : "Disabled", TextOverlay::alignLeft }); - - setTitle(QString("FPS %1 Culling %2 TextureMemory GPU %3 CPU %4") - .arg(_fps).arg(_cullingEnabled) - .arg(toHumanSize(gpu::Context::getTextureGPUMemoryUsage(), 2)) - .arg(toHumanSize(gpu::Texture::getTextureCPUMemoryUsage(), 2))); } +#endif +#if 0 _textOverlay->beginTextUpdate(); for (const auto& e : _textBlocks) { for (const auto& b : e.second) { @@ -489,6 +556,7 @@ private: } } _textOverlay->endTextUpdate(); +#endif } void update() { @@ -525,6 +593,11 @@ private: } void render(RenderArgs* renderArgs) { + auto& gpuContext = renderArgs->_context; + gpuContext->beginFrame(); + gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + batch.resetStages(); + }); PROFILE_RANGE(__FUNCTION__); PerformanceTimer perfTimer("draw"); // The pending changes collecting the changes here @@ -544,6 +617,14 @@ private: // Before the deferred pass, let's try to use the render engine _renderEngine->run(); } + auto frame = gpuContext->endFrame(); + frame->framebuffer = renderArgs->_blitFramebuffer; + frame->framebufferRecycler = [](const gpu::FramebufferPointer& framebuffer){ + DependencyManager::get()->releaseFramebuffer(framebuffer); + }; + _renderThread.queueItem(frame); + + } bool makeCurrent() { @@ -558,9 +639,8 @@ private: if (!_ready) { return; } - _textOverlay->resize(toGlm(_size)); - makeCurrent(); - glViewport(0, 0, size.width(), size.height()); + //_textOverlay->resize(toGlm(_size)); + //glViewport(0, 0, size.width(), size.height()); } void parsePath(const QString& viewpointString) { @@ -704,32 +784,29 @@ private: std::map> _textBlocks; - gpu::ContextPointer _gpuContext; // initialized during window creation render::EnginePointer _renderEngine { new render::Engine() }; render::ScenePointer _main3DScene { new render::Scene(glm::vec3(-0.5f * (float)TREE_SCALE), (float)TREE_SCALE) }; - OffscreenGLCanvas* _offscreenContext { nullptr }; QOpenGLContextWrapper _context; QSize _size; - RateCounter<200> _fpsCounter; QSettings _settings; - ProgramPtr _quadProgram; - ShapeWrapperPtr _plane; - + std::atomic _renderCount; + OffscreenGLCanvas _initContext; + RenderThread _renderThread; QWindowCamera _camera; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _shadowViewFrustum; // current state of view frustum, perspective, orientation, etc. model::SunSkyStage _sunSkyStage; model::LightPointer _globalLight { std::make_shared() }; - QElapsedTimer _elapsed; bool _ready { false }; - float _fps { 0 }; - TextOverlay* _textOverlay; - bool _cullingEnabled { true }; + //TextOverlay* _textOverlay; + static bool _cullingEnabled; bool _stereoEnabled { false }; QSharedPointer _octree; }; +bool QTestWindow::_cullingEnabled = false; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (!message.isEmpty()) { #ifdef Q_OS_WIN