From 58543ac517c2f2521ec77a5b74939294872c3243 Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 26 Feb 2026 23:05:36 -0600 Subject: [PATCH] wine-detection. gpu debug/validation command line args --- src/base/base.cgh | 1 + src/base/base_win32/base_win32.c | 33 ++++- src/base/base_win32/base_win32.h | 1 + src/config.h | 4 +- src/gpu/gpu_dx12/gpu_dx12_core.c | 170 ++++++++++++++------------ src/gpu/gpu_dx12/gpu_dx12_core.h | 15 ++- src/pp/pp.c | 8 +- src/pp/pp.h | 8 ++ src/pp/pp_sim/pp_sim_core.c | 32 +++-- src/pp/pp_vis/pp_vis_core.c | 200 +++++++++++++++++++++++++------ src/pp/pp_vis/pp_vis_core.h | 3 + 11 files changed, 337 insertions(+), 138 deletions(-) diff --git a/src/base/base.cgh b/src/base/base.cgh index f496ec4e..13a50b65 100644 --- a/src/base/base.cgh +++ b/src/base/base.cgh @@ -791,6 +791,7 @@ Inline u64 MixU64s(u64 seed_a, u64 seed_b) b32 Panic(String msg); Callstack CaptureCallstack(u64 skip_frames); b32 IsRunningInDebugger(void); + b32 IsRunningInWine(void); i64 TimeNs(void); void TrueRand(String buffer); CpuTopologyInfo GetCpuTopologyInfo(void); diff --git a/src/base/base_win32/base_win32.c b/src/base/base_win32/base_win32.c index 8518b436..0a19f95e 100644 --- a/src/base/base_win32/base_win32.c +++ b/src/base/base_win32/base_win32.c @@ -102,6 +102,11 @@ b32 IsRunningInDebugger(void) return IsDebuggerPresent(); } +b32 IsRunningInWine(void) +{ + return W32.wine_version.len > 0; +} + i64 TimeNs(void) { LARGE_INTEGER qpc; @@ -445,10 +450,12 @@ i32 W32_Main(void) // Get raw args from command line Arena *perm = PermArena(); + String cmdline_str = Zi; { StringList args_list = Zi; { - LPCWSTR cmdline_wstr = GetCommandLineW(); + wchar_t *cmdline_wstr = GetCommandLineW(); + cmdline_str = StringFromWstrNoLimit(perm, cmdline_wstr); i32 argc = 0; LPWSTR *argv = CommandLineToArgvW(cmdline_wstr, &argc); for (i32 i = 0; i < argc; ++i) @@ -461,6 +468,24 @@ i32 W32_Main(void) W32.raw_command_line = args_list; } + // Detect wine + { + HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); + if (ntdll) + { + typedef char *W32_WineVersionFunc(void); + W32_WineVersionFunc *wine_version_func = (W32_WineVersionFunc *)GetProcAddress(ntdll, "wine_get_version"); + if (wine_version_func) + { + char *wine_version_cstr = wine_version_func(); + if (wine_version_cstr) + { + W32.wine_version = PushString(perm, StringFromCstrNoLimit(wine_version_cstr)); + } + } + } + } + ////////////////////////////// //- Bootstrap @@ -541,7 +566,11 @@ i32 W32_Main(void) // Bootstrap log system W32_BootstrapLogs(); - LogInfoF("Main thread ID: %F", FmtUint(ThreadId())); + LogInfoF("Command line: [%F]", FmtString(cmdline_str)); + if (W32.wine_version.len > 0) + { + LogInfoF("Wine version: \"%F\"", FmtString(W32.wine_version)); + } if (appdir_error.len > 0) { LogError(appdir_error); diff --git a/src/base/base_win32/base_win32.h b/src/base/base_win32/base_win32.h index 86946f86..c5fe0e49 100644 --- a/src/base/base_win32/base_win32.h +++ b/src/base/base_win32/base_win32.h @@ -65,6 +65,7 @@ Struct(W32_Ctx) { SYSTEM_INFO info; u32 main_thread_id; + String wine_version; Atomic32 shutdown; Atomic32 exit_code; diff --git a/src/config.h b/src/config.h index 70059efa..a5632274 100644 --- a/src/config.h +++ b/src/config.h @@ -4,7 +4,7 @@ #define SIM_PHYSICS_SUBSTEPS 4 -#define SIM_TICKS_PER_SECOND 32 +#define SIM_TICKS_PER_SECOND 64 #define SIM_TICK_INTERVAL_NS (NsFromSeconds(1) / SIM_TICKS_PER_SECOND) // Like USER_INTERP_RATIO, but applies to snapshots received by the local sim from the // master sim (how far back in time should the client render the server's state) @@ -12,8 +12,6 @@ #define GPU_NAMES IsRtcEnabled -#define GPU_DEBUG 0 -#define GPU_DEBUG_VALIDATION 0 #define GPU_SHADER_PRINT 1 #define GPU_SHADER_PRINT_BUFFER_SIZE Kibi(64); diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index 815857fd..7b382525 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -10,81 +10,99 @@ void G_Bootstrap(void) Arena *perm = PermArena(); // NOTE: Nsight seems to have trouble attaching when independent devices are enabled - G_D12.independent_devices_enabled = !CommandlineArgFromName(Lit("no-independent-d3d12-device")).exists; - LogInfoF("D3D12 independent devices enabled: %F", FmtSint(G_D12.independent_devices_enabled)); + G_D12.independent_devices_enabled = !CommandlineArgFromName(Lit("no-d3d12-independent-devices")).exists; + G_D12.validation_layer_enabled = CommandlineArgFromName(Lit("gpu-debug-validation")).exists; + G_D12.debug_layer_enabled = G_D12.validation_layer_enabled || CommandlineArgFromName(Lit("gpu-debug")).exists; - ////////////////////////////// - //- Extract agility SDK - - String appdir = GetAppDirectory(); - - u32 sdk_ver_num = 618; - String sdk_ver_str = Lit("1.618.5"); - String sdk_dir_path = StringF(scratch.arena, "%Fd3d12/%F/", FmtString(appdir), FmtString(sdk_ver_str)); + if (G_D12.independent_devices_enabled && IsRunningInWine()) { - LogInfoF("D3D12 agility sdk path: \"%F\"", FmtString(sdk_dir_path)); - String core_path = StringF(scratch.arena, "%FD3D12Core.dll", FmtString(sdk_dir_path)); - String layers_path = StringF(scratch.arena, "%Fd3d12SDKLayers.dll", FmtString(sdk_dir_path)); - if (!PLT_IsFile(core_path) || !PLT_IsFile(layers_path)) - { - LogInfoF("Unpacking D3D12 Agility SDK to %F", FmtString(sdk_dir_path)); - ResourceKey core_key = ResourceKeyFromStore(&G_D12_Resources, Lit("AgilitySDK/1.618.5/D3D12Core.dat")); - ResourceKey layers_key = ResourceKeyFromStore(&G_D12_Resources, Lit("AgilitySDK/1.618.5/d3d12SDKLayers.dat")); - String core_data = PLT_Decompress(scratch.arena, DataFromResource(core_key), PLT_CompressionLevel_3); - String layers_data = PLT_Decompress(scratch.arena, DataFromResource(layers_key), PLT_CompressionLevel_3); - PLT_MkDir(StringF(scratch.arena, "%Fd3d12/", FmtString(appdir))); - PLT_MkDir(StringF(scratch.arena, "%Fd3d12/", FmtString(appdir))); - PLT_MkDir(StringF(scratch.arena, "%Fd3d12/%F/", FmtString(appdir), FmtString(sdk_ver_str))); - { - PLT_File file = PLT_OpenFileWrite(core_path); - PLT_WriteFile(file, core_data); - PLT_CloseFile(file); - } - { - PLT_File file = PLT_OpenFileWrite(layers_path); - PLT_WriteFile(file, layers_data); - PLT_CloseFile(file); - } - if (!PLT_IsFile(core_path) || !PLT_IsFile(layers_path)) - { - Panic(StringF( - scratch.arena, - "Failed to extract D3D12 Agility SDK to \"%F\"", - FmtString(core_path) - )); - } - } + LogInfoF("Wine detected, disabling D3D12 independent devices"); + G_D12.independent_devices_enabled = 0; } + LogInfoF("D3D12 independent devices enabled: %F", FmtSint(G_D12.independent_devices_enabled)); + LogInfoF("D3D12 debug layer enabled: %F", FmtSint(G_D12.debug_layer_enabled)); + LogInfoF("D3D12 validation layer enabled: %F", FmtSint(G_D12.validation_layer_enabled)); + ////////////////////////////// - //- Create device factory + //- Initialize independent device factory with Agility SDK + if (G_D12.independent_devices_enabled) { - D3D12GetInterface(&CLSID_D3D12SDKConfiguration, &IID_ID3D12SDKConfiguration1, (void **)&G_D12.sdk_config); + ////////////////////////////// + //- Extract agility SDK - // Create device factory - char *sdk_dir_path_cstr = CstrFromString(scratch.arena, PathFromString(scratch.arena, sdk_dir_path, '\\')); - HRESULT hr = ID3D12SDKConfiguration1_CreateDeviceFactory( - G_D12.sdk_config, - sdk_ver_num, - sdk_dir_path_cstr, - &IID_ID3D12DeviceFactory, - (void **)&G_D12.device_factory - ); - if (FAILED(hr)) + String appdir = GetAppDirectory(); + + u32 sdk_ver_num = 618; + String sdk_ver_str = Lit("1.618.5"); + String sdk_dir_path = StringF(scratch.arena, "%Fd3d12/%F/", FmtString(appdir), FmtString(sdk_ver_str)); { - Panic(StringF(scratch.arena, "Failed to create ID3D12DeviceFactory: Error code 0x%F", FmtHex(hr))); + LogInfoF("D3D12 agility sdk path: \"%F\"", FmtString(sdk_dir_path)); + String core_path = StringF(scratch.arena, "%FD3D12Core.dll", FmtString(sdk_dir_path)); + String layers_path = StringF(scratch.arena, "%Fd3d12SDKLayers.dll", FmtString(sdk_dir_path)); + if (!PLT_IsFile(core_path) || !PLT_IsFile(layers_path)) + { + LogInfoF("Unpacking D3D12 Agility SDK to %F", FmtString(sdk_dir_path)); + ResourceKey core_key = ResourceKeyFromStore(&G_D12_Resources, Lit("AgilitySDK/1.618.5/D3D12Core.dat")); + ResourceKey layers_key = ResourceKeyFromStore(&G_D12_Resources, Lit("AgilitySDK/1.618.5/d3d12SDKLayers.dat")); + String core_data = PLT_Decompress(scratch.arena, DataFromResource(core_key), PLT_CompressionLevel_3); + String layers_data = PLT_Decompress(scratch.arena, DataFromResource(layers_key), PLT_CompressionLevel_3); + PLT_MkDir(StringF(scratch.arena, "%Fd3d12/", FmtString(appdir))); + PLT_MkDir(StringF(scratch.arena, "%Fd3d12/", FmtString(appdir))); + PLT_MkDir(StringF(scratch.arena, "%Fd3d12/%F/", FmtString(appdir), FmtString(sdk_ver_str))); + { + PLT_File file = PLT_OpenFileWrite(core_path); + PLT_WriteFile(file, core_data); + PLT_CloseFile(file); + } + { + PLT_File file = PLT_OpenFileWrite(layers_path); + PLT_WriteFile(file, layers_data); + PLT_CloseFile(file); + } + if (!PLT_IsFile(core_path) || !PLT_IsFile(layers_path)) + { + Panic(StringF( + scratch.arena, + "Failed to extract D3D12 Agility SDK to \"%F\"", + FmtString(core_path) + )); + } + } } - // Enable debug layer - if (GPU_DEBUG) + ////////////////////////////// + //- Create device factory + + if (G_D12.independent_devices_enabled) { - ID3D12Debug1 *debug = 0; - ID3D12DeviceFactory_GetConfigurationInterface(G_D12.device_factory, &CLSID_D3D12Debug, &IID_ID3D12Debug1, (void **)&debug); - ID3D12Debug1_EnableDebugLayer(debug); - if (GPU_DEBUG_VALIDATION) + D3D12GetInterface(&CLSID_D3D12SDKConfiguration, &IID_ID3D12SDKConfiguration1, (void **)&G_D12.independent.sdk_config); + + // Create device factory + char *sdk_dir_path_cstr = CstrFromString(scratch.arena, PathFromString(scratch.arena, sdk_dir_path, '\\')); + HRESULT hr = ID3D12SDKConfiguration1_CreateDeviceFactory( + G_D12.independent.sdk_config, + sdk_ver_num, + sdk_dir_path_cstr, + &IID_ID3D12DeviceFactory, + (void **)&G_D12.independent.device_factory + ); + if (FAILED(hr)) { - ID3D12Debug1_SetEnableGPUBasedValidation(debug, 1); + Panic(StringF(scratch.arena, "Failed to create ID3D12DeviceFactory: Error code 0x%F", FmtHex(hr))); + } + + // Enable debug layer + if (G_D12.debug_layer_enabled) + { + ID3D12Debug1 *debug = 0; + ID3D12DeviceFactory_GetConfigurationInterface(G_D12.independent.device_factory, &CLSID_D3D12Debug, &IID_ID3D12Debug1, (void **)&debug); + ID3D12Debug1_EnableDebugLayer(debug); + if (G_D12.validation_layer_enabled) + { + ID3D12Debug1_SetEnableGPUBasedValidation(debug, 1); + } } } } @@ -98,7 +116,7 @@ void G_Bootstrap(void) // Create dxgi factory { u32 dxgi_factory_flags = 0; - if (GPU_DEBUG) + if (G_D12.debug_layer_enabled) { dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG; } @@ -133,7 +151,7 @@ void G_Bootstrap(void) { if (G_D12.independent_devices_enabled) { - hr = ID3D12DeviceFactory_CreateDevice(G_D12.device_factory, (IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device10, (void **)&device); + hr = ID3D12DeviceFactory_CreateDevice(G_D12.independent.device_factory, (IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device10, (void **)&device); } else { @@ -237,17 +255,8 @@ void G_Bootstrap(void) G_D12.device = device; } - // Retrieve device configuration - { - hr = ID3D12Device_QueryInterface(G_D12.device, &IID_ID3D12DeviceConfiguration, (void **)&G_D12.device_config); - if (FAILED(hr)) - { - Panic(Lit("Failed to query ID3D12DeviceConfiguration interface")); - } - } - // Enable debug layer breaks - if (GPU_DEBUG) + if (G_D12.debug_layer_enabled) { // Enable D3D12 Debug break { @@ -275,11 +284,17 @@ void G_Bootstrap(void) } } - // Log device configuration + // Retrieve device configuration if (G_D12.independent_devices_enabled) { + hr = ID3D12Device_QueryInterface(G_D12.device, &IID_ID3D12DeviceConfiguration, (void **)&G_D12.independent.device_config); + if (FAILED(hr)) + { + Panic(Lit("Failed to query ID3D12DeviceConfiguration interface")); + } + D3D12_DEVICE_CONFIGURATION_DESC desc = Zi; - ID3D12DeviceConfiguration_GetDesc(G_D12.device_config, &desc); + ID3D12DeviceConfiguration_GetDesc(G_D12.independent.device_config, &desc); StringList flags_list = Zi; { if (desc.Flags & D3D12_DEVICE_FLAG_DEBUG_LAYER_ENABLED) PushStringToList(scratch.arena, &flags_list, Lit("D3D12_DEVICE_FLAG_DEBUG_LAYER_ENABLED")); @@ -439,7 +454,7 @@ void G_Bootstrap(void) desc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED; if (G_D12.independent_devices_enabled) { - hr = ID3D12DeviceConfiguration_SerializeVersionedRootSignature(G_D12.device_config, &desc, &blob, 0); + hr = ID3D12DeviceConfiguration_SerializeVersionedRootSignature(G_D12.independent.device_config, &desc, &blob, 0); } else { @@ -1111,7 +1126,6 @@ i64 G_D12_CommitRawCommandList(G_D12_RawCommandList *cl) { completion_target = ++queue->commit_fence_target; cl->commit_fence_target = completion_target; - ID3D12CommandQueue_Signal(queue->d3d_queue, queue->commit_fence, completion_target); // Append diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.h b/src/gpu/gpu_dx12/gpu_dx12_core.h index 2019ba58..090f6e5d 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.h +++ b/src/gpu/gpu_dx12/gpu_dx12_core.h @@ -442,8 +442,10 @@ Struct(G_D12_AsyncCtx) Struct(G_D12_Ctx) { - b32 independent_devices_enabled; IsolatedAtomic64 resource_creation_gen; + b32 independent_devices_enabled; + b32 debug_layer_enabled; + b32 validation_layer_enabled; // Stats Atomic64 arenas_count; @@ -476,10 +478,15 @@ Struct(G_D12_Ctx) Mutex free_swapchains_mutex; G_D12_Swapchain *first_free_swapchain; + // Independent device (only valid when independent_devices_enabled = 1) + struct + { + ID3D12SDKConfiguration1 *sdk_config; + ID3D12DeviceConfiguration *device_config; + ID3D12DeviceFactory *device_factory; + } independent; + // Device - ID3D12SDKConfiguration1 *sdk_config; - ID3D12DeviceConfiguration *device_config; - ID3D12DeviceFactory *device_factory; IDXGIFactory6 *dxgi_factory; IDXGIAdapter3 *dxgi_adapter; ID3D12Device10 *device; diff --git a/src/pp/pp.c b/src/pp/pp.c index e0fda190..731f047d 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -2067,6 +2067,10 @@ P_Frame *P_PushFrame(P_World *world, P_Frame *src_frame, i64 tick) frame->world = world; frame->tick = tick; frame->time_ns = src_frame->time_ns; + frame->assembled_at_ns = src_frame->assembled_at_ns; + frame->base_time_ns = src_frame->base_time_ns; + frame->base_predict_from = src_frame->base_predict_from; + frame->base_predict_to = src_frame->base_predict_to; frame->first_ent = &P_NilEnt; frame->last_ent = &P_NilEnt; @@ -2412,8 +2416,8 @@ void P_StepFrame(P_Frame *frame) { if (Vec2Len(guy->v) > 0.001) { - // f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100); - f32 damp_force = TweakFloat("Guy damp force", 0, 0, 100); + f32 damp_force = TweakFloat("Guy damp force", 50, 0, 100); + // f32 damp_force = TweakFloat("Guy damp force", 0, 0, 100); Vec2 damp = MulVec2(NegVec2(guy->v), damp_force * sim_dt); guy->v = AddVec2(guy->v, damp); } diff --git a/src/pp/pp.h b/src/pp/pp.h index 265d302c..a2306e0e 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -369,6 +369,14 @@ Struct(P_Frame) u64 fragments_count; u64 received_fragments_count; u64 received_fragment_bits[(P_MaxFrameSnapshotFragments + 63) / 64]; + i64 assembled_at_ns; + + ////////////////////////////// + //- Prediction state + + i64 base_time_ns; + i64 base_predict_from; + i64 base_predict_to; }; Struct(P_FrameBin) diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 65719e33..4453835d 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -102,7 +102,7 @@ void S_TickForever(WaveLaneCtx *lane) // TODO: Keep old frames around for player snapshot deltas P_ClearFrames(world, I64Min, prev_world_frame->tick - 1); - i64 time_ns = TimeNs(); + i64 begin_time_ns = TimeNs(); ////////////////////////////// //- Swap @@ -417,21 +417,27 @@ void S_TickForever(WaveLaneCtx *lane) //- Apply bot controls { - b32 bot_movement_enabled = TweakBool("Bot movement enabled", 1); + f32 move_bias = TweakFloat("Bot movement bias", 1, -1, 1); + f32 move_frequency = TweakFloat("Bot movement frequency", 0, 0, 10); + f32 turn_frequency = TweakFloat("Bot turn frequency", 0, 0, 10); + // b32 bot_movement_enabled = TweakBool("Bot movement enabled", 1); for (P_Ent *bot = P_FirstEnt(world_frame); !P_IsEntNil(bot); bot = P_NextEnt(bot)) { if (bot->is_bot) { - if (bot_movement_enabled) - { - i64 alive_time_ns = time_ns - bot->created_at_ns; - i64 frequency_ns = NsFromSeconds(0.1); - bot->control.move.y = SinF32((f64)alive_time_ns / (f64)frequency_ns); - } - else - { - ZeroStruct(&bot->control.move); - } + i64 alive_time_ns = world_frame->time_ns - bot->created_at_ns; + f64 alive_time = SecondsFromNs(alive_time_ns); + // i64 frequency_ns = NsFromSeconds(0.1); + // bot->control.move.y = SinF32((f64)alive_time_ns / (f64)frequency_ns); + + f32 move_t = alive_time * move_frequency * Tau; + f32 look_t = alive_time * turn_frequency * Tau; + + bot->control.move.y = SinF32(move_t); + bot->control.move.y += move_bias; + bot->control.look = MulVec2(VEC2(CosF32(look_t), SinF32(look_t)), 3); + + DEBUGBREAKABLE; } } } @@ -988,7 +994,7 @@ void S_TickForever(WaveLaneCtx *lane) if (!shutdown) { i64 step_dt_ns = SIM_TICK_INTERVAL_NS; - PLT_SleepFrame(time_ns, step_dt_ns); + PLT_SleepFrame(begin_time_ns, step_dt_ns); } } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index d4013c68..c4dbb65c 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -364,7 +364,7 @@ void V_TickForever(WaveLaneCtx *lane) P_World *sim_world = P_AcquireWorld(); i64 cur_predict_world_seq = 0; - P_World *predict_worlds[2] = Zi; + P_World *predict_worlds[V_MaxInterpRatio] = Zi; for (i64 predict_world_idx = 0; predict_world_idx < countof(predict_worlds); ++predict_world_idx) { predict_worlds[predict_world_idx] = P_AcquireWorld(); @@ -388,8 +388,9 @@ void V_TickForever(WaveLaneCtx *lane) f64 most_recent_rtt = 0; // i64 smoothed_rtt_ns = 0; + f64 smoothed_rtt = 0.0; // f64 smoothed_rtt = 0.300; - f64 smoothed_rtt = 1.00; + // f64 smoothed_rtt = 1.00; // f64 smoothed_rtt = 2; f32 smooth_remote_buffered_ticks = 0; @@ -576,6 +577,8 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Begin frame + i64 prev_frame_ack = ack; + V.cur_frame_tick += 1; V_Frame *prev_frame = V_PrevFrame(); V_Frame *frame = V_CurrentFrame(); @@ -1016,11 +1019,10 @@ void V_TickForever(WaveLaneCtx *lane) Vec2 look_pos = frame->look; look_pos = RotateVec2(look_pos, InvertRot(rot)); - P_Ent *player = P_EntFromKey(frame->local_world->last_frame, V.player_key); - P_Ent *guy = P_EntFromKey(frame->local_world->last_frame, player->guy); - Vec2 guy_center = P_WorldShapeFromEnt(guy).centroid; + P_Ent *follow = P_EntFromKey(frame->local_world->last_frame, V.follow_key); + Vec2 follow_center = P_WorldShapeFromEnt(follow).centroid; Vec2 screen_center = MulVec2(frame->screen_dims, 0.5); - target_camera_pos = guy_center; + target_camera_pos = follow_center; target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(look_pos, look_ratio)); target_camera_zoom = 1; } @@ -1028,6 +1030,12 @@ void V_TickForever(WaveLaneCtx *lane) target_camera_pos.y = ClampF32(target_camera_pos.y, -world_pitch / 2, world_pitch / 2); target_camera_zoom = ClampF32(target_camera_zoom, min_zoom, max_zoom); + if (IsNan(target_camera_pos.x) || IsNan(target_camera_pos.y) || IsNan(target_camera_zoom)) + { + target_camera_pos = VEC2(0, 0); + target_camera_zoom = 1; + } + // Interpolate camera { if (prev_frame->tick == 0) @@ -1302,6 +1310,7 @@ void V_TickForever(WaveLaneCtx *lane) ack = dst_tick; ack_dt_ns = ack_received_at_ns - frame->time_ns; ack_received_at_ns = frame->time_ns; + dst_frame->assembled_at_ns = frame->time_ns; } } } @@ -1569,6 +1578,8 @@ void V_TickForever(WaveLaneCtx *lane) } } + P_Frame *ack_frame = P_FrameFromTick(sim_world, ack); + ////////////////////////////// //- Predict @@ -1608,7 +1619,7 @@ void V_TickForever(WaveLaneCtx *lane) i64 max_predict_ticks = SIM_TICKS_PER_SECOND; last_predict_tick = predict_to; first_predict_tick = ack; - first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); + // first_predict_tick = MaxI64(first_predict_tick, last_predict_tick - max_predict_ticks); } @@ -1629,12 +1640,23 @@ void V_TickForever(WaveLaneCtx *lane) + if (ack != prev_frame_ack) + { + ack_frame->base_time_ns = ack_frame->time_ns; + ack_frame->base_predict_from = ack_frame->tick; + ack_frame->base_predict_to = predict_to; + cur_predict_world_seq += 1; + } + + // Predict P_World *predict_world = predict_worlds[cur_predict_world_seq % countof(predict_worlds)]; + P_World *prev_predict_world = predict_worlds[MaxI64(cur_predict_world_seq - 1, 0) % countof(predict_worlds)]; + P_Frame *predict_frame = predict_world->last_frame; { if (predict_world->tiles_hash != sim_world->tiles_hash) @@ -1645,7 +1667,8 @@ void V_TickForever(WaveLaneCtx *lane) predict_world->seed = sim_world->seed; // P_ClearFrames(predict_world, I64Min, MinI64(first_predict_tick - 1, prev_frame->blend_from_tick - 1)); - P_ClearFrames(predict_world, I64Min, MinI64(first_predict_tick - 2, prev_frame->blend_from_tick - 1)); + // P_ClearFrames(predict_world, I64Min, MinI64(first_predict_tick - 2, prev_frame->blend_from_tick - 1)); + // P_ClearFrames(predict_world, I64Min, MinI64(first_predict_tick - V_MaxInterpRatio, prev_frame->blend_from_tick - 1)); predict_frame = P_PushFrame(predict_world, P_FrameFromTick(sim_world, first_predict_tick), first_predict_tick); for (i64 predict_tick = first_predict_tick + 1; predict_tick <= last_predict_tick; ++predict_tick) @@ -1866,8 +1889,8 @@ void V_TickForever(WaveLaneCtx *lane) frame->local_world->seed = predict_world->seed; P_ClearFrames(frame->local_world, I64Min, I64Max); - i64 target_blend_dt_ns = frame->dt_ns + frame->dt_ns * dilation_factor; - // i64 target_blend_dt_ns = frame->dt_ns; + // i64 target_blend_dt_ns = frame->dt_ns + frame->dt_ns * dilation_factor; + i64 target_blend_dt_ns = frame->dt_ns; i64 blend_dt_ns = frame->dt_ns; V.target_blend_time_ns += target_blend_dt_ns; @@ -1876,14 +1899,16 @@ void V_TickForever(WaveLaneCtx *lane) // How many ticks back in time should the user thread blend between? // = * // E.g: At 1.5, the world will render 75ms back in time if the sim runs at 50tps - f32 interp_ratio = TweakFloat("Interpolation ratio", 1.2, 0, 5); - if (predict_to != prev_frame_predict_to) + f32 interp_ratio = TweakFloat("Interpolation ratio", 1.2, 0, V_MaxInterpRatio); + if (ack != prev_frame_ack) { i64 delay_ns = SIM_TICK_INTERVAL_NS * interp_ratio; - V.target_blend_time_ns = predict_world->last_frame->time_ns - delay_ns; + V.target_blend_time_ns = ack_frame->time_ns - delay_ns; } - f64 blend_to_target_lerp_rate = 0.05; + // f64 blend_to_target_lerp_rate = 0.05; + f64 blend_to_target_lerp_rate = 1; + // f64 blend_to_target_lerp_rate = 1; V.blend_time_ns = LerpI64(V.blend_time_ns, V.target_blend_time_ns, blend_to_target_lerp_rate); if (AbsI64(V.blend_time_ns - V.target_blend_time_ns) > (SIM_TICK_INTERVAL_NS * interp_ratio * 2)) { @@ -1891,24 +1916,107 @@ void V_TickForever(WaveLaneCtx *lane) V.blend_time_ns = V.target_blend_time_ns; } - if (TweakBool("Interpolation enabled", 1)) + if (TweakBool("Interpolation enabled", 0)) { P_Frame *left_predict_frame = &P_NilFrame; P_Frame *right_predict_frame = &P_NilFrame; - for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev) + + // for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev) + // { + // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns) + // { + // right_predict_frame = tmp; + // left_predict_frame = tmp->prev; + // break; + // } + // } + + + + + + + { - if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns) + for ( + i64 predict_world_seq = cur_predict_world_seq; + predict_world_seq > MaxI64(0, cur_predict_world_seq - countof(predict_worlds)); + --predict_world_seq + ) { - right_predict_frame = tmp; - left_predict_frame = tmp->prev; - break; + i64 tmp_right_predict_world_idx = predict_world_seq % countof(predict_worlds); + i64 tmp_left_predict_world_idx = (predict_world_seq - 1) % countof(predict_worlds); + P_World *tmp_right_world = predict_worlds[tmp_right_predict_world_idx]; + P_World *tmp_left_world = predict_worlds[tmp_left_predict_world_idx]; + + // P_Frame *tmp_right = P_FrameFromTick(tmp_right_world, tmp_right_world->last_frame->base_predict_to); + // P_Frame *tmp_left = P_FrameFromTick(tmp_left_world, tmp_left_world->last_frame->base_predict_to); + + // P_Frame *tmp_right = tmp_right_world->last_frame; + // P_Frame *tmp_left = tmp_left_world->last_frame; + + P_Frame *tmp_right = P_FrameFromTick(tmp_right_world, tmp_right_world->last_frame->base_predict_from); + P_Frame *tmp_left = P_FrameFromTick(tmp_left_world, tmp_left_world->last_frame->base_predict_from); + + // P_Frame *tmp_right = tmp_right_world->last_frame; + // P_Frame *tmp_left = tmp_left_world->last_frame; + + if (tmp_right->base_time_ns >= V.blend_time_ns && tmp_left->base_time_ns <= V.blend_time_ns) + { + right_predict_frame = tmp_right; + left_predict_frame = tmp_left; + break; + } } + + // for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev) + // { + // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns) + // { + // right_predict_frame = tmp; + // break; + // } + // } + // for (P_Frame *tmp = prev_predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev) + // { + // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns) + // { + // left_predict_frame = tmp->prev; + // break; + // } + // } } + + + + // for (P_Frame *tmp = predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev) + // { + // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns) + // { + // right_predict_frame = tmp; + // break; + // } + // } + // for (P_Frame *tmp = prev_predict_world->last_frame; !P_IsFrameNil(tmp); tmp = tmp->prev) + // { + // if (tmp->time_ns >= V.blend_time_ns && tmp->prev->time_ns <= V.blend_time_ns) + // { + // left_predict_frame = tmp->prev; + // break; + // } + // } + + + + + if (P_IsFrameNil(left_predict_frame) || P_IsFrameNil(right_predict_frame)) { + LogDebugF("Missing blend"); right_predict_frame = predict_world->last_frame; - left_predict_frame = right_predict_frame->prev; + // left_predict_frame = right_predict_frame->prev; + left_predict_frame = right_predict_frame; } frame->blend_from_tick = left_predict_frame->tick; @@ -1916,6 +2024,9 @@ void V_TickForever(WaveLaneCtx *lane) local_frame = P_PushFrame(frame->local_world, left_predict_frame, left_predict_frame->tick); { + P_Frame *left_sim_frame = left_predict_frame; + P_Frame *right_sim_frame = right_predict_frame; + // P_Frame *left_sim_frame = predict_world->first_frame; // P_Frame *right_sim_frame = left_sim_frame->next; @@ -1926,12 +2037,17 @@ void V_TickForever(WaveLaneCtx *lane) // P_Frame *right_sim_frame = P_FrameFromTick(sim_world, ack - 1); // P_Frame *left_sim_frame = right_sim_frame->prev; - P_Frame *right_sim_frame = P_FrameFromTick(sim_world, right_predict_frame->src_tick); - P_Frame *left_sim_frame = right_sim_frame->prev; + // P_Frame *right_sim_frame = P_FrameFromTick(sim_world, right_predict_frame->src_tick); + // P_Frame *left_sim_frame = right_sim_frame->prev; + + f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->base_time_ns) / (f64)(right_predict_frame->base_time_ns - left_predict_frame->base_time_ns); + + if (IsInf(blend_t)) + { + blend_t = 1; + } - f64 blend_t = (f64)(V.blend_time_ns - left_predict_frame->time_ns) / (f64)(right_predict_frame->time_ns - left_predict_frame->time_ns); - LogDebugF("blend_t: %F", FmtFloat(blend_t)); for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) { @@ -2103,6 +2219,12 @@ void V_TickForever(WaveLaneCtx *lane) P_Ent *local_player = P_EntFromKey(frame->local_world->last_frame, V.player_key); P_Ent *local_guy = P_EntFromKey(frame->local_world->last_frame, local_player->guy); + P_Ent *local_follow = P_EntFromKey(frame->local_world->last_frame, V.follow_key); + if (P_IsEntNil(local_follow)) + { + local_follow = local_guy; + V.follow_key = local_guy->key; + } ////////////////////////////// //- Compute crosshair position @@ -2111,7 +2233,8 @@ void V_TickForever(WaveLaneCtx *lane) if (!P_IsEntNil(local_guy)) { - P_Ent *ent = local_guy; + // P_Ent *ent = local_guy; + P_Ent *ent = local_follow; P_Anim anim = P_AnimFromEnt(local_frame, ent); SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq); @@ -2311,11 +2434,17 @@ void V_TickForever(WaveLaneCtx *lane) } } - - - - - + if (frame->held_buttons[Button_V]) + { + if (frame->held_buttons[Button_Shift]) + { + V.follow_key = local_guy->key; + } + else if (!P_IsEntNil(hovered_ent)) + { + V.follow_key = hovered_ent->key; + } + } ////////////////////////////// //- Draw entities @@ -2725,9 +2854,9 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Push test emitter - // if (frame->held_buttons[Button_F]) + if (frame->held_buttons[Button_F]) // if (frame->held_buttons[Button_F] && !prev_frame->held_buttons[Button_F]) - if (0) + // if (0) { { V_Emitter emitter = Zi; @@ -2787,9 +2916,9 @@ void V_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Push test explosion - // if (frame->held_buttons[Button_G]) + if (frame->held_buttons[Button_G]) // if (frame->held_buttons[Button_G] && !prev_frame->held_buttons[Button_G]) - if (0) + // if (0) { // Fire { @@ -5313,7 +5442,6 @@ void V_TickForever(WaveLaneCtx *lane) G_DumbMemoryLayoutSync(cl, screen_target, G_Layout_DirectQueue_General); } - ////////////////////////////// //- Set UI box texture diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index a5a53ed1..c0e08265 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -218,6 +218,8 @@ Enum(V_DrawFlag) //////////////////////////////////////////////////////////// //~ State types +#define V_MaxInterpRatio 4 + Struct(V_Frame) { Arena *arena; @@ -255,6 +257,7 @@ Struct(V_Frame) Struct(V_Ctx) { P_EntKey player_key; + P_EntKey follow_key; i64 panels_count; i64 windows_count;