From d20c59b162e9532684c9829c71f3144c9cf425c6 Mon Sep 17 00:00:00 2001 From: IonutParau Date: Tue, 24 Jun 2025 19:25:40 +0200 Subject: [PATCH] progress on GPUs not at all complete --- build.zig | 1 + src/components/filesystem.c | 1 - src/components/gpu.c | 156 ++++++++++++++++++++++++++++++++++++ src/components/screen.c | 10 ++- src/components/screen.h | 2 +- src/emulator.c | 62 +++++++++++++- src/neonucleus.h | 43 ++++++++-- src/testLuaArch.c | 1 - src/unicode.c | 4 +- src/universe.c | 3 + 10 files changed, 266 insertions(+), 17 deletions(-) create mode 100644 src/components/gpu.c diff --git a/build.zig b/build.zig index ee2a06d..85515d5 100644 --- a/build.zig +++ b/build.zig @@ -17,6 +17,7 @@ fn addEngineSources(c: *std.Build.Step.Compile) void { "src/components/eeprom.c", "src/components/filesystem.c", "src/components/screen.c", + "src/components/gpu.c", }, }); } diff --git a/src/components/filesystem.c b/src/components/filesystem.c index 139ac59..a07158a 100644 --- a/src/components/filesystem.c +++ b/src/components/filesystem.c @@ -54,7 +54,6 @@ void nn_fs_seekCost(nn_filesystem *fs, size_t count, nn_component *component, nn if(control.pretendRPM == 0) return; // disabled, likely SSD double rps = (double)control.pretendRPM / 60; double seekLatency = 1.0 / ((double)fs->spaceTotal(component, fs->userdata) / control.pretendChunkSize) / rps; - printf("Seek Latency: %lf\n", seekLatency); nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); nn_busySleep(seekLatency * count); diff --git a/src/components/gpu.c b/src/components/gpu.c new file mode 100644 index 0000000..772a90c --- /dev/null +++ b/src/components/gpu.c @@ -0,0 +1,156 @@ +#include "../neonucleus.h" +#include "screen.h" + +typedef struct nni_gpu { + nn_screen *currentScreen; + nn_address screenAddress; + nn_gpuControl ctrl; + int currentFg; + int currentBg; + bool isFgPalette; + bool isBgPalette; + // TODO: think about buffers and stuff +} nni_gpu; + +nni_gpu *nni_newGPU(nn_gpuControl *ctrl) { + nni_gpu *gpu = nn_malloc(sizeof(nni_gpu)); + gpu->currentScreen = NULL; + gpu->screenAddress = NULL; + gpu->ctrl = *ctrl; + gpu->currentFg = 0xFFFFFF; + gpu->currentBg = 0x000000; + gpu->isFgPalette = false; + gpu->isBgPalette = false; + return gpu; +} + +void nni_gpuDeinit(nni_gpu *gpu) { + if(gpu->currentScreen != NULL) { + nn_destroyScreen(gpu->currentScreen); + } + if(gpu->screenAddress != NULL) { + nn_free(gpu->screenAddress); + } + nn_free(gpu); +} + +nn_scrchr_t nni_gpu_makePixel(nni_gpu *gpu, const char *s) { + return (nn_scrchr_t) { + .codepoint = nn_unicode_codepointAt(s, 0), + .fg = gpu->currentFg, + .bg = gpu->currentBg, + .isFgPalette = gpu->isFgPalette, + .isBgPalette = gpu->isBgPalette, + }; +} + +void nni_gpu_bind(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + nn_value argVal = nn_getArgument(computer, 0); + nn_value resetVal = nn_getArgument(computer, 1); + + const char *addr = nn_toCString(argVal); + if(addr == NULL) { + nn_setCError(computer, "bad argument #1 (address expected)"); + return; + } + bool reset = false; + if(resetVal.tag == NN_VALUE_BOOL) reset = nn_toBoolean(resetVal); + + nn_component *c = nn_findComponent(computer, (nn_address)addr); + if(c == NULL) { + nn_setCError(computer, "no such screen"); + return; + } + + nn_componentTable *supportedTable = nn_getScreenTable(nn_getUniverse(computer)); + if(supportedTable != nn_getComponentTable(c)) { + nn_setCError(computer, "incompatible screen"); + return; + } + + if(gpu->currentScreen != NULL) { + nn_destroyScreen(gpu->currentScreen); + } + nn_screen *screen = nn_getComponentUserdata(c); + + if(reset) { + for(size_t i = 0; i < screen->width; i++) { + for(size_t j = 0; j < screen->height; j++) { + nn_setPixel(screen, i, j, nni_gpu_makePixel(gpu, " ")); + } + } + size_t area = screen->width * screen->height; + nn_addHeat(computer, gpu->ctrl.pixelResetHeat * area); + nn_callCost(computer, gpu->ctrl.pixelResetCost * area); + nn_removeEnergy(computer, gpu->ctrl.pixelResetEnergy * area); + nn_busySleep(gpu->ctrl.pixelResetLatency * area); + } + + gpu->currentScreen = screen; + if(gpu->screenAddress != NULL) { + nn_free(gpu->screenAddress); + } + gpu->screenAddress = nn_strdup(addr); + + nn_addHeat(computer, gpu->ctrl.bindHeat); + nn_callCost(computer, gpu->ctrl.bindCost); + nn_removeEnergy(computer, gpu->ctrl.bindEnergy); + nn_busySleep(gpu->ctrl.bindLatency); + + nn_return(computer, nn_values_boolean(true)); +} + +void nni_gpu_set(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + +} + +void nni_gpu_get(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + +} + +void nni_gpu_getScreen(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + if(gpu->screenAddress == NULL) return; + nn_return(computer, nn_values_string(gpu->screenAddress, 0)); +} + +void nni_gpu_maxResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + if(gpu->currentScreen == NULL) return; + int w, h; + nn_maxResolution(gpu->currentScreen, &w, &h); + nn_return(computer, nn_values_integer(w)); + nn_return(computer, nn_values_integer(h)); +} + +void nni_gpu_getResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + if(gpu->currentScreen == NULL) return; + int w, h; + nn_maxResolution(gpu->currentScreen, &w, &h); + nn_return(computer, nn_values_integer(w)); + nn_return(computer, nn_values_integer(h)); +} + +void nni_gpu_setResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { + if(gpu->currentScreen == NULL) return; +} + +void nn_loadGraphicsCardTable(nn_universe *universe) { + nn_componentTable *gpuTable = nn_newComponentTable("gpu", NULL, NULL, (void *)nni_gpuDeinit); + nn_storeUserdata(universe, "NN:GPU", gpuTable); + + nn_defineMethod(gpuTable, "bind", false, (void *)nni_gpu_bind, NULL, "bind(addr: string[, reset: boolean = false]): boolean - Bind a GPU to a screen. Very expensive. If reset is true, it will clear the screen."); + nn_defineMethod(gpuTable, "getScreen", true, (void *)nni_gpu_getScreen, NULL, "getScreen(): string"); + nn_defineMethod(gpuTable, "set", false, (void *)nni_gpu_set, NULL, "set(x: integer, y: integer, text: string[, vertical: boolean = false]) - Modifies the screen at a specific x or y. If vertical is false, it will display it horizontally. If it is true, it will display it vertically."); + nn_defineMethod(gpuTable, "get", false, (void *)nni_gpu_get, NULL, "get(x: integer, y: integer): string, integer, integer, integer?, integer? - Returns the character, foreground color, background color, foreground palette index (if applicable), background palette index (if applicable) of a pixel"); + nn_defineMethod(gpuTable, "maxResolution", true, (void *)nni_gpu_maxResolution, NULL, "maxResolution(): integer, integer - Gets the maximum resolution supported by the bound screen."); + nn_defineMethod(gpuTable, "getResolution", true, (void *)nni_gpu_getResolution, NULL, "getResolution(): integer, integer - Gets the current resolution of the bound screen."); + nn_defineMethod(gpuTable, "setResolution", true, (void *)nni_gpu_setResolution, NULL, "maxResolution(): integer, integer - Changes the resolution of the bound screen."); +} + +nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control) { + nn_componentTable *gpuTable = nn_queryUserdata(nn_getUniverse(computer), "NN:GPU"); + nni_gpu *gpu = nni_newGPU(control); + if(gpu == NULL) { + return NULL; + } + return nn_newComponent(computer, address, slot, gpuTable, gpu); +} diff --git a/src/components/screen.c b/src/components/screen.c index e9154e9..14f13ad 100644 --- a/src/components/screen.c +++ b/src/components/screen.c @@ -3,7 +3,7 @@ nn_screen *nn_newScreen(int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors) { nn_screen *screen = nn_malloc(sizeof(nn_screen)); - screen->buffer = nn_malloc(sizeof(nn_screenChar) * maxWidth * maxHeight); + screen->buffer = nn_malloc(sizeof(nn_scrchr_t) * maxWidth * maxHeight); screen->lock = nn_newGuard(); screen->refc = 1; screen->width = maxWidth; @@ -145,11 +145,11 @@ void nn_setDepth(nn_screen *screen, int depth) { screen->depth = depth; } -void nn_setPixel(nn_screen *screen, int x, int y, nn_screenChar pixel) { +void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel) { screen->buffer[x + y * screen->maxWidth] = pixel; } -nn_screenChar nn_getPixel(nn_screen *screen, int x, int y) { +nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y) { return screen->buffer[x + y * screen->maxWidth]; } @@ -211,6 +211,10 @@ void nn_loadScreenTable(nn_universe *universe) { nn_defineMethod(screenTable, "getKeyboards", false, (void *)nn_screenComp_getKeyboards, NULL, "getKeyboards(): string[] - Returns the keyboards registered to this screen."); } +nn_componentTable *nn_getScreenTable(nn_universe *universe) { + return nn_queryUserdata(universe, "NN:SCREEN"); +} + nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen) { nn_componentTable *screenTable = nn_queryUserdata(nn_getUniverse(computer), "NN:SCREEN"); return nn_newComponent(computer, address, slot, screenTable, screen); diff --git a/src/components/screen.h b/src/components/screen.h index f488898..f620bda 100644 --- a/src/components/screen.h +++ b/src/components/screen.h @@ -4,7 +4,7 @@ #include "../neonucleus.h" typedef struct nn_screen { - nn_screenChar *buffer; + nn_scrchr_t *buffer; nn_guard *lock; nn_refc refc; int width; diff --git a/src/emulator.c b/src/emulator.c index b60c39b..8ef0d3a 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -7,6 +7,10 @@ #include "testLuaArch.h" #include +Color ne_processColor(int color) { + return GetColor(color * 0x100 + 0xFF); +} + nn_eepromControl ne_eeprom_getControl(nn_component *component, void *_) { return (nn_eepromControl) { .randomLatencyMin = 0.001, @@ -267,18 +271,38 @@ int main() { }; nn_addFileSystem(computer, "OpenOS", 1, &genericFS); + nn_screen *s = nn_newScreen(240, 80, 16, 16, 256); + + nn_addScreen(computer, "Main Screen", 2, s); + + nn_gpuControl gpuCtrl = { + .maxWidth = 240, + .maxHeight = 80, + .maxDepth = 16, + // fuck the rest, to be decided later + }; + + nn_addGPU(computer, "RTX 6090", 3, &gpuCtrl); + + InitWindow(800, 600, "emulator"); + double lastTime = nn_realTime(); while(true) { + if(WindowShouldClose()) break; double now = nn_realTime(); double dt = now - lastTime; if(dt == 0) dt = 1.0/60; lastTime = now; + + double heat = nn_getTemperature(computer); + double roomHeat = nn_getRoomTemperature(computer); + + double tx = 0.1; // remove some heat per second - nn_removeHeat(computer, dt * (rand() % 12)); + nn_removeHeat(computer, dt * (rand() % 3) * tx * (heat - roomHeat)); if(nn_isOverheating(computer)) { - printf("Machine overheating.\n"); - continue; + goto render; } int state = nn_tickComputer(computer); @@ -300,11 +324,41 @@ int main() { printf("Error: %s\n", e); break; } + +render: + BeginDrawing(); + + ClearBackground(BLACK); + + int scrW, scrH = 1; + nn_getResolution(s, &scrW, &scrH); + int pixelHeight = GetScreenHeight() / scrH; + int pixelWidth = MeasureText("a", pixelHeight); + + for(size_t x = 0; x < scrW; x++) { + for(size_t y = 0; y < scrH; y++) { + nn_scrchr_t p = nn_getPixel(s, x, y); + int l; + const char *s = CodepointToUTF8(p.codepoint, &l); + // fuck palettes + Color fgColor = ne_processColor(p.fg); + Color bgColor = ne_processColor(p.bg); + DrawRectangle(x * pixelWidth, y * pixelHeight, pixelWidth, pixelHeight, bgColor); + DrawText(s, x * pixelWidth, y * pixelHeight, pixelHeight, fgColor); + } + } + + Color heatColor = GREEN; + if(heat > 60) heatColor = YELLOW; + if(heat > 80) heatColor = RED; + DrawText(TextFormat("Heat: %lf\n", heat), 10, 10, 20, heatColor); + + EndDrawing(); } // destroy nn_deleteComputer(computer); nn_unsafeDeleteUniverse(universe); - printf("Emulator is nowhere close to complete\n"); + CloseWindow(); return 0; } diff --git a/src/neonucleus.h b/src/neonucleus.h index c6c870e..87a42c8 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -532,15 +532,16 @@ nn_component *nn_addDrive(nn_computer *computer, nn_address address, int slot, n // Screens and GPUs typedef struct nn_screen nn_screen; -typedef struct nn_screenChar { +typedef struct nn_scrchr_t { int codepoint; int fg; int bg; bool isFgPalette; bool isBgPalette; -} nn_screenChar; +} nn_scrchr_t; nn_screen *nn_newScreen(int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors); +nn_componentTable *nn_getScreenTable(nn_universe *universe); void nn_retainScreen(nn_screen *screen); void nn_destroyScreen(nn_screen *screen); @@ -573,8 +574,8 @@ int nn_maxDepth(nn_screen *screen); int nn_getDepth(nn_screen *screen); void nn_setDepth(nn_screen *screen, int depth); -void nn_setPixel(nn_screen *screen, int x, int y, nn_screenChar pixel); -nn_screenChar nn_getPixel(nn_screen *screen, int x, int y); +void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel); +nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y); bool nn_isDirty(nn_screen *screen); void nn_setDirty(nn_screen *screen, bool dirty); @@ -588,14 +589,44 @@ void nn_setOn(nn_screen *buffer, bool on); nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen); typedef struct nn_gpuControl { + // resolution and colors int maxWidth; int maxHeight; int maxDepth; + + // VRAM Buffers + int totalVRAM; - // other stuff + // Energy costs + double bindEnergy; + double pixelChangeEnergy; + double pixelResetEnergy; + double colorChangeEnergy; + double vramByteChangeEnergy; + + // Heat + double bindHeat; + double pixelChangeHeat; + double pixelResetHeat; + double colorChangeHeat; + double vramByteChangeHeat; + + // Call budgets + size_t bindCost; + size_t pixelChangeCost; + size_t pixelResetCost; + size_t colorChangeCost; + size_t vramByteChangeCost; + + // Latencies + double bindLatency; + double pixelChangeLatency; + double pixelResetLatency; + double colorChangeLatency; + double vramByteChangeLatency; } nn_gpuControl; // the control is COPIED. -nn_component *nn_addGPU(nn_computer *computer, nn_address address, nn_gpuControl *control); +nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control); #endif diff --git a/src/testLuaArch.c b/src/testLuaArch.c index 3cde68b..7d0affa 100644 --- a/src/testLuaArch.c +++ b/src/testLuaArch.c @@ -395,7 +395,6 @@ static int testLuaArch_component_invoke(lua_State *L) { return 2; } nn_resetCall(c); - printf("%s %s %d\n", addr, method, argc); for(size_t i = 0; i < argc; i++) { nn_addArgument(c, testLuaArch_getValue(L, 3 + i)); } diff --git a/src/unicode.c b/src/unicode.c index 6912f1b..97e899b 100644 --- a/src/unicode.c +++ b/src/unicode.c @@ -43,7 +43,9 @@ size_t nn_unicode_len(const char *s) { return count; } -int nn_unicode_codepointAt(const char *s, size_t byteOffset); +int nn_unicode_codepointAt(const char *s, size_t byteOffset) { + return s[byteOffset]; +} size_t nn_unicode_codepointSize(int codepoint) { if (codepoint <= 0x007f) { diff --git a/src/universe.c b/src/universe.c index abfe736..25aadb7 100644 --- a/src/universe.c +++ b/src/universe.c @@ -47,4 +47,7 @@ double nn_getTime(nn_universe *universe) { void nn_loadCoreComponentTables(nn_universe *universe) { nn_loadEepromTable(universe); nn_loadFilesystemTable(universe); + //nn_loadDriveTable(universe); + nn_loadScreenTable(universe); + nn_loadGraphicsCardTable(universe); }