diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt index f4d6d90..455344c 100644 --- a/Engine/CMakeLists.txt +++ b/Engine/CMakeLists.txt @@ -20,18 +20,21 @@ add_library(ProjectDelta SHARED "include/delta/definitions.h" "src/delta/platform/os_internal.h" "src/delta/platform/os_win32.cpp" + "include/delta/platform/os_types.h" "include/delta/platform/os.h" "include/delta/pch.h" "src/delta/pch.h" "src/delta/pch.cpp" "src/delta/core/EngineTypes.h" - "src/delta/core/ThreadContext.h" - "src/delta/core/ThreadContext.cpp" "src/delta/core/MemoryConfig.h" "src/delta/core/MemoryConfig.cpp" - "src/delta/core/Worker.h" - "src/delta/core/Worker.cpp" "include/delta/utils/StaticArray.h" + "src/delta/platform/os_internal_types.h" + "include/delta/core/core_types.h" + "include/delta/utils/CTMath.h" + "src/delta/core/aos_helper.h" + "src/delta/core/ThreadContextManager.h" + "src/delta/core/ThreadContextManager.cpp" ) target_compile_definitions(ProjectDelta diff --git a/Engine/include/delta/core/core_types.h b/Engine/include/delta/core/core_types.h new file mode 100644 index 0000000..3d331a8 --- /dev/null +++ b/Engine/include/delta/core/core_types.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace delta::core +{ + struct Context + { + delta::platform::WindowHandle window; + bool isRunning; + }; + + enum class AllocationType : uint8_t { TRANSIENT, PERSISTENT }; + + typedef void (*GameInitFunc)(Context*); + typedef void (*GameUpdateFunc)(Context*); + typedef void (*GameShutdownFunc)(Context*); +} diff --git a/Engine/include/delta/core/engine.h b/Engine/include/delta/core/engine.h index 40fc429..3fe4bef 100644 --- a/Engine/include/delta/core/engine.h +++ b/Engine/include/delta/core/engine.h @@ -15,18 +15,11 @@ */ #include +#include +#include -namespace delta::Engine +namespace delta::core { - struct Context - { - bool isRunning; - }; - - typedef void (*GameInitFunc)(Context*); - typedef void (*GameUpdateFunc)(Context*); - typedef void (*GameShutdownFunc)(Context*); - DLT_API void Initialize(Context& context); DLT_API void Update(Context& context); DLT_API void Shutdown(Context& context); diff --git a/Engine/include/delta/core/memory.h b/Engine/include/delta/core/memory.h index 0c1ba98..45ef8ac 100644 --- a/Engine/include/delta/core/memory.h +++ b/Engine/include/delta/core/memory.h @@ -17,21 +17,20 @@ #pragma once #include +#include -namespace delta::Engine +namespace delta::core { - enum class AllocationType : uint8_t { TRANSIENT, PERSISTENT }; - [[nodiscard]] DLT_API void* Allocate(size_t size, AllocationType type, size_t alignment = 8) noexcept; DLT_API void Free(void* ptr) noexcept; } -[[nodiscard]] inline void* operator new(size_t size) { return delta::Engine::Allocate(size, delta::Engine::AllocationType::PERSISTENT); } -[[nodiscard]] inline void* operator new(size_t size, delta::Engine::AllocationType type) { return delta::Engine::Allocate(size, type); } -[[nodiscard]] inline void* operator new[](size_t size) { return delta::Engine::Allocate(size, delta::Engine::AllocationType::PERSISTENT); } -[[nodiscard]] inline void* operator new[](size_t size, delta::Engine::AllocationType type) { return delta::Engine::Allocate(size, type); } +[[nodiscard]] inline void* operator new(size_t size) { return delta::core::Allocate(size, delta::core::AllocationType::PERSISTENT); } +[[nodiscard]] inline void* operator new(size_t size, delta::core::AllocationType type) { return delta::core::Allocate(size, type); } +[[nodiscard]] inline void* operator new[](size_t size) { return delta::core::Allocate(size, delta::core::AllocationType::PERSISTENT); } +[[nodiscard]] inline void* operator new[](size_t size, delta::core::AllocationType type) { return delta::core::Allocate(size, type); } -inline void operator delete(void* ptr) { return delta::Engine::Free(ptr); } -inline void operator delete(void* ptr, delta::Engine::AllocationType) { return delta::Engine::Free(ptr); } -inline void operator delete[](void* ptr) { return delta::Engine::Free(ptr); } -inline void operator delete[](void* ptr, delta::Engine::AllocationType) { return delta::Engine::Free(ptr); } +inline void operator delete(void* ptr) { return delta::core::Free(ptr); } +inline void operator delete(void* ptr, delta::core::AllocationType) { return delta::core::Free(ptr); } +inline void operator delete[](void* ptr) { return delta::core::Free(ptr); } +inline void operator delete[](void* ptr, delta::core::AllocationType) { return delta::core::Free(ptr); } diff --git a/Engine/include/delta/platform/os.h b/Engine/include/delta/platform/os.h index 75e1525..7a29f01 100644 --- a/Engine/include/delta/platform/os.h +++ b/Engine/include/delta/platform/os.h @@ -15,34 +15,21 @@ */ #pragma once +#include namespace delta::platform { - struct OSInfo - { - const char* cpuArchitecture; - - uint32_t cpuPhysicalCoreCount; - uint32_t cpuLogicalProcessorCount; - uint32_t osPageSize; - - char cpuBrandString[sizeof(int) * 12 + 1]; - char cpuManufacturerId[13]; - - bool cpuHasSMT; - bool cpuHasAVX2; - bool cpuHasAVX512f; - bool cpuHasAVX512cd; - bool cpuHasAVX512er; - bool cpuHasAVX512pf; - }; - - struct MemoryStatus - { - uint64_t physicalInstalled; - uint64_t physicalFree; - }; - + // General + // TODO: Change names to the adequate ones DLT_API const OSInfo* getOSInfo() noexcept; DLT_API MemoryStatus getMemoryStatus(); + + // Window API + DLT_API void Window_SetTitle(WindowHandle window, const char* newTitle); + DLT_API void Window_Show(WindowHandle window); + DLT_API void Window_Hide(WindowHandle window); + DLT_API void Window_SetSize(WindowHandle window, uint32_t w, uint32_t h); + DLT_API void Window_SetPos(WindowHandle window, uint32_t x, uint32_t y); + DLT_API void Window_ShowCursor(WindowHandle window); + DLT_API void Window_HideCursor(WindowHandle window); } diff --git a/Engine/include/delta/platform/os_types.h b/Engine/include/delta/platform/os_types.h new file mode 100644 index 0000000..151f302 --- /dev/null +++ b/Engine/include/delta/platform/os_types.h @@ -0,0 +1,36 @@ +#pragma once + +#define DLT_DEFINE_HANDLE(name)\ + struct name;\ + using name##Handle = name*;\ + inline constexpr name##Handle INVALID_##name##_HANDLE = nullptr + +namespace delta::platform +{ + DLT_DEFINE_HANDLE(Window); + + struct OSInfo + { + const char* cpuArchitecture; + + uint32_t cpuPhysicalCoreCount; + uint32_t cpuLogicalProcessorCount; + uint32_t osPageSize; + + char cpuBrandString[sizeof(int) * 12 + 1]; + char cpuManufacturerId[13]; + + bool cpuHasSMT; + bool cpuHasAVX2; + bool cpuHasAVX512f; + bool cpuHasAVX512cd; + bool cpuHasAVX512er; + bool cpuHasAVX512pf; + }; + + struct MemoryStatus + { + uint64_t physicalInstalled; + uint64_t physicalFree; + }; +} diff --git a/Engine/include/delta/utils/CTMath.h b/Engine/include/delta/utils/CTMath.h new file mode 100644 index 0000000..f57d4ca --- /dev/null +++ b/Engine/include/delta/utils/CTMath.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace delta::utils +{ + template + inline constexpr bool is_power_of_two(T value) noexcept + { + return value > 0 && (value & (value - 1)) == 0; + } + + template + [[nodiscard]] inline constexpr T align_up(T value, T alignment) noexcept + { + T a = alignment - 1; + return ((alignment & a) == 0) + ? ((value + a) & ~(a)) + : (((value + a) / alignment) * alignment); + } +} diff --git a/Engine/src/delta/core/EngineTypes.h b/Engine/src/delta/core/EngineTypes.h index 98db880..da613e2 100644 --- a/Engine/src/delta/core/EngineTypes.h +++ b/Engine/src/delta/core/EngineTypes.h @@ -17,95 +17,51 @@ #pragma once #include +#include +#include namespace delta::core { - // TODO: revise this structure and its purpose - struct ThreadPageCoordinator - { - uint8_t* virtualAddressBase; - size_t commitedOffset; - size_t reservedCapacity; - size_t pageSize; - }; - - struct ThreadArena - { - uint8_t* backingMemory; - size_t capacity; - size_t offset; - size_t maxCapacity; - }; - - struct DependencyCounter - { - std::atomic count; - }; - - using task_t = void (*)(void*); - using payload_t = void*; - using queue_index_t = int64_t; - struct TaskQueue // SoA structure, Chase-Lev queue - { - alignas(64) std::atomic top; - alignas(64) std::atomic bottom; - - alignas(64) queue_index_t size; - queue_index_t mask; - - task_t* tasks; - payload_t* payloads; - DependencyCounter* depCounterPtr; - - static inline constexpr size_t FIELD_SIZE = sizeof(task_t) + sizeof(payload_t); - }; - - enum class ThreadType : uint8_t { MAIN, WORKER }; - - struct alignas(64) GenericExecutionContext - { - // SHARED TRAITS - ThreadType type; - uint32_t threadIx; - delta::platform::ThreadHandle threadHandle; - - ThreadPageCoordinator pageCoordinator; - ThreadArena transientArena; - delta::platform::Timer perThreadTimer; - }; - - struct alignas(64) MainExecutionContext - { - // SHARED TRAITS - GenericExecutionContext generic; - - // ROLE TRAITS - ThreadArena persistentStorage; - DependencyCounter depCounter; - }; - - struct alignas(64) WorkerExecutionContext - { - GenericExecutionContext generic; - - // ROLE TRAITS - TaskQueue taskQueue; - delta::platform::SemaphoreHandle sleepSemaphore; - std::atomic isAsleep; - std::atomic shouldClose; - }; - - template - concept ExecutionContext = - std::is_same_v, GenericExecutionContext> || - std::is_same_v, MainExecutionContext> || - std::is_same_v, WorkerExecutionContext>; - - // COMPILE TIME CONSTANTS - inline constexpr size_t THREAD_EXECUTION_CONTEXT_STRIDE = std::max(sizeof(MainExecutionContext), sizeof(WorkerExecutionContext)); - inline constexpr size_t THREAD_EXECUTION_CONTEXT_SIZE = (THREAD_EXECUTION_CONTEXT_STRIDE + 63) & ~(63); - - // EXTERN VARIABLES & FUNCTIONS - extern uintptr_t g_MasterSlabStart; - extern uintptr_t g_MasterSlabEnd; + inline constexpr size_t THREADCTX_ALIGNMENT = 64ull; + + #define DLT_CORE_PAGECRD_FIELDS(Layout) \ + DLT_DEFAULT_FIELD_DEF(Layout, uintptr_t, base) \ + DLT_DEFAULT_FIELD_DEF(Layout, size_t, size) + + #define DLT_CORE_THREADARENA_FIELDS(Layout) \ + DLT_DEFAULT_FIELD_DEF(Layout, uintptr_t, base) \ + DLT_DEFAULT_FIELD_DEF(Layout, size_t, offset) \ + DLT_DEFAULT_FIELD_DEF(Layout, size_t, reserved) \ + DLT_DEFAULT_FIELD_DEF(Layout, size_t, size) + + #define DLT_CORE_MASTERTHREADCTX_FIELDS(Layout) \ + DLT_DEFAULT_FIELD_DEF(Layout, uint32_t, threadId) \ + \ + DLT_RESOURCE_GROUP_OPEN(Layout, pagecrd) \ + DLT_CORE_PAGECRD_FIELDS(Layout) \ + DLT_RESOURCE_GROUP_CLOSE(Layout, pagecrd) \ + \ + DLT_RESOURCE_GROUP_OPEN(Layout, transientStorage) \ + DLT_CORE_THREADARENA_FIELDS(Layout) \ + DLT_RESOURCE_GROUP_CLOSE(Layout, transientStorage) \ + \ + DLT_RESOURCE_GROUP_OPEN(Layout, persistentStorage) \ + DLT_CORE_THREADARENA_FIELDS(Layout) \ + DLT_RESOURCE_GROUP_CLOSE(Layout, persistentStorage) + + #define DLT_CORE_WORKERTHREADCTX_FIELDS(Layout) \ + DLT_DEFAULT_FIELD_DEF(Layout, uint32_t, threadId) \ + \ + DLT_RESOURCE_GROUP_OPEN(Layout, pagecrd) \ + DLT_CORE_PAGECRD_FIELDS(Layout) \ + DLT_RESOURCE_GROUP_CLOSE(Layout, pagecrd) \ + \ + DLT_RESOURCE_GROUP_OPEN(Layout, transientStorage) \ + DLT_CORE_THREADARENA_FIELDS(Layout) \ + DLT_RESOURCE_GROUP_CLOSE(Layout, transientStorage) + + DLT_GEN_STRUCT_REPRESENTATIONS(PageCoordinator, DLT_CORE_PAGECRD_FIELDS) + DLT_GEN_STRUCT_REPRESENTATIONS(ThreadArena, DLT_CORE_THREADARENA_FIELDS) + DLT_GEN_ALIGNED_STRUCT_REPRESENTATIONS(MasterThreadContext, THREADCTX_ALIGNMENT, DLT_CORE_MASTERTHREADCTX_FIELDS) + DLT_GEN_ALIGNED_STRUCT_REPRESENTATIONS(WorkerThreadContext, THREADCTX_ALIGNMENT, DLT_CORE_WORKERTHREADCTX_FIELDS) } diff --git a/Engine/src/delta/core/MemoryConfig.h b/Engine/src/delta/core/MemoryConfig.h index 87ad05b..3c79f6e 100644 --- a/Engine/src/delta/core/MemoryConfig.h +++ b/Engine/src/delta/core/MemoryConfig.h @@ -41,45 +41,32 @@ namespace delta::core size_t baseline; }; - // Generic inline constexpr size_t VIRT_ZONE_SPACE_LENGTH = (1ull << 35); - inline constexpr PoolDescriptor generic_transientArena(0ull, (1ull << 30), (1ull << 26)); - inline constexpr PoolDescriptor generic_eventBuffer(generic_transientArena, (1ull << 27), (1ull << 17)); + // Generic + inline constexpr PoolDescriptor generic_transientStorage(0ull, (1ull << 30), (1ull << 26)); // Index Registry - inline constexpr uint32_t VIRT_ZONE_GENERIC_TA_INDEX = 0u; - inline constexpr uint32_t VIRT_ZONE_GENERIC_EB_INDEX = 1u; + inline constexpr uint32_t VIRT_ZONE_GENERIC_TS_INDEX = 0u; - // -- MAIN -- - inline constexpr uint32_t VIRT_ZONE_MAIN_PS_INDEX = 2u; + // -- MASTER -- + inline constexpr uint32_t VIRT_ZONE_MASTER_PS_INDEX = 1u; // -- WORKER -- - inline constexpr uint32_t VIRT_ZONE_WORKER_TQ_INDEX = 2u; - inline constexpr uint32_t VIRT_ZONE_WORKER_CP_INDEX = 3u; - inline constexpr uint32_t VIRT_ZONE_WORKER_IO_INDEX = 4u; + // empty for now - inline constexpr auto VIRT_MAP_MAIN = []() consteval { - auto permaStorage = PoolDescriptor(generic_eventBuffer, (1ull << 31), (1ull << 26)); + inline constexpr auto VIRT_MAP_MASTER = []() consteval { + auto permaStorage = PoolDescriptor(generic_transientStorage, (1ull << 31), (1ull << 26)); return delta::utils::StaticArray{ - generic_transientArena, - generic_eventBuffer, + generic_transientStorage, permaStorage }; }(); inline constexpr auto VIRT_MAP_WORKER = []() consteval { - auto taskQueue = PoolDescriptor(generic_eventBuffer, (1ull << 16)); - auto componentPool = PoolDescriptor(taskQueue, (1ull << 33), (1ull << 27)); - auto io = PoolDescriptor(componentPool, (1ull << 30)); - return delta::utils::StaticArray{ - generic_transientArena, - generic_eventBuffer, - taskQueue, - componentPool, - io + generic_transientStorage }; }(); @@ -95,7 +82,7 @@ namespace delta::core return sum; } - inline constexpr auto VIRT_MAIN_BASELINE = total_baseline(VIRT_MAP_MAIN); + inline constexpr auto VIRT_MASTER_BASELINE = total_baseline(VIRT_MAP_MASTER); inline constexpr auto VIRT_WORKER_BASELINE = total_baseline(VIRT_MAP_WORKER); } diff --git a/Engine/src/delta/core/ThreadContext.cpp b/Engine/src/delta/core/ThreadContext.cpp deleted file mode 100644 index 159896a..0000000 --- a/Engine/src/delta/core/ThreadContext.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright 2026 Jakub Bączyk - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "ThreadContext.h" -#include "MemoryConfig.h" - -#define ALIGN(size, alignment) ((size + (alignment - 1)) & ~(alignment - 1)) - -namespace delta::core -{ - using PoolDescriptor = MemoryMap::PoolDescriptor; - - uintptr_t g_MasterSlabStart = 0; - uintptr_t g_MasterSlabEnd = 0; - - static uint32_t g_ThreadCount = 0; - static uint32_t g_WorkerCount = 0; - static GenericExecutionContext* g_ThreadContexts = nullptr; - static thread_local GenericExecutionContext* tl_CurrentThreadContext = nullptr; - - DLT_FORCE_INLINE static void InitializePageCoordinator(ThreadPageCoordinator& pageCoord, size_t pageSize, uint8_t* baseAddress) - { - pageCoord.pageSize = pageSize; - pageCoord.virtualAddressBase = baseAddress; - pageCoord.commitedOffset = 0; - pageCoord.reservedCapacity = MemoryMap::VIRT_ZONE_SPACE_LENGTH; - } - - DLT_FORCE_INLINE static void InitializeQueue( - const ThreadPageCoordinator& pageCoord, - TaskQueue& queue, - DependencyCounter* depCounter, - const PoolDescriptor& pd) - { - queue.size = pd.size / TaskQueue::FIELD_SIZE; - queue.mask = queue.size - 1; - queue.top.store(0, std::memory_order_relaxed); - queue.bottom.store(0, std::memory_order_relaxed); - - // a queue is commited in whole - uint8_t* pTarget = pageCoord.virtualAddressBase + pd.offset; - void* p = delta::platform::Memory_Commit(pTarget, pd.size); - if (!delta::platform::Memory_Lock(pTarget, pd.size)) - std::cout << "[DeltaEngine-Warning] Failed to lock task queue memory. You may experience stuttering.\n"; - assert(p != nullptr); - - uint8_t* tasksArrayPtr = pTarget; - uint8_t* payloadsArrayPtr = pTarget + queue.size; - queue.tasks = reinterpret_cast(tasksArrayPtr); - queue.payloads = reinterpret_cast(payloadsArrayPtr); - } - - DLT_FORCE_INLINE static void InitializeArena( - const ThreadPageCoordinator& pageCoord, - ThreadArena& arena, - const PoolDescriptor& pd) - { - uint8_t* pTarget = pageCoord.virtualAddressBase + pd.offset; - void* res = delta::platform::Memory_Commit(pTarget, pd.baseline); - assert(res != nullptr); - - if (!delta::platform::Memory_Lock(pTarget, pd.baseline)) - std::cout << "[DeltaEngine-Warning] Failed to lock arena memory. You may experience stuttering.\n"; - - arena.backingMemory = pTarget; - arena.capacity = pd.baseline; - arena.offset = 0; - arena.maxCapacity = pd.size; - } - - template - DLT_FORCE_INLINE ContextType& GetExecutionContext(size_t i) - { - return reinterpret_cast(*(reinterpret_cast(g_ThreadContexts) + i * THREAD_EXECUTION_CONTEXT_SIZE)); - } - - void ThreadContext_Initialize(uint32_t threadCount, size_t pageSize) - { - g_ThreadCount = threadCount; - g_WorkerCount = threadCount - 1; - - // assign 32Gb of address space per thread - // master pool size is rounded up to page size - constexpr size_t ADDR_SLICE_PER_THREAD = (1ull << 35); - size_t contextArraySize = THREAD_EXECUTION_CONTEXT_SIZE * threadCount; - size_t alignedContextArraySize = ALIGN(contextArraySize, pageSize); - size_t masterPoolSize = (ADDR_SLICE_PER_THREAD * threadCount) + alignedContextArraySize; - - uint8_t* masterPoolBase = reinterpret_cast( - delta::platform::Memory_Reserve(masterPoolSize) - ); - assert(masterPoolBase != nullptr && "Failed to reserve master pool"); - - g_MasterSlabStart = reinterpret_cast(masterPoolBase); - g_MasterSlabEnd = g_MasterSlabStart + masterPoolSize; - - g_ThreadContexts = reinterpret_cast( - delta::platform::Memory_Commit(masterPoolBase, alignedContextArraySize) - ); - assert(g_ThreadContexts != nullptr && "Failed to commit thread context array"); - - if (!delta::platform::Memory_Lock(masterPoolBase, alignedContextArraySize)) - std::cout << "[DeltaEngine-Warning] Failed to lock memory resource: Master Thread Context Pool\n"; - - // pre-initialize generics - uint8_t* runwayCursor = masterPoolBase + alignedContextArraySize; - for (uint32_t i = 0; i < threadCount; i++) - { - GenericExecutionContext& ctx = GetExecutionContext(i); - delta::platform::Timer_Initialize(&ctx.perThreadTimer); - InitializePageCoordinator(ctx.pageCoordinator, pageSize, runwayCursor); - InitializeArena( - ctx.pageCoordinator, - ctx.transientArena, - MemoryMap::VIRT_MAP_MAIN[MemoryMap::VIRT_ZONE_GENERIC_TA_INDEX] - ); - - runwayCursor += MemoryMap::VIRT_ZONE_SPACE_LENGTH; - } - - // Finish initializing main thread context - DependencyCounter* depCounterPtr = nullptr; - { - MainExecutionContext& ctx = GetExecutionContext(0); - ctx.generic.type = ThreadType::MAIN; - ctx.generic.threadIx = 0; - ctx.generic.threadHandle = delta::platform::Thread_GetCurrentHandle(); - ctx.depCounter.count.store(0, std::memory_order_relaxed); - depCounterPtr = &ctx.depCounter; - - InitializeArena( - ctx.generic.pageCoordinator, - ctx.persistentStorage, - MemoryMap::VIRT_MAP_MAIN[MemoryMap::VIRT_ZONE_MAIN_PS_INDEX] - ); - } - - for (uint32_t i = 1; i < threadCount; i++) - { - WorkerExecutionContext& ctx = GetExecutionContext(i); - ctx.generic.type = ThreadType::WORKER; - ctx.generic.threadIx = i; - ctx.generic.threadHandle = delta::platform::INVALID_THREAD_HANDLE; // Initialized when thread starts - ctx.isAsleep.store(false, std::memory_order_relaxed); - ctx.shouldClose.store(false, std::memory_order_relaxed); - ctx.sleepSemaphore = delta::platform::Sync_CreateSemaphore(); - - InitializeQueue( - ctx.generic.pageCoordinator, - ctx.taskQueue, - depCounterPtr, - MemoryMap::VIRT_MAP_WORKER[MemoryMap::VIRT_ZONE_WORKER_TQ_INDEX] - ); - } - - tl_CurrentThreadContext = &g_ThreadContexts[0]; - } - - void ThreadContext_Shutdown() - { - delta::platform::Memory_Release(g_ThreadContexts); - } - - GenericExecutionContext* ThreadContext_GetCurrent() noexcept - { - return tl_CurrentThreadContext; - } - - GenericExecutionContext* ThreadContext_GetForIndex(uint32_t i) noexcept - { - return &GetExecutionContext(i); - } - - void ThreadContext_SetCurrent(GenericExecutionContext* ctx) noexcept - { - tl_CurrentThreadContext = ctx; - } - - void TaskQueue_Push(TaskQueue* queue, task_t task, payload_t payload) - { - queue_index_t b = queue->bottom.load(std::memory_order_relaxed); - queue_index_t t = queue->top.load(std::memory_order_acquire); - - if (b - t > queue->size) - { - // execute immadietely if the queue is full - task(payload); - return; - } - - queue_index_t ix = b & queue->mask; - queue->tasks[ix] = task; - queue->payloads[ix] = payload; - - std::atomic_thread_fence(std::memory_order_release); - queue->bottom.store(b + 1, std::memory_order_relaxed); - } - - bool TaskQueue_Pop(TaskQueue* queue, task_t* outTask, payload_t* outPayload) - { - queue_index_t b = queue->bottom.load(std::memory_order_relaxed) - 1; - queue->bottom.store(b, std::memory_order_relaxed); - - std::atomic_thread_fence(std::memory_order_seq_cst); - queue_index_t t = queue->top.load(std::memory_order_relaxed); - - if (t > b) - { - queue->bottom.store(b + 1, std::memory_order_relaxed); - return false; - } - - queue_index_t ix = b & queue->mask; - *outTask = queue->tasks[ix]; - *outPayload = queue->payloads[ix]; - - if (t == b) - { - queue_index_t expectedTop = t; - if (!queue->top.compare_exchange_strong(expectedTop, expectedTop + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) - { - queue->bottom.store(b + 1, std::memory_order_relaxed); - return false; - } - - queue->bottom.store(b + 1, std::memory_order_relaxed); - } - - return true; - } - - bool TaskQueue_Steal(TaskQueue* queue, task_t* outTask, payload_t* outPayload) - { - queue_index_t t = queue->top.load(std::memory_order_acquire); - - std::atomic_thread_fence(std::memory_order_seq_cst); - queue_index_t b = queue->bottom.load(std::memory_order_acquire); - - if (t >= b) - return false; - - queue_index_t ix = t & queue->mask; - *outTask = queue->tasks[ix]; - *outPayload = queue->payloads[ix]; - - if (!queue->top.compare_exchange_strong(t, t + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) - return false; - - return true; - } - - void Scheduler_ProcessTaskBatch(task_t* tasks, payload_t* payloads, size_t length) - { - assert(IsMainThread()); // CAN BE EXECUTED ONLY ON MAIN THREAD! - GetMainContext().depCounter.count.store(length, std::memory_order_relaxed); - - for (size_t i = 0; i < length; i++) - { - size_t targetIx = 1 + (i % g_WorkerCount); - WorkerExecutionContext& ctx = GetExecutionContext(targetIx); - TaskQueue_Push(&ctx.taskQueue, tasks[i], payloads[i]); - - if (ctx.isAsleep.load(std::memory_order_acquire)) - delta::platform::Sync_SignalSemaphore(ctx.sleepSemaphore); - } - } - - void Scheduler_Sync() - { - assert(tl_CurrentThreadContext->threadIx == 0); // CAN BE EXECUTED ONLY ON MAIN THREAD! - - uint32_t workerIx = 1; - uint32_t consecutiveEmptyQueues = 0; - - while (consecutiveEmptyQueues < g_ThreadCount) - { - WorkerExecutionContext& ctx = GetExecutionContext(workerIx); - task_t task = nullptr; - payload_t payload = nullptr; - - if (TaskQueue_Steal(&ctx.taskQueue, &task, &payload)) - { - consecutiveEmptyQueues = 0; - assert(task && payload); - - size_t snapshot = tl_CurrentThreadContext->transientArena.offset; - task(payload); - tl_CurrentThreadContext->transientArena.offset = snapshot; - } - else - { - consecutiveEmptyQueues++; - } - - workerIx = 1 + (workerIx % g_WorkerCount); - } - } - - ThreadArena* GetTransientArena() noexcept - { - auto* ctx = tl_CurrentThreadContext; - assert(ctx); - return &ctx->transientArena; - } - - void* ThreadArena_Allocate(ThreadArena* arena, size_t size, size_t alignment) - { - uintptr_t currentAddress = reinterpret_cast(arena->backingMemory) + arena->offset; - uintptr_t alignedAddress = ALIGN(currentAddress, alignment); - size_t padding = alignedAddress - currentAddress; - size_t totalSpace = padding + size; - - if (arena->offset + totalSpace > arena->maxCapacity) - { - // TODO: Handle it better. I don't know how yet, but I will know soon. - assert(false); // arena overflown - } - - if (totalSpace > arena->capacity) - { - // the slow path: assign more pages - // assigning twice 2 up front to avoid further slower-path-taking - size_t bytesNeeded = totalSpace - arena->capacity; - size_t spaceInPages = ALIGN(bytesNeeded, tl_CurrentThreadContext->pageCoordinator.pageSize) * 2; - uint8_t* targetAddress = arena->backingMemory + arena->capacity; - void* p = delta::platform::Memory_Commit(targetAddress, spaceInPages); - if (p && delta::platform::Memory_Lock(p, spaceInPages)) - { - return nullptr; - } - - arena->capacity += spaceInPages; - } - - uint8_t* ptr = reinterpret_cast(alignedAddress); - arena->offset += totalSpace; - - return ptr; - } - - void ThreadArena_Reset(ThreadArena* arena) - { - arena->offset = 0; - } -} diff --git a/Engine/src/delta/core/ThreadContext.h b/Engine/src/delta/core/ThreadContext.h deleted file mode 100644 index a4aa6a9..0000000 --- a/Engine/src/delta/core/ThreadContext.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2026 Jakub Bączyk - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "EngineTypes.h" - -namespace delta::core -{ - // Lifecycle - void ThreadContext_Initialize(uint32_t workerCount, size_t pageSize); - void ThreadContext_Shutdown(); - - // Getters - GenericExecutionContext* ThreadContext_GetCurrent() noexcept; - GenericExecutionContext* ThreadContext_GetForIndex(uint32_t ix) noexcept; - void ThreadContext_SetCurrent(GenericExecutionContext* ctx) noexcept; - - // Thread Task Queue API - void TaskQueue_Push(TaskQueue* queue, task_t task, payload_t payload); - bool TaskQueue_Pop(TaskQueue* queue, task_t* outTask, payload_t* outPayload); - bool TaskQueue_Steal(TaskQueue* queue, task_t* outTask, payload_t* outPayload); - - // Scheduler API - void Scheduler_ProcessTaskBatch(task_t* tasks, payload_t* payloads, size_t length); - void Scheduler_Sync(); - - // Engine Arena API - ThreadArena* GetTransientArena() noexcept; - void* ThreadArena_Allocate(ThreadArena* arena, size_t size, size_t alignment = 8); - void ThreadArena_Reset(ThreadArena* arena); - void ThreadArena_Reset(ThreadArena* arena); - - // Inline Helpers - template - [[deprecated]] [[nodiscard]] inline TargetType* ThreadContextCast(GenericExecutionContext* ctx) - { - if (!ctx) return nullptr; - - if constexpr (std::is_same_v) - { - if (ctx->type == ThreadType::MAIN) - return reinterpret_cast(ctx); - } - else if constexpr (std::is_same_v) - { - if (ctx->type == ThreadType::WORKER) - return reinterpret_cast(ctx); - } - - assert(false); // CRITICAL: Casting to invalid type - return nullptr; - } - - inline bool IsCustomAllocated(void* ptr) - { - // range scan - return g_MasterSlabStart <= reinterpret_cast(ptr) && reinterpret_cast(ptr) <= g_MasterSlabEnd; - } - - inline bool IsMainThread() - { - auto* ctx = ThreadContext_GetCurrent(); - return ctx && ctx->type == ThreadType::MAIN; - } - - inline bool IsWorkerThread() - { - auto* ctx = ThreadContext_GetCurrent(); - return ctx && ctx->type == ThreadType::WORKER; - } - - DLT_DISABLE_WARNING_PUSH - DLT_DISABLE_MISSING_RETURN - inline MainExecutionContext& GetMainContext() - { - auto* ctx = ThreadContext_GetCurrent(); - if (ctx->type == ThreadType::MAIN) - return reinterpret_cast(*ctx); - - assert(false); // this shouldn't ever happen - DLT_UNREACHABLE; - } - - inline WorkerExecutionContext& GetWorkerContext() - { - auto* ctx = ThreadContext_GetCurrent(); - if (ctx->type == ThreadType::WORKER) - return reinterpret_cast(*ctx); - - assert(false); - DLT_UNREACHABLE; - } - DLT_DISABLE_WARNING_POP -} diff --git a/Engine/src/delta/core/ThreadContextManager.cpp b/Engine/src/delta/core/ThreadContextManager.cpp new file mode 100644 index 0000000..2c0f81e --- /dev/null +++ b/Engine/src/delta/core/ThreadContextManager.cpp @@ -0,0 +1,130 @@ +#include "ThreadContextManager.h" +#include "MemoryConfig.h" + +#include +#include + +namespace delta::core +{ + static MasterThreadContextAoS g_MasterThreadCtx; + static WorkerThreadContextSoA g_WorkerThreadCtx; + + static DLT_FORCE_INLINE bool InitializeContextArray(tca_cursor_t& cursor, size_t ctxArraySize, uint32_t workers) + { + void* p = delta::platform::Memory_Commit(cursor, ctxArraySize); + if (!p) + return false; + + if (!delta::platform::Memory_Lock(cursor, ctxArraySize)) + return false; + + // set array pointers + g_WorkerThreadCtx.threadId = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::threadId); + + g_WorkerThreadCtx.pagecrd.base = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::pagecrd.base); + + g_WorkerThreadCtx.pagecrd.size = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::pagecrd.size); + + g_WorkerThreadCtx.transientStorage.base = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::transientStorage.base); + + g_WorkerThreadCtx.transientStorage.offset = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::transientStorage.offset); + + g_WorkerThreadCtx.transientStorage.reserved = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::transientStorage.reserved); + + g_WorkerThreadCtx.transientStorage.size = cursor; + cursor += workers * sizeof(WorkerThreadContextAoS::transientStorage.size); + + return true; + } + + static DLT_FORCE_INLINE bool InitializeWorker(uintptr_t& cursor, uint32_t ix) + { + constexpr auto WORKER_TS_DESC = MemoryMap::VIRT_MAP_WORKER[MemoryMap::VIRT_ZONE_GENERIC_TS_INDEX]; + + g_WorkerThreadCtx.threadId[ix] = 0xBADBEEFu; + + g_WorkerThreadCtx.pagecrd.base[ix] = cursor; + g_WorkerThreadCtx.pagecrd.size[ix] = MemoryMap::VIRT_ZONE_SPACE_LENGTH; + + auto& tsBase = g_WorkerThreadCtx.transientStorage.base[ix]; + tsBase = cursor + WORKER_TS_DESC.offset; + g_WorkerThreadCtx.transientStorage.offset[ix] = 0ull; + g_WorkerThreadCtx.transientStorage.reserved[ix] = WORKER_TS_DESC.baseline; + g_WorkerThreadCtx.transientStorage.size[ix] = WORKER_TS_DESC.size; + + void* p = delta::platform::Memory_Commit(reinterpret_cast(tsBase), WORKER_TS_DESC.baseline); + if (!p) + return false; + + if (!delta::platform::Memory_Lock(reinterpret_cast(tsBase), WORKER_TS_DESC.baseline)) + return false; + + cursor += MemoryMap::VIRT_ZONE_SPACE_LENGTH; + return true; +; } + + void ThreadContextManager_Initialize(uint32_t totalThreads, uint32_t pageSize) + { + const uint32_t workers = totalThreads - 1; + const size_t addrSpaceSize = delta::utils::align_up(totalThreads * MemoryMap::VIRT_ZONE_SPACE_LENGTH, pageSize); + const size_t ctxArraySize = delta::utils::align_up(workers * sizeof(WorkerThreadContextAoS), pageSize); + const size_t reservationSize = ctxArraySize + addrSpaceSize; + + const void* begin = delta::platform::Memory_Reserve(reservationSize); + const uintptr_t ctxArray = reinterpret_cast(begin); + const uintptr_t addrSpace = ctxArray + ctxArraySize; + + { + tca_cursor_t cursor(ctxArray); + if (!InitializeContextArray(cursor, ctxArraySize, workers)) + { + std::cout << "[DeltaEngine-Critical] Failed to setup thread context array\n"; + std::abort(); + } + + assert(cursor.cast() - ctxArray <= ctxArraySize); + } + + // initialize contexts (starting from master) + uintptr_t cursor = addrSpace; + + constexpr auto MASTER_TS = MemoryMap::VIRT_MAP_MASTER[MemoryMap::VIRT_ZONE_GENERIC_TS_INDEX]; + constexpr auto MASTER_PS = MemoryMap::VIRT_MAP_MASTER[MemoryMap::VIRT_ZONE_MASTER_PS_INDEX]; + g_MasterThreadCtx.threadId = delta::platform::Thread_GetCurrentId(); + g_MasterThreadCtx.pagecrd.base = cursor; + g_MasterThreadCtx.pagecrd.size = MemoryMap::VIRT_ZONE_SPACE_LENGTH; + + g_MasterThreadCtx.transientStorage.base = cursor + MASTER_TS.offset; + g_MasterThreadCtx.transientStorage.offset = 0ull; + g_MasterThreadCtx.transientStorage.reserved = MASTER_TS.baseline; + g_MasterThreadCtx.transientStorage.size = MASTER_TS.size; + + g_MasterThreadCtx.persistentStorage.base = cursor + MASTER_PS.offset; + g_MasterThreadCtx.persistentStorage.offset = 0ull; + g_MasterThreadCtx.persistentStorage.reserved = MASTER_PS.baseline; + g_MasterThreadCtx.persistentStorage.size = MASTER_PS.size; + cursor += MemoryMap::VIRT_ZONE_SPACE_LENGTH; + + // now workers + constexpr auto WORKER_TS = MemoryMap::VIRT_MAP_WORKER[MemoryMap::VIRT_ZONE_GENERIC_TS_INDEX]; + for (uint32_t i = 0; i < workers; i++) + { + if (!InitializeWorker(cursor, i)) + { + std::cout << "[DeltaEngine-Critical] Failed to initialize worker " << i << "!\n"; + std::abort(); + } + } + } + + void ThreadContextManager_Shutdown() + { + + } +} diff --git a/Engine/src/delta/core/ThreadContextManager.h b/Engine/src/delta/core/ThreadContextManager.h new file mode 100644 index 0000000..36ddd97 --- /dev/null +++ b/Engine/src/delta/core/ThreadContextManager.h @@ -0,0 +1,42 @@ +#pragma once + +#include "EngineTypes.h" + +namespace delta::core +{ + template + struct Cursor + { + Cursor() = delete; + + inline Cursor(uintptr_t _ptr) + : ptr(_ptr) + {} + + uintptr_t ptr; + + template + inline operator T* () const noexcept + { + return reinterpret_cast(ptr); + } + + template + inline T cast() noexcept + { + return reinterpret_cast(ptr); + } + + template + inline Cursor& operator+=(const T& rhs) noexcept + { + ptr = delta::utils::align_up(ptr + rhs, Alignment); + return *this; + } + }; + + using tca_cursor_t = Cursor; + + void ThreadContextManager_Initialize(uint32_t totalThreads, uint32_t pageSize); + void ThreadContextManager_Shutdown(); +} diff --git a/Engine/src/delta/core/Worker.cpp b/Engine/src/delta/core/Worker.cpp deleted file mode 100644 index 7cded61..0000000 --- a/Engine/src/delta/core/Worker.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2026 Jakub Bączyk - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Worker.h" -#include -#include - -namespace delta::core -{ - using ThreadHandle = delta::platform::ThreadHandle; - using ThreadCreateInfo = delta::platform::ThreadCreateInfo; - - using SemaphoreHandle = delta::platform::SemaphoreHandle; - - static uint32_t s_WorkerCount; - static ThreadHandle* s_Threads; - - static void WorkerProc(void* args) - { - WorkerExecutionContext* ctx = reinterpret_cast(args); - ThreadContext_SetCurrent((GenericExecutionContext*)ctx); - - task_t task; - payload_t payload; - DependencyCounter& depCounter = reinterpret_cast(*ctx->taskQueue.depCounterPtr); - while (!ctx->shouldClose.load(std::memory_order_acquire)) - { - // TODO: Figure out how to select a worker to steal some work from. - // I'll probably utilize modulo n cyclic groups. (algebra - group theory) - - if (TaskQueue_Pop(&ctx->taskQueue, &task, &payload)) - { - task(payload); - depCounter.count.fetch_sub(1, std::memory_order_release); - continue; - } - - ThreadArena_Reset(GetTransientArena()); - ctx->isAsleep.store(true, std::memory_order_release); - if (!ctx->shouldClose.load(std::memory_order_acquire)) - { - delta::platform::Sync_WaitSemaphore(ctx->sleepSemaphore); - } - - ctx->isAsleep.store(false, std::memory_order_release); - } - } - - void Worker_Init(uint32_t count) - { - s_WorkerCount = count; - - // TODO: Take a look at native thread api and figure out how this could be done better - // TODO: Set thread affinity mask - s_Threads = new(delta::Engine::AllocationType::PERSISTENT) ThreadHandle[count]; - ThreadCreateInfo* cs = new(delta::Engine::AllocationType::TRANSIENT) ThreadCreateInfo[count]; - - for (uint32_t i = 0; i < count; i++) - { - // looping through thread indices, workers start from ix=1 - ThreadCreateInfo& info = cs[i]; - ThreadHandle& handle = s_Threads[i]; - auto* ctx = ThreadContext_GetForIndex(i + 1); - info.fn = WorkerProc; - info.args = (void*)ctx; - handle = delta::platform::Thread_Create(&info); - ctx->threadHandle = handle; - delta::platform::Thread_AssignPhysicalCore(handle, i + 1); - delta::platform::Thread_SetName(handle, "Worker"); - } - } - - void Worker_Shutdown() - { - delta::platform::Thread_JoinMultiple(s_Threads, s_WorkerCount); - } -} diff --git a/Engine/src/delta/core/Worker.h b/Engine/src/delta/core/Worker.h deleted file mode 100644 index 6ec2a8b..0000000 --- a/Engine/src/delta/core/Worker.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2026 Jakub Bączyk - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace delta::core -{ - void Worker_Init(uint32_t count); - void Worker_Shutdown(); -} diff --git a/Engine/src/delta/core/aos_helper.h b/Engine/src/delta/core/aos_helper.h new file mode 100644 index 0000000..f5f980d --- /dev/null +++ b/Engine/src/delta/core/aos_helper.h @@ -0,0 +1,30 @@ +#pragma once + +#define DLT_INT_AOS_SCALAR(type) type +#define DLT_INT_SOA_POINTER(type) type* + +#define DLT_DEFAULT_FIELD_DEF(Layout, type, name) Layout(type, name, DLT_INT_AOS_SCALAR, DLT_INT_SOA_POINTER) + +#define DLT_PARSE_AOS(type, name, aos_meta, soa_meta) aos_meta(type) name; +#define DLT_PARSE_SOA(type, name, aos_meta, soa_meta) soa_meta(type) name; + +#define DLT_RESOURCE_GROUP_OPEN(Layout, name) struct { +#define DLT_RESOURCE_GROUP_CLOSE(Layout, name) } name; + +#define DLT_GEN_STRUCT_REPRESENTATIONS(StructName, FieldList) \ + struct StructName##AoS { \ + FieldList(DLT_PARSE_AOS) \ + }; \ + \ + struct StructName##SoA { \ + FieldList(DLT_PARSE_SOA) \ + }; + +#define DLT_GEN_ALIGNED_STRUCT_REPRESENTATIONS(StructName, Alignment, FieldList) \ + struct alignas(Alignment) StructName##AoS { \ + FieldList(DLT_PARSE_AOS) \ + }; \ + \ + struct StructName##SoA { \ + FieldList(DLT_PARSE_SOA) \ + }; diff --git a/Engine/src/delta/core/engine.cpp b/Engine/src/delta/core/engine.cpp index bfdcdc7..5df53e8 100644 --- a/Engine/src/delta/core/engine.cpp +++ b/Engine/src/delta/core/engine.cpp @@ -21,10 +21,9 @@ #include "EngineTypes.h" #include "MemoryConfig.h" -#include "ThreadContext.h" -#include "Worker.h" +#include "ThreadContextManager.h" -namespace delta::Engine +namespace delta::core { using GenericExecutionContext = delta::core::GenericExecutionContext; @@ -38,25 +37,25 @@ namespace delta::Engine uint32_t totalThreads = osInfo->cpuPhysicalCoreCount; uint32_t pageSize = osInfo->osPageSize; delta::core::MemoryConfig_Initialize(memStatus.physicalInstalled, pageSize, totalThreads); - delta::core::ThreadContext_Initialize(totalThreads, pageSize); + delta::core::ThreadContextManager_Initialize(totalThreads); - delta::platform::ThreadHandle th = delta::platform::Thread_GetCurrentHandle(); - delta::platform::Thread_AssignPhysicalCore(th, 0); - delta::core::Worker_Init(totalThreads-1); + context.window = delta::platform::Window_Create(); + delta::platform::Window_Show(context.window); + delta::platform::Window_Win32_Update(context.window); } void Update(Context& context) { // blah blah blah // do something - delta::platform::Sync_Sleep(100); - delta::core::ThreadArena_Reset(delta::core::GetTransientArena()); + + delta::platform::Window_ProcessEvents(); + delta::platform::Sync_Sleep(10); } void Shutdown(Context& context) { - delta::core::Worker_Shutdown(); - delta::core::ThreadContext_Shutdown(); + delta::core::Free(context.window); delta::core::MemoryConfig_Shutdown(); } diff --git a/Engine/src/delta/platform/os_internal.h b/Engine/src/delta/platform/os_internal.h index 366dad2..1bd4976 100644 --- a/Engine/src/delta/platform/os_internal.h +++ b/Engine/src/delta/platform/os_internal.h @@ -15,6 +15,7 @@ */ #pragma once +#include namespace delta::platform { @@ -31,28 +32,12 @@ namespace delta::platform bool Memory_ElevateLockLimit(size_t maxBytesToLock); // Timer API - struct Timer_Internal; - struct Timer - { - alignas(8) uint8_t opaqueData[32]; - }; - void Timer_Initialize(Timer* timer); int64_t Timer_GetTimestamp(); double Timer_TicksToMilliseconds(const Timer* timer, int64_t startTicks, int64_t endTicks); double Timer_TicksToMicroseconds(const Timer* timer, int64_t startTicks, int64_t endTicks); // Thread API - struct Thread; - using ThreadHandle = Thread*; - inline constexpr ThreadHandle INVALID_THREAD_HANDLE = nullptr; // random number 696767 - - struct ThreadCreateInfo - { - void (*fn)(void*); - void* args; - }; - uint32_t Thread_GetCurrentId(); uint32_t Thread_GetId(ThreadHandle thread); ThreadHandle Thread_GetCurrentHandle(); @@ -63,15 +48,18 @@ namespace delta::platform void Thread_JoinMultiple(ThreadHandle* threads, uint32_t count); // Sync API - struct Semaphore; - using SemaphoreHandle = Semaphore*; - SemaphoreHandle Sync_CreateSemaphore(); void Sync_DestroySemaphore(SemaphoreHandle sem); void Sync_SignalSemaphore(SemaphoreHandle sem); void Sync_WaitSemaphore(SemaphoreHandle sem); void Sync_Sleep(uint32_t milliseconds); + // Window API + WindowHandle Window_Create(); + void Window_Win32_Update(WindowHandle window); + void Window_ProcessEvents(); + void Window_Destroy(WindowHandle window); + // Misc uint32_t Misc_GetLastError() noexcept; } diff --git a/Engine/src/delta/platform/os_internal_types.h b/Engine/src/delta/platform/os_internal_types.h new file mode 100644 index 0000000..864a8ef --- /dev/null +++ b/Engine/src/delta/platform/os_internal_types.h @@ -0,0 +1,27 @@ +#pragma once +#include + +namespace delta::platform +{ + // Timer API + struct Timer_Internal; + struct Timer + { + alignas(8) uint8_t opaqueData[32]; + }; + + // Thread API + DLT_DEFINE_HANDLE(Thread); + + struct ThreadCreateInfo + { + void (*fn)(void*); + void* args; + }; + + // Sync API + DLT_DEFINE_HANDLE(Semaphore); + + // Window API + // Window defined in the public header +} diff --git a/Engine/src/delta/platform/os_win32.cpp b/Engine/src/delta/platform/os_win32.cpp index dc1010d..99cd3a7 100644 --- a/Engine/src/delta/platform/os_win32.cpp +++ b/Engine/src/delta/platform/os_win32.cpp @@ -25,6 +25,9 @@ #define CHECK_CPUID_FLAG(register, flag) ((register & (1 << flag)) != 0) +_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +_declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; + namespace delta::platform { static OSInfo g_osInfo; @@ -152,11 +155,18 @@ namespace delta::platform return true; } + // TODO: Move this variables to the reasonable place + static HINSTANCE s_hInstance = nullptr; + static STARTUPINFOA s_StartupInfo = { sizeof(STARTUPINFOA) }; + void Initialize() { memset(&g_osInfo, 0u, sizeof(OSInfo)); fetchCpuidValues(); + s_hInstance = GetModuleHandleA(nullptr); + GetStartupInfoA(&s_StartupInfo); + SYSTEM_INFO info; GetSystemInfo(&info); @@ -326,7 +336,7 @@ namespace delta::platform ThreadHandle Thread_GetCurrentHandle() { - Thread* t = new(delta::Engine::AllocationType::PERSISTENT) Thread(); + Thread* t = new(delta::core::AllocationType::PERSISTENT) Thread(); t->hThread = GetCurrentThread(); if (!t->hThread) return nullptr; @@ -363,7 +373,7 @@ namespace delta::platform ThreadHandle Thread_Create(ThreadCreateInfo* createInfo) { - Thread* thread = new(delta::Engine::AllocationType::PERSISTENT) Thread{}; + Thread* thread = new(delta::core::AllocationType::PERSISTENT) Thread{}; thread->hThread = CreateThread(nullptr, 0, DeltaThreadProc, (void*)createInfo, 0, 0); if (thread->hThread == 0) return nullptr; @@ -390,7 +400,7 @@ namespace delta::platform SemaphoreHandle Sync_CreateSemaphore() { - Semaphore* s = new(delta::Engine::AllocationType::PERSISTENT) Semaphore{}; + Semaphore* s = new(delta::core::AllocationType::PERSISTENT) Semaphore{}; s->hSemaphore = CreateSemaphoreA(nullptr, 0, 1000000, nullptr); if (s->hSemaphore == nullptr) return nullptr; @@ -421,6 +431,146 @@ namespace delta::platform Sleep(milliseconds); } + // ------------------------------------------ WINDOW API ------------------------------------------ + + struct Window + { + HWND hwnd; + }; + + static constexpr const char MAIN_WND_CLASS_NAME[] = "DltWindow"; + + static LRESULT CALLBACK DltWindowProc(HWND hwnd, UINT umesg, WPARAM wparam, LPARAM lparam) + { + switch (umesg) + { + case WM_CLOSE: + // DestroyWindow(hwnd); + return 0; + default: + return DefWindowProcA(hwnd, umesg, wparam, lparam); + } + } + + WindowHandle Window_Create() + { + assert(s_hInstance); + WindowHandle window = new(delta::core::AllocationType::PERSISTENT) Window(); + + int nCmdShow = (s_StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ? s_StartupInfo.wShowWindow : SW_SHOWDEFAULT; + + const WNDCLASSEXA wc = + { + .cbSize = sizeof(WNDCLASSEXA), + .style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + .lpfnWndProc = DltWindowProc, + .cbClsExtra = 0, + .cbWndExtra = 0, + .hInstance = s_hInstance, + .hCursor = LoadCursorA(nullptr, IDC_ARROW), + .hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH), + .lpszClassName = MAIN_WND_CLASS_NAME + }; + + if (!RegisterClassExA(&wc)) + { + [[maybe_unused]] DWORD e = GetLastError(); + return nullptr; + } + + uint32_t clientWidth = 1280; + uint32_t clientHeight = 720; + + RECT wr = { 0, 0, static_cast(clientWidth), static_cast(clientHeight) }; + static constexpr DWORD windowStyle = WS_OVERLAPPEDWINDOW; + + AdjustWindowRect(&wr, windowStyle, FALSE); + + window->hwnd = CreateWindowExA( + 0, + MAIN_WND_CLASS_NAME, + "Delta Engine", + windowStyle, + CW_USEDEFAULT, CW_USEDEFAULT, + wr.right - wr.left, + wr.bottom - wr.top, + nullptr, + nullptr, + s_hInstance, + nullptr + ); + + if (!window->hwnd) + { + return nullptr; + } + + return window; + } + + void Window_Win32_Update(WindowHandle window) + { + UpdateWindow(window->hwnd); + } + + void Window_ProcessEvents() + { + MSG msg = {}; + while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + // TODO: Handle this + } + + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + } + + void Window_Destroy(WindowHandle window) + { + DestroyWindow(window->hwnd); + UnregisterClassA(MAIN_WND_CLASS_NAME, s_hInstance); + } + + void Window_SetTitle(WindowHandle window, const char* newTitle) + { + SetWindowTextA(window->hwnd, newTitle); + } + + void Window_Show(WindowHandle window) + { + ShowWindow(window->hwnd, SW_SHOW); + } + + void Window_Hide(WindowHandle window) + { + ShowWindow(window->hwnd, SW_HIDE); + } + + void Window_SetSize(WindowHandle window, uint32_t w, uint32_t h) + { + static constexpr UINT FLAGS = SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW; + SetWindowPos(window->hwnd, nullptr, 0, 0, w, h, FLAGS); + } + + void Window_SetPos(WindowHandle window, uint32_t x, uint32_t y) + { + static constexpr UINT FLAGS = SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW; + SetWindowPos(window->hwnd, nullptr, x, y, 0, 0, FLAGS); + } + + void Window_ShowCursor(WindowHandle window) + { + ShowCursor(TRUE); + } + + void Window_HideCursor(WindowHandle window) + { + ShowCursor(FALSE); + } + uint32_t Misc_GetLastError() noexcept { return static_cast(GetLastError()); diff --git a/Examples/HelloWorldGame/game.cpp b/Examples/HelloWorldGame/game.cpp index 44bb162..39db5d1 100644 --- a/Examples/HelloWorldGame/game.cpp +++ b/Examples/HelloWorldGame/game.cpp @@ -35,33 +35,18 @@ using OSInfo = delta::platform::OSInfo; extern "C" { - void GAME_API Game_OnInit(delta::Engine::Context* context) + void GAME_API Game_OnInit(delta::core::Context* context) { const OSInfo* info = delta::platform::getOSInfo(); - - std::cout << "Initializing game\n"; - std::cout << "System Info:\n"; - std::cout << - "\tOS Page Size: " << info->osPageSize << " bytes\n" << - "\tCPU Architecture: " << info->cpuArchitecture << "\n" << - "\tCPU Manufacturer: " << info->cpuManufacturerId << "\n" << - "\tCPU Model: " << info->cpuBrandString << "\n" << - "\tCPU Physical Cores: " << info->cpuPhysicalCoreCount << "\n" << - "\tCPU Logical Cores: " << info->cpuLogicalProcessorCount << "\n" << - "\tCPU Has SMT: " << STDOUT_BOOL_FORMAT(info->cpuHasSMT) << "\n" << - "\tAVX2 Available: " << STDOUT_BOOL_FORMAT(info->cpuHasAVX2) << "\n" << - "\tAVX512 Foundation Available: " << STDOUT_BOOL_FORMAT(info->cpuHasAVX512f) << "\n" << - "\tAVX512 Conflict Detection Available: " << STDOUT_BOOL_FORMAT(info->cpuHasAVX512cd) << "\n" << - "\tAVX512 Exponential and Reciprocal Available: " << STDOUT_BOOL_FORMAT(info->cpuHasAVX512er) << "\n" << - "\tAVX512 Prefetch Available: " << STDOUT_BOOL_FORMAT(info->cpuHasAVX512pf) << "\n"; + delta::platform::Window_SetTitle(context->window, "Hello World Game"); } - void GAME_API Game_OnUpdate(delta::Engine::Context* context) + void GAME_API Game_OnUpdate(delta::core::Context* context) { } - void GAME_API Game_OnShutdown(delta::Engine::Context* context) + void GAME_API Game_OnShutdown(delta::core::Context* context) { } diff --git a/Launcher/main.cpp b/Launcher/main.cpp index d1525ae..1e38e37 100644 --- a/Launcher/main.cpp +++ b/Launcher/main.cpp @@ -40,9 +40,9 @@ namespace fs = std::filesystem; -using InitFunc = delta::Engine::GameInitFunc; -using UpdateFunc = delta::Engine::GameUpdateFunc; -using ShutdownFunc = delta::Engine::GameShutdownFunc; +using InitFunc = delta::core::GameInitFunc; +using UpdateFunc = delta::core::GameUpdateFunc; +using ShutdownFunc = delta::core::GameShutdownFunc; template inline T loadSymbol(const char* symName, DynamicLibrary lib) @@ -129,7 +129,7 @@ struct Game } }; -static delta::Engine::Context g_context; +static delta::core::Context g_context; static trigger::SignalSocket g_reloadSocket; int main(int argc, char** argv) @@ -148,7 +148,7 @@ int main(int argc, char** argv) } - delta::Engine::Initialize(g_context); + delta::core::Initialize(g_context); const char* libPath = argv[1]; Game game(libPath); @@ -172,12 +172,12 @@ int main(int argc, char** argv) continue; } - delta::Engine::Update(g_context); + delta::core::Update(g_context); game.updateFn(&g_context); } game.shutdownFn(&g_context); - delta::Engine::Shutdown(g_context); + delta::core::Shutdown(g_context); return 0; }