diff --git a/fedac/native/Dockerfile.builder b/fedac/native/Dockerfile.builder --- a/fedac/native/Dockerfile.builder +++ b/fedac/native/Dockerfile.builder @@ -15,7 +15,7 @@ gcc make cpio lz4 bc perl flex bison diffutils \ elfutils-libelf-devel openssl-devel \ curl jq git tar xz findutils pkgconf-pkg-config zstd xorriso \ ca-certificates \ - alsa-lib-devel libdrm-devel flite-devel \ + alsa-lib-devel libdrm-devel flite-devel SDL3-devel \ ffmpeg-free-devel \ wpa_supplicant dhcp-client iw \ iwlwifi-mvm-firmware wireless-regdb \ diff --git a/fedac/native/Makefile b/fedac/native/Makefile --- a/fedac/native/Makefile +++ b/fedac/native/Makefile @@ -47,10 +47,10 @@ CFLAGS += -DHAVE_AVCODEC $(AV_CFLAGS) LDFLAGS += $(AV_LIBS) endif -# SDL2 GPU-accelerated display (optional: make USE_SDL=1) +# SDL3 GPU-accelerated display (optional: make USE_SDL=1) ifdef USE_SDL - SDL_CFLAGS := $(shell pkg-config --cflags sdl2 2>/dev/null) - SDL_LIBS := $(shell pkg-config --libs sdl2 2>/dev/null || echo "-lSDL2") + SDL_CFLAGS := $(shell pkg-config --cflags sdl3 2>/dev/null) + SDL_LIBS := $(shell pkg-config --libs sdl3 2>/dev/null || echo "-lSDL3") CFLAGS += -DUSE_SDL $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif diff --git a/fedac/native/pieces/machine.mjs b/fedac/native/pieces/machine.mjs --- a/fedac/native/pieces/machine.mjs +++ b/fedac/native/pieces/machine.mjs @@ -227,11 +227,25 @@ ink(...c("section2")); write("software", { x: sx, y: ry, size: 1, font }); ry += lineH + 2; - const ver = system?.version || "unknown"; + const ver = system?.version || + [hw?.buildName, hw?.gitHash, hw?.buildTs].filter(Boolean).join(" ") || "unknown"; ink(...c("fg")); const verMaxC = Math.floor(colW / charW); write(ver.slice(0, verMaxC), { x: sx, y: ry, size: 1, font }); ry += lineH; + + // Display driver + GPU + if (hw?.displayDriver) { + ink(...c("dim")); + write("display: " + hw.displayDriver, { x: sx, y: ry, size: 1, font }); + ry += lineH; + } + if (hw?.gpu && hw.gpu !== "unknown") { + ink(...c("dim")); + const gpuStr = hw.gpu.length > verMaxC ? hw.gpu.slice(0, verMaxC - 1) + "…" : hw.gpu; + write("gpu: " + gpuStr, { x: sx, y: ry, size: 1, font }); + ry += lineH; + } const handle = system?.config?.handle; if (handle) { diff --git a/fedac/native/scripts/build-and-flash-initramfs.sh b/fedac/native/scripts/build-and-flash-initramfs.sh --- a/fedac/native/scripts/build-and-flash-initramfs.sh +++ b/fedac/native/scripts/build-and-flash-initramfs.sh @@ -83,6 +83,8 @@ command -v dhclient &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} dhcp-client" command -v iw &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iw" [ -d /lib/firmware ] && ls /lib/firmware/iwlwifi-cc-a0-* &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iwlwifi-mvm-firmware" [ -f /lib/firmware/regulatory.db ] || PKGS_NEEDED="${PKGS_NEEDED} wireless-regdb" + # SDL3 for GPU-accelerated display + pkg-config --exists sdl3 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} SDL3-devel" # ffmpeg libs for video recording pkg-config --exists libavcodec 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} ffmpeg-free-devel" # Flash tools @@ -315,13 +317,13 @@ # Symlink /lib -> /lib64 (some lookups use /lib) ln -sf lib64 "${INITRAMFS_DIR}/lib" fi -# Copy SDL2 + Mesa GPU libraries (if --sdl) +# Copy SDL3 + Mesa GPU libraries (if --sdl) if [ "${USE_SDL}" -eq 1 ]; then - log "Bundling SDL2 + Mesa GPU stack..." + log "Bundling SDL3 + Mesa GPU stack..." mkdir -p "${INITRAMFS_DIR}/lib64/dri" - # SDL2-compat + SDL3 (Fedora uses sdl2-compat over SDL3) - for lib in libSDL2-2.0.so.0 libSDL3.so.0; do + # SDL3 (direct, no sdl2-compat shim) + for lib in libSDL3.so.0; do src=$(readlink -f "/usr/lib64/${lib}" 2>/dev/null) [ -f "$src" ] && cp "$src" "${INITRAMFS_DIR}/lib64/" && ln -sf "$(basename "$src")" "${INITRAMFS_DIR}/lib64/${lib}" done @@ -340,7 +342,6 @@ # DRI driver stubs (Mesa loads these, which then load libgallium) for drv in iris_dri.so i915_dri.so kms_swrast_dri.so swrast_dri.so libdril_dri.so; do src="/usr/lib64/dri/${drv}" if [ -L "$src" ]; then - # Copy as symlink tgt=$(readlink "$src") ln -sf "$tgt" "${INITRAMFS_DIR}/lib64/dri/${drv}" elif [ -f "$src" ]; then @@ -355,7 +356,7 @@ [ -f "$src" ] && cp -n "$src" "${INITRAMFS_DIR}/lib64/" 2>/dev/null && ln -sf "$(basename "$src")" "${INITRAMFS_DIR}/lib64/${lib}" 2>/dev/null done GPU_SIZE=$(du -sh "${INITRAMFS_DIR}/lib64/libgallium"* "${INITRAMFS_DIR}/lib64/libSDL"* "${INITRAMFS_DIR}/lib64/libEGL"* "${INITRAMFS_DIR}/lib64/dri/" 2>/dev/null | tail -1 | cut -f1) - log "SDL2 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" + log "SDL3 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" fi # Copy ALSA config files (required for snd_pcm_open to resolve device names) diff --git a/fedac/native/scripts/build-and-flash.sh b/fedac/native/scripts/build-and-flash.sh --- a/fedac/native/scripts/build-and-flash.sh +++ b/fedac/native/scripts/build-and-flash.sh @@ -83,6 +83,8 @@ command -v dhclient &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} dhcp-client" command -v iw &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iw" [ -d /lib/firmware ] && ls /lib/firmware/iwlwifi-cc-a0-* &>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} iwlwifi-mvm-firmware" [ -f /lib/firmware/regulatory.db ] || PKGS_NEEDED="${PKGS_NEEDED} wireless-regdb" + # SDL3 for GPU-accelerated display + pkg-config --exists sdl3 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} SDL3-devel" # ffmpeg libs for video recording pkg-config --exists libavcodec 2>/dev/null || PKGS_NEEDED="${PKGS_NEEDED} ffmpeg-free-devel" # Flash tools @@ -257,13 +259,13 @@ # Symlink /lib -> /lib64 (some lookups use /lib) ln -sf lib64 "${INITRAMFS_DIR}/lib" fi -# Copy SDL2 + Mesa GPU libraries (if --sdl) +# Copy SDL3 + Mesa GPU libraries (if --sdl) if [ "${USE_SDL}" -eq 1 ]; then - log "Bundling SDL2 + Mesa GPU stack..." + log "Bundling SDL3 + Mesa GPU stack..." mkdir -p "${INITRAMFS_DIR}/lib64/dri" - # SDL2-compat + SDL3 (Fedora uses sdl2-compat over SDL3) - for lib in libSDL2-2.0.so.0 libSDL3.so.0; do + # SDL3 (direct, no sdl2-compat shim) + for lib in libSDL3.so.0; do src=$(readlink -f "/usr/lib64/${lib}" 2>/dev/null) [ -f "$src" ] && cp -L "$src" "${INITRAMFS_DIR}/lib64/${lib}" done @@ -282,7 +284,6 @@ # DRI driver stubs (Mesa loads these, which then load libgallium) for drv in iris_dri.so i915_dri.so kms_swrast_dri.so swrast_dri.so libdril_dri.so; do src="/usr/lib64/dri/${drv}" if [ -L "$src" ]; then - # Copy as symlink tgt=$(readlink "$src") ln -sf "$tgt" "${INITRAMFS_DIR}/lib64/dri/${drv}" elif [ -f "$src" ]; then @@ -297,7 +298,7 @@ [ -f "$src" ] && cp -nL "$src" "${INITRAMFS_DIR}/lib64/${lib}" 2>/dev/null done GPU_SIZE=$(du -sh "${INITRAMFS_DIR}/lib64/libgallium"* "${INITRAMFS_DIR}/lib64/libSDL"* "${INITRAMFS_DIR}/lib64/libEGL"* "${INITRAMFS_DIR}/lib64/dri/" 2>/dev/null | tail -1 | cut -f1) - log "SDL2 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" + log "SDL3 + Mesa GPU stack bundled (gallium: $(du -sh "${GALLIUM}" 2>/dev/null | cut -f1))" fi # Copy ALSA config files (required for snd_pcm_open to resolve device names) diff --git a/fedac/native/src/ac-native.c b/fedac/native/src/ac-native.c --- a/fedac/native/src/ac-native.c +++ b/fedac/native/src/ac-native.c @@ -1579,6 +1579,7 @@ if (alpha > 40) { char ver[64]; char bts[64]; char bname[64] = ""; + char ddrv[64] = ""; snprintf(ver, sizeof(ver), "version %s", AC_GIT_HASH); #ifdef AC_BUILD_TS snprintf(bts, sizeof(bts), "%s", AC_BUILD_TS); @@ -1588,14 +1589,18 @@ #endif #ifdef AC_BUILD_NAME snprintf(bname, sizeof(bname), "%s", AC_BUILD_NAME); #endif + const char *driver = drm_display_driver(display); + snprintf(ddrv, sizeof(ddrv), "display %s", driver); int wv = font_measure_matrix(ver, 1); int wt = font_measure_matrix(bts, 1); int wn = bname[0] ? font_measure_matrix(bname, 1) : 0; + int wd = font_measure_matrix(ddrv, 1); int max_w = wv; if (wt > max_w) max_w = wt; if (wn > max_w) max_w = wn; + if (wd > max_w) max_w = wd; int panel_w = max_w + 8; - int panel_h = bname[0] ? 28 : 20; + int panel_h = (bname[0] ? 28 : 20) + 8; int panel_x = screen->width - panel_w - 3; int panel_y = 3; graph_ink(graph, is_day @@ -1618,6 +1623,11 @@ graph_ink(graph, is_day ? (ACColor){80, 100, 90, (uint8_t)alpha} : (ACColor){210, 235, 220, (uint8_t)alpha}); font_draw_matrix(graph, bts, panel_x + 4, line_y + 8, 1); + // Display driver line + graph_ink(graph, is_day + ? (ACColor){90, 60, 120, (uint8_t)alpha} + : (ACColor){180, 160, 255, (uint8_t)alpha}); + font_draw_matrix(graph, ddrv, panel_x + 4, line_y + 16, 1); // "FRESH" badge when first boot of this version if (is_new_version) { graph_ink(graph, is_day @@ -2053,6 +2063,7 @@ // Init graphics + font ACGraph graph; graph_init(&graph, screen); + if (display) graph_init_gpu(&graph, display); font_init(); // Cursor overlay buffer — drawn separately so KidLisp effects don't smear it @@ -2906,6 +2917,7 @@ if (new_screen) { fb_destroy(screen); screen = new_screen; graph_init(&graph, screen); + if (display) graph_init_gpu(&graph, display); input->scale = pixel_scale; // Update JS runtime's graph reference (holds framebuffer) if (rt) { diff --git a/fedac/native/src/drm-display.c b/fedac/native/src/drm-display.c --- a/fedac/native/src/drm-display.c +++ b/fedac/native/src/drm-display.c @@ -12,76 +12,74 @@ #include #ifdef USE_SDL // ============================================================ -// SDL2 GPU-accelerated display (uses KMSDRM backend on bare metal) +// SDL3 GPU-accelerated display (uses KMSDRM backend on bare metal) // ============================================================ static ACDisplay *sdl_init(void) { // Set KMSDRM hints for bare metal (no X11/Wayland) if (getpid() == 1) { - setenv("SDL_VIDEODRIVER", "kmsdrm", 0); + setenv("SDL_VIDEO_DRIVER", "kmsdrm", 0); } - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - fprintf(stderr, "[sdl] SDL_Init failed: %s\n", SDL_GetError()); + if (!SDL_Init(SDL_INIT_VIDEO)) { + fprintf(stderr, "[sdl3] SDL_Init failed: %s\n", SDL_GetError()); return NULL; } - // Get display mode to know the resolution - SDL_DisplayMode dm; - if (SDL_GetDesktopDisplayMode(0, &dm) < 0) { - fprintf(stderr, "[sdl] GetDesktopDisplayMode failed: %s\n", SDL_GetError()); + // Get primary display and its mode + SDL_DisplayID primary = SDL_GetPrimaryDisplay(); + if (!primary) { + fprintf(stderr, "[sdl3] No primary display: %s\n", SDL_GetError()); + SDL_Quit(); + return NULL; + } + const SDL_DisplayMode *dm = SDL_GetDesktopDisplayMode(primary); + if (!dm) { + fprintf(stderr, "[sdl3] GetDesktopDisplayMode failed: %s\n", SDL_GetError()); SDL_Quit(); return NULL; } - SDL_Window *win = SDL_CreateWindow("ac-native", - SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - dm.w, dm.h, - SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_SHOWN); + SDL_Window *win = SDL_CreateWindow("ac-native", dm->w, dm->h, + SDL_WINDOW_FULLSCREEN); if (!win) { - fprintf(stderr, "[sdl] CreateWindow failed: %s\n", SDL_GetError()); + fprintf(stderr, "[sdl3] CreateWindow failed: %s\n", SDL_GetError()); SDL_Quit(); return NULL; } - SDL_ShowCursor(SDL_DISABLE); + SDL_HideCursor(); - SDL_Renderer *ren = SDL_CreateRenderer(win, -1, - SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (!ren) { - fprintf(stderr, "[sdl] CreateRenderer (accel) failed: %s, trying software\n", SDL_GetError()); - ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE); - } + SDL_Renderer *ren = SDL_CreateRenderer(win, NULL); if (!ren) { - fprintf(stderr, "[sdl] CreateRenderer failed: %s\n", SDL_GetError()); + fprintf(stderr, "[sdl3] CreateRenderer failed: %s\n", SDL_GetError()); SDL_DestroyWindow(win); SDL_Quit(); return NULL; } - // Log renderer info - SDL_RendererInfo info; - SDL_GetRendererInfo(ren, &info); - fprintf(stderr, "[sdl] Renderer: %s (flags: 0x%x)\n", info.name, info.flags); - if (info.flags & SDL_RENDERER_ACCELERATED) - fprintf(stderr, "[sdl] GPU-accelerated rendering active\n"); + // Enable vsync + SDL_SetRenderVSync(ren, 1); - // Set nearest-neighbor scaling for pixel-art look - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); + // Log renderer info + const char *ren_name = SDL_GetRendererName(ren); + fprintf(stderr, "[sdl3] Renderer: %s\n", ren_name ? ren_name : "unknown"); ACDisplay *d = calloc(1, sizeof(ACDisplay)); d->fd = -1; d->is_sdl = 1; - d->width = dm.w; - d->height = dm.h; + d->width = dm->w; + d->height = dm->h; d->sdl_window = win; d->sdl_renderer = ren; // Texture created lazily in display_present (needs framebuffer dimensions) d->sdl_texture = NULL; d->sdl_tex_w = 0; d->sdl_tex_h = 0; + snprintf(d->sdl_renderer_name, sizeof(d->sdl_renderer_name), "%s", + ren_name ? ren_name : "unknown"); - fprintf(stderr, "[sdl] Ready (%dx%d)\n", d->width, d->height); + fprintf(stderr, "[sdl3] Ready (%dx%d)\n", d->width, d->height); return d; } #endif /* USE_SDL */ @@ -259,7 +257,7 @@ ac_log("[drm] drm_init() start\n"); #ifdef USE_SDL ACDisplay *sdl = sdl_init(); if (sdl) return sdl; - fprintf(stderr, "[drm] SDL2 failed, falling back to DRM dumb buffers\n"); + fprintf(stderr, "[drm] SDL3 failed, falling back to DRM dumb buffers\n"); #endif ACDisplay *d = calloc(1, sizeof(ACDisplay)); @@ -469,16 +467,19 @@ if (d->sdl_texture) SDL_DestroyTexture(d->sdl_texture); d->sdl_texture = SDL_CreateTexture(d->sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, screen->width, screen->height); + if (d->sdl_texture) { + SDL_SetTextureScaleMode(d->sdl_texture, SDL_SCALEMODE_NEAREST); + } d->sdl_tex_w = screen->width; d->sdl_tex_h = screen->height; - fprintf(stderr, "[sdl] Created texture %dx%d\n", screen->width, screen->height); + fprintf(stderr, "[sdl3] Created texture %dx%d\n", screen->width, screen->height); } // Upload pixels to GPU texture SDL_UpdateTexture(d->sdl_texture, NULL, screen->pixels, screen->stride * (int)sizeof(uint32_t)); // GPU-accelerated scale to fullscreen SDL_RenderClear(d->sdl_renderer); - SDL_RenderCopy(d->sdl_renderer, d->sdl_texture, NULL, NULL); + SDL_RenderTexture(d->sdl_renderer, d->sdl_texture, NULL, NULL); SDL_RenderPresent(d->sdl_renderer); return; } @@ -757,6 +758,19 @@ drmIoctl(s->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy); } } free(s); +} + +const char *drm_display_driver(ACDisplay *d) { + if (!d) return "none"; +#ifdef USE_SDL + if (d->is_sdl) { + static char buf[48]; + snprintf(buf, sizeof(buf), "sdl3:%s", d->sdl_renderer_name); + return buf; + } +#endif + if (d->is_fbdev) return "fbdev"; + return "drm"; } void drm_destroy(ACDisplay *d) { diff --git a/fedac/native/src/drm-display.h b/fedac/native/src/drm-display.h --- a/fedac/native/src/drm-display.h +++ b/fedac/native/src/drm-display.h @@ -8,7 +8,7 @@ #include "framebuffer.h" #include "graph.h" #ifdef USE_SDL -#include +#include #endif typedef struct { @@ -39,24 +39,28 @@ uint32_t fbdev_size; // fbdev map size int fbdev_stride; // fbdev line length in pixels int fbdev_swap_rb; // 1 if need to swap R and B channels (BGR format) - // SDL2 GPU-accelerated display + // SDL3 GPU-accelerated display #ifdef USE_SDL - int is_sdl; // 1 if using SDL2 backend + int is_sdl; // 1 if using SDL3 backend SDL_Window *sdl_window; SDL_Renderer *sdl_renderer; SDL_Texture *sdl_texture; int sdl_tex_w, sdl_tex_h; // texture dimensions (matches small framebuffer) + char sdl_renderer_name[32]; // renderer driver name (e.g. "opengl", "vulkan") #endif } ACDisplay; -// Initialize display (tries SDL2 GPU first if available, then DRM, then fbdev) +// Get display driver name ("sdl3:opengl", "drm", "fbdev", "wayland") +const char *drm_display_driver(ACDisplay *d); + +// Initialize display (tries SDL3 GPU first if available, then DRM, then fbdev) ACDisplay *drm_init(void); // Present the small framebuffer scaled up to the display // This is the primary display function — handles GPU texture upload or CPU scaling void display_present(ACDisplay *d, ACFramebuffer *screen, int scale); -// Flip to the back buffer (makes it visible) — used by non-SDL paths +// Flip to the back buffer (makes it visible) — used by non-SDL3 paths void drm_flip(ACDisplay *d); // Get pointer to the back buffer (the one to draw into) diff --git a/fedac/native/src/graph.c b/fedac/native/src/graph.c --- a/fedac/native/src/graph.c +++ b/fedac/native/src/graph.c @@ -4,6 +4,10 @@ #include #include #include +#ifdef USE_SDL +#include "drm-display.h" +#endif + static inline uint8_t clamp_u8(int v) { if (v < 0) return 0; if (v > 255) return 255; @@ -15,6 +19,11 @@ g->fb = screen; g->screen = screen; g->ink = (ACColor){255, 255, 255, 255}; g->ink_packed = 0xFFFFFFFF; + g->gpu_display = NULL; +} + +void graph_init_gpu(ACGraph *g, void *display) { + g->gpu_display = display; } void graph_wipe(ACGraph *g, ACColor color) { @@ -158,13 +167,75 @@ void graph_blur(ACGraph *g, int strength) { ACFramebuffer *fb = g->fb; if (strength <= 0) return; +#ifdef USE_SDL + // GPU path: downscale → upscale with bilinear filtering = fast blur + if (g->gpu_display) { + ACDisplay *d = (ACDisplay *)g->gpu_display; + if (d->is_sdl && d->sdl_renderer) { + // Divisor controls blur amount: higher = blurrier + int divisor = strength + 1; + if (divisor > 8) divisor = 8; + int small_w = fb->width / divisor; + int small_h = fb->height / divisor; + if (small_w < 1) small_w = 1; + if (small_h < 1) small_h = 1; + + SDL_Texture *src = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, + fb->width, fb->height); + SDL_Texture *small = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, + small_w, small_h); + SDL_Texture *result = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, + fb->width, fb->height); + + if (src && small && result) { + // Upload framebuffer → source texture + SDL_UpdateTexture(src, NULL, fb->pixels, + fb->stride * (int)sizeof(uint32_t)); + // Set bilinear filtering for smooth blur + SDL_SetTextureScaleMode(src, SDL_SCALEMODE_LINEAR); + SDL_SetTextureScaleMode(small, SDL_SCALEMODE_LINEAR); + + // Pass 1: downscale (src → small) + SDL_SetRenderTarget(d->sdl_renderer, small); + SDL_RenderTexture(d->sdl_renderer, src, NULL, NULL); + + // Pass 2: upscale (small → result) + SDL_SetRenderTarget(d->sdl_renderer, result); + SDL_RenderTexture(d->sdl_renderer, small, NULL, NULL); + + // Read back from result render target + SDL_Rect full = {0, 0, fb->width, fb->height}; + SDL_Surface *rsurf = SDL_RenderReadPixels(d->sdl_renderer, &full); + SDL_SetRenderTarget(d->sdl_renderer, NULL); + if (rsurf) { + const uint32_t *sp = (const uint32_t *)rsurf->pixels; + int spitch = rsurf->pitch / 4; + for (int y = 0; y < fb->height && y < rsurf->h; y++) { + memcpy(&fb->pixels[y * fb->stride], + &sp[y * spitch], + (size_t)fb->width * sizeof(uint32_t)); + } + SDL_DestroySurface(rsurf); + } + } + if (src) SDL_DestroyTexture(src); + if (small) SDL_DestroyTexture(small); + if (result) SDL_DestroyTexture(result); + return; + } + } +#endif + + // CPU fallback: box blur size_t buf_size = (size_t)fb->width * fb->height * sizeof(uint32_t); uint32_t *tmp = malloc(buf_size); if (!tmp) return; memcpy(tmp, fb->pixels, buf_size); int radius = strength; - // Simple box blur for (int y = 0; y < fb->height; y++) { for (int x = 0; x < fb->width; x++) { int r = 0, gr = 0, b = 0, count = 0; @@ -193,6 +264,62 @@ void graph_zoom(ACGraph *g, double level) { ACFramebuffer *fb = g->fb; if (!fb || level <= 0.0 || fabs(level - 1.0) < 1e-6) return; +#ifdef USE_SDL + // GPU path: render texture with scaled src rect for zoom + if (g->gpu_display) { + ACDisplay *d = (ACDisplay *)g->gpu_display; + if (d->is_sdl && d->sdl_renderer) { + const int w = fb->width; + const int h = fb->height; + const float inv = (float)(1.0 / level); + + SDL_Texture *src = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h); + SDL_Texture *dst = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h); + + if (src && dst) { + SDL_UpdateTexture(src, NULL, fb->pixels, + fb->stride * (int)sizeof(uint32_t)); + SDL_SetTextureScaleMode(src, SDL_SCALEMODE_NEAREST); + + // Source rect: the portion of the texture visible after zoom + float crop_w = w * inv; + float crop_h = h * inv; + SDL_FRect srcrect = { + (w - crop_w) * 0.5f, (h - crop_h) * 0.5f, + crop_w, crop_h + }; + SDL_FRect dstrect = {0, 0, (float)w, (float)h}; + + SDL_SetRenderTarget(d->sdl_renderer, dst); + SDL_SetRenderDrawColor(d->sdl_renderer, 0, 0, 0, 255); + SDL_RenderClear(d->sdl_renderer); + SDL_RenderTexture(d->sdl_renderer, src, &srcrect, &dstrect); + + // Read back + SDL_Rect full = {0, 0, w, h}; + SDL_Surface *rsurf = SDL_RenderReadPixels(d->sdl_renderer, &full); + SDL_SetRenderTarget(d->sdl_renderer, NULL); + if (rsurf) { + const uint32_t *sp = (const uint32_t *)rsurf->pixels; + int spitch = rsurf->pitch / 4; + for (int y = 0; y < h && y < rsurf->h; y++) { + memcpy(&fb->pixels[y * fb->stride], + &sp[y * spitch], + (size_t)w * sizeof(uint32_t)); + } + SDL_DestroySurface(rsurf); + } + } + if (src) SDL_DestroyTexture(src); + if (dst) SDL_DestroyTexture(dst); + return; + } + } +#endif + + // CPU fallback const int w = fb->width; const int h = fb->height; size_t buf_size = (size_t)w * h * sizeof(uint32_t); @@ -246,6 +373,57 @@ void graph_spin(ACGraph *g, double angle_radians) { ACFramebuffer *fb = g->fb; if (!fb || fabs(angle_radians) < 1e-6) return; +#ifdef USE_SDL + // GPU path: SDL3 texture rotation + if (g->gpu_display) { + ACDisplay *d = (ACDisplay *)g->gpu_display; + if (d->is_sdl && d->sdl_renderer) { + const int w = fb->width; + const int h = fb->height; + double angle_degrees = angle_radians * (180.0 / M_PI); + + SDL_Texture *src = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, w, h); + SDL_Texture *dst = SDL_CreateTexture(d->sdl_renderer, + SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h); + + if (src && dst) { + SDL_UpdateTexture(src, NULL, fb->pixels, + fb->stride * (int)sizeof(uint32_t)); + SDL_SetTextureScaleMode(src, SDL_SCALEMODE_NEAREST); + + SDL_FRect dstrect = {0, 0, (float)w, (float)h}; + SDL_FPoint center = {w * 0.5f, h * 0.5f}; + + SDL_SetRenderTarget(d->sdl_renderer, dst); + SDL_SetRenderDrawColor(d->sdl_renderer, 0, 0, 0, 255); + SDL_RenderClear(d->sdl_renderer); + SDL_RenderTextureRotated(d->sdl_renderer, src, NULL, &dstrect, + angle_degrees, ¢er, SDL_FLIP_NONE); + + // Read back + SDL_Rect full = {0, 0, w, h}; + SDL_Surface *rsurf = SDL_RenderReadPixels(d->sdl_renderer, &full); + SDL_SetRenderTarget(d->sdl_renderer, NULL); + if (rsurf) { + const uint32_t *sp = (const uint32_t *)rsurf->pixels; + int spitch = rsurf->pitch / 4; + for (int y = 0; y < h && y < rsurf->h; y++) { + memcpy(&fb->pixels[y * fb->stride], + &sp[y * spitch], + (size_t)w * sizeof(uint32_t)); + } + SDL_DestroySurface(rsurf); + } + } + if (src) SDL_DestroyTexture(src); + if (dst) SDL_DestroyTexture(dst); + return; + } + } +#endif + + // CPU fallback size_t buf_size = (size_t)fb->width * fb->height * sizeof(uint32_t); uint32_t *tmp = malloc(buf_size); if (!tmp) return; @@ -258,7 +436,6 @@ const double cy = (h - 1) * 0.5; const double c = cos(angle_radians); const double s = sin(angle_radians); - // Inverse mapping: destination -> source with toroidal wrapping. for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { const double dx = x - cx; diff --git a/fedac/native/src/graph.h b/fedac/native/src/graph.h --- a/fedac/native/src/graph.h +++ b/fedac/native/src/graph.h @@ -10,9 +10,11 @@ ACFramebuffer *fb; // Active render target ACFramebuffer *screen; // Primary screen buffer ACColor ink; // Current drawing color uint32_t ink_packed; // Pre-packed ARGB32 + void *gpu_display; // ACDisplay* when SDL3 GPU is available (NULL = CPU only) } ACGraph; void graph_init(ACGraph *g, ACFramebuffer *screen); +void graph_init_gpu(ACGraph *g, void *display); // Enable GPU-accelerated effects // Core primitives (matching AC API) void graph_wipe(ACGraph *g, ACColor color); diff --git a/fedac/native/src/js-bindings.c b/fedac/native/src/js-bindings.c --- a/fedac/native/src/js-bindings.c +++ b/fedac/native/src/js-bindings.c @@ -4297,6 +4297,37 @@ // Build name (baked in at compile time) #ifdef AC_BUILD_NAME JS_SetPropertyStr(ctx, hw, "buildName", JS_NewString(ctx, AC_BUILD_NAME)); #endif +#ifdef AC_GIT_HASH + JS_SetPropertyStr(ctx, hw, "gitHash", JS_NewString(ctx, AC_GIT_HASH)); +#endif +#ifdef AC_BUILD_TS + JS_SetPropertyStr(ctx, hw, "buildTs", JS_NewString(ctx, AC_BUILD_TS)); +#endif + + // Display driver and GPU info + { + extern void *g_display; + if (g_display) { + const char *drv = drm_display_driver((ACDisplay *)g_display); + JS_SetPropertyStr(ctx, hw, "displayDriver", JS_NewString(ctx, drv)); + } else { + JS_SetPropertyStr(ctx, hw, "displayDriver", JS_NewString(ctx, "wayland")); + } + // Read GPU renderer from sysfs (Mesa exposes via DRI) + char gpu_name[128] = "unknown"; + FILE *fp = popen("cat /sys/kernel/debug/dri/0/name 2>/dev/null || " + "cat /sys/class/drm/card0/device/label 2>/dev/null || " + "echo unknown", "r"); + if (fp) { + if (fgets(gpu_name, sizeof(gpu_name), fp)) { + // Strip trailing newline + char *nl = strchr(gpu_name, '\n'); + if (nl) *nl = '\0'; + } + pclose(fp); + } + JS_SetPropertyStr(ctx, hw, "gpu", JS_NewString(ctx, gpu_name)); + } // Audio diagnostics if (current_rt->audio) { diff --git a/fedac/native/src/machines.c b/fedac/native/src/machines.c --- a/fedac/native/src/machines.c +++ b/fedac/native/src/machines.c @@ -157,6 +157,25 @@ char hostname[64] = ""; read_file("/etc/hostname", hostname, sizeof(hostname)); + // Display driver + extern void *g_display; + const char *drv = "unknown"; + if (g_display) { + drv = drm_display_driver((ACDisplay *)g_display); + } else if (getenv("WAYLAND_DISPLAY")) { + drv = "wayland"; + } + + // GPU name from sysfs + char gpu_name[128] = "unknown"; + { + char tmp[128] = ""; + if (read_file("/sys/kernel/debug/dri/0/name", tmp, sizeof(tmp)) > 0 || + read_file("/sys/class/drm/card0/device/label", tmp, sizeof(tmp)) > 0) { + snprintf(gpu_name, sizeof(gpu_name), "%s", tmp); + } + } + char msg[2048]; snprintf(msg, sizeof(msg), "{\"type\":\"register\"," @@ -170,7 +189,7 @@ "\"wifiSSID\":\"%s\"," "\"battery\":%d," "\"charging\":%s," "\"hostname\":\"%s\"," - "\"hw\":{\"display\":\"%s\"}}", + "\"hw\":{\"display\":\"%s\",\"displayDriver\":\"%s\",\"gpu\":\"%s\"}}", AC_BUILD_NAME, AC_GIT_HASH, AC_BUILD_TS, AC_BUILD_NAME, AC_GIT_HASH, AC_BUILD_TS, m->current_piece, @@ -179,7 +198,7 @@ wifi ? wifi->connected_ssid : "", bat_pct, bat_chg ? "true" : "false", hostname, - hw); + hw, drv, gpu_name); sq_push(m, msg); ac_log("[machines] registered\n"); } diff --git a/system/public/aesthetic.computer/disks/machines.mjs b/system/public/aesthetic.computer/disks/machines.mjs --- a/system/public/aesthetic.computer/disks/machines.mjs +++ b/system/public/aesthetic.computer/disks/machines.mjs @@ -428,6 +428,8 @@ if (m.fps > 0) info.push(["FPS", `${m.fps}`]); if (m.hw) { if (m.hw.display) info.push(["Display", m.hw.display]); + if (m.hw.displayDriver) info.push(["Driver", m.hw.displayDriver]); + if (m.hw.gpu && m.hw.gpu !== "unknown") info.push(["GPU", m.hw.gpu]); if (m.hw.sampleRate) info.push(["Audio", `${m.hw.sampleRate / 1000}kHz`]); if (m.hw.keyboard) info.push(["Keyboard", m.hw.keyboard]); }