From 54dfc33b38848c6d8a76effcec3227fbaa9f4151 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 14 Jun 2017 14:36:37 -0700 Subject: [PATCH 01/18] initial skeleton of qml objects --- .../resources/qml/hifi/SpectatorCamera.qml | 26 ++++++++--------- interface/src/Application.cpp | 18 ++++++------ interface/src/ui/ResourceImageItem.cpp | 28 +++++++++++++++++++ interface/src/ui/ResourceImageItem.h | 27 ++++++++++++++++++ 4 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 interface/src/ui/ResourceImageItem.cpp create mode 100644 interface/src/ui/ResourceImageItem.h diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 544fc72927..37e8fdee01 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -11,6 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +import Hifi 1.0 as Hifi import QtQuick 2.5 import QtQuick.Controls 1.4 import "../styles-uit" @@ -25,7 +26,7 @@ Rectangle { id: spectatorCamera; // Style color: hifi.colors.baseGray; - + // // TITLE BAR START // @@ -64,7 +65,7 @@ Rectangle { // // TITLE BAR END // - + // // SPECTATOR APP DESCRIPTION START // @@ -139,7 +140,7 @@ Rectangle { // Alignment horizontalAlignment: Text.AlignHLeft; verticalAlignment: Text.AlignVCenter; - + MouseArea { anchors.fill: parent; hoverEnabled: enabled; @@ -163,7 +164,7 @@ Rectangle { // SPECTATOR APP DESCRIPTION END // - + // // SPECTATOR CONTROLS START // @@ -193,19 +194,18 @@ Rectangle { } // Spectator Camera Preview - Image { + Rectangle { id: spectatorCameraPreview; height: 250; anchors.left: parent.left; anchors.top: cameraToggleCheckBox.bottom; anchors.topMargin: 20; anchors.right: parent.right; - fillMode: Image.PreserveAspectFit; - horizontalAlignment: Image.AlignHCenter; - verticalAlignment: Image.AlignVCenter; - source: "http://1.bp.blogspot.com/-1GABEq__054/T03B00j_OII/AAAAAAAAAa8/jo55LcvEPHI/s1600/Winning.jpg"; + Hifi.ResourceImageItem { + anchors.fill: parent; + } } - + // "Monitor Shows" Switch Label Glyph HiFiGlyphs { id: monitorShowsSwitchLabelGlyph; @@ -259,7 +259,7 @@ Rectangle { sendToScript({method: 'changeSwitchViewFromControllerPreference', params: checked}); } } - } + } // // SPECTATOR CONTROLS END // @@ -272,11 +272,11 @@ Rectangle { // // Relevant Variables: // None - // + // // Arguments: // message: The message sent from the SpectatorCamera JavaScript. // Messages are in format "{method, params}", like json-rpc. - // + // // Description: // Called when a message is received from spectatorCamera.js. // diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ee1292f4bf..ca4954860a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -170,6 +170,7 @@ #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif +#include "ui/ResourceImageItem.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" #include "ui/DialogsManager.h" @@ -214,7 +215,7 @@ static QTimer pingTimer; static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; -// For processing on QThreadPool, we target a number of threads after reserving some +// For processing on QThreadPool, we target a number of threads after reserving some // based on how many are being consumed by the application and the display plugin. However, // we will never drop below the 'min' value static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1; @@ -1247,7 +1248,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Add periodic checks to send user activity data static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; static int NEARBY_AVATAR_RADIUS_METERS = 10; - + // setup the stats interval depending on if the 1s faster hearbeat was requested static const QString FAST_STATS_ARG = "--fast-heartbeat"; static int SEND_STATS_INTERVAL_MS = arguments().indexOf(FAST_STATS_ARG) != -1 ? 1000 : 10000; @@ -1397,10 +1398,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _autoSwitchDisplayModeSupportedHMDPlugin = nullptr; foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { - if (displayPlugin->isHmd() && + if (displayPlugin->isHmd() && displayPlugin->getSupportsAutoSwitch()) { _autoSwitchDisplayModeSupportedHMDPlugin = displayPlugin; - _autoSwitchDisplayModeSupportedHMDPluginName = + _autoSwitchDisplayModeSupportedHMDPluginName = _autoSwitchDisplayModeSupportedHMDPlugin->getName(); _previousHMDWornStatus = _autoSwitchDisplayModeSupportedHMDPlugin->isDisplayVisible(); @@ -1653,7 +1654,7 @@ void Application::aboutToQuit() { } getActiveDisplayPlugin()->deactivate(); - if (_autoSwitchDisplayModeSupportedHMDPlugin + if (_autoSwitchDisplayModeSupportedHMDPlugin && _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) { _autoSwitchDisplayModeSupportedHMDPlugin->endSession(); } @@ -1918,6 +1919,7 @@ void Application::initializeUi() { LoginDialog::registerType(); Tooltip::registerType(); UpdateDialog::registerType(); + qmlRegisterType("Hifi", 1, 0, "ResourceImageItem"); qmlRegisterType("Hifi", 1, 0, "Preference"); auto offscreenUi = DependencyManager::get(); @@ -5231,7 +5233,7 @@ void Application::clearDomainOctreeDetails() { skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); _recentlyClearedDomain = true; - + DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); @@ -5793,7 +5795,7 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } } -void Application::toggleRunningScriptsWidget() const { +void Application::toggleRunningScriptsWidget() const { auto scriptEngines = DependencyManager::get(); bool scriptsRunning = !scriptEngines->getRunningScripts().isEmpty(); auto tabletScriptingInterface = DependencyManager::get(); @@ -5892,7 +5894,7 @@ void Application::showDialog(const QString& desktopURL, const QString& tabletURL if (!hmd->getShouldShowTablet() && !isHMDMode()) { hmd->openTablet(); } - + } } diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp new file mode 100644 index 0000000000..b1869d5ed2 --- /dev/null +++ b/interface/src/ui/ResourceImageItem.cpp @@ -0,0 +1,28 @@ +// +// ResourceImageItem.cpp +// +// Created by David Kelly and Howard Stearns on 2017/06/08 +// Copyright 2017 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 "ResourceImageItem.h" +#include +#include +#include + +QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(const QSize& size) { + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + return new QOpenGLFramebufferObject(size, format); +} + +void ResourceImageItemRenderer::render() { + auto texture = DependencyManager::get()->getTexture(QUrl("resource://spectatorCameraFrame")); + if (texture) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->getGPUTexture(), 0); + } + +} diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h new file mode 100644 index 0000000000..6d8b33ae64 --- /dev/null +++ b/interface/src/ui/ResourceImageItem.h @@ -0,0 +1,27 @@ +// +// ResourceImageItem.h +// +// Created by David Kelly and Howard Stearns on 2017/06/08 +// Copyright 2017 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 +// + +#pragma once +#ifndef hifi_ResourceImageItem_h +#define hifi_ResourceImageItem_h + +#include +#include + +class ResourceImageItemRenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions { + QOpenGLFramebufferObject* createFramebufferObject(const QSize& size); + void render(); +}; + +class ResourceImageItem : public QQuickFramebufferObject { + QQuickFramebufferObject::Renderer* createRenderer() const { return new ResourceImageItemRenderer; } +}; + +#endif // hifi_ResourceImageItem_h From 3d922f5db63c1cb4a0aab88bf014a2f59e359f3b Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 15 Jun 2017 14:17:43 -0700 Subject: [PATCH 02/18] works, sorta, but blinks like crazy, cursor gets f-ed up... --- .../resources/qml/hifi/SpectatorCamera.qml | 4 ++ interface/src/ui/ResourceImageItem.cpp | 55 +++++++++++++++++-- interface/src/ui/ResourceImageItem.h | 26 ++++++++- .../display-plugins/OpenGLDisplayPlugin.cpp | 32 ++++++++--- .../src/display-plugins/OpenGLDisplayPlugin.h | 6 +- libraries/plugins/src/plugins/DisplayPlugin.h | 12 ++-- 6 files changed, 116 insertions(+), 19 deletions(-) diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 37e8fdee01..44d4759cdf 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -190,6 +190,7 @@ Rectangle { boxSize: 24; onClicked: { sendToScript({method: (checked ? 'spectatorCameraOn' : 'spectatorCameraOff')}); + spectatorCameraPreviewItem.ready = checked; } } @@ -202,7 +203,10 @@ Rectangle { anchors.topMargin: 20; anchors.right: parent.right; Hifi.ResourceImageItem { + id: spectatorCameraPreviewItem; anchors.fill: parent; + url: "resource://spectatorCameraFrame"; + ready: false; } } diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index b1869d5ed2..f1ffae06b5 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -8,11 +8,55 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Application.h" #include "ResourceImageItem.h" #include #include #include + +ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObject(parent) { + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(update())); +} + +void ResourceImageItem::setUrl(const QString& url) { + if (url != m_url) { + m_url = url; + update(); + } +} + +void ResourceImageItem::setReady(bool ready) { + if (ready != m_ready) { + m_ready = ready; + if (m_ready) { + m_updateTimer.start(1000); + } else { + m_updateTimer.stop(); + } + update(); + } +} + +void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { + ResourceImageItem* resourceImageItem = static_cast(item); + bool urlChanged = false; + if( _url != resourceImageItem->getUrl()) { + _url = resourceImageItem->getUrl(); + urlChanged = true; + } + bool readyChanged = false; + if (_ready != resourceImageItem->getReady()) { + _ready = resourceImageItem->getReady(); + readyChanged = true; + } + _window = resourceImageItem->window(); + qDebug() << "synchronize called!!!!!!!"; + if (_ready && !_url.isNull() && !_url.isEmpty() && (readyChanged || urlChanged)) { + _networkTexture = DependencyManager::get()->getTexture(_url); + } +} + QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(const QSize& size) { QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); @@ -20,9 +64,12 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con } void ResourceImageItemRenderer::render() { - auto texture = DependencyManager::get()->getTexture(QUrl("resource://spectatorCameraFrame")); - if (texture) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->getGPUTexture(), 0); + qDebug() << "render called!!!!!!!!!!!!!!"; + if (_networkTexture && _ready) { + auto texture = _networkTexture->getGPUTexture(); + if (texture) { + qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(texture, framebufferObject()); + _window->resetOpenGLState(); + } } - } diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index 6d8b33ae64..a1f332a817 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -13,15 +13,37 @@ #define hifi_ResourceImageItem_h #include -#include +#include +#include -class ResourceImageItemRenderer : public QQuickFramebufferObject::Renderer, protected QOpenGLFunctions { +class ResourceImageItemRenderer : public QQuickFramebufferObject::Renderer { +public: QOpenGLFramebufferObject* createFramebufferObject(const QSize& size); + void synchronize(QQuickFramebufferObject* item); void render(); +private: + bool _ready; + QString _url; + NetworkTexturePointer _networkTexture; + QQuickWindow* _window; }; class ResourceImageItem : public QQuickFramebufferObject { + Q_OBJECT + Q_PROPERTY(QString url READ getUrl WRITE setUrl) + Q_PROPERTY(bool ready READ getReady WRITE setReady) + +public: + ResourceImageItem(QQuickItem* parent = Q_NULLPTR); + QString getUrl() const { return m_url; } + void setUrl(const QString& url); + bool getReady() const { return m_ready; } + void setReady(bool ready); QQuickFramebufferObject::Renderer* createRenderer() const { return new ResourceImageItemRenderer; } +private: + QString m_url; + bool m_ready { false }; + QTimer m_updateTimer; // TODO: something more clever }; #endif // hifi_ResourceImageItem_h diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index ba8842c2ec..c331d1a233 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -16,6 +16,7 @@ #include #include +#include #if defined(Q_OS_MAC) #include #endif @@ -55,7 +56,7 @@ out vec4 outFragColor; float sRGBFloatToLinear(float value) { const float SRGB_ELBOW = 0.04045; - + return (value <= SRGB_ELBOW) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4); } @@ -121,10 +122,10 @@ public: PROFILE_SET_THREAD_NAME("Present Thread"); // FIXME determine the best priority balance between this and the main thread... - // It may be dependent on the display plugin being used, since VR plugins should + // It may be dependent on the display plugin being used, since VR plugins should // have higher priority on rendering (although we could say that the Oculus plugin // doesn't need that since it has async timewarp). - // A higher priority here + // A higher priority here setPriority(QThread::HighPriority); OpenGLDisplayPlugin* currentPlugin{ nullptr }; Q_ASSERT(_context); @@ -233,7 +234,7 @@ public: // Move the context back to the presentation thread _context->moveToThread(this); - // restore control of the context to the presentation thread and signal + // restore control of the context to the presentation thread and signal // the end of the operation _finishedMainThreadOperation = true; lock.unlock(); @@ -291,7 +292,7 @@ bool OpenGLDisplayPlugin::activate() { if (!RENDER_THREAD) { RENDER_THREAD = _presentThread; } - + // Child classes may override this in order to do things like initialize // libraries, etc if (!internalActivate()) { @@ -411,7 +412,7 @@ void OpenGLDisplayPlugin::customizeContext() { gpu::Shader::makeProgram(*program); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(gpu::State::DepthTest(false)); - state->setBlendFunction(true, + 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); @@ -686,7 +687,7 @@ void OpenGLDisplayPlugin::resetPresentRate() { // _presentRate = RateCounter<100>(); } -float OpenGLDisplayPlugin::renderRate() const { +float OpenGLDisplayPlugin::renderRate() const { return _renderRate.rate(); } @@ -820,3 +821,20 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y)); } } + +void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) { + auto glBackend = const_cast(*this).getGLBackend(); + withMainThreadContext([&] { + GLuint sourceTexture = glBackend->getTextureID(source); + GLuint targetTexture = target->texture(); + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); + glCopyTextureSubImage2D(targetTexture, 0, 0, 0, 0, 0, target->width(), target->height()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &fbo); + }); +} + diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index cf874fb721..7e7fcd1116 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -38,7 +38,7 @@ protected: using Condition = std::condition_variable; public: ~OpenGLDisplayPlugin(); - // These must be final to ensure proper ordering of operations + // These must be final to ensure proper ordering of operations // between the main thread and the presentation thread bool activate() override final; void deactivate() override final; @@ -78,6 +78,8 @@ public: // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver int getRequiredThreadCount() const override { return 3; } + void copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target); + protected: friend class PresentThread; @@ -102,7 +104,7 @@ protected: // Returns true on successful activation virtual bool internalActivate() { return true; } virtual void internalDeactivate() {} - + // Returns true on successful activation of standby session virtual bool activateStandBySession() { return true; } virtual void deactivateSession() {} diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 7bfdbddbc5..d45320ec9a 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -25,6 +25,8 @@ #include "Plugin.h" +class QOpenGLFramebufferObject; + class QImage; enum Eye { @@ -61,7 +63,7 @@ namespace gpu { } // Stereo display functionality -// TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when +// TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when // displayPlugin->isStereo returns true class StereoDisplay { public: @@ -78,7 +80,7 @@ public: }; // HMD display functionality -// TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when +// TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when // displayPlugin->isHmd returns true class HmdDisplay : public StereoDisplay { public: @@ -142,7 +144,7 @@ public: virtual float getTargetFrameRate() const { return 1.0f; } virtual bool hasAsyncReprojection() const { return false; } - /// Returns a boolean value indicating whether the display is currently visible + /// Returns a boolean value indicating whether the display is currently visible /// to the user. For monitor displays, false might indicate that a screensaver, /// or power-save mode is active. For HMDs it may reflect a sensor indicating /// whether the HMD is being worn @@ -204,10 +206,12 @@ public: // Rate at which rendered frames are being skipped virtual float droppedFrameRate() const { return -1.0f; } virtual bool getSupportsAutoSwitch() { return false; } - + // Hardware specific stats virtual QJsonObject getHardwareStats() const { return QJsonObject(); } + virtual void copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) = 0; + uint32_t presentCount() const { return _presentedFrameIndex; } // Time since last call to incrementPresentCount (only valid if DEBUG_PAINT_DELAY is defined) int64_t getPaintDelayUsecs() const; From 6d64261e94ef3fad8eff7234799f365ecaa76a44 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 16 Jun 2017 08:40:18 -0700 Subject: [PATCH 03/18] working but often the spectator camera texture is not right. --- interface/src/ui/ResourceImageItem.cpp | 34 +++++++++++++------ interface/src/ui/ResourceImageItem.h | 3 ++ .../src/model-networking/TextureCache.cpp | 22 ++++++------ 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index f1ffae06b5..b2e6833682 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -30,7 +30,7 @@ void ResourceImageItem::setReady(bool ready) { if (ready != m_ready) { m_ready = ready; if (m_ready) { - m_updateTimer.start(1000); + m_updateTimer.start(100); } else { m_updateTimer.stop(); } @@ -40,6 +40,7 @@ void ResourceImageItem::setReady(bool ready) { void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { ResourceImageItem* resourceImageItem = static_cast(item); + bool urlChanged = false; if( _url != resourceImageItem->getUrl()) { _url = resourceImageItem->getUrl(); @@ -50,11 +51,27 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { _ready = resourceImageItem->getReady(); readyChanged = true; } + + if (!_ready && readyChanged) { + qDebug() << "clearing network texture!!!!!!!!!!!!!!!!!"; + _networkTexture.clear(); + } + _window = resourceImageItem->window(); - qDebug() << "synchronize called!!!!!!!"; - if (_ready && !_url.isNull() && !_url.isEmpty() && (readyChanged || urlChanged)) { + if (_ready && !_url.isNull() && !_url.isEmpty() && (urlChanged || readyChanged || !_networkTexture)) { _networkTexture = DependencyManager::get()->getTexture(_url); } + + if (_networkTexture) { + qDebug() << "copying texture"; + auto texture = _networkTexture->getGPUTexture(); + if (texture) { + if (_fboMutex.tryLock()) { + qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(texture, framebufferObject()); + _fboMutex.unlock(); + } + } + } } QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(const QSize& size) { @@ -64,12 +81,7 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con } void ResourceImageItemRenderer::render() { - qDebug() << "render called!!!!!!!!!!!!!!"; - if (_networkTexture && _ready) { - auto texture = _networkTexture->getGPUTexture(); - if (texture) { - qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(texture, framebufferObject()); - _window->resetOpenGLState(); - } - } + _fboMutex.lock(); + _window->resetOpenGLState(); + _fboMutex.unlock(); } diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index a1f332a817..aeb12c8fba 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -24,8 +24,11 @@ public: private: bool _ready; QString _url; + NetworkTexturePointer _networkTexture; QQuickWindow* _window; + QOpenGLFramebufferObject* _copyFbo; + QMutex _fboMutex; }; class ResourceImageItem : public QQuickFramebufferObject { diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 0ecd8e8aab..276ed15acc 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -221,7 +221,7 @@ gpu::TexturePointer TextureCache::cacheTextureByHash(const std::string& hash, co gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { gpu::TexturePointer result; auto textureCache = DependencyManager::get(); - // Since this can be called on a background thread, there's a chance that the cache + // Since this can be called on a background thread, there's a chance that the cache // will be destroyed by the time we request it if (!textureCache) { return result; @@ -373,7 +373,7 @@ void NetworkTexture::makeRequest() { if (!_sourceIsKTX) { Resource::makeRequest(); return; - } + } // We special-handle ktx requests to run 2 concurrent requests right off the bat PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } }); @@ -682,7 +682,7 @@ void NetworkTexture::maybeHandleFinishedInitialLoad() { imageSizeRemaining -= (image._imageSize + ktx::IMAGE_SIZE_WIDTH); } - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner texture = textureCache->cacheTextureByHash(filename, texture); @@ -823,7 +823,7 @@ void ImageReader::read() { } } - // If we found the texture either because it's in use or via KTX deserialization, + // If we found the texture either because it's in use or via KTX deserialization, // set the image and return immediately. if (texture) { QMetaObject::invokeMethod(resource.data(), "setImage", @@ -862,7 +862,7 @@ void ImageReader::read() { const char* data = reinterpret_cast(memKtx->_storage->data()); size_t length = memKtx->_storage->size(); auto& ktxCache = textureCache->_ktxCache; - networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); // + networkTexture->_file = ktxCache.writeFile(data, KTXCache::Metadata(hash, length)); // if (!networkTexture->_file) { qCWarning(modelnetworking) << _url << "file cache failed"; } else { @@ -872,7 +872,7 @@ void ImageReader::read() { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; } - // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different + // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner texture = textureCache->cacheTextureByHash(hash, texture); @@ -891,10 +891,12 @@ NetworkTexturePointer TextureCache::getResourceTexture(QUrl resourceTextureUrl) if (!_spectatorCameraNetworkTexture) { _spectatorCameraNetworkTexture.reset(new NetworkTexture(resourceTextureUrl)); } - texture = _spectatorCameraFramebuffer->getRenderBuffer(0); - if (texture) { - _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); - return _spectatorCameraNetworkTexture; + if (_spectatorCameraFramebuffer) { + texture = _spectatorCameraFramebuffer->getRenderBuffer(0); + if (texture) { + _spectatorCameraNetworkTexture->setImage(texture, texture->getWidth(), texture->getHeight()); + return _spectatorCameraNetworkTexture; + } } } From 9b5b99c2b9f33476b302daaa23f7fab0f1b85eb1 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 16 Jun 2017 08:59:57 -0700 Subject: [PATCH 04/18] not as buggy. Still need ogl work, major cleanup, etc... --- interface/src/ui/ResourceImageItem.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index b2e6833682..bf51d703b2 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -22,7 +22,6 @@ ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObje void ResourceImageItem::setUrl(const QString& url) { if (url != m_url) { m_url = url; - update(); } } @@ -30,11 +29,14 @@ void ResourceImageItem::setReady(bool ready) { if (ready != m_ready) { m_ready = ready; if (m_ready) { + // 10 HZ for now. Also this serves as a small + // delay before getting the network texture, which + // helps a lot. TODO: find better way. m_updateTimer.start(100); } else { m_updateTimer.stop(); + update(); } - update(); } } @@ -62,7 +64,7 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { _networkTexture = DependencyManager::get()->getTexture(_url); } - if (_networkTexture) { + if (_networkTexture && _networkTexture->isLoaded()) { qDebug() << "copying texture"; auto texture = _networkTexture->getGPUTexture(); if (texture) { From 0f799e52f457dbaba5d03a5121e2d3578c2f6003 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 16 Jun 2017 15:30:36 -0700 Subject: [PATCH 05/18] blit entire texture. Only issue is that it is upside down --- .../display-plugins/OpenGLDisplayPlugin.cpp | 31 ++++++++++++++----- .../src/display-plugins/OpenGLDisplayPlugin.h | 2 +- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 456d338db9..b044477c1f 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -826,16 +826,31 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) { auto glBackend = const_cast(*this).getGLBackend(); withMainThreadContext([&] { + qDebug() << "initial gl error:" << glGetError(); GLuint sourceTexture = glBackend->getTextureID(source); GLuint targetTexture = target->texture(); - GLuint fbo { 0 }; - glCreateFramebuffers(1, &fbo); - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); - glCopyTextureSubImage2D(targetTexture, 0, 0, 0, 0, 0, target->width(), target->height()); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbo); - glDeleteTextures(1, &fbo); + GLuint fbo[2] {0, 0}; + + glGenerateTextureMipmap(sourceTexture); + qDebug() << "errors: " << glGetError(); + + glCreateFramebuffers(2, fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); + qDebug() << "errors: " << glGetError(); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); + qDebug() << "errors: " << glGetError(); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0); + qDebug() << "errors: " << glGetError(); + + glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, source->getWidth(), source->getHeight(), 0, 0, target->width(), target->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + qDebug() << "errors: " << glGetError() << "bound?" << target->isBound(); + + glDeleteFramebuffers(2, fbo); + glDeleteTextures(1, fbo); + glFinish(); }); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 945708f6a7..58a1157969 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -79,7 +79,7 @@ public: // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver int getRequiredThreadCount() const override { return 3; } - void copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target); + void copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) override; protected: friend class PresentThread; From 5284b8dc0c4ff50fd51a163033dd42dea1b33899 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 16 Jun 2017 15:37:25 -0700 Subject: [PATCH 06/18] fix for upside down preview, for now --- interface/resources/qml/hifi/SpectatorCamera.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 44d4759cdf..2016b1704b 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -207,6 +207,7 @@ Rectangle { anchors.fill: parent; url: "resource://spectatorCameraFrame"; ready: false; + mirrorVertically: true; } } From c7e4649ec20135a568d99a9dfbb24ba0cb817834 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Sat, 17 Jun 2017 13:15:22 -0700 Subject: [PATCH 07/18] add override to avoid warnings in ubuntu/mac --- interface/src/ui/ResourceImageItem.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index aeb12c8fba..bf3c44e205 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -18,9 +18,9 @@ class ResourceImageItemRenderer : public QQuickFramebufferObject::Renderer { public: - QOpenGLFramebufferObject* createFramebufferObject(const QSize& size); - void synchronize(QQuickFramebufferObject* item); - void render(); + QOpenGLFramebufferObject* createFramebufferObject(const QSize& size) override; + void synchronize(QQuickFramebufferObject* item) override; + void render() override; private: bool _ready; QString _url; @@ -42,7 +42,7 @@ public: void setUrl(const QString& url); bool getReady() const { return m_ready; } void setReady(bool ready); - QQuickFramebufferObject::Renderer* createRenderer() const { return new ResourceImageItemRenderer; } + QQuickFramebufferObject::Renderer* createRenderer() const override { return new ResourceImageItemRenderer; } private: QString m_url; bool m_ready { false }; From b4c75c3f877bed841c53cedaacd1564919857468 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 19 Jun 2017 14:41:42 -0700 Subject: [PATCH 08/18] working, except tablet issue --- .../resources/qml/hifi/SpectatorCamera.qml | 14 +++++------- interface/src/ui/ResourceImageItem.cpp | 17 ++++---------- .../display-plugins/OpenGLDisplayPlugin.cpp | 22 +++++++++---------- .../src/display-plugins/OpenGLDisplayPlugin.h | 2 +- libraries/plugins/src/plugins/DisplayPlugin.h | 6 +++-- 5 files changed, 24 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 7058c49fe6..c376a653e6 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -190,25 +190,21 @@ Rectangle { boxSize: 24; onClicked: { sendToScript({method: (checked ? 'spectatorCameraOn' : 'spectatorCameraOff')}); - spectatorCameraPreviewItem.ready = checked; + spectatorCameraPreview.ready = checked; } } // Spectator Camera Preview - Rectangle { + Hifi.ResourceImageItem { id: spectatorCameraPreview; + url: "resource://spectatorCameraFrame"; + ready: false; + mirrorVertically: true; height: 250; anchors.left: parent.left; anchors.top: cameraToggleCheckBox.bottom; anchors.topMargin: 20; anchors.right: parent.right; - Hifi.ResourceImageItem { - id: spectatorCameraPreviewItem; - anchors.fill: parent; - url: "resource://spectatorCameraFrame"; - ready: false; - mirrorVertically: true; - } } // "Monitor Shows" Switch Label Glyph diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index bf51d703b2..6a5f4a431c 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -54,24 +54,15 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { readyChanged = true; } - if (!_ready && readyChanged) { - qDebug() << "clearing network texture!!!!!!!!!!!!!!!!!"; - _networkTexture.clear(); - } - _window = resourceImageItem->window(); if (_ready && !_url.isNull() && !_url.isEmpty() && (urlChanged || readyChanged || !_networkTexture)) { _networkTexture = DependencyManager::get()->getTexture(_url); } - if (_networkTexture && _networkTexture->isLoaded()) { - qDebug() << "copying texture"; - auto texture = _networkTexture->getGPUTexture(); - if (texture) { - if (_fboMutex.tryLock()) { - qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(texture, framebufferObject()); - _fboMutex.unlock(); - } + if (_ready && _networkTexture && _networkTexture->isLoaded()) { + if(_fboMutex.tryLock()) { + qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, framebufferObject()); + _fboMutex.unlock(); } } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index b044477c1f..6050211eba 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -42,7 +42,7 @@ #include #include #include - +#include #include "CompositorHelper.h" #include "Logging.h" @@ -823,33 +823,31 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { } } -void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) { +void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target) { auto glBackend = const_cast(*this).getGLBackend(); withMainThreadContext([&] { - qDebug() << "initial gl error:" << glGetError(); - GLuint sourceTexture = glBackend->getTextureID(source); + GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture()); GLuint targetTexture = target->texture(); GLuint fbo[2] {0, 0}; + // need mipmaps for blitting texture glGenerateTextureMipmap(sourceTexture); - qDebug() << "errors: " << glGetError(); + // create 2 fbos (one for initial texture, second for scaled one) glCreateFramebuffers(2, fbo); + + // setup source fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); - qDebug() << "errors: " << glGetError(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); - qDebug() << "errors: " << glGetError(); + // setup destination fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0); - qDebug() << "errors: " << glGetError(); - glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, source->getWidth(), source->getHeight(), 0, 0, target->width(), target->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); - qDebug() << "errors: " << glGetError() << "bound?" << target->isBound(); + glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, networkTexture->getWidth(), networkTexture->getHeight(), 0, 0, target->width(), target->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + // don't delete the textures! glDeleteFramebuffers(2, fbo); - glDeleteTextures(1, fbo); glFinish(); }); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 58a1157969..d9e810bf97 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -79,7 +79,7 @@ public: // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver int getRequiredThreadCount() const override { return 3; } - void copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) override; + void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target); protected: friend class PresentThread; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index d45320ec9a..e9b31b5c35 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -22,7 +22,6 @@ #include #include #include - #include "Plugin.h" class QOpenGLFramebufferObject; @@ -62,6 +61,9 @@ namespace gpu { using TexturePointer = std::shared_ptr; } +class NetworkTexture; +using NetworkTexturePointer = QSharedPointer; + // Stereo display functionality // TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when // displayPlugin->isStereo returns true @@ -210,7 +212,7 @@ public: // Hardware specific stats virtual QJsonObject getHardwareStats() const { return QJsonObject(); } - virtual void copyTextureToQuickFramebuffer(gpu::TexturePointer source, QOpenGLFramebufferObject* target) = 0; + virtual void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target) = 0; uint32_t presentCount() const { return _presentedFrameIndex; } // Time since last call to incrementPresentCount (only valid if DEBUG_PAINT_DELAY is defined) From 06b59fe13e9f00734ab259b45b8dae050412dffc Mon Sep 17 00:00:00 2001 From: David Kelly Date: Tue, 20 Jun 2017 15:02:05 -0700 Subject: [PATCH 09/18] still broken, but added initial cut of GLsync stuff --- interface/src/ui/ResourceImageItem.cpp | 33 +++++++++++++++++-- interface/src/ui/ResourceImageItem.h | 3 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 11 +++++-- .../src/display-plugins/OpenGLDisplayPlugin.h | 2 +- libraries/plugins/src/plugins/DisplayPlugin.h | 3 +- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 6a5f4a431c..9051305a2e 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -10,11 +10,15 @@ #include "Application.h" #include "ResourceImageItem.h" + #include +#include +#include +#include + #include #include - ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObject(parent) { connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(update())); } @@ -55,26 +59,49 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { } _window = resourceImageItem->window(); + _window->setClearBeforeRendering(true); if (_ready && !_url.isNull() && !_url.isEmpty() && (urlChanged || readyChanged || !_networkTexture)) { _networkTexture = DependencyManager::get()->getTexture(_url); } if (_ready && _networkTexture && _networkTexture->isLoaded()) { if(_fboMutex.tryLock()) { - qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, framebufferObject()); + qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, _copyFbo, &_fenceSync); _fboMutex.unlock(); } } + glFlush(); + } QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(const QSize& size) { QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + _copyFbo = new QOpenGLFramebufferObject(size, format); return new QOpenGLFramebufferObject(size, format); } void ResourceImageItemRenderer::render() { + qDebug() << "initial error" << glGetError(); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + QOpenGLExtraFunctions* extras = QOpenGLContext::currentContext()->extraFunctions(); _fboMutex.lock(); - _window->resetOpenGLState(); + if (_fenceSync) { + extras->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); + qDebug() << "wait error" << glGetError(); + qDebug() << "waited on fence"; + } + if (_ready) { + QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST); + /*f->glBindTexture(GL_TEXTURE_2D, _copyFbo->texture()); + qDebug() << "bind tex error" << f->glGetError() << "texture" << _copyFbo->texture(); + f->glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _copyFbo->width(), _copyFbo->height());*/ + qDebug() << "copy error" << f->glGetError(); + } else { + framebufferObject()->release(); + } _fboMutex.unlock(); + update(); } diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index bf3c44e205..9b1d685ee1 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -27,8 +27,9 @@ private: NetworkTexturePointer _networkTexture; QQuickWindow* _window; - QOpenGLFramebufferObject* _copyFbo; QMutex _fboMutex; + QOpenGLFramebufferObject* _copyFbo; + GLsync _fenceSync { 0 }; }; class ResourceImageItem : public QQuickFramebufferObject { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 6050211eba..e028fd14c1 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -823,32 +823,37 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { } } -void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target) { +void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { auto glBackend = const_cast(*this).getGLBackend(); withMainThreadContext([&] { GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture()); GLuint targetTexture = target->texture(); GLuint fbo[2] {0, 0}; - + qDebug() << "initial" << glGetError(); // need mipmaps for blitting texture glGenerateTextureMipmap(sourceTexture); // create 2 fbos (one for initial texture, second for scaled one) glCreateFramebuffers(2, fbo); + qDebug() << "error" << glGetError(); // setup source fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); + qDebug() << "error" << glGetError(); // setup destination fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0); + qDebug() << "error" << glGetError(); glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, networkTexture->getWidth(), networkTexture->getHeight(), 0, 0, target->width(), target->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + qDebug() << "error" << glGetError(); // don't delete the textures! glDeleteFramebuffers(2, fbo); - glFinish(); + qDebug() << "error" << glGetError(); + *fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); }); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index d9e810bf97..dc2e251d65 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -79,7 +79,7 @@ public: // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver int getRequiredThreadCount() const override { return 3; } - void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target); + void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync); protected: friend class PresentThread; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index e9b31b5c35..481a2609fc 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -63,6 +63,7 @@ namespace gpu { class NetworkTexture; using NetworkTexturePointer = QSharedPointer; +typedef struct __GLsync *GLsync; // Stereo display functionality // TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when @@ -212,7 +213,7 @@ public: // Hardware specific stats virtual QJsonObject getHardwareStats() const { return QJsonObject(); } - virtual void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target) = 0; + virtual void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) = 0; uint32_t presentCount() const { return _presentedFrameIndex; } // Time since last call to incrementPresentCount (only valid if DEBUG_PAINT_DELAY is defined) From f30b12fca6c90341c264801f0e0c3d1aafa7df9f Mon Sep 17 00:00:00 2001 From: David Kelly Date: Wed, 21 Jun 2017 10:28:18 -0700 Subject: [PATCH 10/18] still doesn't work, pushing just to save it --- interface/src/ui/ResourceImageItem.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 9051305a2e..8e101f2892 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -78,30 +78,28 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); _copyFbo = new QOpenGLFramebufferObject(size, format); + _copyFbo->bind(); return new QOpenGLFramebufferObject(size, format); } void ResourceImageItemRenderer::render() { qDebug() << "initial error" << glGetError(); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); - QOpenGLExtraFunctions* extras = QOpenGLContext::currentContext()->extraFunctions(); + /*glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT);*/ + QOpenGLExtraFunctions* f = QOpenGLContext::currentContext()->extraFunctions(); _fboMutex.lock(); if (_fenceSync) { - extras->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); - qDebug() << "wait error" << glGetError(); - qDebug() << "waited on fence"; + f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); + qDebug() << "wait error" << f->glGetError(); } if (_ready) { - QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST); - /*f->glBindTexture(GL_TEXTURE_2D, _copyFbo->texture()); + f->glBindTexture(GL_TEXTURE_2D, _copyFbo->texture()); qDebug() << "bind tex error" << f->glGetError() << "texture" << _copyFbo->texture(); - f->glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _copyFbo->width(), _copyFbo->height());*/ + GLint internalFormat { 0 }; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + qDebug() << "getTexLevelParameteriv error" << f->glGetError() << internalFormat; + f->glCopyTexImage2D(GL_TEXTURE_2D, 0, (GLenum)internalFormat, 0, 0, _copyFbo->width(), _copyFbo->height(), 0); qDebug() << "copy error" << f->glGetError(); - } else { - framebufferObject()->release(); } _fboMutex.unlock(); - update(); } From 02b712716b34ac4e1bf0e09f0691c256e0ba5cbc Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 29 Jun 2017 11:12:10 -0700 Subject: [PATCH 11/18] testing some rendering issues... --- interface/src/ui/ResourceImageItem.cpp | 35 ++++++++------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 9051305a2e..b4a726dcef 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -32,6 +32,8 @@ void ResourceImageItem::setUrl(const QString& url) { void ResourceImageItem::setReady(bool ready) { if (ready != m_ready) { m_ready = ready; + update(); + /* if (m_ready) { // 10 HZ for now. Also this serves as a small // delay before getting the network texture, which @@ -40,7 +42,7 @@ void ResourceImageItem::setReady(bool ready) { } else { m_updateTimer.stop(); update(); - } + }*/ } } @@ -59,18 +61,18 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { } _window = resourceImageItem->window(); - _window->setClearBeforeRendering(true); if (_ready && !_url.isNull() && !_url.isEmpty() && (urlChanged || readyChanged || !_networkTexture)) { _networkTexture = DependencyManager::get()->getTexture(_url); } + resourceImageItem->forceActiveFocus(); if (_ready && _networkTexture && _networkTexture->isLoaded()) { if(_fboMutex.tryLock()) { qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, _copyFbo, &_fenceSync); _fboMutex.unlock(); } + } - glFlush(); } @@ -82,26 +84,11 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con } void ResourceImageItemRenderer::render() { - qDebug() << "initial error" << glGetError(); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + static int colorInt = 0; + float red = (float)((colorInt++ % 20)/(float)20); + glClearColor(red, 0.0f, 0.0f, 0.5f + red/2.0f); glClear(GL_COLOR_BUFFER_BIT); - QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); - QOpenGLExtraFunctions* extras = QOpenGLContext::currentContext()->extraFunctions(); - _fboMutex.lock(); - if (_fenceSync) { - extras->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); - qDebug() << "wait error" << glGetError(); - qDebug() << "waited on fence"; - } - if (_ready) { - QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST); - /*f->glBindTexture(GL_TEXTURE_2D, _copyFbo->texture()); - qDebug() << "bind tex error" << f->glGetError() << "texture" << _copyFbo->texture(); - f->glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _copyFbo->width(), _copyFbo->height());*/ - qDebug() << "copy error" << f->glGetError(); - } else { - framebufferObject()->release(); - } - _fboMutex.unlock(); - update(); + glFlush(); + _window->resetOpenGLState(); + update(); // schedule a call to render again on next frame (only do this when camera is on, in the future) } From 133af0b522d47372f37d80bff79adeda38945259 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 30 Jun 2017 12:55:11 -0700 Subject: [PATCH 12/18] jittery, still not working in tablet --- interface/src/ui/ResourceImageItem.cpp | 62 ++++++++++++++++--- interface/src/ui/ResourceImageItem.h | 5 ++ .../display-plugins/OpenGLDisplayPlugin.cpp | 1 + 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index b4a726dcef..396b81c7df 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -20,35 +20,46 @@ #include ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObject(parent) { - connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(update())); + connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(onUpdateTimer())); +} + +void ResourceImageItem::onUpdateTimer() { + qDebug() << "onUpdateTimer called"; + // turn timer off if not visible + if (!isVisible()) { + m_updateTimer.stop(); + } else { + update(); + } } void ResourceImageItem::setUrl(const QString& url) { if (url != m_url) { m_url = url; } + update(); } void ResourceImageItem::setReady(bool ready) { if (ready != m_ready) { m_ready = ready; - update(); - /* if (m_ready) { // 10 HZ for now. Also this serves as a small // delay before getting the network texture, which // helps a lot. TODO: find better way. + qDebug() << "setReady called"; m_updateTimer.start(100); } else { m_updateTimer.stop(); update(); - }*/ + } } } void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { ResourceImageItem* resourceImageItem = static_cast(item); + resourceImageItem->setFlag(QQuickItem::ItemHasContents); bool urlChanged = false; if( _url != resourceImageItem->getUrl()) { _url = resourceImageItem->getUrl(); @@ -65,7 +76,6 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { _networkTexture = DependencyManager::get()->getTexture(_url); } - resourceImageItem->forceActiveFocus(); if (_ready && _networkTexture && _networkTexture->isLoaded()) { if(_fboMutex.tryLock()) { qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, _copyFbo, &_fenceSync); @@ -73,6 +83,7 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { } } + glFlush(); } @@ -82,13 +93,46 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con _copyFbo = new QOpenGLFramebufferObject(size, format); return new QOpenGLFramebufferObject(size, format); } - +/* +// this is helpful to look for problems without the confusion of the framebufer stuff void ResourceImageItemRenderer::render() { + qDebug() << "rendering"; static int colorInt = 0; - float red = (float)((colorInt++ % 20)/(float)20); + float red = (float)((colorInt++ % 50)/(float)50); glClearColor(red, 0.0f, 0.0f, 0.5f + red/2.0f); glClear(GL_COLOR_BUFFER_BIT); glFlush(); - _window->resetOpenGLState(); - update(); // schedule a call to render again on next frame (only do this when camera is on, in the future) + update(); + //_window->resetOpenGLState(); + // we only want to call Renderer::update once per timer tick, and + // so waiting on the fenceSync and resetting it does that + auto f = QOpenGLContext::currentContext()->extraFunctions(); + if (_fenceSync) { + f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); + f->glDeleteSync(_fenceSync); + _fenceSync = 0; + update(); + } +} +*/ +void ResourceImageItemRenderer::render() { + auto f = QOpenGLContext::currentContext()->extraFunctions(); + bool doUpdate = false; + if (_fenceSync) { + f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); + f->glDeleteSync(_fenceSync); + _fenceSync = 0; + doUpdate = true; + } + if (_ready) { + _fboMutex.lock(); + _copyFbo->bind(); + QOpenGLFramebufferObject::blitFramebuffer(framebufferObject(), _copyFbo, GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT, GL_NEAREST); + _copyFbo->release(); + _fboMutex.unlock(); + if (doUpdate) { + update(); + } + } + glFlush(); } diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index 9b1d685ee1..c2cc857829 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -44,10 +44,15 @@ public: bool getReady() const { return m_ready; } void setReady(bool ready); QQuickFramebufferObject::Renderer* createRenderer() const override { return new ResourceImageItemRenderer; } + +public slots: + void onUpdateTimer(); + private: QString m_url; bool m_ready { false }; QTimer m_updateTimer; // TODO: something more clever + }; #endif // hifi_ResourceImageItem_h diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e028fd14c1..a8a80af01e 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -847,6 +847,7 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0); qDebug() << "error" << glGetError(); + // TODO: perhaps maintain aspect ratio glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, networkTexture->getWidth(), networkTexture->getHeight(), 0, 0, target->width(), target->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); qDebug() << "error" << glGetError(); From 455233dc161c88613a8acce96aae785be559dc44 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 30 Jun 2017 16:08:40 -0700 Subject: [PATCH 13/18] black background when camera is off --- interface/src/ui/ResourceImageItem.cpp | 5 ++++- interface/src/ui/ResourceImageItem.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 396b81c7df..30275bdf14 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -17,7 +17,6 @@ #include #include -#include ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObject(parent) { connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(onUpdateTimer())); @@ -118,6 +117,10 @@ void ResourceImageItemRenderer::render() { void ResourceImageItemRenderer::render() { auto f = QOpenGLContext::currentContext()->extraFunctions(); bool doUpdate = false; + // black background + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + if (_fenceSync) { f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); f->glDeleteSync(_fenceSync); diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index c2cc857829..b4faed7413 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -16,6 +16,8 @@ #include #include +#include + class ResourceImageItemRenderer : public QQuickFramebufferObject::Renderer { public: QOpenGLFramebufferObject* createFramebufferObject(const QSize& size) override; From 073eae39b2c4f2596be010471deb021be1ca89a9 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 30 Jun 2017 18:44:32 -0700 Subject: [PATCH 14/18] timer now resumes when visible again --- interface/resources/qml/hifi/SpectatorCamera.qml | 9 +++++++-- interface/src/ui/ResourceImageItem.cpp | 9 +-------- interface/src/ui/ResourceImageItem.h | 1 - 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 10182a3e7b..4540b0a6ea 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -39,7 +39,7 @@ Rectangle { letterboxMessage.visible = true; letterboxMessage.popupRadius = 0; } - + // // TITLE BAR START // @@ -221,13 +221,18 @@ Rectangle { Hifi.ResourceImageItem { id: spectatorCameraPreview; url: "resource://spectatorCameraFrame"; - ready: false; + ready: cameraToggleCheckBox.checked; mirrorVertically: true; height: 250; anchors.left: parent.left; anchors.top: cameraToggleCheckBox.bottom; anchors.topMargin: 20; anchors.right: parent.right; + onVisibleChanged: { + ready = cameraToggleCheckBox.checked; + update(); + } + } // "Monitor Shows" Switch Label Glyph diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 30275bdf14..6f50e6713d 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -23,13 +23,7 @@ ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObje } void ResourceImageItem::onUpdateTimer() { - qDebug() << "onUpdateTimer called"; - // turn timer off if not visible - if (!isVisible()) { - m_updateTimer.stop(); - } else { - update(); - } + update(); } void ResourceImageItem::setUrl(const QString& url) { @@ -46,7 +40,6 @@ void ResourceImageItem::setReady(bool ready) { // 10 HZ for now. Also this serves as a small // delay before getting the network texture, which // helps a lot. TODO: find better way. - qDebug() << "setReady called"; m_updateTimer.start(100); } else { m_updateTimer.stop(); diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index b4faed7413..839324cbf1 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -38,7 +38,6 @@ class ResourceImageItem : public QQuickFramebufferObject { Q_OBJECT Q_PROPERTY(QString url READ getUrl WRITE setUrl) Q_PROPERTY(bool ready READ getReady WRITE setReady) - public: ResourceImageItem(QQuickItem* parent = Q_NULLPTR); QString getUrl() const { return m_url; } From 373bd0cc8a07d1b3e0afc036622fa84ed2034e4a Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 3 Jul 2017 14:23:23 -0700 Subject: [PATCH 15/18] aspect ratio preserved upon blitting, minor qml tweak, warnings fixed --- .../resources/qml/hifi/SpectatorCamera.qml | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 21 +++++++++++++++++-- .../src/display-plugins/OpenGLDisplayPlugin.h | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/SpectatorCamera.qml b/interface/resources/qml/hifi/SpectatorCamera.qml index 4540b0a6ea..e3572a544f 100644 --- a/interface/resources/qml/hifi/SpectatorCamera.qml +++ b/interface/resources/qml/hifi/SpectatorCamera.qml @@ -242,7 +242,7 @@ Rectangle { size: 32; color: hifi.colors.blueHighlight; anchors.top: spectatorCameraPreview.bottom; - anchors.topMargin: 12; + anchors.topMargin: 20; anchors.left: parent.left; } // "Monitor Shows" Switch Label diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index a8a80af01e..24fe40873e 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -841,14 +841,31 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); qDebug() << "error" << glGetError(); + GLint texWidth, texHeight; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texWidth); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texHeight); // setup destination fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0); qDebug() << "error" << glGetError(); - // TODO: perhaps maintain aspect ratio - glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, networkTexture->getWidth(), networkTexture->getHeight(), 0, 0, target->width(), target->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + + // maintain aspect ratio, filling the width first if possible. If that makes the height too + // much, fill height instead. + GLint newX = 0; + GLint newY = 0; + float aspectRatio = (float)texHeight / (float)texWidth; + GLint newWidth = target->width(); + GLint newHeight = std::round(aspectRatio * (float) target->width()); + if (newHeight > target->height()) { + newHeight = target->height(); + newWidth = std::round((float)target->height() / aspectRatio); + newX = (target->width() - newWidth) / 2; + } else { + newY = (target->height() - newHeight) / 2; + } + glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, texWidth, texHeight, newX, newY, newWidth, newHeight, GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT, GL_NEAREST); qDebug() << "error" << glGetError(); // don't delete the textures! diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index dc2e251d65..237864ded6 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -79,7 +79,7 @@ public: // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver int getRequiredThreadCount() const override { return 3; } - void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync); + void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override; protected: friend class PresentThread; From 99bf100ff82e6a3f190b353c5f1425c1e52abe39 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 3 Jul 2017 15:19:04 -0700 Subject: [PATCH 16/18] fix for jitter and not rendering in tablet --- interface/src/ui/ResourceImageItem.cpp | 72 ++++++++------------------ interface/src/ui/ResourceImageItem.h | 12 ++--- 2 files changed, 29 insertions(+), 55 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index edb9549850..68ad50f2a1 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -18,34 +18,36 @@ #include -ResourceImageItem::ResourceImageItem(QQuickItem* parent) : QQuickFramebufferObject(parent) { - connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(onUpdateTimer())); -} - -void ResourceImageItem::onUpdateTimer() { - update(); -} - void ResourceImageItem::setUrl(const QString& url) { if (url != m_url) { m_url = url; + update(); } - update(); } void ResourceImageItem::setReady(bool ready) { if (ready != m_ready) { m_ready = ready; - if (m_ready) { - // 10 HZ for now. Also this serves as a small - // delay before getting the network texture, which - // helps a lot. TODO: find better way. - m_updateTimer.start(100); + update(); + } +} + +void ResourceImageItemRenderer::onUpdateTimer() { + if (_ready && _networkTexture && _networkTexture->isLoaded()) { + if(_fboMutex.tryLock()) { + invalidateFramebufferObject(); + qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, _copyFbo, &_fenceSync); + _fboMutex.unlock(); } else { - m_updateTimer.stop(); - update(); + qDebug() << "couldn't get a lock, using last frame"; } } + + update(); +} + +ResourceImageItemRenderer::ResourceImageItemRenderer() : QQuickFramebufferObject::Renderer() { + connect(&_updateTimer, SIGNAL(timeout()), this, SLOT(onUpdateTimer())); } void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { @@ -68,18 +70,11 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { _networkTexture = DependencyManager::get()->getTexture(_url); } - if (_ready && _networkTexture && _networkTexture->isLoaded()) { - if(_fboMutex.tryLock()) { - invalidateFramebufferObject(); - qApp->getActiveDisplayPlugin()->copyTextureToQuickFramebuffer(_networkTexture, _copyFbo, &_fenceSync); - _fboMutex.unlock(); - } else { - qDebug() << "couldn't get a lock, using last frame"; - } - + if (_ready && !_updateTimer.isActive()) { + _updateTimer.start(100); + } else if (!_ready && _updateTimer.isActive()) { + _updateTimer.stop(); } - glFlush(); - } QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(const QSize& size) { @@ -90,28 +85,6 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con return new QOpenGLFramebufferObject(size, format); } -/* -// this is helpful to look for problems without the confusion of the framebufer stuff -void ResourceImageItemRenderer::render() { - qDebug() << "rendering"; - static int colorInt = 0; - float red = (float)((colorInt++ % 50)/(float)50); - glClearColor(red, 0.0f, 0.0f, 0.5f + red/2.0f); - glClear(GL_COLOR_BUFFER_BIT); - glFlush(); - update(); - //_window->resetOpenGLState(); - // we only want to call Renderer::update once per timer tick, and - // so waiting on the fenceSync and resetting it does that - auto f = QOpenGLContext::currentContext()->extraFunctions(); - if (_fenceSync) { - f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); - f->glDeleteSync(_fenceSync); - _fenceSync = 0; - update(); - } -} -*/ void ResourceImageItemRenderer::render() { auto f = QOpenGLContext::currentContext()->extraFunctions(); bool doUpdate = false; @@ -136,4 +109,5 @@ void ResourceImageItemRenderer::render() { } } glFlush(); + _window->resetOpenGLState(); } diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index 839324cbf1..2fd106a828 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -18,8 +18,10 @@ #include -class ResourceImageItemRenderer : public QQuickFramebufferObject::Renderer { +class ResourceImageItemRenderer : public QObject, public QQuickFramebufferObject::Renderer { + Q_OBJECT public: + ResourceImageItemRenderer(); QOpenGLFramebufferObject* createFramebufferObject(const QSize& size) override; void synchronize(QQuickFramebufferObject* item) override; void render() override; @@ -32,6 +34,9 @@ private: QMutex _fboMutex; QOpenGLFramebufferObject* _copyFbo; GLsync _fenceSync { 0 }; + QTimer _updateTimer; +public slots: + void onUpdateTimer(); }; class ResourceImageItem : public QQuickFramebufferObject { @@ -39,20 +44,15 @@ class ResourceImageItem : public QQuickFramebufferObject { Q_PROPERTY(QString url READ getUrl WRITE setUrl) Q_PROPERTY(bool ready READ getReady WRITE setReady) public: - ResourceImageItem(QQuickItem* parent = Q_NULLPTR); QString getUrl() const { return m_url; } void setUrl(const QString& url); bool getReady() const { return m_ready; } void setReady(bool ready); QQuickFramebufferObject::Renderer* createRenderer() const override { return new ResourceImageItemRenderer; } -public slots: - void onUpdateTimer(); - private: QString m_url; bool m_ready { false }; - QTimer m_updateTimer; // TODO: something more clever }; From 2a3b4b3bfb206816cf278a71683aea277d3dd91e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 3 Jul 2017 15:54:54 -0700 Subject: [PATCH 17/18] minor cleanup, and adjusted includes per austin's suggestion --- interface/src/ui/ResourceImageItem.cpp | 4 +--- interface/src/ui/ResourceImageItem.h | 2 ++ .../src/display-plugins/OpenGLDisplayPlugin.cpp | 9 ++------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 68ad50f2a1..fb0f10ac05 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "Application.h" +//#include "Application.h" #include "ResourceImageItem.h" #include @@ -16,7 +16,6 @@ #include #include -#include void ResourceImageItem::setUrl(const QString& url) { if (url != m_url) { @@ -42,7 +41,6 @@ void ResourceImageItemRenderer::onUpdateTimer() { qDebug() << "couldn't get a lock, using last frame"; } } - update(); } diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index 2fd106a828..2d6e2cf625 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -12,6 +12,8 @@ #ifndef hifi_ResourceImageItem_h #define hifi_ResourceImageItem_h +#include "Application.h" + #include #include #include diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 24fe40873e..63df7fdda9 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -829,18 +829,16 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture()); GLuint targetTexture = target->texture(); GLuint fbo[2] {0, 0}; - qDebug() << "initial" << glGetError(); + // need mipmaps for blitting texture glGenerateTextureMipmap(sourceTexture); // create 2 fbos (one for initial texture, second for scaled one) glCreateFramebuffers(2, fbo); - qDebug() << "error" << glGetError(); // setup source fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sourceTexture, 0); - qDebug() << "error" << glGetError(); GLint texWidth, texHeight; glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texWidth); glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texHeight); @@ -848,11 +846,10 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne // setup destination fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, targetTexture, 0); - qDebug() << "error" << glGetError(); // maintain aspect ratio, filling the width first if possible. If that makes the height too - // much, fill height instead. + // much, fill height instead. TODO: only do this when texture changes GLint newX = 0; GLint newY = 0; float aspectRatio = (float)texHeight / (float)texWidth; @@ -866,11 +863,9 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne newY = (target->height() - newHeight) / 2; } glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, texWidth, texHeight, newX, newY, newWidth, newHeight, GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT, GL_NEAREST); - qDebug() << "error" << glGetError(); // don't delete the textures! glDeleteFramebuffers(2, fbo); - qDebug() << "error" << glGetError(); *fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); }); } From 36467983d8ac3cce3cce24a76da6592bda79fa94 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Mon, 3 Jul 2017 16:29:26 -0700 Subject: [PATCH 18/18] cr stuff --- interface/src/ui/ResourceImageItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index fb0f10ac05..9b5595c6ed 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -53,7 +53,7 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { resourceImageItem->setFlag(QQuickItem::ItemHasContents); bool urlChanged = false; - if( _url != resourceImageItem->getUrl()) { + if (_url != resourceImageItem->getUrl()) { _url = resourceImageItem->getUrl(); urlChanged = true; } @@ -67,9 +67,9 @@ void ResourceImageItemRenderer::synchronize(QQuickFramebufferObject* item) { if (_ready && !_url.isNull() && !_url.isEmpty() && (urlChanged || readyChanged || !_networkTexture)) { _networkTexture = DependencyManager::get()->getTexture(_url); } - + static const int UPDATE_TIMER_DELAY_IN_MS = 100; // 100 ms = 10 hz for now if (_ready && !_updateTimer.isActive()) { - _updateTimer.start(100); + _updateTimer.start(UPDATE_TIMER_DELAY_IN_MS); } else if (!_ready && _updateTimer.isActive()) { _updateTimer.stop(); }