diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index d2bfdde7ea..5ad4849f38 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -187,7 +187,7 @@ void AvatarMixer::start() { // NOTE: nodeData->getAvatar() might be side effected, must be called when access to node/nodeData -// is guarenteed to not be accessed by other thread +// is guaranteed to not be accessed by other thread void AvatarMixer::manageDisplayName(const SharedNodePointer& node) { AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) { diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.h b/assignment-client/src/avatars/AvatarMixerSlavePool.h index 6bef0515bb..e6ac2a1f4e 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.h +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.h @@ -49,7 +49,7 @@ private: bool _stop { false }; }; -// Slave pool for audio mixers +// Slave pool for avatar mixers // AvatarMixerSlavePool is not thread-safe! It should be instantiated and used from a single thread. class AvatarMixerSlavePool { using Queue = tbb::concurrent_queue; diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml index 5176d9d11e..1bbbbd6cbe 100644 --- a/interface/resources/QtWebEngine/UIDelegates/Menu.qml +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -1,7 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 as Controls -import "../../qml/menus" import "../../qml/controls-uit" import "../../qml/styles-uit" diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ed6068ece7..62917b304c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -180,6 +180,7 @@ #include "FrameTimingsScriptingInterface.h" #include #include +#include #include #include @@ -522,7 +523,9 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); + return previousSessionCrashed; } @@ -550,7 +553,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f; const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false; -const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = true; +const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) : QApplication(argc, argv), @@ -4572,6 +4575,8 @@ void Application::update(float deltaTime) { } AnimDebugDraw::getInstance().update(); + + DependencyManager::get()->update(); } void Application::sendAvatarViewFrustum() { @@ -5518,6 +5523,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get().data()); + if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) { scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine, steamClient.get())); } diff --git a/interface/src/scripting/LimitlessConnection.cpp b/interface/src/scripting/LimitlessConnection.cpp new file mode 100644 index 0000000000..b9f4eacd4b --- /dev/null +++ b/interface/src/scripting/LimitlessConnection.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include "LimitlessConnection.h" +#include "LimitlessVoiceRecognitionScriptingInterface.h" + +LimitlessConnection::LimitlessConnection() : + _streamingAudioForTranscription(false) +{ +} + +void LimitlessConnection::startListening(QString authCode) { + _transcribeServerSocket.reset(new QTcpSocket(this)); + connect(_transcribeServerSocket.get(), &QTcpSocket::readyRead, this, + &LimitlessConnection::transcriptionReceived); + connect(_transcribeServerSocket.get(), &QTcpSocket::disconnected, this, [this](){stopListening();}); + + static const auto host = "gserv_devel.studiolimitless.com"; + _transcribeServerSocket->connectToHost(host, 1407); + _transcribeServerSocket->waitForConnected(); + QString requestHeader = QString::asprintf("Authorization: %s\r\nfs: %i\r\n", + authCode.toLocal8Bit().data(), AudioConstants::SAMPLE_RATE); + qCDebug(interfaceapp) << "Sending Limitless Audio Stream Request: " << requestHeader; + _transcribeServerSocket->write(requestHeader.toLocal8Bit()); + _transcribeServerSocket->waitForBytesWritten(); +} + +void LimitlessConnection::stopListening() { + emit onFinishedSpeaking(_currentTranscription); + _streamingAudioForTranscription = false; + _currentTranscription = ""; + if (!isConnected()) + return; + _transcribeServerSocket->close(); + disconnect(_transcribeServerSocket.get(), &QTcpSocket::readyRead, this, + &LimitlessConnection::transcriptionReceived); + _transcribeServerSocket.release()->deleteLater(); + disconnect(DependencyManager::get().data(), &AudioClient::inputReceived, this, + &LimitlessConnection::audioInputReceived); + qCDebug(interfaceapp) << "Connection to Limitless Voice Server closed."; +} + +void LimitlessConnection::audioInputReceived(const QByteArray& inputSamples) { + if (isConnected()) { + _transcribeServerSocket->write(inputSamples.data(), inputSamples.size()); + _transcribeServerSocket->waitForBytesWritten(); + } +} + +void LimitlessConnection::transcriptionReceived() { + while (_transcribeServerSocket && _transcribeServerSocket->bytesAvailable() > 0) { + const QByteArray data = _transcribeServerSocket->readAll(); + _serverDataBuffer.append(data); + int begin = _serverDataBuffer.indexOf('<'); + int end = _serverDataBuffer.indexOf('>'); + while (begin > -1 && end > -1) { + const int len = end - begin; + const QByteArray serverMessage = _serverDataBuffer.mid(begin+1, len-1); + if (serverMessage.contains("1407")) { + qCDebug(interfaceapp) << "Limitless Speech Server denied the request."; + // Don't spam the server with further false requests please. + DependencyManager::get()->setListeningToVoice(true); + stopListening(); + return; + } else if (serverMessage.contains("1408")) { + qCDebug(interfaceapp) << "Limitless Audio request authenticated!"; + _serverDataBuffer.clear(); + connect(DependencyManager::get().data(), &AudioClient::inputReceived, this, + &LimitlessConnection::audioInputReceived); + return; + } + QJsonObject json = QJsonDocument::fromJson(serverMessage.data()).object(); + _serverDataBuffer.remove(begin, len+1); + _currentTranscription = json["alternatives"].toArray()[0].toObject()["transcript"].toString(); + emit onReceivedTranscription(_currentTranscription); + if (json["isFinal"] == true) { + qCDebug(interfaceapp) << "Final transcription: " << _currentTranscription; + stopListening(); + return; + } + begin = _serverDataBuffer.indexOf('<'); + end = _serverDataBuffer.indexOf('>'); + } + } +} + +bool LimitlessConnection::isConnected() const { + return _transcribeServerSocket.get() && _transcribeServerSocket->isWritable() + && _transcribeServerSocket->state() != QAbstractSocket::SocketState::UnconnectedState; +} diff --git a/interface/src/scripting/LimitlessConnection.h b/interface/src/scripting/LimitlessConnection.h new file mode 100644 index 0000000000..ee049aff8e --- /dev/null +++ b/interface/src/scripting/LimitlessConnection.h @@ -0,0 +1,44 @@ +// +// SpeechRecognitionScriptingInterface.h +// interface/src/scripting +// +// Created by Trevor Berninger on 3/24/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_LimitlessConnection_h +#define hifi_LimitlessConnection_h + +#include +#include +#include + +class LimitlessConnection : public QObject { + Q_OBJECT +public: + LimitlessConnection(); + + Q_INVOKABLE void startListening(QString authCode); + Q_INVOKABLE void stopListening(); + + std::atomic _streamingAudioForTranscription; + +signals: + void onReceivedTranscription(QString speech); + void onFinishedSpeaking(QString speech); + +private: + void transcriptionReceived(); + void audioInputReceived(const QByteArray& inputSamples); + + bool isConnected() const; + + std::unique_ptr _transcribeServerSocket; + QByteArray _serverDataBuffer; + QString _currentTranscription; +}; + +#endif //hifi_LimitlessConnection_h diff --git a/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp b/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp new file mode 100644 index 0000000000..1352630f84 --- /dev/null +++ b/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.cpp @@ -0,0 +1,64 @@ +// +// SpeechRecognitionScriptingInterface.h +// interface/src/scripting +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include "LimitlessVoiceRecognitionScriptingInterface.h" + +const float LimitlessVoiceRecognitionScriptingInterface::_audioLevelThreshold = 0.33f; +const int LimitlessVoiceRecognitionScriptingInterface::_voiceTimeoutDuration = 2000; + +LimitlessVoiceRecognitionScriptingInterface::LimitlessVoiceRecognitionScriptingInterface() : + _shouldStartListeningForVoice(false) +{ + _voiceTimer.setSingleShot(true); + connect(&_voiceTimer, &QTimer::timeout, this, &LimitlessVoiceRecognitionScriptingInterface::voiceTimeout); + connect(&_connection, &LimitlessConnection::onReceivedTranscription, this, [this](QString transcription){emit onReceivedTranscription(transcription);}); + connect(&_connection, &LimitlessConnection::onFinishedSpeaking, this, [this](QString transcription){emit onFinishedSpeaking(transcription);}); + _connection.moveToThread(&_connectionThread); + _connectionThread.setObjectName("Limitless Connection"); + _connectionThread.start(); +} + +void LimitlessVoiceRecognitionScriptingInterface::update() { + const float audioLevel = AvatarInputs::getInstance()->loudnessToAudioLevel(DependencyManager::get()->getAudioAverageInputLoudness()); + + if (_shouldStartListeningForVoice) { + if (_connection._streamingAudioForTranscription) { + if (audioLevel > _audioLevelThreshold) { + if (_voiceTimer.isActive()) { + _voiceTimer.stop(); + } + } else if (!_voiceTimer.isActive()){ + _voiceTimer.start(_voiceTimeoutDuration); + } + } else if (audioLevel > _audioLevelThreshold) { + // to make sure invoke doesn't get called twice before the method actually gets called + _connection._streamingAudioForTranscription = true; + QMetaObject::invokeMethod(&_connection, "startListening", Q_ARG(QString, authCode)); + } + } +} + +void LimitlessVoiceRecognitionScriptingInterface::setListeningToVoice(bool listening) { + _shouldStartListeningForVoice = listening; +} + +void LimitlessVoiceRecognitionScriptingInterface::setAuthKey(QString key) { + authCode = key; +} + +void LimitlessVoiceRecognitionScriptingInterface::voiceTimeout() { + if (_connection._streamingAudioForTranscription) { + QMetaObject::invokeMethod(&_connection, "stopListening"); + } +} diff --git a/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.h b/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.h new file mode 100644 index 0000000000..d1b1139695 --- /dev/null +++ b/interface/src/scripting/LimitlessVoiceRecognitionScriptingInterface.h @@ -0,0 +1,50 @@ +// +// SpeechRecognitionScriptingInterface.h +// interface/src/scripting +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_SpeechRecognitionScriptingInterface_h +#define hifi_SpeechRecognitionScriptingInterface_h + +#include +#include +#include +#include "LimitlessConnection.h" + +class LimitlessVoiceRecognitionScriptingInterface : public QObject, public Dependency { + Q_OBJECT +public: + LimitlessVoiceRecognitionScriptingInterface(); + + void update(); + + QString authCode; + +public slots: + void setListeningToVoice(bool listening); + void setAuthKey(QString key); + +signals: + void onReceivedTranscription(QString speech); + void onFinishedSpeaking(QString speech); + +private: + + bool _shouldStartListeningForVoice; + static const float _audioLevelThreshold; + static const int _voiceTimeoutDuration; + + QTimer _voiceTimer; + QThread _connectionThread; + LimitlessConnection _connection; + + void voiceTimeout(); +}; + +#endif //hifi_SpeechRecognitionScriptingInterface_h diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index f2d97a0137..7239e49d89 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -85,7 +85,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts - renderStatsAndLogs(renderArgs); // currently renders nothing }); renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch @@ -159,27 +158,6 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) { qApp->getOverlays().renderHUD(renderArgs); } -void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) { - - // Display stats and log text onscreen - - // Determine whether to compute timing details - - /* - // Show on-screen msec timer - if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { - auto canvasSize = qApp->getCanvasSize(); - quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); - QString frameTimer = QString("%1\n").arg((int)(mSecsNow % 1000)); - int timerBottom = - (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) - ? 80 : 20; - drawText(canvasSize.x - 100, canvasSize.y - timerBottom, - 0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT); - } - */ -} - void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) { auto geometryCache = DependencyManager::get(); static std::once_flag once; @@ -229,13 +207,13 @@ void ApplicationOverlay::buildFramebufferObject() { auto width = uiSize.x; auto height = uiSize.y; if (!_overlayFramebuffer->getDepthStencilBuffer()) { - auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER)); + auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER)); _overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT); } 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::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER)); + auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER)); _overlayFramebuffer->setRenderBuffer(0, colorBuffer); } } diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 7d63a85a27..341915e57f 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -62,24 +62,13 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { } \ } -void AvatarInputs::update() { - if (!Menu::getInstance()) { - return; - } - AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); - AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); - AI_UPDATE(isHMD, qApp->isHMDMode()); - - AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools)); - - auto audioIO = DependencyManager::get(); +float AvatarInputs::loudnessToAudioLevel(float loudness) { const float AUDIO_METER_AVERAGING = 0.5; const float LOG2 = log(2.0f); const float METER_LOUDNESS_SCALE = 2.8f / 5.0f; const float LOG2_LOUDNESS_FLOOR = 11.0f; float audioLevel = 0.0f; - auto audio = DependencyManager::get(); - float loudness = audio->getLastInputLoudness() + 1.0f; + loudness += 1.0f; _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness; @@ -93,6 +82,24 @@ void AvatarInputs::update() { if (audioLevel > 1.0f) { audioLevel = 1.0; } + return audioLevel; +} + +void AvatarInputs::update() { + if (!Menu::getInstance()) { + return; + } + + AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); + AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); + AI_UPDATE(isHMD, qApp->isHMDMode()); + + AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools)); + + auto audioIO = DependencyManager::get(); + + const float audioLevel = loudnessToAudioLevel(DependencyManager::get()->getLastInputLoudness()); + AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01f); AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f))); AI_UPDATE(audioMuted, audioIO->isMuted()); diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 0c4fc0f23c..34b2cbca8b 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -34,6 +34,7 @@ class AvatarInputs : public QQuickItem { public: static AvatarInputs* getInstance(); + float loudnessToAudioLevel(float loudness); AvatarInputs(QQuickItem* parent = nullptr); void update(); bool showAudioTools() const { return _showAudioTools; } diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index e05ae1aacd..e50cf3a671 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -350,11 +350,15 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); QPointF windowPoint(windowPos.x, windowPos.y); - if (event.getButtons() == PointerEvent::NoButtons && event.getType() == PointerEvent::Move) { - // Forward a mouse move event to the Web surface. + if (event.getType() == PointerEvent::Move) { + // Forward a mouse move event to the Web surface so that hover events are generated. + // Must send a mouse move event that matches up with touch move event in order for scroll bars to work. + + // Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times). + // This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery". + QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier); QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); - return; } if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index dc806a4115..a1ea103edb 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1495,6 +1495,9 @@ void AvatarData::processAvatarIdentity(const Identity& identity, bool& identityC setAvatarEntityData(identity.avatarEntityData); identityChanged = true; } + // flag this avatar as non-stale by updating _averageBytesReceived + const int BOGUS_NUM_BYTES = 1; + _averageBytesReceived.updateAverage(BOGUS_NUM_BYTES); } QByteArray AvatarData::identityByteArray() const { diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 5a317f64bc..a3cf91fcd5 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -356,15 +356,16 @@ void OpenGLDisplayPlugin::customizeContext() { cursorData.texture.reset( gpu::Texture::createStrict( - gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), - image.width(), image.height(), - gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), + image.width(), image.height(), + gpu::Texture::MAX_NUM_MIPS, + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); cursorData.texture->setSource("cursor texture"); auto usage = gpu::Texture::Usage::Builder().withColor().withAlpha(); cursorData.texture->setUsage(usage.build()); cursorData.texture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); cursorData.texture->assignStoredMip(0, image.byteCount(), image.constBits()); - cursorData.texture->autoGenerateMips(-1); + cursorData.texture->setAutoGenerateMips(true); } } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index c55d985a62..52c689ec00 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -299,12 +299,13 @@ void HmdDisplayPlugin::internalPresent() { gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), + gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); _previewTexture->setSource("HMD Preview Texture"); _previewTexture->setUsage(gpu::Texture::Usage::Builder().withColor().build()); _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); - _previewTexture->autoGenerateMips(-1); + _previewTexture->setAutoGenerateMips(true); } auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 8aab6abaa9..1dad72dbc1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -229,7 +229,7 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { _resource._textures[slot] = resourceTexture; - _stats._RSAmountTextureMemoryBounded += object->size(); + _stats._RSAmountTextureMemoryBounded += (int) object->size(); } else { releaseResourceTexture(slot); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index 1f91e17157..b47aa3e8dd 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -43,7 +43,7 @@ public: static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; protected: - virtual uint32 size() const = 0; + virtual Size size() const = 0; virtual void generateMips() const = 0; GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); @@ -57,7 +57,7 @@ public: protected: GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); void generateMips() const override {} - uint32 size() const override { return 0; } + Size size() const override { return 0; } }; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 6d2f91c436..93d65b74dd 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -40,30 +40,59 @@ public: class GL41Texture : public GLTexture { using Parent = GLTexture; - static GLuint allocate(); - - public: - ~GL41Texture(); - - private: - GL41Texture(const std::weak_ptr& backend, const Texture& buffer); - - void generateMips() const override; - uint32 size() const override; - friend class GL41Backend; - const Stamp _storageStamp; - mutable Stamp _contentStamp { 0 }; - mutable Stamp _samplerStamp { 0 }; - const uint32 _size; + static GLuint allocate(const Texture& texture); + protected: + GL41Texture(const std::weak_ptr& backend, const Texture& texture); + void generateMips() const override; + void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const; + virtual void syncSampler() const; - - bool isOutdated() const; void withPreservedTexture(std::function f) const; - void syncContent() const; - void syncSampler() const; }; + // + // Textures that have fixed allocation sizes and cannot be managed at runtime + // + + class GL41FixedAllocationTexture : public GL41Texture { + using Parent = GL41Texture; + friend class GL41Backend; + + public: + GL41FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL41FixedAllocationTexture(); + + protected: + Size size() const override { return _size; } + void allocateStorage() const; + void syncSampler() const override; + const Size _size { 0 }; + }; + + class GL41AttachmentTexture : public GL41FixedAllocationTexture { + using Parent = GL41FixedAllocationTexture; + friend class GL41Backend; + protected: + GL41AttachmentTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL41AttachmentTexture(); + }; + + class GL41StrictResourceTexture : public GL41FixedAllocationTexture { + using Parent = GL41FixedAllocationTexture; + friend class GL41Backend; + protected: + GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); + }; + + class GL41ResourceTexture : public GL41FixedAllocationTexture { + using Parent = GL41FixedAllocationTexture; + friend class GL41Backend; + protected: + GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL41ResourceTexture(); + }; protected: GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 46672b3b65..2056085091 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -19,20 +19,11 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl41; -using GL41TexelFormat = GLTexelFormat; -using GL41Texture = GL41Backend::GL41Texture; - -GLuint GL41Texture::allocate() { - Backend::incrementTextureGPUCount(); - GLuint result; - glGenTextures(1, &result); - return result; -} - GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { if (!texturePointer) { return nullptr; } + const Texture& texture = *texturePointer; if (TextureUsageType::EXTERNAL == texture.getUsageType()) { return Parent::syncGPUObject(texturePointer); @@ -43,90 +34,58 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { return nullptr; } - // If the object hasn't been created, or the object definition is out of date, drop and re-create GL41Texture* object = Backend::getGPUObject(texture); - if (!object || object->_storageStamp < texture.getStamp()) { - // This automatically any previous texture - object = new GL41Texture(shared_from_this(), texture); - } + if (!object) { + switch (texture.getUsageType()) { + case TextureUsageType::RENDERBUFFER: + object = new GL41AttachmentTexture(shared_from_this(), texture); + break; - // FIXME internalize to GL41Texture 'sync' function - if (object->isOutdated()) { - object->withPreservedTexture([&] { - if (object->_contentStamp <= texture.getDataStamp()) { - // FIXME implement synchronous texture transfer here - object->syncContent(); + case TextureUsageType::STRICT_RESOURCE: + qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); + object = new GL41StrictResourceTexture(shared_from_this(), texture); + break; + + case TextureUsageType::RESOURCE: { + qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str(); + object = new GL41ResourceTexture(shared_from_this(), texture); + break; } - if (object->_samplerStamp <= texture.getSamplerStamp()) { - object->syncSampler(); - } - }); + default: + Q_UNREACHABLE(); + } } return object; } -GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) - : GLTexture(backend, texture, allocate()), _storageStamp { texture.getStamp() }, _size(texture.evalTotalSize()) { +using GL41Texture = GL41Backend::GL41Texture; + +GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)) { incrementTextureGPUCount(); - withPreservedTexture([&] { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - auto numMips = _gpuObject.getNumMipLevels(); - for (uint16_t mipLevel = 0; mipLevel < numMips; ++mipLevel) { - // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(mipLevel); - uint8_t face = 0; - for (GLenum target : getFaceTargets(_target)) { - const Byte* mipData = nullptr; - if (_gpuObject.isStoredMipFaceAvailable(mipLevel, face)) { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - mipData = mip->readData(); - } - glTexImage2D(target, mipLevel, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, mipData); - (void)CHECK_GL_ERROR(); - ++face; - } - } - }); } -GL41Texture::~GL41Texture() { - +GLuint GL41Texture::allocate(const Texture& texture) { + GLuint result; + glGenTextures(1, &result); + return result; } -bool GL41Texture::isOutdated() const { - if (_samplerStamp <= _gpuObject.getSamplerStamp()) { - return true; - } - if (TextureUsageType::RESOURCE == _gpuObject.getUsageType() && _contentStamp <= _gpuObject.getDataStamp()) { - return true; - } - return false; -} void GL41Texture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; - - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; - - default: - qFatal("Unsupported texture type"); - } + const GLint TRANSFER_TEXTURE_UNIT = 32; + glActiveTexture(GL_TEXTURE0 + TRANSFER_TEXTURE_UNIT); + glBindTexture(_target, _texture); (void)CHECK_GL_ERROR(); - glBindTexture(_target, _texture); f(); - glBindTexture(_target, boundTex); + glBindTexture(_target, 0); (void)CHECK_GL_ERROR(); } + void GL41Texture::generateMips() const { withPreservedTexture([&] { glGenerateMipmap(_target); @@ -134,13 +93,35 @@ void GL41Texture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GL41Texture::syncContent() const { - // FIXME actually copy the texture data - _contentStamp = _gpuObject.getDataStamp() + 1; +void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const { + if (GL_TEXTURE_2D == _target) { + glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); + } else if (GL_TEXTURE_CUBE_MAP == _target) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); + } else { + assert(false); + } + (void)CHECK_GL_ERROR(); +} + +void GL41Texture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { + return; + } + auto size = _gpuObject.evalMipDimensions(sourceMip); + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + if (mipData) { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + copyMipFaceLinesFromTexture(targetMip, face, size, 0, texelFormat.format, texelFormat.type, mipData->readData()); + } else { + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + } } void GL41Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); + const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); @@ -158,12 +139,106 @@ void GL41Texture::syncSampler() const { glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); + glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - _samplerStamp = _gpuObject.getSamplerStamp() + 1; } -uint32 GL41Texture::size() const { - return _size; +using GL41FixedAllocationTexture = GL41Backend::GL41FixedAllocationTexture; + +GL41FixedAllocationTexture::GL41FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL41Texture(backend, texture), _size(texture.evalTotalSize()) { + withPreservedTexture([&] { + allocateStorage(); + syncSampler(); + }); +} + +GL41FixedAllocationTexture::~GL41FixedAllocationTexture() { +} + +void GL41FixedAllocationTexture::allocateStorage() const { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto numMips = _gpuObject.getNumMips(); + + // glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + for (GLint level = 0; level < numMips; level++) { + Vec3u dimensions = _gpuObject.evalMipDimensions(level); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, level, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, nullptr); + } + } + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1); +} + +void GL41FixedAllocationTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); + glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); + glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); +} + +// Renderbuffer attachment textures +using GL41AttachmentTexture = GL41Backend::GL41AttachmentTexture; + +GL41AttachmentTexture::GL41AttachmentTexture(const std::weak_ptr& backend, const Texture& texture) : GL41FixedAllocationTexture(backend, texture) { + Backend::updateTextureGPUFramebufferMemoryUsage(0, size()); +} + +GL41AttachmentTexture::~GL41AttachmentTexture() { + Backend::updateTextureGPUFramebufferMemoryUsage(size(), 0); +} + +// Strict resource textures +using GL41StrictResourceTexture = GL41Backend::GL41StrictResourceTexture; + +GL41StrictResourceTexture::GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41FixedAllocationTexture(backend, texture) { + withPreservedTexture([&] { + + auto mipLevels = _gpuObject.getNumMips(); + for (uint16_t sourceMip = 0; sourceMip < mipLevels; sourceMip++) { + uint16_t targetMip = sourceMip; + size_t maxFace = GLTexture::getFaceCount(_target); + for (uint8_t face = 0; face < maxFace; face++) { + copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + }); + + if (texture.isAutogenerateMips()) { + generateMips(); + } +} + +// resource textures +using GL41ResourceTexture = GL41Backend::GL41ResourceTexture; + +GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41FixedAllocationTexture(backend, texture) { + Backend::updateTextureGPUMemoryUsage(0, size()); + + withPreservedTexture([&] { + + auto mipLevels = _gpuObject.getNumMips(); + for (uint16_t sourceMip = 0; sourceMip < mipLevels; sourceMip++) { + uint16_t targetMip = sourceMip; + size_t maxFace = GLTexture::getFaceCount(_target); + for (uint8_t face = 0; face < maxFace; face++) { + copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + }); + + if (texture.isAutogenerateMips()) { + generateMips(); + } +} + +GL41ResourceTexture::~GL41ResourceTexture() { + Backend::updateTextureGPUMemoryUsage(size(), 0); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 6a9811b055..ca8028aff6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -58,10 +58,10 @@ public: ~GL45FixedAllocationTexture(); protected: - uint32 size() const override { return _size; } + Size size() const override { return _size; } void allocateStorage() const; void syncSampler() const override; - const uint32 _size { 0 }; + const Size _size { 0 }; }; class GL45AttachmentTexture : public GL45FixedAllocationTexture { @@ -173,7 +173,7 @@ public: bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } void executeNextTransfer(const TexturePointer& currentTexture); - uint32 size() const override { return _size; } + Size size() const override { return _size; } virtual void populateTransferQueue() = 0; virtual void promote() = 0; virtual void demote() = 0; @@ -188,7 +188,7 @@ public: // The highest (lowest resolution) mip that we will support, relative to the number // of mips in the gpu::Texture object uint16 _maxAllocatedMip { 0 }; - uint32 _size { 0 }; + Size _size { 0 }; // Contains a series of lambdas that when executed will transfer data to the GPU, modify // the _populatedMip and update the sampler in order to fully populate the allocated texture // until _populatedMip == _allocatedMip diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index c82c13c57c..ab4153c04c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include "../gl/GLTexelFormat.h" @@ -167,8 +166,10 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_S, WRAP_MODES[sampler.getWrapModeU()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); + glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } @@ -186,10 +187,12 @@ GL45FixedAllocationTexture::~GL45FixedAllocationTexture() { void GL45FixedAllocationTexture::allocateStorage() const { const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const auto dimensions = _gpuObject.getDimensions(); - const auto mips = _gpuObject.getNumMipLevels(); + const auto mips = _gpuObject.getNumMips(); glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, mips - 1); } void GL45FixedAllocationTexture::syncSampler() const { @@ -216,7 +219,7 @@ GL45AttachmentTexture::~GL45AttachmentTexture() { using GL45StrictResourceTexture = GL45Backend::GL45StrictResourceTexture; GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45FixedAllocationTexture(backend, texture) { - auto mipLevels = _gpuObject.getNumMipLevels(); + auto mipLevels = _gpuObject.getNumMips(); for (uint16_t sourceMip = 0; sourceMip < mipLevels; ++sourceMip) { uint16_t targetMip = sourceMip; size_t maxFace = GLTexture::getFaceCount(_target); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 4083f09251..a614d62221 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -441,7 +441,7 @@ void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& cu using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL45VariableAllocationTexture(backend, texture) { - auto mipLevels = texture.evalNumMips(); + auto mipLevels = texture.getNumMips(); _allocatedMip = mipLevels; uvec3 mipDimensions; for (uint16_t mip = 0; mip < mipLevels; ++mip) { @@ -463,10 +463,10 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { _allocatedMip = allocatedMip; const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); - const auto totalMips = _gpuObject.getNumMipLevels(); + const auto totalMips = _gpuObject.getNumMips(); const auto mips = totalMips - _allocatedMip; glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); - auto mipLevels = _gpuObject.getNumMipLevels(); + auto mipLevels = _gpuObject.getNumMips(); _size = 0; for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { _size += _gpuObject.evalMipSize(mip); @@ -476,7 +476,7 @@ void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { } void GL45ResourceTexture::copyMipsFromTexture() { - auto mipLevels = _gpuObject.getNumMipLevels(); + auto mipLevels = _gpuObject.getNumMips(); size_t maxFace = GLTexture::getFaceCount(_target); for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { uint16_t targetMip = sourceMip - _allocatedMip; @@ -495,13 +495,13 @@ void GL45ResourceTexture::promote() { PROFILE_RANGE(render_gpu_gl, __FUNCTION__); Q_ASSERT(_allocatedMip > 0); GLuint oldId = _id; - uint32_t oldSize = _size; + auto oldSize = _size; // create new texture const_cast(_id) = allocate(_gpuObject); uint16_t oldAllocatedMip = _allocatedMip; // allocate storage for new level allocateStorage(_allocatedMip - std::min(_allocatedMip, 2)); - uint16_t mips = _gpuObject.getNumMipLevels(); + uint16_t mips = _gpuObject.getNumMips(); // copy pre-existing mips for (uint16_t mip = _populatedMip; mip < mips; ++mip) { auto mipDimensions = _gpuObject.evalMipDimensions(mip); @@ -534,7 +534,7 @@ void GL45ResourceTexture::demote() { const_cast(_id) = allocate(_gpuObject); allocateStorage(_allocatedMip + 1); _populatedMip = std::max(_populatedMip, _allocatedMip); - uint16_t mips = _gpuObject.getNumMipLevels(); + uint16_t mips = _gpuObject.getNumMips(); // copy pre-existing mips for (uint16_t mip = _populatedMip; mip < mips; ++mip) { auto mipDimensions = _gpuObject.evalMipDimensions(mip); diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index cc570f696f..0030b2fa88 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -265,7 +265,7 @@ void Context::incrementBufferGPUCount() { auto total = ++_bufferGPUCount; if (total > max.load()) { max = total; - qCDebug(gpulogging) << "New max GPU buffers " << total; + // qCDebug(gpulogging) << "New max GPU buffers " << total; } } void Context::decrementBufferGPUCount() { @@ -299,7 +299,7 @@ void Context::incrementTextureGPUCount() { auto total = ++_textureGPUCount; if (total > max.load()) { max = total; - qCDebug(gpulogging) << "New max GPU textures " << total; + // qCDebug(gpulogging) << "New max GPU textures " << total; } } void Context::decrementTextureGPUCount() { @@ -311,7 +311,7 @@ void Context::incrementTextureGPUSparseCount() { auto total = ++_textureGPUSparseCount; if (total > max.load()) { max = total; - qCDebug(gpulogging) << "New max GPU textures " << total; + // qCDebug(gpulogging) << "New max GPU textures " << total; } } void Context::decrementTextureGPUSparseCount() { @@ -378,7 +378,7 @@ void Context::incrementTextureGPUTransferCount() { auto total = ++_textureGPUTransferCount; if (total > max.load()) { max = total; - qCDebug(gpulogging) << "New max GPU textures transfers" << total; + // qCDebug(gpulogging) << "New max GPU textures transfers" << total; } } diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 0d3291a74d..b49c681889 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -32,7 +32,7 @@ Framebuffer* Framebuffer::create(const std::string& name) { Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_POINT))); colorTexture->setSource("Framebuffer::colorTexture"); framebuffer->setRenderBuffer(0, colorTexture); @@ -43,8 +43,8 @@ Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBuf Framebuffer* Framebuffer::create(const std::string& name, const Format& colorBufferFormat, const Format& depthStencilBufferFormat, uint16 width, uint16 height) { auto framebuffer = Framebuffer::create(name); - auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); - auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthStencilBufferFormat, width, height, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto colorTexture = TexturePointer(Texture::createRenderBuffer(colorBufferFormat, width, height, Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_POINT))); + auto depthTexture = TexturePointer(Texture::createRenderBuffer(depthStencilBufferFormat, width, height, Texture::SINGLE_MIP, Sampler(Sampler::FILTER_MIN_MAG_POINT))); framebuffer->setRenderBuffer(0, colorTexture); framebuffer->setDepthStencilBuffer(depthTexture, depthStencilBufferFormat); diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 38019c5a03..1e65972114 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -149,8 +149,14 @@ PixelsPointer MemoryStorage::getMipFace(uint16 level, uint8 face) const { return PixelsPointer(); } + Size MemoryStorage::getMipFaceSize(uint16 level, uint8 face) const { - return getMipFace(level, face)->getSize(); + PixelsPointer mipFace = getMipFace(level, face); + if (mipFace) { + return mipFace->getSize(); + } else { + return 0; + } } bool MemoryStorage::isMipAvailable(uint16 level, uint8 face) const { @@ -209,44 +215,43 @@ void Texture::MemoryStorage::assignMipFaceData(uint16 level, uint8 face, const s Texture* Texture::createExternal(const ExternalRecycler& recycler, const Sampler& sampler) { Texture* tex = new Texture(TextureUsageType::EXTERNAL); tex->_type = TEX_2D; - tex->_maxMip = 0; + tex->_maxMipLevel = 0; tex->_sampler = sampler; tex->setExternalRecycler(recycler); return tex; } -Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); +Texture* Texture::createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) { + return create(TextureUsageType::RENDERBUFFER, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler); } -Texture* Texture::create1D(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, sampler); +Texture* Texture::create1D(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) { + return create(TextureUsageType::RESOURCE, TEX_1D, texelFormat, width, 1, 1, 1, 0, numMips, sampler); } -Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); +Texture* Texture::create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) { + return create(TextureUsageType::RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler); } -Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler) { - return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, sampler); +Texture* Texture::createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips, const Sampler& sampler) { + return create(TextureUsageType::STRICT_RESOURCE, TEX_2D, texelFormat, width, height, 1, 1, 0, numMips, sampler); } -Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, sampler); +Texture* Texture::create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips, const Sampler& sampler) { + return create(TextureUsageType::RESOURCE, TEX_3D, texelFormat, width, height, depth, 1, 0, numMips, sampler); } -Texture* Texture::createCube(const Element& texelFormat, uint16 width, const Sampler& sampler) { - return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, sampler); +Texture* Texture::createCube(const Element& texelFormat, uint16 width, uint16 numMips, const Sampler& sampler) { + return create(TextureUsageType::RESOURCE, TEX_CUBE, texelFormat, width, width, 1, 1, 0, numMips, sampler); } -Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler) +Texture* Texture::create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler) { Texture* tex = new Texture(usageType); tex->_storage.reset(new MemoryStorage()); tex->_type = type; tex->_storage->assignTexture(tex); - tex->_maxMip = 0; - tex->resize(type, texelFormat, width, height, depth, numSamples, numSlices); + tex->resize(type, texelFormat, width, height, depth, numSamples, numSlices, numMips); tex->_sampler = sampler; @@ -278,7 +283,7 @@ Texture::~Texture() { } } -Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) { +Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips) { if (width && height && depth && numSamples) { bool changed = false; @@ -313,9 +318,19 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt _depth = depth; changed = true; } - + + if ((_maxMipLevel + 1) != numMips) { + _maxMipLevel = safeNumMips(numMips) - 1; + changed = true; + } + + if (texelFormat != _texelFormat) { + _texelFormat = texelFormat; + changed = true; + } + // Evaluate the new size with the new format - uint32_t size = NUM_FACES_PER_TYPE[_type] *_width * _height * _depth * _numSamples * texelFormat.getSize(); + Size size = NUM_FACES_PER_TYPE[_type] * _height * _depth * evalPaddedSize(_numSamples * _width * _texelFormat.getSize()); // If size change then we need to reset if (changed || (size != getSize())) { @@ -324,12 +339,6 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt _stamp++; } - // TexelFormat might have change, but it's mostly interpretation - if (texelFormat != _texelFormat) { - _texelFormat = texelFormat; - _stamp++; - } - // Here the Texture has been fully defined from the gpu point of view (size and format) _defined = true; } else { @@ -339,23 +348,6 @@ Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 widt return _size; } -Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) { - return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 0); -} -Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) { - return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 0); -} -Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) { - return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 0); -} -Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) { - return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 0); -} - -Texture::Size Texture::reformat(const Element& texelFormat) { - return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), _numSlices); -} - bool Texture::isColorRenderTarget() const { return (_texelFormat.getSemantic() == gpu::RGBA); } @@ -364,7 +356,7 @@ bool Texture::isDepthStencilRenderTarget() const { return (_texelFormat.getSemantic() == gpu::DEPTH) || (_texelFormat.getSemantic() == gpu::DEPTH_STENCIL); } -uint16 Texture::evalDimNumMips(uint16 size) { +uint16 Texture::evalDimMaxNumMips(uint16 size) { double largerDim = size; double val = log(largerDim)/log(2.0); return 1 + (uint16) val; @@ -372,7 +364,7 @@ uint16 Texture::evalDimNumMips(uint16 size) { static const double LOG_2 = log(2.0); -uint16 Texture::evalNumMips(const Vec3u& dimensions) { +uint16 Texture::evalMaxNumMips(const Vec3u& dimensions) { double largerDim = glm::compMax(dimensions); double val = log(largerDim) / LOG_2; return 1 + (uint16)val; @@ -380,8 +372,34 @@ uint16 Texture::evalNumMips(const Vec3u& dimensions) { // The number mips that the texture could have if all existed // = log2(max(width, height, depth)) -uint16 Texture::evalNumMips() const { - return evalNumMips({ _width, _height, _depth }); +uint16 Texture::evalMaxNumMips() const { + return evalMaxNumMips({ _width, _height, _depth }); +} + +// Check a num of mips requested against the maximum possible specified +// if passing -1 then answer the max +// simply does (askedNumMips == 0 ? maxNumMips : (numstd::min(askedNumMips, maxNumMips)) +uint16 Texture::safeNumMips(uint16 askedNumMips, uint16 maxNumMips) { + if (askedNumMips > 0) { + return std::min(askedNumMips, maxNumMips); + } else { + return maxNumMips; + } +} + +// Same but applied to this texture's num max mips from evalNumMips() +uint16 Texture::safeNumMips(uint16 askedNumMips) const { + return safeNumMips(askedNumMips, evalMaxNumMips()); +} + +Size Texture::evalTotalSize(uint16 startingMip) const { + Size size = 0; + uint16 minMipLevel = std::max(getMinMip(), startingMip); + uint16 maxMipLevel = getMaxMip(); + for (uint16 level = minMipLevel; level <= maxMipLevel; level++) { + size += evalMipSize(level); + } + return size * getNumSlices(); } void Texture::setStoredMipFormat(const Element& format) { @@ -408,7 +426,7 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { if (_autoGenerateMips) { return; } - if (level >= evalNumMips()) { + if (level >= getNumMips()) { return; } } @@ -418,7 +436,6 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { auto size = storage->size(); if (storage->size() == expectedSize) { _storage->assignMipData(level, storage); - _maxMip = std::max(_maxMip, level); _stamp++; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images @@ -426,7 +443,6 @@ void Texture::assignStoredMip(uint16 level, storage::StoragePointer& storage) { // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... _storage->assignMipData(level, storage); - _maxMip = std::max(_maxMip, level); _stamp++; } } @@ -437,7 +453,7 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin if (_autoGenerateMips) { return; } - if (level >= evalNumMips()) { + if (level >= getNumMips()) { return; } } @@ -447,7 +463,6 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin auto size = storage->size(); if (size == expectedSize) { _storage->assignMipFaceData(level, face, storage); - _maxMip = std::max(_maxMip, level); _stamp++; } else if (size > expectedSize) { // NOTE: We are facing this case sometime because apparently QImage (from where we get the bits) is generating images @@ -455,71 +470,36 @@ void Texture::assignStoredMipFace(uint16 level, uint8 face, storage::StoragePoin // We should probably consider something a bit more smart to get the correct result but for now (UI elements) // it seems to work... _storage->assignMipFaceData(level, face, storage); - _maxMip = std::max(_maxMip, level); _stamp++; } } - -uint16 Texture::autoGenerateMips(uint16 maxMip) { +void Texture::setAutoGenerateMips(bool enable) { bool changed = false; if (!_autoGenerateMips) { changed = true; _autoGenerateMips = true; } - auto newMaxMip = std::min((uint16)(evalNumMips() - 1), maxMip); - if (newMaxMip != _maxMip) { - changed = true; - _maxMip = newMaxMip;; - } - if (changed) { _stamp++; } - - return _maxMip; } -uint16 Texture::getStoredMipWidth(uint16 level) const { - if (!isStoredMipFaceAvailable(level)) { - return 0; +Size Texture::getStoredMipSize(uint16 level) const { + PixelsPointer mipFace = accessStoredMipFace(level); + Size size = 0; + if (mipFace && mipFace->getSize()) { + for (int face = 0; face < getNumFaces(); face++) { + size += getStoredMipFaceSize(level, face); + } } - return evalMipWidth(level); + return size; } -uint16 Texture::getStoredMipHeight(uint16 level) const { - if (!isStoredMipFaceAvailable(level)) { - return 0; - } - return evalMipHeight(level); -} - -uint16 Texture::getStoredMipDepth(uint16 level) const { - if (!isStoredMipFaceAvailable(level)) { - return 0; - } - return evalMipDepth(level); -} - -uint32 Texture::getStoredMipNumTexels(uint16 level) const { - if (!isStoredMipFaceAvailable(level)) { - return 0; - } - return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level); -} - -uint32 Texture::getStoredMipSize(uint16 level) const { - if (!isStoredMipFaceAvailable(level)) { - return 0; - } - - return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level) * getTexelFormat().getSize(); -} - -gpu::Resource::Size Texture::getStoredSize() const { - auto size = 0; - for (int level = 0; level < evalNumMips(); ++level) { +Size Texture::getStoredSize() const { + Size size = 0; + for (int level = 0; level < getNumMips(); level++) { size += getStoredMipSize(level); } return size; @@ -937,7 +917,7 @@ bool TextureSource::isDefined() const { bool Texture::setMinMip(uint16 newMinMip) { uint16 oldMinMip = _minMip; - _minMip = std::min(std::max(_minMip, newMinMip), _maxMip); + _minMip = std::min(std::max(_minMip, newMinMip), getMaxMip()); return oldMinMip != _minMip; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index d6185df0d4..eab02141f0 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -232,9 +232,7 @@ public: bool operator!=(const Usage& usage) { return (_flags != usage._flags); } }; - using PixelsPointer = storage::StoragePointer; - - enum Type { + enum Type : uint8 { TEX_1D = 0, TEX_2D, TEX_3D, @@ -255,7 +253,13 @@ public: NUM_CUBE_FACES, // Not a valid vace index }; + // Lines of pixels are padded to be a multiple of "PACKING_SIZE" which is 4 bytes + static const uint32 PACKING_SIZE = 4; + static uint8 evalPaddingNumBytes(Size byteSize) { return (uint8) (3 - (byteSize + 3) % PACKING_SIZE); } + static Size evalPaddedSize(Size byteSize) { return byteSize + (Size) evalPaddingNumBytes(byteSize); } + + using PixelsPointer = storage::StoragePointer; class Storage { public: Storage() {} @@ -322,14 +326,19 @@ public: friend class Texture; }; - static Texture* create1D(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); - static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, const Sampler& sampler = Sampler()); - static Texture* createCube(const Element& texelFormat, uint16 width, const Sampler& sampler = Sampler()); - static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); - static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, const Sampler& sampler = Sampler()); + static const uint16 MAX_NUM_MIPS = 0; + static const uint16 SINGLE_MIP = 1; + static Texture* create1D(const Element& texelFormat, uint16 width, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); + static Texture* create2D(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); + static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); + static Texture* createCube(const Element& texelFormat, uint16 width, uint16 numMips = 1, const Sampler& sampler = Sampler()); + static Texture* createRenderBuffer(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); + static Texture* createStrict(const Element& texelFormat, uint16 width, uint16 height, uint16 numMips = SINGLE_MIP, const Sampler& sampler = Sampler()); static Texture* createExternal(const ExternalRecycler& recycler, const Sampler& sampler = Sampler()); + // After the texture has been created, it should be defined + bool isDefined() const { return _defined; } + Texture(TextureUsageType usageType); Texture(const Texture& buf); // deep copy of the sysmem texture Texture& operator=(const Texture& buf); // deep copy of the sysmem texture @@ -339,20 +348,9 @@ public: Stamp getDataStamp() const { return _storage->getStamp(); } // The theoretical size in bytes of data stored in the texture + // For the master (level) first level of mip Size getSize() const override { return _size; } - // The actual size in bytes of data stored in the texture - Size getStoredSize() const; - - // Resize, unless auto mips mode would destroy all the sub mips - Size resize1D(uint16 width, uint16 numSamples); - Size resize2D(uint16 width, uint16 height, uint16 numSamples); - Size resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples); - Size resizeCube(uint16 width, uint16 numSamples); - - // Reformat, unless auto mips mode would destroy all the sub mips - Size reformat(const Element& texelFormat); - // Size and format Type getType() const { return _type; } TextureUsageType getUsageType() const { return _usageType; } @@ -361,23 +359,18 @@ public: bool isDepthStencilRenderTarget() const; const Element& getTexelFormat() const { return _texelFormat; } - bool hasBorder() const { return false; } Vec3u getDimensions() const { return Vec3u(_width, _height, _depth); } uint16 getWidth() const { return _width; } uint16 getHeight() const { return _height; } uint16 getDepth() const { return _depth; } - uint32 getRowPitch() const { return getWidth() * getTexelFormat().getSize(); } - // The number of faces is mostly used for cube map, and maybe for stereo ? otherwise it's 1 // For cube maps, this means the pixels of the different faces are supposed to be packed back to back in a mip // as if the height was NUM_FACES time bigger. static uint8 NUM_FACES_PER_TYPE[NUM_TYPES]; uint8 getNumFaces() const { return NUM_FACES_PER_TYPE[getType()]; } - uint32 getNumTexels() const { return _width * _height * _depth * getNumFaces(); } - // The texture is an array if the _numSlices is not 0. // otherwise, if _numSLices is 0, then the texture is NOT an array // The number of slices returned is 1 at the minimum (if not an array) or the actual _numSlices. @@ -385,79 +378,74 @@ public: uint16 getNumSlices() const { return (isArray() ? _numSlices : 1); } uint16 getNumSamples() const { return _numSamples; } - - // NumSamples can only have certain values based on the hw static uint16 evalNumSamplesUsed(uint16 numSamplesTried); + // max mip is in the range [ 0 if no sub mips, log2(max(width, height, depth))] + // It is defined at creation time (immutable) + uint16 getMaxMip() const { return _maxMipLevel; } + uint16 getNumMips() const { return _maxMipLevel + 1; } + // Mips size evaluation // The number mips that a dimension could haves // = 1 + log2(size) - static uint16 evalDimNumMips(uint16 size); + static uint16 evalDimMaxNumMips(uint16 size); // The number mips that the texture could have if all existed // = 1 + log2(max(width, height, depth)) - uint16 evalNumMips() const; + uint16 evalMaxNumMips() const; + static uint16 evalMaxNumMips(const Vec3u& dimensions); - static uint16 evalNumMips(const Vec3u& dimensions); + // Check a num of mips requested against the maximum possible specified + // if passing -1 then answer the max + // simply does (askedNumMips == -1 ? maxMips : (numstd::min(askedNumMips, max)) + static uint16 safeNumMips(uint16 askedNumMips, uint16 maxMips); + + // Same but applied to this texture's num max mips from evalNumMips() + uint16 safeNumMips(uint16 askedNumMips) const; // Eval the size that the mips level SHOULD have // not the one stored in the Texture - static const uint MIN_DIMENSION = 1; Vec3u evalMipDimensions(uint16 level) const; uint16 evalMipWidth(uint16 level) const { return std::max(_width >> level, 1); } uint16 evalMipHeight(uint16 level) const { return std::max(_height >> level, 1); } uint16 evalMipDepth(uint16 level) const { return std::max(_depth >> level, 1); } + // The size of a face is a multiple of the padded line = (width * texelFormat_size + alignment padding) + Size evalMipLineSize(uint16 level) const { return evalPaddedSize(evalMipWidth(level) * getTexelFormat().getSize()); } + // Size for each face of a mip at a particular level uint32 evalMipFaceNumTexels(uint16 level) const { return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level); } - uint32 evalMipFaceSize(uint16 level) const { return evalMipFaceNumTexels(level) * getTexelFormat().getSize(); } + Size evalMipFaceSize(uint16 level) const { return evalMipLineSize(level) * evalMipHeight(level) * evalMipDepth(level); } // Total size for the mip uint32 evalMipNumTexels(uint16 level) const { return evalMipFaceNumTexels(level) * getNumFaces(); } - uint32 evalMipSize(uint16 level) const { return evalMipNumTexels(level) * getTexelFormat().getSize(); } + Size evalMipSize(uint16 level) const { return evalMipFaceSize(level) * getNumFaces(); } - uint32 evalStoredMipFaceSize(uint16 level, const Element& format) const { return evalMipFaceNumTexels(level) * format.getSize(); } - uint32 evalStoredMipSize(uint16 level, const Element& format) const { return evalMipNumTexels(level) * format.getSize(); } + // Total size for all the mips of the texture + Size evalTotalSize(uint16 startingMip = 0) const; - uint32 evalTotalSize(uint16 startingMip = 0) const { - uint32 size = 0; - uint16 minMipLevel = std::max(getMinMip(), startingMip); - uint16 maxMipLevel = getMaxMip(); - for (uint16 l = minMipLevel; l <= maxMipLevel; l++) { - size += evalMipSize(l); - } - return size * getNumSlices(); - } - - // max mip is in the range [ 0 if no sub mips, log2(max(width, height, depth))] - // if autoGenerateMip is on => will provide the maxMIp level specified - // else provide the deepest mip level provided through assignMip - uint16 getMaxMip() const { return _maxMip; } - uint16 getMinMip() const { return _minMip; } - uint16 getNumMipLevels() const { return _maxMip + 1; } - uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } + // Compute the theorical size of the texture elements storage depending on the specified format + Size evalStoredMipLineSize(uint16 level, const Element& format) const { return evalPaddedSize(evalMipWidth(level) * format.getSize()); } + Size evalStoredMipFaceSize(uint16 level, const Element& format) const { return evalMipFaceNumTexels(level) * format.getSize(); } + Size evalStoredMipSize(uint16 level, const Element& format) const { return evalMipNumTexels(level) * format.getSize(); } + // For convenience assign a source name const std::string& source() const { return _source; } void setSource(const std::string& source) { _source = source; } + + // Potentially change the minimum mip (mostly for debugging purpose) bool setMinMip(uint16 newMinMip); bool incremementMinMip(uint16 count = 1); + uint16 getMinMip() const { return _minMip; } + uint16 usedMipLevels() const { return (getNumMips() - _minMip); } - // Generate the mips automatically - // But the sysmem version is not available + // Generate the sub mips automatically for the texture + // If the storage version is not available (from CPU memory) // Only works for the standard formats - // Specify the maximum Mip level available - // 0 is the default one - // 1 is the first level - // ... - // nbMips - 1 is the last mip level - // - // If -1 then all the mips are generated - // - // Return the totalnumber of mips that will be available - uint16 autoGenerateMips(uint16 maxMip); + void setAutoGenerateMips(bool enable); bool isAutogenerateMips() const { return _autoGenerateMips; } // Managing Storage and mips @@ -471,30 +459,22 @@ public: // in case autoGen is on, this doesn't allocate // Explicitely assign mip data for a certain level // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed - void assignStoredMip(uint16 level, Size size, const Byte* bytes); void assignStoredMipFace(uint16 level, uint8 face, Size size, const Byte* bytes); void assignStoredMip(uint16 level, storage::StoragePointer& storage); void assignStoredMipFace(uint16 level, uint8 face, storage::StoragePointer& storage); - // Access the the sub mips - bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } + // Access the stored mips and faces const PixelsPointer accessStoredMipFace(uint16 level, uint8 face = 0) const { return _storage->getMipFace(level, face); } + bool isStoredMipFaceAvailable(uint16 level, uint8 face = 0) const { return _storage->isMipAvailable(level, face); } Size getStoredMipFaceSize(uint16 level, uint8 face = 0) const { return _storage->getMipFaceSize(level, face); } + Size getStoredMipSize(uint16 level) const; + Size getStoredSize() const; void setStorage(std::unique_ptr& newStorage); void setKtxBacking(const std::string& filename); - // access sizes for the stored mips - uint16 getStoredMipWidth(uint16 level) const; - uint16 getStoredMipHeight(uint16 level) const; - uint16 getStoredMipDepth(uint16 level) const; - uint32 getStoredMipNumTexels(uint16 level) const; - uint32 getStoredMipSize(uint16 level) const; - - bool isDefined() const { return _defined; } - // Usage is a a set of flags providing Semantic about the usage of the Texture. void setUsage(const Usage& usage) { _usage = usage; } Usage getUsage() const { return _usage; } @@ -520,7 +500,7 @@ public: ExternalUpdates getUpdates() const; - // Textures can be serialized directly to ktx data file, here is how + // Textures can be serialized directly to ktx data file, here is how static ktx::KTXUniquePointer serialize(const Texture& texture); static Texture* unserialize(const std::string& ktxFile, TextureUsageType usageType = TextureUsageType::RESOURCE, Usage usage = Usage(), const Sampler::Desc& sampler = Sampler::Desc()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); @@ -545,7 +525,7 @@ protected: Sampler _sampler; Stamp _samplerStamp { 0 }; - uint32 _size { 0 }; + Size _size { 0 }; Element _texelFormat; uint16 _width { 1 }; @@ -553,9 +533,14 @@ protected: uint16 _depth { 1 }; uint16 _numSamples { 1 }; - uint16 _numSlices { 0 }; // if _numSlices is 0, the texture is not an "Array", the getNumSlices reported is 1 - uint16 _maxMip { 0 }; + // if _numSlices is 0, the texture is not an "Array", the getNumSlices reported is 1 + uint16 _numSlices { 0 }; + + // valid _maxMipLevel is in the range [ 0 if no sub mips, log2(max(width, height, depth) ] + // The num of mips returned is _maxMipLevel + 1 + uint16 _maxMipLevel { 0 }; + uint16 _minMip { 0 }; Type _type { TEX_1D }; @@ -567,9 +552,9 @@ protected: bool _isIrradianceValid = false; bool _defined = false; - static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, const Sampler& sampler); + static Texture* create(TextureUsageType usageType, Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips, const Sampler& sampler); - Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); + Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices, uint16 numMips); }; typedef std::shared_ptr TexturePointer; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 913ced8141..8befdf3e16 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -128,7 +128,7 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } // Number level of mips coming - header.numberOfMipmapLevels = texture.getNumMipLevels(); + header.numberOfMipmapLevels = texture.getNumMips(); ktx::Images images; for (uint32_t level = 0; level < header.numberOfMipmapLevels; level++) { @@ -224,6 +224,7 @@ Texture* Texture::unserialize(const std::string& ktxfile, TextureUsageType usage header.getPixelDepth(), 1, // num Samples header.getNumberOfSlices(), + header.getNumberOfLevels(), (isGPUKTXPayload ? gpuktxKeyValue._samplerDesc : sampler)); tex->setUsage((isGPUKTXPayload ? gpuktxKeyValue._usage : usage)); diff --git a/libraries/model/src/model/Light.cpp b/libraries/model/src/model/Light.cpp index 4ac0573cf6..11b13606b8 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/model/src/model/Light.cpp @@ -148,7 +148,7 @@ void Light::setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset) { void Light::setAmbientMap(gpu::TexturePointer ambientMap) { _ambientMap = ambientMap; if (ambientMap) { - setAmbientMapNumMips(_ambientMap->evalNumMips()); + setAmbientMapNumMips(_ambientMap->getNumMips()); } else { setAmbientMapNumMips(0); } diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index d07eae2166..e619a2d70f 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -210,7 +210,7 @@ const QImage& image, bool isLinear, bool doCompress) { void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateMips"); - auto numMips = texture->evalNumMips(); + auto numMips = texture->getNumMips(); for (uint16 level = 1; level < numMips; ++level) { QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); if (fastResize) { @@ -230,7 +230,7 @@ void generateMips(gpu::Texture* texture, QImage& image, bool fastResize) { void generateFaceMips(gpu::Texture* texture, QImage& image, uint8 face) { #if CPU_MIPMAPS PROFILE_RANGE(resource_parse, "generateFaceMips"); - auto numMips = texture->evalNumMips(); + auto numMips = texture->getNumMips(); for (uint16 level = 1; level < numMips; ++level) { QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); @@ -255,9 +255,9 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); if (isStrict) { - theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::createStrict(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); } else { - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); } theTexture->setSource(srcImageName); auto usage = gpu::Texture::Usage::Builder().withColor(); @@ -317,7 +317,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& src gpu::Element formatMip = gpu::Element::COLOR_BGRA_32; gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); @@ -345,8 +345,8 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm PROFILE_RANGE(resource_parse, "createNormalTextureFromBumpImage"); QImage image = processSourceImage(srcImage, false); - if (image.format() != QImage::Format_RGB888) { - image = image.convertToFormat(QImage::Format_RGB888); + if (image.format() != QImage::Format_Grayscale8) { + image = image.convertToFormat(QImage::Format_Grayscale8); } // PR 5540 by AlessandroSigna integrated here as a specialized TextureLoader for bumpmaps @@ -395,7 +395,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm glm::normalize(v); // convert to rgb from the value obtained computing the filter - QRgb qRgbValue = qRgba(mapComponent(v.x), mapComponent(v.y), mapComponent(v.z), 1.0); + QRgb qRgbValue = qRgba(mapComponent(v.z), mapComponent(v.y), mapComponent(v.x), 1.0); result.setPixel(i, j, qRgbValue); } } @@ -407,7 +407,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm gpu::Element formatGPU = gpu::Element::COLOR_RGBA_32; - theTexture = (gpu::Texture::create2D(formatGPU, result.width(), result.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::create2D(formatGPU, result.width(), result.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, result.byteCount(), result.constBits()); @@ -443,7 +443,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma #endif gpu::Element formatMip = gpu::Element::COLOR_R_8; - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); @@ -483,7 +483,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s #endif gpu::Element formatMip = gpu::Element::COLOR_R_8; - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); @@ -520,7 +520,7 @@ gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImag #endif gpu::Element formatMip = gpu::Element::COLOR_R_8; - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); @@ -836,7 +836,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm // If the 6 faces have been created go on and define the true Texture if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { - theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); + theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); int f = 0; @@ -848,11 +848,6 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm f++; } - if (generateMips) { - PROFILE_RANGE(resource_parse, "generateMips"); - theTexture->autoGenerateMips(-1); - } - // Generate irradiance while we are at it if (generateIrradiance) { PROFILE_RANGE(resource_parse, "generateIrradiance"); diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index ac6163c227..e4ce3c691a 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -325,7 +325,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm auto gpuTexture = _channels[i]->getGPUTexture(); if (gpuTexture) { gpuTexture->setSampler(sampler); - gpuTexture->autoGenerateMips(-1); + gpuTexture->setAutoGenerateMips(true); } batch.setResourceTexture((gpu::uint32)i, gpuTexture); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 3bacf85273..678d8b1baf 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -74,11 +74,11 @@ void AmbientOcclusionFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - _occlusionTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _occlusionTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusion")); _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); - _occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("occlusionBlurred")); _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); } diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index f95d45de04..f11d62acbe 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -52,7 +52,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { _antialiasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, defaultSampler)); + _antialiasingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler)); _antialiasingBuffer->setRenderBuffer(0, _antialiasingTexture); } diff --git a/libraries/render-utils/src/DeferredFramebuffer.cpp b/libraries/render-utils/src/DeferredFramebuffer.cpp index 40c22beba4..52329931d0 100644 --- a/libraries/render-utils/src/DeferredFramebuffer.cpp +++ b/libraries/render-utils/src/DeferredFramebuffer.cpp @@ -53,9 +53,9 @@ void DeferredFramebuffer::allocate() { auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); - _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, defaultSampler)); - _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, defaultSampler)); + _deferredColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler)); + _deferredNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(linearFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler)); + _deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler)); _deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture); _deferredFramebuffer->setRenderBuffer(1, _deferredNormalTexture); @@ -65,7 +65,7 @@ void DeferredFramebuffer::allocate() { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format if (!_primaryDepthTexture) { - _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, defaultSampler)); + _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, width, height, gpu::Texture::SINGLE_MIP, defaultSampler)); } _deferredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); @@ -75,7 +75,7 @@ void DeferredFramebuffer::allocate() { auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - _lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, defaultSampler)); + _lightingTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::R11G11B10), width, height, gpu::Texture::SINGLE_MIP, defaultSampler)); _lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("lighting")); _lightingFramebuffer->setRenderBuffer(0, _lightingTexture); _lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index ce340583ee..dc1822c0f5 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -496,14 +496,14 @@ void PreparePrimaryFramebuffer::run(const SceneContextPointer& sceneContext, con auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryColorTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler)); _primaryFramebuffer->setRenderBuffer(0, primaryColorTexture); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, defaultSampler)); + auto primaryDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler)); _primaryFramebuffer->setDepthStencilBuffer(primaryDepthTexture, depthFormat); } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 41a1bb4c74..51ce0fffa7 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -168,8 +168,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getGrayTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, textureCache->getWhiteTexture()); } // Roughness map @@ -182,8 +180,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, textureCache->getWhiteTexture()); } // Normal map @@ -196,8 +192,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, textureCache->getBlueTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, nullptr); } // Metallic map @@ -210,8 +204,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, textureCache->getBlackTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, nullptr); } // Occlusion map @@ -224,8 +216,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, textureCache->getWhiteTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, nullptr); } // Scattering map @@ -238,8 +228,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, textureCache->getWhiteTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, nullptr); } // Emissive / Lightmap @@ -259,8 +247,6 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat } else { batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getBlackTexture()); } - } else { - batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, nullptr); } } diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 45a32c1aaf..49090c2f5f 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -73,11 +73,11 @@ void PrepareFramebuffer::run(const SceneContextPointer& sceneContext, const Rend auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto colorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, defaultSampler)); + auto colorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler)); _framebuffer->setRenderBuffer(0, colorTexture); auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto depthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, defaultSampler)); + auto depthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler)); _framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); } diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index 25a01bff1b..a57657a353 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -414,7 +414,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringProfile(Rend const int PROFILE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto profileMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, PROFILE_RESOLUTION, 1, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); profileMap->setSource("Generated Scattering Profile"); diffuseProfileGPU(profileMap, args); return profileMap; @@ -425,7 +425,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin const int TABLE_RESOLUTION = 512; // const auto pixelFormat = gpu::Element::COLOR_SRGBA_32; const auto pixelFormat = gpu::Element::COLOR_R11G11B10; - auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto scatteringLUT = gpu::TexturePointer(gpu::Texture::createRenderBuffer(pixelFormat, TABLE_RESOLUTION, TABLE_RESOLUTION, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); //diffuseScatter(scatteringLUT); scatteringLUT->setSource("Generated pre-integrated scattering"); diffuseScatterGPU(profile, scatteringLUT, args); @@ -434,7 +434,7 @@ gpu::TexturePointer SubsurfaceScatteringResource::generatePreIntegratedScatterin gpu::TexturePointer SubsurfaceScatteringResource::generateScatteringSpecularBeckmann(RenderArgs* args) { const int SPECULAR_RESOLUTION = 256; - auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32 /*gpu::Element(gpu::SCALAR, gpu::HALF, gpu::RGB)*/, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); + auto beckmannMap = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, SPECULAR_RESOLUTION, SPECULAR_RESOLUTION, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP))); beckmannMap->setSource("Generated beckmannMap"); computeSpecularBeckmannGPU(beckmannMap, args); return beckmannMap; diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index f713e3ce75..a4a83bb6c5 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -72,18 +72,18 @@ void LinearDepthFramebuffer::allocate() { auto height = _frameSize.y; // For Linear Depth: - _linearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, + _linearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("linearDepth")); _linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture); _linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); // For Downsampling: - _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, + const uint16_t HALF_LINEAR_DEPTH_MAX_MIP_LEVEL = 5; + _halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RED), _halfFrameSize.x, _halfFrameSize.y, HALF_LINEAR_DEPTH_MAX_MIP_LEVEL, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); - _halfLinearDepthTexture->autoGenerateMips(5); - _halfNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, + _halfNormalTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _halfFrameSize.x, _halfFrameSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _downsampleFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("halfLinearDepth")); @@ -304,15 +304,15 @@ void SurfaceGeometryFramebuffer::allocate() { auto width = _frameSize.x; auto height = _frameSize.y; - _curvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _curvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _curvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::curvature")); _curvatureFramebuffer->setRenderBuffer(0, _curvatureTexture); - _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _lowCurvatureTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _lowCurvatureFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::lowCurvature")); _lowCurvatureFramebuffer->setRenderBuffer(0, _lowCurvatureTexture); - _blurringTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); + _blurringTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, width, height, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT))); _blurringFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("surfaceGeometry::blurring")); _blurringFramebuffer->setRenderBuffer(0, _blurringTexture); } diff --git a/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf b/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf index 46554dd7f8..205dad124e 100644 --- a/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf +++ b/libraries/render-utils/src/surfaceGeometry_downsampleDepthNormal.slf @@ -21,17 +21,17 @@ out vec4 outLinearDepth; out vec4 outNormal; void main(void) { - // Gather 2 by 2 quads from texture + // Gather 2 by 2 quads from texture and downsample // Try different filters for Z - //vec4 Zeyes = textureGather(linearDepthMap, varTexCoord0, 0); - //float Zeye = min(min(Zeyes.x, Zeyes.y), min(Zeyes.z, Zeyes.w)); - float Zeye = texture(linearDepthMap, varTexCoord0).x; + vec4 Zeyes = textureGather(linearDepthMap, varTexCoord0, 0); + // float Zeye = texture(linearDepthMap, varTexCoord0).x; vec4 rawNormalsX = textureGather(normalMap, varTexCoord0, 0); vec4 rawNormalsY = textureGather(normalMap, varTexCoord0, 1); vec4 rawNormalsZ = textureGather(normalMap, varTexCoord0, 2); + float Zeye = min(min(Zeyes.x, Zeyes.y), min(Zeyes.z, Zeyes.w)); vec3 normal = vec3(0.0); normal += unpackNormal(vec3(rawNormalsX[0], rawNormalsY[0], rawNormalsZ[0])); diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index c405f6d6ae..00fcabd7da 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -207,7 +207,7 @@ void Font::read(QIODevice& in) { formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::BGRA); } - _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), + _texture = gpu::TexturePointer(gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_POINT_MAG_LINEAR))); _texture->setStoredMipFormat(formatMip); _texture->assignStoredMip(0, image.byteCount(), image.constBits()); diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index f8b5546b92..b0329b22a5 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -108,7 +108,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra // _blurredFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat()); //} auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); - auto blurringTarget = gpu::TexturePointer(gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), blurringSampler)); + auto blurringTarget = gpu::TexturePointer(gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler)); _blurredFramebuffer->setRenderBuffer(0, blurringTarget); } @@ -131,7 +131,7 @@ bool BlurInOutResource::updateResources(const gpu::FramebufferPointer& sourceFra _outputFramebuffer->setDepthStencilBuffer(sourceFramebuffer->getDepthStencilBuffer(), sourceFramebuffer->getDepthStencilBufferFormat()); }*/ auto blurringSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT); - auto blurringTarget = gpu::TexturePointer(gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), blurringSampler)); + auto blurringTarget = gpu::TexturePointer(gpu::Texture::create2D(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), sourceFramebuffer->getWidth(), sourceFramebuffer->getHeight(), gpu::Texture::SINGLE_MIP, blurringSampler)); _outputFramebuffer->setRenderBuffer(0, blurringTarget); } diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 46c2cf3ff2..9f95e64361 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -494,7 +494,7 @@ void OpenVrDisplayPlugin::customizeContext() { _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0); for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) { if (0 != i) { - _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); + _compositeInfos[i].texture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x, _renderTargetSize.y, gpu::Texture::SINGLE_MIP, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT))); } _compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture); } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a6760a37b..e1b432d09f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1181,7 +1181,7 @@ function MyController(hand) { this.updateStylusTip(); - var DEFAULT_USE_FINGER_AS_STYLUS = true; + var DEFAULT_USE_FINGER_AS_STYLUS = false; var USE_FINGER_AS_STYLUS = Settings.getValue("preferAvatarFingerOverStylus"); if (USE_FINGER_AS_STYLUS === "") { USE_FINGER_AS_STYLUS = DEFAULT_USE_FINGER_AS_STYLUS; diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 06a60b5405..c033a2d2cd 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -81,6 +81,19 @@ body { overflow-y: auto; } +/* HACK + Makes entity properties dialog's scrollbar work on tablet such that don't need to keep pointer within scrollbar width when + using scroll handle. +*/ +body { + padding-right: 0; + margin-right: -21px; +} +body > * { + margin-right: 42px; +} +/* END OF HACK */ + table { font-family: FiraSans-SemiBold; font-size: 15px; diff --git a/scripts/system/html/css/marketplaces.css b/scripts/system/html/css/marketplaces.css index bb57bea3bc..04c132eab1 100644 --- a/scripts/system/html/css/marketplaces.css +++ b/scripts/system/html/css/marketplaces.css @@ -5,6 +5,93 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html */ + +/* + CSS rules copied from edit-style.css. + Edit-style.css is not used in its entirety because don't want custom scrollbars; default scrollbar styling is used in order + to match other marketplace pages. +*/ + +@font-face { + font-family: Raleway-Regular; + src: url(../../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ + url(../../../../fonts/Raleway-Regular.ttf), /* OSX production */ + url(../../../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */ +} + +@font-face { + font-family: Raleway-Bold; + src: url(../../../../resources/fonts/Raleway-Bold.ttf), + url(../../../../fonts/Raleway-Bold.ttf), + url(../../../../interface/resources/fonts/Raleway-Bold.ttf); +} + +@font-face { + font-family: Raleway-SemiBold; + src: url(../../../../resources/fonts/Raleway-SemiBold.ttf), + url(../../../../fonts/Raleway-SemiBold.ttf), + url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf); +} + +@font-face { + font-family: FiraSans-SemiBold; + src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf), + url(../../../../fonts/FiraSans-SemiBold.ttf), + url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf); +} + +* { + margin: 0; + padding: 0; +} + +body { + padding: 21px 21px 21px 21px; + + color: #afafaf; + background-color: #404040; + font-family: Raleway-Regular; + font-size: 15px; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + overflow-x: hidden; + overflow-y: auto; +} + +input[type=button] { + font-family: Raleway-Bold; + font-size: 13px; + text-transform: uppercase; + vertical-align: top; + height: 28px; + min-width: 120px; + padding: 0px 18px; + margin-right: 6px; + border-radius: 5px; + border: none; + color: #fff; + background-color: #000; + background: linear-gradient(#343434 20%, #000 100%); + cursor: pointer; +} + +input[type=button].blue { + color: #fff; + background-color: #1080b8; + background: linear-gradient(#00b4ef 20%, #1080b8 100%); +} + + +/* + Marketplaces-specific CSS. +*/ + body { background: white; padding: 0 0 0 0; diff --git a/scripts/system/html/marketplaces.html b/scripts/system/html/marketplaces.html index 976c0f294f..6051a9df96 100644 --- a/scripts/system/html/marketplaces.html +++ b/scripts/system/html/marketplaces.html @@ -10,7 +10,6 @@ Marketplaces - diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index c24034b38e..3488352cc0 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -72,7 +72,7 @@ function showMarketplace() { tablet.webEventReceived.connect(function (message) { if (message === GOTO_DIRECTORY) { - tablet.gotoWebScreen(MARKETPLACES_URL, MARKETPLACES_INJECT_SCRIPT_URL); + tablet.gotoWebScreen(MARKETPLACES_URL, injectURL); } if (message === QUERY_CAN_WRITE_ASSETS) { diff --git a/scripts/system/selectAudioDevice.js b/scripts/system/selectAudioDevice.js index f5929ce151..2dd426932f 100644 --- a/scripts/system/selectAudioDevice.js +++ b/scripts/system/selectAudioDevice.js @@ -15,95 +15,94 @@ (function() { // BEGIN LOCAL_SCOPE -if (typeof String.prototype.startsWith != 'function') { - String.prototype.startsWith = function (str){ - return this.slice(0, str.length) == str; - }; -} - -if (typeof String.prototype.endsWith != 'function') { - String.prototype.endsWith = function (str){ - return this.slice(-str.length) == str; - }; -} - -if (typeof String.prototype.trimStartsWith != 'function') { - String.prototype.trimStartsWith = function (str){ - if (this.startsWith(str)) { - return this.substr(str.length); - } - return this; - }; -} - -if (typeof String.prototype.trimEndsWith != 'function') { - String.prototype.trimEndsWith = function (str){ - if (this.endsWith(str)) { - return this.substr(0,this.length - str.length); - } - return this; - }; +const INPUT = "Input"; +const OUTPUT = "Output"; +function parseMenuItem(item) { + const USE = "Use "; + const FOR_INPUT = " for " + INPUT; + const FOR_OUTPUT = " for " + OUTPUT; + if (item.slice(0, USE.length) == USE) { + if (item.slice(-FOR_INPUT.length) == FOR_INPUT) { + return { device: item.slice(USE.length, -FOR_INPUT.length), mode: INPUT }; + } else if (item.slice(-FOR_OUTPUT.length) == FOR_OUTPUT) { + return { device: item.slice(USE.length, -FOR_OUTPUT.length), mode: OUTPUT }; + } + } } +// +// VAR DEFINITIONS +// +var debugPrintStatements = true; const INPUT_DEVICE_SETTING = "audio_input_device"; const OUTPUT_DEVICE_SETTING = "audio_output_device"; +var audioDevicesList = []; +var wasHmdActive = false; // assume it's not active to start +var switchedAudioInputToHMD = false; +var switchedAudioOutputToHMD = false; +var previousSelectedInputAudioDevice = ""; +var previousSelectedOutputAudioDevice = ""; +var skipMenuEvents = true; -var selectedInputMenu = ""; -var selectedOutputMenu = ""; - - var audioDevicesList = []; -function setupAudioMenus() { - removeAudioMenus(); - Menu.addSeparator("Audio", "Input Audio Device"); - - var inputDeviceSetting = Settings.getValue(INPUT_DEVICE_SETTING); - var inputDevices = AudioDevice.getInputDevices(); - var selectedInputDevice = AudioDevice.getInputDevice(); - if (inputDevices.indexOf(inputDeviceSetting) != -1 && selectedInputDevice != inputDeviceSetting) { - if (AudioDevice.setInputDevice(inputDeviceSetting)) { - selectedInputDevice = inputDeviceSetting; - } +// +// BEGIN FUNCTION DEFINITIONS +// +function debug() { + if (debugPrintStatements) { + print.apply(null, [].concat.apply(["selectAudioDevice.js:"], [].map.call(arguments, JSON.stringify))); } - print("audio input devices: " + inputDevices); - for(var i = 0; i < inputDevices.length; i++) { - var thisDeviceSelected = (inputDevices[i] == selectedInputDevice); - var menuItem = "Use " + inputDevices[i] + " for Input"; +} + + +function setupAudioMenus() { + // menu events can be triggered asynchronously; skip them for 200ms to avoid recursion and false switches + skipMenuEvents = true; + Script.setTimeout(function() { skipMenuEvents = false; }, 200); + + removeAudioMenus(); + + // Setup audio input devices + Menu.addSeparator("Audio", "Input Audio Device"); + var inputDevices = AudioDevice.getInputDevices(); + for (var i = 0; i < inputDevices.length; i++) { + var audioDeviceMenuString = "Use " + inputDevices[i] + " for Input"; Menu.addMenuItem({ menuName: "Audio", - menuItemName: menuItem, + menuItemName: audioDeviceMenuString, isCheckable: true, - isChecked: thisDeviceSelected + isChecked: inputDevices[i] == AudioDevice.getInputDevice() }); - audioDevicesList.push(menuItem); - if (thisDeviceSelected) { - selectedInputMenu = menuItem; - } + audioDevicesList.push(audioDeviceMenuString); } + // Setup audio output devices Menu.addSeparator("Audio", "Output Audio Device"); + var outputDevices = AudioDevice.getOutputDevices(); + for (var i = 0; i < outputDevices.length; i++) { + var audioDeviceMenuString = "Use " + outputDevices[i] + " for Output"; + Menu.addMenuItem({ + menuName: "Audio", + menuItemName: audioDeviceMenuString, + isCheckable: true, + isChecked: outputDevices[i] == AudioDevice.getOutputDevice() + }); + audioDevicesList.push(audioDeviceMenuString); + } +} + +function checkDeviceMismatch() { + var inputDeviceSetting = Settings.getValue(INPUT_DEVICE_SETTING); + var interfaceInputDevice = AudioDevice.getInputDevice(); + if (interfaceInputDevice != inputDeviceSetting) { + debug("Input Setting & Device mismatch! Input SETTING: " + inputDeviceSetting + "Input DEVICE IN USE: " + interfaceInputDevice); + switchAudioDevice("Use " + inputDeviceSetting + " for Input"); + } var outputDeviceSetting = Settings.getValue(OUTPUT_DEVICE_SETTING); - var outputDevices = AudioDevice.getOutputDevices(); - var selectedOutputDevice = AudioDevice.getOutputDevice(); - if (outputDevices.indexOf(outputDeviceSetting) != -1 && selectedOutputDevice != outputDeviceSetting) { - if (AudioDevice.setOutputDevice(outputDeviceSetting)) { - selectedOutputDevice = outputDeviceSetting; - } - } - print("audio output devices: " + outputDevices); - for (var i = 0; i < outputDevices.length; i++) { - var thisDeviceSelected = (outputDevices[i] == selectedOutputDevice); - var menuItem = "Use " + outputDevices[i] + " for Output"; - Menu.addMenuItem({ - menuName: "Audio", - menuItemName: menuItem, - isCheckable: true, - isChecked: thisDeviceSelected - }); - audioDevicesList.push(menuItem); - if (thisDeviceSelected) { - selectedOutputMenu = menuItem; - } + var interfaceOutputDevice = AudioDevice.getOutputDevice(); + if (interfaceOutputDevice != outputDeviceSetting) { + debug("Output Setting & Device mismatch! Output SETTING: " + outputDeviceSetting + "Output DEVICE IN USE: " + interfaceOutputDevice); + switchAudioDevice("Use " + outputDeviceSetting + " for Output"); } } @@ -112,130 +111,170 @@ function removeAudioMenus() { Menu.removeSeparator("Audio", "Output Audio Device"); for (var index = 0; index < audioDevicesList.length; index++) { - Menu.removeMenuItem("Audio", audioDevicesList[index]); + if (Menu.menuItemExists("Audio", audioDevicesList[index])) { + Menu.removeMenuItem("Audio", audioDevicesList[index]); + } } + Menu.removeMenu("Audio > Devices"); + audioDevicesList = []; } function onDevicechanged() { - print("audio devices changed, removing Audio > Devices menu..."); - Menu.removeMenu("Audio > Devices"); - print("now setting up Audio > Devices menu"); + debug("System audio devices changed. Removing and replacing Audio Menus..."); setupAudioMenus(); + checkDeviceMismatch(); } -// Have a small delay before the menu's get setup and the audio devices can switch to the last selected ones -Script.setTimeout(function () { - print("connecting deviceChanged"); - AudioDevice.deviceChanged.connect(onDevicechanged); - print("setting up Audio > Devices menu for first time"); - setupAudioMenus(); -}, 5000); - -function scriptEnding() { - Menu.removeMenu("Audio > Devices"); +function onMenuEvent(audioDeviceMenuString) { + if (!skipMenuEvents) { + switchAudioDevice(audioDeviceMenuString); + } } -Script.scriptEnding.connect(scriptEnding); +function switchAudioDevice(audioDeviceMenuString) { + // if the device is not plugged in, short-circuit + if (!~audioDevicesList.indexOf(audioDeviceMenuString)) { + return; + } -function menuItemEvent(menuItem) { - if (menuItem.startsWith("Use ")) { - if (menuItem.endsWith(" for Output")) { - var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Output"); - print("output audio selection..." + selectedDevice); - Menu.menuItemEvent.disconnect(menuItemEvent); - Menu.setIsOptionChecked(selectedOutputMenu, false); - selectedOutputMenu = menuItem; - Menu.setIsOptionChecked(selectedOutputMenu, true); - if (AudioDevice.setOutputDevice(selectedDevice)) { - Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice); - } - Menu.menuItemEvent.connect(menuItemEvent); - } else if (menuItem.endsWith(" for Input")) { - var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Input"); - print("input audio selection..." + selectedDevice); - Menu.menuItemEvent.disconnect(menuItemEvent); - Menu.setIsOptionChecked(selectedInputMenu, false); - selectedInputMenu = menuItem; - Menu.setIsOptionChecked(selectedInputMenu, true); + var selection = parseMenuItem(audioDeviceMenuString); + if (!selection) { + debug("Invalid Audio audioDeviceMenuString! Doesn't end with 'for Input' or 'for Output'"); + return; + } + + // menu events can be triggered asynchronously; skip them for 200ms to avoid recursion and false switches + skipMenuEvents = true; + Script.setTimeout(function() { skipMenuEvents = false; }, 200); + + var selectedDevice = selection.device; + if (selection.mode == INPUT) { + var currentInputDevice = AudioDevice.getInputDevice(); + if (selectedDevice != currentInputDevice) { + debug("Switching audio INPUT device from " + currentInputDevice + " to " + selectedDevice); + Menu.setIsOptionChecked("Use " + currentInputDevice + " for Input", false); if (AudioDevice.setInputDevice(selectedDevice)) { Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice); + Menu.setIsOptionChecked(audioDeviceMenuString, true); + } else { + debug("Error setting audio input device!") + Menu.setIsOptionChecked(audioDeviceMenuString, false); } - Menu.menuItemEvent.connect(menuItemEvent); + } else { + debug("Selected input device is the same as the current input device!") + Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice); + Menu.setIsOptionChecked(audioDeviceMenuString, true); + AudioDevice.setInputDevice(selectedDevice); // Still try to force-set the device (in case the user's trying to forcefully debug an issue) + } + } else if (selection.mode == OUTPUT) { + var currentOutputDevice = AudioDevice.getOutputDevice(); + if (selectedDevice != currentOutputDevice) { + debug("Switching audio OUTPUT device from " + currentOutputDevice + " to " + selectedDevice); + Menu.setIsOptionChecked("Use " + currentOutputDevice + " for Output", false); + if (AudioDevice.setOutputDevice(selectedDevice)) { + Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice); + Menu.setIsOptionChecked(audioDeviceMenuString, true); + } else { + debug("Error setting audio output device!") + Menu.setIsOptionChecked(audioDeviceMenuString, false); + } + } else { + debug("Selected output device is the same as the current output device!") + Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice); + Menu.setIsOptionChecked(audioDeviceMenuString, true); + AudioDevice.setOutputDevice(selectedDevice); // Still try to force-set the device (in case the user's trying to forcefully debug an issue) } } } -Menu.menuItemEvent.connect(menuItemEvent); +function restoreAudio() { + if (switchedAudioInputToHMD) { + debug("Switching back from HMD preferred audio input to: " + previousSelectedInputAudioDevice); + switchAudioDevice("Use " + previousSelectedInputAudioDevice + " for Input"); + switchedAudioInputToHMD = false; + } + if (switchedAudioOutputToHMD) { + debug("Switching back from HMD preferred audio output to: " + previousSelectedOutputAudioDevice); + switchAudioDevice("Use " + previousSelectedOutputAudioDevice + " for Output"); + switchedAudioOutputToHMD = false; + } +} // Some HMDs (like Oculus CV1) have a built in audio device. If they // do, then this function will handle switching to that device automatically // when you goActive with the HMD active. -var wasHmdMounted = false; // assume it's un-mounted to start -var switchedAudioInputToHMD = false; -var switchedAudioOutputToHMD = false; -var previousSelectedInputAudioDevice = ""; -var previousSelectedOutputAudioDevice = ""; - -function restoreAudio() { - if (switchedAudioInputToHMD) { - print("switching back from HMD preferred audio input to:" + previousSelectedInputAudioDevice); - menuItemEvent("Use " + previousSelectedInputAudioDevice + " for Input"); - } - if (switchedAudioOutputToHMD) { - print("switching back from HMD preferred audio output to:" + previousSelectedOutputAudioDevice); - menuItemEvent("Use " + previousSelectedOutputAudioDevice + " for Output"); - } -} - function checkHMDAudio() { - // Mounted state is changing... handle switching - if (HMD.mounted != wasHmdMounted) { - print("HMD mounted changed..."); + // HMD Active state is changing; handle switching + if (HMD.active != wasHmdActive) { + debug("HMD Active state changed!"); - // We're putting the HMD on... switch to those devices - if (HMD.mounted) { - print("NOW mounted..."); + // We're putting the HMD on; switch to those devices + if (HMD.active) { + debug("HMD is now Active."); var hmdPreferredAudioInput = HMD.preferredAudioInput(); var hmdPreferredAudioOutput = HMD.preferredAudioOutput(); - print("hmdPreferredAudioInput:" + hmdPreferredAudioInput); - print("hmdPreferredAudioOutput:" + hmdPreferredAudioOutput); + debug("hmdPreferredAudioInput: " + hmdPreferredAudioInput); + debug("hmdPreferredAudioOutput: " + hmdPreferredAudioOutput); - - var hmdHasPreferredAudio = (hmdPreferredAudioInput !== "") || (hmdPreferredAudioOutput !== ""); - if (hmdHasPreferredAudio) { - print("HMD has preferred audio!"); + if (hmdPreferredAudioInput !== "") { + debug("HMD has preferred audio input device."); previousSelectedInputAudioDevice = Settings.getValue(INPUT_DEVICE_SETTING); - previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING); - print("previousSelectedInputAudioDevice:" + previousSelectedInputAudioDevice); - print("previousSelectedOutputAudioDevice:" + previousSelectedOutputAudioDevice); - if (hmdPreferredAudioInput != previousSelectedInputAudioDevice && hmdPreferredAudioInput !== "") { - print("switching to HMD preferred audio input to:" + hmdPreferredAudioInput); + debug("previousSelectedInputAudioDevice: " + previousSelectedInputAudioDevice); + if (hmdPreferredAudioInput != previousSelectedInputAudioDevice) { switchedAudioInputToHMD = true; - menuItemEvent("Use " + hmdPreferredAudioInput + " for Input"); + switchAudioDevice("Use " + hmdPreferredAudioInput + " for Input"); } - if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice && hmdPreferredAudioOutput !== "") { - print("switching to HMD preferred audio output to:" + hmdPreferredAudioOutput); + } + if (hmdPreferredAudioOutput !== "") { + debug("HMD has preferred audio output device."); + previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING); + debug("previousSelectedOutputAudioDevice: " + previousSelectedOutputAudioDevice); + if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice) { switchedAudioOutputToHMD = true; - menuItemEvent("Use " + hmdPreferredAudioOutput + " for Output"); + switchAudioDevice("Use " + hmdPreferredAudioOutput + " for Output"); } } } else { - print("HMD NOW un-mounted..."); + debug("HMD no longer active. Restoring audio I/O devices..."); restoreAudio(); } } - wasHmdMounted = HMD.mounted; + wasHmdActive = HMD.active; } -Script.update.connect(checkHMDAudio); +// +// END FUNCTION DEFINITIONS +// +// +// BEGIN SCRIPT BODY +// +// Wait for the C++ systems to fire up before trying to do anything with audio devices +Script.setTimeout(function () { + debug("Connecting deviceChanged(), displayModeChanged(), and switchAudioDevice()..."); + AudioDevice.deviceChanged.connect(onDevicechanged); + HMD.displayModeChanged.connect(checkHMDAudio); + Menu.menuItemEvent.connect(onMenuEvent); + debug("Setting up Audio I/O menu for the first time..."); + setupAudioMenus(); + checkDeviceMismatch(); + debug("Checking HMD audio status...") + checkHMDAudio(); +}, 3000); + +debug("Connecting scriptEnding()"); Script.scriptEnding.connect(function () { restoreAudio(); removeAudioMenus(); - Menu.menuItemEvent.disconnect(menuItemEvent); - Script.update.disconnect(checkHMDAudio); + Menu.menuItemEvent.disconnect(onMenuEvent); + HMD.displayModeChanged.disconnect(checkHMDAudio); + AudioDevice.deviceChanged.disconnect(onDevicechanged); }); +// +// END SCRIPT BODY +// + }()); // END LOCAL_SCOPE