mirror of
https://github.com/overte-org/overte.git
synced 2025-04-07 20:14:22 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into bullet-constraints-1
This commit is contained in:
commit
f7378abb08
52 changed files with 1096 additions and 911 deletions
|
@ -1124,19 +1124,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
return qApp->isHMDMode() ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float {
|
||||
return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0;
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_MIRROR ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float {
|
||||
return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float {
|
||||
return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float {
|
||||
return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
|
||||
return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
|
||||
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
|
||||
|
@ -1436,6 +1436,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<TextureCache>();
|
||||
|
@ -1785,14 +1786,23 @@ void Application::cleanupBeforeQuit() {
|
|||
DependencyManager::destroy<AudioClient>();
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
||||
// shutdown render engine
|
||||
_main3DScene = nullptr;
|
||||
_renderEngine = nullptr;
|
||||
|
||||
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
DependencyManager::get<AvatarManager>()->deleteAllAvatars();
|
||||
|
||||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// shutdown render engine
|
||||
_main3DScene = nullptr;
|
||||
_renderEngine = nullptr;
|
||||
|
||||
DependencyManager::destroy<Preferences>();
|
||||
|
||||
_entityClipboard->eraseAllOctreeElements();
|
||||
|
@ -1804,14 +1814,6 @@ Application::~Application() {
|
|||
_octreeProcessor.terminate();
|
||||
_entityEditSender.terminate();
|
||||
|
||||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
DependencyManager::get<AvatarManager>()->deleteAllAvatars();
|
||||
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
DependencyManager::destroy<AnimationCache>();
|
||||
|
@ -2083,7 +2085,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;
|
||||
}
|
||||
|
||||
|
@ -2440,7 +2442,7 @@ void Application::resizeGL() {
|
|||
// Possible change in aspect ratio
|
||||
{
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
_myCamera.loadViewFrustum(_viewFrustum);
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
@ -4525,7 +4527,7 @@ void Application::update(float deltaTime) {
|
|||
// to the server.
|
||||
{
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
loadViewFrustum(_myCamera, _viewFrustum);
|
||||
_myCamera.loadViewFrustum(_viewFrustum);
|
||||
}
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
|
@ -4853,24 +4855,6 @@ QRect Application::getDesirableApplicationGeometry() const {
|
|||
return applicationGeometry;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// loadViewFrustum()
|
||||
//
|
||||
// Description: this will load the view frustum bounds for EITHER the head
|
||||
// or the "myCamera".
|
||||
//
|
||||
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
|
||||
// We will use these below, from either the camera or head vectors calculated above
|
||||
viewFrustum.setProjection(camera.getProjection());
|
||||
|
||||
// Set the viewFrustum up with the correct position and orientation of the camera
|
||||
viewFrustum.setPosition(camera.getPosition());
|
||||
viewFrustum.setOrientation(camera.getOrientation());
|
||||
|
||||
// Ask the ViewFrustum class to calculate our corners
|
||||
viewFrustum.calculate();
|
||||
}
|
||||
|
||||
glm::vec3 Application::getSunDirection() const {
|
||||
// Sun direction is in fact just the location of the sun relative to the origin
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
|
@ -5042,7 +5026,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
// load the view frustum
|
||||
{
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
loadViewFrustum(theCamera, _displayViewFrustum);
|
||||
theCamera.loadViewFrustum(_displayViewFrustum);
|
||||
}
|
||||
|
||||
// TODO fix shadows and make them use the GPU library
|
||||
|
@ -5059,7 +5043,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
transaction.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
|
||||
}
|
||||
|
||||
// Assuming nothing get's rendered through that
|
||||
// Assuming nothing gets rendered through that
|
||||
if (!selfAvatarOnly) {
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||
// render models...
|
||||
|
@ -5115,6 +5099,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
QMutexLocker viewLocker(&_viewMutex);
|
||||
renderArgs->setViewFrustum(_displayViewFrustum);
|
||||
}
|
||||
renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK
|
||||
renderArgs->_scene = getMain3DScene();
|
||||
_renderEngine->getRenderContext()->args = renderArgs;
|
||||
|
||||
// Before the deferred pass, let's try to use the render engine
|
||||
|
@ -6501,6 +6487,14 @@ 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<void()> f) {
|
||||
if (this->thread() == QThread::currentThread()) {
|
||||
f();
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
#include "avatar/MyAvatar.h"
|
||||
#include "BandwidthRecorder.h"
|
||||
#include "Camera.h"
|
||||
#include "FancyCamera.h"
|
||||
#include "ConnectionMonitor.h"
|
||||
#include "gpu/Context.h"
|
||||
#include "Menu.h"
|
||||
|
@ -174,8 +174,8 @@ public:
|
|||
|
||||
bool isThrottleRendering() const;
|
||||
|
||||
Camera* getCamera() { return &_myCamera; }
|
||||
const Camera* getCamera() const { return &_myCamera; }
|
||||
Camera& getCamera() { return _myCamera; }
|
||||
const Camera& getCamera() const { return _myCamera; }
|
||||
// Represents the current view frustum of the avatar.
|
||||
void copyViewFrustum(ViewFrustum& viewOut) const;
|
||||
// Represents the view frustum of the current rendering pass,
|
||||
|
@ -268,7 +268,7 @@ public:
|
|||
int getMaxOctreePacketsPerSecond() const;
|
||||
|
||||
render::ScenePointer getMain3DScene() override { return _main3DScene; }
|
||||
render::ScenePointer getMain3DScene() const { return _main3DScene; }
|
||||
const render::ScenePointer& getMain3DScene() const { return _main3DScene; }
|
||||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||
|
||||
|
@ -278,7 +278,7 @@ public:
|
|||
|
||||
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
|
||||
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
|
||||
|
||||
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
|
||||
|
||||
|
@ -415,6 +415,7 @@ private slots:
|
|||
void faceTrackerMuteToggled();
|
||||
|
||||
void activeChanged(Qt::ApplicationState state);
|
||||
void windowMinimizedChanged(bool minimized);
|
||||
|
||||
void notifyPacketVersionMismatch();
|
||||
|
||||
|
@ -462,7 +463,6 @@ private:
|
|||
void updateDialogs(float deltaTime) const;
|
||||
|
||||
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false);
|
||||
static void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
|
||||
|
||||
glm::vec3 getSunDirection() const;
|
||||
|
||||
|
@ -558,7 +558,7 @@ private:
|
|||
SimpleMovingAverage _avatarSimsPerSecond {10};
|
||||
int _avatarSimsPerSecondReport {0};
|
||||
quint64 _lastAvatarSimsPerSecondUpdate {0};
|
||||
Camera _myCamera; // My view onto the world
|
||||
FancyCamera _myCamera; // My view onto the world
|
||||
|
||||
Setting::Handle<QString> _previousScriptLocation;
|
||||
Setting::Handle<float> _fieldOfView;
|
||||
|
|
|
@ -98,18 +98,7 @@ void Camera::setMode(CameraMode mode) {
|
|||
emit modeUpdated(modeToString(mode));
|
||||
}
|
||||
|
||||
QUuid Camera::getCameraEntity() const {
|
||||
if (_cameraEntity != nullptr) {
|
||||
return _cameraEntity->getID();
|
||||
}
|
||||
return QUuid();
|
||||
};
|
||||
|
||||
void Camera::setCameraEntity(QUuid entityID) {
|
||||
_cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID);
|
||||
}
|
||||
|
||||
void Camera::setProjection(const glm::mat4& projection) {
|
||||
void Camera::setProjection(const glm::mat4& projection) {
|
||||
_projection = projection;
|
||||
}
|
||||
|
||||
|
@ -119,7 +108,7 @@ PickRay Camera::computePickRay(float x, float y) {
|
|||
|
||||
void Camera::setModeString(const QString& mode) {
|
||||
CameraMode targetMode = stringToMode(mode);
|
||||
|
||||
|
||||
switch (targetMode) {
|
||||
case CAMERA_MODE_FIRST_PERSON:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
|
@ -139,7 +128,7 @@ void Camera::setModeString(const QString& mode) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
qApp->cameraMenuChanged();
|
||||
|
||||
if (_mode != targetMode) {
|
||||
|
@ -177,12 +166,6 @@ void Camera::loadViewFrustum(ViewFrustum& frustum) const {
|
|||
frustum.calculate();
|
||||
}
|
||||
|
||||
ViewFrustum Camera::toViewFrustum() const {
|
||||
ViewFrustum result;
|
||||
loadViewFrustum(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap Camera::getViewFrustum() {
|
||||
ViewFrustum frustum;
|
||||
loadViewFrustum(frustum);
|
||||
|
|
|
@ -43,15 +43,11 @@ class Camera : public QObject {
|
|||
* @property position {Vec3} The position of the camera.
|
||||
* @property orientation {Quat} The orientation of the camera.
|
||||
* @property mode {string} The current camera mode.
|
||||
* @property cameraEntity {EntityID} The position and rotation properties of
|
||||
* the entity specified by this ID are then used as the camera's position and
|
||||
* orientation. Only works when <code>mode</code> is "entity".
|
||||
* @property frustum {Object} The frustum of the camera.
|
||||
*/
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
||||
Q_PROPERTY(QString mode READ getModeString WRITE setModeString)
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT)
|
||||
|
||||
public:
|
||||
|
@ -65,9 +61,6 @@ public:
|
|||
void setMode(CameraMode m);
|
||||
|
||||
void loadViewFrustum(ViewFrustum& frustum) const;
|
||||
ViewFrustum toViewFrustum() const;
|
||||
|
||||
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
|
||||
|
||||
const glm::mat4& getTransform() const { return _transform; }
|
||||
void setTransform(const glm::mat4& transform);
|
||||
|
@ -87,9 +80,6 @@ public slots:
|
|||
glm::quat getOrientation() const { return _orientation; }
|
||||
void setOrientation(const glm::quat& orientation);
|
||||
|
||||
QUuid getCameraEntity() const;
|
||||
void setCameraEntity(QUuid entityID);
|
||||
|
||||
/**jsdoc
|
||||
* Compute a {PickRay} based on the current camera configuration and the position x,y on the screen.
|
||||
* @function Camera.computePickRay
|
||||
|
@ -143,7 +133,6 @@ private:
|
|||
glm::quat _orientation;
|
||||
bool _isKeepLookingAt{ false };
|
||||
glm::vec3 _lookingAt;
|
||||
EntityItemPointer _cameraEntity;
|
||||
};
|
||||
|
||||
#endif // hifi_Camera_h
|
||||
|
|
25
interface/src/FancyCamera.cpp
Normal file
25
interface/src/FancyCamera.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// FancyCamera.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "FancyCamera.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
||||
QUuid FancyCamera::getCameraEntity() const {
|
||||
if (_cameraEntity != nullptr) {
|
||||
return _cameraEntity->getID();
|
||||
}
|
||||
return QUuid();
|
||||
};
|
||||
|
||||
void FancyCamera::setCameraEntity(QUuid entityID) {
|
||||
_cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID);
|
||||
}
|
43
interface/src/FancyCamera.h
Normal file
43
interface/src/FancyCamera.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// FancyCamera.h
|
||||
// interface/src
|
||||
//
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_FancyCamera_h
|
||||
#define hifi_FancyCamera_h
|
||||
|
||||
#include "Camera.h"
|
||||
|
||||
#include <EntityItem.h>
|
||||
|
||||
// TODO: come up with a better name than "FancyCamera"
|
||||
class FancyCamera : public Camera {
|
||||
Q_OBJECT
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Camera
|
||||
* @property cameraEntity {EntityID} The position and rotation properties of
|
||||
* the entity specified by this ID are then used as the camera's position and
|
||||
* orientation. Only works when <code>mode</code> is "entity".
|
||||
*/
|
||||
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||
|
||||
public:
|
||||
FancyCamera() : Camera() {}
|
||||
|
||||
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
|
||||
|
||||
public slots:
|
||||
QUuid getCameraEntity() const;
|
||||
void setCameraEntity(QUuid entityID);
|
||||
|
||||
private:
|
||||
EntityItemPointer _cameraEntity;
|
||||
};
|
||||
|
||||
#endif // hifi_FancyCamera_h
|
|
@ -1,44 +0,0 @@
|
|||
//
|
||||
// Physics.cpp
|
||||
// interface/src
|
||||
//
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "Physics.h"
|
||||
|
||||
//
|
||||
// Applies static friction: maxVelocity is the largest velocity for which there
|
||||
// there is friction, and strength is the amount of friction force applied to reduce
|
||||
// velocity.
|
||||
//
|
||||
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength) {
|
||||
float v = glm::length(velocity);
|
||||
if (v < maxVelocity) {
|
||||
velocity *= glm::clamp((1.0f - deltaTime * strength * (1.0f - v / maxVelocity)), 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Applies velocity damping, with a strength value for linear and squared velocity damping
|
||||
//
|
||||
|
||||
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength) {
|
||||
if (squaredStrength == 0.0f) {
|
||||
velocity *= glm::clamp(1.0f - deltaTime * linearStrength, 0.0f, 1.0f);
|
||||
} else {
|
||||
velocity *= glm::clamp(1.0f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void applyDampedSpring(float deltaTime, glm::vec3& velocity, glm::vec3& position, glm::vec3& targetPosition, float k, float damping) {
|
||||
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
//
|
||||
// Physics.h
|
||||
// interface/src
|
||||
//
|
||||
// Created by Philip on 4/25/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Physics_h
|
||||
#define hifi_Physics_h
|
||||
|
||||
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength);
|
||||
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength);
|
||||
|
||||
#endif // hifi_Physics_h
|
|
@ -9,44 +9,32 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
#include "Avatar.h"
|
||||
|
||||
#include <QDesktopWidget>
|
||||
#include <QWindow>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <QtCore/QThread>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/vector_query.hpp>
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <LODManager.h>
|
||||
#include <EntityTreeRenderer.h>
|
||||
#include <NodeList.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <OctreeUtils.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <PerfStat.h>
|
||||
#include <Rig.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextRenderer3D.h>
|
||||
#include <TextureCache.h>
|
||||
#include <VariantMapToScriptValue.h>
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
#include "AvatarManager.h"
|
||||
#include "AvatarMotionState.h"
|
||||
#include "Head.h"
|
||||
#include "Camera.h"
|
||||
#include "Menu.h"
|
||||
#include "Physics.h"
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
#include "SoftAttachmentModel.h"
|
||||
#include <Rig.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -71,7 +59,8 @@ namespace render {
|
|||
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
|
||||
if (avatarPtr->isInitialized() && args) {
|
||||
PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
|
||||
avatarPtr->render(args, qApp->getCamera()->getPosition());
|
||||
// TODO AVATARS_RENDERER: remove need for qApp
|
||||
avatarPtr->render(args);
|
||||
}
|
||||
}
|
||||
template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) {
|
||||
|
@ -85,7 +74,7 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
Avatar::Avatar(RigPointer rig) :
|
||||
Avatar::Avatar(QThread* thread, RigPointer rig) :
|
||||
AvatarData(),
|
||||
_skeletonOffset(0.0f),
|
||||
_bodyYawDelta(0.0f),
|
||||
|
@ -108,7 +97,7 @@ Avatar::Avatar(RigPointer rig) :
|
|||
_voiceSphereID(GeometryCache::UNKNOWN_ID)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(qApp->thread());
|
||||
moveToThread(thread);
|
||||
|
||||
setScale(glm::vec3(1.0f)); // avatar scale is uniform
|
||||
|
||||
|
@ -128,7 +117,7 @@ Avatar::Avatar(RigPointer rig) :
|
|||
Avatar::~Avatar() {
|
||||
assert(isDead()); // mark dead before calling the dtor
|
||||
|
||||
auto treeRenderer = qApp->getEntities();
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
if (entityTree) {
|
||||
entityTree->withWriteLock([&] {
|
||||
|
@ -237,7 +226,7 @@ void Avatar::updateAvatarEntities() {
|
|||
return; // wait until MyAvatar gets an ID before doing this.
|
||||
}
|
||||
|
||||
auto treeRenderer = qApp->getEntities();
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
if (!entityTree) {
|
||||
return;
|
||||
|
@ -518,7 +507,7 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
|
|||
return displayNameRenderer;
|
||||
}
|
||||
|
||||
void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
auto avatarPayload = new render::Payload<AvatarData>(self);
|
||||
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
||||
_renderItemID = scene->allocateID();
|
||||
|
@ -530,7 +519,7 @@ void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
transaction.removeItem(_renderItemID);
|
||||
render::Item::clearID(_renderItemID);
|
||||
_skeletonModel->removeFromScene(scene, transaction);
|
||||
|
@ -578,11 +567,13 @@ void Avatar::postUpdate(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||
auto& batch = *renderArgs->_batch;
|
||||
void Avatar::render(RenderArgs* renderArgs) {
|
||||
auto& batch = *(renderArgs->_batch);
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
|
||||
|
||||
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) {
|
||||
glm::vec3 viewPos = renderArgs->getViewFrustum().getPosition();
|
||||
const float MAX_DISTANCE_SQUARED_FOR_SHOWING_POINTING_LASERS = 100.0f; // 10^2
|
||||
if (glm::distance2(viewPos, getPosition()) < MAX_DISTANCE_SQUARED_FOR_SHOWING_POINTING_LASERS) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
// render pointing lasers
|
||||
|
@ -641,23 +632,16 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
}
|
||||
|
||||
{ // simple frustum check
|
||||
ViewFrustum frustum;
|
||||
if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
|
||||
qApp->copyShadowViewFrustum(frustum);
|
||||
} else {
|
||||
qApp->copyDisplayViewFrustum(frustum);
|
||||
}
|
||||
if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) {
|
||||
return;
|
||||
}
|
||||
ViewFrustum frustum = renderArgs->getViewFrustum();
|
||||
if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 toTarget = cameraPosition - getPosition();
|
||||
glm::vec3 toTarget = frustum.getPosition() - getPosition();
|
||||
float distanceToTarget = glm::length(toTarget);
|
||||
|
||||
{
|
||||
fixupModelsInScene();
|
||||
fixupModelsInScene(renderArgs->_scene);
|
||||
|
||||
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
|
||||
// add local lights
|
||||
|
@ -686,8 +670,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
const float DISPLAYNAME_DISTANCE = 20.0f;
|
||||
setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE);
|
||||
|
||||
auto cameraMode = qApp->getCamera()->getMode();
|
||||
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
|
||||
if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) {
|
||||
auto& frustum = renderArgs->getViewFrustum();
|
||||
auto textPosition = getDisplayNamePosition();
|
||||
if (frustum.pointIntersectsFrustum(textPosition)) {
|
||||
|
@ -712,12 +695,11 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
|
|||
return glm::angleAxis(angle * proportion, axis);
|
||||
}
|
||||
|
||||
void Avatar::fixupModelsInScene() {
|
||||
void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
||||
_attachmentsToDelete.clear();
|
||||
|
||||
// check to see if when we added our models to the scene they were ready, if they were not ready, then
|
||||
// fix them up in the scene
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
|
||||
_skeletonModel->removeFromScene(scene, transaction);
|
||||
|
@ -1490,8 +1472,7 @@ QList<QVariant> Avatar::getSkeleton() {
|
|||
return QList<QVariant>();
|
||||
}
|
||||
|
||||
void Avatar::addToScene(AvatarSharedPointer myHandle) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) {
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
auto nodelist = DependencyManager::get<NodeList>();
|
||||
|
@ -1505,8 +1486,9 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) {
|
|||
qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown";
|
||||
}
|
||||
}
|
||||
void Avatar::ensureInScene(AvatarSharedPointer self) {
|
||||
|
||||
void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene) {
|
||||
if (!render::Item::isValidID(_renderItemID)) {
|
||||
addToScene(self);
|
||||
addToScene(self, scene);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,17 +14,15 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <QtCore/QScopedPointer>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <AvatarData.h>
|
||||
#include <ShapeInfo.h>
|
||||
|
||||
#include <render/Scene.h>
|
||||
|
||||
|
||||
#include "Head.h"
|
||||
#include "SkeletonModel.h"
|
||||
#include "world.h"
|
||||
#include "Rig.h"
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
|
@ -68,7 +66,7 @@ class Avatar : public AvatarData {
|
|||
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
|
||||
|
||||
public:
|
||||
explicit Avatar(RigPointer rig = nullptr);
|
||||
explicit Avatar(QThread* thread, RigPointer rig = nullptr);
|
||||
~Avatar();
|
||||
|
||||
typedef render::Payload<AvatarData> Payload;
|
||||
|
@ -79,12 +77,12 @@ public:
|
|||
void simulate(float deltaTime, bool inView);
|
||||
virtual void simulateAttachments(float deltaTime);
|
||||
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||
virtual void render(RenderArgs* renderArgs);
|
||||
|
||||
void addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
|
||||
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction);
|
||||
|
||||
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
|
||||
void removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction);
|
||||
|
||||
void updateRenderItem(render::Transaction& transaction);
|
||||
|
@ -305,7 +303,7 @@ protected:
|
|||
Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const;
|
||||
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const;
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
|
||||
virtual void fixupModelsInScene();
|
||||
virtual void fixupModelsInScene(const render::ScenePointer& scene);
|
||||
|
||||
virtual void updatePalms();
|
||||
|
||||
|
@ -316,8 +314,8 @@ protected:
|
|||
ThreadSafeValueCache<glm::vec3> _rightPalmPositionCache { glm::vec3() };
|
||||
ThreadSafeValueCache<glm::quat> _rightPalmRotationCache { glm::quat() };
|
||||
|
||||
void addToScene(AvatarSharedPointer self);
|
||||
void ensureInScene(AvatarSharedPointer self);
|
||||
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
|
||||
|
||||
// Some rate tracking support
|
||||
|
|
|
@ -69,7 +69,7 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
|
|||
|
||||
AvatarManager::AvatarManager(QObject* parent) :
|
||||
_avatarsToFade(),
|
||||
_myAvatar(std::make_shared<MyAvatar>(std::make_shared<Rig>()))
|
||||
_myAvatar(std::make_shared<MyAvatar>(qApp->thread(), std::make_shared<Rig>()))
|
||||
{
|
||||
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
|
||||
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
|
||||
|
@ -105,7 +105,7 @@ void AvatarManager::init() {
|
|||
this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection);
|
||||
|
||||
if (_shouldRender) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
_myAvatar->addToScene(_myAvatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
@ -198,7 +198,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
// for ALL avatars...
|
||||
if (_shouldRender) {
|
||||
avatar->ensureInScene(avatar);
|
||||
avatar->ensureInScene(avatar, qApp->getMain3DScene());
|
||||
}
|
||||
if (!avatar->getMotionState()) {
|
||||
ShapeInfo shapeInfo;
|
||||
|
@ -306,12 +306,10 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
// fading to zero is such a rare event we push unique transaction for each one
|
||||
if (avatar->isInScene()) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(*itr, scene, transaction);
|
||||
if (scene) {
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
// only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine
|
||||
|
@ -329,7 +327,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::newSharedAvatar() {
|
||||
return std::make_shared<Avatar>(std::make_shared<Rig>());
|
||||
return std::make_shared<Avatar>(qApp->thread(), std::make_shared<Rig>());
|
||||
}
|
||||
|
||||
void AvatarManager::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
|
@ -343,19 +341,19 @@ void AvatarManager::processAvatarDataPacket(QSharedPointer<ReceivedMessage> mess
|
|||
if (avatar->isInScene()) {
|
||||
if (!_shouldRender) {
|
||||
// rare transition so we process the transaction immediately
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
} else if (_shouldRender) {
|
||||
// very rare transition so we process the transaction immediately
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
avatar->addToScene(avatar, scene, transaction);
|
||||
if (scene) {
|
||||
render::Transaction transaction;
|
||||
avatar->addToScene(avatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
@ -394,7 +392,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
void AvatarManager::clearOtherAvatars() {
|
||||
// Remove other avatars from the world but don't actually remove them from _avatarHash
|
||||
// each will either be removed on timeout or will re-added to the world on receipt of update.
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
|
||||
QReadLocker locker(&_hashLock);
|
||||
|
@ -414,9 +412,7 @@ void AvatarManager::clearOtherAvatars() {
|
|||
}
|
||||
++avatarIterator;
|
||||
}
|
||||
if (scene) {
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
_myAvatar->clearLookAtTargetAvatar();
|
||||
}
|
||||
|
||||
|
@ -522,7 +518,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
|||
|
||||
void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
|
||||
_shouldRender = shouldRenderAvatars;
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
const render::ScenePointer& scene = qApp->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
if (_shouldRender) {
|
||||
for (auto avatarData : _avatarHash) {
|
||||
|
@ -535,9 +531,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
|
|||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
}
|
||||
}
|
||||
if (scene) {
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <shared/RateCounter.h>
|
||||
|
||||
#include "Avatar.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "AvatarMotionState.h"
|
||||
#include "ScriptAvatar.h"
|
||||
|
||||
|
@ -42,6 +43,7 @@ public:
|
|||
void init();
|
||||
|
||||
std::shared_ptr<MyAvatar> getMyAvatar() { return _myAvatar; }
|
||||
glm::vec3 getMyAvatarPosition() const { return _myAvatar->getPosition(); }
|
||||
|
||||
// Null/Default-constructed QUuids will return MyAvatar
|
||||
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); }
|
||||
|
@ -110,7 +112,7 @@ private:
|
|||
|
||||
QVector<AvatarSharedPointer> _avatarsToFade;
|
||||
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
QSet<AvatarMotionState*> _motionStatesThatMightUpdate;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
|
||||
|
|
|
@ -83,6 +83,4 @@ protected:
|
|||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
||||
typedef QSet<AvatarMotionState*> SetOfAvatarMotionStates;
|
||||
|
||||
#endif // hifi_AvatarMotionState_h
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
#include "AvatarActionHold.h"
|
||||
#include "Menu.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "Physics.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "DebugDraw.h"
|
||||
|
@ -83,8 +82,8 @@ const float MyAvatar::ZOOM_MIN = 0.5f;
|
|||
const float MyAvatar::ZOOM_MAX = 25.0f;
|
||||
const float MyAvatar::ZOOM_DEFAULT = 1.5f;
|
||||
|
||||
MyAvatar::MyAvatar(RigPointer rig) :
|
||||
Avatar(rig),
|
||||
MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
|
||||
Avatar(thread, rig),
|
||||
_wasPushing(false),
|
||||
_isPushing(false),
|
||||
_isBeingPushed(false),
|
||||
|
@ -260,7 +259,7 @@ void MyAvatar::simulateAttachments(float deltaTime) {
|
|||
}
|
||||
|
||||
QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) {
|
||||
CameraMode mode = qApp->getCamera()->getMode();
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
_globalPosition = getPosition();
|
||||
_globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();
|
||||
_globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight();
|
||||
|
@ -752,13 +751,12 @@ controller::Pose MyAvatar::getRightHandTipPose() const {
|
|||
}
|
||||
|
||||
// virtual
|
||||
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||
void MyAvatar::render(RenderArgs* renderArgs) {
|
||||
// don't render if we've been asked to disable local rendering
|
||||
if (!_shouldRender) {
|
||||
return; // exit early
|
||||
}
|
||||
|
||||
Avatar::render(renderArgs, cameraPosition);
|
||||
Avatar::render(renderArgs);
|
||||
}
|
||||
|
||||
void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
|
@ -927,8 +925,7 @@ void MyAvatar::setEnableDebugDrawIKTargets(bool isEnabled) {
|
|||
}
|
||||
|
||||
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
_skeletonModel->setVisibleInScene(isEnabled, scene);
|
||||
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene());
|
||||
}
|
||||
|
||||
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
|
||||
|
@ -1079,7 +1076,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
_targetAvatarPosition = glm::vec3(0.0f);
|
||||
|
||||
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD;
|
||||
glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||
glm::vec3 cameraPosition = qApp->getCamera().getPosition();
|
||||
|
||||
float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f;
|
||||
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
|
||||
|
@ -1225,8 +1222,7 @@ void MyAvatar::clearJointsData() {
|
|||
|
||||
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||
Avatar::setSkeletonModelURL(skeletonModelURL);
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
_skeletonModel->setVisibleInScene(true, scene);
|
||||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene());
|
||||
_headBoneSet.clear();
|
||||
}
|
||||
|
||||
|
@ -1275,7 +1271,7 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
|
|||
}
|
||||
|
||||
glm::vec3 MyAvatar::getSkeletonPosition() const {
|
||||
CameraMode mode = qApp->getCamera()->getMode();
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||
// to get the skeleton offset contribution in the world-frame.
|
||||
|
@ -1540,7 +1536,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
|
|||
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
|
||||
}
|
||||
|
||||
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
|
||||
void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) {
|
||||
if (model->isActive() && model->isRenderable()) {
|
||||
model->setVisibleInScene(visible, scene);
|
||||
}
|
||||
|
@ -1634,8 +1630,7 @@ void MyAvatar::postUpdate(float deltaTime) {
|
|||
|
||||
Avatar::postUpdate(deltaTime);
|
||||
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && _skeletonModel->initWhenReady(scene)) {
|
||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && _skeletonModel->initWhenReady(qApp->getMain3DScene())) {
|
||||
initHeadBones();
|
||||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||
|
@ -1704,13 +1699,13 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
|||
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
|
||||
|
||||
bool MyAvatar::cameraInsideHead() const {
|
||||
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||
const glm::vec3 cameraPosition = qApp->getCamera().getPosition();
|
||||
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale());
|
||||
}
|
||||
|
||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
|
||||
bool firstPerson = qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON;
|
||||
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
|
||||
bool insideHead = cameraInsideHead();
|
||||
return !defaultMode || !firstPerson || !insideHead;
|
||||
}
|
||||
|
@ -2265,7 +2260,7 @@ glm::vec3 MyAvatar::getPositionForAudio() {
|
|||
case AudioListenerMode::FROM_HEAD:
|
||||
return getHead()->getPosition();
|
||||
case AudioListenerMode::FROM_CAMERA:
|
||||
return qApp->getCamera()->getPosition();
|
||||
return qApp->getCamera().getPosition();
|
||||
case AudioListenerMode::CUSTOM:
|
||||
return _customListenPosition;
|
||||
}
|
||||
|
@ -2277,7 +2272,7 @@ glm::quat MyAvatar::getOrientationForAudio() {
|
|||
case AudioListenerMode::FROM_HEAD:
|
||||
return getHead()->getFinalOrientationInWorldFrame();
|
||||
case AudioListenerMode::FROM_CAMERA:
|
||||
return qApp->getCamera()->getOrientation();
|
||||
return qApp->getCamera().getOrientation();
|
||||
case AudioListenerMode::CUSTOM:
|
||||
return _customListenOrientation;
|
||||
}
|
||||
|
@ -2367,7 +2362,7 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
|
|||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
auto cameraMode = qApp->getCamera()->getMode();
|
||||
auto cameraMode = qApp->getCamera().getMode();
|
||||
if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -2515,8 +2510,8 @@ void MyAvatar::setAway(bool value) {
|
|||
glm::mat4 MyAvatar::computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const {
|
||||
|
||||
// Fetch the current camera transform.
|
||||
glm::mat4 cameraWorldMatrix = qApp->getCamera()->getTransform();
|
||||
if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
|
||||
glm::mat4 cameraWorldMatrix = qApp->getCamera().getTransform();
|
||||
if (qApp->getCamera().getMode() == CAMERA_MODE_MIRROR) {
|
||||
cameraWorldMatrix *= createMatFromScaleQuatAndPos(vec3(-1.0f, 1.0f, 1.0f), glm::quat(), glm::vec3());
|
||||
}
|
||||
|
||||
|
@ -2561,7 +2556,7 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
|||
Transform avatarTransform;
|
||||
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
|
||||
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
|
||||
return glmExtractRotation(invAvatarMat * qApp->getCamera()->getTransform());
|
||||
return glmExtractRotation(invAvatarMat * qApp->getCamera().getTransform());
|
||||
}
|
||||
default: {
|
||||
return Avatar::getAbsoluteJointRotationInObjectFrame(index);
|
||||
|
@ -2598,7 +2593,7 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
|||
Transform avatarTransform;
|
||||
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
|
||||
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
|
||||
return extractTranslation(invAvatarMat * qApp->getCamera()->getTransform());
|
||||
return extractTranslation(invAvatarMat * qApp->getCamera().getTransform());
|
||||
}
|
||||
default: {
|
||||
return Avatar::getAbsoluteJointTranslationInObjectFrame(index);
|
||||
|
|
|
@ -146,7 +146,7 @@ public:
|
|||
};
|
||||
Q_ENUM(DriveKeys)
|
||||
|
||||
explicit MyAvatar(RigPointer rig);
|
||||
explicit MyAvatar(QThread* thread, RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
||||
void registerMetaTypes(QScriptEngine* engine);
|
||||
|
@ -525,7 +525,7 @@ private:
|
|||
|
||||
void simulate(float deltaTime);
|
||||
void updateFromTrackers(float deltaTime);
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
|
||||
virtual void render(RenderArgs* renderArgs) override;
|
||||
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
|
||||
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
|
||||
bool getShouldRenderLocally() const { return _shouldRender; }
|
||||
|
@ -551,7 +551,7 @@ private:
|
|||
// These are made private for MyAvatar so that you will use the "use" methods instead
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
||||
void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
|
||||
void setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visiblity);
|
||||
|
||||
// derive avatar body position and orientation from the current HMD Sensor location.
|
||||
// results are in HMD frame
|
||||
|
|
|
@ -105,9 +105,9 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* conte
|
|||
}
|
||||
|
||||
bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const {
|
||||
Camera* camera = qApp->getCamera();
|
||||
glm::vec3 position = camera->getPosition();
|
||||
glm::quat orientation = camera->getOrientation();
|
||||
const Camera& camera = qApp->getCamera();
|
||||
glm::vec3 position = camera.getPosition();
|
||||
glm::quat orientation = camera.getOrientation();
|
||||
|
||||
glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ QVariant Billboardable::getProperty(const QString &property) {
|
|||
void Billboardable::pointTransformAtCamera(Transform& transform, glm::quat offsetRotation) {
|
||||
if (isFacingAvatar()) {
|
||||
glm::vec3 billboardPos = transform.getTranslation();
|
||||
glm::vec3 cameraPos = qApp->getCamera()->getPosition();
|
||||
glm::vec3 cameraPos = qApp->getCamera().getPosition();
|
||||
glm::vec3 look = cameraPos - billboardPos;
|
||||
float elevation = -asinf(look.y / glm::length(look));
|
||||
float azimuth = atan2f(look.x, look.z);
|
||||
|
|
|
@ -58,13 +58,13 @@ void ModelOverlay::update(float deltatime) {
|
|||
_isLoaded = _model->isActive();
|
||||
}
|
||||
|
||||
bool ModelOverlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
Volume3DOverlay::addToScene(overlay, scene, transaction);
|
||||
_model->addToScene(scene, transaction);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
Volume3DOverlay::removeFromScene(overlay, scene, transaction);
|
||||
_model->removeFromScene(scene, transaction);
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ public:
|
|||
|
||||
virtual ModelOverlay* createClone() const override;
|
||||
|
||||
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual bool addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
|
||||
void locationChanged(bool tellPhysics) override;
|
||||
|
||||
|
|
|
@ -196,13 +196,13 @@ float Overlay::updatePulse() {
|
|||
return _pulse;
|
||||
}
|
||||
|
||||
bool Overlay::addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
bool Overlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
_renderItemID = scene->allocateID();
|
||||
transaction.resetItem(_renderItemID, std::make_shared<Overlay::Payload>(overlay));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Overlay::removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void Overlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
transaction.removeItem(_renderItemID);
|
||||
render::Item::clearID(_renderItemID);
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ public:
|
|||
virtual AABox getBounds() const = 0;
|
||||
virtual bool supportsGetProperty() const { return true; }
|
||||
|
||||
virtual bool addToScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction);
|
||||
virtual void removeFromScene(Overlay::Pointer overlay, std::shared_ptr<render::Scene> scene, render::Transaction& transaction);
|
||||
virtual bool addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction);
|
||||
virtual void removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction);
|
||||
|
||||
virtual const render::ShapeKey getShapeKey() { return render::ShapeKey::Builder::ownPipeline(); }
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace render {
|
|||
// Mixin class for implementing basic single item rendering
|
||||
class SimpleRenderableEntityItem {
|
||||
public:
|
||||
bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
_myItem = scene->allocateID();
|
||||
|
||||
auto renderData = std::make_shared<RenderableEntityItemProxy>(self, _myItem);
|
||||
|
@ -69,7 +69,7 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
transaction.removeItem(_myItem);
|
||||
render::Item::clearID(_myItem);
|
||||
}
|
||||
|
@ -99,8 +99,8 @@ private:
|
|||
|
||||
#define SIMPLE_RENDERABLE() \
|
||||
public: \
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \
|
||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \
|
||||
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { return _renderHelper.addToScene(self, scene, transaction); } \
|
||||
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override { _renderHelper.removeFromScene(self, scene, transaction); } \
|
||||
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \
|
||||
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \
|
||||
void checkFading() { \
|
||||
|
|
|
@ -27,7 +27,7 @@ RenderableLightEntityItem::RenderableLightEntityItem(const EntityItemID& entityI
|
|||
{
|
||||
}
|
||||
|
||||
bool RenderableLightEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
bool RenderableLightEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
_myItem = scene->allocateID();
|
||||
|
||||
auto renderItem = std::make_shared<LightPayload>();
|
||||
|
@ -51,7 +51,7 @@ void RenderableLightEntityItem::somethingChangedNotification() {
|
|||
LightEntityItem::somethingChangedNotification();
|
||||
}
|
||||
|
||||
void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void RenderableLightEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
transaction.removeItem(_myItem);
|
||||
render::Item::clearID(_myItem);
|
||||
}
|
||||
|
|
|
@ -30,10 +30,10 @@ public:
|
|||
|
||||
void updateLightFromEntity(render::Transaction& transaction);
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
|
||||
virtual void somethingChangedNotification() override;
|
||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
|
||||
virtual void locationChanged(bool tellPhysics = true) override;
|
||||
|
||||
|
|
|
@ -91,13 +91,13 @@ bool RenderableModelEntityItem::setProperties(const EntityItemProperties& proper
|
|||
return somethingChanged;
|
||||
}
|
||||
|
||||
int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
int RenderableModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) {
|
||||
QString oldModelURL = getModelURL();
|
||||
int bytesRead = ModelEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead,
|
||||
args, propertyFlags,
|
||||
int bytesRead = ModelEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead,
|
||||
args, propertyFlags,
|
||||
overwriteLocalData, somethingChanged);
|
||||
if (oldModelURL != getModelURL()) {
|
||||
_needsModelReload = true;
|
||||
|
@ -137,7 +137,7 @@ void RenderableModelEntityItem::remapTextures() {
|
|||
if (!_model) {
|
||||
return; // nothing to do if we don't have a model
|
||||
}
|
||||
|
||||
|
||||
if (!_model->isLoaded()) {
|
||||
return; // nothing to do if the model has not yet loaded
|
||||
}
|
||||
|
@ -189,16 +189,16 @@ public:
|
|||
RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
|
||||
typedef render::Payload<RenderableModelEntityItemMeta> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
|
||||
EntityItemPointer entity;
|
||||
};
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) {
|
||||
template <> const ItemKey payloadGetKey(const RenderableModelEntityItemMeta::Pointer& payload) {
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta();
|
||||
}
|
||||
|
||||
template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) {
|
||||
|
||||
template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) {
|
||||
if (payload && payload->entity) {
|
||||
bool success;
|
||||
auto result = payload->entity->getAABox(success);
|
||||
|
@ -228,7 +228,7 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
_myMetaItem = scene->allocateID();
|
||||
|
||||
|
@ -249,7 +249,7 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p
|
|||
return true;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
void RenderableModelEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
transaction.removeItem(_myMetaItem);
|
||||
render::Item::clearID(_myMetaItem);
|
||||
|
@ -437,7 +437,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
_model->renderDebugMeshBoxes(batch);
|
||||
#endif
|
||||
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||
// the renderable item. As it stands now the model checks it's visible/invisible state
|
||||
|
@ -502,11 +502,11 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRender
|
|||
_myRenderer = renderer;
|
||||
}
|
||||
assert(_myRenderer == renderer); // you should only ever render on one renderer
|
||||
|
||||
|
||||
if (!_myRenderer || QThread::currentThread() != _myRenderer->thread()) {
|
||||
return _model;
|
||||
}
|
||||
|
||||
|
||||
_needsModelReload = false; // this is the reload
|
||||
|
||||
// If we have a URL, then we will want to end up returning a model...
|
||||
|
@ -526,7 +526,7 @@ ModelPointer RenderableModelEntityItem::getModel(QSharedPointer<EntityTreeRender
|
|||
// If we have no URL, then we can delete any model we do have...
|
||||
} else if (_model) {
|
||||
// remove from scene
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
_model->removeFromScene(scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
@ -552,7 +552,7 @@ void RenderableModelEntityItem::update(const quint64& now) {
|
|||
properties.setLastEdited(usecTimestampNow()); // we must set the edit time since we're editing it
|
||||
auto extents = _model->getMeshExtents();
|
||||
properties.setDimensions(extents.maximum - extents.minimum);
|
||||
qCDebug(entitiesrenderer) << "Autoresizing" << (!getName().isEmpty() ? getName() : getModelURL())
|
||||
qCDebug(entitiesrenderer) << "Autoresizing" << (!getName().isEmpty() ? getName() : getModelURL())
|
||||
<< "from mesh extents";
|
||||
QMetaObject::invokeMethod(DependencyManager::get<EntityScriptingInterface>().data(), "editEntity",
|
||||
Qt::QueuedConnection,
|
||||
|
@ -594,8 +594,8 @@ bool RenderableModelEntityItem::supportsDetailedRayIntersection() const {
|
|||
return _model && _model->isLoaded();
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face,
|
||||
glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const {
|
||||
if (!_model) {
|
||||
return true;
|
||||
|
@ -1239,11 +1239,10 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics) {
|
|||
return;
|
||||
}
|
||||
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
render::Transaction transaction;
|
||||
|
||||
transaction.updateItem(myMetaItem);
|
||||
scene->enqueueTransaction(transaction);
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ public:
|
|||
|
||||
void doInitialModelSimulation();
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
|
||||
|
||||
void updateModelBounds();
|
||||
|
|
|
@ -161,7 +161,7 @@ RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const Ent
|
|||
}
|
||||
|
||||
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
|
||||
render::ScenePointer scene,
|
||||
const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
_scene = scene;
|
||||
_renderItemId = _scene->allocateID();
|
||||
|
@ -176,7 +176,7 @@ bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
|
|||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self,
|
||||
render::ScenePointer scene,
|
||||
const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
transaction.removeItem(_renderItemId);
|
||||
_scene = nullptr;
|
||||
|
@ -323,4 +323,4 @@ void RenderableParticleEffectEntityItem::notifyBoundChanged() {
|
|||
});
|
||||
|
||||
_scene->enqueueTransaction(transaction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@ public:
|
|||
|
||||
void updateRenderItem();
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::Transaction& transaction) override;
|
||||
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
|
||||
protected:
|
||||
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }
|
||||
|
|
|
@ -816,7 +816,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
|
||||
std::shared_ptr<render::Scene> scene,
|
||||
const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
_myItem = scene->allocateID();
|
||||
|
||||
|
@ -834,7 +834,7 @@ bool RenderablePolyVoxEntityItem::addToScene(EntityItemPointer self,
|
|||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::removeFromScene(EntityItemPointer self,
|
||||
std::shared_ptr<render::Scene> scene,
|
||||
const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
transaction.removeItem(_myItem);
|
||||
render::Item::clearID(_myItem);
|
||||
|
|
|
@ -106,10 +106,10 @@ public:
|
|||
virtual void setZTextureURL(const QString& zTextureURL) override;
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self,
|
||||
std::shared_ptr<render::Scene> scene,
|
||||
const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self,
|
||||
std::shared_ptr<render::Scene> scene,
|
||||
const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) override;
|
||||
|
||||
virtual void setXNNeighborID(const EntityItemID& xNNeighborID) override;
|
||||
|
|
|
@ -217,7 +217,7 @@ namespace render {
|
|||
}
|
||||
}
|
||||
|
||||
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
_myMetaItem = scene->allocateID();
|
||||
|
||||
|
@ -232,7 +232,7 @@ bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, std::shared_pt
|
|||
return true;
|
||||
}
|
||||
|
||||
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
transaction.removeItem(_myMetaItem);
|
||||
render::Item::clearID(_myMetaItem);
|
||||
|
|
|
@ -38,8 +38,8 @@ public:
|
|||
virtual void render(RenderArgs* args) override;
|
||||
virtual bool contains(const glm::vec3& point) const override;
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) override;
|
||||
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
|
||||
|
||||
render::ItemID getRenderItemID() const { return _myMetaItem; }
|
||||
|
||||
|
|
|
@ -151,9 +151,9 @@ public:
|
|||
bool& somethingChanged)
|
||||
{ somethingChanged = false; return 0; }
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
virtual bool addToScene(EntityItemPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) { return false; } // by default entity items don't add to scene
|
||||
virtual void removeFromScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) { } // by default entity items don't add to scene
|
||||
virtual void render(RenderArgs* args) { } // by default entity items don't know how to render
|
||||
|
||||
|
|
|
@ -429,7 +429,7 @@ void ModelEntityItem::setJointTranslations(const QVector<glm::vec3>& translation
|
|||
for (int index = 0; index < translations.size(); index++) {
|
||||
if (_localJointTranslationsSet[index]) {
|
||||
_localJointTranslations[index] = translations[index];
|
||||
_localJointTranslationsSet[index] = true;
|
||||
_localJointTranslationsDirty[index] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#include "GLTexture.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#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<GLBackend>& backend, const Texture& texture, GLuint id)
|
||||
: Parent(backend, texture, id) { }
|
||||
|
@ -127,3 +144,420 @@ GLExternalTexture::~GLExternalTexture() {
|
|||
const_cast<GLuint&>(_id) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Variable sized textures
|
||||
using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState;
|
||||
using WorkQueue = GLVariableAllocationSupport::WorkQueue;
|
||||
|
||||
std::list<TextureWeakPointer> GLVariableAllocationSupport::_memoryManagedTextures;
|
||||
MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle };
|
||||
std::atomic<bool> 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<std::thread> TransferJob::_bufferThread { nullptr };
|
||||
std::atomic<bool> TransferJob::_shutdownBufferingThread { false };
|
||||
Mutex TransferJob::_mutex;
|
||||
TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue;
|
||||
|
||||
void TransferJob::startTransferLoop() {
|
||||
if (_bufferThread) {
|
||||
return;
|
||||
}
|
||||
_shutdownBufferingThread = false;
|
||||
_bufferThread = std::make_shared<std::thread>([] {
|
||||
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<uint8_t> emptyVector;
|
||||
_buffer.swap(emptyVector);
|
||||
};
|
||||
}
|
||||
|
||||
TransferJob::TransferJob(const GLTexture& parent, std::function<void()> 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<GLTexture>(*texturePointer);
|
||||
GLVariableAllocationSupport* vargltexture = dynamic_cast<GLVariableAllocationSupport*>(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<TexturePointer> 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<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(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) {
|
||||
// Race conditions can still leave nulls in the list, so we need to check
|
||||
if (!texture) {
|
||||
continue;
|
||||
}
|
||||
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<GLTexture>(*texture);
|
||||
GLVariableAllocationSupport* vartexture = dynamic_cast<GLVariableAllocationSupport*>(gltexture);
|
||||
if (MemoryPressureState::Oversubscribed == _memoryPressureState) {
|
||||
if (!vartexture->canDemote()) {
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
#include "GLShared.h"
|
||||
#include "GLBackend.h"
|
||||
#include "GLTexelFormat.h"
|
||||
#include <thread>
|
||||
|
||||
#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<TextureWeakPointer, float>;
|
||||
struct QueuePairLess {
|
||||
bool operator()(const QueuePair& a, const QueuePair& b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
};
|
||||
using WorkQueue = std::priority_queue<QueuePair, std::vector<QueuePair>, QueuePairLess>;
|
||||
|
||||
class TransferJob {
|
||||
using VoidLambda = std::function<void()>;
|
||||
using VoidLambdaQueue = std::queue<VoidLambda>;
|
||||
using ThreadPointer = std::shared_ptr<std::thread>;
|
||||
const GLTexture& _parent;
|
||||
// Holds the contents to transfer to the GPU in CPU memory
|
||||
std::vector<uint8_t> _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<bool> _shutdownBufferingThread;
|
||||
static void bufferLoop();
|
||||
#endif
|
||||
|
||||
public:
|
||||
TransferJob(const TransferJob& other) = delete;
|
||||
TransferJob(const GLTexture& parent, std::function<void()> 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<std::unique_ptr<TransferJob>>;
|
||||
static MemoryPressureState _memoryPressureState;
|
||||
|
||||
public:
|
||||
static void addMemoryManagedTexture(const TexturePointer& texturePointer);
|
||||
|
||||
protected:
|
||||
static size_t _frameTexturesCreated;
|
||||
static std::atomic<bool> _memoryPressureStateStale;
|
||||
static std::list<TextureWeakPointer> _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<Texture> {
|
||||
using Parent = GLObject<Texture>;
|
||||
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<gl::GLBackend>& backend, const Texture& texture, GLuint id);
|
||||
};
|
||||
|
@ -57,6 +177,8 @@ public:
|
|||
protected:
|
||||
GLExternalTexture(const std::weak_ptr<gl::GLBackend>& 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; }
|
||||
};
|
||||
|
||||
|
|
|
@ -45,8 +45,7 @@ public:
|
|||
protected:
|
||||
GL41Texture(const std::weak_ptr<GLBackend>& 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<void()> f) const;
|
||||
|
@ -86,8 +85,29 @@ public:
|
|||
GL41StrictResourceTexture(const std::weak_ptr<GLBackend>& 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<void()>;
|
||||
|
||||
|
||||
protected:
|
||||
GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& 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<GLBackend>& backend, const Texture& texture);
|
||||
|
|
|
@ -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<GLBackend>& 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<GLBacke
|
|||
}
|
||||
}
|
||||
|
||||
using GL41VariableAllocationTexture = GL41Backend::GL41VariableAllocationTexture;
|
||||
|
||||
GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr<GLBackend>& 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<uint16_t>(_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(_size, 0);
|
||||
}
|
||||
|
||||
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<GLuint&>(_id) = allocate(_gpuObject);
|
||||
uint16_t oldAllocatedMip = _allocatedMip;
|
||||
|
||||
// allocate storage for new level
|
||||
allocateStorage(_allocatedMip - std::min<uint16_t>(_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<GLuint&>(_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<uint32_t>(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<GLBackend>& 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<GLBackend>& backend, const Texture& texture) : GL41VariableAllocationTexture(backend, texture) {
|
||||
if (texture.isAutogenerateMips()) {
|
||||
generateMips();
|
||||
}
|
||||
}
|
||||
|
||||
GL41ResourceTexture::~GL41ResourceTexture() {
|
||||
Backend::updateTextureGPUMemoryUsage(size(), 0);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -40,8 +40,7 @@ public:
|
|||
protected:
|
||||
GL45Texture(const std::weak_ptr<GLBackend>& 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<void()>;
|
||||
|
||||
public:
|
||||
enum class MemoryPressureState {
|
||||
Idle,
|
||||
Transfer,
|
||||
Oversubscribed,
|
||||
Undersubscribed,
|
||||
};
|
||||
|
||||
using QueuePair = std::pair<TextureWeakPointer, float>;
|
||||
struct QueuePairLess {
|
||||
bool operator()(const QueuePair& a, const QueuePair& b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
};
|
||||
using WorkQueue = std::priority_queue<QueuePair, std::vector<QueuePair>, QueuePairLess>;
|
||||
|
||||
class TransferJob {
|
||||
using VoidLambda = std::function<void()>;
|
||||
using VoidLambdaQueue = std::queue<VoidLambda>;
|
||||
using ThreadPointer = std::shared_ptr<std::thread>;
|
||||
const GL45VariableAllocationTexture& _parent;
|
||||
// Holds the contents to transfer to the GPU in CPU memory
|
||||
std::vector<uint8_t> _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<bool> _shutdownBufferingThread;
|
||||
static void bufferLoop();
|
||||
#endif
|
||||
|
||||
public:
|
||||
TransferJob(const TransferJob& other) = delete;
|
||||
TransferJob(const GL45VariableAllocationTexture& parent, std::function<void()> 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<std::unique_ptr<TransferJob>>;
|
||||
static MemoryPressureState _memoryPressureState;
|
||||
protected:
|
||||
static size_t _frameTexturesCreated;
|
||||
static std::atomic<bool> _memoryPressureStateStale;
|
||||
static std::list<TextureWeakPointer> _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<GLBackend>& 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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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<TextureWeakPointer> GL45VariableAllocationTexture::_memoryManagedTextures;
|
||||
MemoryPressureState GL45VariableAllocationTexture::_memoryPressureState = MemoryPressureState::Idle;
|
||||
std::atomic<bool> 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<std::thread> TransferJob::_bufferThread { nullptr };
|
||||
std::atomic<bool> TransferJob::_shutdownBufferingThread { false };
|
||||
Mutex TransferJob::_mutex;
|
||||
TransferJob::VoidLambdaQueue TransferJob::_bufferLambdaQueue;
|
||||
|
||||
void TransferJob::startTransferLoop() {
|
||||
if (_bufferThread) {
|
||||
return;
|
||||
}
|
||||
_shutdownBufferingThread = false;
|
||||
_bufferThread = std::make_shared<std::thread>([] {
|
||||
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<uint8_t> emptyVector;
|
||||
_buffer.swap(emptyVector);
|
||||
};
|
||||
}
|
||||
|
||||
TransferJob::TransferJob(const GL45VariableAllocationTexture& parent, std::function<void()> 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<GL45VariableAllocationTexture>(*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<TexturePointer> 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<GL45VariableAllocationTexture>(*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<GL45VariableAllocationTexture>(*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<GLBackend>& 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<GLBackend>& backend
|
|||
|
||||
uint16_t allocatedMip = _populatedMip - std::min<uint16_t>(_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();
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ namespace gpu {
|
|||
class Sampler;
|
||||
class Texture;
|
||||
using TexturePointer = std::shared_ptr<Texture>;
|
||||
using TextureWeakPointer = std::weak_ptr<Texture>;
|
||||
using Textures = std::vector<TexturePointer>;
|
||||
class TextureView;
|
||||
using TextureViews = std::vector<TextureView>;
|
||||
|
|
|
@ -231,8 +231,6 @@ void Model::updateRenderItems() {
|
|||
// We need to update them here so we can correctly update the bounding box.
|
||||
self->updateClusterMatrices();
|
||||
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
|
||||
|
||||
render::Transaction transaction;
|
||||
|
@ -266,7 +264,7 @@ void Model::updateRenderItems() {
|
|||
});
|
||||
}
|
||||
|
||||
scene->enqueueTransaction(transaction);
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -534,7 +532,7 @@ void Model::calculateTriangleSets() {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene) {
|
||||
void Model::setVisibleInScene(bool newValue, const render::ScenePointer& scene) {
|
||||
if (_isVisible != newValue) {
|
||||
_isVisible = newValue;
|
||||
|
||||
|
@ -550,7 +548,7 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
|
|||
}
|
||||
|
||||
|
||||
void Model::setLayeredInFront(bool layered, std::shared_ptr<render::Scene> scene) {
|
||||
void Model::setLayeredInFront(bool layered, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInFront != layered) {
|
||||
_isLayeredInFront = layered;
|
||||
|
||||
|
@ -565,7 +563,7 @@ void Model::setLayeredInFront(bool layered, std::shared_ptr<render::Scene> scene
|
|||
}
|
||||
}
|
||||
|
||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||
bool Model::addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters) {
|
||||
bool readyToRender = _collisionGeometry || isLoaded();
|
||||
|
@ -622,7 +620,7 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
|||
return somethingAdded;
|
||||
}
|
||||
|
||||
void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
|
||||
void Model::removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.removeItem(item);
|
||||
}
|
||||
|
@ -795,7 +793,7 @@ void Model::setURL(const QUrl& url) {
|
|||
|
||||
{
|
||||
render::Transaction transaction;
|
||||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
if (scene) {
|
||||
removeFromScene(scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
|
@ -1271,7 +1269,7 @@ bool Model::isRenderable() const {
|
|||
return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty());
|
||||
}
|
||||
|
||||
bool Model::initWhenReady(render::ScenePointer scene) {
|
||||
bool Model::initWhenReady(const render::ScenePointer& scene) {
|
||||
// NOTE: this only called by SkeletonModel
|
||||
if (_addedToScene || !isRenderable()) {
|
||||
return false;
|
||||
|
|
|
@ -81,21 +81,21 @@ public:
|
|||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
// new Scene/Engine rendering support
|
||||
void setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene);
|
||||
void setLayeredInFront(bool layered, std::shared_ptr<render::Scene> scene);
|
||||
void setVisibleInScene(bool newValue, const render::ScenePointer& scene);
|
||||
void setLayeredInFront(bool layered, const render::ScenePointer& scene);
|
||||
bool needsFixupInScene() const;
|
||||
|
||||
bool needsReload() const { return _needsReload; }
|
||||
bool initWhenReady(render::ScenePointer scene);
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||
bool initWhenReady(const render::ScenePointer& scene);
|
||||
bool addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction) {
|
||||
auto getters = render::Item::Status::Getters(0);
|
||||
return addToScene(scene, transaction, getters);
|
||||
}
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||
bool addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters);
|
||||
void removeFromScene(std::shared_ptr<render::Scene> scene, render::Transaction& transaction);
|
||||
void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction);
|
||||
bool isRenderable() const;
|
||||
|
||||
bool isVisible() const { return _isVisible; }
|
||||
|
|
|
@ -410,40 +410,36 @@ void TabletProxy::loadQMLSource(const QVariant& path) {
|
|||
}
|
||||
|
||||
void TabletProxy::pushOntoStack(const QVariant& path) {
|
||||
if (_qmlTabletRoot) {
|
||||
auto stack = _qmlTabletRoot->findChild<QQuickItem*>("stack");
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
} else if (_toolbarMode && _desktopWindow) {
|
||||
root = _desktopWindow->asQuickItem();
|
||||
}
|
||||
|
||||
if (root) {
|
||||
auto stack = root->findChild<QQuickItem*>("stack");
|
||||
if (stack) {
|
||||
QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path));
|
||||
} else {
|
||||
loadQMLSource(path);
|
||||
}
|
||||
} else if (_desktopWindow) {
|
||||
auto stack = _desktopWindow->asQuickItem()->findChild<QQuickItem*>("stack");
|
||||
if (stack) {
|
||||
QMetaObject::invokeMethod(stack, "pushSource", Q_ARG(const QVariant&, path));
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot push QML because _desktopWindow doesn't have child stack";
|
||||
}
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null";
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::popFromStack() {
|
||||
if (_qmlTabletRoot) {
|
||||
auto stack = _qmlTabletRoot->findChild<QQuickItem*>("stack");
|
||||
if (stack) {
|
||||
QMetaObject::invokeMethod(stack, "popSource");
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot doesn't have child stack";
|
||||
}
|
||||
} else if (_desktopWindow) {
|
||||
auto stack = _desktopWindow->asQuickItem()->findChild<QQuickItem*>("stack");
|
||||
if (stack) {
|
||||
QMetaObject::invokeMethod(stack, "popSource");
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot pop QML because _desktopWindow doesn't have child stack";
|
||||
}
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
} else if (_toolbarMode && _desktopWindow) {
|
||||
root = _desktopWindow->asQuickItem();
|
||||
}
|
||||
|
||||
if (root) {
|
||||
auto stack = root->findChild<QQuickItem*>("stack");
|
||||
QMetaObject::invokeMethod(stack, "popSource");
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null";
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
|
||||
class AABox;
|
||||
class OctreeRenderer;
|
||||
namespace render {
|
||||
class Scene;
|
||||
using ScenePointer = std::shared_ptr<Scene>;
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
|
@ -126,6 +130,8 @@ public:
|
|||
bool _enableTexturing { true };
|
||||
|
||||
RenderDetails _details;
|
||||
render::ScenePointer _scene; // HACK
|
||||
int8_t _cameraMode { -1 }; // HACK
|
||||
};
|
||||
|
||||
#endif // hifi_RenderArgs_h
|
||||
|
|
|
@ -109,8 +109,12 @@ void MainWindow::changeEvent(QEvent* event) {
|
|||
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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -75,7 +75,7 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe
|
|||
//
|
||||
// distant manipulation
|
||||
//
|
||||
|
||||
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
|
||||
|
@ -2524,7 +2524,7 @@ function MyController(hand) {
|
|||
this.mass = this.getMass(grabbedProperties.dimensions, grabbedProperties.density);
|
||||
var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, grabbedProperties.position));
|
||||
var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject);
|
||||
|
||||
this.linearTimeScale = timeScale;
|
||||
this.actionID = NULL_UUID;
|
||||
this.actionID = Entities.addAction("far-grab", this.grabbedThingID, {
|
||||
targetPosition: this.currentObjectPosition,
|
||||
|
@ -2574,7 +2574,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;
|
||||
|
@ -2622,11 +2621,9 @@ function MyController(hand) {
|
|||
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) {
|
||||
|
@ -2659,9 +2656,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
|
||||
|
|
|
@ -400,6 +400,7 @@ function getProfilePicture(username, callback) { // callback(url) if successfull
|
|||
var matched = !error && html.match(/img class="users-img" src="([^"]*)"/);
|
||||
if (!matched) {
|
||||
print('Error: Unable to get profile picture for', username, error);
|
||||
callback('');
|
||||
return;
|
||||
}
|
||||
callback(matched[1]);
|
||||
|
|
Loading…
Reference in a new issue