Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ if(RX_SHARED)
"-DRX_INSTALL=OFF (it defaults off for add_subdirectory consumers).")
endif()
set(RX_LIB_TYPE SHARED)
if(WIN32 AND RX_MIMALLOC)
# new_override.cc replaces operator new in the executable only. A DLL cannot
# see an executable's replacement on Windows, so a block allocated in a
# module would be freed through mimalloc. Component builds are for
# iteration, not benchmarks.
message(STATUS "RX_SHARED on Windows: mimalloc off")
set(RX_MIMALLOC OFF CACHE BOOL "" FORCE)
endif()
# Third-party static archives (equilibrium::base, volk, VMA, Jolt, kinema, NRD,
# FSR3) get linked into rx shared objects, so their objects must be PIC.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
Expand Down Expand Up @@ -221,9 +229,16 @@ if(NOT SDL3_FOUND AND TARGET SDL3::SDL3)
endif()
if(NOT SDL3_FOUND AND RX_FETCH_SDL3)
set(SDL_TEST_LIBRARY OFF)
# Static keeps the artifacts self contained, no SDL3.dll shipping.
set(SDL_SHARED OFF)
set(SDL_STATIC ON)
if(RX_SHARED AND WIN32)
# Every DLL that links a static SDL gets its own copy, with its own event
# queue and window list; one SDL3.dll keeps them one.
set(SDL_SHARED ON)
set(SDL_STATIC OFF)
else()
# Static keeps the artifacts self contained, no SDL3.dll shipping.
set(SDL_SHARED OFF)
set(SDL_STATIC ON)
endif()
FetchContent_Declare(SDL3
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
GIT_TAG release-3.2.16
Expand Down Expand Up @@ -259,6 +274,12 @@ if(NOT TARGET equilibrium::base)
# std::memory_order arguments) does not keep it from its cache.
set(EQ_USE_STD_ATOMIC OFF CACHE BOOL "" FORCE)
set(EQ_BUILD_TESTS OFF CACHE BOOL "" FORCE)
# PE gives each DLL its own copy of a static library's globals (the ELF build
# shares them through default visibility), so the Windows component build
# needs base as one DLL for one Option/Feature registry, log and allocator.
if(RX_SHARED AND WIN32)
set(EQ_BASE_SHARED ON CACHE BOOL "" FORCE)
endif()
add_subdirectory(third_party/equilibrium equilibrium EXCLUDE_FROM_ALL)
# base's std::formatter specializations exist for consumers that format base
# strings with <format>; nothing here does, and they drag <format> into every
Expand Down
53 changes: 34 additions & 19 deletions cmake/toolchain-mingw-w64.cmake
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# Cross toolchain: x86_64-w64-mingw32 via the nixpkgs cross gcc wrapper, used
# to build the d3d12 RHI acceptance tests as Windows PEs and run them under
# Wine (box64-emulated on aarch64 hosts). Configure inside `nix develop` so
# dxc/glslang stay the host tools:
# cmake -B build/mingw -G Ninja \
# Cross toolchain: x86_64-w64-mingw32 (ucrt) via the nixpkgs cross gcc
# wrapper. Builds rx as Windows PEs (static, or DLLs with -DRX_SHARED=ON) that
# run under Wine. Configure inside `nix develop .#mingw`, which provides the
# compiler, the Windows libraries and the host shader tools:
# cmake -B build/mingw -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo \
# -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-mingw-w64.cmake \
# -DRX_MODULES=ui/events\;ui/window\;asset\;render -DRX_RHI_VULKAN=OFF -DRX_RHI_D3D12=ON \
# -DRX_BUILD_TESTS=ON -DRX_BUILD_RUNTIME=OFF
# The MINGW_CC/MINGW_MCF paths can be overridden with -DRX_MINGW_CC=... /
# -DRX_MINGW_MCF=... when the store paths differ.
# -DRX_NRD=OFF -DRX_DLSS=OFF -DRX_FSR3=OFF -DRX_MIMALLOC=OFF -DRX_FETCH_SDL3=ON \
# -DRX_RXPACK=$PWD/build/linux/apps/rxpack/rxpack
# RX_RXPACK is a host rxpack that packs the engine archives (Data/*.rxp), which
# the cross-built one cannot run to do. Add -DRX_SHARED=ON for DLLs.
# RX_MINGW_CC, RX_MINGW_MCF, RX_MINGW_MCF_DEV and RX_MINGW_ROOTS come from that
# shell's environment; -D overrides them.
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)

