From 1b2887d5b1784a9ebd6af66ece5b3c4b5a315a15 Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Thu, 6 Apr 2017 17:06:06 +0100 Subject: [PATCH 01/11] change paintGL to return if window is minimized --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e19eea0a2e..593136e7b4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2071,7 +2071,7 @@ void Application::initializeUi() { void Application::paintGL() { // Some plugins process message events, allowing paintGL to be called reentrantly. - if (_inPaint || _aboutToQuit) { + if (_inPaint || _aboutToQuit || _window->isMinimized()) { return; } From 279997d9051420f3d2c577af9fe188cdf335d2cb Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 7 Apr 2017 06:49:28 +0100 Subject: [PATCH 02/11] change display plugin to deactivate when window minimized --- interface/src/Application.cpp | 10 ++++++++++ interface/src/Application.h | 1 + libraries/ui/src/MainWindow.cpp | 5 +++++ libraries/ui/src/MainWindow.h | 1 + 4 files changed, 17 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 593136e7b4..c48c5b11a8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1429,6 +1429,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo entityPacketSender->setMyAvatar(myAvatar.get()); connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); + connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool))); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); auto textureCache = DependencyManager::get(); @@ -6476,6 +6477,15 @@ void Application::activeChanged(Qt::ApplicationState state) { } } +void Application::windowMinimizedChanged(bool minimized) { + if (!minimized && !getActiveDisplayPlugin()->isActive()) { + getActiveDisplayPlugin()->activate(); + } else if (minimized && getActiveDisplayPlugin()->isActive()) { + getActiveDisplayPlugin()->deactivate(); + } +} + + void Application::postLambdaEvent(std::function f) { if (this->thread() == QThread::currentThread()) { f(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 44c0f127c2..ad468bab25 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -416,6 +416,7 @@ private slots: void faceTrackerMuteToggled(); void activeChanged(Qt::ApplicationState state); + void windowMinimizedChanged(bool minimized); void notifyPacketVersionMismatch(); diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index c3ad59bf48..7da79725aa 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -105,12 +105,17 @@ void MainWindow::hideEvent(QHideEvent* event) { void MainWindow::changeEvent(QEvent* event) { if (event->type() == QEvent::WindowStateChange) { QWindowStateChangeEvent* stateChangeEvent = static_cast(event); + if ((stateChangeEvent->oldState() == Qt::WindowNoState || stateChangeEvent->oldState() == Qt::WindowMaximized) && windowState() == Qt::WindowMinimized) { emit windowShown(false); + emit windowMinimizedChanged(true); } else { emit windowShown(true); + if (stateChangeEvent->oldState() == Qt::WindowMinimized) { + emit windowMinimizedChanged(false); + } } } else if (event->type() == QEvent::ActivationChange) { if (isActiveWindow()) { diff --git a/libraries/ui/src/MainWindow.h b/libraries/ui/src/MainWindow.h index db02bfa1b6..75421340a2 100644 --- a/libraries/ui/src/MainWindow.h +++ b/libraries/ui/src/MainWindow.h @@ -29,6 +29,7 @@ public slots: signals: void windowGeometryChanged(QRect geometry); void windowShown(bool shown); + void windowMinimizedChanged(bool minimized); protected: virtual void closeEvent(QCloseEvent* event) override; From 27a404d0ea3e3eda6a8893d7ba473e8d47c92e2c Mon Sep 17 00:00:00 2001 From: Triplelexx Date: Fri, 7 Apr 2017 07:09:18 +0100 Subject: [PATCH 03/11] remove extra lines --- interface/src/Application.cpp | 1 - libraries/ui/src/MainWindow.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c48c5b11a8..679de39df8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6485,7 +6485,6 @@ void Application::windowMinimizedChanged(bool minimized) { } } - void Application::postLambdaEvent(std::function f) { if (this->thread() == QThread::currentThread()) { f(); diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index 7da79725aa..20f994e63d 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -105,7 +105,6 @@ void MainWindow::hideEvent(QHideEvent* event) { void MainWindow::changeEvent(QEvent* event) { if (event->type() == QEvent::WindowStateChange) { QWindowStateChangeEvent* stateChangeEvent = static_cast(event); - if ((stateChangeEvent->oldState() == Qt::WindowNoState || stateChangeEvent->oldState() == Qt::WindowMaximized) && windowState() == Qt::WindowMinimized) { From 7bc3435cfa5d17ae37531e2265860bdbde7ee328 Mon Sep 17 00:00:00 2001 From: volansystech Date: Thu, 13 Apr 2017 02:51:30 +0530 Subject: [PATCH 04/11] Initial Commit.. If distanceToObject from avatar is more than 15, we are accelarting entity travel by 2x. This will help user to grab the entity in lesser time. --- .../system/controllers/handControllerGrab.js | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 9a6760a37b..307c7fba16 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -75,7 +75,8 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // - +var shouldTravelAtRapidPace = false; +var accelarationStartingPoint = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified @@ -2524,7 +2525,19 @@ function MyController(hand) { // compute the mass for the purpose of energy and how quickly to move object this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density); + this.shouldTravelAtRapidPace = false; var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); + + if(distanceToObject >= 15){ // Apply Rapid Pace only if distanceToObject is far than 15. + this.shouldTravelAtRapidPace = true; + var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); + var distanceToHandFromAvatarHips = worldControllerPosition.x - MyAvatarHipsPosition.x; + var ACCELERATION_START_THRESHOLD = 0.90; + this.accelarationStartingPoint = (distanceToHandFromAvatarHips * ACCELERATION_START_THRESHOLD); + }else{ + this.shouldTravelAtRapidPace = false; + } + var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); this.actionID = NULL_UUID; @@ -2619,16 +2632,22 @@ function MyController(hand) { this.grabRadius * RADIAL_GRAB_AMPLIFIER); } + if(this.shouldTravelAtRapidPace = true){ + var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); + var distanceToHandFromAvatarHips_x = worldControllerPosition.x - MyAvatarHipsPosition.x; + if(distanceToHandFromAvatarHips_x <= this.accelarationStartingPoint){ + var RAPID_PACE_RATE = 2; + this.grabRadius = (this.grabRadius / RAPID_PACE_RATE); + } + } // don't let grabRadius go all the way to zero, because it can't come back from that var MINIMUM_GRAB_RADIUS = 0.1; if (this.grabRadius < MINIMUM_GRAB_RADIUS) { this.grabRadius = MINIMUM_GRAB_RADIUS; } - var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(worldControllerRotation)); newTargetPosition = Vec3.sum(newTargetPosition, worldControllerPosition); newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition); - var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position); var handControllerData = getEntityCustomData('handControllerKey', this.grabbedThingID, defaultMoveWithHeadData); if (handControllerData.disableMoveWithHead !== true) { From b2aa3271f75ff33644db7e2ea54d69114ad05d32 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 11 Apr 2017 15:40:59 -0700 Subject: [PATCH 05/11] Adding support for variable allocated textures to the GL 4.1 backend --- libraries/gpu-gl/src/gpu/gl/GLBackend.cpp | 4 + libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 428 ++++++++++++++++++ libraries/gpu-gl/src/gpu/gl/GLTexture.h | 122 +++++ libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 28 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 242 ++++++++-- libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp | 2 - libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 104 +---- .../src/gpu/gl45/GL45BackendTexture.cpp | 16 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 403 ----------------- libraries/gpu/src/gpu/Forward.h | 1 + 10 files changed, 791 insertions(+), 559 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 0800c27839..d0db376a9d 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -744,6 +744,10 @@ void GLBackend::recycle() const { glDeleteQueries((GLsizei)ids.size(), ids.data()); } } + + GLVariableAllocationSupport::manageMemory(); + GLVariableAllocationSupport::_frameTexturesCreated = 0; + } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 65c5788227..199513fc0a 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -8,6 +8,9 @@ #include "GLTexture.h" +#include +#include + #include "GLBackend.h" using namespace gpu; @@ -111,6 +114,20 @@ GLTexture::~GLTexture() { } } +void GLTexture::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(); + } +} + GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : Parent(backend, texture, id) { } @@ -127,3 +144,414 @@ GLExternalTexture::~GLExternalTexture() { const_cast(_id) = 0; } } + + +// Variable sized textures +using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; +using WorkQueue = GLVariableAllocationSupport::WorkQueue; + +std::list GLVariableAllocationSupport::_memoryManagedTextures; +MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; +std::atomic GLVariableAllocationSupport::_memoryPressureStateStale { false }; +const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; +WorkQueue GLVariableAllocationSupport::_transferQueue; +WorkQueue GLVariableAllocationSupport::_promoteQueue; +WorkQueue GLVariableAllocationSupport::_demoteQueue; +TexturePointer GLVariableAllocationSupport::_currentTransferTexture; +size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; + +#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f +#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) + +static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); + +using TransferJob = GLVariableAllocationSupport::TransferJob; + +const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; +const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4; + +#if THREADED_TEXTURE_BUFFERING +std::shared_ptr TransferJob::_bufferThread { nullptr }; +std::atomic TransferJob::_shutdownBufferingThread { false }; +Mutex TransferJob::_mutex; +TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; + +void TransferJob::startTransferLoop() { + if (_bufferThread) { + return; + } + _shutdownBufferingThread = false; + _bufferThread = std::make_shared([] { + TransferJob::bufferLoop(); + }); +} + +void TransferJob::stopTransferLoop() { + if (!_bufferThread) { + return; + } + _shutdownBufferingThread = true; + _bufferThread->join(); + _bufferThread.reset(); + _shutdownBufferingThread = false; +} +#endif + +TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) + : _parent(parent) { + + auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + GLenum format; + GLenum type; + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); + format = texelFormat.format; + type = texelFormat.type; + auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); + + + if (0 == lines) { + _transferSize = mipSize; + _bufferingLambda = [=] { + auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData(), _transferSize); + _bufferingCompleted = true; + }; + + } else { + transferDimensions.y = lines; + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + auto bytesPerLine = (uint32_t)mipSize / dimensions.y; + auto sourceOffset = bytesPerLine * lineOffset; + _transferSize = bytesPerLine * lines; + _bufferingLambda = [=] { + auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + _buffer.resize(_transferSize); + memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); + _bufferingCompleted = true; + }; + } + + Backend::updateTextureTransferPendingSize(0, _transferSize); + + _transferLambda = [=] { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); + std::vector emptyVector; + _buffer.swap(emptyVector); + }; +} + +TransferJob::TransferJob(const GLTexture& parent, std::function transferLambda) + : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { +} + +TransferJob::~TransferJob() { + Backend::updateTextureTransferPendingSize(_transferSize, 0); +} + + +bool TransferJob::tryTransfer() { + // Disable threaded texture transfer for now +#if THREADED_TEXTURE_BUFFERING + // Are we ready to transfer + if (_bufferingCompleted) { + _transferLambda(); + return true; + } + + startBuffering(); + return false; +#else + if (!_bufferingCompleted) { + _bufferingLambda(); + _bufferingCompleted = true; + } + _transferLambda(); + return true; +#endif +} + +#if THREADED_TEXTURE_BUFFERING + +void TransferJob::startBuffering() { + if (_bufferingStarted) { + return; + } + _bufferingStarted = true; + { + Lock lock(_mutex); + _bufferLambdaQueue.push(_bufferingLambda); + } +} + +void TransferJob::bufferLoop() { + while (!_shutdownBufferingThread) { + VoidLambdaQueue workingQueue; + { + Lock lock(_mutex); + _bufferLambdaQueue.swap(workingQueue); + } + + if (workingQueue.empty()) { + QThread::msleep(5); + continue; + } + + while (!workingQueue.empty()) { + workingQueue.front()(); + workingQueue.pop(); + } + } +} +#endif + +GLVariableAllocationSupport::GLVariableAllocationSupport() { + _memoryPressureStateStale = true; +} + +GLVariableAllocationSupport::~GLVariableAllocationSupport() { + _memoryPressureStateStale = true; +} + +void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) { + _memoryManagedTextures.push_back(texturePointer); + addToWorkQueue(texturePointer); +} + +void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) { + GLTexture* gltexture = Backend::getGPUObject(*texturePointer); + GLVariableAllocationSupport* vargltexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (vargltexture->canDemote()) { + // Demote largest first + _demoteQueue.push({ texturePointer, (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Undersubscribed: + if (vargltexture->canPromote()) { + // Promote smallest first + _promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Transfer: + if (vargltexture->hasPendingTransfers()) { + // Transfer priority given to smaller mips first + _transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) }); + } + break; + + case MemoryPressureState::Idle: + break; + + default: + Q_UNREACHABLE(); + } +} + +WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() { + static WorkQueue empty; + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return _demoteQueue; + + case MemoryPressureState::Undersubscribed: + return _promoteQueue; + + case MemoryPressureState::Transfer: + return _transferQueue; + + default: + break; + } + Q_UNREACHABLE(); + return empty; +} + +// FIXME hack for stats display +QString getTextureMemoryPressureModeString() { + switch (GLVariableAllocationSupport::_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return "Oversubscribed"; + + case MemoryPressureState::Undersubscribed: + return "Undersubscribed"; + + case MemoryPressureState::Transfer: + return "Transfer"; + + case MemoryPressureState::Idle: + return "Idle"; + } + Q_UNREACHABLE(); + return "Unknown"; +} + +void GLVariableAllocationSupport::updateMemoryPressure() { + static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale + // so that we react + if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { + _memoryPressureStateStale = true; + lastAllowedMemoryAllocation = allowedMemoryAllocation; + } + + if (!_memoryPressureStateStale.exchange(false)) { + return; + } + + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + + // Clear any defunct textures (weak pointers that no longer have a valid texture) + _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { + return weakPointer.expired(); + }); + + // Convert weak pointers to strong. This new list may still contain nulls if a texture was + // deleted on another thread between the previous line and this one + std::vector strongTextures; { + strongTextures.reserve(_memoryManagedTextures.size()); + std::transform( + _memoryManagedTextures.begin(), _memoryManagedTextures.end(), + std::back_inserter(strongTextures), + [](const TextureWeakPointer& p) { return p.lock(); }); + } + + size_t totalVariableMemoryAllocation = 0; + size_t idealMemoryAllocation = 0; + bool canDemote = false; + bool canPromote = false; + bool hasTransfers = false; + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + // Track how much the texture thinks it should be using + idealMemoryAllocation += texture->evalTotalSize(); + // Track how much we're actually using + totalVariableMemoryAllocation += gltexture->size(); + canDemote |= vartexture->canDemote(); + canPromote |= vartexture->canPromote(); + hasTransfers |= vartexture->hasPendingTransfers(); + } + + size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; + float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; + + auto newState = MemoryPressureState::Idle; + if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + newState = MemoryPressureState::Oversubscribed; + } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { + newState = MemoryPressureState::Undersubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } + + if (newState != _memoryPressureState) { +#if THREADED_TEXTURE_BUFFERING + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::stopTransferLoop(); + } + _memoryPressureState = newState; + if (MemoryPressureState::Transfer == _memoryPressureState) { + TransferJob::startTransferLoop(); + } +#else + _memoryPressureState = newState; +#endif + // Clear the existing queue + _transferQueue = WorkQueue(); + _promoteQueue = WorkQueue(); + _demoteQueue = WorkQueue(); + + // Populate the existing textures into the queue + for (const auto& texture : strongTextures) { + addToWorkQueue(texture); + } + } +} + +void GLVariableAllocationSupport::processWorkQueues() { + if (MemoryPressureState::Idle == _memoryPressureState) { + return; + } + + auto& workQueue = getActiveWorkQueue(); + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + while (!workQueue.empty()) { + auto workTarget = workQueue.top(); + workQueue.pop(); + auto texture = workTarget.first.lock(); + if (!texture) { + continue; + } + + // Grab the first item off the demote queue + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + if (MemoryPressureState::Oversubscribed == _memoryPressureState) { + if (!vartexture->canDemote()) { + continue; + } + vartexture->demote(); + } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { + if (!vartexture->canPromote()) { + continue; + } + vartexture->promote(); + } else if (MemoryPressureState::Transfer == _memoryPressureState) { + if (!vartexture->hasPendingTransfers()) { + continue; + } + vartexture->executeNextTransfer(texture); + } else { + Q_UNREACHABLE(); + } + + // Reinject into the queue if more work to be done + addToWorkQueue(texture); + break; + } + + if (workQueue.empty()) { + _memoryPressureStateStale = true; + } +} + +void GLVariableAllocationSupport::manageMemory() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + updateMemoryPressure(); + processWorkQueues(); +} + + +void GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) { + if (_populatedMip <= _allocatedMip) { + return; + } + + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + + if (!_pendingTransfers.empty()) { + // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture + _currentTransferTexture = currentTexture; + if (_pendingTransfers.front()->tryTransfer()) { + _pendingTransfers.pop(); + _currentTransferTexture.reset(); + } + } +} diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.h b/libraries/gpu-gl/src/gpu/gl/GLTexture.h index b47aa3e8dd..960575827f 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.h @@ -11,6 +11,9 @@ #include "GLShared.h" #include "GLBackend.h" #include "GLTexelFormat.h" +#include + +#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl { @@ -19,9 +22,124 @@ struct GLFilterMode { GLint magFilter; }; +class GLVariableAllocationSupport { + friend class GLBackend; + +public: + GLVariableAllocationSupport(); + virtual ~GLVariableAllocationSupport(); + + enum class MemoryPressureState { + Idle, + Transfer, + Oversubscribed, + Undersubscribed, + }; + + using QueuePair = std::pair; + struct QueuePairLess { + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } + }; + using WorkQueue = std::priority_queue, QueuePairLess>; + + class TransferJob { + using VoidLambda = std::function; + using VoidLambdaQueue = std::queue; + using ThreadPointer = std::shared_ptr; + const GLTexture& _parent; + // Holds the contents to transfer to the GPU in CPU memory + std::vector _buffer; + // Indicates if a transfer from backing storage to interal storage has started + bool _bufferingStarted { false }; + bool _bufferingCompleted { false }; + VoidLambda _transferLambda; + VoidLambda _bufferingLambda; + +#if THREADED_TEXTURE_BUFFERING + static Mutex _mutex; + static VoidLambdaQueue _bufferLambdaQueue; + static ThreadPointer _bufferThread; + static std::atomic _shutdownBufferingThread; + static void bufferLoop(); +#endif + + public: + TransferJob(const TransferJob& other) = delete; + TransferJob(const GLTexture& parent, std::function transferLambda); + TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + ~TransferJob(); + bool tryTransfer(); + +#if THREADED_TEXTURE_BUFFERING + static void startTransferLoop(); + static void stopTransferLoop(); +#endif + + private: + size_t _transferSize { 0 }; +#if THREADED_TEXTURE_BUFFERING + void startBuffering(); +#endif + void transfer(); + }; + + using TransferQueue = std::queue>; + static MemoryPressureState _memoryPressureState; + +public: + static void addMemoryManagedTexture(const TexturePointer& texturePointer); + +protected: + static size_t _frameTexturesCreated; + static std::atomic _memoryPressureStateStale; + static std::list _memoryManagedTextures; + static WorkQueue _transferQueue; + static WorkQueue _promoteQueue; + static WorkQueue _demoteQueue; + static TexturePointer _currentTransferTexture; + static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; + static const uvec3 MAX_TRANSFER_DIMENSIONS; + static const size_t MAX_TRANSFER_SIZE; + + + static void updateMemoryPressure(); + static void processWorkQueues(); + static void addToWorkQueue(const TexturePointer& texture); + static WorkQueue& getActiveWorkQueue(); + + static void manageMemory(); + + //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } + bool canPromote() const { return _allocatedMip > 0; } + bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } + void executeNextTransfer(const TexturePointer& currentTexture); + virtual void populateTransferQueue() = 0; + virtual void promote() = 0; + virtual void demote() = 0; + + // The allocated mip level, relative to the number of mips in the gpu::Texture object + // The relationship between a given glMip to the original gpu::Texture mip is always + // glMip + _allocatedMip + uint16 _allocatedMip { 0 }; + // The populated mip level, relative to the number of mips in the gpu::Texture object + // This must always be >= the allocated mip + uint16 _populatedMip { 0 }; + // The highest (lowest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _maxAllocatedMip { 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 + TransferQueue _pendingTransfers; +}; + class GLTexture : public GLObject { using Parent = GLObject; friend class GLBackend; + friend class GLVariableAllocationSupport; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; @@ -45,6 +163,8 @@ public: protected: virtual Size size() const = 0; virtual void generateMips() const = 0; + virtual void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const = 0; + virtual void copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final; GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); }; @@ -57,6 +177,8 @@ public: protected: GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); void generateMips() const override {} + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override {} + 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 93d65b74dd..0c3c70dbb8 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -45,8 +45,7 @@ public: 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; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override; virtual void syncSampler() const; void withPreservedTexture(std::function f) const; @@ -86,8 +85,29 @@ public: GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); }; - class GL41ResourceTexture : public GL41FixedAllocationTexture { - using Parent = GL41FixedAllocationTexture; + class GL41VariableAllocationTexture : public GL41Texture, public GLVariableAllocationSupport { + using Parent = GL41Texture; + friend class GL41Backend; + using PromoteLambda = std::function; + + + protected: + GL41VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GL41VariableAllocationTexture(); + + void allocateStorage(uint16 allocatedMip); + void syncSampler() const override; + void promote() override; + void demote() override; + void populateTransferQueue() override; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override; + + Size size() const override { return _size; } + Size _size { 0 }; + }; + + class GL41ResourceTexture : public GL41VariableAllocationTexture { + using Parent = GL41VariableAllocationTexture; friend class GL41Backend; protected: GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 2056085091..1cb5a42ab9 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -46,11 +46,11 @@ GLTexture* GL41Backend::syncGPUObject(const TexturePointer& texturePointer) { object = new GL41StrictResourceTexture(shared_from_this(), texture); break; - case TextureUsageType::RESOURCE: { + case TextureUsageType::RESOURCE: qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str(); object = new GL41ResourceTexture(shared_from_this(), texture); + GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); break; - } default: Q_UNREACHABLE(); @@ -69,7 +69,9 @@ GL41Texture::GL41Texture(const std::weak_ptr& backend, const Texture& GLuint GL41Texture::allocate(const Texture& texture) { GLuint result; - glGenTextures(1, &result); + // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension + glCreateTextures(getGLTextureType(texture), 1, &result); + //glGenTextures(1, &result); return result; } @@ -105,20 +107,6 @@ void GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const (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(); @@ -216,29 +204,217 @@ GL41StrictResourceTexture::GL41StrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41Texture(backend, texture) { + auto mipLevels = texture.getNumMips(); + _allocatedMip = mipLevels; + uvec3 mipDimensions; + for (uint16_t mip = 0; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + _maxAllocatedMip = _populatedMip = mip; + break; + } + } + + uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); + allocateStorage(allocatedMip); + _memoryPressureStateStale = true; + size_t maxFace = GLTexture::getFaceCount(_target); + for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip - _allocatedMip; + for (uint8_t face = 0; face < maxFace; ++face) { + copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + syncSampler(); +} + +GL41VariableAllocationTexture::~GL41VariableAllocationTexture() { + Backend::updateTextureGPUMemoryUsage(0, _size); +} + +void GL41VariableAllocationTexture::allocateStorage(uint16 allocatedMip) { + _allocatedMip = allocatedMip; + + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); + const auto totalMips = _gpuObject.getNumMips(); + const auto mips = totalMips - _allocatedMip; + withPreservedTexture([&] { + // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension + glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + }); + auto mipLevels = _gpuObject.getNumMips(); + _size = 0; + for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { + _size += _gpuObject.evalMipSize(mip); + } + Backend::updateTextureGPUMemoryUsage(0, _size); +} + + +void GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const { + withPreservedTexture([&] { + Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, format, type, sourcePointer); + }); +} + +void GL41VariableAllocationTexture::syncSampler() const { + withPreservedTexture([&] { + Parent::syncSampler(); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + }); +} + +void GL41VariableAllocationTexture::promote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip > 0); + GLuint oldId = _id; + 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)); + + withPreservedTexture([&] { + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + syncSampler(); + }); + + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + populateTransferQueue(); +} + +void GL41VariableAllocationTexture::demote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip < _maxAllocatedMip); + auto oldId = _id; + auto oldSize = _size; + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + allocateStorage(_allocatedMip + 1); + _populatedMip = std::max(_populatedMip, _allocatedMip); + + withPreservedTexture([&] { + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = _gpuObject.getNumMips(); + // copy pre-existing mips + for (uint16_t mip = _populatedMip; mip < mips; ++mip) { + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + uint16_t targetMip = mip - _allocatedMip; + uint16_t sourceMip = mip - oldAllocatedMip; + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + syncSampler(); + }); + + + // destroy the old texture + glDeleteTextures(1, &oldId); + // update the memory usage + Backend::updateTextureGPUMemoryUsage(oldSize, 0); + populateTransferQueue(); +} + + +void GL41VariableAllocationTexture::populateTransferQueue() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + if (_populatedMip <= _allocatedMip) { + return; + } + _pendingTransfers = TransferQueue(); + + const uint8_t maxFace = GLTexture::getFaceCount(_target); + uint16_t sourceMip = _populatedMip; + do { + --sourceMip; + auto targetMip = sourceMip - _allocatedMip; + auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + for (uint8_t face = 0; face < maxFace; ++face) { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + continue; + } + + // If the mip is less than the max transfer size, then just do it in one transfer + if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + // Can the mip be transferred in one go + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + continue; + } + + // break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); + const auto lines = mipDimensions.y; + auto bytesPerLine = mipSize / lines; + Q_ASSERT(0 == (mipSize % lines)); + uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + uint32_t lineOffset = 0; + while (lineOffset < lines) { + uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); + lineOffset += linesToCopy; + } + } + + // queue up the sampler and populated mip change for after the transfer has completed + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } while (sourceMip != _allocatedMip); +} + // 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); - } - } - }); - +GL41ResourceTexture::GL41ResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GL41VariableAllocationTexture(backend, texture) { if (texture.isAutogenerateMips()) { generateMips(); } } GL41ResourceTexture::~GL41ResourceTexture() { - Backend::updateTextureGPUMemoryUsage(size(), 0); } + + diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index 12c4b818f7..c9d1fb1b2f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -20,8 +20,6 @@ using namespace gpu::gl45; void GL45Backend::recycle() const { Parent::recycle(); - GL45VariableAllocationTexture::manageMemory(); - GL45VariableAllocationTexture::_frameTexturesCreated = 0; } void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index ca8028aff6..152d4cfec5 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -40,8 +40,7 @@ public: protected: GL45Texture(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; + void copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum format, GLenum type, const void* sourcePointer) const override; virtual void syncSampler() const; }; @@ -83,116 +82,17 @@ public: // Textures that can be managed at runtime to increase or decrease their memory load // - class GL45VariableAllocationTexture : public GL45Texture { + class GL45VariableAllocationTexture : public GL45Texture, public GLVariableAllocationSupport { using Parent = GL45Texture; friend class GL45Backend; using PromoteLambda = std::function; - public: - enum class MemoryPressureState { - Idle, - Transfer, - Oversubscribed, - Undersubscribed, - }; - - using QueuePair = std::pair; - struct QueuePairLess { - bool operator()(const QueuePair& a, const QueuePair& b) { - return a.second < b.second; - } - }; - using WorkQueue = std::priority_queue, QueuePairLess>; - - class TransferJob { - using VoidLambda = std::function; - using VoidLambdaQueue = std::queue; - using ThreadPointer = std::shared_ptr; - const GL45VariableAllocationTexture& _parent; - // Holds the contents to transfer to the GPU in CPU memory - std::vector _buffer; - // Indicates if a transfer from backing storage to interal storage has started - bool _bufferingStarted { false }; - bool _bufferingCompleted { false }; - VoidLambda _transferLambda; - VoidLambda _bufferingLambda; -#if THREADED_TEXTURE_BUFFERING - static Mutex _mutex; - static VoidLambdaQueue _bufferLambdaQueue; - static ThreadPointer _bufferThread; - static std::atomic _shutdownBufferingThread; - static void bufferLoop(); -#endif - - public: - TransferJob(const TransferJob& other) = delete; - TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda); - TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); - ~TransferJob(); - bool tryTransfer(); - -#if THREADED_TEXTURE_BUFFERING - static void startTransferLoop(); - static void stopTransferLoop(); -#endif - - private: - size_t _transferSize { 0 }; -#if THREADED_TEXTURE_BUFFERING - void startBuffering(); -#endif - void transfer(); - }; - - using TransferQueue = std::queue>; - static MemoryPressureState _memoryPressureState; - protected: - static size_t _frameTexturesCreated; - static std::atomic _memoryPressureStateStale; - static std::list _memoryManagedTextures; - static WorkQueue _transferQueue; - static WorkQueue _promoteQueue; - static WorkQueue _demoteQueue; - static TexturePointer _currentTransferTexture; - static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; - - - static void updateMemoryPressure(); - static void processWorkQueues(); - static void addMemoryManagedTexture(const TexturePointer& texturePointer); - static void addToWorkQueue(const TexturePointer& texture); - static WorkQueue& getActiveWorkQueue(); - - static void manageMemory(); protected: GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); ~GL45VariableAllocationTexture(); - //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } - bool canPromote() const { return _allocatedMip > 0; } - bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } - bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } - void executeNextTransfer(const TexturePointer& currentTexture); Size size() const override { return _size; } - virtual void populateTransferQueue() = 0; - virtual void promote() = 0; - virtual void demote() = 0; - - // The allocated mip level, relative to the number of mips in the gpu::Texture object - // The relationship between a given glMip to the original gpu::Texture mip is always - // glMip + _allocatedMip - uint16 _allocatedMip { 0 }; - // The populated mip level, relative to the number of mips in the gpu::Texture object - // This must always be >= the allocated mip - uint16 _populatedMip { 0 }; - // The highest (lowest resolution) mip that we will support, relative to the number - // of mips in the gpu::Texture object - uint16 _maxAllocatedMip { 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 - TransferQueue _pendingTransfers; }; class GL45ResourceTexture : public GL45VariableAllocationTexture { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index ab4153c04c..73974addff 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -67,7 +67,7 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { #else object = new GL45ResourceTexture(shared_from_this(), texture); #endif - GL45VariableAllocationTexture::addMemoryManagedTexture(texturePointer); + GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); } else { auto fallback = texturePointer->getFallbackTexture(); if (fallback) { @@ -135,20 +135,6 @@ void GL45Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const (void)CHECK_GL_ERROR(); } -void GL45Texture::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 GL45Texture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index a614d62221..a453d4207d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -27,416 +27,16 @@ using namespace gpu; using namespace gpu::gl; using namespace gpu::gl45; -// Variable sized textures using GL45VariableAllocationTexture = GL45Backend::GL45VariableAllocationTexture; -using MemoryPressureState = GL45VariableAllocationTexture::MemoryPressureState; -using WorkQueue = GL45VariableAllocationTexture::WorkQueue; - -std::list GL45VariableAllocationTexture::_memoryManagedTextures; -MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle; -std::atomic GL45VariableAllocationTexture::_memoryPressureStateStale { false }; -const uvec3 GL45VariableAllocationTexture::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; -WorkQueue GL45VariableAllocationTexture::_transferQueue; -WorkQueue GL45VariableAllocationTexture::_promoteQueue; -WorkQueue GL45VariableAllocationTexture::_demoteQueue; -TexturePointer GL45VariableAllocationTexture::_currentTransferTexture; - -#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f -#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f -#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) - -static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); - -using TransferJob = GL45VariableAllocationTexture::TransferJob; - -static const uvec3 MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; -static const size_t MAX_TRANSFER_SIZE = MAX_TRANSFER_DIMENSIONS.x * MAX_TRANSFER_DIMENSIONS.y * 4; - -#if THREADED_TEXTURE_BUFFERING -std::shared_ptr TransferJob::_bufferThread { nullptr }; -std::atomic TransferJob::_shutdownBufferingThread { false }; -Mutex TransferJob::_mutex; -TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue; - -void TransferJob::startTransferLoop() { - if (_bufferThread) { - return; - } - _shutdownBufferingThread = false; - _bufferThread = std::make_shared([] { - TransferJob::bufferLoop(); - }); -} - -void TransferJob::stopTransferLoop() { - if (!_bufferThread) { - return; - } - _shutdownBufferingThread = true; - _bufferThread->join(); - _bufferThread.reset(); - _shutdownBufferingThread = false; -} -#endif - -TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) - : _parent(parent) { - - auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - GLenum format; - GLenum type; - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); - format = texelFormat.format; - type = texelFormat.type; - auto mipSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); - - - if (0 == lines) { - _transferSize = mipSize; - _bufferingLambda = [=] { - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData(), _transferSize); - _bufferingCompleted = true; - }; - - } else { - transferDimensions.y = lines; - auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); - auto bytesPerLine = (uint32_t)mipSize / dimensions.y; - auto sourceOffset = bytesPerLine * lineOffset; - _transferSize = bytesPerLine * lines; - _bufferingLambda = [=] { - auto mipData = _parent._gpuObject.accessStoredMipFace(sourceMip, face); - _buffer.resize(_transferSize); - memcpy(&_buffer[0], mipData->readData() + sourceOffset, _transferSize); - _bufferingCompleted = true; - }; - } - - Backend::updateTextureTransferPendingSize(0, _transferSize); - - _transferLambda = [=] { - _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, format, type, _buffer.data()); - std::vector emptyVector; - _buffer.swap(emptyVector); - }; -} - -TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function transferLambda) - : _parent(parent), _bufferingCompleted(true), _transferLambda(transferLambda) { -} - -TransferJob::~TransferJob() { - Backend::updateTextureTransferPendingSize(_transferSize, 0); -} - - -bool TransferJob::tryTransfer() { - // Disable threaded texture transfer for now -#if THREADED_TEXTURE_BUFFERING - // Are we ready to transfer - if (_bufferingCompleted) { - _transferLambda(); - return true; - } - - startBuffering(); - return false; -#else - if (!_bufferingCompleted) { - _bufferingLambda(); - _bufferingCompleted = true; - } - _transferLambda(); - return true; -#endif -} - -#if THREADED_TEXTURE_BUFFERING - -void TransferJob::startBuffering() { - if (_bufferingStarted) { - return; - } - _bufferingStarted = true; - { - Lock lock(_mutex); - _bufferLambdaQueue.push(_bufferingLambda); - } -} - -void TransferJob::bufferLoop() { - while (!_shutdownBufferingThread) { - VoidLambdaQueue workingQueue; - { - Lock lock(_mutex); - _bufferLambdaQueue.swap(workingQueue); - } - - if (workingQueue.empty()) { - QThread::msleep(5); - continue; - } - - while (!workingQueue.empty()) { - workingQueue.front()(); - workingQueue.pop(); - } - } -} -#endif - - -void GL45VariableAllocationTexture::addMemoryManagedTexture(const TexturePointer& texturePointer) { - _memoryManagedTextures.push_back(texturePointer); - addToWorkQueue(texturePointer); -} - -void GL45VariableAllocationTexture::addToWorkQueue(const TexturePointer& texturePointer) { - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texturePointer); - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - if (object->canDemote()) { - // Demote largest first - _demoteQueue.push({ texturePointer, (float)object->size() }); - } - break; - - case MemoryPressureState::Undersubscribed: - if (object->canPromote()) { - // Promote smallest first - _promoteQueue.push({ texturePointer, 1.0f / (float)object->size() }); - } - break; - - case MemoryPressureState::Transfer: - if (object->hasPendingTransfers()) { - // Transfer priority given to smaller mips first - _transferQueue.push({ texturePointer, 1.0f / (float)object->_gpuObject.evalMipSize(object->_populatedMip) }); - } - break; - - case MemoryPressureState::Idle: - break; - - default: - Q_UNREACHABLE(); - } -} - -WorkQueue& GL45VariableAllocationTexture::getActiveWorkQueue() { - static WorkQueue empty; - switch (_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return _demoteQueue; - - case MemoryPressureState::Undersubscribed: - return _promoteQueue; - - case MemoryPressureState::Transfer: - return _transferQueue; - - default: - break; - } - Q_UNREACHABLE(); - return empty; -} - -// FIXME hack for stats display -QString getTextureMemoryPressureModeString() { - switch (GL45VariableAllocationTexture::_memoryPressureState) { - case MemoryPressureState::Oversubscribed: - return "Oversubscribed"; - - case MemoryPressureState::Undersubscribed: - return "Undersubscribed"; - - case MemoryPressureState::Transfer: - return "Transfer"; - - case MemoryPressureState::Idle: - return "Idle"; - } - Q_UNREACHABLE(); - return "Unknown"; -} - -void GL45VariableAllocationTexture::updateMemoryPressure() { - static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - - size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); - if (0 == allowedMemoryAllocation) { - allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; - } - - // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale - // so that we react - if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { - _memoryPressureStateStale = true; - lastAllowedMemoryAllocation = allowedMemoryAllocation; - } - - if (!_memoryPressureStateStale.exchange(false)) { - return; - } - - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - - // Clear any defunct textures (weak pointers that no longer have a valid texture) - _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { - return weakPointer.expired(); - }); - - // Convert weak pointers to strong. This new list may still contain nulls if a texture was - // deleted on another thread between the previous line and this one - std::vector strongTextures; { - strongTextures.reserve(_memoryManagedTextures.size()); - std::transform( - _memoryManagedTextures.begin(), _memoryManagedTextures.end(), - std::back_inserter(strongTextures), - [](const TextureWeakPointer& p) { return p.lock(); }); - } - - size_t totalVariableMemoryAllocation = 0; - size_t idealMemoryAllocation = 0; - bool canDemote = false; - bool canPromote = false; - bool hasTransfers = false; - for (const auto& texture : strongTextures) { - // Race conditions can still leave nulls in the list, so we need to check - if (!texture) { - continue; - } - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - // Track how much the texture thinks it should be using - idealMemoryAllocation += texture->evalTotalSize(); - // Track how much we're actually using - totalVariableMemoryAllocation += object->size(); - canDemote |= object->canDemote(); - canPromote |= object->canPromote(); - hasTransfers |= object->hasPendingTransfers(); - } - - size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; - float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; - - auto newState = MemoryPressureState::Idle; - if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { - newState = MemoryPressureState::Oversubscribed; - } else if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && unallocated != 0 && canPromote) { - newState = MemoryPressureState::Undersubscribed; - } else if (hasTransfers) { - newState = MemoryPressureState::Transfer; - } - - if (newState != _memoryPressureState) { -#if THREADED_TEXTURE_BUFFERING - if (MemoryPressureState::Transfer == _memoryPressureState) { - TransferJob::stopTransferLoop(); - } - _memoryPressureState = newState; - if (MemoryPressureState::Transfer == _memoryPressureState) { - TransferJob::startTransferLoop(); - } -#else - _memoryPressureState = newState; -#endif - // Clear the existing queue - _transferQueue = WorkQueue(); - _promoteQueue = WorkQueue(); - _demoteQueue = WorkQueue(); - - // Populate the existing textures into the queue - for (const auto& texture : strongTextures) { - addToWorkQueue(texture); - } - } -} - -void GL45VariableAllocationTexture::processWorkQueues() { - if (MemoryPressureState::Idle == _memoryPressureState) { - return; - } - - auto& workQueue = getActiveWorkQueue(); - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - while (!workQueue.empty()) { - auto workTarget = workQueue.top(); - workQueue.pop(); - auto texture = workTarget.first.lock(); - if (!texture) { - continue; - } - - // Grab the first item off the demote queue - GL45VariableAllocationTexture* object = Backend::getGPUObject(*texture); - if (MemoryPressureState::Oversubscribed == _memoryPressureState) { - if (!object->canDemote()) { - continue; - } - object->demote(); - } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { - if (!object->canPromote()) { - continue; - } - object->promote(); - } else if (MemoryPressureState::Transfer == _memoryPressureState) { - if (!object->hasPendingTransfers()) { - continue; - } - object->executeNextTransfer(texture); - } else { - Q_UNREACHABLE(); - } - - // Reinject into the queue if more work to be done - addToWorkQueue(texture); - break; - } - - if (workQueue.empty()) { - _memoryPressureStateStale = true; - } -} - -void GL45VariableAllocationTexture::manageMemory() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - updateMemoryPressure(); - processWorkQueues(); -} - -size_t GL45VariableAllocationTexture::_frameTexturesCreated { 0 }; GL45VariableAllocationTexture::GL45VariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture) { ++_frameTexturesCreated; } GL45VariableAllocationTexture::~GL45VariableAllocationTexture() { - _memoryPressureStateStale = true; Backend::updateTextureGPUMemoryUsage(_size, 0); } -void GL45VariableAllocationTexture::executeNextTransfer(const TexturePointer& currentTexture) { - if (_populatedMip <= _allocatedMip) { - return; - } - - if (_pendingTransfers.empty()) { - populateTransferQueue(); - } - - if (!_pendingTransfers.empty()) { - // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture - _currentTransferTexture = currentTexture; - if (_pendingTransfers.front()->tryTransfer()) { - _pendingTransfers.pop(); - _currentTransferTexture.reset(); - } - } -} - // Managed size resource textures using GL45ResourceTexture = GL45Backend::GL45ResourceTexture; @@ -453,7 +53,6 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend uint16_t allocatedMip = _populatedMip - std::min(_populatedMip, 2); allocateStorage(allocatedMip); - _memoryPressureStateStale = true; copyMipsFromTexture(); syncSampler(); @@ -521,7 +120,6 @@ void GL45ResourceTexture::promote() { glDeleteTextures(1, &oldId); // update the memory usage Backend::updateTextureGPUMemoryUsage(oldSize, 0); - _memoryPressureStateStale = true; syncSampler(); populateTransferQueue(); } @@ -554,7 +152,6 @@ void GL45ResourceTexture::demote() { glDeleteTextures(1, &oldId); // update the memory usage Backend::updateTextureGPUMemoryUsage(oldSize, 0); - _memoryPressureStateStale = true; syncSampler(); populateTransferQueue(); } diff --git a/libraries/gpu/src/gpu/Forward.h b/libraries/gpu/src/gpu/Forward.h index f28c18eb11..ce95e8657e 100644 --- a/libraries/gpu/src/gpu/Forward.h +++ b/libraries/gpu/src/gpu/Forward.h @@ -87,6 +87,7 @@ namespace gpu { class Sampler; class Texture; using TexturePointer = std::shared_ptr; + using TextureWeakPointer = std::weak_ptr; using Textures = std::vector; class TextureView; using TextureViews = std::vector; From 7e0efed062ad1e95ddc784496161cab37f632de9 Mon Sep 17 00:00:00 2001 From: volansystech Date: Tue, 18 Apr 2017 00:54:01 +0530 Subject: [PATCH 06/11] Improve speed of grabbing. --- .../system/controllers/handControllerGrab.js | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 307c7fba16..d49579ce3f 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -75,7 +75,6 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // -var shouldTravelAtRapidPace = false; var accelarationStartingPoint = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position @@ -2525,19 +2524,13 @@ function MyController(hand) { // compute the mass for the purpose of energy and how quickly to move object this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density); - this.shouldTravelAtRapidPace = false; var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); - if(distanceToObject >= 15){ // Apply Rapid Pace only if distanceToObject is far than 15. - this.shouldTravelAtRapidPace = true; - var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); - var distanceToHandFromAvatarHips = worldControllerPosition.x - MyAvatarHipsPosition.x; - var ACCELERATION_START_THRESHOLD = 0.90; - this.accelarationStartingPoint = (distanceToHandFromAvatarHips * ACCELERATION_START_THRESHOLD); - }else{ - this.shouldTravelAtRapidPace = false; - } - + + var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); + var distanceToHandFromAvatarHips = Vec3.length(Vec3.subtract(worldControllerPosition, MyAvatarHipsPosition)); + var ACCELERATION_START_THRESHOLD = 0.95; + this.accelarationStartingPoint = (distanceToHandFromAvatarHips * ACCELERATION_START_THRESHOLD); var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); this.actionID = NULL_UUID; @@ -2589,7 +2582,6 @@ function MyController(hand) { var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); - var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -2632,16 +2624,14 @@ function MyController(hand) { this.grabRadius * RADIAL_GRAB_AMPLIFIER); } - if(this.shouldTravelAtRapidPace = true){ - var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); - var distanceToHandFromAvatarHips_x = worldControllerPosition.x - MyAvatarHipsPosition.x; - if(distanceToHandFromAvatarHips_x <= this.accelarationStartingPoint){ - var RAPID_PACE_RATE = 2; - this.grabRadius = (this.grabRadius / RAPID_PACE_RATE); - } - } // don't let grabRadius go all the way to zero, because it can't come back from that - var MINIMUM_GRAB_RADIUS = 0.1; + var MINIMUM_GRAB_RADIUS = 0.3; + var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); + var distanceToHandFromAvatarHips = Vec3.length(Vec3.subtract(worldControllerPosition, MyAvatarHipsPosition)); + if(distanceToHandFromAvatarHips <= this.accelarationStartingPoint && this.grabRadius >= MINIMUM_GRAB_RADIUS){ + var RAPID_PACE_RATE = 2; + this.grabRadius = (this.grabRadius / RAPID_PACE_RATE); + } if (this.grabRadius < MINIMUM_GRAB_RADIUS) { this.grabRadius = MINIMUM_GRAB_RADIUS; } From e5c7587ad8b36f16f108098a5cadd2ae5eec04d4 Mon Sep 17 00:00:00 2001 From: volansystech Date: Tue, 18 Apr 2017 20:01:02 +0530 Subject: [PATCH 07/11] Improve far grabbing based on timescale. --- .../system/controllers/handControllerGrab.js | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index d49579ce3f..aa61081da9 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -75,7 +75,7 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // -var accelarationStartingPoint = 0; +var linearTimeScale = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified @@ -2525,14 +2525,8 @@ function MyController(hand) { // compute the mass for the purpose of energy and how quickly to move object this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position)); - - - var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); - var distanceToHandFromAvatarHips = Vec3.length(Vec3.subtract(worldControllerPosition, MyAvatarHipsPosition)); - var ACCELERATION_START_THRESHOLD = 0.95; - this.accelarationStartingPoint = (distanceToHandFromAvatarHips * ACCELERATION_START_THRESHOLD); var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject); - + this.linearTimeScale = timeScale; this.actionID = NULL_UUID; this.actionID = Entities.addAction("spring", this.grabbedThingID, { targetPosition: this.currentObjectPosition, @@ -2625,13 +2619,7 @@ function MyController(hand) { } // don't let grabRadius go all the way to zero, because it can't come back from that - var MINIMUM_GRAB_RADIUS = 0.3; - var MyAvatarHipsPosition = MyAvatar.getJointPosition("Hips"); - var distanceToHandFromAvatarHips = Vec3.length(Vec3.subtract(worldControllerPosition, MyAvatarHipsPosition)); - if(distanceToHandFromAvatarHips <= this.accelarationStartingPoint && this.grabRadius >= MINIMUM_GRAB_RADIUS){ - var RAPID_PACE_RATE = 2; - this.grabRadius = (this.grabRadius / RAPID_PACE_RATE); - } + var MINIMUM_GRAB_RADIUS = 0.1; if (this.grabRadius < MINIMUM_GRAB_RADIUS) { this.grabRadius = MINIMUM_GRAB_RADIUS; } @@ -2670,9 +2658,13 @@ function MyController(hand) { this.grabbedThingID); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); + this.linearTimeScale = (this.linearTimeScale / 2); + if(this.linearTimeScale <= DISTANCE_HOLDING_ACTION_TIMEFRAME){ + this.linearTimeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; + } var success = Entities.updateAction(this.grabbedThingID, this.actionID, { targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), + linearTimeScale: this.linearTimeScale, targetRotation: this.currentObjectRotation, angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), ttl: ACTION_TTL From 2a2102c7fe3c1239acf651121163966fa7e504d1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 18 Apr 2017 15:12:34 -0700 Subject: [PATCH 08/11] Recompute memory pressure state after each promote / demote call --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 199513fc0a..22743f106b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -506,11 +506,13 @@ void GLVariableAllocationSupport::processWorkQueues() { continue; } vartexture->demote(); + _memoryPressureStateStale = true; } else if (MemoryPressureState::Undersubscribed == _memoryPressureState) { if (!vartexture->canPromote()) { continue; } vartexture->promote(); + _memoryPressureStateStale = true; } else if (MemoryPressureState::Transfer == _memoryPressureState) { if (!vartexture->hasPendingTransfers()) { continue; From b194ddeb66d4552270e3894710cfc9166fbae2a4 Mon Sep 17 00:00:00 2001 From: volansystech Date: Wed, 19 Apr 2017 16:39:23 +0530 Subject: [PATCH 09/11] Resolve Comments of @sethalves --- scripts/system/controllers/handControllerGrab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index aa61081da9..5af1f5d5c3 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -2659,7 +2659,7 @@ function MyController(hand) { var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); this.linearTimeScale = (this.linearTimeScale / 2); - if(this.linearTimeScale <= DISTANCE_HOLDING_ACTION_TIMEFRAME){ + if (this.linearTimeScale <= DISTANCE_HOLDING_ACTION_TIMEFRAME) { this.linearTimeScale = DISTANCE_HOLDING_ACTION_TIMEFRAME; } var success = Entities.updateAction(this.grabbedThingID, this.actionID, { From bccc06d2fe6f228e44b09934992279f4189c3122 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 19 Apr 2017 10:35:19 -0700 Subject: [PATCH 10/11] Fix resource texture memory usage tracking --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 1cb5a42ab9..1e02b3a385 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -231,7 +231,7 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr } GL41VariableAllocationTexture::~GL41VariableAllocationTexture() { - Backend::updateTextureGPUMemoryUsage(0, _size); + Backend::updateTextureGPUMemoryUsage(_size, 0); } void GL41VariableAllocationTexture::allocateStorage(uint16 allocatedMip) { From 472835d65c627285331f7639a2b1e172382afd27 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 19 Apr 2017 11:22:20 -0700 Subject: [PATCH 11/11] Port fix from master --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 22743f106b..19891d3370 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -478,6 +478,10 @@ void GLVariableAllocationSupport::updateMemoryPressure() { // Populate the existing textures into the queue for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } addToWorkQueue(texture); } }