Merging with master

This commit is contained in:
samcake 2016-04-19 11:59:44 -07:00
commit 3dad4f576e
17 changed files with 348 additions and 84 deletions

View file

@ -0,0 +1,56 @@
//
// exampleSelfCallingTimeoutNoCleanup.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 4/18/16.
// Copyright 2016 High Fidelity, Inc.
//
// This is an example of an entity script which hooks the update signal
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this;
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
ExampleUpdate = function() {
_this = this;
};
ExampleUpdate.prototype = {
timeOutFunction: function() {
var entityID = _this.entityID;
print("timeOutFunction in entityID:" + entityID);
Script.setTimeout(function() {
_this.timeOutFunction();
}, 3000);
},
// preload() will be called when the entity has become visible (or known) to the interface
// it gives us a chance to set our local JavaScript object up. In this case it means:
// * remembering our entityID, so we can access it in cases where we're called without an entityID
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
print("preload - entityID:" + entityID);
this.entityID = entityID;
print("preload - entityID:" + entityID + "-- calling timeOutFunction()....");
_this.timeOutFunction();
},
// unload() will be called when our entity is no longer available. It may be because we were deleted,
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
// to the update signal
unload: function(entityID) {
print("unload - entityID:" + entityID);
print("NOTE --- WE DID NOT CALL clear our timeout");
},
};
// entity scripts always need to return a newly constructed object of our type
return new ExampleUpdate();
})

View file

@ -0,0 +1,50 @@
//
// exampleTimeoutNoCleanup.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 4/18/16.
// Copyright 2016 High Fidelity, Inc.
//
// This is an example of an entity script which hooks the update signal
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this;
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
ExampleUpdate = function() {
_this = this;
};
ExampleUpdate.prototype = {
// preload() will be called when the entity has become visible (or known) to the interface
// it gives us a chance to set our local JavaScript object up. In this case it means:
// * remembering our entityID, so we can access it in cases where we're called without an entityID
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
print("preload - entityID:" + entityID);
this.entityID = entityID;
Script.setInterval(function() {
var entityID = _this.entityID;
print("timer interval in entityID:" + entityID);
}, 3000);
},
// unload() will be called when our entity is no longer available. It may be because we were deleted,
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
// to the update signal
unload: function(entityID) {
print("unload - entityID:" + entityID);
print("NOTE --- WE DID NOT CALL clear our timeout");
},
};
// entity scripts always need to return a newly constructed object of our type
return new ExampleUpdate();
})

View file

@ -0,0 +1,54 @@
//
// exampleUpdate.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 4/18/16.
// Copyright 2016 High Fidelity, Inc.
//
// This is an example of an entity script which hooks the update signal
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this;
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
ExampleUpdate = function() {
_this = this;
};
ExampleUpdate.prototype = {
// update() will be called regulary, because we've hooked the update signal in our preload() function.
// we will check the avatars hand positions and if either hand is in our bounding box, we will notice that
update: function() {
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
var entityID = _this.entityID;
print("update in entityID:" + entityID);
},
// preload() will be called when the entity has become visible (or known) to the interface
// it gives us a chance to set our local JavaScript object up. In this case it means:
// * remembering our entityID, so we can access it in cases where we're called without an entityID
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
print("preload - entityID:" + entityID);
this.entityID = entityID;
Script.update.connect(this.update);
},
// unload() will be called when our entity is no longer available. It may be because we were deleted,
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
// to the update signal
unload: function(entityID) {
print("unload - entityID:" + entityID);
Script.update.disconnect(this.update);
},
};
// entity scripts always need to return a newly constructed object of our type
return new ExampleUpdate();
})

View file

