Exemplo n.º 1
0
// This method is called when the edit packet layer has determined that it has a fully formed packet destined for
// a known nodeID.
void OctreeEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned char* buffer, ssize_t length) {
    NodeList* nodeList = NodeList::getInstance();

    foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
        // only send to the NodeTypes that are getMyNodeType()
        if (node->getType() == getMyNodeType() &&
            ((node->getUUID() == nodeUUID) || (nodeUUID.isNull()))) {
            if (node->getActiveSocket()) {
                queuePacketForSending(node, QByteArray(reinterpret_cast<char*>(buffer), length));

                // debugging output...
                bool wantDebugging = false;
                if (wantDebugging) {
                    int numBytesPacketHeader = numBytesForPacketHeader(reinterpret_cast<char*>(buffer));
                    unsigned short int sequence = (*((unsigned short int*)(buffer + numBytesPacketHeader)));
                    quint64 createdAt = (*((quint64*)(buffer + numBytesPacketHeader + sizeof(sequence))));
                    quint64 queuedAt = usecTimestampNow();
                    quint64 transitTime = queuedAt - createdAt;

                    qDebug() << "OctreeEditPacketSender::queuePacketToNode() queued " << buffer[0] <<
                            " - command to node bytes=" << length <<
                            " sequence=" << sequence <<
                            " transitTimeSoFar=" << transitTime << " usecs";
                }
            }
        }
    }
}
Exemplo n.º 2
0
bool OctreeEditPacketSender::serversExist() const {
    bool hasServers = false;
    bool atLeastOnJurisdictionMissing = false; // assume the best
    NodeList* nodeList = NodeList::getInstance();

    foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
        // only send to the NodeTypes that are getMyNodeType()
        if (node->getType() == getMyNodeType() &&  node->getActiveSocket()) {
            QUuid nodeUUID = node->getUUID();
            // If we've got Jurisdictions set, then check to see if we know the jurisdiction for this server
            if (_serverJurisdictions) {
                // lookup our nodeUUID in the jurisdiction map, if it's missing then we're
                // missing at least one jurisdiction
                if ((*_serverJurisdictions).find(nodeUUID) == (*_serverJurisdictions).end()) {
                    atLeastOnJurisdictionMissing = true;
                }
            }
            hasServers = true;
        }
        if (atLeastOnJurisdictionMissing) {
            break; // no point in looking further...
        }
    }

    return (hasServers && !atLeastOnJurisdictionMissing);
}
Exemplo n.º 3
0
void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
    NodeList* nodeList = NodeList::getInstance();
    
    if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
        // pull any new audio data from nodes off of the network stack
        PacketType mixerPacketType = packetTypeForPacket(receivedPacket);
        if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho
            || mixerPacketType == PacketTypeMicrophoneAudioWithEcho
            || mixerPacketType == PacketTypeInjectAudio
            || mixerPacketType == PacketTypeSilentAudioFrame
            || mixerPacketType == PacketTypeAudioStreamStats) {
            
            nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
        } else if (mixerPacketType == PacketTypeMuteEnvironment) {
            QByteArray packet = receivedPacket;
            populatePacketHeader(packet, PacketTypeMuteEnvironment);
            
            foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
                if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
                    nodeList->writeDatagram(packet, packet.size(), node);
                }
            }
            
        } else {
Exemplo n.º 4
0
void AudioMixer::run() {

    commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);

    NodeList* nodeList = NodeList::getInstance();

    nodeList->addNodeTypeToInterestSet(NodeType::Agent);

    nodeList->linkedDataCreateCallback = attachNewBufferToNode;

    int nextFrame = 0;
    timeval startTime;

    gettimeofday(&startTime, NULL);

    int numBytesPacketHeader = numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio);
    // note: Visual Studio 2010 doesn't support variable sized local arrays
    #ifdef _WIN32
    unsigned char clientPacket[MAX_PACKET_SIZE];
    #else
    unsigned char clientPacket[NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader];
    #endif
    populatePacketHeader(reinterpret_cast<char*>(clientPacket), PacketTypeMixedAudio);

    while (!_isFinished) {

        QCoreApplication::processEvents();

        if (_isFinished) {
            break;
        }

        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getLinkedData()) {
                ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES);
            }
        }

        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData()
                && ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) {
                prepareMixForListeningNode(node.data());

                memcpy(clientPacket + numBytesPacketHeader, _clientSamples, sizeof(_clientSamples));
                nodeList->getNodeSocket().writeDatagram((char*) clientPacket, sizeof(clientPacket),
                                                        node->getActiveSocket()->getAddress(),
                                                        node->getActiveSocket()->getPort());
            }
        }

        // push forward the next output pointers for any audio buffers we used
        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getLinkedData()) {
                ((AudioMixerClientData*) node->getLinkedData())->pushBuffersAfterFrameSend();
            }
        }

        int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();

        if (usecToSleep > 0) {
            usleep(usecToSleep);
        } else {
            qDebug() << "AudioMixer loop took" << -usecToSleep << "of extra time. Not sleeping.";
        }

    }
}
Exemplo n.º 5
0
void NodeBounds::draw() {
    if (!(_showVoxelNodes || _showModelNodes || _showParticleNodes)) {
        _overlayText[0] = '\0';
        return;
    }

    NodeToJurisdictionMap& voxelServerJurisdictions = Application::getInstance()->getVoxelServerJurisdictions();
    NodeToJurisdictionMap& modelServerJurisdictions = Application::getInstance()->getModelServerJurisdictions();
    NodeToJurisdictionMap& particleServerJurisdictions = Application::getInstance()->getParticleServerJurisdictions();
    NodeToJurisdictionMap* serverJurisdictions;

    // Compute ray to find selected nodes later on.  We can't use the pre-computed ray in Application because it centers
    // itself after the cursor disappears.
    Application* application = Application::getInstance();
    QGLWidget* glWidget = application->getGLWidget();
    float mouseX = application->getMouseX() / (float)glWidget->width();
    float mouseY = application->getMouseY() / (float)glWidget->height();
    glm::vec3 mouseRayOrigin;
    glm::vec3 mouseRayDirection;
    application->getViewFrustum()->computePickRay(mouseX, mouseY, mouseRayOrigin, mouseRayDirection);

    // Variables to keep track of the selected node and properties to draw the cube later if needed
    Node* selectedNode = NULL;
    float selectedDistance = FLT_MAX;
    bool selectedIsInside = true;
    glm::vec3 selectedCenter;
    float selectedScale = 0;

    NodeList* nodeList = NodeList::getInstance();

    foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
        NodeType_t nodeType = node->getType();

        if (nodeType == NodeType::VoxelServer && _showVoxelNodes) {
            serverJurisdictions = &voxelServerJurisdictions;
        } else if (nodeType == NodeType::ModelServer && _showModelNodes) {
            serverJurisdictions = &modelServerJurisdictions;
        } else if (nodeType == NodeType::ParticleServer && _showParticleNodes) {
            serverJurisdictions = &particleServerJurisdictions;
        } else {
            continue;
        }

        QUuid nodeUUID = node->getUUID();
        if (serverJurisdictions->find(nodeUUID) != serverJurisdictions->end()) {
            const JurisdictionMap& map = serverJurisdictions->value(nodeUUID);

            unsigned char* rootCode = map.getRootOctalCode();

            if (rootCode) {
                VoxelPositionSize rootDetails;
                voxelDetailsForCode(rootCode, rootDetails);
                glm::vec3 location(rootDetails.x, rootDetails.y, rootDetails.z);
                location *= (float)TREE_SCALE;

                AABox serverBounds(location, rootDetails.s * TREE_SCALE);

                glm::vec3 center = serverBounds.getVertex(BOTTOM_RIGHT_NEAR)
                    + ((serverBounds.getVertex(TOP_LEFT_FAR) - serverBounds.getVertex(BOTTOM_RIGHT_NEAR)) / 2.0f);

                const float VOXEL_NODE_SCALE = 1.00f;
                const float MODEL_NODE_SCALE = 0.99f;
                const float PARTICLE_NODE_SCALE = 0.98f;

                float scaleFactor = rootDetails.s * TREE_SCALE;

                // Scale by 0.92 - 1.00 depending on the scale of the node.  This allows smaller nodes to scale in
                // a bit and not overlap larger nodes.
                scaleFactor *= 0.92 + (rootDetails.s * 0.08);

                // Scale different node types slightly differently because it's common for them to overlap.
                if (nodeType == NodeType::VoxelServer) {
                    scaleFactor *= VOXEL_NODE_SCALE;
                } else if (nodeType == NodeType::ModelServer) {
                    scaleFactor *= MODEL_NODE_SCALE;
                } else {
                    scaleFactor *= PARTICLE_NODE_SCALE;
                }

                float red, green, blue;
                getColorForNodeType(nodeType, red, green, blue);
                drawNodeBorder(center, scaleFactor, red, green, blue);

                float distance;
                BoxFace face;
                bool inside = serverBounds.contains(mouseRayOrigin);
                bool colliding = serverBounds.findRayIntersection(mouseRayOrigin, mouseRayDirection, distance, face);

                // If the camera is inside a node it will be "selected" if you don't have your cursor over another node
                // that you aren't inside.
                if (colliding && (!selectedNode || (!inside && (distance < selectedDistance || selectedIsInside)))) {
                    selectedNode = node.data();
                    selectedDistance = distance;
                    selectedIsInside = inside;
                    selectedCenter = center;
                    selectedScale = scaleFactor;
                }
            }
        }
    }

    if (selectedNode) {
        glPushMatrix();

        glTranslatef(selectedCenter.x, selectedCenter.y, selectedCenter.z);
        glScalef(selectedScale, selectedScale, selectedScale);

        NodeType_t selectedNodeType = selectedNode->getType();
        float red, green, blue;
        getColorForNodeType(selectedNode->getType(), red, green, blue);

        glColor4f(red, green, blue, 0.2);
        glutSolidCube(1.0);

        glPopMatrix();

        HifiSockAddr addr = selectedNode->getPublicSocket();
        QString overlay = QString("%1:%2  %3ms")
            .arg(addr.getAddress().toString())
            .arg(addr.getPort())
            .arg(selectedNode->getPingMs())
            .left(MAX_OVERLAY_TEXT_LENGTH);

        // Ideally we'd just use a QString, but I ran into weird blinking issues using
        // constData() directly, as if the data was being overwritten.
        strcpy(_overlayText, overlay.toLocal8Bit().constData());
    } else {
        _overlayText[0] = '\0';
    }
}
Exemplo n.º 6
0
// NOTE: some additional optimizations to consider.
//    1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
//       if the avatar is not in view or in the keyhole.
void AvatarMixer::broadcastAvatarData() {

    int idleTime = QDateTime::currentMSecsSinceEpoch() - _lastFrameTimestamp;

    ++_numStatFrames;

    const float STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.10f;
    const float BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD = 0.20f;

    const float RATIO_BACK_OFF = 0.02f;

    const int TRAILING_AVERAGE_FRAMES = 100;
    int framesSinceCutoffEvent = TRAILING_AVERAGE_FRAMES;

    const float CURRENT_FRAME_RATIO = 1.0f / TRAILING_AVERAGE_FRAMES;
    const float PREVIOUS_FRAMES_RATIO = 1.0f - CURRENT_FRAME_RATIO;

    _trailingSleepRatio = (PREVIOUS_FRAMES_RATIO * _trailingSleepRatio)
                          + (idleTime * CURRENT_FRAME_RATIO / (float) AVATAR_DATA_SEND_INTERVAL_MSECS);

    float lastCutoffRatio = _performanceThrottlingRatio;
    bool hasRatioChanged = false;

    if (framesSinceCutoffEvent >= TRAILING_AVERAGE_FRAMES) {
        if (_trailingSleepRatio <= STRUGGLE_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD) {
            // we're struggling - change our min required loudness to reduce some load
            _performanceThrottlingRatio = _performanceThrottlingRatio + (0.5f * (1.0f - _performanceThrottlingRatio));

            qDebug() << "Mixer is struggling, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
                     << lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
            hasRatioChanged = true;
        } else if (_trailingSleepRatio >= BACK_OFF_TRIGGER_SLEEP_PERCENTAGE_THRESHOLD && _performanceThrottlingRatio != 0) {
            // we've recovered and can back off the required loudness
            _performanceThrottlingRatio = _performanceThrottlingRatio - RATIO_BACK_OFF;

            if (_performanceThrottlingRatio < 0) {
                _performanceThrottlingRatio = 0;
            }

            qDebug() << "Mixer is recovering, sleeping" << _trailingSleepRatio * 100 << "% of frame time. Old cutoff was"
                     << lastCutoffRatio << "and is now" << _performanceThrottlingRatio;
            hasRatioChanged = true;
        }

        if (hasRatioChanged) {
            framesSinceCutoffEvent = 0;
        }
    }

    if (!hasRatioChanged) {
        ++framesSinceCutoffEvent;
    }

    static QByteArray mixedAvatarByteArray;

    int numPacketHeaderBytes = populatePacketHeader(mixedAvatarByteArray, PacketTypeBulkAvatarData);

    NodeList* nodeList = NodeList::getInstance();

    AvatarMixerClientData* nodeData = NULL;
    AvatarMixerClientData* otherNodeData = NULL;

    foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
        if (node->getLinkedData() && node->getType() == NodeType::Agent && node->getActiveSocket()
                && (nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData()))->getMutex().tryLock()) {
            ++_sumListeners;

            // reset packet pointers for this node
            mixedAvatarByteArray.resize(numPacketHeaderBytes);

            AvatarData& avatar = nodeData->getAvatar();
            glm::vec3 myPosition = avatar.getPosition();

            // this is an AGENT we have received head data from
            // send back a packet with other active node data to this node
            foreach (const SharedNodePointer& otherNode, nodeList->getNodeHash()) {
                if (otherNode->getLinkedData() && otherNode->getUUID() != node->getUUID()
                        && (otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData()))->getMutex().tryLock()) {

                    AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
                    AvatarData& otherAvatar = otherNodeData->getAvatar();
                    glm::vec3 otherPosition = otherAvatar.getPosition();

                    float distanceToAvatar = glm::length(myPosition - otherPosition);
                    //  The full rate distance is the distance at which EVERY update will be sent for this avatar
                    //  at a distance of twice the full rate distance, there will be a 50% chance of sending this avatar's update
                    const float FULL_RATE_DISTANCE = 2.0f;

                    //  Decide whether to send this avatar's data based on it's distance from us
                    if ((_performanceThrottlingRatio == 0 || randFloat() < (1.0f - _performanceThrottlingRatio))
                            && (distanceToAvatar == 0.0f || randFloat() < FULL_RATE_DISTANCE / distanceToAvatar)) {
                        QByteArray avatarByteArray;
                        avatarByteArray.append(otherNode->getUUID().toRfc4122());
                        avatarByteArray.append(otherAvatar.toByteArray());

                        if (avatarByteArray.size() + mixedAvatarByteArray.size() > MAX_PACKET_SIZE) {
                            nodeList->writeDatagram(mixedAvatarByteArray, node);

                            // reset the packet
                            mixedAvatarByteArray.resize(numPacketHeaderBytes);
                        }

                        // copy the avatar into the mixedAvatarByteArray packet
                        mixedAvatarByteArray.append(avatarByteArray);

                        // if the receiving avatar has just connected make sure we send out the mesh and billboard
                        // for this avatar (assuming they exist)
                        bool forceSend = !nodeData->checkAndSetHasReceivedFirstPackets();

                        // we will also force a send of billboard or identity packet
                        // if either has changed in the last frame

                        if (otherNodeData->getBillboardChangeTimestamp() > 0
                                && (forceSend
                                    || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
                                    || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
                            QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
                            billboardPacket.append(otherNode->getUUID().toRfc4122());
                            billboardPacket.append(otherNodeData->getAvatar().getBillboard());
                            nodeList->writeDatagram(billboardPacket, node);

                            ++_sumBillboardPackets;
                        }

                        if (otherNodeData->getIdentityChangeTimestamp() > 0
                                && (forceSend
                                    || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
                                    || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {

                            QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);

                            QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
                            individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
                            identityPacket.append(individualData);

                            nodeList->writeDatagram(identityPacket, node);

                            ++_sumIdentityPackets;
                        }
                    }

                    otherNodeData->getMutex().unlock();
                }
            }

            nodeList->writeDatagram(mixedAvatarByteArray, node);

            nodeData->getMutex().unlock();
        }
    }
Exemplo n.º 7
0
void AudioMixer::run() {

    commonInit(AUDIO_MIXER_LOGGING_TARGET_NAME, NodeType::AudioMixer);

    NodeList* nodeList = NodeList::getInstance();

    nodeList->addNodeTypeToInterestSet(NodeType::Agent);

    nodeList->linkedDataCreateCallback = attachNewBufferToNode;

    int nextFrame = 0;
    timeval startTime;

    gettimeofday(&startTime, NULL);
    
    char* clientMixBuffer = new char[NETWORK_BUFFER_LENGTH_BYTES_STEREO
                                     + numBytesForPacketHeaderGivenPacketType(PacketTypeMixedAudio)];

    while (!_isFinished) {

        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getLinkedData()) {
                ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES);
            }
        }

        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData()
                && ((AudioMixerClientData*) node->getLinkedData())->getAvatarAudioRingBuffer()) {
                prepareMixForListeningNode(node.data());
                
                int numBytesPacketHeader = populatePacketHeader(clientMixBuffer, PacketTypeMixedAudio);

                memcpy(clientMixBuffer + numBytesPacketHeader, _clientSamples, NETWORK_BUFFER_LENGTH_BYTES_STEREO);
                nodeList->writeDatagram(clientMixBuffer, NETWORK_BUFFER_LENGTH_BYTES_STEREO + numBytesPacketHeader, node);
            }
        }

        // push forward the next output pointers for any audio buffers we used
        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getLinkedData()) {
                ((AudioMixerClientData*) node->getLinkedData())->pushBuffersAfterFrameSend();
            }
        }
        
        QCoreApplication::processEvents();
        
        if (_isFinished) {
            break;
        }

        int usecToSleep = usecTimestamp(&startTime) + (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - usecTimestampNow();

        if (usecToSleep > 0) {
            usleep(usecToSleep);
        } else {
            qDebug() << "AudioMixer loop took" << -usecToSleep << "of extra time. Not sleeping.";
        }

    }
    
    delete[] clientMixBuffer;
}
Exemplo n.º 8
0
// display expanded or contracted stats
void Stats::display(
        const float* color, 
        int horizontalOffset, 
        float fps, 
        int packetsPerSecond, 
        int bytesPerSecond, 
        int voxelPacketsToProcess) 
{
    GLCanvas* glWidget = Application::getInstance()->getGLWidget();

    unsigned int backgroundColor = 0x33333399;
    int verticalOffset = 0, lines = 0;
    float scale = 0.10f;
    float rotation = 0.0f;
    int font = 2;

    QLocale locale(QLocale::English);
    std::stringstream voxelStats;

    if (_lastHorizontalOffset != horizontalOffset) {
        resetWidth(glWidget->width(), horizontalOffset);
        _lastHorizontalOffset = horizontalOffset;
    }

    glPointSize(1.0f);

    // we need to take one avatar out so we don't include ourselves
    int totalAvatars = Application::getInstance()->getAvatarManager().size() - 1;
    int totalServers = NodeList::getInstance()->size();

    lines = _expanded ? 5 : 3;
    int columnOneWidth = _generalStatsWidth;

    PerformanceTimer::tallyAllTimerRecords(); // do this even if we're not displaying them, so they don't stack up
    
    if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {

        columnOneWidth = _generalStatsWidth + _pingStatsWidth + _geoStatsWidth; // make it 3 columns wide...
        // we will also include room for 1 line per timing record and a header of 4 lines
        lines += 4;

        const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
        QMapIterator<QString, PerformanceTimerRecord> i(allRecords);
        while (i.hasNext()) {
            i.next();
            if (includeTimingRecord(i.key())) {
                lines++;
            }
        }
    }
    
    drawBackground(backgroundColor, horizontalOffset, 0, columnOneWidth, lines * STATS_PELS_PER_LINE + 10);
    horizontalOffset += 5;
    
    int columnOneHorizontalOffset = horizontalOffset;

    char serverNodes[30];
    sprintf(serverNodes, "Servers: %d", totalServers);
    char avatarNodes[30];
    sprintf(avatarNodes, "Avatars: %d", totalAvatars);
    char framesPerSecond[30];
    sprintf(framesPerSecond, "Framerate: %3.0f FPS", fps);
 
    verticalOffset += STATS_PELS_PER_LINE;
    drawText(horizontalOffset, verticalOffset, scale, rotation, font, serverNodes, color);
    verticalOffset += STATS_PELS_PER_LINE;
    drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarNodes, color);
    verticalOffset += STATS_PELS_PER_LINE;
    drawText(horizontalOffset, verticalOffset, scale, rotation, font, framesPerSecond, color);

    if (_expanded) {
        char packetsPerSecondString[30];
        sprintf(packetsPerSecondString, "Pkts/sec: %d", packetsPerSecond);
        char averageMegabitsPerSecond[30];
        sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.0f / 1000000.0f);

        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString, color);
        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, scale, rotation, font, averageMegabitsPerSecond, color);
    }
    
    // TODO: the display of these timing details should all be moved to JavaScript
    if (_expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayTimingDetails)) {
        // Timing details...
        const int TIMER_OUTPUT_LINE_LENGTH = 1000;
        char perfLine[TIMER_OUTPUT_LINE_LENGTH];
        verticalOffset += STATS_PELS_PER_LINE * 4; // skip 4 lines to be under the other columns
        drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, 
                "-------------------------------------------------------- Function "
                "------------------------------------------------------- --msecs- -calls--", color);

        // First iterate all the records, and for the ones that should be included, insert them into 
        // a new Map sorted by average time...
        QMap<float, QString> sortedRecords;
        const QMap<QString, PerformanceTimerRecord>& allRecords = PerformanceTimer::getAllTimerRecords();
        QMapIterator<QString, PerformanceTimerRecord> i(allRecords);

        while (i.hasNext()) {
            i.next();
            if (includeTimingRecord(i.key())) {
                float averageTime = (float)i.value().getMovingAverage() / (float)USECS_PER_MSEC;
                sortedRecords.insertMulti(averageTime, i.key());
            }
        }

        QMapIterator<float, QString> j(sortedRecords);
        j.toBack();
        while (j.hasPrevious()) {
            j.previous();
            QString functionName = j.value(); 
            const PerformanceTimerRecord& record = allRecords.value(functionName);

            sprintf(perfLine, "%120s: %8.4f [%6llu]", qPrintable(functionName),
                        (float)record.getMovingAverage() / (float)USECS_PER_MSEC,
                        record.getCount());
        
            verticalOffset += STATS_PELS_PER_LINE;
            drawText(columnOneHorizontalOffset, verticalOffset, scale, rotation, font, perfLine, color);
        }
    }

    verticalOffset = 0;
    horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1;

    if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
        int pingAudio = -1, pingAvatar = -1, pingVoxel = -1, pingVoxelMax = -1;

        NodeList* nodeList = NodeList::getInstance();
        SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
        SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);

        pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : -1;
        pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : -1;

        // Now handle voxel servers, since there could be more than one, we average their ping times
        unsigned long totalPingVoxel = 0;
        int voxelServerCount = 0;

        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            // TODO: this should also support entities
            if (node->getType() == NodeType::VoxelServer) {
                totalPingVoxel += node->getPingMs();
                voxelServerCount++;
                if (pingVoxelMax < node->getPingMs()) {
                    pingVoxelMax = node->getPingMs();
                }
            }
        }

        if (voxelServerCount) {
            pingVoxel = totalPingVoxel/voxelServerCount;
        }

        lines = _expanded ? 4 : 3;
        
        // only draw our background if column one didn't draw a wide background
        if (columnOneWidth == _generalStatsWidth) {
            drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
        }
        horizontalOffset += 5;

        
        char audioPing[30];
        if (pingAudio >= 0) {
            sprintf(audioPing, "Audio ping: %d", pingAudio);
        } else {
            sprintf(audioPing, "Audio ping: --");
        }

        char avatarPing[30];
        if (pingAvatar >= 0) {
            sprintf(avatarPing, "Avatar ping: %d", pingAvatar);
        } else {
            sprintf(avatarPing, "Avatar ping: --");
        }

        char voxelAvgPing[30];
        if (pingVoxel >= 0) {
            sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);
        } else {
            sprintf(voxelAvgPing, "Voxel avg ping: --");
        }

        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, scale, rotation, font, audioPing, color);
        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, scale, rotation, font, avatarPing, color);
        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelAvgPing, color);

        if (_expanded) {
            char voxelMaxPing[30];
            if (pingVoxel >= 0) {  // Average is only meaningful if pingVoxel is valid.
                sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);
            } else {
                sprintf(voxelMaxPing, "Voxel max ping: --");
            }

            verticalOffset += STATS_PELS_PER_LINE;
            drawText(horizontalOffset, verticalOffset, scale, rotation, font, voxelMaxPing, color);
        }

        verticalOffset = 0;
        horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
    }
Exemplo n.º 9
0
void ScriptEngine::run() {
    if (!_isInitialized) {
        init();
    }
    _isRunning = true;
    _isFinished = false;
    emit runningStateChanged();

    QScriptValue result = evaluate(_scriptContents);
    if (hasUncaughtException()) {
        int line = uncaughtExceptionLineNumber();
        qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
        emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString());
        clearExceptions();
    }

    QElapsedTimer startTime;
    startTime.start();

    int thisFrame = 0;

    NodeList* nodeList = NodeList::getInstance();

    qint64 lastUpdate = usecTimestampNow();

    while (!_isFinished) {
        int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec
        if (usecToSleep > 0) {
            usleep(usecToSleep);
        }

        if (_isFinished) {
            break;
        }

        QCoreApplication::processEvents();

        if (_isFinished) {
            break;
        }

        if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) {
            // release the queue of edit voxel messages.
            _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages();

            // since we're in non-threaded mode, call process so that the packets are sent
            if (!_voxelsScriptingInterface.getVoxelPacketSender()->isThreaded()) {
                _voxelsScriptingInterface.getVoxelPacketSender()->process();
            }
        }

        if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) {
            // release the queue of edit voxel messages.
            _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages();

            // since we're in non-threaded mode, call process so that the packets are sent
            if (!_particlesScriptingInterface.getParticlePacketSender()->isThreaded()) {
                _particlesScriptingInterface.getParticlePacketSender()->process();
            }
        }

        if (_entityScriptingInterface.getEntityPacketSender()->serversExist()) {
            // release the queue of edit voxel messages.
            _entityScriptingInterface.getEntityPacketSender()->releaseQueuedMessages();

            // since we're in non-threaded mode, call process so that the packets are sent
            if (!_entityScriptingInterface.getEntityPacketSender()->isThreaded()) {
                _entityScriptingInterface.getEntityPacketSender()->process();
            }
        }

        if (_isAvatar && _avatarData) {

            const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000)) + 0.5);
            const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t);

            QByteArray avatarPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData);
            avatarPacket.append(_avatarData->toByteArray());

            nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer);

            if (_isListeningToAudioStream || _avatarSound) {
                // if we have an avatar audio stream then send it out to our audio-mixer
                bool silentFrame = true;

                int16_t numAvailableSamples = SCRIPT_AUDIO_BUFFER_SAMPLES;
                const int16_t* nextSoundOutput = NULL;

                if (_avatarSound) {

                    const QByteArray& soundByteArray = _avatarSound->getByteArray();
                    nextSoundOutput = reinterpret_cast<const int16_t*>(soundByteArray.data()
                                                                       + _numAvatarSoundSentBytes);

                    int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > SCRIPT_AUDIO_BUFFER_BYTES
                        ? SCRIPT_AUDIO_BUFFER_BYTES
                        : soundByteArray.size() - _numAvatarSoundSentBytes;
                    numAvailableSamples = numAvailableBytes / sizeof(int16_t);


                    // check if the all of the _numAvatarAudioBufferSamples to be sent are silence
                    for (int i = 0; i < numAvailableSamples; ++i) {
                        if (nextSoundOutput[i] != 0) {
                            silentFrame = false;
                            break;
                        }
                    }

                    _numAvatarSoundSentBytes += numAvailableBytes;
                    if (_numAvatarSoundSentBytes == soundByteArray.size()) {
                        // we're done with this sound object - so set our pointer back to NULL
                        // and our sent bytes back to zero
                        _avatarSound = NULL;
                        _numAvatarSoundSentBytes = 0;
                    }
                }
                
                QByteArray audioPacket = byteArrayWithPopulatedHeader(silentFrame
                                                                      ? PacketTypeSilentAudioFrame
                                                                      : PacketTypeMicrophoneAudioNoEcho);

                QDataStream packetStream(&audioPacket, QIODevice::Append);

                // pack a placeholder value for sequence number for now, will be packed when destination node is known
                int numPreSequenceNumberBytes = audioPacket.size();
                packetStream << (quint16) 0;

                if (silentFrame) {
                    if (!_isListeningToAudioStream) {
                        // if we have a silent frame and we're not listening then just send nothing and break out of here
                        break;
                    }

                    // write the number of silent samples so the audio-mixer can uphold timing
                    packetStream.writeRawData(reinterpret_cast<const char*>(&SCRIPT_AUDIO_BUFFER_SAMPLES), sizeof(int16_t));

                    // use the orientation and position of this avatar for the source of this audio
                    packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3));
                    glm::quat headOrientation = _avatarData->getHeadOrientation();
                    packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat));

                } else if (nextSoundOutput) {
                    // assume scripted avatar audio is mono and set channel flag to zero
                    packetStream << (quint8)0;

                    // use the orientation and position of this avatar for the source of this audio
                    packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3));
                    glm::quat headOrientation = _avatarData->getHeadOrientation();
                    packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat));

                    // write the raw audio data
                    packetStream.writeRawData(reinterpret_cast<const char*>(nextSoundOutput), numAvailableSamples * sizeof(int16_t));
                }
                
                // write audio packet to AudioMixer nodes
                NodeList* nodeList = NodeList::getInstance();
                foreach(const SharedNodePointer& node, nodeList->getNodeHash()) {
                    // only send to nodes of type AudioMixer
                    if (node->getType() == NodeType::AudioMixer) {
                        // pack sequence number
                        quint16 sequence = _outgoingScriptAudioSequenceNumbers[node->getUUID()]++;
                        memcpy(audioPacket.data() + numPreSequenceNumberBytes, &sequence, sizeof(quint16));

                        // send audio packet
                        nodeList->writeDatagram(audioPacket, node);
                    }
                }
            }
        }

        qint64 now = usecTimestampNow();
        float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND;

        if (hasUncaughtException()) {
            int line = uncaughtExceptionLineNumber();
            qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString();
            emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString());
            clearExceptions();
        }

        emit update(deltaTime);
        lastUpdate = now;
    }
Exemplo n.º 10
0
// display expanded or contracted stats
void Stats::display(
        const float* color, 
        int horizontalOffset, 
        float fps, 
        int packetsPerSecond, 
        int bytesPerSecond, 
        int voxelPacketsToProcess) 
{
    QGLWidget* glWidget = Application::getInstance()->getGLWidget();

    unsigned int backgroundColor = 0x33333399;
    int verticalOffset = 0, lines = 0;

    QLocale locale(QLocale::English);
    std::stringstream voxelStats;

    if (_lastHorizontalOffset != horizontalOffset) {
        resetWidth(glWidget->width(), horizontalOffset);
        _lastHorizontalOffset = horizontalOffset;
    }

    glPointSize(1.0f);

    // we need to take one avatar out so we don't include ourselves
    int totalAvatars = Application::getInstance()->getAvatarManager().size() - 1;
    int totalServers = NodeList::getInstance()->size();

    lines = _expanded ? 5 : 3;
    drawBackground(backgroundColor, horizontalOffset, 0, _generalStatsWidth, lines * STATS_PELS_PER_LINE + 10);
    horizontalOffset += 5;

    char serverNodes[30];
    sprintf(serverNodes, "Servers: %d", totalServers);
    char avatarNodes[30];
    sprintf(avatarNodes, "Avatars: %d", totalAvatars);
    char framesPerSecond[30];
    sprintf(framesPerSecond, "Framerate: %3.0f FPS", fps);
 
    verticalOffset += STATS_PELS_PER_LINE;
    drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, serverNodes, color);
    verticalOffset += STATS_PELS_PER_LINE;
    drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarNodes, color);
    verticalOffset += STATS_PELS_PER_LINE;
    drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, framesPerSecond, color);

    if (_expanded) {
        char packetsPerSecondString[30];
        sprintf(packetsPerSecondString, "Pkts/sec: %d", packetsPerSecond);
        char averageMegabitsPerSecond[30];
        sprintf(averageMegabitsPerSecond, "Mbps: %3.2f", (float)bytesPerSecond * 8.f / 1000000.f);

        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, packetsPerSecondString, color);
        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, averageMegabitsPerSecond, color);
    }

    verticalOffset = 0;
    horizontalOffset = _lastHorizontalOffset + _generalStatsWidth +1;

    if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
        int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0;

        NodeList* nodeList = NodeList::getInstance();
        SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
        SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);

        pingAudio = audioMixerNode ? audioMixerNode->getPingMs() : 0;
        pingAvatar = avatarMixerNode ? avatarMixerNode->getPingMs() : 0;

        // Now handle voxel servers, since there could be more than one, we average their ping times
        unsigned long totalPingVoxel = 0;
        int voxelServerCount = 0;

        foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
            if (node->getType() == NodeType::VoxelServer) {
                totalPingVoxel += node->getPingMs();
                voxelServerCount++;
                if (pingVoxelMax < node->getPingMs()) {
                    pingVoxelMax = node->getPingMs();
                }
            }
        }

        if (voxelServerCount) {
            pingVoxel = totalPingVoxel/voxelServerCount;
        }

        lines = _expanded ? 4 : 3;
        drawBackground(backgroundColor, horizontalOffset, 0, _pingStatsWidth, lines * STATS_PELS_PER_LINE + 10);
        horizontalOffset += 5;

        Audio* audio = Application::getInstance()->getAudio();

        char audioJitter[30];
        sprintf(audioJitter,
                "Buffer msecs %.1f",
                (float) (audio->getNetworkBufferLengthSamplesPerChannel() + (float) audio->getJitterBufferSamples()) /
                (float) audio->getNetworkSampleRate() * 1000.f);
        drawText(30, glWidget->height() - 22, 0.10f, 0.f, 2.f, audioJitter, color);
        
                 
        char audioPing[30];
        sprintf(audioPing, "Audio ping: %d", pingAudio);
       
                 
        char avatarPing[30];
        sprintf(avatarPing, "Avatar ping: %d", pingAvatar);
        char voxelAvgPing[30];
        sprintf(voxelAvgPing, "Voxel avg ping: %d", pingVoxel);

        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, audioPing, color);
        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarPing, color);
        verticalOffset += STATS_PELS_PER_LINE;
        drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelAvgPing, color);

        if (_expanded) {
            char voxelMaxPing[30];
            sprintf(voxelMaxPing, "Voxel max ping: %d", pingVoxelMax);

            verticalOffset += STATS_PELS_PER_LINE;
            drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, voxelMaxPing, color);
        }

        verticalOffset = 0;
        horizontalOffset = _lastHorizontalOffset + _generalStatsWidth + _pingStatsWidth + 2;
    }