diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 90c5d32449..ffd7cc703b 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -386,8 +386,11 @@ bool AudioMixer::prepareMixForListeningNode(Node* node) { // loop through all other nodes that have sufficient audio to mix DependencyManager::get()->eachNode([&](const SharedNodePointer& otherNode){ - // make sure that we have audio data for this other node and that it isn't being ignored by our listening node - if (otherNode->getLinkedData() && !node->isIgnoringNodeWithID(otherNode->getUUID())) { + // make sure that we have audio data for this other node + // and that it isn't being ignored by our listening node + // and that it isn't ignoring our listening node + if (otherNode->getLinkedData() + && !node->isIgnoringNodeWithID(otherNode->getUUID()) && !otherNode->isIgnoringNodeWithID(node->getUUID())) { AudioMixerClientData* otherNodeClientData = (AudioMixerClientData*) otherNode->getLinkedData(); // enumerate the ARBs attached to the otherNode and add all that should be added to mix diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 041449dc57..2c9fadc7b1 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -230,9 +230,11 @@ void AvatarMixer::broadcastAvatarData() { [&](const SharedNodePointer& otherNode)->bool { // make sure we have data for this avatar, that it isn't the same node, // and isn't an avatar that the viewing node has ignored + // or that has ignored the viewing node if (!otherNode->getLinkedData() || otherNode->getUUID() == node->getUUID() - || node->isIgnoringNodeWithID(otherNode->getUUID())) { + || node->isIgnoringNodeWithID(otherNode->getUUID()) + || otherNode->isIgnoringNodeWithID(node->getUUID())) { return false; } else { return true; diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index abe7ed176a..051465efd2 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -109,7 +109,14 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer(node->getLinkedData()); nodeData->setSendingSockAddr(message->getSenderSockAddr()); - nodeData->setNodeInterestSet(nodeConnection.interestList.toSet()); + + // guard against patched agents asking to hear about other agents + auto safeInterestSet = nodeConnection.interestList.toSet(); + if (nodeConnection.nodeType == NodeType::Agent) { + safeInterestSet.remove(NodeType::Agent); + } + + nodeData->setNodeInterestSet(safeInterestSet); nodeData->setPlaceName(nodeConnection.placeName); // signal that we just connected a node so the DomainServer can get it a list diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5208cb2326..6668ed54dc 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -843,7 +843,14 @@ void DomainServer::processListRequestPacket(QSharedPointer mess // update the NodeInterestSet in case there have been any changes DomainServerNodeData* nodeData = reinterpret_cast(sendingNode->getLinkedData()); - nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet()); + + // guard against patched agents asking to hear about other agents + auto safeInterestSet = nodeRequestData.interestList.toSet(); + if (sendingNode->getType() == NodeType::Agent) { + safeInterestSet.remove(NodeType::Agent); + } + + nodeData->setNodeInterestSet(safeInterestSet); // update the connecting hostname in case it has changed nodeData->setPlaceName(nodeRequestData.placeName); @@ -950,7 +957,8 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ - if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) { + if (otherNode->getUUID() != node->getUUID() + && nodeInterestSet.contains(otherNode->getType())) { // since we're about to add a node to the packet we start a segment domainListPackets->startSegment(); diff --git a/interface/resources/images/steam-min-spec-failed.png b/interface/resources/images/steam-min-spec-failed.png index 99abac9e1c..0061167a92 100644 Binary files a/interface/resources/images/steam-min-spec-failed.png and b/interface/resources/images/steam-min-spec-failed.png differ diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 792dcb4a40..cedfb3f185 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -37,7 +37,17 @@ static int cameraModeId = qRegisterMetaType(); class Camera : public QObject { Q_OBJECT - + + /**jsdoc + * @namespace Camera + * @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 mode 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) @@ -47,13 +57,13 @@ class Camera : public QObject { public: Camera(); - void initialize(); // instantly put the camera at the ideal position and orientation. + void initialize(); // instantly put the camera at the ideal position and orientation. void update( float deltaTime ); CameraMode getMode() const { return _mode; } void setMode(CameraMode m); - + void loadViewFrustum(ViewFrustum& frustum) const; ViewFrustum toViewFrustum() const; @@ -80,20 +90,44 @@ public slots: 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 + * @param {float} x X-coordinate on screen. + * @param {float} y Y-coordinate on screen. + * @return {PickRay} The computed {PickRay}. + */ PickRay computePickRay(float x, float y); - // These only work on independent cameras - /// one time change to what the camera is looking at - void lookAt(const glm::vec3& value); + /**jsdoc + * Set the camera to look at position position. Only works while in independent. + * camera mode. + * @function Camera.lookAt + * @param {Vec3} Position Position to look at. + */ + void lookAt(const glm::vec3& position); - /// fix what the camera is looking at, and keep the camera looking at this even if position changes - void keepLookingAt(const glm::vec3& value); + /**jsdoc + * Set the camera to continue looking at position position. + * Only works while in `independent` camera mode. + * @function Camera.keepLookingAt + * @param {Vec3} position Position to keep looking at. + */ + void keepLookingAt(const glm::vec3& position); - /// stops the keep looking at feature, doesn't change what's being looked at, but will stop camera from - /// continuing to update it's orientation to keep looking at the item + /**jsdoc + * Stops the camera from continually looking at a position that was set with + * `keepLookingAt` + * @function Camera.stopLookingAt + */ void stopLooking() { _isKeepLookingAt = false; } signals: + /**jsdoc + * Triggered when camera mode has changed. + * @function Camera.modeUpdated + * @return {Signal} + */ void modeUpdated(const QString& newMode); private: diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h index 4737a194df..3c10475242 100644 --- a/interface/src/scripting/ClipboardScriptingInterface.h +++ b/interface/src/scripting/ClipboardScriptingInterface.h @@ -13,6 +13,9 @@ #include +/**jsdoc + * @namespace Clipboard + */ class ClipboardScriptingInterface : public QObject { Q_OBJECT public: @@ -21,13 +24,47 @@ public: signals: void readyToImport(); -public slots: - glm::vec3 getContentsDimensions(); /// returns the overall dimensions of everything on the blipboard - float getClipboardContentsLargestDimension(); /// returns the largest dimension of everything on the clipboard - bool importEntities(const QString& filename); - bool exportEntities(const QString& filename, const QVector& entityIDs); - bool exportEntities(const QString& filename, float x, float y, float z, float s); - QVector pasteEntities(glm::vec3 position); +public: + /**jsdoc + * @function Clipboard.getContentsDimensions + * @return {Vec3} The extents of the contents held in the clipboard. + */ + Q_INVOKABLE glm::vec3 getContentsDimensions(); + + /**jsdoc + * Compute largest dimension of the extents of the contents held in the clipboard + * @function Clipboard.getClipboardContentsLargestDimension + * @return {float} The largest dimension computed. + */ + Q_INVOKABLE float getClipboardContentsLargestDimension(); + + /**jsdoc + * Import entities from a .json file containing entity data into the clipboard. + * You can generate * a .json file using {Clipboard.exportEntities}. + * @function Clipboard.importEntities + * @param {string} filename Filename of file to import. + * @return {bool} True if the import was succesful, otherwise false. + */ + Q_INVOKABLE bool importEntities(const QString& filename); + + /**jsdoc + * Export the entities listed in `entityIDs` to the file `filename` + * @function Clipboard.exportEntities + * @param {string} filename Path to the file to export entities to. + * @param {EntityID[]} entityIDs IDs of entities to export. + * @return {bool} True if the export was succesful, otherwise false. + */ + Q_INVOKABLE bool exportEntities(const QString& filename, const QVector& entityIDs); + Q_INVOKABLE bool exportEntities(const QString& filename, float x, float y, float z, float s); + + /**jsdoc + * Paste the contents of the clipboard into the world. + * @function Clipboard.pasteEntities + * @param {Vec3} position Position to paste clipboard at. + * @return {EntityID[]} Array of entity IDs for the new entities that were + * created as a result of the paste operation. + */ + Q_INVOKABLE QVector pasteEntities(glm::vec3 position); }; #endif // hifi_ClipboardScriptingInterface_h diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 3d46113611..7cef753de9 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -56,6 +56,9 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results); +/**jsdoc + * @namespace Entities + */ /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT @@ -87,40 +90,90 @@ public: ActivityTracking getActivityTracking() const { return _activityTracking; } public slots: - // returns true if the DomainServer will allow this Node/Avatar to make changes + /**jsdoc + * Returns `true` if the DomainServer will allow this Node/Avatar to make changes + * + * @function Entities.canAdjustLocks + * @return {bool} `true` if the client can adjust locks, `false` if not. + */ Q_INVOKABLE bool canAdjustLocks(); - // returns true if the DomainServer will allow this Node/Avatar to rez new entities + /**jsdoc + * @function Entities.canRez + * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new entities + */ Q_INVOKABLE bool canRez(); + + /**jsdoc + * @function Entities.canRezTmp + * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new temporary entities + */ Q_INVOKABLE bool canRezTmp(); - /// adds a model with the specific properties + /**jsdoc + * Add a new entity with the specified properties. If `clientOnly` is true, the entity will + * not be sent to the server and will only be visible/accessible on the local client. + * + * @function Entities.addEntity + * @param {EntityItemProperties} properties Properties of the entity to create. + * @param {bool} [clientOnly=false] Whether the entity should only exist locally or not. + * @return {EntityID} The entity ID of the newly created entity. The ID will be a null + * UUID (`{00000000-0000-0000-0000-000000000000}`) if the entity could not be created. + */ Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); /// temporary method until addEntity can be used from QJSEngine + /// Deliberately not adding jsdoc, only used internally. Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const QString& shapeType, bool dynamic, const glm::vec3& position, const glm::vec3& gravity); - /// gets the current model properties for a specific model - /// this function will not find return results in script engine contexts which don't have access to models + /**jsdoc + * Return the properties for the specified {EntityID}. + * not be sent to the server and will only be visible/accessible on the local client. + * @param {EntityItemProperties} properties Properties of the entity to create. + * @param {EntityPropertyFlags} [desiredProperties=[]] Array containing the names of the properties you + * would like to get. If the array is empty, all properties will be returned. + * @return {EntityItemProperties} The entity properties for the specified entity. + */ Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID); Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties); - /// edits a model updating only the included properties, will return the identified EntityItemID in case of - /// successful edit, if the input entityID is for an unknown model this function will have no effect + /**jsdoc + * Updates an entity with the specified properties. + * + * @function Entities.editEntity + * @return {EntityID} The EntityID of the entity if the edit was successful, otherwise the null {EntityID}. + */ Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties); - /// deletes a model + /**jsdoc + * Deletes an entity. + * + * @function Entities.deleteEntity + * @param {EntityID} entityID The ID of the entity to delete. + */ Q_INVOKABLE void deleteEntity(QUuid entityID); /// Allows a script to call a method on an entity's script. The method will execute in the entity script /// engine. If the entity does not have an entity script or the method does not exist, this call will have /// no effect. + /**jsdoc + * Call a method on an entity. If it is running an entity script (specified by the `script` property) + * and it exposes a property with the specified name `method`, it will be called + * using `params` as the list of arguments. + * + * @function Entities.callEntityMethod + * @param {EntityID} entityID The ID of the entity to call the method on. + * @param {string} method The name of the method to call. + * @param {string[]} params The list of parameters to call the specified method with. + */ Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); /// finds the closest model to the center point, within the radius /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models + /**jsdoc + */ Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; /// finds models within the search sphere specified by the center point and radius diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp index 428bf86c6f..f24a9bb932 100644 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ b/libraries/gl/src/gl/OpenGLVersionChecker.cpp @@ -50,7 +50,8 @@ QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { valid = true; override = false; - QGLWidget* glWidget = new QGLWidget(); + QGLWidget* glWidget = new QGLWidget(getDefaultGLFormat()); + valid = glWidget->isValid(); // Inform user if no OpenGL support if (!valid) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 3513d7a05b..719989a07a 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -307,11 +307,20 @@ void GLBackend::render(const Batch& batch) { renderPassTransfer(batch); } +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + if (_stereo._enable) { + glEnable(GL_CLIP_DISTANCE0); + } +#endif { PROFILE_RANGE(_stereo._enable ? "Render Stereo" : "Render"); renderPassDraw(batch); } - +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + if (_stereo._enable) { + glDisable(GL_CLIP_DISTANCE0); + } +#endif // Restore the saved stereo state for the next batch _stereo._enable = savedStereo; } @@ -326,13 +335,24 @@ void GLBackend::syncCache() { glEnable(GL_LINE_SMOOTH); } +#ifdef GPU_STEREO_DRAWCALL_DOUBLED void GLBackend::setupStereoSide(int side) { ivec4 vp = _transform._viewport; vp.z /= 2; glViewport(vp.x + side * vp.z, vp.y, vp.z, vp.w); + +#ifdef GPU_STEREO_CAMERA_BUFFER +#ifdef GPU_STEREO_DRAWCALL_DOUBLED + glVertexAttribI1i(14, side); +#endif +#else _transform.bindCurrentCamera(side); +#endif + } +#else +#endif void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); @@ -385,8 +405,11 @@ void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { // term strategy is to get rid of any GL calls in favor of the HIFI GPU API // As long as we don;t use several versions of shaders we can avoid this more complex code path -// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo()); +#ifdef GPU_STEREO_CAMERA_BUFFER +#define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, (GLShader::Version) isStereo()) +#else #define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc +#endif void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 417b090d31..42557ddd97 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -29,6 +29,29 @@ #include "GLShared.h" + +// Different versions for the stereo drawcall +// Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only +//#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +//#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER +#define GPU_STEREO_TECHNIQUE_INSTANCED + + +// Let these be configured by the one define picked above +#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#define GPU_STEREO_DRAWCALL_DOUBLED +#endif + +#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER +#define GPU_STEREO_DRAWCALL_DOUBLED +#define GPU_STEREO_CAMERA_BUFFER +#endif + +#ifdef GPU_STEREO_TECHNIQUE_INSTANCED +#define GPU_STEREO_DRAWCALL_INSTANCED +#define GPU_STEREO_CAMERA_BUFFER +#endif + namespace gpu { namespace gl { class GLBackend : public Backend, public std::enable_shared_from_this { @@ -204,7 +227,10 @@ protected: void renderPassTransfer(const Batch& batch); void renderPassDraw(const Batch& batch); + +#ifdef GPU_STEREO_DRAWCALL_DOUBLED void setupStereoSide(int side); +#endif virtual void initInput() final; virtual void killInput() final; @@ -262,7 +288,19 @@ protected: }; struct TransformStageState { +#ifdef GPU_STEREO_CAMERA_BUFFER + struct Cameras { + TransformCamera _cams[2]; + + Cameras() {}; + Cameras(const TransformCamera& cam) { memcpy(_cams, &cam, sizeof(TransformCamera)); }; + Cameras(const TransformCamera& camL, const TransformCamera& camR) { memcpy(_cams, &camL, sizeof(TransformCamera)); memcpy(_cams + 1, &camR, sizeof(TransformCamera)); }; + }; + + using CameraBufferElement = Cameras; +#else using CameraBufferElement = TransformCamera; +#endif using TransformCameras = std::vector; TransformCamera _camera; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp index 9256a42b80..618988a217 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendInput.cpp @@ -187,7 +187,11 @@ void GLBackend::updateInput() { glVertexAttribFormat(slot + locNum, count, type, isNormalized, offset + locNum * perLocationSize); glVertexAttribBinding(slot + locNum, attrib._channel); } +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexBindingDivisor(attrib._channel, attrib._frequency * (isStereo() ? 2 : 1)); +#else glVertexBindingDivisor(attrib._channel, attrib._frequency); +#endif } (void)CHECK_GL_ERROR(); } @@ -306,7 +310,11 @@ void GLBackend::updateInput() { for (size_t locNum = 0; locNum < locationCount; ++locNum) { glVertexAttribPointer(slot + (GLuint)locNum, count, type, isNormalized, stride, reinterpret_cast(pointer + perLocationStride * (GLuint)locNum)); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1)); +#else glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency); +#endif } // TODO: Support properly the IAttrib version diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index ed0b9607e6..81d38c3339 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -48,7 +48,13 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { // check the program cache // pick the program version + // check the program cache + // pick the program version +#ifdef GPU_STEREO_CAMERA_BUFFER + GLuint glprogram = pipelineObject->_program->getProgram((GLShader::Version) isStereo()); +#else GLuint glprogram = pipelineObject->_program->getProgram(); +#endif if (_pipeline._program != glprogram) { _pipeline._program = glprogram; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp index 7f821078cd..eb53f5d45b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp @@ -31,10 +31,25 @@ void GLBackend::do_setProjectionTransform(const Batch& batch, size_t paramOffset void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) { memcpy(&_transform._viewport, batch.readData(batch._params[paramOffset]._uint), sizeof(Vec4i)); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + { + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); + + // Where we assign the GL viewport + if (_stereo._enable) { + vp.z /= 2; + if (_stereo._pass) { + vp.x += vp.z; + } + } + } +#else if (!_inRenderTransferPass && !isStereo()) { ivec4& vp = _transform._viewport; glViewport(vp.x, vp.y, vp.z, vp.w); } +#endif // The Viewport is tagged invalid because the CameraTransformUBO is not up to date and will need update on next drawcall _transform._invalidViewport = true; @@ -100,13 +115,21 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView || _invalidProj || _invalidViewport) { size_t offset = _cameraUboSize * _cameras.size(); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); - if (stereo._enable) { - _cameras.push_back((_camera.getEyeCamera(0, stereo, _view))); - _cameras.push_back((_camera.getEyeCamera(1, stereo, _view))); - } else { - _cameras.push_back((_camera.recomputeDerived(_view))); - } + if (stereo._enable) { +#ifdef GPU_STEREO_CAMERA_BUFFER + _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); +#else + _cameras.push_back((_camera.getEyeCamera(0, stereo, _view))); + _cameras.push_back((_camera.getEyeCamera(1, stereo, _view))); +#endif + } else { +#ifdef GPU_STEREO_CAMERA_BUFFER + _cameras.push_back(CameraBufferElement(_camera.recomputeDerived(_view))); +#else + _cameras.push_back((_camera.recomputeDerived(_view))); +#endif + } } // Flags are clean @@ -122,9 +145,13 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta } if (offset != INVALID_OFFSET) { +#ifdef GPU_STEREO_CAMERA_BUFFER + bindCurrentCamera(0); +#else if (!stereo._enable) { bindCurrentCamera(0); } +#endif } (void)CHECK_GL_ERROR(); } @@ -148,7 +175,11 @@ void GLBackend::updateTransform(const Batch& batch) { glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, _transform._drawCallInfoOffsets[batch._currentNamedCall]); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, (isStereo() ? 2 : 1)); +#else glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1); +#endif } (void)CHECK_GL_ERROR(); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp index f8d493c25b..23439a640a 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.cpp @@ -54,9 +54,24 @@ static const std::array DOMAIN_DEFINES { { "#define GPU_GEOMETRY_SHADER", } }; +// Stereo specific defines +static const std::string stereoVersion { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" +#endif +#ifdef GPU_STEREO_DRAWCALL_DOUBLED +#ifdef GPU_STEREO_CAMERA_BUFFER + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" +#else + "#define GPU_TRANSFORM_IS_STEREO" +#endif +#endif +}; + // Versions specific of the shader static const std::array VERSION_DEFINES { { - "" + "", + stereoVersion } }; GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) { @@ -181,7 +196,8 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin // Define the public slots only from the default version if (version == 0) { shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs); - } else { + } // else + { GLShader::UniformMapping mapping; for (auto srcUniform : shader.getUniforms()) { mapping[srcUniform._location] = uniforms.findLocation(srcUniform._name); diff --git a/libraries/gpu-gl/src/gpu/gl/GLShader.h b/libraries/gpu-gl/src/gpu/gl/GLShader.h index e75e96cf16..40dd0b7be9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShader.h @@ -19,6 +19,8 @@ public: enum Version { Mono = 0, + Stereo, + NumVersions }; @@ -40,8 +42,8 @@ public: GLint getUniformLocation(GLint srcLoc, Version version = Mono) { // THIS will be used in the future PR as we grow the number of versions - // return _uniformMappings[version][srcLoc]; - return srcLoc; + return _uniformMappings[version][srcLoc]; + // return srcLoc; } const std::weak_ptr _backend; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 6c2b2f434e..a87d0ad6b8 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -25,11 +25,14 @@ void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { uint32 startVertex = batch._params[paramOffset + 0]._uint; if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else setupStereoSide(0); glDrawArrays(mode, startVertex, numVertices); setupStereoSide(1); glDrawArrays(mode, startVertex, numVertices); - +#endif _stats._DSNumTriangles += 2 * numVertices / 3; _stats._DSNumDrawcalls += 2; @@ -55,11 +58,14 @@ void GL41Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, 2); +#else setupStereoSide(0); glDrawElements(mode, numIndices, glType, indexBufferByteOffset); setupStereoSide(1); glDrawElements(mode, numIndices, glType, indexBufferByteOffset); - +#endif _stats._DSNumTriangles += 2 * numIndices / 3; _stats._DSNumDrawcalls += 2; } else { @@ -83,11 +89,14 @@ void GL41Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) { if (isStereo()) { GLint trueNumInstances = 2 * numInstances; +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, trueNumInstances); +#else setupStereoSide(0); - glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); setupStereoSide(1); - glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); - + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); +#endif _stats._DSNumTriangles += (trueNumInstances * numVertices) / 3; _stats._DSNumDrawcalls += trueNumInstances; } else { @@ -124,10 +133,14 @@ void GL41Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset if (isStereo()) { GLint trueNumInstances = 2 * numInstances; +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, trueNumInstances, 0, startInstance); +#else setupStereoSide(0); glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); setupStereoSide(1); glbackend_glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); +#endif _stats._DSNumTriangles += (trueNumInstances * numIndices) / 3; _stats._DSNumDrawcalls += trueNumInstances; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index bae6326e8f..d7dde8b7d6 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -25,10 +25,14 @@ void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { uint32 startVertex = batch._params[paramOffset + 0]._uint; if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else setupStereoSide(0); glDrawArrays(mode, startVertex, numVertices); setupStereoSide(1); glDrawArrays(mode, startVertex, numVertices); +#endif _stats._DSNumTriangles += 2 * numVertices / 3; _stats._DSNumDrawcalls += 2; @@ -55,11 +59,14 @@ void GL45Backend::do_drawIndexed(const Batch& batch, size_t paramOffset) { GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, 2); +#else setupStereoSide(0); glDrawElements(mode, numIndices, glType, indexBufferByteOffset); setupStereoSide(1); glDrawElements(mode, numIndices, glType, indexBufferByteOffset); - +#endif _stats._DSNumTriangles += 2 * numIndices / 3; _stats._DSNumDrawcalls += 2; } else { @@ -83,10 +90,14 @@ void GL45Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) { if (isStereo()) { GLint trueNumInstances = 2 * numInstances; +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, trueNumInstances); +#else setupStereoSide(0); glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); setupStereoSide(1); glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); +#endif _stats._DSNumTriangles += (trueNumInstances * numVertices) / 3; _stats._DSNumDrawcalls += trueNumInstances; @@ -112,10 +123,15 @@ void GL45Backend::do_drawIndexedInstanced(const Batch& batch, size_t paramOffset if (isStereo()) { GLint trueNumInstances = 2 * numInstances; + +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, trueNumInstances, 0, startInstance); +#else setupStereoSide(0); glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); setupStereoSide(1); glDrawElementsInstancedBaseVertexBaseInstance(mode, numIndices, glType, indexBufferByteOffset, numInstances, 0, startInstance); +#endif _stats._DSNumTriangles += (trueNumInstances * numIndices) / 3; _stats._DSNumDrawcalls += trueNumInstances; } else { diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 0f13a6b4d1..d4b15e1fd4 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -22,29 +22,84 @@ struct TransformCamera { }; layout(std140) uniform transformCameraBuffer { +#ifdef GPU_TRANSFORM_IS_STEREO +#ifdef GPU_TRANSFORM_STEREO_CAMERA + TransformCamera _camera[2]; +#else TransformCamera _camera; +#endif +#else + TransformCamera _camera; +#endif }; +#ifdef GPU_VERTEX_SHADER +#ifdef GPU_TRANSFORM_IS_STEREO + +#ifdef GPU_TRANSFORM_STEREO_CAMERA +#ifdef GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED +layout(location=14) in int _inStereoSide; +#endif + +flat out int _stereoSide; +#endif + +#endif + +#endif + +#ifdef GPU_PIXEL_SHADER +#ifdef GPU_TRANSFORM_STEREO_CAMERA +flat in int _stereoSide; +#endif +#endif + TransformCamera getTransformCamera() { +#ifdef GPU_TRANSFORM_IS_STEREO + #ifdef GPU_TRANSFORM_STEREO_CAMERA + #ifdef GPU_VERTEX_SHADER + #ifdef GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED + _stereoSide = _inStereoSide; + #endif + #ifdef GPU_TRANSFORM_STEREO_CAMERA_INSTANCED + _stereoSide = gl_InstanceID % 2; + #endif + #endif + return _camera[_stereoSide]; + #else + return _camera; + #endif +#else return _camera; +#endif } vec3 getEyeWorldPos() { - return _camera._viewInverse[3].xyz; + return getTransformCamera()._viewInverse[3].xyz; } - bool cam_isStereo() { +#ifdef GPU_TRANSFORM_IS_STEREO + return getTransformCamera()._stereoInfo.x > 0.0; +#else return _camera._stereoInfo.x > 0.0; +#endif } float cam_getStereoSide() { +#ifdef GPU_TRANSFORM_IS_STEREO +#ifdef GPU_TRANSFORM_STEREO_CAMERA + return getTransformCamera()._stereoInfo.y; +#else return _camera._stereoInfo.y; +#endif +#else + return _camera._stereoInfo.y; +#endif } <@endfunc@> - <@func declareStandardObjectTransform()@> struct TransformObject { mat4 _model; @@ -92,6 +147,25 @@ TransformObject getTransformObject() { <$viewport$> = <$cameraTransform$>._viewport; <@endfunc@> +<@func transformStereoClipsSpace(cameraTransform, clipPos)@> + { +#ifdef GPU_TRANSFORM_IS_STEREO + +#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN + vec4 eyeClipEdge[2]= vec4[2](vec4(-1,0,0,1), vec4(1,0,0,1)); + vec2 eyeOffsetScale = vec2(-0.5, +0.5); + uint eyeIndex = _stereoSide; + gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); + float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; + <$clipPos$>.x = newClipPosX; +#endif + +#else +#endif + } +<@endfunc@> + + <@func transformModelToEyeWorldAlignedPos(cameraTransform, objectTransform, modelPos, eyeWorldAlignedPos)@> { // _transformModelToEyeWorldAlignedPos @@ -108,6 +182,8 @@ TransformObject getTransformObject() { <$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$> <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos; + + <$transformStereoClipsSpace($cameraTransform$, $clipPos$)$> } <@endfunc@> @@ -117,6 +193,8 @@ TransformObject getTransformObject() { <$transformModelToEyeWorldAlignedPos($cameraTransform$, $objectTransform$, $modelPos$, eyeWAPos)$> <$clipPos$> = <$cameraTransform$>._projectionViewUntranslated * eyeWAPos; <$eyePos$> = vec4((<$cameraTransform$>._view * vec4(eyeWAPos.xyz, 0.0)).xyz, 1.0); + + <$transformStereoClipsSpace($cameraTransform$, $clipPos$)$> } <@endfunc@> @@ -143,11 +221,10 @@ TransformObject getTransformObject() { <@endfunc@> <@func transformModelToWorldDir(cameraTransform, objectTransform, modelDir, worldDir)@> - { // transformModelToEyeDir + { // transformModelToEyeDir vec3 mr0 = <$objectTransform$>._modelInverse[0].xyz; vec3 mr1 = <$objectTransform$>._modelInverse[1].xyz; vec3 mr2 = <$objectTransform$>._modelInverse[2].xyz; - <$worldDir$> = vec3(dot(mr0, <$modelDir$>), dot(mr1, <$modelDir$>), dot(mr2, <$modelDir$>)); } <@endfunc@> @@ -181,6 +258,8 @@ TransformObject getTransformObject() { <@func transformEyeToClipPos(cameraTransform, eyePos, clipPos)@> { // transformEyeToClipPos <$clipPos$> = <$cameraTransform$>._projection * vec4(<$eyePos$>.xyz, 1.0); + + <$transformStereoClipsSpace($cameraTransform$, $clipPos$)$> } <@endfunc@> diff --git a/libraries/model/src/model/skybox.slv b/libraries/model/src/model/skybox.slv index 5df1aa0a4a..6fd9532fa1 100755 --- a/libraries/model/src/model/skybox.slv +++ b/libraries/model/src/model/skybox.slv @@ -36,4 +36,6 @@ void main(void) { // Position is supposed to come in clip space gl_Position = vec4(inPosition.xy, 0.0, 1.0); + + <$transformStereoClipsSpace(cam, gl_Position)$> } diff --git a/libraries/render-utils/src/deferred_light_limited.slv b/libraries/render-utils/src/deferred_light_limited.slv index 0525509f0b..36e281ab5b 100644 --- a/libraries/render-utils/src/deferred_light_limited.slv +++ b/libraries/render-utils/src/deferred_light_limited.slv @@ -24,7 +24,7 @@ out vec4 _texCoord0; void main(void) { if (sphereParam.w != 0.0) { - + // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); @@ -33,9 +33,14 @@ void main(void) { vec4 projected = gl_Position / gl_Position.w; projected.xy = (projected.xy + 1.0) * 0.5; +#ifdef GPU_TRANSFORM_IS_STEREO +#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN +#else if (cam_isStereo()) { projected.x = 0.5 * (projected.x + cam_getStereoSide()); } +#endif +#endif _texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w; } else { const float depth = -1.0; //Draw at near plane @@ -47,11 +52,27 @@ void main(void) { ); vec4 pos = UNIT_QUAD[gl_VertexID]; + +#ifdef GPU_TRANSFORM_IS_STEREO +#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN + TransformCamera cam = getTransformCamera(); + <$transformStereoClipsSpace(cam, pos)$> +#endif +#endif + _texCoord0 = vec4((pos.xy + 1.0) * 0.5, 0.0, 1.0); +#ifdef GPU_TRANSFORM_IS_STEREO +#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN +#else if (cam_isStereo()) { _texCoord0.x = 0.5 * (_texCoord0.x + cam_getStereoSide()); } +#endif +#endif + gl_Position = pos; + } + } diff --git a/libraries/render-utils/src/deferred_light_spot.slv b/libraries/render-utils/src/deferred_light_spot.slv index f9e81549dd..f6dc7faaf1 100755 --- a/libraries/render-utils/src/deferred_light_spot.slv +++ b/libraries/render-utils/src/deferred_light_spot.slv @@ -57,8 +57,13 @@ void main(void) { vec4 projected = gl_Position / gl_Position.w; projected.xy = (projected.xy + 1.0) * 0.5; - if (cam_isStereo()) { - projected.x = 0.5 * (projected.x + cam_getStereoSide()); - } +#ifdef GPU_TRANSFORM_IS_STEREO +#ifdef GPU_TRANSFORM_STEREO_SPLIT_SCREEN +#else + if (cam_isStereo()) { + projected.x = 0.5 * (projected.x + cam_getStereoSide()); + } +#endif +#endif _texCoord0 = vec4(projected.xy, 0.0, 1.0) * gl_Position.w; } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0becf6a8dd..070bc98dbc 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -1188,20 +1188,15 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac thisURL = resolvePath(file); } - if (!_includedURLs.contains(thisURL)) { - if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") && - (currentSandboxURL.scheme() != "file" || - !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) { - qCWarning(scriptengine) << "Script.include() ignoring file path" - << thisURL << "outside of original entity script" << currentSandboxURL; - } else { - // We could also check here for CORS, but we don't yet. - // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here. - urls.append(thisURL); - _includedURLs << thisURL; - } + if (!isStandardLibrary && !currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") && + (currentSandboxURL.scheme() != "file" || + !thisURL.toString(strippingFlags).startsWith(currentSandboxURL.toString(strippingFlags), getSensitivity()))) { + qCWarning(scriptengine) << "Script.include() ignoring file path" + << thisURL << "outside of original entity script" << currentSandboxURL; } else { - qCDebug(scriptengine) << "Script.include() ignoring previously included url:" << thisURL; + // We could also check here for CORS, but we don't yet. + // It turns out that QUrl.resolve will not change hosts and copy authority, so we don't need to check that here. + urls.append(thisURL); } } @@ -1221,13 +1216,20 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac if (contents.isNull()) { qCDebug(scriptengine) << "Error loading file: " << url << "line:" << __LINE__; } else { - // Set the parent url so that path resolution will be relative - // to this script's url during its initial evaluation - _parentURL = url.toString(); - auto operation = [&]() { - evaluate(contents, url.toString()); - }; - doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); + std::lock_guard lock(_lock); + if (!_includedURLs.contains(url)) { + _includedURLs << url; + // Set the parent url so that path resolution will be relative + // to this script's url during its initial evaluation + _parentURL = url.toString(); + auto operation = [&]() { + evaluate(contents, url.toString()); + }; + + doWithEnvironment(capturedEntityIdentifier, capturedSandboxURL, operation); + } else { + qCDebug(scriptengine) << "Script.include() skipping evaluation of previously included url:" << url; + } } } _parentURL = parentURL; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 01088660ff..2b2cb3c81a 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -245,6 +245,7 @@ protected: std::function _emitScriptUpdates{ [](){ return true; } }; + std::recursive_mutex _lock; }; #endif // hifi_ScriptEngine_h diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index eb0e58eb4a..bfa61f3b36 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -401,9 +401,13 @@ bool OpenVrDisplayPlugin::internalActivate() { memset(&timing, 0, sizeof(timing)); timing.m_nSize = sizeof(vr::Compositor_FrameTiming); vr::VRCompositor()->GetFrameTiming(&timing); - _asyncReprojectionActive = timing.m_nReprojectionFlags & VRCompositor_ReprojectionAsync; + auto usingOpenVRForOculus = oculusViaOpenVR(); + _asyncReprojectionActive = (timing.m_nReprojectionFlags & VRCompositor_ReprojectionAsync) || usingOpenVRForOculus; _threadedSubmit = !_asyncReprojectionActive; + if (usingOpenVRForOculus) { + qDebug() << "Oculus active via OpenVR: " << usingOpenVRForOculus; + } qDebug() << "OpenVR Async Reprojection active: " << _asyncReprojectionActive; qDebug() << "OpenVR Threaded submit enabled: " << _threadedSubmit; diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 0c3a2faed6..56a5dd3f5b 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -60,6 +60,12 @@ bool isOculusPresent() { return result; } +bool oculusViaOpenVR() { + static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); + static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); + return enableDebugOpenVR && isOculusPresent() && vr::VR_IsHmdPresent(); +} + bool openVrSupported() { static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index be79dd1155..f00cd9e117 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -15,6 +15,7 @@ #include #include +bool oculusViaOpenVR(); // is the user using Oculus via OpenVR bool openVrSupported(); vr::IVRSystem* acquireOpenVrSystem(); diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 8016aa2ae5..084ff57fb5 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -19,6 +19,7 @@ exports.handlers = { '../../libraries/script-engine/src', '../../libraries/networking/src', '../../libraries/animation/src', + '../../libraries/entities/src', ]; var exts = ['.h', '.cpp'];