// F04 — vsync / frame pacing, Win32 (../../f04-frame-pacing.md).
//
// Implements the Win32 half of ../../../features/f04-frame-pacing.md on top
// of the scaffold (../scaffold/app.d): drive a trivially cheap redraw (solid
// color flip) from the platform frame clock — no sleep, no busy loop — and
// measure how steady it actually is.
//
// * Primary pacing source: DwmFlush() from dwmapi.dll, which blocks until
// the next DWM composition pass (≈ the next vblank when composition is
// active). DwmIsCompositionEnabled() is checked first, then a 10-call
// probe measures whether DwmFlush actually *blocks* — a broken/stubbed
// DwmFlush that returns immediately (or errors) would otherwise turn the
// pacing loop into a busy spin. Whatever the probe finds is the finding.
// * Documented fallback chain: DwmFlush -> SetTimer at 16 ms. The chosen
// path is logged (`pacing_path path=dwm|timer reason=...`) and stamped on
// every frame. WSI_FORCE_TIMER=1 / WSI_FORCE_DWM=1 override the choice so
// both paths stay reachable on any host.
// * 600 frames of `frame_callback t=...` are collected; at exit the
// inter-frame deltas' min/p50/p99/max and a coarse jitter histogram are
// printed to *stdout* (the instrumentation stream stays on stderr).
// * Occlusion probe (WSI_AUTO_EXIT=1): at frame 300 the window is minimized
// (ShowWindow(SW_MINIMIZE), `vis_change state=minimized`) and restored
// ~3 s later — does the pacing source keep ticking while the window is
// hidden? The frame log answers directly.
// * A stall watchdog thread aborts (exit 2) if no frame lands for 10 s, so
// the bounded mode can never hang CI even if a pacing source wedges.
//
// The DXGI waitable-swapchain path (the real-Windows gold path) is
// documented in ../../f04-frame-pacing.md but deliberately out of scope here:
// it needs COM + D3D11/DXGI interface bring-up that core.sys.windows does not
// carry. Only druntime's built-in bindings are used — plus two hand-declared
// dwmapi entry points (druntime ships no dwmapi module).
module (module) appapp;
import (package) corecore.(module) core.atomicThe atomic module provides basic support for lock-free
concurrent programming.
Use the -preview=nosharedaccess compiler flag to detect
unsafe individual read or write operations on shared data.
Source
core/atomic.d
Examples
int y = 2;
shared int x = y; // OK
//x++; // read modify write error
x.atomicOp!"+="(1); // OK
//y = x; // read error with preview flag
y = x.atomicLoad(); // OK
assert(y == 3);
//x = 5; // write error with preview flag
x.atomicStore(5); // OK
assert(x.atomicLoad() == 5);
atomic : (alias template) app.atomicLoad = core.atomic.atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(auto ref return scope const T val) if (!is(T == shared(U), U) && !is(T == shared(inout(U)), U) && !is(T == shared(const(U)), U))Loads 'val' from memory and returns it. The memory barrier specified
by 'ms' is applied to the operation, which is fully sequenced by
default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq,
and MemoryOrder.seq.
atomicLoad, (alias template) app.atomicStore = core.atomic.atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) if (!is(T == shared) && !is(V == shared))Writes 'newval' into 'val'. The memory barrier specified by 'ms' is
applied to the operation, which is fully sequenced by default.
Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and
MemoryOrder.seq.
atomicStore;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) app.fflush = int core.stdc.stdio.fflush(shared(core.stdc.stdio._IO_FILE)* stream) nothrow @nogc @trustedfflush, (alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf, (alias shared global) app.stdout = shared(core.stdc.stdio._IO_FILE*) core.stdc.stdio.stdoutstdout;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdlibD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h
Source
core/stdc/stdlib.d
stdlib : (alias) app.qsort = void core.stdc.stdlib.qsort(void* base, ulong nmemb, ulong size, extern (C) int function(const(void*), const(void*)) compar) nothrow @nogcqsort;
import (package) corecore.(package) core.syssys.(package) core.sys.windowswindows.(module) core.sys.windows.windowsWindows API header module
Translated from MinGW API for MS-Windows 4.0
Source
core/sys/windows/windows.d
windows;
import instrument;
// druntime has no core.sys.windows.dwmapi — declare the two entry points.
// pragma(lib) emits /DEFAULTLIB:dwmapi, resolved against the SDK import libs.
pragma(lib, "dwmapi");
extern (Windows) nothrow @nogc
{
HRESULT app.DwmIsCompositionEnabledDwmIsCompositionEnabled(BOOL* pfEnabled);
HRESULT app.DwmFlushDwmFlush();
}
enum (constant) int app.FRAMES_TOTAL = 600FRAMES_TOTAL = 600; // F04 requirement 2
enum (constant) int app.TIMER_MS = 16TIMER_MS = 16; // the documented fallback cadence
enum UINT_PTR (constant) _error_ app.FRAME_TIMER_ID = __errorFRAME_TIMER_ID = 1;
enum (constant) int app.MINIMIZE_AT_FRAME = 300MINIMIZE_AT_FRAME = 300; // occlusion probe (requirement 3)
enum (constant) int app.MINIMIZED_HOLD_US = 3000000MINIMIZED_HOLD_US = 3_000_000;
struct (struct) app.DemoDemo
{
HDC (field) _error_ app.Demo.memDcmemDc;
HBITMAP (field) _error_ app.Demo.dibdib;
HBITMAP (field) _error_ app.Demo.stockBmpstockBmp;
uint* (field) uint* app.Demo.pixelspixels;
int (field) int app.Demo.widthwidth, (field) int app.Demo.heightheight;
uint (field) uint app.Demo.frameframe; // frame_callback counter
const(char)* (field) const(char)* app.Demo.pathpath = "unset"; // dwm | timer
long[(constant) int app.FRAMES_TOTAL = 600FRAMES_TOTAL] (field) long[600] app.Demo.frameTimesframeTimes; // µs timestamps, [0 .. frame)
bool (field) bool app.Demo.autoExitautoExit;
bool (field) bool app.Demo.forceTimerforceTimer, (field) bool app.Demo.forceDwmforceDwm;
bool (field) bool app.Demo.minimizedminimized;
long (field) long app.Demo.minimizedAtUsminimizedAtUs;
bool (field) bool app.Demo.occlusionDoneocclusionDone;
bool (field) bool app.Demo.statsDonestatsDone;
}
__gshared (struct) app.DemoDemo _error_ app.gg;
__gshared HWND _error_ app.g_hwndg_hwnd;
shared long (shared global) shared(long) app.s_lastFrameUss_lastFrameUs; // stall watchdog heartbeat
shared bool (shared global) shared(bool) app.s_dones_done;
// ---------------------------------------------------------------------------
// Backbuffer (scaffold strategy: realloc on client-size change).
void void app.createBackbuffer(int w, int h) nothrowcreateBackbuffer(int (parameter) int ww, int (parameter) int hh) nothrow
{
if (g.(field) _error_ g.dibdib !is null)
{
SelectObject(g.g.memDcmemDc, g.g.stockBmpstockBmp);
DeleteObject(g.g.dibdib);
g.g.dibdib = null;
g.g.pixelspixels = null;
g.g.widthwidth = g.g.heightheight = 0;
}
if ((parameter) int ww <= 0 || (parameter) int hh <= 0)
return;
BITMAPINFO (local variable) _error_ bmibmi;
bmi.bmiHeader.biSize = BITMAPINFOHEADER.BITMAPINFOHEADER.sizeofsizeof;
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* (local variable) void* bitsbits;
g.g.dibdib = CreateDIBSection(null, &bmi, DIB_RGB_COLORS, &bits, null, 0);
if (g.(field) _error_ g.dibdib is null)
{
logEvent("error what=CreateDIBSection code=%lu", GetLastError());
return;
}
g.g.pixelspixels = cast(uint*) bits;
g.g.widthwidth = w;
g.g.heightheight = h;
g.g.stockBmpstockBmp = cast(HBITMAP) SelectObject(g.g.memDcmemDc, g.g.dibdib);
logEvent("buffer_alloc size=%dx%d bytes=%d", w, h, w * h * 4);
}
// ---------------------------------------------------------------------------
// One paced frame: flip between two solid colors (trivially cheap), log the
// callback, drive the occlusion probe, present synchronously.
void void app.onFrame() nothrowonFrame() nothrow
{
const (local variable) const(_error_) tt = nowUs();
if (g.(field) _error_ g.frameframe < FRAMES_TOTAL)
g.(field) _error_ g.frameTimesframeTimes[g.g.frameframe] = t;
++g.(field) _error_ g.frameframe;
(template function) core.atomic.atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) if (!is(T == shared) && !is(V == shared))atomicStore((shared global) shared(long) app.s_lastFrameUss_lastFrameUs, t);
logEvent("frame_callback t=%lld frame=%u path=%s", t, g.g.frameframe, g.g.pathpath);
if (g.(field) _error_ g.pixelspixels !is null)
{
const (local variable) const(_error_) colorcolor = (g.(field) _error_ g.frameframe & 1) ? 0x2060c0 : 0xc06020; // the solid flip
const (local variable) const(_error_) nn = cast((alias) object.size_t = ulongsize_t) g.(field) _error_ g.widthwidth * g.(field) _error_ g.heightheight;
foreach ((parameter) ii; 0 .. n)
g.g.pixelspixels[i] = color;
}
InvalidateRect(g_hwnd, null, FALSE);
UpdateWindow(g_hwnd);
// Occlusion probe: minimize mid-run, restore after ~3 s. Both transitions
// happen from inside the pacing callback — if the pacing source stops
// while minimized, the restore never runs and the stall watchdog reports.
if (g.(field) _error_ g.autoExitautoExit && !g.(field) _error_ g.occlusionDoneocclusionDone)
{
if (!g.(field) _error_ g.minimizedminimized && g.(field) _error_ g.frameframe == MINIMIZE_AT_FRAME)
{
g.g.minimizedminimized = true;
g.g.minimizedAtUsminimizedAtUs = t;
logEvent("vis_change state=minimized t=%lld frame=%u", t, g.g.frameframe);
ShowWindow(g_hwnd, SW_MINIMIZE);
}
else if (g.(field) _error_ g.minimizedminimized && t - g.(field) _error_ g.minimizedAtUsminimizedAtUs > MINIMIZED_HOLD_US)
{
g.g.minimizedminimized = false;
g.g.occlusionDoneocclusionDone = true;
logEvent("vis_change state=restored t=%lld frame=%u", t, g.g.frameframe);
ShowWindow(g_hwnd, SW_RESTORE);
}
}
}
// ---------------------------------------------------------------------------
// Stats: min/p50/p99/max inter-frame delta + coarse jitter histogram, printed
// to stdout at exit (F04 requirement 2).
extern (C) int int app.cmpLong(const(void)* a, const(void)* b) nothrow @nogccmpLong(const(void)* (parameter) const(void)* aa, const(void)* (parameter) const(void)* bb) nothrow @nogc
{
const (local variable) const(long) xx = *cast(const(long)*) (parameter) const(void)* aa, (local variable) const(long) yy = *cast(const(long)*) (parameter) const(void)* bb;
return ((local variable) const(long) xx > (local variable) const(long) yy) - ((local variable) const(long) xx < (local variable) const(long) yy);
}
void void app.printStats() nothrowprintStats() nothrow
{
if (g.(field) _error_ g.statsDonestatsDone)
return;
g.g.statsDonestatsDone = true;
const (local variable) const(_error_) nn = (g.(field) _error_ g.frameframe < FRAMES_TOTAL ? g.(field) _error_ g.frameframe : FRAMES_TOTAL);
if (n < 2)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("stats path=%s frames=%u deltas=0\n", g.(field) _error_ g.pathpath, g.(field) _error_ g.frameframe);
int core.stdc.stdio.fflush(shared(core.stdc.stdio._IO_FILE)* stream) nothrow @nogc @trustedfflush((shared global) shared(core.stdc.stdio._IO_FILE*) core.stdc.stdio.stdoutstdout);
return;
}
static long[(constant) int app.FRAMES_TOTAL = 600FRAMES_TOTAL - 1] (thread local global) long[599] app.printStats.deltasdeltas;
const (local variable) const(_error_) ndnd = n - 1;
foreach ((parameter) ii; 0 .. nd)
deltas[i] = g.g.frameTimesframeTimes[i + 1] - g.g.frameTimesframeTimes[i];
// Histogram over the raw (unsorted) deltas.
static immutable long[7] (immutable global) immutable(long[7]) app.printStats.edgesedges = [2_000, 8_000, 12_000, 17_000, 20_000,
34_000, 100_000];
static immutable (alias) object.string = stringstring[8] (immutable global) immutable(string[8]) app.printStats.labelslabels = [
"<2ms", "2-8ms", "8-12ms", "12-17ms", "17-20ms", "20-34ms",
"34-100ms", ">=100ms",
];
uint[8] (local variable) uint[8] bucketsbuckets;
foreach ((parameter) ii; 0 .. nd)
{
(unresolved type) size_tsize_t _error_ bb = edges.edges.lengthlength; // last bucket unless an edge catches it
foreach ((parameter) jj, (parameter) ee; edges)
if (deltas[i] < e)
{
b = j;
break;
}
++buckets[b];
}
void core.stdc.stdlib.qsort(void* base, ulong nmemb, ulong size, extern (C) int function(const(void*), const(void*)) compar) nothrow @nogcqsort((thread local global) long[599] app.printStats.deltasdeltas.(constant) long* long[599].ptr = &deltasptr, nd, long.(constant) ulong long.sizeof = 8LUsizeof, &int app.cmpLong(const(void)* a, const(void)* b) nothrow @nogccmpLong);
const long (local variable) const(long) mnmn = (thread local global) long[599] app.printStats.deltasdeltas[0];
const long (local variable) const(long) p50p50 = (thread local global) long[599] app.printStats.deltasdeltas[nd / 2];
const long (local variable) const(long) p99p99 = (thread local global) long[599] app.printStats.deltasdeltas[(nd * 99) / 100];
const long (local variable) const(long) mxmx = (thread local global) long[599] app.printStats.deltasdeltas[nd - 1];
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("stats path=%s frames=%u deltas=%u min_us=%lld p50_us=%lld "
~ "p99_us=%lld max_us=%lld\n",
g.(field) _error_ g.pathpath, g.(field) _error_ g.frameframe, cast(uint) nd, (local variable) const(long) mnmn, (local variable) const(long) p50p50, (local variable) const(long) p99p99, (local variable) const(long) mxmx);
foreach ((parameter) ulong jj, (parameter) immutable(string) labellabel; (immutable global) immutable(string[8]) app.printStats.labelslabels)
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("histogram bucket=%.*s count=%u\n",
cast(int) (local variable) immutable(string) labellabel.(field) ulong immutable(string).lengthlength, (local variable) immutable(string) labellabel.(field) immutable(char)* immutable(string).ptrptr, (local variable) uint[8] bucketsbuckets[(local variable) ulong jj]);
int core.stdc.stdio.fflush(shared(core.stdc.stdio._IO_FILE)* stream) nothrow @nogc @trustedfflush((shared global) shared(core.stdc.stdio._IO_FILE*) core.stdc.stdio.stdoutstdout);
}
// ---------------------------------------------------------------------------
// Stall watchdog: the bounded mode must never hang CI. If no frame lands for
// 10 s (a pacing source that blocks forever, e.g. a DwmFlush that never
// returns), report and abort with exit code 2.
extern (Windows) DWORD app.stallWatchdogstallWatchdog(LPVOID) nothrow
{
while (!atomicLoad(s_done))
{
Sleep(500);
const _error_ lastlast = atomicLoad(s_lastFrameUs);
if (!atomicLoad(s_done) && nowUs() - last > 10_000_000)
{
logEvent("watchdog event=stall last_frame_us=%lld path=%s",
last, g.g.pathpath);
printStats();
ExitProcess(2);
}
}
return 0;
}
// ---------------------------------------------------------------------------
// The window procedure.
extern (Windows) LRESULT app.wndProcwndProc(HWND hwnd, UINT (parameter) UINT msgmsg, WPARAM wParam, LPARAM lParam) nothrow
{
switch (msg)
{
case WM_CREATE:
g.g.memDcmemDc = CreateCompatibleDC(null);
return 0;
case WM_SIZE:
const _error_ ww = cast(int)(lParam & 0xffff);
const _error_ hh = cast(int)((lParam >> 16) & 0xffff);
logEvent("resize size=%dx%d wparam=%d", w, h, cast(int) wParam);
if (wParam == SIZE_MINIMIZED)
{
logEvent("vis_change state=size_minimized");
return 0; // keep the backbuffer; pacing continues (or not — log!)
}
if (w != g.g.widthwidth || h != g.g.heightheight)
createBackbuffer(w, h);
return 0;
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
PAINTSTRUCT _error_ psps;
HDC _error_ hdchdc = BeginPaint(hwnd, &ps);
if (g.g.pixelspixels !is null)
BitBlt(hdc, 0, 0, g.g.widthwidth, g.g.heightheight, g.g.memDcmemDc, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
return 0;
case WM_TIMER:
if (wParam != FRAME_TIMER_ID)
return 0;
onFrame();
if (g.g.autoExitautoExit && g.g.frameframe >= FRAMES_TOTAL)
{
KillTimer(hwnd, FRAME_TIMER_ID);
printStats();
DestroyWindow(hwnd);
}
return 0;
case WM_CLOSE:
logEvent("close_requested");
goto default;
case WM_DESTROY:
logEvent("msg name=WM_DESTROY");
KillTimer(hwnd, FRAME_TIMER_ID);
createBackbuffer(0, 0);
if (g.g.memDcmemDc !is null)
{
DeleteDC(g.g.memDcmemDc);
g.g.memDcmemDc = null;
}
PostQuitMessage(0);
return 0;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
}
// ---------------------------------------------------------------------------
// Pacing-path selection: report what DWM says, then measure what DwmFlush
// actually does. A real composition clock blocks ~per-vblank (2–50 ms); an
// immediate return (Wine stubs, composition off) or an error HRESULT means
// the documented fallback (SetTimer) must take over.
const(char)* const(char)* app.probeDwm() nothrowprobeDwm() nothrow
{
BOOL (local variable) _error_ enabledenabled = FALSE;
const (local variable) const(_error_) hrEnabledhrEnabled = DwmIsCompositionEnabled(&enabled);
logEvent("step name=DwmIsCompositionEnabled hr=0x%08x enabled=%d",
cast(uint) hrEnabled, enabled ? 1 : 0);
long (local variable) long minDtminDt = long.(constant) long long.max = 9223372036854775807Lmax, (local variable) long maxDtmaxDt = 0;
HRESULT (local variable) _error_ lastHrlastHr = 0;
uint (local variable) uint failuresfailures = 0;
foreach ((local variable) int ii; 0 .. 10)
{
const (local variable) const(_error_) t0t0 = nowUs();
const (local variable) const(_error_) hrhr = DwmFlush();
const (local variable) const(_error_) dtdt = nowUs() - t0;
if (hr != 0)
{
++(local variable) uint failuresfailures;
lastHr = hr;
}
if (dt < (local variable) long minDtminDt)
(local variable) long minDtminDt = dt;
if (dt > (local variable) long maxDtmaxDt)
(local variable) long maxDtmaxDt = dt;
logEvent("pacing_probe call=%d hr=0x%08x dt_us=%lld", i,
cast(uint) hr, dt);
}
if (g.(field) _error_ g.forceTimerforceTimer)
return "forced_timer";
if ((local variable) uint failuresfailures == 10 && !g.(field) _error_ g.forceDwmforceDwm)
return "dwmflush_failed";
if ((local variable) uint failuresfailures > 0 && !g.(field) _error_ g.forceDwmforceDwm)
return "dwmflush_unreliable";
// "Blocks at least once for >= 2 ms" is the cheapest honest signature of
// a real composition clock; immediate returns would busy-spin the loop.
if ((local variable) long maxDtmaxDt < 2_000 && !g.(field) _error_ g.forceDwmforceDwm)
return "dwmflush_returns_immediately";
if (!enabled && !g.(field) _error_ g.forceDwmforceDwm)
return "composition_disabled";
return null; // use the DWM path
}
// The DwmFlush-paced loop. Returns true when the run is complete (or the
// window died); false means "fall back to the timer path mid-run".
bool bool app.runDwmLoop() nothrowrunDwmLoop() nothrow
{
uint (local variable) uint consecutiveFailuresconsecutiveFailures = 0;
while (true)
{
const (local variable) const(_error_) hrhr = DwmFlush();
if (hr != 0)
{
logEvent("pacing_error hr=0x%08x frame=%u", cast(uint) hr, g.g.frameframe);
if (++(local variable) uint consecutiveFailuresconsecutiveFailures >= 5)
{
logEvent("pacing_path path=timer reason=dwmflush_failed_midrun");
return false;
}
}
else
(local variable) uint consecutiveFailuresconsecutiveFailures = 0;
void app.onFrame() nothrowonFrame();
// Drain whatever the frame produced; the loop owns the cadence.
MSG (local variable) _error_ msgmsg;
while (PeekMessageW(&msg, null, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
return true;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (g.(field) _error_ g.autoExitautoExit && g.(field) _error_ g.frameframe >= FRAMES_TOTAL)
{
void app.printStats() nothrowprintStats();
DestroyWindow(g_hwnd);
while (GetMessageW(&msg, null, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return true;
}
}
}
bool bool app.envFlag(const(wchar)* name) nothrowenvFlag(const(wchar)* (parameter) const(wchar)* namename) nothrow
{
WCHAR[8] (local variable) _error_ bufbuf;
const (local variable) const(_error_) nn = GetEnvironmentVariableW(name, buf.buf.ptrptr, buf.buf.lengthlength);
return n >= 1 && n < buf.(field) _error_ buf.lengthlength && buf[0] == '1';
}
int int D main()main()
{
instrumentInit("f04_frame_pacing_win32");
logEvent("init_start");
g.g.autoExitautoExit = envFlag("WSI_AUTO_EXIT"w."WSI_AUTO_EXIT"w.ptrptr);
g.g.forceTimerforceTimer = envFlag("WSI_FORCE_TIMER"w."WSI_FORCE_TIMER"w.ptrptr);
g.g.forceDwmforceDwm = envFlag("WSI_FORCE_DWM"w."WSI_FORCE_DWM"w.ptrptr);
logEvent("mode auto_exit=%d force_timer=%d force_dwm=%d",
g.g.autoExitautoExit ? 1 : 0, g.g.forceTimerforceTimer ? 1 : 0, g.g.forceDwmforceDwm ? 1 : 0);
HINSTANCE (local variable) _error_ hInsthInst = GetModuleHandleW(null);
HCURSOR (local variable) _error_ arrowarrow = LoadCursorW(null, IDC_ARROW);
auto (local variable) wstring clsNameclsName = "wsi-f04-class"w;
WNDCLASSEXW (local variable) _error_ wcwc;
wc.cbSize = WNDCLASSEXW.WNDCLASSEXW.sizeofsizeof;
wc.lpfnWndProc = &wndProc;
wc.hInstance = hInst;
wc.lpszClassName = clsName.clsName.ptrptr;
wc.hCursor = arrow;
logEvent("step name=RegisterClassExW");
if (!RegisterClassExW(&wc))
{
logEvent("error what=RegisterClassExW code=%lu", GetLastError());
return 1;
}
logEvent("step name=CreateWindowExW");
g_hwnd = CreateWindowExW(0, clsName.clsName.ptrptr, "wsi-f04-frame-pacing"w."wsi-f04-frame-pacing"w.ptrptr,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
null, null, hInst, null);
if (g_hwnd is null)
{
logEvent("error what=CreateWindowExW code=%lu", GetLastError());
return 1;
}
logEvent("window_created");
ShowWindow(g_hwnd, SW_SHOW);
UpdateWindow(g_hwnd);
(template function) core.atomic.atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) if (!is(T == shared) && !is(V == shared))atomicStore((shared global) shared(long) app.s_lastFrameUss_lastFrameUs, nowUs());
HANDLE (local variable) _error_ wdwd = null;
if (g.(field) _error_ g.autoExitautoExit)
wd = CreateThread(null, 0, &stallWatchdog, null, 0, null);
const (local variable) const(char*) reasonreason = const(char)* app.probeDwm() nothrowprobeDwm();
bool (local variable) bool completedcompleted = false;
if ((local variable) const(char*) reasonreason is null)
{
g.g.pathpath = "dwm";
logEvent("pacing_path path=dwm reason=%s",
g.g.forceDwmforceDwm ? "forced_dwm"."forced_dwm".ptrptr : "probe_blocked"."probe_blocked".ptrptr);
(local variable) bool completedcompleted = bool app.runDwmLoop() nothrowrunDwmLoop();
}
else
logEvent("pacing_path path=timer reason=%s", reason);
int (local variable) int codecode = 0;
if (!(local variable) bool completedcompleted)
{
g.g.pathpath = "timer";
logEvent("step name=SetTimer interval_ms=%d", TIMER_MS);
SetTimer(g_hwnd, FRAME_TIMER_ID, TIMER_MS, null);
MSG (local variable) _error_ msgmsg;
while (GetMessageW(&msg, null, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
(local variable) int codecode = cast(int) msg.wParam;
}
void core.atomic.atomicStore!(MemoryOrder.seq, bool, bool)(ref shared(bool) val, bool newval) pure nothrow @nogc @trustedWrites 'newval' into 'val'. The memory barrier specified by 'ms' is
applied to the operation, which is fully sequenced by default.
Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and
MemoryOrder.seq.
atomicStore((shared global) shared(bool) app.s_dones_done, true);
if (wd !is null)
{
WaitForSingleObject(wd, 1500);
CloseHandle(wd);
}
void app.printStats() nothrowprintStats(); // no-op if already printed
logEvent("exit code=%d", code);
return (local variable) int codecode;
}