Merge branch 'master' into hazeZone

This commit is contained in:
Nissim 2017-10-17 09:28:07 -07:00
commit afa93f46e5
9 changed files with 164 additions and 30 deletions

View file

@ -13,6 +13,7 @@ import "."
Rectangle { Rectangle {
id: keyboardBase id: keyboardBase
objectName: "keyboard"
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -27,6 +28,8 @@ Rectangle {
readonly property int mirrorTextHeight: keyboardRowHeight readonly property int mirrorTextHeight: keyboardRowHeight
property bool password: false
property alias mirroredText: mirrorText.text
property bool showMirrorText: true property bool showMirrorText: true
readonly property int raisedHeight: 200 readonly property int raisedHeight: 200
@ -112,16 +115,20 @@ Rectangle {
color: "#252525" color: "#252525"
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
TextEdit { TextInput {
id: mirrorText id: mirrorText
visible: showMirrorText visible: showMirrorText
size: 13.5 FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
font.family: ralewaySemiBold.name
font.pointSize: 13.5
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
color: "#FFFFFF"; color: "#FFFFFF";
anchors.fill: parent anchors.fill: parent
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
readOnly: false // we need to leave this property read-only to allow control to accept QKeyEvent readOnly: false // we need this to allow control to accept QKeyEvent
selectByMouse: false selectByMouse: false
echoMode: password ? TextInput.Password : TextInput.Normal
Keys.onPressed: { Keys.onPressed: {
if (event.key == Qt.Key_Return || event.key == Qt.Key_Space) { if (event.key == Qt.Key_Return || event.key == Qt.Key_Space) {

View file

@ -207,6 +207,17 @@
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#include <VersionHelpers.h> #include <VersionHelpers.h>
#ifdef DEBUG_EVENT_QUEUE
// This is a HACK that uses private headers included with the qt source distrubution.
// To use this feature you need to add these directores to your include path:
// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1/QtCore
// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1
#define QT_BOOTSTRAPPED
#include <private/qthread_p.h>
#include <private/qobject_p.h>
#undef QT_BOOTSTRAPPED
#endif
extern "C" { extern "C" {
_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
} }
@ -264,9 +275,7 @@ private:
switch ((int)event->type()) { switch ((int)event->type()) {
case ApplicationEvent::Render: case ApplicationEvent::Render:
render(); render();
// Ensure we never back up the render events. Each render should be triggered only in response qApp->_pendingRenderEvent.store(false);
// to the NEXT render event after the last render occured
QCoreApplication::removePostedEvents(this, ApplicationEvent::Render);
return true; return true;
default: default:
@ -2712,9 +2721,14 @@ bool Application::importFromZIP(const QString& filePath) {
return true; return true;
} }
// thread-safe
void Application::onPresent(quint32 frameCount) { void Application::onPresent(quint32 frameCount) {
postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); bool expected = false;
if (_renderEventHandler && !isAboutToQuit()) { if (_pendingIdleEvent.compare_exchange_strong(expected, true)) {
postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority);
}
expected = false;
if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) {
postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render));
} }
} }
@ -2781,7 +2795,26 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) {
return false; return false;
} }
#ifdef DEBUG_EVENT_QUEUE
static int getEventQueueSize(QThread* thread) {
auto threadData = QThreadData::get2(thread);
QMutexLocker locker(&threadData->postEventList.mutex);
return threadData->postEventList.size();
}
static void dumpEventQueue(QThread* thread) {
auto threadData = QThreadData::get2(thread);
QMutexLocker locker(&threadData->postEventList.mutex);
qDebug() << "AJT: event list, size =" << threadData->postEventList.size();
for (auto& postEvent : threadData->postEventList) {
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
qDebug() << "AJT: " << type;
}
}
#endif // DEBUG_EVENT_QUEUE
bool Application::event(QEvent* event) { bool Application::event(QEvent* event) {
if (!Menu::getInstance()) { if (!Menu::getInstance()) {
return false; return false;
} }
@ -2801,8 +2834,18 @@ bool Application::event(QEvent* event) {
// see (windowMinimizedChanged) // see (windowMinimizedChanged)
case ApplicationEvent::Idle: case ApplicationEvent::Idle:
idle(); idle();
// Don't process extra idle events that arrived in the event queue while we were doing this idle
QCoreApplication::removePostedEvents(this, ApplicationEvent::Idle); #ifdef DEBUG_EVENT_QUEUE
{
int count = getEventQueueSize(QThread::currentThread());
if (count > 400) {
dumpEventQueue(QThread::currentThread());
}
}
#endif // DEBUG_EVENT_QUEUE
_pendingIdleEvent.store(false);
return true; return true;
case QEvent::MouseMove: case QEvent::MouseMove:
@ -7203,7 +7246,7 @@ void Application::updateDisplayMode() {
_offscreenContext->makeCurrent(); _offscreenContext->makeCurrent();
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
_displayPlugin = newDisplayPlugin; _displayPlugin = newDisplayPlugin;
connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection);
auto desktop = offscreenUi->getDesktop(); auto desktop = offscreenUi->getDesktop();
if (desktop) { if (desktop) {
desktop->setProperty("repositionLocked", wasRepositionLocked); desktop->setProperty("repositionLocked", wasRepositionLocked);

View file

@ -717,5 +717,8 @@ private:
LaserPointerManager _laserPointerManager; LaserPointerManager _laserPointerManager;
friend class RenderEventHandler; friend class RenderEventHandler;
std::atomic<bool> _pendingIdleEvent { false };
std::atomic<bool> _pendingRenderEvent { false };
}; };
#endif // hifi_Application_h #endif // hifi_Application_h

View file

@ -72,6 +72,12 @@ void Application::paintGL() {
{ {
QMutexLocker viewLocker(&_renderArgsMutex); QMutexLocker viewLocker(&_renderArgsMutex);
renderArgs = _appRenderArgs._renderArgs; renderArgs = _appRenderArgs._renderArgs;
// don't render if there is no context.
if (!_appRenderArgs._renderArgs._context) {
return;
}
HMDSensorPose = _appRenderArgs._headPose; HMDSensorPose = _appRenderArgs._headPose;
eyeToWorld = _appRenderArgs._eyeToWorld; eyeToWorld = _appRenderArgs._eyeToWorld;
sensorToWorld = _appRenderArgs._sensorToWorld; sensorToWorld = _appRenderArgs._sensorToWorld;

View file

@ -46,6 +46,10 @@ static const int STATS_FOR_STATS_PACKET_WINDOW_SECONDS = 30;
// _currentJitterBufferFrames is updated with the time-weighted avg and the running time-weighted avg is reset. // _currentJitterBufferFrames is updated with the time-weighted avg and the running time-weighted avg is reset.
static const quint64 FRAMES_AVAILABLE_STAT_WINDOW_USECS = 10 * USECS_PER_SECOND; static const quint64 FRAMES_AVAILABLE_STAT_WINDOW_USECS = 10 * USECS_PER_SECOND;
// When the audio codec is switched, temporary codec mismatch is expected due to packets in-flight.
// A SelectedAudioFormat packet is not sent until this threshold is exceeded.
static const int MAX_MISMATCHED_AUDIO_CODEC_COUNT = 10;
InboundAudioStream::InboundAudioStream(int numChannels, int numFrames, int numBlocks, int numStaticJitterBlocks) : InboundAudioStream::InboundAudioStream(int numChannels, int numFrames, int numBlocks, int numStaticJitterBlocks) :
_ringBuffer(numChannels * numFrames, numBlocks), _ringBuffer(numChannels * numFrames, numBlocks),
_numChannels(numChannels), _numChannels(numChannels),
@ -153,6 +157,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
// If we recieved a SilentAudioFrame from our sender, we might want to drop // If we recieved a SilentAudioFrame from our sender, we might want to drop
// some of the samples in order to catch up to our desired jitter buffer size. // some of the samples in order to catch up to our desired jitter buffer size.
writeDroppableSilentFrames(networkFrames); writeDroppableSilentFrames(networkFrames);
} else { } else {
// note: PCM and no codec are identical // note: PCM and no codec are identical
bool selectedPCM = _selectedCodecName == "pcm" || _selectedCodecName == ""; bool selectedPCM = _selectedCodecName == "pcm" || _selectedCodecName == "";
@ -160,20 +165,33 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) { if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) {
auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead());
parseAudioData(message.getType(), afterProperties); parseAudioData(message.getType(), afterProperties);
_mismatchedAudioCodecCount = 0;
} else { } else {
qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; _mismatchedAudioCodecCount++;
qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket;
// Since the data in the stream is using a codec that we aren't prepared for, if (packetPCM) {
// we need to let the codec know that we don't have data for it, this will // If there are PCM packets in-flight after the codec is changed, use them.
// allow the codec to interpolate missing data and produce a fade to silence. auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead());
lostAudioData(1); _ringBuffer.writeData(afterProperties.data(), afterProperties.size());
} else {
// inform others of the mismatch // Since the data in the stream is using a codec that we aren't prepared for,
auto sendingNode = DependencyManager::get<NodeList>()->nodeWithUUID(message.getSourceID()); // we need to let the codec know that we don't have data for it, this will
if (sendingNode) { // allow the codec to interpolate missing data and produce a fade to silence.
emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket); lostAudioData(1);
} }
if (_mismatchedAudioCodecCount > MAX_MISMATCHED_AUDIO_CODEC_COUNT) {
_mismatchedAudioCodecCount = 0;
// inform others of the mismatch
auto sendingNode = DependencyManager::get<NodeList>()->nodeWithUUID(message.getSourceID());
if (sendingNode) {
emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket);
qDebug(audio) << "Codec mismatch threshold exceeded, SelectedAudioFormat(" << _selectedCodecName << " ) sent";
}
}
} }
} }
break; break;

View file

@ -186,6 +186,7 @@ protected:
CodecPluginPointer _codec; CodecPluginPointer _codec;
QString _selectedCodecName; QString _selectedCodecName;
Decoder* _decoder { nullptr }; Decoder* _decoder { nullptr };
int _mismatchedAudioCodecCount { 0 };
}; };
float calculateRepeatedFrameFadeFactor(int indexOfRepeat); float calculateRepeatedFrameFadeFactor(int indexOfRepeat);

View file

@ -983,6 +983,9 @@ public:
glm::vec2 dstCoord; glm::vec2 dstCoord;
glm::ivec2 srcPixel; glm::ivec2 srcPixel;
for (int y = 0; y < faceWidth; ++y) { for (int y = 0; y < faceWidth; ++y) {
QRgb* destScanLineBegin = reinterpret_cast<QRgb*>( image.scanLine(y) );
QRgb* destPixelIterator = destScanLineBegin;
dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom
for (int x = 0; x < faceWidth; ++x) { for (int x = 0; x < faceWidth; ++x) {
dstCoord.x = (x + 0.5f) * dstInvSize.x; dstCoord.x = (x + 0.5f) * dstInvSize.x;
@ -995,13 +998,19 @@ public:
srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight); srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight);
if (((uint32)srcPixel.x < (uint32)source.width()) && ((uint32)srcPixel.y < (uint32)source.height())) { if (((uint32)srcPixel.x < (uint32)source.width()) && ((uint32)srcPixel.y < (uint32)source.height())) {
image.setPixel(x, y, source.pixel(QPoint(srcPixel.x, srcPixel.y))); // We can't directly use the pixel() method because that launches a pixel color conversion to output
// a correct RGBA8 color. But in our case we may have stored HDR values encoded in a RGB30 format which
// are not convertible by Qt. The same goes with the setPixel method, by the way.
const QRgb* sourcePixelIterator = reinterpret_cast<const QRgb*>(source.scanLine(srcPixel.y));
sourcePixelIterator += srcPixel.x;
*destPixelIterator = *sourcePixelIterator;
// Keep for debug, this is showing the dir as a color // Keep for debug, this is showing the dir as a color
// glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256); // glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256);
// unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16); // unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16);
// image.setPixel(x, y, val); // *destPixelIterator = val;
} }
++destPixelIterator;
} }
} }
return image; return image;
@ -1192,6 +1201,10 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage&
if ((srcImage.width() > 0) && (srcImage.height() > 0)) { if ((srcImage.width() > 0) && (srcImage.height() > 0)) {
QImage image = processSourceImage(srcImage, true); QImage image = processSourceImage(srcImage, true);
if (image.format() != QIMAGE_HDR_FORMAT) {
image = convertToHDRFormat(image, HDR_FORMAT);
}
gpu::Element formatMip; gpu::Element formatMip;
gpu::Element formatGPU; gpu::Element formatGPU;
if (isCubeTexturesCompressionEnabled()) { if (isCubeTexturesCompressionEnabled()) {
@ -1229,13 +1242,6 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage&
faces.push_back(faceImage); faces.push_back(faceImage);
} }
} }
if (image.format() != QIMAGE_HDR_FORMAT) {
for (auto& face : faces) {
face = convertToHDRFormat(face, HDR_FORMAT);
}
}
} else { } else {
qCDebug(imagelogging) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str()); qCDebug(imagelogging) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str());
return nullptr; return nullptr;

View file

@ -1018,6 +1018,32 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid
} }
} }
static void forEachKeyboard(QQuickItem* item, std::function<void(QQuickItem*)> function) {
QObject* itemObject = item;
while (itemObject) {
if (itemObject->parent()) {
itemObject = itemObject->parent();
} else {
break;
}
}
auto keyboards = itemObject->findChildren<QObject*>("keyboard");
for (auto keyboardObject : keyboards) {
auto keyboard = qobject_cast<QQuickItem*>(keyboardObject);
if (keyboard == nullptr) {
continue;
}
if (function) {
function(keyboard);
}
}
}
static const int TEXTINPUT_PASSWORD = 2;
void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) { void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) {
#if Q_OS_ANDROID #if Q_OS_ANDROID
return; return;
@ -1030,6 +1056,26 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n
// if HMD is being worn, allow keyboard to open. allow it to close, HMD or not. // if HMD is being worn, allow keyboard to open. allow it to close, HMD or not.
if (!raised || qApp->property(hifi::properties::HMD).toBool()) { if (!raised || qApp->property(hifi::properties::HMD).toBool()) {
QQuickItem* item = dynamic_cast<QQuickItem*>(object); QQuickItem* item = dynamic_cast<QQuickItem*>(object);
if (!item) {
return;
}
auto echoMode = item->property("echoMode");
bool isPasswordField = echoMode.isValid() && echoMode.toInt() == TEXTINPUT_PASSWORD;
// we need to somehow pass 'isPasswordField' to visible keyboard so it will change its 'mirror text' to asterixes
// the issue in some cases there might be more than one keyboard in object tree and it is hard to understand which one is being used at the moment
// unfortunately attempts to check for visibility failed becuase visibility is not updated yet. So... I don't see other way than just update properties for all the keyboards
forEachKeyboard(item, [&](QQuickItem* keyboard) {
keyboard->setProperty("mirroredText", QVariant::fromValue(QString("")));
keyboard->setProperty("password", isPasswordField);
});
// for future probably makes sense to consider one of the following:
// 1. make keyboard a singleton, which will be dynamically re-parented before showing
// 2. track currently visible keyboard somewhere, allow to subscribe for this signal
// any of above should also eliminate need in duplicated properties and code below
while (item) { while (item) {
// Numeric value may be set in parameter from HTML UI; for QML UI, detect numeric fields here. // Numeric value may be set in parameter from HTML UI; for QML UI, detect numeric fields here.
numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox"; numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox";

View file

@ -53,4 +53,8 @@ ScriptDiscoveryService.clearDebugWindow.connect(function() {
window.clearDebugWindow(); window.clearDebugWindow();
}); });
Script.scriptEnding.connect(function () {
window.close();
})
}()); }());