if(NOT DEFINED RX_MINGW_CC)
set(RX_MINGW_CC "/nix/store/ha64priyfaxkdg7ylvwz3pc0p5vw9nfz-x86_64-w64-mingw32-gcc-wrapper-15.2.0")
endif()
if(NOT DEFINED RX_MINGW_MCF)
set(RX_MINGW_MCF "/nix/store/y8iv45r55ryaxgwdxjkbbprmcd8jyl91-mcfgthread-x86_64-w64-mingw32-2.4.1")
endif()
if(NOT DEFINED RX_MINGW_MCF_DEV)
set(RX_MINGW_MCF_DEV "/nix/store/75dflqgi4q9saldsdzpwx56wf6cnpfhv-mcfgthread-x86_64-w64-mingw32-2.4.1-dev")
foreach(_var RX_MINGW_CC RX_MINGW_MCF RX_MINGW_MCF_DEV RX_MINGW_ROOTS)
if(NOT DEFINED ${_var} AND DEFINED ENV{${_var}})
set(${_var} "$ENV{${_var}}")
endif()
endforeach()
if(NOT RX_MINGW_CC OR NOT RX_MINGW_MCF OR NOT RX_MINGW_MCF_DEV)
message(FATAL_ERROR "toolchain-mingw-w64: RX_MINGW_CC/MCF/MCF_DEV unset; "
"configure inside `nix develop .#mingw`")
endif()
# libstdc++'s gthr model on this toolchain is mcfgthread; its headers live in
# the dev output. SHELL: keeps each "-isystem <dir>" pair intact through
Expand All @@ -29,16 +31,29 @@ add_compile_options("SHELL:-isystem ${RX_MINGW_MCF_DEV}/include")
add_compile_options("SHELL:-isystem ${CMAKE_CURRENT_LIST_DIR}/mingw-shims")
# gcc has no MSVC __int64 builtin (equilibrium's minwin.h relies on it).
add_compile_options("-D__int64=long long")
# COFF caps an object at 32k sections; unity sources like harfbuzz.cc exceed it.
add_compile_options(-Wa,-mbig-obj)

set(CMAKE_C_COMPILER "${RX_MINGW_CC}/bin/x86_64-w64-mingw32-gcc")
set(CMAKE_CXX_COMPILER "${RX_MINGW_CC}/bin/x86_64-w64-mingw32-g++")
set(CMAKE_RC_COMPILER "${RX_MINGW_CC}/bin/x86_64-w64-mingw32-windres" CACHE FILEPATH "" )

# mcfgthread ships separately from the gcc wrapper in nixpkgs cross.
add_link_options("-L${RX_MINGW_MCF}/lib")
# CMake's compiler checks re-read this file in a scratch project; forward the
# overrides so they see the same store paths.
list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES RX_MINGW_CC RX_MINGW_MCF RX_MINGW_MCF_DEV RX_MINGW_ROOTS)
# mcfgthread ships separately from the gcc wrapper in nixpkgs cross. *_INIT
# rather than add_link_options so the compiler checks link too.
set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${RX_MINGW_MCF}/lib")
set(CMAKE_SHARED_LINKER_FLAGS_INIT "-L${RX_MINGW_MCF}/lib")
set(CMAKE_MODULE_LINKER_FLAGS_INIT "-L${RX_MINGW_MCF}/lib")
# Self-contained test binaries: no libstdc++/libgcc DLL hunting under Wine.
add_link_options(-static-libstdc++ -static-libgcc)

# Target packages come from the mingw sysroot or are fetched, never from the
# host dev shell (whose SDL3, OpenSSL, ... are Linux builds). The *_MODE_* lines
# below only take effect with a root path set.
set(CMAKE_FIND_ROOT_PATH "${RX_MINGW_CC}" "${RX_MINGW_MCF}" ${RX_MINGW_ROOTS})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
14 changes: 11 additions & 3 deletions docs/STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ out of scope.
|---|---|---|
| `linux-dev` (`build/linux-dev`) | ON | day-to-day work: one DSO per module |
| `linux`, `windows`, `android`, ... | OFF | shipping, benchmarks, install/export |
| mingw cross (`nix develop .#mingw`, toolchain file) | either | Windows builds on Linux, run under Wine |

