功能:完善2K串流、显示恢复与输入优化

This commit is contained in:
songyiming committed 2026-11-03 13:43:50 +08:00
1 parent e47b01beec
commit 938ec97cce
95 files changed
+5788 -72

No files matched your search

@@ -0,0 +1,63 @@
// Include the production implementation so batching can be inspected without
// starting a real network connection. Unused transport code is linker-collected.
#include "InputStream.c"
#undef NDEBUG
#include <assert.h>
static PPACKET_HOLDER pop(void) {
PPACKET_HOLDER holder = NULL;
assert(LbqPollQueueElement(&packetQueue, (void**)&holder) == LBQ_SUCCESS);
return holder;
}
static void expectPosition(int x) {
PPACKET_HOLDER holder = pop();
assert(holder->packet.header.magic == LE32(MOUSE_MOVE_ABS_MAGIC));
latchAbsoluteMousePacket(holder);
assert(holder->packet.mouseMoveAbs.x == BE16(x));
assert((uint16_t)holder->packet.mouseMoveAbs.width == (uint16_t)BE16(2559));
freePacketHolder(holder);
}
int main(void) {
AppVersionQuad[0] = 7;
LbqInitializeLinkedBlockingQueue(&packetQueue, MAX_QUEUED_INPUT_PACKETS);
LbqInitializeLinkedBlockingQueue(&packetHolderFreeList, MAX_QUEUED_INPUT_PACKETS);
PltCreateMutex(&batchedInputMutex);
initialized = true;
for (int i = 0; i < 1000; ++i) {
// A deliberately stalled sender must preserve each click's coordinates.
assert(LiSendMousePositionEvent(10, 10, 2560, 1440) == 0);
assert(LiSendMousePositionEvent(20, 20, 2560, 1440) == 0);
assert(LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT) == 0);
assert(LiSendMousePositionEvent(30, 30, 2560, 1440) == 0);
assert(LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT) == 0);
assert(LiSendMousePositionEvent(40, 40, 2560, 1440) == 0);
expectPosition(20);
PPACKET_HOLDER press = pop();
assert(press->packet.header.magic == LE32(BUTTON_ACTION_PRESS + 1));
freePacketHolder(press);
expectPosition(30);
PPACKET_HOLDER release = pop();
assert(release->packet.header.magic == LE32(BUTTON_ACTION_RELEASE + 1));
freePacketHolder(release);
// Latching an older packet above must not prevent the latest batch
// from still absorbing newer positions.
assert(LiSendMousePositionEvent(50, 50, 2560, 1440) == 0);
expectPosition(50);
PPACKET_HOLDER empty;
assert(LbqPollQueueElement(&packetQueue, (void**)&empty) == LBQ_NO_ELEMENT);
}
initialized = false;
PLINKED_BLOCKING_QUEUE_ENTRY entry = LbqDestroyLinkedBlockingQueue(&packetQueue);
assert(entry == NULL);
entry = LbqDestroyLinkedBlockingQueue(&packetHolderFreeList);
while (entry) {
PLINKED_BLOCKING_QUEUE_ENTRY next = entry->flink;
free(entry->data);
entry = next;
}
PltDeleteMutex(&batchedInputMutex);
puts("PASS: 1000 queued move/press/move/release cycles preserve positions");
return 0;
}
@@ -0,0 +1,86 @@
#include "vdi_input_priority.h"
#undef NDEBUG
#include <cassert>
#include <cstdio>
constexpr Sint32 frameCode = 123;
static SDL_Event frame() {
SDL_Event e{}; e.type = SDL_USEREVENT; e.user.code = frameCode; return e;
}
static SDL_Event input(Uint32 type, Uint32 window = 1) {
SDL_Event e{}; e.type = type; e.key.windowID = window; return e;
}
static void push(SDL_Event e) { assert(SDL_PushEvent(&e) == 1); }
static SDL_Event pop() {
SDL_Event e{};
assert(SDL_PeepEvents(&e,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)==1);
return e;
}
int main() {
assert(SDL_Init(SDL_INIT_EVENTS)==0);
SDL_Event out{};
VdiInputPriority disabled(false, frameCode);
push(input(SDL_MOUSEMOTION));
assert(!disabled.deferFrame(frame()) && !disabled.takeDeferred(out,1));
assert(pop().type == SDL_MOUSEMOTION);
// Only a contiguous input prefix can pass a frame. No focus/quit/decoder
// barrier may be skipped, even if input is waiting further down the queue.
for (Uint32 barrier : {Uint32(SDL_WINDOWEVENT),Uint32(SDL_QUIT),
Uint32(SDL_USEREVENT),Uint32(SDL_RENDER_DEVICE_RESET),
Uint32(SDL_CONTROLLERDEVICEADDED)}) {
VdiInputPriority scheduler(true, frameCode);
push(input(SDL_MOUSEBUTTONDOWN)); push(input(SDL_MOUSEBUTTONUP));
push(input(barrier)); push(input(SDL_KEYDOWN));
assert(scheduler.deferFrame(frame()));
assert(scheduler.takeDeferred(out,1) && out.type==SDL_MOUSEBUTTONDOWN);
assert(scheduler.takeDeferred(out,1) && out.type==SDL_MOUSEBUTTONUP);
assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT && out.user.code==frameCode);
assert(!scheduler.takeDeferred(out,1));
assert(pop().type==barrier && pop().type==SDL_KEYDOWN);
assert(scheduler.prioritized()==2);
}
// Preserve all input order, including keyboard releases and drag edges.
{
VdiInputPriority scheduler(true,frameCode);
for (Uint32 t : {Uint32(SDL_KEYDOWN),Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONDOWN),
Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONUP),Uint32(SDL_KEYUP)}) push(input(t));
assert(scheduler.deferFrame(frame()));
for (Uint32 t : {Uint32(SDL_KEYDOWN),Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONDOWN),
Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONUP),Uint32(SDL_KEYUP)})
assert(scheduler.takeDeferred(out,1) && out.type==t);
assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT);
}
for (bool synthetic : {false,true}) {
VdiInputPriority scheduler(true,frameCode);
auto e=input(SDL_MOUSEMOTION,synthetic?1:2);
if(synthetic) e.motion.which=SDL_TOUCH_MOUSEID;
push(e); assert(scheduler.deferFrame(frame()));
assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT);
assert(pop().type==SDL_MOUSEMOTION);
}
// A high-rate input device cannot starve video.
{
VdiInputPriority scheduler(true,frameCode);
for(int i=0;i<1000;++i) push(input(SDL_MOUSEWHEEL));
assert(scheduler.deferFrame(frame()));
unsigned n=0;
while(scheduler.takeDeferred(out,1) && out.type!=SDL_USEREVENT) ++n;
assert(n<=32 && n>0 && out.type==SDL_USEREVENT);
SDL_FlushEvents(SDL_FIRSTEVENT,SDL_LASTEVENT);
}
{
VdiInputPriority scheduler(true,frameCode);
push(input(SDL_MOUSEBUTTONUP));
assert(scheduler.deferFrame(frame())); SDL_Delay(4);
assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT);
assert(pop().type==SDL_MOUSEBUTTONUP);
}
// Instrumentation never consumes events or changes cursor visibility.
VdiInputTiming timing(true);
auto e=input(SDL_MOUSEMOTION); e.common.timestamp=SDL_GetTicks();
timing.observe(e,1); auto start=timing.startRender(); timing.endRender(start);
timing.report(0);
SDL_Quit();
std::puts("PASS: input FIFO, focus/quit/reset/window barriers, disabled mode, device/window isolation, count/time bounds");
}
+75
View File
@@ -0,0 +1,75 @@
#include <SDL.h>
#include <cstdio>
#include "vdi_mouse_batch.h"
#include "vdi_latest_frame.h"
#undef NDEBUG
#include <cassert>
static SDL_Event motion(int x, Uint32 which = 1)
{
SDL_Event event{};
event.type = SDL_MOUSEMOTION;
event.motion.which = which;
event.motion.windowID = 1;
event.motion.x = x;
event.motion.y = 100;
event.motion.xrel = 1;
return event;
}
int main()
{
assert(SDL_Init(SDL_INIT_EVENTS) == 0);
for (Uint32 boundary : {Uint32(SDL_MOUSEBUTTONDOWN), Uint32(SDL_MOUSEBUTTONUP),
Uint32(SDL_KEYDOWN), Uint32(SDL_USEREVENT)}) {
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
SDL_Event first = motion(10), adjacent = motion(20), later = motion(90), barrier{};
barrier.type = boundary;
assert(SDL_PushEvent(&adjacent) == 1);
assert(SDL_PushEvent(&barrier) == 1);
assert(SDL_PushEvent(&later) == 1);
vdiBatchMouseMotion(first.motion);
assert(first.motion.x == 20 && first.motion.xrel == 2);
SDL_Event next;
assert(SDL_PeepEvents(&next, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1);
assert(next.type == boundary);
assert(SDL_PeepEvents(&next, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1);
assert(next.motion.x == 90);
}
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
SDL_Event first = motion(0);
for (int i = 1; i <= 200; ++i) {
SDL_Event next = motion(i);
assert(SDL_PushEvent(&next) == 1);
}
vdiBatchMouseMotion(first.motion);
assert(first.motion.x == 64); // Work per dispatch is bounded.
assert(SDL_PeepEvents(nullptr, 0, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 136);
SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT);
first = motion(10);
SDL_Event synthetic = motion(99, SDL_TOUCH_MOUSEID);
assert(SDL_PushEvent(&synthetic) == 1);
vdiBatchMouseMotion(first.motion);
assert(first.motion.x == 10);
AVFrame* presented = av_frame_alloc();
assert(presented);
presented->buf[0] = av_buffer_alloc(16);
assert(presented->buf[0]);
QQueue<AVFrame*> queue;
assert(vdiDiscardQueuedFrames(queue) == 0);
for (int i = 0; i < 200; ++i) {
unsigned dropped = vdiDiscardQueuedFrames(queue);
assert(dropped == (i == 0 ? 0u : 1u));
AVFrame* frame = av_frame_alloc();
assert(frame);
frame->pts = i;
queue.enqueue(frame);
assert(queue.size() == 1 && queue.head()->pts == i);
assert(av_buffer_get_ref_count(presented->buf[0]) == 1);
}
assert(vdiDiscardQueuedFrames(queue) == 1);
av_frame_free(&presented);
SDL_Quit();
std::puts("PASS: SDL event boundaries, bounded motion work, latest decoded frame ownership");
}