@ -0,0 +1,54 @@
//
// exampleUpdateNoDisconnect.js
// examples/entityScripts
//
// Created by Brad Hefta-Gaub on 4/18/16.
// Copyright 2016 High Fidelity, Inc.
//
// This is an example of an entity script which hooks the update signal
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
(function() {
var _this;
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
ExampleUpdate = function() {
_this = this;
};
ExampleUpdate.prototype = {
// update() will be called regulary, because we've hooked the update signal in our preload() function.
// we will check the avatars hand positions and if either hand is in our bounding box, we will notice that
update: function() {
// because the update() signal doesn't have a valid this, we need to use our memorized _this to access our entityID
var entityID = _this.entityID;
print("update in entityID:" + entityID);
},
// preload() will be called when the entity has become visible (or known) to the interface
// it gives us a chance to set our local JavaScript object up. In this case it means:
// * remembering our entityID, so we can access it in cases where we're called without an entityID
// * connecting to the update signal so we can check our grabbed state
preload: function(entityID) {
print("preload - entityID:" + entityID);
this.entityID = entityID;
Script.update.connect(this.update);
},
// unload() will be called when our entity is no longer available. It may be because we were deleted,
// or because we've left the domain or quit the application. In all cases we want to unhook our connection
// to the update signal
unload: function(entityID) {
print("unload - entityID:" + entityID);
print("NOTE --- WE DID NOT CALL Script.update.disconnect()");
},
};
// entity scripts always need to return a newly constructed object of our type
return new ExampleUpdate();
})

View file

