Pack NV_UNICODE_PACKET densely for Sunshine

This reduces overhead up to 32x for large text blocks and avoids
flooding the ENet connection with packets all at once.
This commit is contained in:
Cameron Gutman committed 2026-10-19 22:51:41 -05:00
1 parent 4a95eb35b2
commit 04053843c9
1 file changed
+73 -28
+73 -28
View File
@@ -319,6 +319,36 @@ static void floatToNetfloat(float in, netfloat out) {
} }
} }
// Returns the length in bytes of the UTF-8 code point at the given offset in the text,
// or 0 if the byte at that offset does not start a valid UTF-8 code point.
static uint32_t getUtf8CodePointLength(const char* utf8Text, uint32_t offset) {
uint8_t firstByte = (uint8_t)utf8Text[offset];
uint32_t codePointLength;
if ((firstByte & 0x80) == 0x00) {
// 1 byte code point
codePointLength = 1;
}
else if ((firstByte & 0xE0) == 0xC0) {
// 2 byte code point
codePointLength = 2;
}
else if ((firstByte & 0xF0) == 0xE0) {
// 3 byte code point
codePointLength = 3;
}
else if ((firstByte & 0xF8) == 0xF0) {
// 4 byte code point
codePointLength = 4;
}
else {
Limelog("Invalid unicode code point starting byte: %02x\n", firstByte);
return 0;
}
return codePointLength;
}
// Input thread proc // Input thread proc
static void inputSendThreadProc(void* context) { static void inputSendThreadProc(void* context) {
SOCK_RET err; SOCK_RET err;
@@ -549,6 +579,7 @@ static void inputSendThreadProc(void* context) {
uint32_t totalLength = PAYLOAD_SIZE(holder) - sizeof(uint32_t); uint32_t totalLength = PAYLOAD_SIZE(holder) - sizeof(uint32_t);
uint32_t i = 0; uint32_t i = 0;
if (!IS_SUNSHINE()) {
// HACK: This is a workaround for the fact that GFE doesn't appear to synchronize keyboard // HACK: This is a workaround for the fact that GFE doesn't appear to synchronize keyboard
// and UTF-8 text events with each other. We need to make sure any previous keyboard events // and UTF-8 text events with each other. We need to make sure any previous keyboard events
// have been processed prior to sending these UTF-8 events to avoid interference between // have been processed prior to sending these UTF-8 events to avoid interference between
@@ -560,45 +591,59 @@ static void inputSendThreadProc(void* context) {
// Finally, sleep an additional 50 ms to allow the events to be processed by Windows // Finally, sleep an additional 50 ms to allow the events to be processed by Windows
PltSleepMs(50); PltSleepMs(50);
}
// We send each Unicode code point individually. This way we can always ensure they will
// never straddle a packet boundary (which will cause a parsing error on the host).
while (i < totalLength && !PltIsThreadInterrupted(&inputSendThread)) { while (i < totalLength && !PltIsThreadInterrupted(&inputSendThread)) {
uint32_t codePointLength; uint32_t chunkLength = 0;
uint8_t firstByte = (uint8_t)holder->packet.unicode.text[i]; uint32_t remaining = totalLength - i;
if ((firstByte & 0x80) == 0x00) {
// 1 byte code point if (IS_SUNSHINE()) {
codePointLength = 1; // Sunshine accepts multiple code points per packet, so we pack as many as
} // fit within the NV_UNICODE_PACKET to send fewer packets overall. Code points
else if ((firstByte & 0xE0) == 0xC0) { // are never split across packet boundaries.
// 2 byte code point while (chunkLength < remaining) {
codePointLength = 2; uint32_t codePointLength = getUtf8CodePointLength(holder->packet.unicode.text, i + chunkLength);
} if (codePointLength == 0) {
else if ((firstByte & 0xF0) == 0xE0) {
// 3 byte code point
codePointLength = 3;
}
else if ((firstByte & 0xF8) == 0xF0) {
// 4 byte code point
codePointLength = 4;
}
else {
Limelog("Invalid unicode code point starting byte: %02x\n", firstByte);
break; break;
} }
if (chunkLength + codePointLength > UTF8_TEXT_EVENT_MAX_COUNT) {
break;
}
chunkLength += codePointLength;
}
// Use the original packet as a template and fixup to send one code point at a time // No code point fit, so stop sending
if (chunkLength == 0) {
break;
}
}
else {
// We send each Unicode code point individually. This way we can always ensure they will
// never straddle a packet boundary (which will cause a parsing error on the host).
uint32_t codePointLength = getUtf8CodePointLength(holder->packet.unicode.text, i);
if (codePointLength == 0) {
break;
}
chunkLength = codePointLength;
}
// Clamp the chunk if the final code point is truncated
if (chunkLength > remaining) {
chunkLength = remaining;
}
// Use the original packet as a template and fixup to send the chunk
splitPacket = *holder; splitPacket = *holder;
splitPacket.packet.unicode.header.size = BE32(sizeof(uint32_t) + codePointLength); splitPacket.packet.unicode.header.size = BE32(sizeof(uint32_t) + chunkLength);
memcpy(splitPacket.packet.unicode.text, &holder->packet.unicode.text[i], codePointLength); memcpy(splitPacket.packet.unicode.text, &holder->packet.unicode.text[i], chunkLength);
// Encrypt and send the split packet // Encrypt and send the chunk
if (!sendInputPacket(&splitPacket, i + 1 < totalLength)) { if (!sendInputPacket(&splitPacket, i + chunkLength < totalLength)) {
freePacketHolder(holder); freePacketHolder(holder);
return; return;
} }
i += codePointLength; i += chunkLength;
} }
freePacketHolder(holder); freePacketHolder(holder);