diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3ba66cf2a0..9d28e40a66 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3391,14 +3391,10 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi // set the bounds of rear mirror view gpu::Vec4i viewport; if (billboard) { - QSize size = DependencyManager::get()->getFrameBufferSize(); viewport = gpu::Vec4i(0, 0, region.width(), region.height()); } else { // if not rendering the billboard, the region is in device independent coordinates; must convert to device - QSize size = DependencyManager::get()->getFrameBufferSize(); float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale(); - int x = region.x() * ratio; - int y = region.y() * ratio; int width = region.width() * ratio; int height = region.height() * ratio; viewport = gpu::Vec4i(0, 0, width, height); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ddc76663f4..91ae6a4d02 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -252,8 +252,6 @@ Menu::Menu() { avatar, SLOT(updateMotionBehavior())); MenuWrapper* viewMenu = addMenu("View"); - - addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, @@ -489,6 +487,7 @@ Menu::Menu() { #endif MenuWrapper* networkMenu = developerMenu->addMenu("Network"); + addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false, qApp->getEntityEditPacketSender(), SLOT(toggleNackPackets())); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9120632f80..095a225952 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -443,36 +443,57 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { _skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f); } - // Stack indicator spheres - float indicatorOffset = 0.0f; - if (!_displayName.isEmpty() && _displayNameAlpha != 0.0f) { - const float DISPLAY_NAME_INDICATOR_OFFSET = 0.22f; - indicatorOffset = DISPLAY_NAME_INDICATOR_OFFSET; - } - const float INDICATOR_RADIUS = 0.03f; - const float INDICATOR_INDICATOR_OFFSET = 3.0f * INDICATOR_RADIUS; - // If this is the avatar being looked at, render a little ball above their head if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) { + const float INDICATOR_OFFSET = 0.22f; + const float INDICATOR_RADIUS = 0.03f; const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f }; - glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z); + glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + INDICATOR_OFFSET, _position.z); Transform transform; transform.setTranslation(position); batch.setModelTransform(transform); DependencyManager::get()->renderSolidSphere(batch, INDICATOR_RADIUS, 15, 15, LOOK_AT_INDICATOR_COLOR); - indicatorOffset += INDICATOR_INDICATOR_OFFSET; } - // If the avatar is looking at me, render an indication that they area - if (getHead()->getIsLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { - const glm::vec4 LOOKING_AT_ME_COLOR = { 0.8f, 0.65f, 0.0f, 0.1f }; - glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + indicatorOffset, _position.z); - Transform transform; - transform.setTranslation(position); - batch.setModelTransform(transform); - DependencyManager::get()->renderSolidSphere(batch, INDICATOR_RADIUS, - 15, 15, LOOKING_AT_ME_COLOR); + // If the avatar is looking at me, indicate that they are + if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) { + const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f }; + const float LOOKING_AT_ME_ALPHA_START = 0.8f; + const float LOOKING_AT_ME_DURATION = 0.5f; // seconds + quint64 now = usecTimestampNow(); + float alpha = LOOKING_AT_ME_ALPHA_START + * (1.0f - ((float)(now - getHead()->getLookingAtMeStarted())) + / (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND)); + if (alpha > 0.0f) { + QSharedPointer geometry = getHead()->getFaceModel().getGeometry(); + if (geometry) { + const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye + const float RADIUS_INCREMENT = 0.005f; + Transform transform; + + glm::vec3 position = getHead()->getLeftEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + float eyeDiameter = geometry->getFBXGeometry().leftEyeSize; + if (eyeDiameter == 0.0f) { + eyeDiameter = DEFAULT_EYE_DIAMETER; + } + DependencyManager::get()->renderSolidSphere(batch, + eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + + position = getHead()->getRightEyePosition(); + transform.setTranslation(position); + batch.setModelTransform(transform); + eyeDiameter = geometry->getFBXGeometry().rightEyeSize; + if (eyeDiameter == 0.0f) { + eyeDiameter = DEFAULT_EYE_DIAMETER; + } + DependencyManager::get()->renderSolidSphere(batch, + eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT, 15, 15, glm::vec4(LOOKING_AT_ME_COLOR, alpha)); + + } + } } // quick check before falling into the code below: diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index a39654712f..55f33f57a4 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -55,6 +55,8 @@ Head::Head(Avatar* owningAvatar) : _deltaLeanForward(0.0f), _isCameraMoving(false), _isLookingAtMe(false), + _lookingAtMeStarted(0), + _wasLastLookingAtMe(0), _faceModel(this), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) @@ -316,7 +318,7 @@ glm::quat Head::getFinalOrientationInLocalFrame() const { } glm::vec3 Head::getCorrectedLookAtPosition() { - if (_isLookingAtMe) { + if (isLookingAtMe()) { return _correctedLookAtPosition; } else { return getLookAtPosition(); @@ -324,10 +326,21 @@ glm::vec3 Head::getCorrectedLookAtPosition() { } void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { + if (!isLookingAtMe()) { + _lookingAtMeStarted = usecTimestampNow(); + } _isLookingAtMe = true; + _wasLastLookingAtMe = usecTimestampNow(); _correctedLookAtPosition = correctedLookAtPosition; } +bool Head::isLookingAtMe() { + // Allow for outages such as may be encountered during avatar movement + quint64 now = usecTimestampNow(); + const quint64 LOOKING_AT_ME_GAP_ALLOWED = 1000000; // microseconds + return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED; +} + glm::quat Head::getCameraOrientation() const { // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so // you may wonder why this code is here. This method will be called while in Oculus mode to determine how diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index f6283c93ea..a1c70f9dff 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -52,8 +52,9 @@ public: void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition); glm::vec3 getCorrectedLookAtPosition(); void clearCorrectedLookAtPosition() { _isLookingAtMe = false; } - bool getIsLookingAtMe() { return _isLookingAtMe; } - + bool isLookingAtMe(); + quint64 getLookingAtMeStarted() { return _lookingAtMeStarted; } + float getScale() const { return _scale; } glm::vec3 getPosition() const { return _position; } const glm::vec3& getEyePosition() const { return _eyePosition; } @@ -139,6 +140,8 @@ private: bool _isCameraMoving; bool _isLookingAtMe; + quint64 _lookingAtMeStarted; + quint64 _wasLastLookingAtMe; FaceModel _faceModel; glm::vec3 _correctedLookAtPosition; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c6c6919325..f332173568 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1290,7 +1290,6 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { void MyAvatar::updateOrientation(float deltaTime) { // Smoothly rotate body with arrow keys - float driveLeft = _driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]; float targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED; if (targetSpeed != 0.0f) { const float ROTATION_RAMP_TIMESCALE = 0.1f; diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index 5536fa14bd..9673f541d2 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -91,13 +91,12 @@ private: int _leftBlinkIndex; int _rightBlinkIndex; - int _leftEyeOpenIndex; - int _rightEyeOpenIndex; - int _leftEyeDownIndex; int _rightEyeDownIndex; int _leftEyeInIndex; int _rightEyeInIndex; + int _leftEyeOpenIndex; + int _rightEyeOpenIndex; int _browDownLeftIndex; int _browDownRightIndex; diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 3bd7e390ec..f187de95d2 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -57,7 +57,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid } else { auto dialogWidget = new QDialog(Application::getInstance()->getWindow(), Qt::Window); dialogWidget->setWindowTitle(title); - dialogWidget->setMinimumSize(width, height); + dialogWidget->resize(width, height); connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed); auto layout = new QVBoxLayout(dialogWidget); diff --git a/interface/src/ui/ApplicationCompositor.cpp b/interface/src/ui/ApplicationCompositor.cpp index 54fb4fbd1f..9bda88b3bf 100644 --- a/interface/src/ui/ApplicationCompositor.cpp +++ b/interface/src/ui/ApplicationCompositor.cpp @@ -495,7 +495,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { glm::vec3 direction = glm::inverse(myAvatar->getOrientation()) * palmData->getFingerDirection(); // Get the angles, scaled between (-0.5,0.5) - float xAngle = (atan2(direction.z, direction.x) + PI_OVER_TWO); + float xAngle = (atan2f(direction.z, direction.x) + PI_OVER_TWO); float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO)); // Get the pixel range over which the xAngle and yAngle are scaled diff --git a/interface/src/ui/AudioStatsDialog.cpp b/interface/src/ui/AudioStatsDialog.cpp index 116cc60b5e..e57182e251 100644 --- a/interface/src/ui/AudioStatsDialog.cpp +++ b/interface/src/ui/AudioStatsDialog.cpp @@ -125,8 +125,10 @@ void AudioStatsDialog::renderStats() { audioInputBufferLatency = (double)_stats->getAudioInputMsecsReadStats().getWindowAverage(); inputRingBufferLatency = (double)_stats->getInputRungBufferMsecsAvailableStats().getWindowAverage(); networkRoundtripLatency = (double) audioMixerNodePointer->getPingMs(); - mixerRingBufferLatency = (double)_stats->getMixerAvatarStreamStats()._framesAvailableAverage * AudioConstants::NETWORK_FRAME_MSECS; - outputRingBufferLatency = (double)downstreamAudioStreamStats._framesAvailableAverage * AudioConstants::NETWORK_FRAME_MSECS; + mixerRingBufferLatency = (double)_stats->getMixerAvatarStreamStats()._framesAvailableAverage * + (double)AudioConstants::NETWORK_FRAME_MSECS; + outputRingBufferLatency = (double)downstreamAudioStreamStats._framesAvailableAverage * + (double)AudioConstants::NETWORK_FRAME_MSECS; audioOutputBufferLatency = (double)_stats->getAudioOutputMsecsUnplayedStats().getWindowAverage(); } diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 961d7f765b..200a1a328f 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -36,7 +36,6 @@ void Cube3DOverlay::render(RenderArgs* args) { // TODO: handle registration point?? glm::vec3 position = getPosition(); - glm::vec3 center = getCenter(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 4d7bff4df0..466b3de3ee 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2616,10 +2616,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, buildModelMesh(extracted); # endif + if (extracted.mesh.isEye) { + if (maxJointIndex == geometry.leftEyeJointIndex) { + geometry.leftEyeSize = extracted.mesh.meshExtents.largestDimension() * offsetScale; + } else { + geometry.rightEyeSize = extracted.mesh.meshExtents.largestDimension() * offsetScale; + } + } + geometry.meshes.append(extracted.mesh); int meshIndex = geometry.meshes.size() - 1; meshIDsToMeshIndices.insert(it.key(), meshIndex); - } // now that all joints have been scanned, compute a collision shape for each joint diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 200cd4a121..3b3d90eb05 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -232,7 +232,10 @@ public: int rightHandJointIndex = -1; int leftToeJointIndex = -1; int rightToeJointIndex = -1; - + + float leftEyeSize = 0.0f; // Maximum mesh extents dimension + float rightEyeSize = 0.0f; + QVector humanIKJointIndices; glm::vec3 palmDirection; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 567ce66cd8..4ac33d8f14 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -106,36 +106,6 @@ void Batch::drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, ui _params.push_back(nbInstances); } -void Batch::clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil, bool enableScissor) { - ADD_COMMAND(clearFramebuffer); - - _params.push_back(enableScissor); - _params.push_back(stencil); - _params.push_back(depth); - _params.push_back(color.w); - _params.push_back(color.z); - _params.push_back(color.y); - _params.push_back(color.x); - _params.push_back(targets); -} - -void Batch::clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color, bool enableScissor) { - clearFramebuffer(targets & Framebuffer::BUFFER_COLORS, color, 1.0f, 0, enableScissor); -} - -void Batch::clearDepthFramebuffer(float depth, bool enableScissor) { - clearFramebuffer(Framebuffer::BUFFER_DEPTH, Vec4(0.0f), depth, 0, enableScissor); -} - -void Batch::clearStencilFramebuffer(int stencil, bool enableScissor) { - clearFramebuffer(Framebuffer::BUFFER_STENCIL, Vec4(0.0f), 1.0f, stencil, enableScissor); -} - -void Batch::clearDepthStencilFramebuffer(float depth, int stencil, bool enableScissor) { - clearFramebuffer(Framebuffer::BUFFER_DEPTHSTENCIL, Vec4(0.0f), depth, stencil, enableScissor); -} - - void Batch::setInputFormat(const Stream::FormatPointer& format) { ADD_COMMAND(setInputFormat); @@ -255,6 +225,35 @@ void Batch::setFramebuffer(const FramebufferPointer& framebuffer) { } +void Batch::clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil, bool enableScissor) { + ADD_COMMAND(clearFramebuffer); + + _params.push_back(enableScissor); + _params.push_back(stencil); + _params.push_back(depth); + _params.push_back(color.w); + _params.push_back(color.z); + _params.push_back(color.y); + _params.push_back(color.x); + _params.push_back(targets); +} + +void Batch::clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color, bool enableScissor) { + clearFramebuffer(targets & Framebuffer::BUFFER_COLORS, color, 1.0f, 0, enableScissor); +} + +void Batch::clearDepthFramebuffer(float depth, bool enableScissor) { + clearFramebuffer(Framebuffer::BUFFER_DEPTH, Vec4(0.0f), depth, 0, enableScissor); +} + +void Batch::clearStencilFramebuffer(int stencil, bool enableScissor) { + clearFramebuffer(Framebuffer::BUFFER_STENCIL, Vec4(0.0f), 1.0f, stencil, enableScissor); +} + +void Batch::clearDepthStencilFramebuffer(float depth, int stencil, bool enableScissor) { + clearFramebuffer(Framebuffer::BUFFER_DEPTHSTENCIL, Vec4(0.0f), depth, stencil, enableScissor); +} + void Batch::blit(const FramebufferPointer& src, const Vec4i& srcViewport, const FramebufferPointer& dst, const Vec4i& dstViewport) { ADD_COMMAND(blit); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index acc1f6fdac..58fe03c9cf 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -54,15 +54,6 @@ public: void drawInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbVertices, uint32 startVertex = 0, uint32 startInstance = 0); void drawIndexedInstanced(uint32 nbInstances, Primitive primitiveType, uint32 nbIndices, uint32 startIndex = 0, uint32 startInstance = 0); - // Clear framebuffer layers - // Targets can be any of the render buffers contained in the Framebuffer - // Optionally the scissor test can be enabled locally for this command and to restrict the clearing command to the pixels contained in the scissor rectangle - void clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil, bool enableScissor = false); - void clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, mask out targets to make sure it touches only color targets - void clearDepthFramebuffer(float depth, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, it touches only depth target - void clearStencilFramebuffer(int stencil, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, it touches only stencil target - void clearDepthStencilFramebuffer(float depth, int stencil, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, it touches depth and stencil target - // Input Stage // InputFormat // InputBuffers @@ -105,8 +96,17 @@ public: // Framebuffer Stage void setFramebuffer(const FramebufferPointer& framebuffer); - void blit(const FramebufferPointer& src, const Vec4i& srcViewport, - const FramebufferPointer& dst, const Vec4i& dstViewport); + + // Clear framebuffer layers + // Targets can be any of the render buffers contained in the currnetly bound Framebuffer + // Optionally the scissor test can be enabled locally for this command and to restrict the clearing command to the pixels contained in the scissor rectangle + void clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil, bool enableScissor = false); + void clearColorFramebuffer(Framebuffer::Masks targets, const Vec4& color, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, mask out targets to make sure it touches only color targets + void clearDepthFramebuffer(float depth, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, it touches only depth target + void clearStencilFramebuffer(int stencil, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, it touches only stencil target + void clearDepthStencilFramebuffer(float depth, int stencil, bool enableScissor = false); // not a command, just a shortcut for clearFramebuffer, it touches depth and stencil target + + void blit(const FramebufferPointer& src, const Vec4i& srcViewport, const FramebufferPointer& dst, const Vec4i& dstViewport); // Query Section void beginQuery(const QueryPointer& query); @@ -162,8 +162,6 @@ public: COMMAND_drawInstanced, COMMAND_drawIndexedInstanced, - COMMAND_clearFramebuffer, - COMMAND_setInputFormat, COMMAND_setInputBuffer, COMMAND_setIndexBuffer, @@ -181,6 +179,7 @@ public: COMMAND_setResourceTexture, COMMAND_setFramebuffer, + COMMAND_clearFramebuffer, COMMAND_blit, COMMAND_beginQuery, diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index ae50b96bc5..6b1d552be9 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -21,7 +21,6 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_drawIndexed), (&::gpu::GLBackend::do_drawInstanced), (&::gpu::GLBackend::do_drawIndexedInstanced), - (&::gpu::GLBackend::do_clearFramebuffer), (&::gpu::GLBackend::do_setInputFormat), (&::gpu::GLBackend::do_setInputBuffer), @@ -40,6 +39,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setResourceTexture), (&::gpu::GLBackend::do_setFramebuffer), + (&::gpu::GLBackend::do_clearFramebuffer), (&::gpu::GLBackend::do_blit), (&::gpu::GLBackend::do_beginQuery), @@ -246,71 +246,6 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } -void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) { - - uint32 masks = batch._params[paramOffset + 7]._uint; - Vec4 color; - color.x = batch._params[paramOffset + 6]._float; - color.y = batch._params[paramOffset + 5]._float; - color.z = batch._params[paramOffset + 4]._float; - color.w = batch._params[paramOffset + 3]._float; - float depth = batch._params[paramOffset + 2]._float; - int stencil = batch._params[paramOffset + 1]._int; - int useScissor = batch._params[paramOffset + 0]._int; - - GLuint glmask = 0; - if (masks & Framebuffer::BUFFER_STENCIL) { - glClearStencil(stencil); - glmask |= GL_STENCIL_BUFFER_BIT; - } - - if (masks & Framebuffer::BUFFER_DEPTH) { - glClearDepth(depth); - glmask |= GL_DEPTH_BUFFER_BIT; - } - - std::vector drawBuffers; - if (masks & Framebuffer::BUFFER_COLORS) { - for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) { - if (masks & (1 << i)) { - drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i); - } - } - - if (!drawBuffers.empty()) { - glDrawBuffers(drawBuffers.size(), drawBuffers.data()); - glClearColor(color.x, color.y, color.z, color.w); - glmask |= GL_COLOR_BUFFER_BIT; - } - - // Force the color mask cache to WRITE_ALL if not the case - do_setStateColorWriteMask(State::ColorMask::WRITE_ALL); - } - - // Apply scissor if needed and if not already on - bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable)); - if (doEnableScissor) { - glEnable(GL_SCISSOR_TEST); - } - - glClear(glmask); - - // Restore scissor if needed - if (doEnableScissor) { - glDisable(GL_SCISSOR_TEST); - } - - // Restore the color draw buffers only if a frmaebuffer is bound - if (_output._framebuffer && !drawBuffers.empty()) { - auto glFramebuffer = syncGPUObject(*_output._framebuffer); - if (glFramebuffer) { - glDrawBuffers(glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data()); - } - } - - (void) CHECK_GL_ERROR(); -} - // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 894e2c4548..9d8c9ef805 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -241,8 +241,6 @@ protected: void do_drawInstanced(Batch& batch, uint32 paramOffset); void do_drawIndexedInstanced(Batch& batch, uint32 paramOffset); - void do_clearFramebuffer(Batch& batch, uint32 paramOffset); - // Input Stage void do_setInputFormat(Batch& batch, uint32 paramOffset); void do_setInputBuffer(Batch& batch, uint32 paramOffset); @@ -385,6 +383,7 @@ protected: // Output stage void do_setFramebuffer(Batch& batch, uint32 paramOffset); + void do_clearFramebuffer(Batch& batch, uint32 paramOffset); void do_blit(Batch& batch, uint32 paramOffset); // Synchronize the state cache of this Backend with the actual real state of the GL Context diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 1b22649ad6..b37def48e7 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -180,7 +180,6 @@ void GLBackend::syncOutputStateCache() { void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) { auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); - if (_output._framebuffer != framebuffer) { auto newFBO = getFramebufferID(framebuffer); if (_output._drawFBO != newFBO) { @@ -191,6 +190,72 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) { } } +void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) { + + uint32 masks = batch._params[paramOffset + 7]._uint; + Vec4 color; + color.x = batch._params[paramOffset + 6]._float; + color.y = batch._params[paramOffset + 5]._float; + color.z = batch._params[paramOffset + 4]._float; + color.w = batch._params[paramOffset + 3]._float; + float depth = batch._params[paramOffset + 2]._float; + int stencil = batch._params[paramOffset + 1]._int; + int useScissor = batch._params[paramOffset + 0]._int; + + GLuint glmask = 0; + if (masks & Framebuffer::BUFFER_STENCIL) { + glClearStencil(stencil); + glmask |= GL_STENCIL_BUFFER_BIT; + } + + if (masks & Framebuffer::BUFFER_DEPTH) { + glClearDepth(depth); + glmask |= GL_DEPTH_BUFFER_BIT; + } + + std::vector drawBuffers; + if (masks & Framebuffer::BUFFER_COLORS) { + for (unsigned int i = 0; i < Framebuffer::MAX_NUM_RENDER_BUFFERS; i++) { + if (masks & (1 << i)) { + drawBuffers.push_back(GL_COLOR_ATTACHMENT0 + i); + } + } + + if (!drawBuffers.empty()) { + glDrawBuffers(drawBuffers.size(), drawBuffers.data()); + glClearColor(color.x, color.y, color.z, color.w); + glmask |= GL_COLOR_BUFFER_BIT; + } + + // Force the color mask cache to WRITE_ALL if not the case + do_setStateColorWriteMask(State::ColorMask::WRITE_ALL); + } + + // Apply scissor if needed and if not already on + bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable)); + if (doEnableScissor) { + glEnable(GL_SCISSOR_TEST); + } + + // Clear! + glClear(glmask); + + // Restore scissor if needed + if (doEnableScissor) { + glDisable(GL_SCISSOR_TEST); + } + + // Restore the color draw buffers only if a frmaebuffer is bound + if (_output._framebuffer && !drawBuffers.empty()) { + auto glFramebuffer = syncGPUObject(*_output._framebuffer); + if (glFramebuffer) { + glDrawBuffers(glFramebuffer->_colorBuffers.size(), glFramebuffer->_colorBuffers.data()); + } + } + + (void) CHECK_GL_ERROR(); +} + void GLBackend::do_blit(Batch& batch, uint32 paramOffset) { auto srcframebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); Vec4i srcvp; @@ -203,19 +268,31 @@ void GLBackend::do_blit(Batch& batch, uint32 paramOffset) { for (size_t i = 0; i < 4; ++i) { dstvp[i] = batch._params[paramOffset + 6 + i]._int; } - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getFramebufferID(dstframebuffer)); + + // Assign dest framebuffer if not bound already + auto newDrawFBO = getFramebufferID(dstframebuffer); + if (_output._drawFBO != newDrawFBO) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, newDrawFBO); + } + + // always bind the read fbo glBindFramebuffer(GL_READ_FRAMEBUFFER, getFramebufferID(srcframebuffer)); + + // Blit! glBlitFramebuffer(srcvp.x, srcvp.y, srcvp.z, srcvp.w, dstvp.x, dstvp.y, dstvp.z, dstvp.w, GL_COLOR_BUFFER_BIT, GL_LINEAR); - - (void) CHECK_GL_ERROR(); - if (_output._framebuffer) { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getFramebufferID(_output._framebuffer)); + // Always clean the read fbo to 0 + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + // Restore draw fbo if changed + if (_output._drawFBO != newDrawFBO) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _output._drawFBO); } -} + (void) CHECK_GL_ERROR(); +} void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) { auto readFBO = gpu::GLBackend::getFramebufferID(srcFramebuffer); diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp index 3ebc7704a8..056f9dbc6d 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.cpp +++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp @@ -19,6 +19,7 @@ #include "FboCache.h" #include +#include class QMyQuickRenderControl : public QQuickRenderControl { protected: @@ -44,7 +45,10 @@ Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. -static const int SMALL_INTERVAL = 5; +static const int MAX_QML_FRAMERATE = 10; +static const int MIN_RENDER_INTERVAL_US = USECS_PER_SECOND / MAX_QML_FRAMERATE; +static const int MIN_TIMER_MS = 5; + OffscreenQmlSurface::OffscreenQmlSurface() : _renderControl(new QMyQuickRenderControl), _fboCache(new FboCache) { @@ -90,7 +94,6 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // 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. _updateTimer.setSingleShot(true); - _updateTimer.setInterval(SMALL_INTERVAL); connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); // Now hook up the signals. For simplicy we don't differentiate between @@ -170,13 +173,18 @@ QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, std::function MIN_RENDER_INTERVAL_US) { + _updateTimer.setInterval(MIN_TIMER_MS); + } else { + _updateTimer.setInterval((MIN_RENDER_INTERVAL_US - lastInterval) / USECS_PER_MSEC); + } _updateTimer.start(); } } @@ -243,6 +251,7 @@ void OffscreenQmlSurface::updateQuick() { if (_paused) { return; } + if (!makeCurrent()) { return; } @@ -270,11 +279,11 @@ void OffscreenQmlSurface::updateQuick() { // Need a debug context with sync logging to figure out why. // for now just clear the errors glGetError(); -// Q_ASSERT(!glGetError()); _quickWindow->resetOpenGLState(); QOpenGLFramebufferObject::bindDefault(); + _lastRenderTime = usecTimestampNow(); // Force completion of all the operations before we emit the texture as being ready for use glFinish(); diff --git a/libraries/render-utils/src/OffscreenQmlSurface.h b/libraries/render-utils/src/OffscreenQmlSurface.h index b892806c44..1fbf69ef4d 100644 --- a/libraries/render-utils/src/OffscreenQmlSurface.h +++ b/libraries/render-utils/src/OffscreenQmlSurface.h @@ -86,6 +86,7 @@ private: QQuickItem* _rootItem{ nullptr }; QTimer _updateTimer; FboCache* _fboCache; + quint64 _lastRenderTime{ 0 }; bool _polish{ true }; bool _paused{ true }; MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } };