From 2cc42f67738313e9a63b0bcccd1f6bd23fc4c4ac Mon Sep 17 00:00:00 2001
From: ZappoMan <bradh@konamoxt.com>
Date: Fri, 19 Jul 2013 15:59:47 -0700
Subject: [PATCH] support piggybacking voxel and voxel stats messages into
 single packet

---
 interface/src/Application.cpp            | 35 +++++++++----
 libraries/voxels/src/VoxelSceneStats.cpp |  4 ++
 libraries/voxels/src/VoxelSceneStats.h   | 12 +++++
 voxel-server/src/main.cpp                | 64 ++++++++++++++++--------
 4 files changed, 85 insertions(+), 30 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 524b5ccd09..345ede0274 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3339,12 +3339,6 @@ void* Application::networkReceive(void* args) {
             if (packetVersionMatch(app->_incomingPacket)) {
                 // only process this packet if we have a match on the packet version
                 switch (app->_incomingPacket[0]) {
-                    case PACKET_TYPE_VOXEL_STATS:{
-                        VoxelSceneStats stats;
-                        int statsMessageLength = stats.unpackFromMessage(app->_incomingPacket, bytesReceived);
-                        stats.printDebugDetails();
-                        break;
-                    }
                     case PACKET_TYPE_TRANSMITTER_DATA_V2:
                         //  V2 = IOS transmitter app
                         app->_myTransmitter.processIncomingData(app->_incomingPacket, bytesReceived);
@@ -3357,16 +3351,39 @@ void* Application::networkReceive(void* args) {
                     case PACKET_TYPE_VOXEL_DATA_MONOCHROME:
                     case PACKET_TYPE_Z_COMMAND:
                     case PACKET_TYPE_ERASE_VOXEL:
+                    case PACKET_TYPE_VOXEL_STATS:
                     case PACKET_TYPE_ENVIRONMENT_DATA: {
+                    
+                        unsigned char* messageData = app->_incomingPacket;
+                        ssize_t messageLength = bytesReceived;
+
+                        // note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME
+                        // immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
+                        // then process any remaining bytes as if it was another packet
+                        if (messageData[0] == PACKET_TYPE_VOXEL_STATS) {
+                            VoxelSceneStats stats;
+                            int statsMessageLength = stats.unpackFromMessage(messageData, messageLength);
+                            stats.printDebugDetails();
+                            if (messageLength > statsMessageLength) {
+                                messageData += statsMessageLength;
+                                messageLength -= statsMessageLength;
+                                if (!packetVersionMatch(messageData)) {
+                                    break; // bail since piggyback data doesn't match our versioning
+                                }
+                            } else {
+                                break; // bail since no piggyback data
+                            }
+                        } // fall through to piggyback message
+                        
                         if (app->_renderVoxels->isChecked()) {
                             Node* voxelServer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_VOXEL_SERVER);
                             if (voxelServer) {
                                 voxelServer->lock();
                                 
-                                if (app->_incomingPacket[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
-                                    app->_environment.parseData(&senderAddress, app->_incomingPacket, bytesReceived);
+                                if (messageData[0] == PACKET_TYPE_ENVIRONMENT_DATA) {
+                                    app->_environment.parseData(&senderAddress, messageData, messageLength);
                                 } else {
-                                    app->_voxels.parseData(app->_incomingPacket, bytesReceived);
+                                    app->_voxels.parseData(messageData, messageLength);
                                 }
                                 
                                 voxelServer->unlock();
diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp
index 0368c530ec..ab25b725bb 100644
--- a/libraries/voxels/src/VoxelSceneStats.cpp
+++ b/libraries/voxels/src/VoxelSceneStats.cpp
@@ -14,6 +14,7 @@
 
 VoxelSceneStats::VoxelSceneStats() {
     reset();
+    _readyToSend = false;
 }
 
 VoxelSceneStats::~VoxelSceneStats() {
@@ -29,6 +30,9 @@ void VoxelSceneStats::sceneStarted(bool fullScene, bool moving) {
 void VoxelSceneStats::sceneCompleted() {
     _end = usecTimestampNow();
     _elapsed = _end - _start;
+
+    _statsMessageLength = packIntoMessage(_statsMessage, sizeof(_statsMessage));
+    _readyToSend = true;
 }
 
 void VoxelSceneStats::encodeStarted() {
diff --git a/libraries/voxels/src/VoxelSceneStats.h b/libraries/voxels/src/VoxelSceneStats.h
index 93146ffa88..05545f3320 100644
--- a/libraries/voxels/src/VoxelSceneStats.h
+++ b/libraries/voxels/src/VoxelSceneStats.h
@@ -10,6 +10,7 @@
 #define __hifi__VoxelSceneStats__
 
 #include <stdint.h>
+#include <NodeList.h>
 
 class VoxelNode;
 
@@ -20,6 +21,7 @@ public:
     void reset();
     void sceneStarted(bool fullScene, bool moving);
     void sceneCompleted();
+
     void printDebugDetails();
     void packetSent(int bytes);
 
@@ -41,8 +43,18 @@ public:
 
     int packIntoMessage(unsigned char* destinationBuffer, int availableBytes);
     int unpackFromMessage(unsigned char* sourceBuffer, int availableBytes);
+
+    bool readyToSend() const { return _readyToSend; }
+    void markAsSent() { _readyToSend = false; }
+    unsigned char* getStatsMessage() { return &_statsMessage[0]; }
+    int getStatsMessageLength() const { return _statsMessageLength; }
+
     
 private:
+    bool _readyToSend;
+    unsigned char _statsMessage[MAX_PACKET_SIZE];
+    int _statsMessageLength;
+
     // scene timing data in usecs
     uint64_t _start;
     uint64_t _end;
diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp
index 5ae2874a34..6244ee190d 100644
--- a/voxel-server/src/main.cpp
+++ b/voxel-server/src/main.cpp
@@ -112,6 +112,44 @@ void eraseVoxelTreeAndCleanupNodeVisitData() {
 
 pthread_mutex_t treeLock;
 
+void handlePacketSend(NodeList* nodeList, 
+                      NodeList::iterator& node,
+                      VoxelNodeData* nodeData, 
+                      int& trueBytesSent, int& truePacketsSent) {
+    // If we've got a stats message ready to send, then see if we can piggyback them together
+    if (nodeData->stats.readyToSend()) {
+        // Send the stats message to the client
+        unsigned char* statsMessage = nodeData->stats.getStatsMessage();
+        int statsMessageLength = nodeData->stats.getStatsMessageLength();
+
+        // If the size of the stats message and the voxel message will fit in a packet, then piggyback them
+        if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) {
+
+            // copy voxel message to back of stats message
+            memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
+            statsMessageLength += nodeData->getPacketLength();
+
+            // actually send it
+            nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
+        } else {
+            // not enough room in the packet, send two packets
+            nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
+            nodeList->getNodeSocket()->send(node->getActiveSocket(),
+                                            nodeData->getPacket(), nodeData->getPacketLength());
+        }
+    } else {
+        // just send the voxel packet
+        nodeList->getNodeSocket()->send(node->getActiveSocket(),
+                                        nodeData->getPacket(), nodeData->getPacketLength());
+    }
+    // remember to track our stats
+    nodeData->stats.packetSent(nodeData->getPacketLength());
+    trueBytesSent += nodeData->getPacketLength();
+    truePacketsSent++;
+    nodeData->resetVoxelPacket();
+}
+
+
 // Version of voxel distributor that sends the deepest LOD level at once
 void deepestLevelVoxelDistributor(NodeList* nodeList, 
                                   NodeList::iterator& node,
@@ -142,12 +180,9 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
                 printf("wantColor=%s --- SENDING PARTIAL PACKET! nodeData->getCurrentPacketIsColor()=%s\n", 
                        debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()));
             }
-            nodeList->getNodeSocket()->send(node->getActiveSocket(),
-                                            nodeData->getPacket(), nodeData->getPacketLength());
-            nodeData->stats.packetSent(nodeData->getPacketLength());
-            trueBytesSent += nodeData->getPacketLength();
-            truePacketsSent++;
-            nodeData->resetVoxelPacket();
+
+            handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent);
+
         } else {
             if (::debugVoxelSending) {
                 printf("wantColor=%s --- FIXING HEADER! nodeData->getCurrentPacketIsColor()=%s\n", 
@@ -209,11 +244,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
         if (::displayVoxelStats) {
             nodeData->stats.printDebugDetails();
         }
-
-        // Send the stats message to the client
-        unsigned char statsMessage[MAX_PACKET_SIZE];
-        int statsMessageLength = nodeData->stats.packIntoMessage(statsMessage, sizeof(statsMessage));
-        nodeList->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
         
         // This is the start of "resending" the scene.
         nodeData->nodeBag.insert(serverTree.rootNode);
@@ -271,22 +301,14 @@ void deepestLevelVoxelDistributor(NodeList* nodeList,
                 if (nodeData->getAvailable() >= bytesWritten) {
                     nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
                 } else {
-                    nodeList->getNodeSocket()->send(node->getActiveSocket(),
-                                                    nodeData->getPacket(), nodeData->getPacketLength());
-                    nodeData->stats.packetSent(nodeData->getPacketLength());
-                    trueBytesSent += nodeData->getPacketLength();
-                    truePacketsSent++;
+                    handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent);
                     packetsSentThisInterval++;
                     nodeData->resetVoxelPacket();
                     nodeData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
                 }
             } else {
                 if (nodeData->isPacketWaiting()) {
-                    nodeList->getNodeSocket()->send(node->getActiveSocket(),
-                                                    nodeData->getPacket(), nodeData->getPacketLength());
-                    nodeData->stats.packetSent(nodeData->getPacketLength());
-                    trueBytesSent += nodeData->getPacketLength();
-                    truePacketsSent++;
+                    handlePacketSend(nodeList, node, nodeData, trueBytesSent, truePacketsSent);
                     nodeData->resetVoxelPacket();
                 }
                 packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left