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<u8> 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 <noreply@anthropic.com>
This commit is contained in:
ToastconcernandClaude Opus 5.5 committed 2026-10-22 10:56:57 -05:00
1 parent dc9cfd9838
commit 9b28f864d3
3 files changed
+22 -11

No files matched your search

+4 -1
View File
@@ -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
+9 -1
View File
@@ -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")
+9 -9
View File
@@ -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<size_t>(w) * h * 4) {
const uint32_t tw = 512, th = std::max<uint32_t>(1, h * tw / w);
std::vector<u8> small(static_cast<size_t>(tw) * th * 4);
std::vector<u8> scaled(static_cast<size_t>(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<size_t>(y * h / th) * w + x * w / tw) * 4];
u8* dst = &small[(static_cast<size_t>(y) * tw + x) * 4];
u8* dst = &scaled[(static_cast<size_t>(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<std::vector<u8>*>(ctx);
v->insert(v->end(), static_cast<u8*>(data), static_cast<u8*>(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<int>(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<u8> small(static_cast<size_t>(tw) * th * 4);
std::vector<u8> scaled(static_cast<size_t>(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<size_t>(yy) * w + xx) * 4 + k];
for (int k = 0; k < 4; ++k) small[(static_cast<size_t>(y) * tw + x) * 4 + k] = static_cast<u8>(k == 3 ? 255 : acc[k] / n);
for (int k = 0; k < 4; ++k) scaled[(static_cast<size_t>(y) * tw + x) * 4 + k] = static_cast<u8>(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<std::vector<u8>*>(ctx);
v->insert(v->end(), static_cast<u8*>(data), static_cast<u8*>(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<int>(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<u8> small(static_cast<size_t>(tw) * th * 4);
std::vector<u8> scaled(static_cast<size_t>(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<size_t>(yy) * w + xx) * 4 + k];
for (int k = 0; k < 4; ++k) small[(static_cast<size_t>(y) * tw + x) * 4 + k] = static_cast<u8>(k == 3 ? 255 : acc[k] / n);
for (int k = 0; k < 4; ++k) scaled[(static_cast<size_t>(y) * tw + x) * 4 + k] = static_cast<u8>(k == 3 ? 255 : acc[k] / n);
}
stbi_image_free(px);
m.thumbView = renderer.createPicture(small.data(), static_cast<uint32_t>(tw), static_cast<uint32_t>(th));
m.thumbView = renderer.createPicture(scaled.data(), static_cast<uint32_t>(tw), static_cast<uint32_t>(th));
if (!m.thumbView) return;
m.thumb = ImGui_ImplVulkan_AddTexture(viewSampler, m.thumbView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m.thumbAspect = static_cast<float>(th) / static_cast<float>(tw);