diff --git a/Core/Memory.hpp b/Core/Memory.hpp new file mode 100644 index 00000000..c9b2ba54 --- /dev/null +++ b/Core/Memory.hpp @@ -0,0 +1,52 @@ +typedef struct virtual_memory_arena { + // TBD: Store this header in the arena itself (required for free-lists/resizes - later)? + void* baseAddress; + size_t reservedSize; + size_t committedSize; + size_t used; + size_t allocationCount; +} memory_arena_t; + +typedef struct program_memory_state { + memory_arena_t persistentMemory; + memory_arena_t transientMemory; +} program_memory_t; + +typedef struct memory_allocation_options { + uint32 allocationType; + uint32 protectionConstraints; + size_t reservedSize; + void* startingAddress; +} allocation_options_t; + +typedef struct program_memory_requirements { + allocation_options_t persistentMemoryOptions; + allocation_options_t transientMemoryOptions; +} memory_config_t; + +INTERNAL void* SystemMemoryAllocate(memory_arena_t& arena, size_t allocationSize) { + size_t totalUsed = arena.used + allocationSize; + ASSUME(totalUsed <= arena.reservedSize, "Attempting to allocate outside the reserved set"); + + void* memoryRegionStartPointer = (uint8*)arena.baseAddress + arena.used; + arena.used = totalUsed; + arena.allocationCount++; + + return memoryRegionStartPointer; +} + +INTERNAL bool SystemMemoryCanAllocate(memory_arena_t& arena, size_t allocationSize) { + if(arena.used + allocationSize > arena.reservedSize) return false; + return true; +} + +void SystemMemoryReset(memory_arena_t& arena) { + arena.allocationCount = 0; + arena.used = 0; +} + +INTERNAL inline void SystemMemoryDebugTouch(memory_arena_t& arena, uint8* address) { + ASSUME(address >= arena.baseAddress, "Attempted to access an invalid arena offset"); + size_t offset = address - (uint8*)arena.baseAddress; + // TODO: Update last accessed time +} \ No newline at end of file diff --git a/Core/Modules.hpp b/Core/Modules.hpp new file mode 100644 index 00000000..f8cbdc7d --- /dev/null +++ b/Core/Modules.hpp @@ -0,0 +1,29 @@ +#pragma once + +typedef struct simulation_frame_inputs { + uint64 clock; + milliseconds uptime; + // TODO: Controller/keyboard/mouse inputs +} program_input_t; + +// TODO sync with GDI struct (single source of truth) +typedef struct { + int32 width; // TBD: uint32? + int32 height; + int32 bytesPerPixel; + int32 stride; + void* pixelBuffer; +} offscreen_buffer_t; + +typedef struct simulation_frame_outputs { + // TODO: Should push render commands and not actually draw into a buffer + offscreen_buffer_t canvas; + + // TODO: Audio buffer/outputs + uint32 bitrateSamplesPerSecond; + uint32 samplesArraySize; + int16 samples; +} program_output_t; + +// TBD: AdvanceSimulation +EXPORT void SimulateNextFrame(program_memory_t* memory, program_input_t* inputs, program_output_t* outputs); diff --git a/Core/Platforms/Win32.cpp b/Core/Platforms/Win32.cpp index 603996aa..a25ac6fe 100644 --- a/Core/Platforms/Win32.cpp +++ b/Core/Platforms/Win32.cpp @@ -2,16 +2,6 @@ #define TODO(msg) OutputDebugStringA(msg); -// TODO: Replace these with the actual game/application state later -typedef struct volatile_game_state { - int32 offsetX; - int32 offsetY; -} game_state_t; -GLOBAL game_state_t PLACEHOLDER_DEMO_APP = { - .offsetX = 0, - .offsetY = 0, -}; - constexpr size_t MAX_ERROR_MSG_SIZE = 512; GLOBAL TCHAR SYSTEM_ERROR_MESSAGE[MAX_ERROR_MSG_SIZE]; @@ -84,7 +74,6 @@ INTERNAL const char* ArchitectureToDebugName(WORD wProcessorArchitecture) { } #include "Win32/DebugDraw.hpp" -#include "Win32/Memory.hpp" #include "Win32/GamePad.cpp" #include "Win32/Keyboard.cpp" @@ -128,7 +117,8 @@ INTERNAL void MainWindowRedrawEverything(HWND& window) { } hardware_tick_t before = PerformanceMetricsNow(); - DebugDrawIntoFrameBuffer(GDI_BACKBUFFER, PLACEHOLDER_DEMO_APP.offsetX, PLACEHOLDER_DEMO_APP.offsetY); + // TODO move to program code (cannot access buffer/clock directly, though) + // DebugDrawIntoFrameBuffer(GDI_BACKBUFFER, 0, 0); CPU_PERFORMANCE_METRICS.worldRenderTime = PerformanceMetricsGetTimeSince(before); before = PerformanceMetricsNow(); @@ -203,7 +193,7 @@ LRESULT CALLBACK MainWindowProcessIncomingMessage(HWND window, UINT message, WPA case WM_SIZE: { MainWindowCreateFrameBuffers(window, GDI_SURFACE, GDI_BACKBUFFER); // NOTE: Updating again allows the simulation to appear more fluid (evaluate UX later) - DebugDrawUpdateBackgroundPattern(); + // DebugDrawUpdateBackgroundPattern(); MainWindowRedrawEverything(window); } break; @@ -323,10 +313,16 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, MONOTONIC_CLOCK_SPEED = ticksPerSecond.QuadPart; hardware_tick_t lastUpdateTime = PerformanceMetricsNow(); - // TODO Override via CLI arguments or something? (Can also compute based on available RAM) - constexpr size_t MAIN_MEMORY_SIZE = Megabytes(85); - constexpr size_t TRANSIENT_MEMORY_SIZE = Megabytes(1596) + Kilobytes(896); - SystemMemoryInitializeArenas(MAIN_MEMORY_SIZE, TRANSIENT_MEMORY_SIZE); + PLACEHOLDER_MEMORY_CONFIGURATION.persistentMemoryOptions = PlatformDefaultAllocationOptions(); + PLACEHOLDER_MEMORY_CONFIGURATION.transientMemoryOptions = PlatformDefaultAllocationOptions(); +#ifdef RAGLITE_PREDICTABLE_MEMORY + PLACEHOLDER_MEMORY_CONFIGURATION.persistentMemoryOptions.startingAddress = (LPVOID)HIGHEST_VIRTUAL_ADDRESS; +#endif + PLACEHOLDER_MEMORY_CONFIGURATION.persistentMemoryOptions.reservedSize = RAGLITE_PERSISTENT_MEMORY; + PLACEHOLDER_MEMORY_CONFIGURATION.transientMemoryOptions.reservedSize = RAGLITE_TRANSIENT_MEMORY; + PlatformInitializeProgramMemory(PLACEHOLDER_PROGRAM_MEMORY, PLACEHOLDER_MEMORY_CONFIGURATION); + // PlatformLoadModule("RagLite2Dbg.dll"); + GameCode game = LoadGameCode("RagLite2Dbg.dll", "RagLite2Dbg.pdb"); // TBD Dbg or release... WNDCLASSEX windowClass = {}; // TODO Is this really a good idea? Beware the CS_OWNDC footguns... @@ -389,32 +385,42 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, hardware_tick_t before = PerformanceMetricsNow(); if(!APPLICATION_SHOULD_PAUSE) { + // GamePadPollControllers(worldState.offsetX, worldState.offsetY); // TODO pass to program + + // TODO Add to debug UI (?) + FILETIME new_time = GetLastWriteTime("RagLite2Dbg.dll"); // TBD Dbg or release + if(CompareFileTime(&new_time, &game.last_write_time) != 0) { + // DLL changed + UnloadGameCode(&game); + game = LoadGameCode("RagLite2Dbg.dll", "RagLite2Dbg.pdb"); + } - // NOTE: Application/game state updates should go here (later) - PLACEHOLDER_DEMO_APP.offsetX++; - PLACEHOLDER_DEMO_APP.offsetY++; - PLACEHOLDER_DEMO_APP.offsetY++; + program_input_t inputs = { + .clock = PerformanceMetricsNow(), + .uptime = PerformanceMetricsGetTimeSince(applicationStartTime), + }; + program_output_t outputs = { + .canvas = { + .width = GDI_BACKBUFFER.width, + .height = GDI_BACKBUFFER.height, + .bytesPerPixel = GDI_BACKBUFFER.bytesPerPixel, + .stride = GDI_BACKBUFFER.stride, + .pixelBuffer = GDI_BACKBUFFER.pixelBuffer, + } + }; - size_t allocationSize = Megabytes(2); - if(!SystemMemoryCanAllocate(MAIN_MEMORY, allocationSize)) { - SystemMemoryReset(MAIN_MEMORY); - } else { - uint8* mainMemory = (uint8*)SystemMemoryAllocate(MAIN_MEMORY, allocationSize); - *mainMemory = 0xDE; - SystemMemoryDebugTouch(MAIN_MEMORY, mainMemory); - } + ASSUME(game.SimulateNextFrame, "Failed to load program module (cannot advance the simulation)"); + if(game.SimulateNextFrame) game.SimulateNextFrame(&PLACEHOLDER_PROGRAM_MEMORY, &inputs, &outputs); - if(!SystemMemoryCanAllocate(TRANSIENT_MEMORY, 2 * allocationSize)) { - SystemMemoryReset(TRANSIENT_MEMORY); + size_t allocationSize = Megabytes(2); + if(!SystemMemoryCanAllocate(PLACEHOLDER_PROGRAM_MEMORY.transientMemory, 2 * allocationSize)) { + SystemMemoryReset(PLACEHOLDER_PROGRAM_MEMORY.transientMemory); } else { - uint8* transientMemory = (uint8*)SystemMemoryAllocate(TRANSIENT_MEMORY, 2 * allocationSize); + uint8* transientMemory = (uint8*)SystemMemoryAllocate(PLACEHOLDER_PROGRAM_MEMORY.transientMemory, 2 * allocationSize); *transientMemory = 0xAB; - SystemMemoryDebugTouch(TRANSIENT_MEMORY, transientMemory); + SystemMemoryDebugTouch(PLACEHOLDER_PROGRAM_MEMORY.transientMemory, transientMemory); } - - GamePadPollControllers(PLACEHOLDER_DEMO_APP.offsetX, PLACEHOLDER_DEMO_APP.offsetY); - DebugDrawUpdateBackgroundPattern(); } CPU_PERFORMANCE_METRICS.worldUpdateTime = PerformanceMetricsGetTimeSince(before); diff --git a/Core/Platforms/Win32.hpp b/Core/Platforms/Win32.hpp index d3e4bb21..e03316ec 100644 --- a/Core/Platforms/Win32.hpp +++ b/Core/Platforms/Win32.hpp @@ -6,4 +6,46 @@ #include #include #include -#include \ No newline at end of file +#include + +struct GameCode { + HMODULE dll; + FILETIME last_write_time; + void (*SimulateNextFrame)(program_memory_t* memory, program_input_t* inputs, program_output_t* outputs); +}; + +FILETIME GetLastWriteTime(const char* filename) { + WIN32_FILE_ATTRIBUTE_DATA data; + if(GetFileAttributesExA(filename, GetFileExInfoStandard, &data)) + return data.ftLastWriteTime; + FILETIME empty = {}; + return empty; +} + +GameCode LoadGameCode(const char* dll_path, const char* pdb_path) { + GameCode result = {}; + result.last_write_time = GetLastWriteTime(dll_path); + + // char temp_dll[MAX_PATH]; + // sprintf_s(temp_dll, "", dll_path); + + // Copy to temp to allow rebuilds + // TBD: Also copy debug info to avoid having to add custom path mappings in the debugger... + CopyFileA(pdb_path, "RagLite2Dbg.loaded.pdb", FALSE); + CopyFileA(dll_path, "RagLite2Dbg.loaded.dll", FALSE); + result.dll = LoadLibraryA("RagLite2Dbg.loaded.dll"); + // TBD: Should probably delete this on exit? + // result.dll = LoadLibraryA(dll_path); + if(result.dll) + result.SimulateNextFrame = (void (*)(program_memory_t*, program_input_t*, program_output_t*))GetProcAddress(result.dll, "SimulateNextFrame"); + + return result; +} + +void UnloadGameCode(GameCode* code) { + if(code->dll) { + FreeLibrary(code->dll); + code->dll = 0; + code->SimulateNextFrame = 0; + } +} \ No newline at end of file diff --git a/Core/Platforms/Win32/DebugDraw.cpp b/Core/Platforms/Win32/DebugDraw.cpp index d40fdb06..85f0484e 100644 --- a/Core/Platforms/Win32/DebugDraw.cpp +++ b/Core/Platforms/Win32/DebugDraw.cpp @@ -509,20 +509,6 @@ INTERNAL void DebugDrawMemoryArenaHeatmap(HDC& displayDeviceContext, memory_aren constexpr size_t FORMAT_BUFFER_SIZE = 256; char formatBuffer[FORMAT_BUFFER_SIZE]; - StringCbPrintfA(formatBuffer, FORMAT_BUFFER_SIZE, "Name: %s", arena.displayName.buffer); - TextOutA(displayDeviceContext, startX + DEBUG_OVERLAY_PADDING_SIZE, lineY, formatBuffer, lstrlenA(formatBuffer)); - lineY += DEBUG_OVERLAY_LINE_HEIGHT; - - String lifetime = SystemMemoryDebugLifetime(arena); - StringCbPrintfA(formatBuffer, FORMAT_BUFFER_SIZE, "Lifetime: %s", lifetime.buffer); - TextOutA(displayDeviceContext, startX + DEBUG_OVERLAY_PADDING_SIZE, lineY, formatBuffer, lstrlenA(formatBuffer)); - lineY += DEBUG_OVERLAY_LINE_HEIGHT; - - String usage = SystemMemoryDebugUsage(arena); - StringCbPrintfA(formatBuffer, FORMAT_BUFFER_SIZE, "Usage: %s", usage.buffer); - TextOutA(displayDeviceContext, startX + DEBUG_OVERLAY_PADDING_SIZE, lineY, formatBuffer, lstrlenA(formatBuffer)); - lineY += DEBUG_OVERLAY_LINE_HEIGHT; - StringCbPrintfA(formatBuffer, FORMAT_BUFFER_SIZE, "Base: 0x%p", arena.baseAddress); TextOutA(displayDeviceContext, startX + DEBUG_OVERLAY_PADDING_SIZE, lineY, formatBuffer, lstrlenA(formatBuffer)); lineY += DEBUG_OVERLAY_LINE_HEIGHT; @@ -646,12 +632,12 @@ INTERNAL void DebugDrawMemoryUsageOverlay(HDC& displayDeviceContext) { startX += DEBUG_OVERLAY_PADDING_SIZE; heatmapWidth = MAIN_MEMORY_PANELS * heatmapWidth + (MAIN_MEMORY_PANELS - 1) * DEBUG_OVERLAY_MARGIN_SIZE; - DebugDrawMemoryArenaHeatmap(displayDeviceContext, MAIN_MEMORY, startX, lineY, heatmapWidth, heatmapHeight); + DebugDrawMemoryArenaHeatmap(displayDeviceContext, PLACEHOLDER_PROGRAM_MEMORY.persistentMemory, startX, lineY, heatmapWidth, heatmapHeight); startX += heatmapWidth; startX += DEBUG_OVERLAY_MARGIN_SIZE; heatmapWidth = TRANSIENT_MEMORY_PANELS * heatmapWidth + (TRANSIENT_MEMORY_PANELS - 1) * DEBUG_OVERLAY_MARGIN_SIZE; - DebugDrawMemoryArenaHeatmap(displayDeviceContext, TRANSIENT_MEMORY, startX, lineY, heatmapWidth, heatmapHeight); + DebugDrawMemoryArenaHeatmap(displayDeviceContext, PLACEHOLDER_PROGRAM_MEMORY.transientMemory, startX, lineY, heatmapWidth, heatmapHeight); startX += heatmapWidth; startX += DEBUG_OVERLAY_PADDING_SIZE; @@ -1018,170 +1004,3 @@ INTERNAL void DebugDrawKeyboardOverlay(HDC& displayDeviceContext) { SelectObject(displayDeviceContext, oldFont); } - -INTERNAL void DebugDrawUpdateBackgroundPattern() { - DWORD ticks = GetTickCount(); - seconds elapsed = (seconds)ticks / MILLISECONDS_PER_SECOND; - seconds updateInterval = 5.0f; - - gdi_debug_pattern_t newPattern = (gdi_debug_pattern_t)(elapsed / updateInterval); - GDI_DEBUG_PATTERN = (gdi_debug_pattern_t)(newPattern % PATTERN_COUNT); -} - -INTERNAL void DebugDrawUseMarchingGradientPattern(gdi_offscreen_buffer_t& bitmap, - int offsetBlue, - int offsetGreen) { - if(!bitmap.pixelBuffer) - return; - - uint8* row = (uint8*)bitmap.pixelBuffer; - for(int y = 0; y < bitmap.height; ++y) { - uint32* pixel = (uint32*)row; - for(int x = 0; x < bitmap.width; ++x) { - uint8 blue = (x + offsetBlue) & 0xFF; - uint8 green = (y + offsetGreen) & 0xFF; - - *pixel++ = ((green << 8) | blue); - } - - row += bitmap.stride; - } -} - -INTERNAL void DebugDrawUseRipplingSpiralPattern(gdi_offscreen_buffer_t& bitmap, int time, - int) { - if(!bitmap.pixelBuffer) - return; - - uint8* row = (uint8*)bitmap.pixelBuffer; - - for(int y = 0; y < bitmap.height; ++y) { - uint32* pixel = (uint32*)row; - for(int x = 0; x < bitmap.width; ++x) { - - int centerX = bitmap.width / 2; - int centerY = bitmap.height / 2; - float dx = (float)(x - centerX); - float dy = (float)(y - centerY); - float dist = sqrtf(dx * dx + dy * dy); - float wave = 0.5f + 0.5f * sinf(dist / 5.0f - time * 0.1f); - - uint8 blue = (uint8)(wave * 255); - uint8 green = (uint8)((1.0f - wave) * 255); - uint8 red = (uint8)((0.5f + 0.5f * sinf(time * 0.05f)) * 255); - - *pixel++ = (red << 16) | (green << 8) | blue; - } - row += bitmap.stride; - } -} - -INTERNAL void DebugDrawUseCheckeredFloorPattern(gdi_offscreen_buffer_t& bitmap, int time, - int) { - if(!bitmap.pixelBuffer) - return; - - uint8* row = (uint8*)bitmap.pixelBuffer; - - float angle = time * 0.02f; - float cosA = cosf(angle); - float sinA = sinf(angle); - - int cx = bitmap.width / 2; - int cy = bitmap.height / 2; - - int squareSize = 32; - - for(int y = 0; y < bitmap.height; ++y) { - uint32* pixel = (uint32*)row; - for(int x = 0; x < bitmap.width; ++x) { - int rx = x - cx; - int ry = y - cy; - - float rX = rx * cosA - ry * sinA; - float rY = rx * sinA + ry * cosA; - - int checkerX = ((int)floorf(rX / squareSize)) & 1; - int checkerY = ((int)floorf(rY / squareSize)) & 1; - - uint8 c = (checkerX ^ checkerY) ? (PROGRESS_BAR_WIDTH - 1) : 80; - *pixel++ = (c << 16) | (c << 8) | c; - } - row += bitmap.stride; - } -} - -INTERNAL void DebugDrawUseColorGradientPattern(gdi_offscreen_buffer_t& bitmap, int, - int) { - if(!bitmap.pixelBuffer) - return; - - uint8* row = (uint8*)bitmap.pixelBuffer; - - int cx = bitmap.width / 2; - int cy = bitmap.height / 2; - - for(int y = 0; y < bitmap.height; ++y) { - uint32* pixel = (uint32*)row; - for(int x = 0; x < bitmap.width; ++x) { - uint8 red = (uint8)((x * 255) / bitmap.width); - uint8 green = (uint8)((y * 255) / bitmap.height); - uint8 blue = 0; - - if(x == cx || y == cy) { - red = green = blue = 255; - } - - *pixel++ = (red << 16) | (green << 8) | blue; - } - row += bitmap.stride; - } -} - -INTERNAL void DebugDrawUseMovingScanlinePattern(gdi_offscreen_buffer_t& bitmap, int time, - int) { - if(!bitmap.pixelBuffer) - return; - - uint8* row = (uint8*)bitmap.pixelBuffer; - - int gridSpacing = 32; - int scanY = (time / 2) % bitmap.height; - - for(int y = 0; y < bitmap.height; ++y) { - uint32* pixel = (uint32*)row; - for(int x = 0; x < bitmap.width; ++x) { - uint8 c = 180; - - if(x % gridSpacing == 0 || y % gridSpacing == 0) - c = 100; - - if(y == scanY) - c = 255; - - *pixel++ = (c << 16) | (c << 8) | c; - } - row += bitmap.stride; - } -} - -INTERNAL void DebugDrawIntoFrameBuffer(gdi_offscreen_buffer_t& bitmap, int paramA, - int paramB) { - switch(GDI_DEBUG_PATTERN) { - case PATTERN_SHIFTING_GRADIENT: - DebugDrawUseMarchingGradientPattern(bitmap, paramA, paramB); - break; - case PATTERN_CIRCULAR_RIPPLE: - DebugDrawUseRipplingSpiralPattern(bitmap, paramA, paramB); - break; - case PATTERN_CHECKERBOARD: - DebugDrawUseCheckeredFloorPattern(bitmap, paramA, paramB); - break; - case PATTERN_AXIS_GRADIENTS: - DebugDrawUseColorGradientPattern(bitmap, paramA, paramB); - break; - case PATTERN_GRID_SCANLINE: - DebugDrawUseMovingScanlinePattern(bitmap, paramA, paramB); - break; - } -} diff --git a/Core/Platforms/Win32/DebugDraw.hpp b/Core/Platforms/Win32/DebugDraw.hpp index 2651240d..60733a17 100644 --- a/Core/Platforms/Win32/DebugDraw.hpp +++ b/Core/Platforms/Win32/DebugDraw.hpp @@ -17,18 +17,8 @@ typedef struct gdi_surface { int height; } gdi_surface_t; -typedef enum : uint8 { - PATTERN_SHIFTING_GRADIENT, - PATTERN_CIRCULAR_RIPPLE, - PATTERN_CHECKERBOARD, - PATTERN_AXIS_GRADIENTS, - PATTERN_GRID_SCANLINE, - PATTERN_COUNT -} gdi_debug_pattern_t; - GLOBAL gdi_offscreen_buffer_t GDI_BACKBUFFER = {}; GLOBAL gdi_surface_t GDI_SURFACE = {}; -GLOBAL gdi_debug_pattern_t GDI_DEBUG_PATTERN = PATTERN_SHIFTING_GRADIENT; typedef union gdi_rgba_color { // NOTE: For simplicity, ensure this matches the pixel format used by GDI bitmaps diff --git a/Core/Platforms/Win32/Memory.cpp b/Core/Platforms/Win32/Memory.cpp index 6dd0afb9..b5fbed79 100644 --- a/Core/Platforms/Win32/Memory.cpp +++ b/Core/Platforms/Win32/Memory.cpp @@ -1,118 +1,28 @@ constexpr size_t HIGHEST_VIRTUAL_ADDRESS = Terabytes(1); -constexpr size_t INVALID_VIRTUAL_ADDRESS = 0xDEADBEEFULL; +constexpr size_t UNSPECIFIED_VIRTUAL_ADDRESS = NULL; // NOTE: OS will determine where to allocate the region -GLOBAL memory_arena_t MAIN_MEMORY = { - .displayName = StringLiteral("Main Memory"), - .lifetime = KEEP_FOREVER_MANUAL_RESET, - .usage = UNUSED_PLACEHOLDER, - .baseAddress = (void*)INVALID_VIRTUAL_ADDRESS, - .reservedSize = 0, - .committedSize = 0, - .used = 0, - .allocationCount = 0 -}; - -GLOBAL memory_arena_t TRANSIENT_MEMORY = { - .displayName = StringLiteral("Transient Memory"), - .lifetime = RESET_AFTER_EACH_FRAME, - .usage = UNUSED_PLACEHOLDER, - .baseAddress = (void*)INVALID_VIRTUAL_ADDRESS, - .reservedSize = 0, - .committedSize = 0, - .used = 0, - .allocationCount = 0 -}; - -// TBD Guard with feature flag (check if compiler removes when unused - assumption: yes) -INTERNAL String SystemMemoryDebugUsage(memory_arena_t& arena) { - switch(arena.lifetime) { - case UNUSED_PLACEHOLDER: - return StringLiteral("Unused (Placeholder)"); - case PREALLOCATED_ON_LOAD: - return StringLiteral("Preallocated (Default)"); - case DYNAMIC_RESIZE_FREELIST: - return StringLiteral("Dynamic (Resizeable)"); - case CAN_HOT_RELOAD: - return StringLiteral("Reloadable (Pinned)"); - default: - return StringLiteral("N/A"); - } -} - -INTERNAL String SystemMemoryDebugLifetime(memory_arena_t& arena) { - switch(arena.lifetime) { - case KEEP_FOREVER_MANUAL_RESET: - return StringLiteral("Forever (Global Arena)"); - case RESET_AFTER_EACH_FRAME: - return StringLiteral("Frame (Scoped Arena)"); - case RESET_AFTER_TASK_COMPLETION: - return StringLiteral("Task Completion (Transfer Arena)"); - case RESET_AUTOMATICALLY_TIMED_EXPIRY: - return StringLiteral("Auto-Expires (Caching Arena)"); - default: - return StringLiteral("N/A"); - } -} - -INTERNAL void SystemMemoryInitializeArenas(size_t mainMemorySize, size_t transientMemorySize) { - -#ifdef RAGLITE_PREDICTABLE_MEMORY - LPVOID baseAddress = 0; -#else - LPVOID baseAddress = (LPVOID)HIGHEST_VIRTUAL_ADDRESS; -#endif - - DWORD allocationTypeFlags = MEM_RESERVE | MEM_COMMIT; - DWORD memoryProtectionFlags = PAGE_READWRITE; - MAIN_MEMORY = { - .displayName = StringLiteral("Main Memory"), - .lifetime = KEEP_FOREVER_MANUAL_RESET, - .usage = PREALLOCATED_ON_LOAD, - .baseAddress = VirtualAlloc(baseAddress, mainMemorySize + transientMemorySize, allocationTypeFlags, memoryProtectionFlags), - .reservedSize = mainMemorySize, - .committedSize = 0, - .used = 0, - .allocationCount = 0 +INTERNAL inline allocation_options_t PlatformDefaultAllocationOptions() { + memory_allocation_options options = { + .allocationType = MEM_RESERVE | MEM_COMMIT, + .protectionConstraints = PAGE_READWRITE, + .startingAddress = (LPVOID)UNSPECIFIED_VIRTUAL_ADDRESS, }; - - TRANSIENT_MEMORY = { - .displayName = StringLiteral("Transient Memory"), - .lifetime = KEEP_FOREVER_MANUAL_RESET, - .usage = PREALLOCATED_ON_LOAD, - .baseAddress = (uint8*)MAIN_MEMORY.baseAddress + mainMemorySize, - .reservedSize = transientMemorySize, - .committedSize = 0, - .used = 0, - .allocationCount = 0 - }; - - MAIN_MEMORY.committedSize = mainMemorySize; - TRANSIENT_MEMORY.committedSize = transientMemorySize; -} - -INTERNAL void* SystemMemoryAllocate(memory_arena_t& arena, size_t allocationSize) { - size_t totalUsed = arena.used + allocationSize; - ASSUME(totalUsed <= arena.reservedSize, "Attempting to allocate outside the reserved set"); - - void* memoryRegionStartPointer = (uint8*)arena.baseAddress + arena.used; - arena.used = totalUsed; - arena.allocationCount++; - - return memoryRegionStartPointer; + // TODO: Zeroize on push + // TODO: Align on push + // TODO: Append guard pages (in debug mode) + // TODO: Add source location (in debug mode) + return options; } -INTERNAL bool SystemMemoryCanAllocate(memory_arena_t& arena, size_t allocationSize) { - if(arena.used + allocationSize > arena.reservedSize) return false; - return true; -} +INTERNAL void PlatformInitializeMemoryArena(memory_arena_t& arena, allocation_options_t& options) { + ZeroMemory(&arena, sizeof(arena)); + arena.baseAddress = VirtualAlloc(options.startingAddress, options.reservedSize, options.allocationType, options.protectionConstraints); -void SystemMemoryReset(memory_arena_t& arena) { - arena.allocationCount = 0; - arena.used = 0; + arena.reservedSize = options.reservedSize; + if(options.allocationType & MEM_COMMIT) arena.committedSize = options.reservedSize; } -INTERNAL inline void SystemMemoryDebugTouch(memory_arena_t& arena, uint8* address) { - ASSUME(address >= arena.baseAddress, "Attempted to access an invalid arena offset"); - size_t offset = address - (uint8*)arena.baseAddress; - // TODO: Update last accessed time +INTERNAL inline void PlatformInitializeProgramMemory(program_memory_t& programMemory, memory_config_t& configOptions) { + PlatformInitializeMemoryArena(programMemory.persistentMemory, configOptions.persistentMemoryOptions); + PlatformInitializeMemoryArena(programMemory.transientMemory, configOptions.transientMemoryOptions); } \ No newline at end of file diff --git a/Core/Platforms/Win32/Memory.hpp b/Core/Platforms/Win32/Memory.hpp deleted file mode 100644 index 9d8eed98..00000000 --- a/Core/Platforms/Win32/Memory.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// NOTE: These are mutually exclusive - do not combine them ever (unhappiness will find you) -enum arena_lifetime_flag { - KEEP_FOREVER_MANUAL_RESET = 0, // Default choice (make sure the arena is small) - RESET_AFTER_EACH_FRAME, // Transient memory likely wants to use this mode - RESET_AFTER_TASK_COMPLETION, // Async workloads/resource loading (NYI) - RESET_AUTOMATICALLY_TIMED_EXPIRY, // For persistent resources/prefetcher/memory pressure mode (NYI) -}; - -// TBD: Not sure if these are useful for anything other than debug annotations (revisit later) -enum arena_usage_flag { - UNUSED_PLACEHOLDER = 0, - PREALLOCATED_ON_LOAD, // Sane default (choose whenever possible) - DYNAMIC_RESIZE_FREELIST, // NYI: Can remove this ideally? (not sure yet, keep as a reminder for now) - CAN_HOT_RELOAD, // NOTE: Must not change structures with this flag or things will go horribly wrong -}; - -typedef struct virtual_memory_arena { - // TBD: Gate debug-time features via flags? Unlikely to matter for the time being (revisit later) - String displayName; - arena_lifetime_flag lifetime; - arena_usage_flag usage; - // TBD: Store this header in the arena itself (required for free-lists/resizes - later)? - void* baseAddress; - size_t reservedSize; - size_t committedSize; - size_t used; - size_t allocationCount; -} memory_arena_t; \ No newline at end of file diff --git a/Core/RagLite2.cpp b/Core/RagLite2.cpp index f4abb3f7..a2a74ec2 100644 --- a/Core/RagLite2.cpp +++ b/Core/RagLite2.cpp @@ -9,6 +9,231 @@ #include "Numbers.hpp" #include "Strings.hpp" +#include "Memory.hpp" +#include "Modules.hpp" + +// TODO move or remove +GLOBAL program_memory_t PLACEHOLDER_PROGRAM_MEMORY = {}; +GLOBAL memory_config_t PLACEHOLDER_MEMORY_CONFIGURATION = {}; + +#ifdef RAGLITE_PLATFORM_NONE + +// TODO Eliminate this +#include + +typedef enum : uint8 { + PATTERN_SHIFTING_GRADIENT, + PATTERN_CIRCULAR_RIPPLE, + PATTERN_CHECKERBOARD, + PATTERN_AXIS_GRADIENTS, + PATTERN_GRID_SCANLINE, + PATTERN_COUNT +} gdi_debug_pattern_t; + +typedef struct volatile_world_state { + uint64 createdTimestamp; + int32 offsetX; + int32 offsetY; + gdi_debug_pattern_t activeDebugDrawingPattern; +} world_state_t; + +INTERNAL void DebugDrawUpdateBackgroundPattern(world_state_t* worldState, program_input_t* inputs) { + milliseconds updateInterval = 5.0f * MILLISECONDS_PER_SECOND; + gdi_debug_pattern_t newPattern = (gdi_debug_pattern_t)(inputs->uptime / updateInterval); + worldState->activeDebugDrawingPattern = (gdi_debug_pattern_t)(newPattern % PATTERN_COUNT); +} + +INTERNAL void DebugDrawUseMarchingGradientPattern(offscreen_buffer_t& bitmap, + int offsetBlue, + int offsetGreen) { + if(!bitmap.pixelBuffer) + return; + + uint8* row = (uint8*)bitmap.pixelBuffer; + for(int y = 0; y < bitmap.height; ++y) { + uint32* pixel = (uint32*)row; + for(int x = 0; x < bitmap.width; ++x) { + uint8 blue = (x + offsetBlue) & 0xFF; + uint8 green = (y + offsetGreen) & 0xFF; + + *pixel++ = ((green << 8) | blue); + } + + row += bitmap.stride; + } +} + +INTERNAL void DebugDrawUseRipplingSpiralPattern(offscreen_buffer_t& bitmap, int time, + int) { + if(!bitmap.pixelBuffer) + return; + + uint8* row = (uint8*)bitmap.pixelBuffer; + + for(int y = 0; y < bitmap.height; ++y) { + uint32* pixel = (uint32*)row; + for(int x = 0; x < bitmap.width; ++x) { + + int centerX = bitmap.width / 2; + int centerY = bitmap.height / 2; + float dx = (float)(x - centerX); + float dy = (float)(y - centerY); + float dist = sqrtf(dx * dx + dy * dy); + float wave = 0.5f + 0.5f * sinf(dist / 5.0f - time * 0.1f); + + uint8 blue = (uint8)(wave * 255); + uint8 green = (uint8)((1.0f - wave) * 255); + uint8 red = (uint8)((0.5f + 0.5f * sinf(time * 0.05f)) * 255); + + *pixel++ = (red << 16) | (green << 8) | blue; + } + row += bitmap.stride; + } +} + +INTERNAL void DebugDrawUseCheckeredFloorPattern(offscreen_buffer_t& bitmap, program_input_t* inputs) { + if(!bitmap.pixelBuffer) + return; + + uint8* row = (uint8*)bitmap.pixelBuffer; + + milliseconds rotationInterval = 5.0f * MILLISECONDS_PER_SECOND; + float angle = inputs->uptime / rotationInterval; + float cosA = cosf(angle); + float sinA = sinf(angle); + + int cx = bitmap.width / 2; + int cy = bitmap.height / 2; + + int squareSize = 32; + + for(int y = 0; y < bitmap.height; ++y) { + uint32* pixel = (uint32*)row; + for(int x = 0; x < bitmap.width; ++x) { + int rx = x - cx; + int ry = y - cy; + + float rX = rx * cosA - ry * sinA; + float rY = rx * sinA + ry * cosA; + + int checkerX = ((int)floorf(rX / squareSize)) & 1; + int checkerY = ((int)floorf(rY / squareSize)) & 1; + + uint8 c = (checkerX ^ checkerY) ? UINT8_MAX : 80; + *pixel++ = (c << 16) | (c << 8) | c; + } + row += bitmap.stride; + } +} + +INTERNAL void DebugDrawUseColorGradientPattern(offscreen_buffer_t& bitmap, int, + int) { + if(!bitmap.pixelBuffer) + return; + + uint8* row = (uint8*)bitmap.pixelBuffer; + + int cx = bitmap.width / 2; + int cy = bitmap.height / 2; + + for(int y = 0; y < bitmap.height; ++y) { + uint32* pixel = (uint32*)row; + for(int x = 0; x < bitmap.width; ++x) { + uint8 red = (uint8)((x * 255) / bitmap.width); + uint8 green = (uint8)((y * 255) / bitmap.height); + uint8 blue = 0; + + if(x == cx || y == cy) { + red = green = blue = 255; + } + + *pixel++ = (red << 16) | (green << 8) | blue; + } + row += bitmap.stride; + } +} + +INTERNAL void DebugDrawUseMovingScanlinePattern(offscreen_buffer_t& bitmap, int time, + int) { + if(!bitmap.pixelBuffer) + return; + + uint8* row = (uint8*)bitmap.pixelBuffer; + + int gridSpacing = 32; + int scanY = (time / 2) % bitmap.height; + + for(int y = 0; y < bitmap.height; ++y) { + uint32* pixel = (uint32*)row; + for(int x = 0; x < bitmap.width; ++x) { + uint8 c = 180; + + if(x % gridSpacing == 0 || y % gridSpacing == 0) + c = 100; + + if(y == scanY) + c = 255; + + *pixel++ = (c << 16) | (c << 8) | c; + } + row += bitmap.stride; + } +} + +INTERNAL void DebugDrawIntoFrameBuffer(world_state_t* worldState, program_input_t* inputs, program_output_t* outputs) { + offscreen_buffer_t bitmap = outputs->canvas; + int paramA = worldState->offsetX; + int paramB = worldState->offsetY; + switch(worldState->activeDebugDrawingPattern) { + case PATTERN_SHIFTING_GRADIENT: + DebugDrawUseMarchingGradientPattern(bitmap, paramA, paramB); + break; + case PATTERN_CIRCULAR_RIPPLE: + DebugDrawUseRipplingSpiralPattern(bitmap, paramA, paramB); + break; + case PATTERN_CHECKERBOARD: + DebugDrawUseCheckeredFloorPattern(bitmap, inputs); + break; + case PATTERN_AXIS_GRADIENTS: + DebugDrawUseColorGradientPattern(bitmap, paramA, paramB); + break; + case PATTERN_GRID_SCANLINE: + DebugDrawUseMovingScanlinePattern(bitmap, paramA, paramB); + break; + } +} + +EXPORT void SimulateNextFrame(program_memory_t* memory, program_input_t* inputs, program_output_t* outputs) { + world_state_t* worldState = (world_state_t*)memory->persistentMemory.baseAddress; + if(!worldState->createdTimestamp) { + worldState->createdTimestamp = inputs->clock; + worldState->activeDebugDrawingPattern = PATTERN_SHIFTING_GRADIENT; + } + + worldState->offsetX++; + worldState->offsetX++; + worldState->offsetY++; + // TODO update/render time needs to be fixed in the profiler? + DebugDrawUpdateBackgroundPattern(worldState, inputs); + DebugDrawIntoFrameBuffer(worldState, inputs, outputs); + + // NOTE: Access to the memory arena APIs should be platform-agnostic (no OS interaction required, just pointer math) + memory->persistentMemory.used += sizeof(worldState); // Avoid overwriting the actual program state (temporary hack) + size_t allocationSize = Megabytes(2); + if(!SystemMemoryCanAllocate(memory->persistentMemory, 2 * allocationSize)) { + SystemMemoryReset(memory->persistentMemory); + } else { + uint8* address = (uint8*)SystemMemoryAllocate(memory->persistentMemory, 2 * allocationSize); + *address = 0xAB; + SystemMemoryDebugTouch(memory->persistentMemory, address); + } + + // NYI: Push draw commands to the platform's render queue +} +#else + #ifdef RAGLITE_PLATFORM_WINDOWS #include "Platforms/Win32.cpp" +#endif + #endif \ No newline at end of file diff --git a/Core/RagLite2.hpp b/Core/RagLite2.hpp index f1858c16..eee20561 100644 --- a/Core/RagLite2.hpp +++ b/Core/RagLite2.hpp @@ -4,6 +4,9 @@ #define RAGLITE_COMMIT_HASH "N/A" #endif +#ifndef RAGLITE_PLATFORM_NONE +// NOTE: Disabling all platform APIs for program DLLs for now to avoid accidental callbacks into the OS + #ifdef _WIN32 #define RAGLITE_PLATFORM_WINDOWS #elifdef __APPLE__ @@ -18,6 +21,8 @@ #error "Unsupported Platform: OS-specific code paths have yet to be ported" #endif +#endif + #define RAGLITE_COMPILER_GCC 0 #define RAGLITE_COMPILER_LLVM 0 #define RAGLITE_COMPILER_MSVC 0 @@ -25,6 +30,7 @@ #ifdef _MSC_VER #undef RAGLITE_COMPILER_MSVC #define RAGLITE_COMPILER_MSVC 1 +#define EXPORT extern "C" __declspec(dllexport) #else #define RAGLITE_UNSUPPORTED_COMPILER #endif @@ -48,4 +54,12 @@ static_assert(PLATFORM_POINTER_SIZE == Bits(64), "Only 64-bit platforms are curr #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #error "Only Little-Endian platforms are currently supported" +#endif + +#ifndef RAGLITE_PERSISTENT_MEMORY +#define RAGLITE_PERSISTENT_MEMORY Megabytes(85) +#endif + +#ifndef RAGLITE_TRANSIENT_MEMORY +#define RAGLITE_TRANSIENT_MEMORY Megabytes(1596) + Kilobytes(896) #endif \ No newline at end of file diff --git a/build.bat b/build.bat index aa7b41ba..b77a4911 100644 --- a/build.bat +++ b/build.bat @@ -1,8 +1,16 @@ @echo off +set DEBUG_EXE=RagLiteWin32Dbg.exe +set RELEASE_EXE=RagLiteWin32.exe + set CPP_MAIN=Core\RagLite2.cpp -set DEBUG_EXE=RagLite2Dbg.exe -set RELEASE_EXE=RagLite2.exe +set DLL_MAIN=Core\RagLite2.cpp +set DEBUG_DLL=RagLite2Dbg.dll +set RELEASE_DLL=RagLite2.dll +set DLL_FLAGS=/LD /DRAGLITE_PLATFORM_NONE + +:: TODO: Skip .lib generation? + set RUNTIME_LIBS=gdi32.lib shlwapi.lib user32.lib xinput.lib winmm.lib for /f "delims=" %%i in ('call git describe --always --dirty') do set GIT_COMMIT_HASH=\"%%i\" @@ -38,7 +46,7 @@ set SHARED_COMPILE_FLAGS=%SHARED_COMPILE_FLAGS% /options:strict :: /W4 Enable informational warnings (levels 0 through 4) set SHARED_COMPILE_FLAGS=%SHARED_COMPILE_FLAGS% /W4 :: ...except useless ones -set SHARED_COMPILE_FLAGS=%SHARED_COMPILE_FLAGS% /wd4189 /wd4100 +set SHARED_COMPILE_FLAGS=%SHARED_COMPILE_FLAGS% /wd4189 /wd4100 /wd4505 :: /WX Treat all warnings as errors set SHARED_COMPILE_FLAGS=%SHARED_COMPILE_FLAGS% /WX :: /Zc:strictStrings Require const qualifier for pointers initialized via string literals @@ -80,13 +88,8 @@ set DEBUG_LINK_FLAGS=%DEBUG_LINK_FLAGS% /DEBUG set DEBUG_COMPILE_FLAGS=%DEBUG_COMPILE_FLAGS% %SHARED_COMPILE_FLAGS% set DEBUG_LINK_FLAGS=%DEBUG_LINK_FLAGS% %SHARED_LINK_FLAGS% -echo The Ancient One speaketh: -echo Let us now turn %CPP_MAIN% into %DEBUG_EXE%! -echo Harken, mortal, as I prepare thy unholy incantation... -echo cl%DEBUG_COMPILE_FLAGS% %CPP_MAIN% %RUNTIME_LIBS% /link %DEBUG_LINK_FLAGS% %ICON_RES% /out:%DEBUG_EXE% -echo -------------------------------------------------------------------------------------------------------- cl %DEBUG_COMPILE_FLAGS% %CPP_MAIN% %RUNTIME_LIBS% /link %DEBUG_LINK_FLAGS% %ICON_RES% /out:%DEBUG_EXE% || exit /b -echo -------------------------------------------------------------------------------------------------------- +cl %DEBUG_COMPILE_FLAGS% %DLL_MAIN% %RUNTIME_LIBS% %DLL_FLAGS% /link %DEBUG_LINK_FLAGS% /out:%DEBUG_DLL% || exit /b :::::: Build release binary set RELEASE_COMPILE_FLAGS= @@ -119,16 +122,13 @@ set RELEASE_LINK_FLAGS=%RELEASE_LINK_FLAGS% /OPT:ICF set RELEASE_COMPILE_FLAGS=%RELEASE_COMPILE_FLAGS% %SHARED_COMPILE_FLAGS% set RELEASE_LINK_FLAGS=%RELEASE_LINK_FLAGS% %SHARED_LINK_FLAGS% -echo The Ancient One speaketh: -echo Let us now turn %CPP_MAIN% into %RELEASE_EXE%! -echo Harken, mortal, as I prepare thy unholy incantation... -echo cl%RELEASE_COMPILE_FLAGS% %CPP_MAIN% %RUNTIME_LIBS% /link %RELEASE_LINK_FLAGS% %ICON_RES% /out:%RELEASE_EXE% -echo -------------------------------------------------------------------------------------------------------- cl %RELEASE_COMPILE_FLAGS% %CPP_MAIN% %RUNTIME_LIBS% /link %RELEASE_LINK_FLAGS% %ICON_RES% /out:%RELEASE_EXE% || exit /b -echo -------------------------------------------------------------------------------------------------------- +cl %RELEASE_COMPILE_FLAGS% %DLL_MAIN% %RUNTIME_LIBS% %DLL_FLAGS% /link %RELEASE_LINK_FLAGS% /out:%RELEASE_DLL% || exit /b :: Cleanup -move RagLite2*.exe %DEFAULT_BUILD_DIR% 2> NUL +move RagLite*.exe %DEFAULT_BUILD_DIR% 2> NUL +move RagLite*.dll %DEFAULT_BUILD_DIR% 2> NUL move *.idb %DEFAULT_BUILD_DIR% 2> NUL +move *.lib %DEFAULT_BUILD_DIR% 2> NUL move *.obj %DEFAULT_BUILD_DIR% 2> NUL move *.pdb %DEFAULT_BUILD_DIR% 2> NUL \ No newline at end of file