Benchmarks and captures keep running on the static presets, so the shared build
never skews a performance number.
Expand All @@ -344,9 +345,16 @@ What has to exist once per process, and how the shared build keeps it so:
- log sinks, the feature table, app identity and the memory tracker live in
`rx_foundation` and are reached through exported functions; the allocator
override is compiled into the executable only (`rx_enable_mimalloc`).
- Windows: PE has no unique symbols, so a `windows-dev` preset first needs
equilibrium's base linked as a DLL and the shared CRT (`/MD`) everywhere. Not
done yet.
- Windows: PE has no unique symbols, and every DLL that links a static library
gets its own copy of its globals. So what must be one per process is a DLL
there: equilibrium's base (`EQ_BASE_SHARED`, which also moves the
Option/Feature chain roots out of line), libultragui (`ULTRAGUI_SHARED`) and
SDL3. zetanet, kinema, stb and the imgui core stay static in each DLL that
links them: no state of theirs crosses modules (zetanet's packet pools fall
back to the one CRT heap, and only the executable calls imgui). mimalloc is
off, because an executable's `operator new` replacement does not reach a DLL.
Verified with the mingw cross build (`nix develop .#mingw`,
`cmake/toolchain-mingw-w64.cmake`) under Wine; not yet with MSVC.

## Violations to fix before moving files

Expand Down
25 changes: 25 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,31 @@
fi
'';
};
} // pkgs.lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
# Windows cross build (cmake/toolchain-mingw-w64.cmake): the default
# shell's host tools plus a ucrt64 mingw gcc and Windows builds of the
# libraries rx links. UCRT, not msvcrt, because that
# is the CRT a real Windows build uses (msvcrt lacks _strtof_l).
mingw = let
w = pkgs.pkgsCross.ucrt64;
# freetype and harfbuzz are not here: libultragui builds them from
# source on Windows (their nixpkgs cross builds pull a broken python).
roots = [ w.windows.mcfgthreads w.openssl.dev w.openssl.out ];
in pkgs.mkShell {
inputsFrom = [ self.devShells.${pkgs.stdenv.hostPlatform.system}.default ];
# The cross gcc on PATH too: windres runs it as its preprocessor.
# No wine here: this nixpkgs' wine segfaults inside the dev shell's
# environment; run the PEs with the host's wine.
packages = [ w.buildPackages.gcc ];
RX_MINGW_CC = "${w.buildPackages.gcc}";
RX_MINGW_MCF = "${w.windows.mcfgthreads}";
RX_MINGW_MCF_DEV = "${w.windows.mcfgthreads.dev}";
RX_MINGW_ROOTS = pkgs.lib.concatStringsSep ";" (map toString roots);
# zetanet's find_package(OpenSSL): headers and import libs live in
# different outputs.
OPENSSL_ROOT_DIR = "${w.openssl.dev}:${w.openssl.out}";
shellHook = self.devShells.${pkgs.stdenv.hostPlatform.system}.default.shellHook;
};
});
};
}
3 changes: 2 additions & 1 deletion rxe/net/http/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ if(WIN32)
target_link_libraries(rx_http PRIVATE ws2_32)
endif()

if(RX_BUILD_TESTS)
# Not on Windows: the test's local server speaks POSIX sockets.
if(RX_BUILD_TESTS AND NOT WIN32)
# rx::http unit tests: url parsing, response-head parsing, chunked decoding and
# the refusals that happen before a socket is opened. Nothing here touches the
# network; the live fetch inside it only runs with RX_HTTP_LIVE set.
Expand Down
8 changes: 4 additions & 4 deletions rxe/render/path_motion_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
#include <stdlib.h>
#include <string.h>

#include "base/algorithm.h"
#include "base/memory/mem_ops.h"
#include "foundation/math/scalar.h"
#include "rxe/asset/mesh.h"
#include "rxe/gpu/rhi/device.h"
#include "rxe/render/core/bindless.h"
#include "rxe/render/gi/path_scene_history.h"
#include "rxe/render/gi/skinned_rt.h"
#include "rxe/gpu/rhi/device.h"
#include "shaders/path_motion_recon_cs_hlsl.h"
#ifdef RX_HAS_NRD
#include "base/algorithm.h"
#include "base/memory/mem_ops.h"
#include "foundation/math/scalar.h"
#include "shaders/path_motion_nrd_cs_hlsl.h"
#endif

Expand Down
5 changes: 5 additions & 0 deletions rxe/ui/ugui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ if(NOT TARGET ultragui)
# No STL, no exceptions: ugui's containers are base's (ugui::String is
# base::String), built against rx's own equilibrium::base.
set(ULTRAGUI_USE_BASE ON CACHE BOOL "" FORCE)
# One ultragui for all of rx's DLLs on Windows (see ULTRAGUI_SHARED); ELF
# shares a static copy's globals already.
if(RX_SHARED AND WIN32)
set(ULTRAGUI_SHARED ON CACHE BOOL "" FORCE)
endif()
set(ULTRAGUI_PLATFORM_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/ugui_platform.cc"
CACHE FILEPATH "" FORCE)
set(ULTRAGUI_RHI_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/ugui_rhi.cc"
Expand Down