@ -1178,8 +1178,6 @@ void Application::cleanupBeforeQuit() {
} }
_keyboardFocusHighlight = nullptr; _keyboardFocusHighlight = nullptr;
getEntities()->clear(); // this will allow entity scripts to properly shutdown
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
// send the domain a disconnect packet, force stoppage of domain-server check-ins // send the domain a disconnect packet, force stoppage of domain-server check-ins
@ -1190,6 +1188,7 @@ void Application::cleanupBeforeQuit() {
nodeList->getPacketReceiver().setShouldDropPackets(true); nodeList->getPacketReceiver().setShouldDropPackets(true);
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
DependencyManager::get<ScriptEngines>()->saveScripts(); DependencyManager::get<ScriptEngines>()->saveScripts();
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
DependencyManager::destroy<ScriptEngines>(); DependencyManager::destroy<ScriptEngines>();
@ -4184,8 +4183,13 @@ void Application::updateWindowTitle() const {
} }
void Application::clearDomainOctreeDetails() { void Application::clearDomainOctreeDetails() {
// if we're about to quit, we really don't need to do any of these things...
if (_aboutToQuit) {
return;
}
qCDebug(interfaceapp) << "Clearing domain octree details..."; qCDebug(interfaceapp) << "Clearing domain octree details...";
// reset the environment so that we don't erroneously end up with multiple
resetPhysicsReadyInformation(); resetPhysicsReadyInformation();
@ -4209,7 +4213,6 @@ void Application::clearDomainOctreeDetails() {
void Application::domainChanged(const QString& domainHostname) { void Application::domainChanged(const QString& domainHostname) {
updateWindowTitle(); updateWindowTitle();
clearDomainOctreeDetails();
// disable physics until we have enough information about our new location to not cause craziness. // disable physics until we have enough information about our new location to not cause craziness.
resetPhysicsReadyInformation(); resetPhysicsReadyInformation();
} }

View file

@ -75,9 +75,29 @@ EntityTreeRenderer::~EntityTreeRenderer() {
// it is registered with ScriptEngines, which will call deleteLater for us. // it is registered with ScriptEngines, which will call deleteLater for us.
} }
int EntityTreeRenderer::_entitiesScriptEngineCount = 0;
void EntityTreeRenderer::setupEntitiesScriptEngine() {
QSharedPointer<ScriptEngine> oldEngine = _entitiesScriptEngine; // save the old engine through this function, so the EntityScriptingInterface doesn't have problems with it.
_entitiesScriptEngine = QSharedPointer<ScriptEngine>(new ScriptEngine(NO_SCRIPT, QString("Entities %1").arg(++_entitiesScriptEngineCount)), &QObject::deleteLater);
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine.data());
_entitiesScriptEngine->runInThread();
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine.data());
}
void EntityTreeRenderer::clear() { void EntityTreeRenderer::clear() {
leaveAllEntities(); leaveAllEntities();
_entitiesScriptEngine->unloadAllEntityScripts(); if (_entitiesScriptEngine) {
_entitiesScriptEngine->unloadAllEntityScripts();
_entitiesScriptEngine->stop();
}
if (_wantScripts && !_shuttingDown) {
// NOTE: you can't actually need to delete it here because when we call setupEntitiesScriptEngine it will
// assign a new instance to our shared pointer, which will deref the old instance and ultimately call
// the custom deleter which calls deleteLater
setupEntitiesScriptEngine();
}
auto scene = _viewState->getMain3DScene(); auto scene = _viewState->getMain3DScene();
render::PendingChanges pendingChanges; render::PendingChanges pendingChanges;
@ -94,7 +114,7 @@ void EntityTreeRenderer::reloadEntityScripts() {
_entitiesScriptEngine->unloadAllEntityScripts(); _entitiesScriptEngine->unloadAllEntityScripts();
foreach(auto entity, _entitiesInScene) { foreach(auto entity, _entitiesInScene) {
if (!entity->getScript().isEmpty()) { if (!entity->getScript().isEmpty()) {
_entitiesScriptEngine->loadEntityScript(entity->getEntityItemID(), entity->getScript(), true); ScriptEngine::loadEntityScript(_entitiesScriptEngine, entity->getEntityItemID(), entity->getScript(), true);
} }
} }
} }
@ -105,10 +125,7 @@ void EntityTreeRenderer::init() {
entityTree->setFBXService(this); entityTree->setFBXService(this);
if (_wantScripts) { if (_wantScripts) {
_entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities"); setupEntitiesScriptEngine();
_scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
_entitiesScriptEngine->runInThread();
DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);
} }
forceRecheckEntities(); // setup our state to force checking our inside/outsideness of entities forceRecheckEntities(); // setup our state to force checking our inside/outsideness of entities
@ -122,6 +139,8 @@ void EntityTreeRenderer::init() {
void EntityTreeRenderer::shutdown() { void EntityTreeRenderer::shutdown() {
_entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential _entitiesScriptEngine->disconnectNonEssentialSignals(); // disconnect all slots/signals from the script engine, except essential
_shuttingDown = true; _shuttingDown = true;
clear(); // always clear() on shutdown
} }
void EntityTreeRenderer::setTree(OctreePointer newTree) { void EntityTreeRenderer::setTree(OctreePointer newTree) {
@ -763,7 +782,7 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const
if (entity && entity->shouldPreloadScript()) { if (entity && entity->shouldPreloadScript()) {
QString scriptUrl = entity->getScript(); QString scriptUrl = entity->getScript();
scriptUrl = ResourceManager::normalizeURL(scriptUrl); scriptUrl = ResourceManager::normalizeURL(scriptUrl);
_entitiesScriptEngine->loadEntityScript(entityID, scriptUrl, reload); ScriptEngine::loadEntityScript(_entitiesScriptEngine, entityID, scriptUrl, reload);
entity->scriptHasPreloaded(); entity->scriptHasPreloaded();
} }
} }

View file

@ -126,6 +126,8 @@ protected:
} }
private: private:
void setupEntitiesScriptEngine();
void addEntityToScene(EntityItemPointer entity); void addEntityToScene(EntityItemPointer entity);
bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar); bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar);
@ -155,7 +157,7 @@ private:
NetworkTexturePointer _ambientTexture; NetworkTexturePointer _ambientTexture;
bool _wantScripts; bool _wantScripts;
ScriptEngine* _entitiesScriptEngine; QSharedPointer<ScriptEngine> _entitiesScriptEngine;
bool isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree, bool isCollisionOwner(const QUuid& myNodeID, EntityTreePointer entityTree,
const EntityItemID& id, const Collision& collision); const EntityItemID& id, const Collision& collision);
@ -196,6 +198,8 @@ private:
QHash<EntityItemID, EntityItemPointer> _entitiesInScene; QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
// For Scene.shouldRenderEntities // For Scene.shouldRenderEntities
QList<EntityItemID> _entityIDsLastInScene; QList<EntityItemID> _entityIDsLastInScene;
static int _entitiesScriptEngineCount;
}; };

View file

