From b4a51ff5af3ab1c15f37043416f5709218f78fb9 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 19 Nov 2025 16:32:42 -0600 Subject: [PATCH] gpu layer refactor progress --- src/gpu/gpu_core.h | 12 +- src/gpu/gpu_dx12/gpu_dx12.c | 475 +++++++----------- src/gpu/gpu_dx12/gpu_dx12.h | 10 - src/pp/pp_res/sound/test.mp3 | 3 - src/proto/pp_vis/pp_vis_res/icon.ico | Bin 4286 -> 0 bytes src/proto/pp_vis/pp_vis_res/sound/test.mp3 | 3 - src/proto/pp_vis/pp_vis_res/sprite/blood.ase | 3 - src/proto/pp_vis/pp_vis_res/sprite/box.ase | 3 - .../pp_vis/pp_vis_res/sprite/box_rounded.ase | 3 - src/proto/pp_vis/pp_vis_res/sprite/bullet.ase | 3 - .../pp_vis/pp_vis_res/sprite/crosshair.ase | 3 - src/proto/pp_vis/pp_vis_res/sprite/gun.ase | 3 - src/proto/pp_vis/pp_vis_res/sprite/tile.ase | 3 - src/proto/pp_vis/pp_vis_res/sprite/tim.ase | 3 - 14 files changed, 193 insertions(+), 334 deletions(-) delete mode 100644 src/pp/pp_res/sound/test.mp3 delete mode 100644 src/proto/pp_vis/pp_vis_res/icon.ico delete mode 100644 src/proto/pp_vis/pp_vis_res/sound/test.mp3 delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/blood.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/box.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/box_rounded.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/bullet.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/crosshair.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/gun.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/tile.ase delete mode 100644 src/proto/pp_vis/pp_vis_res/sprite/tim.ase diff --git a/src/gpu/gpu_core.h b/src/gpu/gpu_core.h index 039ccae0..7798c1eb 100644 --- a/src/gpu/gpu_core.h +++ b/src/gpu/gpu_core.h @@ -13,15 +13,13 @@ Enum(GPU_QueueKind) { #if GPU_MultiQueueEnabled GPU_QueueKind_Direct = 0, - GPU_QueueKind_Compute = 1, - GPU_QueueKind_Copy = 2, - GPU_QueueKind_BackgroundCopy = 3, - GPU_NumQueues = 4 + GPU_QueueKind_AsyncCompute = 1, + GPU_QueueKind_AsyncCopy = 2, + GPU_NumQueues = 3 #else GPU_QueueKind_Direct = 0, - GPU_QueueKind_Compute = 0, - GPU_QueueKind_Copy = 0, - GPU_QueueKind_BackgroundCopy = 0, + GPU_QueueKind_AsyncCompute = 0, + GPU_QueueKind_AsyncCopy = 0, GPU_NumQueues = 1 #endif }; diff --git a/src/gpu/gpu_dx12/gpu_dx12.c b/src/gpu/gpu_dx12/gpu_dx12.c index 795a09e1..d04183ef 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.c +++ b/src/gpu/gpu_dx12/gpu_dx12.c @@ -1,94 +1,200 @@ GPU_D12_SharedState GPU_D12_shared_state = ZI; -//////////////////////////////////////////////////////////// -//~ Helpers - -GPU_D12_FiberState *GPU_D12_FiberStateFromId(i16 fiber_id) -{ - GPU_D12_SharedState *g = &GPU_D12_shared_state; - GPU_D12_FiberState **f = &g->fiber_states[fiber_id]; - if (!*f) - { - Arena *perm = PermArena(); - *f = PushStruct(perm, GPU_D12_FiberState); - } - return *f; -} - -DXGI_FORMAT GPU_D12_DxgiFormatFromGpuFormat(GPU_Format format) -{ - return (DXGI_FORMAT)format; -} - -GPU_D12_Command *GPU_D12_PushCmd(GPU_D12_CommandList *cl) -{ - GPU_D12_FiberState *f = GPU_D12_FiberStateFromId(FiberId()); - Arena *perm = PermArena(); - GPU_D12_Command *cmd = f->first_free_command; - if (cmd) - { - f->first_free_command = cmd->next; - } - else - { - cmd = PushStructNoZero(perm, GPU_D12_Command); - } - ZeroStruct(cmd); - SllQueuePush(cl->first, cl->last, cmd); - ++cl->count; - return cmd; -} - -u64 GPU_D12_ReuseHashFromResourceDesc(GPU_ResourceDesc desc, u64 buffer_size) -{ - u64 result = RandU64FromSeeds(desc.kind, desc.flags); - switch(desc.kind) - { - default: break; - case GPU_ResourceKind_Texture1D: - case GPU_ResourceKind_Texture2D: - case GPU_ResourceKind_Texture3D: - { - result = RandU64FromSeeds(result, desc.texture.format); - result = RandU64FromSeeds(result, desc.texture.mip_levels); - result = RandU64FromSeeds(result, desc.clear_color.x); - result = RandU64FromSeeds(result, desc.clear_color.y); - result = RandU64FromSeeds(result, desc.clear_color.z); - result = RandU64FromSeeds(result, desc.clear_color.w); - result = RandU64FromSeeds(result, desc.texture.size.x); - result = RandU64FromSeeds(result, desc.texture.size.y); - result = RandU64FromSeeds(result, desc.texture.size.z); - } break; - case GPU_ResourceKind_Buffer: - { - result = RandU64FromSeeds(result, desc.buffer.heap_kind); - result = RandU64FromSeeds(result, buffer_size); - } break; - } - return result; -} - //////////////////////////////////////////////////////////// //~ Startup void GPU_D12_Startup(void) { GPU_D12_SharedState *g = &GPU_D12_shared_state; + TempArena scratch = BeginScratchNoConflict(); + Arena *perm = PermArena(); - /* Init device */ - GPU_D12_InitDevice(); + ////////////////////////////// + //- Initialize device - /* Init queues */ { - GPU_D12_QueueDesc descs[] = { - {.kind = GPU_QueueKind_Direct, .d3d_type = D3D12_COMMAND_LIST_TYPE_DIRECT, .d3d_priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, .dbg_name = Lit("Direct queue") }, - {.kind = GPU_QueueKind_Compute, .d3d_type = D3D12_COMMAND_LIST_TYPE_COMPUTE, .d3d_priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, .dbg_name = Lit("Compute queue") }, - {.kind = GPU_QueueKind_Copy, .d3d_type = D3D12_COMMAND_LIST_TYPE_COPY, .d3d_priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH, .dbg_name = Lit("Copy queue") }, - {.kind = GPU_QueueKind_BackgroundCopy, .d3d_type = D3D12_COMMAND_LIST_TYPE_COPY, .d3d_priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, .dbg_name = Lit("Background copy queue") } - }; - u32 job_count = 0; Fence job_fence = ZI; - job_count += RunJob(GPU_D12_InitQueue, .count = GPU_NumQueues, .sig.descs = descs, .fence = &job_fence); - YieldOnFence(&job_fence, job_count); + HRESULT hr = 0; + + //- Enable debug layer + u32 dxgi_factory_flags = 0; +#if GPU_DEBUG + { + __profn("Enable debug layer"); + ID3D12Debug *debug_controller0 = 0; + { + hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller0); + if (FAILED(hr)) + { + Panic(Lit("Failed to create ID3D12Debug0")); + } + ID3D12Debug_EnableDebugLayer(debug_controller0); +#if GPU_DEBUG_VALIDATION + { + ID3D12Debug1 *debug_controller1 = 0; + { + hr = ID3D12Debug_QueryInterface(debug_controller0, &IID_ID3D12Debug1, (void **)&debug_controller1); + if (FAILED(hr)) + { + Panic(Lit("Failed to create ID3D12Debug1")); + } + ID3D12Debug1_SetEnableGPUBasedValidation(debug_controller1, 1); + } + ID3D12Debug_Release(debug_controller1); + } +#endif + } + ID3D12Debug_Release(debug_controller0); + dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG; + } +#endif + + //- Create factory + { + __profn("Create factory"); + hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&g->factory); + if (FAILED(hr)) + { + Panic(Lit("Failed to initialize DXGI factory")); + } + } + + //- Create device + { + __profn("Create device"); + IDXGIAdapter3 *adapter = 0; + ID3D12Device *device = 0; + String error = Lit("Could not initialize GPU device."); + String first_gpu_name = ZI; + u32 adapter_index = 0; + b32 skip = 0; /* For iGPU testing */ + for (;;) + { + { + hr = IDXGIFactory6_EnumAdapterByGpuPreference(g->factory, adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, &IID_IDXGIAdapter3, (void **)&adapter); + } + if (SUCCEEDED(hr)) + { + DXGI_ADAPTER_DESC1 desc; + IDXGIAdapter3_GetDesc1(adapter, &desc); + if (first_gpu_name.len == 0) + { + first_gpu_name = StringFromWstrNoLimit(scratch.arena, desc.Description); + } + { + /* TODO: Verify feature support: + * - HighestShaderModel >= D3D_SHADER_MODEL_6_6 + * - ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_3 + * - EnhancedBarriersSupported == 1 + */ + hr = D3D12CreateDevice((IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&device); + } + if (SUCCEEDED(hr) && !skip) + { + break; + } + skip = 0; + ID3D12Device_Release(device); + IDXGIAdapter3_Release(adapter); + adapter = 0; + device = 0; + ++adapter_index; + } + else + { + break; + } + } + if (!device) + { + if (first_gpu_name.len > 0) + { + error = StringF(scratch.arena, + "Could not initialize device '%F' with D3D_FEATURE_LEVEL_12_0. Ensure that the device is capable and drivers are up to date.", + FmtString(first_gpu_name)); + } + Panic(error); + } + g->adapter = adapter; + g->device = device; + } + + //- Enable debug layer breaks + { +#if GPU_DEBUG + /* Enable D3D12 Debug break */ + { + __profn("Enable d3d12 debug break"); + ID3D12InfoQueue *info = 0; + hr = ID3D12Device_QueryInterface(g->device, &IID_ID3D12InfoQueue, (void **)&info); + if (FAILED(hr)) + { + Panic(Lit("Failed to query ID3D12Device interface")); + } + ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_CORRUPTION, 1); + ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_ERROR, 1); + ID3D12InfoQueue_Release(info); + } + /* Enable DXGI Debug break */ + { + __profn("Enable dxgi debug break"); + IDXGIInfoQueue *dxgi_info = 0; + hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info); + if (FAILED(hr)) + { + Panic(Lit("Failed to get DXGI debug interface")); + } + IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, 1); + IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, 1); + IDXGIInfoQueue_Release(dxgi_info); + } +#endif + } + } + + ////////////////////////////// + //- Initialize queues + + { + GPU_D12_Queue *direct = PushStruct(perm, GPU_D12_Queue); + GPU_D12_Queue *async_compute = PushStruct(perm, GPU_D12_Queue); + GPU_D12_Queue *async_copy = PushStruct(perm, GPU_D12_Queue); + g->queues[GPU_QueueKind_Direct] = direct; + g->queues[GPU_QueueKind_AsyncCompute] = async_compute; + g->queues[GPU_QueueKind_AsyncCopy] = async_copy; + b32 ok = 1; + { + if (ok) + { + D3D12_COMMAND_QUEUE_DESC desc = { .Type = D3D12_COMMAND_LIST_TYPE_DIRECT, .Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH }; + ok = SUCCEEDED(ID3D12Device_CreateCommandQueue(g->device, &desc, &IID_ID3D12CommandQueue, (void **)&direct->d3d_queue)); + if (ok) + { + ok = SUCCEEDED(ID3D12Device_CreateFence(g->device, 0, 0, &IID_ID3D12Fence, (void **)&direct->submit_fence)); + } + } + if (ok) + { + D3D12_COMMAND_QUEUE_DESC desc = { .Type = D3D12_COMMAND_LIST_TYPE_COMPUTE, .Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL }; + ok = SUCCEEDED(ID3D12Device_CreateCommandQueue(g->device, &desc, &IID_ID3D12CommandQueue, (void **)&async_compute->d3d_queue)); + if (ok) + { + ok = SUCCEEDED(ID3D12Device_CreateFence(g->device, 0, 0, &IID_ID3D12Fence, (void **)&async_compute->submit_fence)); + } + } + if (ok) + { + D3D12_COMMAND_QUEUE_DESC desc = { .Type = D3D12_COMMAND_LIST_TYPE_COPY, .Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL }; + ok = SUCCEEDED(ID3D12Device_CreateCommandQueue(g->device, &desc, &IID_ID3D12CommandQueue, (void **)&async_copy->d3d_queue)); + if (ok) + { + ok = SUCCEEDED(ID3D12Device_CreateFence(g->device, 0, 0, &IID_ID3D12Fence, (void **)&async_copy->submit_fence)); + } + } + } + if (!ok) + { + Panic(Lit("Failed to create GPU Command Queues")); + } } /* Init descriptor heaps */ @@ -113,192 +219,12 @@ void GPU_D12_Startup(void) /* Start queue sync job */ JobPoolId sync_pool = InitJobPool(1, Lit("Dx12 queue sync"), JobPoolPriority_Critical); RunJob(GPU_D12_StartQueueSync, .pool = sync_pool); -} - -//////////////////////////////////////////////////////////// -//~ Initialization - -//- Device initialization - -void GPU_D12_InitDevice(void) -{ - GPU_D12_SharedState *g = &GPU_D12_shared_state; - TempArena scratch = BeginScratchNoConflict(); - HRESULT hr = 0; - - /* Enable debug layer */ - u32 dxgi_factory_flags = 0; -#if GPU_DEBUG - { - __profn("Enable debug layer"); - ID3D12Debug *debug_controller0 = 0; - { - hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller0); - if (FAILED(hr)) - { - Panic(Lit("Failed to create ID3D12Debug0")); - } - ID3D12Debug_EnableDebugLayer(debug_controller0); -#if GPU_DEBUG_VALIDATION - { - ID3D12Debug1 *debug_controller1 = 0; - { - hr = ID3D12Debug_QueryInterface(debug_controller0, &IID_ID3D12Debug1, (void **)&debug_controller1); - if (FAILED(hr)) - { - Panic(Lit("Failed to create ID3D12Debug1")); - } - ID3D12Debug1_SetEnableGPUBasedValidation(debug_controller1, 1); - } - ID3D12Debug_Release(debug_controller1); - } -#endif - } - ID3D12Debug_Release(debug_controller0); - dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG; - } -#endif -#if GPU_DEBUG == 0 && GPU_DEBUG_VALIDATION != 0 -# error Gpu validation is enabled but gpu debugging is not -#endif - - /* Create factory */ - { - __profn("Create factory"); - hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&g->factory); - if (FAILED(hr)) - { - Panic(Lit("Failed to initialize DXGI factory")); - } - } - - /* Create device */ - { - __profn("Create device"); - IDXGIAdapter3 *adapter = 0; - ID3D12Device *device = 0; - String error = Lit("Could not initialize GPU device."); - String first_gpu_name = ZI; - u32 adapter_index = 0; - b32 skip = 0; /* For debugging iGPU */ - for (;;) - { - { - hr = IDXGIFactory6_EnumAdapterByGpuPreference(g->factory, adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, &IID_IDXGIAdapter3, (void **)&adapter); - } - if (SUCCEEDED(hr)) - { - DXGI_ADAPTER_DESC1 desc; - IDXGIAdapter3_GetDesc1(adapter, &desc); - if (first_gpu_name.len == 0) - { - first_gpu_name = StringFromWstrNoLimit(scratch.arena, desc.Description); - } - { - /* TODO: Verify feature support: - * - HighestShaderModel >= D3D_SHADER_MODEL_6_6 - * - ResourceBindingTier >= D3D12_RESOURCE_BINDING_TIER_3 - * - EnhancedBarriersSupported == 1 - */ - hr = D3D12CreateDevice((IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&device); - } - if (SUCCEEDED(hr) && !skip) - { - break; - } - skip = 0; - ID3D12Device_Release(device); - IDXGIAdapter3_Release(adapter); - adapter = 0; - device = 0; - ++adapter_index; - } - else - { - break; - } - } - if (!device) - { - if (first_gpu_name.len > 0) - { - error = StringF(scratch.arena, - "Could not initialize device '%F' with D3D_FEATURE_LEVEL_12_0. Ensure that the device is capable and drivers are up to date.", - FmtString(first_gpu_name)); - } - Panic(error); - } - g->adapter = adapter; - g->device = device; - } - -#if GPU_DEBUG - /* Enable D3D12 Debug break */ - { - __profn("Enable d3d12 debug break"); - ID3D12InfoQueue *info = 0; - hr = ID3D12Device_QueryInterface(g->device, &IID_ID3D12InfoQueue, (void **)&info); - if (FAILED(hr)) - { - Panic(Lit("Failed to query ID3D12Device interface")); - } - ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_CORRUPTION, 1); - ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_ERROR, 1); - ID3D12InfoQueue_Release(info); - } - - /* Enable DXGI Debug break */ - { - __profn("Enable dxgi debug break"); - IDXGIInfoQueue *dxgi_info = 0; - hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info); - if (FAILED(hr)) - { - Panic(Lit("Failed to get DXGI debug interface")); - } - IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, 1); - IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, 1); - IDXGIInfoQueue_Release(dxgi_info); - } -#endif EndScratch(scratch); } -//- Queue initialization - -JobDef(GPU_D12_InitQueue, sig, id) -{ - GPU_D12_SharedState *g = &GPU_D12_shared_state; - GPU_D12_QueueDesc desc = sig->descs[id]; - Arena *perm = PermArena(); - HRESULT hr = 0; - - GPU_D12_Queue *queue = 0; - { - PushAlign(perm, CachelineSize); - queue = PushStruct(perm, GPU_D12_Queue); - PushAlign(perm, CachelineSize); - } - queue->desc = desc; - - D3D12_COMMAND_QUEUE_DESC d3d_desc = ZI; - d3d_desc.Type = desc.d3d_type; - d3d_desc.Priority = desc.d3d_priority; - hr = ID3D12Device_CreateCommandQueue(g->device, &d3d_desc, &IID_ID3D12CommandQueue, (void **)&queue->d3d_queue); - if (FAILED(hr)) - { - Panic(Lit("Failed to create command queue")); - } - - hr = ID3D12Device_CreateFence(g->device, 0, 0, &IID_ID3D12Fence, (void **)&queue->submit_fence); - if (FAILED(hr)) - { - Panic(Lit("Failed to create command queue fence")); - } - - g->queues[desc.kind] = queue; -} +//////////////////////////////////////////////////////////// +//~ Initialization //- Heap initialization @@ -772,31 +698,6 @@ void GPU_QueueWait(GPU_QueueKind a, GPU_QueueKind b, i64 b_target_fence_value) ID3D12CommandQueue_Wait(queue_a->d3d_queue, b_fence, b_target_fence_value); } -//////////////////////////////////////////////////////////// -//~ @hookdef Rasterizer helper hooks - -GPU_Viewport GPU_ViewportFromRect(Rng2 rect) -{ - GPU_Viewport viewport = ZI; - viewport.top_left_x = rect.p0.x; - viewport.top_left_y = rect.p0.y; - viewport.width = rect.p1.x - rect.p0.x; - viewport.height = rect.p1.y - rect.p0.y; - viewport.min_depth = 0.0f; - viewport.max_depth = 1.0f; - return viewport; -} - -GPU_Scissor GPU_ScissorFromRect(Rng2 rect) -{ - GPU_Scissor scissor = ZI; - scissor.left = rect.p0.x; - scissor.top = rect.p0.y; - scissor.right = rect.p1.x; - scissor.bottom = rect.p1.y; - return scissor; -} - //////////////////////////////////////////////////////////// //~ @hookdef Resource hooks diff --git a/src/gpu/gpu_dx12/gpu_dx12.h b/src/gpu/gpu_dx12/gpu_dx12.h index d2f857c9..903b7cf3 100644 --- a/src/gpu/gpu_dx12/gpu_dx12.h +++ b/src/gpu/gpu_dx12/gpu_dx12.h @@ -130,14 +130,6 @@ Struct(GPU_D12_ResourceReuseListBin) //////////////////////////////////////////////////////////// //~ Queue types -Struct(GPU_D12_QueueDesc) -{ - GPU_QueueKind kind; - D3D12_COMMAND_LIST_TYPE d3d_type; - D3D12_COMMAND_QUEUE_PRIORITY d3d_priority; - String dbg_name; -}; - Struct(GPU_D12_Queue) { GPU_D12_QueueDesc desc; @@ -287,8 +279,6 @@ Struct(GPU_D12_FiberState) Struct(GPU_D12_SharedState) { - GPU_D12_FiberState *fiber_states[MaxFibers]; - Atomic64Padded resource_barrier_gen; /* Stats */ diff --git a/src/pp/pp_res/sound/test.mp3 b/src/pp/pp_res/sound/test.mp3 deleted file mode 100644 index 58ecaffd..00000000 --- a/src/pp/pp_res/sound/test.mp3 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0640de834464d484a20878ccbf635d02e8a8e11b7bf1e38e5a8a6c66e7fe93b5 -size 2547394 diff --git a/src/proto/pp_vis/pp_vis_res/icon.ico b/src/proto/pp_vis/pp_vis_res/icon.ico deleted file mode 100644 index ab6dc4ff9d4257f5ede2f2838a9d2ad3922a21c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmd^6F%H5&3=0!06LUwN!5bKm_!w{CH++Fdz`%fxOC?KHb#YI!nx%(=Tc?R@*KAjR z!(jezkAvCK%ucl`W#?MXkEiyObGhD1PbnH~*0t~7&-e~goL>7gY=_e8@xHeH-0Son z*NR6r8|4o^L%-)6Fb_%(J|oQV4~-UrQ#`WSp6vY@wnOPfxr5(=|DJE4WXxufbA(&S zA0Ar>j%BFyqTIn}=)2ZZDIVD@d`6hz9~vzL$2=&#rQFe1G}vsEJNOL$&}bnz=0WKp z-z8Ku*er66FvCAIS_qDLP