协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
platform-abstraction【开发与工程】Expert platform abstraction layers for game engines — OS abstraction, filesystem, threads, windowing, input, timers, dynamic loading, and platform-conditional code across Windows/Linux/console.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
The PAL (Platform Abstraction Layer) is the layer that lets one engine codebase run on Windows, Linux, macOS, and every console generation. If it's leaky, every feature becomes a platform #ifdef. If it's designed well, 95% of engine code is pure, portable C++.
| Concern | Platform-specific | Engine API |
|---|---|---|
| Memory | VirtualAlloc / mmap / heap | VirtualAlloc, Memory::Reserve |
| Threads | CreateThread / pthread / fiber | Thread, JobSystem handles |
| Filesystem | CreateFile / open | FileSystem::Open, paths {Platform.VirtualPath} |
| Time | QueryPerformanceCounter / clock_gettime | Time::Now(), Time::Delta() |
| Windowing | Win32 / SDL / GLFW | Window, InputEvent |
| Input | RawInput / XInput / hid | InputDevice, ActionMap |
| Dynamic loading | LoadLibrary / dlopen | Module::Load |
| Console slots | device-specific | Console::WaitForDebugger |
| Render RHI | D3D12 / Vulkan / Metal | RHIDevice (see vulkan skill) |
| Networking | winsock / BSD sockets | NetworkProtocol (see netcode skill) |
Rule: every platform-only symbol lives behind an interface; portals are forbidden (#ifdef WIN32 inside gameplay code).
struct PAL {
// Memory
virtual MemoryPage* Reserve(uint64_t bytes) = 0;
virtual void Commit(void* p, uint64_t bytes) = 0;
// Threads
virtual Thread* CreateThread(ThreadFn, void*, size_t stack) = 0;
virtual void Sleep(uint32_t ms) = 0;
virtual uint32_t HardwareThreadCount() = 0;
// Files
virtual FileHandle* Open(const Path&, FileMode) = 0;
virtual void ReadAsync(FileHandle*, uint8_t*, size_t, IoCallback) = 0;
// Time
virtual TimePoint Now() = 0;
virtual double PeriodSeconds() = 0;
// Misc
virtual uint32_t ProcessId() = 0;
virtual void* LoadModule(const Path&) = 0;
virtual void Exit(int code) = 0;
};
extern PAL* g_platform; // set once at startup to the platform implementation
Debug-build placeholder: a NullPAL that asserts on unsupported calls keeps unsupported code from silently running.
PlatformWindows.cpp — Win32 + DirectXMath conveniences.PlatformPosix.cpp — pthread, open, clock_gettime(CLOCK_MONOTONIC_RAW).If a feature isn't available (e.g., energy-perf mode, large pages on older Windows), the PAL returns "unsupported" and the engine falls back — no hard crash. Platform::Capabilities struct at init reports feature flags (huge pages, GPU async, RTX, max threads, etc.).
The Path type has:
{GameData}/levels/Zone1/lvl.map — portable, artifact-independent.{GameData} -> D:\...\Packaged\Game\Data).Path p = Platform::ResolveVirtual("{GameData}/shaders/lighting.hlsl");
{EngineRoot} -> engine-source-tree
{GameData} -> <install>/GameData
{SaveFiles} -> platform writable dir (AppData / xdg-data-home / console save)
{Temp} -> sphere-scoped scratch
Save dir differs per platform — the PAL owns the resolution. All asset loading goes through the mount table (no raw D:\ paths in assets).
See virtual-memory.md and asset-pipeline.md. Crude: ReadFile, fopen. Production: async I/O + memory mapping for big read-only assets; the PAL exposes both.
QueryPerformanceCounter (QPC), divisor = frequency.clock_gettime(CLOCK_MONOTONIC_RAW) (unaffected by NTP jumps).struct TimePoint { uint64_t ticks; };
static TimePoint Now() { return { static_cast<uint64_t>(g_qpc) }; }
dt, and the engine's accumulator converts to fixed-step sim (see game-loop-and-timestep.md).PAL threads wrap CreateThread/std::thread with generous split:
Thread = OS thread handle + id + created name (for the debugger).JobSystem (see its skill) uses PAL threads underneath.Thread* t = Platform::CreateThread(bind(&MySystem::Run), this, 256 * 1024);
Platform::CreateFiber, SwitchToFiber — the PAL isolates the platform fiber API so switch cost stays cheap. (See fibers-and-async.md.)
Platform::AtomicIncrement, InterlockedCompareExchange, YieldProcessor, WaitOnAddress, WaitForSingleObject. Wrap so the engine never touches platform intrinsics directly.
Raw device → engine input:
struct InputRawFrame {
float leftStickX, leftStickY, rightStickX, rightStickY;
float triggers[2]; bool buttons[14];
// per player: controller ID
};
The PAL enqueues a raw frame per input device per frame; the engine's input system (see client-engine/input-systems.md) maps actions onto it.
The PAL owns window creation (or wraps SDL/GLFW). Gameplay code must NOT see Win32 WM_ messages. Convert to engine events: WindowEvent::Closed, InputEvent::KeyDown(KeyCode::W), AppEvent::Focus, DropFileEvent.
PAL owns: window resize (render resizes the RHI swapchain), minimize (frame skip), focus change (pause), and the app-level quit.
void* h = Platform::LoadModule("{Engine}/devmods/buddha-bot");
auto fn = (CreateDevModFn)Platform::GetProcAddress(h, "CreateDevMod");
Used for: dev mods, platform-specific render modules, tool builds, and the game DLL (hot-reload of gameplay). The hot-reload pipeline reloads the module, re-wires entry points, and migrates state (see asset-pipeline.md).
while(!IsDebuggerAttached()) Sleep(1) so a debugger can attach before crash.SetUnhandledExceptionFilter) → writes a minidump + logs the callstack + hook to telemetry.Platform::GetMemoryUsage() for the memory dashboard.Platform::GetCommandLine, GetArguments.Acceptable #ifdefs (in the PAL and its consumers):
PLATFORM_WINDOWS, PLATFORM_POSIX, PLATFORM_CONSOLE_*.BUILD_DEBUG, BUILD_SHIPPING.FEATURE_HUGE_PAGES, FEATURE_ASAN.Forbidden: CPU #ifdefs deep in gameplay; #ifdef _WIN32 touching window or file code outside the PAL; #ifndef as feature gates.
Keeping the engine's gameplay code #ifdef-free is the goal; every platform symbol in gameplay is a code smell.
#ifdefs confined to the PAL + feature flags.references/os-interface.md — PAL interface, capabilities, platform detection, error handlingreferences/filesystem.md — mount table, virtual paths, async I/O, mmap, save/loadreferences/threading.md — thread/atomic/fiber wrappers, worker setup, priorityreferences/windowing.md — window creation, events, resize/focus, multi-windowreferences/input-devices.md — raw input, gamepads, keyboard/mouse, hid, mapping to engine eventsreferences/modules-and-tools.md — dynamic loading, hot reload, crash handling, dev-tool integration