@ -414,7 +414,13 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
} }
} }
void EntityScriptingInterface::setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) {
std::lock_guard<std::mutex> lock(_entitiesScriptEngineLock);
_entitiesScriptEngine = engine;
}
void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) { void EntityScriptingInterface::callEntityMethod(QUuid id, const QString& method, const QStringList& params) {
std::lock_guard<std::mutex> lock(_entitiesScriptEngineLock);
if (_entitiesScriptEngine) { if (_entitiesScriptEngine) {
EntityItemID entityID{ id }; EntityItemID entityID{ id };
_entitiesScriptEngine->callEntityScriptMethod(entityID, method, params); _entitiesScriptEngine->callEntityScriptMethod(entityID, method, params);

View file

@ -71,7 +71,7 @@ public:
void setEntityTree(EntityTreePointer modelTree); void setEntityTree(EntityTreePointer modelTree);
EntityTreePointer getEntityTree() { return _entityTree; } EntityTreePointer getEntityTree() { return _entityTree; }
void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine) { _entitiesScriptEngine = engine; } void setEntitiesScriptEngine(EntitiesScriptEngineProvider* engine);
float calculateCost(float mass, float oldVelocity, float newVelocity); float calculateCost(float mass, float oldVelocity, float newVelocity);
public slots: public slots:
@ -214,6 +214,8 @@ private:
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard); bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
EntityTreePointer _entityTree; EntityTreePointer _entityTree;
std::mutex _entitiesScriptEngineLock;
EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr }; EntitiesScriptEngineProvider* _entitiesScriptEngine { nullptr };
bool _bidOnSimulationOwnership { false }; bool _bidOnSimulationOwnership { false };

View file

@ -83,11 +83,18 @@ protected:
}; };
friend class OffscreenQmlSurface; friend class OffscreenQmlSurface;
QJsonObject getGLContextData();
Queue _queue; Queue _queue;
QMutex _mutex; QMutex _mutex;
QWaitCondition _waitCondition; QWaitCondition _waitCondition;
std::atomic<bool> _rendering { false }; std::atomic<bool> _rendering { false };
QJsonObject _glData;
QMutex _glMutex;
QWaitCondition _glWait;
private: private:
// Event-driven methods // Event-driven methods
void init(); void init();
@ -211,22 +218,31 @@ void OffscreenQmlRenderThread::setupFbo() {
} }
} }
QJsonObject OffscreenQmlRenderThread::getGLContextData() {
_glMutex.lock();
if (_glData.isEmpty()) {
_glWait.wait(&_glMutex);
}
_glMutex.unlock();
return _glData;
}
void OffscreenQmlRenderThread::init() { void OffscreenQmlRenderThread::init() {
qDebug() << "Initializing QML Renderer"; qDebug() << "Initializing QML Renderer";
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
if (!_canvas.makeCurrent()) { if (!_canvas.makeCurrent()) {
qWarning("Failed to make context current on QML Renderer Thread"); qWarning("Failed to make context current on QML Renderer Thread");
_quit = true; _quit = true;
return; return;
} }
// Expose GL data to QML _glMutex.lock();
auto glData = getGLContextData(); _glData = ::getGLContextData();
auto setGL = [=]{ _surface->getRootContext()->setContextProperty("GL", glData); }; _glMutex.unlock();
_surface->executeOnUiThread(setGL); _glWait.wakeAll();
connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender);
connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate);
_renderControl->initialize(_canvas.getContext()); _renderControl->initialize(_canvas.getContext());
setupFbo(); setupFbo();
@ -386,14 +402,16 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
_qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController()); _qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController());
} }
_qmlEngine->rootContext()->setContextProperty("GL", _renderer->getGLContextData());
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
_qmlComponent = new QQmlComponent(_qmlEngine);
// When Quick says there is a need to render, we will not render immediately. Instead, // When Quick says there is a need to render, we will not render immediately. Instead,
// a timer with a small interval is used to get better performance. // a timer with a small interval is used to get better performance.
_updateTimer.setInterval(MIN_TIMER_MS);
QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick);
QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit); QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit);
_updateTimer.setInterval(MIN_TIMER_MS);
_updateTimer.start(); _updateTimer.start();
_qmlComponent = new QQmlComponent(_qmlEngine);
_qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow()));
} }
void OffscreenQmlSurface::resize(const QSize& newSize_) { void OffscreenQmlSurface::resize(const QSize& newSize_) {

View file

@ -13,10 +13,10 @@
<@if GLPROFILE == PC_GL @> <@if GLPROFILE == PC_GL @>
<@def GPU_FEATURE_PROFILE GPU_CORE@> <@def GPU_FEATURE_PROFILE GPU_CORE@>
<@def VERSION_HEADER //410 core@> <@def VERSION_HEADER //PC 410 core@>
<@elif GLPROFILE == MAC_GL @> <@elif GLPROFILE == MAC_GL @>
<@def GPU_FEATURE_PROFILE GPU_CORE@> <@def GPU_FEATURE_PROFILE GPU_CORE@>
<@def VERSION_HEADER //410 core@> <@def VERSION_HEADER //MAC 410 core@>
<@else@> <@else@>
<@def GPU_FEATURE_PROFILE GPU_CORE@> <@def GPU_FEATURE_PROFILE GPU_CORE@>
<@def VERSION_HEADER //410 core@> <@def VERSION_HEADER //410 core@>

View file

@ -560,7 +560,7 @@ void GLBackend::resetStages() {
#define ADD_COMMAND_GL(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size()); #define ADD_COMMAND_GL(call) _commands.push_back(COMMAND_##call); _commandOffsets.push_back(_params.size());
#ifdef GPU_STEREO_CAMERA_BUFFER #ifdef GPU_STEREO_CAMERA_BUFFER
#define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(isStereo(), shaderUniformLoc) #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo())
#else #else
#define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc #define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc
#endif #endif
@ -577,7 +577,7 @@ void Batch::_glActiveBindTexture(GLenum unit, GLenum target, GLuint texture) {
void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) { void GLBackend::do_glActiveBindTexture(Batch& batch, size_t paramOffset) {
glActiveTexture(batch._params[paramOffset + 2]._uint); glActiveTexture(batch._params[paramOffset + 2]._uint);
glBindTexture( glBindTexture(
batch._params[paramOffset + 1]._uint, GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._uint),
batch._params[paramOffset + 0]._uint); batch._params[paramOffset + 0]._uint);
(void) CHECK_GL_ERROR(); (void) CHECK_GL_ERROR();

View file

@ -198,25 +198,18 @@ public:
~GLShader(); ~GLShader();
ShaderObjects _shaderObjects; ShaderObjects _shaderObjects;
UniformMappingVersions _uniformMappings; UniformMappingVersions _uniformMappings;
GLuint getProgram(bool isStereo) const { GLuint getProgram(Version version = Mono) const {
if (isStereo && _shaderObjects[Stereo].glprogram) { return _shaderObjects[version].glprogram;
return _shaderObjects[Stereo].glprogram;
} else {
return _shaderObjects[Mono].glprogram;
}
} }
GLint getUniformLocation(bool isStereo, GLint srcLoc) { GLint getUniformLocation(GLint srcLoc, Version version = Mono) {
if (isStereo) { // THIS will be used in the next PR
if (srcLoc >= 0) { // return _uniformMappings[version][srcLoc];
return _uniformMappings[0][srcLoc];
}
}
return srcLoc; return srcLoc;
} }
}; };
static GLShader* syncGPUObject(const Shader& shader); static GLShader* syncGPUObject(const Shader& shader);

View file

@ -85,7 +85,12 @@ void GLBackend::do_setPipeline(Batch& batch, size_t paramOffset) {
// check the program cache // check the program cache
// pick the program version // pick the program version
#ifdef GPU_STEREO_CAMERA_BUFFER
GLuint glprogram = pipelineObject->_program->getProgram(isStereo()); GLuint glprogram = pipelineObject->_program->getProgram(isStereo());
#else
GLuint glprogram = pipelineObject->_program->getProgram();
#endif
if (_pipeline._program != glprogram) { if (_pipeline._program != glprogram) {
_pipeline._program = glprogram; _pipeline._program = glprogram;
_pipeline._programShader = pipelineObject->_program; _pipeline._programShader = pipelineObject->_program;

View file

@ -42,8 +42,8 @@ bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const s
} }
// Assign the source // Assign the source
const int NUM_SOURCE_STRINGS = 3; const int NUM_SOURCE_STRINGS = 2;
const GLchar* srcstr[] = { "#version 430 core\n", defines.c_str(), shaderSource.c_str() }; const GLchar* srcstr[] = { defines.c_str(), shaderSource.c_str() };
glShaderSource(glshader, NUM_SOURCE_STRINGS, srcstr, NULL); glShaderSource(glshader, NUM_SOURCE_STRINGS, srcstr, NULL);
// Compile ! // Compile !
@ -297,6 +297,11 @@ GLBackend::GLShader* compileBackendShader(const Shader& shader) {
// Any GLSLprogram ? normally yes... // Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode(); const std::string& shaderSource = shader.getSource().getCode();
// GLSL version
const std::string glslVersion = {
"#version 410 core"
};
// Shader domain // Shader domain
const int NUM_SHADER_DOMAINS = 2; const int NUM_SHADER_DOMAINS = 2;
const GLenum SHADER_DOMAINS[NUM_SHADER_DOMAINS] = { const GLenum SHADER_DOMAINS[NUM_SHADER_DOMAINS] = {
@ -327,13 +332,12 @@ GLBackend::GLShader* compileBackendShader(const Shader& shader) {
stereoVersion stereoVersion
}; };
GLBackend::GLShader::ShaderObjects shaderObjects; GLBackend::GLShader::ShaderObjects shaderObjects;
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) { for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
auto& shaderObject = shaderObjects[version]; auto& shaderObject = shaderObjects[version];
std::string shaderDefines = domainDefines[shader.getType()] + "\n" + versionDefines[version]; std::string shaderDefines = glslVersion + "\n" + domainDefines[shader.getType()] + "\n" + versionDefines[version];
bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram); bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram);
if (!result) { if (!result) {
@ -349,7 +353,7 @@ GLBackend::GLShader* compileBackendShader(const Shader& shader) {
} }
GLBackend::GLShader* compileBackendProgram(const Shader& program) { GLBackend::GLShader* compileBackendProgram(const Shader& program) {
if(!program.isProgram()) { if (!program.isProgram()) {
return nullptr; return nullptr;
} }
@ -380,7 +384,6 @@ GLBackend::GLShader* compileBackendProgram(const Shader& program) {
makeProgramBindings(programObject); makeProgramBindings(programObject);
} }
// So far so good, the program versions have all been created successfully // So far so good, the program versions have all been created successfully
GLBackend::GLShader* object = new GLBackend::GLShader(); GLBackend::GLShader* object = new GLBackend::GLShader();
object->_shaderObjects = programObjects; object->_shaderObjects = programObjects;
@ -397,14 +400,14 @@ GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) {
} }
// need to have a gpu object? // need to have a gpu object?
if (shader.isProgram()) { if (shader.isProgram()) {
GLShader* tempObject = compileBackendProgram(shader); GLShader* tempObject = compileBackendProgram(shader);
if (tempObject) { if (tempObject) {
object = tempObject; object = tempObject;
Backend::setGPUObject(shader, object); Backend::setGPUObject(shader, object);
} }
} else if (shader.isDomain()) { } else if (shader.isDomain()) {
GLShader* tempObject = compileBackendShader(shader); GLShader* tempObject = compileBackendShader(shader);
if (tempObject) { if (tempObject) {
object = tempObject; object = tempObject;
Backend::setGPUObject(shader, object); Backend::setGPUObject(shader, object);
} }
@ -765,7 +768,7 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin
return false; return false;
} }
// APply bindings to all program versions and generate list of slots from default version // Apply bindings to all program versions and generate list of slots from default version
for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) { for (int version = 0; version < GLBackend::GLShader::NumVersions; version++) {
auto& shaderObject = object->_shaderObjects[version]; auto& shaderObject = object->_shaderObjects[version];
if (shaderObject.glprogram) { if (shaderObject.glprogram) {
@ -796,6 +799,7 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin
} }
} }
return true; return true;
} }

