From 9b28f864d347a43726d31aab05c6dff9bc391b6b Mon Sep 17 00:00:00 2001 From: Toastconcern Date: Thu, 22 Oct 2026 10:56:57 -0500 Subject: [PATCH] ci: fix the Windows and Linux x86 builds Every Windows job and Linux x86 were failing; reproduced each locally (MSVC 18 x64/x86, gcc -m32) and fixed: - studio.cpp: thumbnail buffers were named `small`, which Windows' rpcndr.h #defines to `char`, so MSVC saw `std::vector char(...)`. Renamed to `scaled`. (All three Windows builds.) - 32-bit x86: astcenc's SSE4.1 code calls __builtin_popcountll on 32-bit MSVC, which doesn't exist; 32-bit builds now use astcenc's plain backend. - opus defaulted to the DLL C runtime while the app links the static one; x86 MSVC can't link that mix (__except_handler4_common). OPUS_STATIC_RUNTIME. - 32-bit gcc/clang: turn on SSE2 (-msse2 -mfpmath=sse) as MSVC does; rtm (under ACL) uses SSE intrinsics on any x86. - CI Linux x86: install the i386 X11 dev packages; GLFW couldn't find a 32-bit libX11 to link. The x86 and x64 Windows builds pass the same --selftest checks (97/104; the 7 failures are pre-existing and identical on both). Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 5 ++++- studio/CMakeLists.txt | 10 +++++++++- studio/studio.cpp | 18 +++++++++--------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b55b27..cf1617c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,8 @@ jobs: matrix: include: - { arch: x64, runner: ubuntu-24.04, extra: "" } - - { arch: x86, runner: ubuntu-24.04, extra: "gcc-multilib g++-multilib" } + - { arch: x86, runner: ubuntu-24.04, i386: true, + extra: "gcc-multilib g++-multilib libx11-dev:i386 libxrandr-dev:i386 libxinerama-dev:i386 libxcursor-dev:i386 libxi-dev:i386" } - { arch: arm64, runner: ubuntu-24.04-arm, extra: "" } runs-on: ${{ matrix.runner }} timeout-minutes: 45 @@ -30,6 +31,8 @@ jobs: - uses: actions/checkout@v4 - name: Tools and X11 headers run: | + # x86 links against 32-bit X11, so apt needs the i386 packages too + if [ "${{ matrix.i386 }}" = "true" ]; then sudo dpkg --add-architecture i386; fi sudo apt-get update sudo apt-get install -y ninja-build cmake libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxkbcommon-dev ${{ matrix.extra }} - name: Build diff --git a/studio/CMakeLists.txt b/studio/CMakeLists.txt index 3aee8d7..50d2be7 100644 --- a/studio/CMakeLists.txt +++ b/studio/CMakeLists.txt @@ -19,6 +19,9 @@ if(WIN32) endif() if(MSVC) add_compile_options(/utf-8 /bigobj /W3 /wd4244 /wd4267 /wd4305) +elseif(CMAKE_SIZEOF_VOID_P EQUAL 4 AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(i[3-6]86|x86|X86|x86_64|AMD64)$") + # 32-bit x86 with gcc/clang: SSE2 on, as MSVC has it by default (rtm, under ACL, uses it on any x86) + add_compile_options(-msse2 -mfpmath=sse) endif() add_library(volk STATIC "${TP}/volk/volk.c") @@ -39,7 +42,8 @@ set(BUILD_TESTING OFF CACHE BOOL "" FORCE) set(ASTCENC_CLI OFF CACHE BOOL "" FORCE) set(ASTCENC_DECOMPRESSOR OFF CACHE BOOL "" FORCE) set(ASTCENC_UNIVERSAL_BUILD OFF CACHE BOOL "" FORCE) -# astcenc's SIMD: NEON on ARM (Windows on ARM, Apple Silicon, ARM Linux), SSE4.1 on x86-64. +# astcenc's SIMD: NEON on ARM (Windows on ARM, Apple Silicon, ARM Linux), SSE4.1 on x86-64, none on 32-bit x86 +# (its SSE4.1 code needs 64-bit popcount, which 32-bit MSVC doesn't have). # (on macOS the architecture asked for decides: an Apple Silicon Mac builds the Intel version too) if(CMAKE_OSX_ARCHITECTURES) set(STUDIO_ARCH "${CMAKE_OSX_ARCHITECTURES}") @@ -49,6 +53,9 @@ endif() if(STUDIO_ARCH MATCHES "^(arm64|ARM64|aarch64|AARCH64)$") set(ASTCENC_ISA_NEON ON CACHE BOOL "" FORCE) set(STUDIO_ASTCENC astcenc-neon-static) +elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(ASTCENC_ISA_NONE ON CACHE BOOL "" FORCE) + set(STUDIO_ASTCENC astcenc-none-static) else() set(ASTCENC_ISA_SSE41 ON CACHE BOOL "" FORCE) set(STUDIO_ASTCENC astcenc-sse4.1-static) @@ -60,6 +67,7 @@ set(OPUS_BUILD_PROGRAMS OFF CACHE BOOL "" FORCE) set(OPUS_BUILD_TESTING OFF CACHE BOOL "" FORCE) set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF CACHE BOOL "" FORCE) set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF CACHE BOOL "" FORCE) +set(OPUS_STATIC_RUNTIME ON CACHE BOOL "" FORCE) # same static C runtime as the app (32-bit MSVC can't link a mix) add_subdirectory("${TP}/opus" opus EXCLUDE_FROM_ALL) add_library(ogg STATIC "${TP}/ogg/src/bitwise.c" "${TP}/ogg/src/framing.c") target_include_directories(ogg PUBLIC "${TP}/ogg/include") diff --git a/studio/studio.cpp b/studio/studio.cpp index 54b7a69..808894f 100644 --- a/studio/studio.cpp +++ b/studio/studio.cpp @@ -2333,17 +2333,17 @@ struct Studio { const uint32_t w = renderer.width(), h = renderer.height(); if (rendered && w > 8 && h > 8 && renderer.readback(0, rgba) && rgba.size() >= static_cast(w) * h * 4) { const uint32_t tw = 512, th = std::max(1, h * tw / w); - std::vector small(static_cast(tw) * th * 4); + std::vector scaled(static_cast(tw) * th * 4); for (uint32_t y = 0; y < th; ++y) for (uint32_t x = 0; x < tw; ++x) { const u8* src = &rgba[(static_cast(y * h / th) * w + x * w / tw) * 4]; - u8* dst = &small[(static_cast(y) * tw + x) * 4]; + u8* dst = &scaled[(static_cast(y) * tw + x) * 4]; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = 255; } stbi_write_png_to_func([](void* ctx, void* data, int size) { auto* v = static_cast*>(ctx); v->insert(v->end(), static_cast(data), static_cast(data) + size); - }, &png, tw, th, 4, small.data(), tw * 4); + }, &png, tw, th, 4, scaled.data(), tw * 4); } cam = kept; return png; @@ -2359,7 +2359,7 @@ struct Studio { unsigned char* px = bytes.empty() ? nullptr : stbi_load_from_memory(bytes.data(), static_cast(bytes.size()), &w, &h, &c, 4); if (!px) return png; const int tw = std::min(512, w), th = std::max(1, h * tw / std::max(1, w)); - std::vector small(static_cast(tw) * th * 4); + std::vector scaled(static_cast(tw) * th * 4); for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) { // box filter over the source pixels each covers const int x0 = x * w / tw, x1 = std::max(x0 + 1, (x + 1) * w / tw), y0 = y * h / th, y1 = std::max(y0 + 1, (y + 1) * h / th); @@ -2367,13 +2367,13 @@ struct Studio { for (int yy = y0; yy < y1; ++yy) for (int xx = x0; xx < x1; ++xx, ++n) for (int k = 0; k < 4; ++k) acc[k] += px[(static_cast(yy) * w + xx) * 4 + k]; - for (int k = 0; k < 4; ++k) small[(static_cast(y) * tw + x) * 4 + k] = static_cast(k == 3 ? 255 : acc[k] / n); + for (int k = 0; k < 4; ++k) scaled[(static_cast(y) * tw + x) * 4 + k] = static_cast(k == 3 ? 255 : acc[k] / n); } stbi_image_free(px); stbi_write_png_to_func([](void* ctx, void* data, int size) { auto* v = static_cast*>(ctx); v->insert(v->end(), static_cast(data), static_cast(data) + size); - }, &png, tw, th, 4, small.data(), tw * 4); + }, &png, tw, th, 4, scaled.data(), tw * 4); return png; } @@ -7568,7 +7568,7 @@ struct Studio { unsigned char* px = stbi_load_from_memory(png.data(), static_cast(png.size()), &w, &h, &c, 4); if (!px) return; const int tw = std::min(160, w), th = std::max(1, h * tw / std::max(1, w)); - std::vector small(static_cast(tw) * th * 4); + std::vector scaled(static_cast(tw) * th * 4); for (int y = 0; y < th; ++y) for (int x = 0; x < tw; ++x) { // box filter over the source pixels each covers const int x0 = x * w / tw, x1 = std::max(x0 + 1, (x + 1) * w / tw), y0 = y * h / th, y1 = std::max(y0 + 1, (y + 1) * h / th); @@ -7576,10 +7576,10 @@ struct Studio { for (int yy = y0; yy < y1; ++yy) for (int xx = x0; xx < x1; ++xx, ++n) for (int k = 0; k < 4; ++k) acc[k] += px[(static_cast(yy) * w + xx) * 4 + k]; - for (int k = 0; k < 4; ++k) small[(static_cast(y) * tw + x) * 4 + k] = static_cast(k == 3 ? 255 : acc[k] / n); + for (int k = 0; k < 4; ++k) scaled[(static_cast(y) * tw + x) * 4 + k] = static_cast(k == 3 ? 255 : acc[k] / n); } stbi_image_free(px); - m.thumbView = renderer.createPicture(small.data(), static_cast(tw), static_cast(th)); + m.thumbView = renderer.createPicture(scaled.data(), static_cast(tw), static_cast(th)); if (!m.thumbView) return; m.thumb = ImGui_ImplVulkan_AddTexture(viewSampler, m.thumbView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); m.thumbAspect = static_cast(th) / static_cast(tw);