Repository navigation
Expand file tree
/
Copy pathFramePosePacingProbe.cpp
More file actions
46 lines (45 loc) · 3.37 KB
/
Copy pathFramePosePacingProbe.cpp
File metadata and controls
46 lines (45 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "FramePosePacing.h"
#include <atomic>
#include <cstdio>
#include <thread>
using namespace FramePosePacing;
struct Fixture {
Transport::Header header{};Transport::RenderInfo rendered{};
HANDLE event=CreateEventW(nullptr,FALSE,FALSE,nullptr),host=CreateEventW(nullptr,TRUE,FALSE,nullptr);
Fixture(){header.magic=Transport::Magic;header.version=Transport::Version;Put(50);rendered={header.tracking,1,3};}
~Fixture(){CloseHandle(event);CloseHandle(host);}
void Put(uint64_t id,bool valid=true){Transport::Tracking t{};t.id=id;t.tick=GetTickCount64();t.valid=valid;Transport::WriteTracking(&header,t);}
Report Pace(bool enabled=true,DWORD timeout=150){return Wait(&header,event,host,rendered,enabled,timeout);}
};
int main(){
// Reproduce the old pending-signal bug, then verify the production wait
// cannot return until the mailbox contains a genuinely newer sample.
{Fixture f;SetEvent(f.event);if(f.Pace(false).result!=Result::LegacySignal)return 1;}
{Fixture f;SetEvent(f.event);std::atomic_bool released{};
std::thread writer([&]{Sleep(25);f.Put(50);SetEvent(f.event);Sleep(25);released=true;f.Put(51);SetEvent(f.event);});
auto r=f.Pace();bool early=!released;writer.join();if(early||r.result!=Result::Advanced||r.staleSignals<2)return 2;}
// Advance before waiting, and the check-to-wait race: neither loses a wake.
{Fixture f;f.Put(53);if(f.Pace().result!=Result::Advanced)return 3;}
{Fixture f;std::thread writer([&]{f.Put(51);SetEvent(f.event);});auto r=f.Pace();writer.join();if(r.result!=Result::Advanced)return 4;}
// Disconnection/stale samples, dead host and bounded timeout remain exits.
{Fixture f;f.Put(51,false);if(f.Pace().result!=Result::Inactive)return 5;}
{Fixture f;f.header.tracking.tick=GetTickCount64()-251;if(f.Pace().result!=Result::Inactive)return 6;}
{Fixture f;SetEvent(f.host);if(f.Pace().result!=Result::HostExited)return 7;}
{Fixture f;SetEvent(f.event);if(f.Pace(true,25).result!=Result::Timeout)return 8;}
// Read contention does not release the frame or keep the lock held.
{Fixture f;InterlockedExchange(&f.header.trackingLock,1);SetEvent(f.event);
std::thread writer([&]{Sleep(25);InterlockedExchange(&f.header.trackingLock,0);f.Put(51);SetEvent(f.event);});
auto r=f.Pace();writer.join();if(r.result!=Result::Advanced||!r.contendedReads||f.header.trackingLock)return 9;}
// Screen/partial/invalid-source pairs retain the original event pacing.
for(unsigned i=0;i<3;++i){Fixture f;if(i==0)f.rendered.mode=0;else if(i==1)f.rendered.eyeMask=1;else f.rendered.tracking.valid=0;
SetEvent(f.event);if(f.Pace().result!=Result::LegacySignal)return 10;}
// A long run alternates old notifications and prepublished samples. Both
// eyes stay on one source sample; the producer advances once per render.
{Fixture f;
for(unsigned i=0;i<360;++i){const auto previous=f.rendered.tracking.id;SetEvent(f.event);
std::thread writer([&]{Sleep(i%3?1:2);f.Put(previous+1);SetEvent(f.event);});
auto r=f.Pace();writer.join();if(r.result!=Result::Advanced)return 11;
Transport::Tracking t{};if(!Transport::ReadTracking(&f.header,t)||t.id!=previous+1)return 12;f.rendered.tracking=t;
}}
puts("PASS: stale-event regression, newer-pose gating, check/wait race, contention, invalid/stale/host exits, bounded timeout, screen fallback and 360 paired-frame advances.");
}