View file

@ -18,6 +18,7 @@
#include <QtNetwork/QNetworkReply> #include <QtNetwork/QNetworkReply>
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <QtScript/QScriptValue> #include <QtScript/QScriptValue>
#include <QtScript/QScriptValueIterator>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <AudioConstants.h> #include <AudioConstants.h>
@ -143,7 +144,6 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
ScriptEngine::~ScriptEngine() { ScriptEngine::~ScriptEngine() {
qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename(); qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename();
auto scriptEngines = DependencyManager::get<ScriptEngines>(); auto scriptEngines = DependencyManager::get<ScriptEngines>();
if (scriptEngines) { if (scriptEngines) {
scriptEngines->removeScriptEngine(this); scriptEngines->removeScriptEngine(this);
@ -1047,39 +1047,25 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin
// since all of these operations can be asynch we will always do the actual work in the response handler // since all of these operations can be asynch we will always do the actual work in the response handler
// for the download // for the download
void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) { void ScriptEngine::loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
if (QThread::currentThread() != thread()) { // NOTE: If the script content is not currently in the cache, the LAMBDA here will be called on the Main Thread
// which means we're guaranteed that it's not the correct thread for the ScriptEngine. This means
// when we get into entityScriptContentAvailable() we will likely invokeMethod() to get it over
// to the "Entities" ScriptEngine thread.
DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [theEngine, entityID](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
QSharedPointer<ScriptEngine> strongEngine = theEngine.toStrongRef();
if (strongEngine) {
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
qDebug() << "*** WARNING *** ScriptEngine::loadEntityScript() called on wrong thread [" qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
<< QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]";
"entityID:" << entityID << "entityScript:" << entityScript <<"forceRedownload:" << forceRedownload;
#endif #endif
strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
QMetaObject::invokeMethod(this, "loadEntityScript", } else {
Q_ARG(const EntityItemID&, entityID), // FIXME - I'm leaving this in for testing, so that QA can confirm that sometimes the script contents
Q_ARG(const QString&, entityScript), // returns after the ScriptEngine has been deleted, we can remove this after QA verifies the
Q_ARG(bool, forceRedownload)); // repro case.
return; qDebug() << "ScriptCache::getScriptContents() returned after our ScriptEngine was deleted... script:" << scriptOrURL;
} }
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::loadEntityScript() called on correct thread [" << thread() << "] "
"entityID:" << entityID << "entityScript:" << entityScript << "forceRedownload:" << forceRedownload;
#endif
// If we've been called our known entityScripts should not know about us..
assert(!_entityScripts.contains(entityID));
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::loadEntityScript() calling scriptCache->getScriptContents() on thread ["
<< QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
DependencyManager::get<ScriptCache>()->getScriptContents(entityScript, [=](const QString& scriptOrURL, const QString& contents, bool isURL, bool success) {
#ifdef THREAD_DEBUGGING
qDebug() << "ScriptEngine::entityScriptContentAvailable() IN LAMBDA contentAvailable on thread ["
<< QThread::currentThread() << "] expected thread [" << thread() << "]";
#endif
this->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success);
}, forceRedownload); }, forceRedownload);
} }
@ -1213,6 +1199,16 @@ void ScriptEngine::unloadAllEntityScripts() {
callEntityScriptMethod(entityID, "unload"); callEntityScriptMethod(entityID, "unload");
} }
_entityScripts.clear(); _entityScripts.clear();
#ifdef DEBUG_ENGINE_STATE
qDebug() << "---- CURRENT STATE OF ENGINE: --------------------------";
QScriptValueIterator it(globalObject());
while (it.hasNext()) {
it.next();
qDebug() << it.name() << ":" << it.value().toString();
}
qDebug() << "--------------------------------------------------------";
#endif // DEBUG_ENGINE_STATE
} }
void ScriptEngine::refreshFileScript(const EntityItemID& entityID) { void ScriptEngine::refreshFileScript(const EntityItemID& entityID) {

View file

@ -124,7 +124,7 @@ public:
Q_INVOKABLE QUrl resolvePath(const QString& path) const; Q_INVOKABLE QUrl resolvePath(const QString& path) const;
// Entity Script Related methods // Entity Script Related methods
Q_INVOKABLE void loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload = false); // will call the preload method once loaded static void loadEntityScript(QWeakPointer<ScriptEngine> theEngine, const EntityItemID& entityID, const QString& entityScript, bool forceRedownload);
Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method
Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void unloadAllEntityScripts();
Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList());