diff --git a/.github/workflows/pr_build.yml b/.github/workflows/pr_build.yml index 168cf1ab786..640edf3753e 100644 --- a/.github/workflows/pr_build.yml +++ b/.github/workflows/pr_build.yml @@ -41,14 +41,14 @@ jobs: # build_type: full # qt_source: aqt - os: Ubuntu 22.04 OpenGL - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 # Container image used for building. Built from /tools/ci-script/linux-ci/Dockerfile_x + image: docker.io/overte/overte-full-build:2026-06-05-ubuntu-22.04-amd64 # Container image used for building. Built from /tools/ci-script/linux-ci/Dockerfile_x runner: ubuntu-latest arch: amd64 build_type: full qt_source: system rendering_backend: OpenGL - os: Ubuntu 22.04 Vulkan - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 + image: docker.io/overte/overte-full-build:2026-06-05-ubuntu-22.04-amd64 runner: ubuntu-latest arch: amd64 build_type: full @@ -60,14 +60,14 @@ jobs: # apt-dependencies: mesa-common-dev libegl1 libglvnd-dev libdouble-conversion1 libpulse0 python3-github python3-distro # Do not change the names of self-hosted runners without knowing what you are doing, as they correspond to labels that have to be set on the runner. - os: Ubuntu 22.04 OpenGL ES - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-aarch64 + image: docker.io/overte/overte-full-build:2026-06-05-ubuntu-22.04-aarch64 runner: ubuntu-24.04-arm arch: aarch64 build_type: full qt_source: system rendering_backend: GLES - os: Ubuntu 22.04 Vulkan - image: docker.io/overte/overte-full-build:2026-02-09-ubuntu-22.04-aarch64 + image: docker.io/overte/overte-full-build:2026-06-05-ubuntu-22.04-aarch64 runner: ubuntu-24.04-arm arch: aarch64 build_type: full diff --git a/.gitignore b/.gitignore index 7d0ed694882..dcd7e795440 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ pkglist.json **/__pycache__ **/.venv +# Rust/Cargo +/target # Android Studio *.iml diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000..49a20255cb8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,323 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cxx" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "ovlib-render-utils" +version = "2026.1.1" +dependencies = [ + "crc32fast", + "cxx", + "cxx-build", + "itertools", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000000..e88953c65c0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +resolver = "2" +members = [ + "libraries/render-utils" +] + +[workspace.package] +version = "2026.1.1" +edition = "2024" +license = "apache-2.0" + +[workspace.dependencies] +cxx = "1.0" +cxx-build = "1.0" +itertools = "0.14.0" + diff --git a/cmake/init.cmake b/cmake/init.cmake index e94269095d1..9adef6ee665 100644 --- a/cmake/init.cmake +++ b/cmake/init.cmake @@ -57,4 +57,7 @@ if (UNIX) set(BUILD_SHARED_LIBS ON) endif () -find_package(Python3 REQUIRED) \ No newline at end of file +find_package(Python3 REQUIRED) + +set(CARGO_MANIFEST ${CMAKE_SOURCE_DIR}/Cargo.toml) +set(CARGO_TARGET_DIR ${CMAKE_SOURCE_DIR}/target) diff --git a/cmake/macros/AddCppRustBridge.cmake b/cmake/macros/AddCppRustBridge.cmake new file mode 100644 index 00000000000..4735d0dca10 --- /dev/null +++ b/cmake/macros/AddCppRustBridge.cmake @@ -0,0 +1,117 @@ +# +# Copyright 2026 Overte e.V. +# Created by RTUnreal on 2026/03/13 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + + +# basic setup of a new crate: +# +# 1. create these files (replace `${TARGET_NAME}` with the library name): +# `libraries/${TARGET_NAME}/Cargo.toml`: +# ```toml +# [package] +# name = "ovlib-${TARGET_NAME}" +# version.workspace = true +# edition.workspace = true +# license.workspace = true +# +# [lib] +# crate-type = ["staticlib"] +# path = "src/${TARGET_NAME}.rs" +# +# [dependencies] +# cxx.workspace = true +# +# [build-dependencies] +# cxx-build.workspace = true +# ``` +# +# `libraries/${TARGET_NAME}/build.rs`: +# ```rs +# #[allow(unused_must_use)] +# fn main() { +# cxx_build::bridge("src/${TARGET_NAME}.rs"); +# println!("cargo:rerun-if-changed=src/${TARGET_NAME}.rs"); +# } +# ``` +# +# `libraries/${TARGET_NAME}/src/${TARGET_NAME}.rs`: +# ```rs +# #[cxx::bridge(namespace = "image::rust")] +# mod ffi { +# // Rust types and signatures exposed to C++. +# extern "Rust" { +# } +# +# // C++ types and signatures exposed to Rust. +# unsafe extern "C++" {} +# } +# ``` +# +# 2. add rust bridge generator by adding the following line to `CMakeLists.txt`: +# `libraries/${TARGET_NAME}/CMakeLists.txt`: +# ```cmake +# add_cpp_rust_bridge() +# ``` +# +# 3. add the new rust crate to to the rust workspace by adding Cargo.toml dir to the Cargo.toml in the root directory: +# +# ```toml +# [workspace] +# members = [ +# ... +# "libraries/${TARGET_NAME}", +# ... +# ] +# ``` +# +# 3. use the rust code from C++ by including the header file from any cpp file: +# +# ```cpp +# #include <${TARGET_NAME}.rs.h> +# ``` +# +macro(add_cpp_rust_bridge) + set(${TARGET_NAME}_RUST_LIB_NAME ovlib-${TARGET_NAME}) + set(${TARGET_NAME}_RUST_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/${TARGET_NAME}.rs) + set(${TARGET_NAME}_RUST_SOURCE_DIR ${CARGO_TARGET_DIR}/cxxbridge/${${TARGET_NAME}_RUST_LIB_NAME}/src) + set(${TARGET_NAME}_RUST_BRIDGE_CPP ${${TARGET_NAME}_RUST_SOURCE_DIR}/${TARGET_NAME}.rs.cc) + # RUSTTODO: make use of release as well + string(REGEX REPLACE "-" "_" ${TARGET_NAME}_RUST_LIB_FILE_NAME "${TARGET_NAME}") + set(${TARGET_NAME}_RUST_LIB ${CARGO_TARGET_DIR}/debug/${CMAKE_STATIC_LIBRARY_PREFIX}ovlib_${${TARGET_NAME}_RUST_LIB_FILE_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}) + + # Add a custom command that builds the rust crate and generates C++ bridge code + add_custom_command( + OUTPUT ${${TARGET_NAME}_RUST_LIB} ${${TARGET_NAME}_RUST_BRIDGE_CPP} + COMMAND cargo build --manifest-path ${CARGO_MANIFEST} -p ${${TARGET_NAME}_RUST_LIB_NAME} + DEPENDS ${${TARGET_NAME}_RUST_SOURCE_FILE} + USES_TERMINAL + COMMENT "Running cargo for ${${TARGET_NAME}_RUST_LIB_NAME} ..." + ) + + target_sources(${TARGET_NAME} PRIVATE ${${TARGET_NAME}_RUST_BRIDGE_CPP}) + target_include_directories(${TARGET_NAME} PRIVATE ${${TARGET_NAME}_RUST_SOURCE_DIR}/) + + target_link_libraries(${TARGET_NAME} ${${TARGET_NAME}_RUST_LIB}) + + # RUSTTODO: make these usable + #set_target_properties( + # cpp_with_rust + # PROPERTIES ADDITIONAL_CLEAN_FILES ${CARGO_TARGET_DIR} + #) + + ## Windows-only configuration + #if(WIN32) + # target_link_libraries(cpp_with_rust userenv ws2_32 bcrypt) + # set_target_properties( + # cpp_with_rust + # PROPERTIES + # MSVC_RUNTIME_LIBRARY "MultiThreadedDLL" + # RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR} + # RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR} + # ) + #endif() +endmacro() diff --git a/cmake/macros/TargetAFF.cmake b/cmake/macros/TargetAFF.cmake deleted file mode 100644 index 4ecf3f93952..00000000000 --- a/cmake/macros/TargetAFF.cmake +++ /dev/null @@ -1,9 +0,0 @@ -macro(TARGET_AFF) - if(OVERTE_USE_SYSTEM_LIBS) - find_path(AFF_INCLUDE_DIRS "artery-font/artery-font.h") - target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${AFF_INCLUDE_DIRS}) - else() - find_package(artery-font-format REQUIRED) - target_link_libraries(${TARGET_NAME} artery-font-format::artery-font-format) - endif() -endmacro() diff --git a/flake.lock b/flake.lock index cc94b9578f5..46389365fe6 100644 --- a/flake.lock +++ b/flake.lock @@ -49,10 +49,45 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1778383025, + "narHash": "sha256-UK7s2LJS1YwIMFL7PSaNJvLXT9pyRgm7X+HNPgMXiEE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "4568a557ca325ff81fb354382d4a9968daa1001a", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index abe2a8c8b01..4e888ad6a21 100644 --- a/flake.nix +++ b/flake.nix @@ -2,6 +2,7 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; + rust-overlay.url = "github:oxalica/rust-overlay"; }; outputs = inputs: @@ -17,20 +18,28 @@ perSystem = { pkgs, + system, lib, self', inputs', ... }: { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ + (import inputs.rust-overlay) + (self: _super: { + rust-overte = self.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + }) + ]; + }; packages = { glad = pkgs.callPackage ./nix/glad.nix { }; etc2comp = pkgs.callPackage ./nix/etc2comp.nix { }; cgltf = pkgs.callPackage ./nix/cgltf.nix { }; - artery-font-format = pkgs.callPackage ./nix/artery-font-format.nix { }; - gif_creator = pkgs.callPackage ./nix/gif_creator.nix { }; scribe = pkgs.callPackage ./nix/scribe.nix { }; @@ -40,7 +49,6 @@ glad scribe gif_creator - artery-font-format cgltf etc2comp draco @@ -61,7 +69,11 @@ pkgs.conan pkgs.ninja pkgs.gdb + pkgs.nixd + pkgs.nixfmt pkgs.clang-tools + pkgs.llvmPackages_20.clang-unwrapped + pkgs.rust-overte ]; inputsFrom = [ self'.packages.overte-full ]; diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 971d09fbb65..b437f2e28a0 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -16,8 +16,7 @@ include_hifi_library_headers(octree) include_hifi_library_headers(script-engine) include_hifi_library_headers(hfm) -# Artery Font Format -target_aff() +add_cpp_rust_bridge() # tell CMake to exclude qrc_fonts.cpp for policy CMP0071 set_property(SOURCE qrc_fonts.cpp PROPERTY SKIP_AUTOMOC ON) diff --git a/libraries/render-utils/Cargo.toml b/libraries/render-utils/Cargo.toml new file mode 100644 index 00000000000..e96e4beebe0 --- /dev/null +++ b/libraries/render-utils/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ovlib-render-utils" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lib] +crate-type = ["staticlib"] +path = "src/render-utils.rs" + +[dependencies] +crc32fast = "1.5.0" +cxx.workspace = true +itertools.workspace = true + +[build-dependencies] +cxx-build.workspace = true diff --git a/libraries/render-utils/build.rs b/libraries/render-utils/build.rs new file mode 100644 index 00000000000..17a1b2d0aee --- /dev/null +++ b/libraries/render-utils/build.rs @@ -0,0 +1,5 @@ +#[allow(unused_must_use)] +fn main() { + cxx_build::bridge("src/render-utils.rs"); + println!("cargo:rerun-if-changed=src/render-utils.rs"); +} diff --git a/libraries/render-utils/src/render-utils.rs b/libraries/render-utils/src/render-utils.rs new file mode 100644 index 00000000000..06ebdac81a2 --- /dev/null +++ b/libraries/render-utils/src/render-utils.rs @@ -0,0 +1,554 @@ +#[cxx::bridge(namespace = "render_utils::rust")] +mod ffi { + + // NOTE: This only implements the float variant + struct Arfont { + metadata_format: MetadataFormat, + metadata: String, + variants: Vec, + images: Vec, + appendices: Vec, + } + enum ArfontError { + Ok, + UnexpectedEOF, + BrokenMagic, + MalformedFile, + MalformedString, + Unknown, + } + #[repr(u32)] + enum CodepointType { + CP_UNSPECIFIED = 0, + CP_UNICODE = 1, + CP_INDEXED = 2, + CP_ICONOGRAPHIC = 14, + } + + #[repr(u32)] + enum MetadataFormat { + METADATA_NONE = 0, + METADATA_PLAINTEXT = 1, + METADATA_JSON = 2, + } + + #[repr(u32)] + enum ImageType { + IMAGE_NONE = 0, + IMAGE_SRGB_IMAGE = 1, + IMAGE_LINEAR_MASK = 2, + IMAGE_MASKED_SRGB_IMAGE = 3, + IMAGE_SDF = 4, + IMAGE_PSDF = 5, + IMAGE_MSDF = 6, + IMAGE_MTSDF = 7, + IMAGE_MIXED_CONTENT = 255, + } + #[repr(u32)] + enum PixelFormat { + PIXEL_UNKNOWN = 0, + PIXEL_BOOLEAN1 = 1, + PIXEL_UNSIGNED8 = 8, + PIXEL_FLOAT32 = 32, + } + + #[repr(u32)] + enum ImageEncoding { + IMAGE_UNKNOWN_ENCODING = 0, + IMAGE_RAW_BINARY = 1, + IMAGE_BMP = 4, + IMAGE_TIFF = 5, + IMAGE_PNG = 8, + IMAGE_TGA = 9, + } + + #[repr(i32)] + enum ImageOrientation { + ORIENTATION_TOP_DOWN = 1, + ORIENTATION_BOTTOM_UP = -1, + } + + struct Bounds { + pub l: f32, + pub b: f32, + pub r: f32, + pub t: f32, + } + struct Advance { + pub h: f32, + pub v: f32, + } + struct Glyph { + pub codepoint: u32, + pub image: u32, + pub plane_bounds: Bounds, + pub image_bounds: Bounds, + pub advance: Advance, + } + struct KernPair { + codepoint1: u32, + codepoint2: u32, + advance: Advance, + } + struct Metrics { + // In pixels: + fontSize: f32, + distanceRange: f32, + // Proportional to font size: + emSize: f32, + ascender: f32, + descender: f32, + lineHeight: f32, + underlineY: f32, + underlineThickness: f32, + // In pixels: + distanceRangeMiddle: f32, + } + struct RawBinaryFormat { + row_length: u32, + orientation: ImageOrientation, + } + + struct FontVariant { + flags: u32, + weight: u32, + codepoint_type: CodepointType, + image_type: ImageType, + fallback_variant: u32, + fallback_glyph: u32, + metrics: Metrics, + name: String, + metadata: String, + glyphs: Vec, + kern_pairs: Vec, + } + struct FontImage { + flags: u32, + encoding: ImageEncoding, + width: u32, + height: u32, + channels: u32, + pixel_format: PixelFormat, + image_type: ImageType, + raw_binary_format: RawBinaryFormat, + child_images: u32, + texture_flags: u32, + metadata: String, + data: Vec, + } + struct FontAppendix { + metadata: String, + data: Vec, + } + + // Rust types and signatures exposed to C++. + extern "Rust" { + fn decode_arfont(input: &CxxVector, out: &mut Arfont) -> ArfontError; + } + + // C++ types and signatures exposed to Rust. + unsafe extern "C++" {} +} + +use cxx::CxxVector; +use std::io::Read; + +use crate::ffi::{Arfont, ArfontError}; + +impl From for ArfontError { + fn from(v: std::io::ErrorKind) -> Self { + use std::io::ErrorKind; + match v { + ErrorKind::UnexpectedEof => Self::UnexpectedEOF, + _ => todo!(), + } + } +} + +impl From<[f32; 32]> for ffi::Metrics { + fn from(val: [f32; 32]) -> Self { + ffi::Metrics { + fontSize: val[0], + distanceRange: val[1], + emSize: val[2], + ascender: val[3], + descender: val[4], + lineHeight: val[5], + underlineY: val[6], + underlineThickness: val[7], + distanceRangeMiddle: val[8], + } + } +} + +impl From for ArfontError { + fn from(v: std::io::Error) -> Self { + v.into() + } +} + +struct CRC32Reader { + inner: R, + crc32: crc32fast::Hasher, + counter: usize, +} + +impl CRC32Reader { + pub fn new(inner: R) -> Self { + Self { + inner, + crc32: crc32fast::Hasher::new(), + counter: 0, + } + } + pub fn get_counter(&self) -> usize { + self.counter + } + pub fn read_u32(&mut self) -> std::io::Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(u32::from_le_bytes(buf)) + } + pub fn read_f32(&mut self) -> std::io::Result { + let mut buf = [0u8; 4]; + self.read_exact(&mut buf)?; + Ok(f32::from_le_bytes(buf)) + } + pub fn read_bytes(&mut self, len: usize) -> std::io::Result> { + let mut res = vec![0; len]; + self.read_exact(&mut res)?; + Ok(res) + } + pub fn read_string(&mut self, len: usize) -> Result { + if len > 0 { + let buf = self.read_bytes(len)?; + self.realign()?; + String::from_utf8(buf).map_err(|_| ffi::ArfontError::MalformedString) + } else { + Ok(String::new()) + } + } + pub fn read_bounds(&mut self) -> std::io::Result { + let l = self.read_f32()?; + let b = self.read_f32()?; + let r = self.read_f32()?; + let t = self.read_f32()?; + Ok(ffi::Bounds { l, b, r, t }) + } + pub fn read_advance(&mut self) -> std::io::Result { + let h = self.read_f32()?; + let v = self.read_f32()?; + Ok(ffi::Advance { h, v }) + } + pub fn realign(&mut self) -> std::io::Result<()> { + if self.counter & 0x3 != 0 { + let len = 0x4 - (self.counter & 0x3); + let mut a = [0u8; 4]; + self.read_exact(&mut a[0..len])?; + } + Ok(()) + } + + pub fn finish(self) -> (u32, R, usize) { + let res = self.crc32.finalize(); + (res, self.inner, self.counter) + } +} + +impl Read for CRC32Reader { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let len = self.inner.read(buf)?; + self.crc32.update(&buf[0..len]); + self.counter += len; + Ok(len) + } +} + +fn decode_arfont_inner(input: &CxxVector) -> Result { + let mut reader = CRC32Reader::new(input.as_slice()); + + { + let mut tag = [0u8; 16]; + reader.read_exact(&mut tag)?; + if &tag != b"ARTERY/FONT\0\0\0\0\0" { + return Err(ArfontError::BrokenMagic); + } + let magic_no = reader.read_u32()?; + if magic_no != 0x4d276a5c { + return Err(ArfontError::BrokenMagic); + } + } + let _version = reader.read_u32()?; + let _flags = reader.read_u32()?; + { + let real_type = reader.read_u32()?; + if real_type != 0x14 { + return Err(ArfontError::BrokenMagic); + } + } + for _ in 0..4 { + let _reserved = reader.read_u32()?; + } + let metadata_format = { + let i = reader.read_u32()?; + match i { + 0 => ffi::MetadataFormat::METADATA_NONE, + 1 => ffi::MetadataFormat::METADATA_PLAINTEXT, + 2 => ffi::MetadataFormat::METADATA_JSON, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let metadata_length = reader.read_u32()?; + + let variant_count = reader.read_u32()?; + let variant_length = reader.read_u32()?; + let image_count = reader.read_u32()?; + let image_length = reader.read_u32()?; + let appendix_count = reader.read_u32()?; + let appendix_length = reader.read_u32()?; + for _ in 0..8 { + let _reserved = reader.read_u32()?; + } + let metadata = reader.read_string(metadata_length as usize)?; + + let prev_length = reader.get_counter(); + + let mut variants: Vec = Vec::with_capacity(variant_count as usize); + for _ in 0..variant_count { + let flags = reader.read_u32()?; + let weight = reader.read_u32()?; + let codepoint_type = { + let i = reader.read_u32()?; + match i { + 0 => ffi::CodepointType::CP_UNSPECIFIED, + 1 => ffi::CodepointType::CP_UNICODE, + 2 => ffi::CodepointType::CP_INDEXED, + 14 => ffi::CodepointType::CP_ICONOGRAPHIC, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let image_type = { + let i = reader.read_u32()?; + match i { + 0 => ffi::ImageType::IMAGE_NONE, + 1 => ffi::ImageType::IMAGE_SRGB_IMAGE, + 2 => ffi::ImageType::IMAGE_LINEAR_MASK, + 3 => ffi::ImageType::IMAGE_MASKED_SRGB_IMAGE, + 4 => ffi::ImageType::IMAGE_SDF, + 5 => ffi::ImageType::IMAGE_PSDF, + 6 => ffi::ImageType::IMAGE_MSDF, + 7 => ffi::ImageType::IMAGE_MTSDF, + 255 => ffi::ImageType::IMAGE_MIXED_CONTENT, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let fallback_variant = reader.read_u32()?; + let fallback_glyph = reader.read_u32()?; + for _ in 0..6 { + let _reserved = reader.read_u32()?; + } + let mut metrics = [0.0; 32]; + for i in 0..32 { + metrics[i] = reader.read_f32()?; + } + let metrics = metrics.into(); + let name_length = reader.read_u32()?; + let metadata_length = reader.read_u32()?; + let glyph_count = reader.read_u32()?; + let kern_pair_count = reader.read_u32()?; + + let name = reader.read_string(name_length as usize)?; + let metadata = reader.read_string(metadata_length as usize)?; + + let mut glyphs = Vec::with_capacity(glyph_count as usize); + for _ in 0..glyph_count { + let codepoint = reader.read_u32()?; + let image = reader.read_u32()?; + let plane_bounds = reader.read_bounds()?; + let image_bounds = reader.read_bounds()?; + let advance = reader.read_advance()?; + glyphs.push(ffi::Glyph { + codepoint, + image, + plane_bounds, + image_bounds, + advance, + }); + } + + let mut kern_pairs = Vec::with_capacity(kern_pair_count as usize); + for _ in 0..kern_pair_count { + let codepoint1 = reader.read_u32()?; + let codepoint2 = reader.read_u32()?; + let advance = reader.read_advance()?; + kern_pairs.push(ffi::KernPair { + codepoint1, + codepoint2, + advance, + }); + } + variants.push(ffi::FontVariant { + flags, + weight, + codepoint_type, + image_type, + fallback_variant, + fallback_glyph, + metrics, + name, + metadata, + glyphs, + kern_pairs, + }); + } + + let total_length = reader.get_counter(); + + if total_length - prev_length != variant_length as usize { + return Err(ffi::ArfontError::MalformedFile); + } + let prev_length = total_length; + + let mut images = Vec::with_capacity(image_count as usize); + for _ in 0..image_count { + let flags = reader.read_u32()?; + let encoding = { + let i = reader.read_u32()?; + match i { + 0 => ffi::ImageEncoding::IMAGE_UNKNOWN_ENCODING, + 1 => ffi::ImageEncoding::IMAGE_RAW_BINARY, + 4 => ffi::ImageEncoding::IMAGE_BMP, + 5 => ffi::ImageEncoding::IMAGE_TIFF, + 8 => ffi::ImageEncoding::IMAGE_PNG, + 9 => ffi::ImageEncoding::IMAGE_TGA, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let width = reader.read_u32()?; + let height = reader.read_u32()?; + let channels = reader.read_u32()?; + let pixel_format = { + let i = reader.read_u32()?; + match i { + 0 => ffi::PixelFormat::PIXEL_UNKNOWN, + 1 => ffi::PixelFormat::PIXEL_BOOLEAN1, + 8 => ffi::PixelFormat::PIXEL_UNSIGNED8, + 32 => ffi::PixelFormat::PIXEL_FLOAT32, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let image_type = { + let i = reader.read_u32()?; + match i { + 0 => ffi::ImageType::IMAGE_NONE, + 1 => ffi::ImageType::IMAGE_SRGB_IMAGE, + 2 => ffi::ImageType::IMAGE_LINEAR_MASK, + 3 => ffi::ImageType::IMAGE_MASKED_SRGB_IMAGE, + 4 => ffi::ImageType::IMAGE_SDF, + 5 => ffi::ImageType::IMAGE_PSDF, + 6 => ffi::ImageType::IMAGE_MSDF, + 7 => ffi::ImageType::IMAGE_MTSDF, + 255 => ffi::ImageType::IMAGE_MIXED_CONTENT, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let row_length = reader.read_u32()?; + let orientation = { + let i = reader.read_u32()? as i32; + match i { + // XXX: This is a fix, bcs some file use 0 for whatever reason + 0 | 1 => ffi::ImageOrientation::ORIENTATION_TOP_DOWN, + -1 => ffi::ImageOrientation::ORIENTATION_BOTTOM_UP, + _ => return Err(ffi::ArfontError::MalformedFile), + } + }; + let child_images = reader.read_u32()?; + let texture_flags = reader.read_u32()?; + let _ = reader.read_u32()?; + let _ = reader.read_u32()?; + let _ = reader.read_u32()?; + let metadata_length = reader.read_u32()?; + let data_length = reader.read_u32()?; + let metadata = reader.read_string(metadata_length as usize)?; + let data = reader.read_bytes(data_length as usize)?; + reader.realign()?; + images.push(ffi::FontImage { + flags, + encoding, + width, + height, + channels, + pixel_format, + image_type, + raw_binary_format: ffi::RawBinaryFormat { + row_length, + orientation, + }, + child_images, + texture_flags, + metadata, + data, + }); + } + + let total_length = reader.get_counter(); + + if total_length - prev_length != image_length as usize { + return Err(ffi::ArfontError::MalformedFile); + } + let prev_length = total_length; + + let mut appendices = Vec::with_capacity(appendix_count as usize); + for _ in 0..appendix_count { + let metadata_length = reader.read_u32()?; + let data_length = reader.read_u32()?; + let metadata = reader.read_string(metadata_length as usize)?; + let data = reader.read_bytes(data_length as usize)?; + appendices.push(ffi::FontAppendix { metadata, data }); + } + + let total_length = reader.get_counter(); + + if total_length - prev_length != appendix_length as usize { + return Err(ffi::ArfontError::MalformedFile); + } + + { + let _salt = reader.read_u32()?; + let magic_no = reader.read_u32()?; + if magic_no != 0x55ccb363 { + return Err(ffi::ArfontError::BrokenMagic); + } + let _reserved = reader.read_u32()?; + let _reserved = reader.read_u32()?; + let _reserved = reader.read_u32()?; + let _reserved = reader.read_u32()?; + let total_length = reader.read_u32()?; + let (checksum, mut reader, final_counter) = reader.finish(); + let mut a = [0u8; 4]; + reader.read_exact(&mut a)?; + let cs2 = u32::from_le_bytes(a); + if !checksum != cs2 || total_length as usize != final_counter + 4 { + println!("ASDFASDF crc32 {:x} {:x}", checksum, cs2); + return Err(ffi::ArfontError::MalformedFile); + } + } + + Ok(ffi::Arfont { + metadata_format, + metadata, + variants, + images, + appendices, + }) +} + +fn decode_arfont(input: &CxxVector, out: &mut Arfont) -> ArfontError { + match decode_arfont_inner(input) { + Ok(res) => { + *out = res; + ArfontError::Ok + } + Err(e) => e, + } +} diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 710a3f63e1b..e3b6f4d54c7 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -14,9 +14,9 @@ #include #include #include +#include -#include "artery-font/artery-font.h" -#include "artery-font/std-artery-font.h" +#include #include @@ -43,7 +43,8 @@ struct TextureVertex { glm::vec4 bounds; float isTofu; TextureVertex() {} - TextureVertex(const glm::vec2& pos, const glm::vec2& tex, const glm::vec4& bounds, bool isTofu) : pos(pos), tex(tex), bounds(bounds), isTofu(isTofu ? 1.0f : 0.0f) {} + TextureVertex(const glm::vec2& pos, const glm::vec2& tex, const glm::vec4& bounds, bool isTofu) : + pos(pos), tex(tex), bounds(bounds), isTofu(isTofu ? 1.0f : 0.0f) {} }; static const int NUMBER_OF_INDICES_PER_QUAD = 6; // 1 quad = 2 triangles @@ -72,14 +73,10 @@ struct QuadBuilder { } // min = bottomLeft - vertices[0] = TextureVertex(min, - texMin + glm::vec2(0.0f, texSize.y), bounds, glyph.isTofu); - vertices[1] = TextureVertex(min + glm::vec2(size.x, 0.0f), - texMin + texSize, bounds, glyph.isTofu); - vertices[2] = TextureVertex(min + glm::vec2(0.0f, size.y), - texMin, bounds, glyph.isTofu); - vertices[3] = TextureVertex(min + size, - texMin + glm::vec2(texSize.x, 0.0f), bounds, glyph.isTofu); + vertices[0] = TextureVertex(min, texMin + glm::vec2(0.0f, texSize.y), bounds, glyph.isTofu); + vertices[1] = TextureVertex(min + glm::vec2(size.x, 0.0f), texMin + texSize, bounds, glyph.isTofu); + vertices[2] = TextureVertex(min + glm::vec2(0.0f, size.y), texMin, bounds, glyph.isTofu); + vertices[3] = TextureVertex(min + size, texMin + glm::vec2(texSize.x, 0.0f), bounds, glyph.isTofu); } }; @@ -96,7 +93,7 @@ void Font::handleFontNetworkReply() { if (requestReply->error() == QNetworkReply::NoError) { read(*requestReply); } else { - qDebug() << "Error downloading " << requestReply->url() << " - " << requestReply->errorString(); + qCDebug(renderutils) << "Error downloading " << requestReply->url() << " - " << requestReply->errorString(); } } @@ -104,14 +101,15 @@ QThreadStorage _readOffset; QThreadStorage _readMax; int readHelper(void* dst, int length, void* data) { if (!dst) { - qWarning() << "readHelper called with NULL pointer as first argument: dst = " << dst << "; length = " << length << "; data =" << data; + qCWarning(renderutils) << "readHelper called with NULL pointer as first argument: dst = " << dst + << "; length = " << length << "; data =" << data; return 0; } if (_readOffset.localData() + length > _readMax.localData()) { return -1; } - memcpy(dst, (char *)data + _readOffset.localData(), length); + memcpy(dst, (char*)data + _readOffset.localData(), length); _readOffset.setLocalData(_readOffset.localData() + length); return length; }; @@ -122,16 +120,17 @@ void Font::read(QIODevice& in) { QByteArray data = in.readAll(); _readOffset.setLocalData(0); _readMax.setLocalData(data.length()); - artery_font::StdArteryFont arteryFont; - bool success = artery_font::decode<&readHelper, float, artery_font::StdList, artery_font::StdByteArray, artery_font::StdString>(arteryFont, (void *)data.data()); + std::vector vecData(data.data(), data.data() + data.length()); + render_utils::rust::Arfont arteryFont; + render_utils::rust::ArfontError res = render_utils::rust::decode_arfont(vecData, arteryFont); - if (!success) { - qDebug() << "Font" << _family << "failed to decode."; + if (res != render_utils::rust::ArfontError::Ok) { + qCDebug(renderutils) << "Font" << _family << "failed to decode. Errcode:" << (uint32_t)res; return; } - if (arteryFont.variants.length() == 0) { - qDebug() << "Font" << _family << "has 0 variants."; + if (arteryFont.variants.empty()) { + qCDebug(renderutils) << "Font" << _family << "has 0 variants."; return; } @@ -139,24 +138,24 @@ void Font::read(QIODevice& in) { const float ascent = arteryFont.variants[0].metrics.ascender; _fontHeight = ascent + fabs(arteryFont.variants[0].metrics.descender); _leading = arteryFont.variants[0].metrics.lineHeight; - _spaceWidth = 0.5f * arteryFont.variants[0].metrics.emSize; // We use half the emSize as a first guess for _spaceWidth + _spaceWidth = 0.5f * arteryFont.variants[0].metrics.emSize; // We use half the emSize as a first guess for _spaceWidth - if (arteryFont.variants[0].glyphs.length() == 0) { - qDebug() << "Font" << _family << "has 0 glyphs."; + if (arteryFont.variants[0].glyphs.empty()) { + qCDebug(renderutils) << "Font" << _family << "has 0 glyphs."; return; } QVector glyphs; - glyphs.reserve(arteryFont.variants[0].glyphs.length()); - for (int i = 0; i < arteryFont.variants[0].glyphs.length(); i++) { + glyphs.reserve(arteryFont.variants[0].glyphs.size()); + for (size_t i = 0; i < arteryFont.variants[0].glyphs.size(); i++) { auto& g = arteryFont.variants[0].glyphs[i]; Glyph glyph; glyph.c = g.codepoint; - glyph.texOffset = glm::vec2(g.imageBounds.l, g.imageBounds.b); - glyph.texSize = glm::vec2(g.imageBounds.r, g.imageBounds.t) - glyph.texOffset; - glyph.offset = glm::vec2(g.planeBounds.l, g.planeBounds.b); - glyph.size = glm::vec2(g.planeBounds.r, g.planeBounds.t) - glyph.offset; + glyph.texOffset = glm::vec2(g.image_bounds.l, g.image_bounds.b); + glyph.texSize = glm::vec2(g.image_bounds.r, g.image_bounds.t) - glyph.texOffset; + glyph.offset = glm::vec2(g.plane_bounds.l, g.plane_bounds.b); + glyph.size = glm::vec2(g.plane_bounds.r, g.plane_bounds.t) - glyph.offset; glyph.d = g.advance.h; glyphs.push_back(glyph); @@ -166,35 +165,40 @@ void Font::read(QIODevice& in) { } } - if (arteryFont.images.length() == 0) { - qDebug() << "Font" << _family << "has 0 images."; + if (arteryFont.images.empty()) { + qCDebug(renderutils) << "Font" << _family << "has 0 images."; return; } - if (arteryFont.images[0].imageType != artery_font::ImageType::IMAGE_MTSDF) { - qDebug() << "Font" << _family << "has the wrong image type. Expected MTSDF (7), got" << arteryFont.images[0].imageType; + if (arteryFont.images[0].image_type != render_utils::rust::ImageType::IMAGE_MTSDF) { + qCDebug(renderutils) << "Font" << _family << "has the wrong image type. Expected MTSDF (7), got" + << (uint32_t)arteryFont.images[0].image_type; return; } - if (arteryFont.images[0].encoding != artery_font::ImageEncoding::IMAGE_PNG) { - qDebug() << "Font" << _family << "has the wrong encoding. Expected PNG (8), got" << arteryFont.images[0].encoding; + if (arteryFont.images[0].encoding != render_utils::rust::ImageEncoding::IMAGE_PNG) { + qCDebug(renderutils) << "Font" << _family << "has the wrong encoding. Expected PNG (8), got" + << (uint32_t)arteryFont.images[0].encoding; return; } - if (arteryFont.images[0].pixelFormat != artery_font::PixelFormat::PIXEL_UNSIGNED8) { - qDebug() << "Font" << _family << "has the wrong pixel format. Expected unsigned char (8), got" << arteryFont.images[0].pixelFormat; + if (arteryFont.images[0].pixel_format != render_utils::rust::PixelFormat::PIXEL_UNSIGNED8) { + qCDebug(renderutils) << "Font" << _family << "has the wrong pixel format. Expected unsigned char (8), got" + << (uint32_t)arteryFont.images[0].pixel_format; return; } if (arteryFont.images[0].width == 0 || arteryFont.images[0].height == 0) { - qDebug() << "Font" << _family << "has image with width or height of 0. Width:" << arteryFont.images[0].width << ", height:"<< arteryFont.images[0].height; + qCDebug(renderutils) << "Font" << _family + << "has image with width or height of 0. Width:" << arteryFont.images[0].width + << ", height:" << arteryFont.images[0].height; return; } // read image data QImage image; - if (!image.loadFromData((const unsigned char*)arteryFont.images[0].data, arteryFont.images[0].data.length(), "PNG")) { - qDebug() << "Failed to read image for font" << _family; + if (!image.loadFromData((const unsigned char*)arteryFont.images[0].data.data(), arteryFont.images[0].data.size(), "PNG")) { + qCDebug(renderutils) << "Failed to read image for font" << _family; return; } @@ -202,7 +206,7 @@ void Font::read(QIODevice& in) { glm::vec2 imageSize = toGlm(image.size()); _distanceRange /= imageSize; bool hasTofu = false; - foreach(Glyph g, glyphs) { + foreach (Glyph g, glyphs) { // Adjust the pixel texture coordinates into UV coordinates, g.texSize /= imageSize; g.texOffset /= imageSize; @@ -297,9 +301,7 @@ Font::Pointer Font::load(const QString& family) { Font::Font(const QString& family) : _family(family) { static std::once_flag once; - std::call_once(once, []{ - Q_INIT_RESOURCE(fonts); - }); + std::call_once(once, [] { Q_INIT_RESOURCE(fonts); }); } // NERD RAGE: why doesn't QHash have a 'const T & operator[] const' member @@ -316,7 +318,7 @@ QStringList Font::splitLines(const QString& str) const { QStringList Font::tokenizeForWrapping(const QString& str) const { QStringList tokens; - for(auto line : splitLines(str)) { + for (auto line : splitLines(str)) { if (!tokens.empty()) { tokens << QString('\n'); } @@ -327,7 +329,7 @@ QStringList Font::tokenizeForWrapping(const QString& str) const { float Font::computeTokenWidth(const QString& token) const { float advance = 0.0f; - foreach(QChar c, token) { + foreach (QChar c, token) { Q_ASSERT(c != '\n'); advance += (c == ' ') ? _spaceWidth : getGlyph(c).d; } @@ -339,7 +341,7 @@ glm::vec2 Font::computeExtent(const QString& str) const { QStringList lines = splitLines(str); if (!lines.empty()) { - for(const auto& line : lines) { + for (const auto& line : lines) { float tokenWidth = computeTokenWidth(line); extent.x = std::max(tokenWidth, extent.x); } @@ -361,15 +363,19 @@ void Font::setupGPU() { std::make_tuple(false, false, true, false, false, sdf_text3D_forward), std::make_tuple(true, false, true, false, false, sdf_text3D_forward /*sdf_text3D_translucent_forward*/), std::make_tuple(false, true, true, false, false, sdf_text3D_translucent_unlit /*sdf_text3D_unlit_forward*/), - std::make_tuple(true, true, true, false, false, sdf_text3D_translucent_unlit/*sdf_text3D_translucent_unlit_forward*/), + std::make_tuple(true, true, true, false, false, + sdf_text3D_translucent_unlit /*sdf_text3D_translucent_unlit_forward*/), std::make_tuple(false, false, false, true, false, sdf_text3D_mirror), std::make_tuple(true, false, false, true, false, sdf_text3D_translucent_mirror), std::make_tuple(false, true, false, true, false, sdf_text3D_unlit_mirror), std::make_tuple(true, true, false, true, false, sdf_text3D_translucent_unlit_mirror), std::make_tuple(false, false, true, true, false, sdf_text3D_forward_mirror), - std::make_tuple(true, false, true, true, false, sdf_text3D_forward_mirror /*sdf_text3D_translucent_forward_mirror*/), - std::make_tuple(false, true, true, true, false, sdf_text3D_translucent_unlit_mirror /*sdf_text3D_unlit_forward_mirror*/), - std::make_tuple(true, true, true, true, false, sdf_text3D_translucent_unlit_mirror/*sdf_text3D_translucent_unlit_forward_mirror*/), + std::make_tuple(true, false, true, true, false, + sdf_text3D_forward_mirror /*sdf_text3D_translucent_forward_mirror*/), + std::make_tuple(false, true, true, true, false, + sdf_text3D_translucent_unlit_mirror /*sdf_text3D_unlit_forward_mirror*/), + std::make_tuple(true, true, true, true, false, + sdf_text3D_translucent_unlit_mirror /*sdf_text3D_translucent_unlit_forward_mirror*/), std::make_tuple(false, false, false, false, true, sdf_text3D_fade), std::make_tuple(true, false, false, false, true, sdf_text3D_translucent_fade), std::make_tuple(false, true, false, false, true, sdf_text3D_unlit_fade), @@ -390,9 +396,8 @@ void Font::setupGPU() { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, !transparent, ComparisonFunction::LESS_EQUAL); - state->setBlendFunction(transparent, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + state->setBlendFunction(transparent, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); if (transparent) { PrepareStencil::testMask(*state); } else { @@ -405,7 +410,8 @@ void Font::setupGPU() { state->setDepthBias(-2.0f); state->setDepthBiasSlopeScale(-2.0f); - _pipelines[std::make_tuple(transparent, unlit, forward, mirror, fade)] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<5>(key)), state); + _pipelines[std::make_tuple(transparent, unlit, forward, mirror, fade)] = + gpu::Pipeline::create(gpu::Shader::createProgram(std::get<5>(key)), state); } // Sanity checks @@ -425,8 +431,14 @@ void Font::setupGPU() { } } -inline QuadBuilder adjustedQuadBuilderForAlignmentMode(const Glyph& glyph, glm::vec2 advance, float scale, float enlargeForShadows, - TextAlignment alignment, float rightSpacing, TextVerticalAlignment verticalAlignment, float bottomSpacing) { +inline QuadBuilder adjustedQuadBuilderForAlignmentMode(const Glyph& glyph, + glm::vec2 advance, + float scale, + float enlargeForShadows, + TextAlignment alignment, + float rightSpacing, + TextVerticalAlignment verticalAlignment, + float bottomSpacing) { if (alignment == TextAlignment::RIGHT) { advance.x += rightSpacing; } else if (alignment == TextAlignment::CENTER) { @@ -440,8 +452,14 @@ inline QuadBuilder adjustedQuadBuilderForAlignmentMode(const Glyph& glyph, glm:: return QuadBuilder(glyph, advance, scale, enlargeForShadows); } -void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm::vec2& origin, const glm::vec2& bounds, float scale, bool enlargeForShadows, - TextAlignment alignment, TextVerticalAlignment verticalAlignment) { +void Font::buildVertices(Font::DrawInfo& drawInfo, + const QString& str, + const glm::vec2& origin, + const glm::vec2& bounds, + float scale, + bool enlargeForShadows, + TextAlignment alignment, + TextVerticalAlignment verticalAlignment) { drawInfo.verticesBuffer = std::make_shared(gpu::Buffer::VertexBuffer); drawInfo.indicesBuffer = std::make_shared(gpu::Buffer::IndexBuffer); drawInfo.indexCount = 0; @@ -524,7 +542,9 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm int i = (int)glyphsAndCorners.size() - 1; while (!foundBottomSpacing && i >= 0) { auto* nextGlyphAndCorner = &glyphsAndCorners[i]; - bottomSpacing = std::max(bottomSpacing, bottomEdge - (nextGlyphAndCorner->second.y + (nextGlyphAndCorner->first.offset.y - nextGlyphAndCorner->first.size.y))); + bottomSpacing = + std::max(bottomSpacing, bottomEdge - (nextGlyphAndCorner->second.y + (nextGlyphAndCorner->first.offset.y - + nextGlyphAndCorner->first.size.y))); i--; while (i >= 0) { auto& prevGlyphAndCorner = glyphsAndCorners[i]; @@ -534,7 +554,9 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm break; } nextGlyphAndCorner = &prevGlyphAndCorner; - bottomSpacing = std::max(bottomSpacing, bottomEdge - (nextGlyphAndCorner->second.y + (nextGlyphAndCorner->first.offset.y - nextGlyphAndCorner->first.size.y))); + bottomSpacing = std::max(bottomSpacing, + bottomEdge - (nextGlyphAndCorner->second.y + (nextGlyphAndCorner->first.offset.y - + nextGlyphAndCorner->first.size.y))); i--; } } @@ -544,8 +566,9 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm while (i >= 0) { auto* nextGlyphAndCorner = &glyphsAndCorners[i]; float rightSpacing = rightEdge - (nextGlyphAndCorner->second.x + nextGlyphAndCorner->first.d); - quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(nextGlyphAndCorner->first, nextGlyphAndCorner->second, scale, enlargeForShadows, - alignment, rightSpacing, verticalAlignment, bottomSpacing)); + quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(nextGlyphAndCorner->first, nextGlyphAndCorner->second, + scale, enlargeForShadows, alignment, rightSpacing, + verticalAlignment, bottomSpacing)); i--; while (i >= 0) { auto& prevGlyphAndCorner = glyphsAndCorners[i]; @@ -555,8 +578,9 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm break; } - quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(prevGlyphAndCorner.first, prevGlyphAndCorner.second, scale, enlargeForShadows, - alignment, rightSpacing, verticalAlignment, bottomSpacing)); + quadBuilders.push_back(adjustedQuadBuilderForAlignmentMode(prevGlyphAndCorner.first, prevGlyphAndCorner.second, + scale, enlargeForShadows, alignment, rightSpacing, + verticalAlignment, bottomSpacing)); nextGlyphAndCorner = &prevGlyphAndCorner; i--; @@ -610,20 +634,22 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro const bool boundsChanged = props.bounds != drawInfo.bounds || props.origin != drawInfo.origin; // If we're switching to or from shadow effect mode, we need to rebuild the vertices - if (props.str != drawInfo.string || boundsChanged || props.alignment != drawInfo.alignment || props.verticalAlignment != drawInfo.verticalAlignment || - (drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) || - (textEffect == SHADOW_EFFECT && props.scale != drawInfo.scale)) { + if (props.str != drawInfo.string || boundsChanged || props.alignment != drawInfo.alignment || + props.verticalAlignment != drawInfo.verticalAlignment || + (drawInfo.params.effect != textEffect && (textEffect == SHADOW_EFFECT || drawInfo.params.effect == SHADOW_EFFECT)) || + (textEffect == SHADOW_EFFECT && props.scale != drawInfo.scale)) { drawInfo.scale = props.scale; drawInfo.alignment = props.alignment; drawInfo.verticalAlignment = props.verticalAlignment; - buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, drawInfo.alignment, drawInfo.verticalAlignment); + buildVertices(drawInfo, props.str, props.origin, props.bounds, props.scale, textEffect == SHADOW_EFFECT, + drawInfo.alignment, drawInfo.verticalAlignment); } setupGPU(); if (!drawInfo.paramsBuffer || boundsChanged || _needsParamsUpdate || drawInfo.params.color != props.color || - drawInfo.params.effectColor != props.effectColor || drawInfo.params.effectThickness != props.effectThickness || - drawInfo.params.effect != textEffect) { + drawInfo.params.effectColor != props.effectColor || drawInfo.params.effectThickness != props.effectThickness || + drawInfo.params.effect != textEffect) { drawInfo.params.color = props.color; drawInfo.params.effectColor = props.effectColor; drawInfo.params.effectThickness = props.effectThickness; @@ -645,7 +671,8 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const DrawPro _needsParamsUpdate = false; } - batch.setPipeline(_pipelines[std::make_tuple(props.color.a < 1.0f, props.unlit, props.forward, props.mirror, props.fading)]); + batch.setPipeline( + _pipelines[std::make_tuple(props.color.a < 1.0f, props.unlit, props.forward, props.mirror, props.fading)]); batch.setInputFormat(_format); batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride); batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture); diff --git a/nix/artery-font-format.nix b/nix/artery-font-format.nix deleted file mode 100644 index fbc8ae2e604..00000000000 --- a/nix/artery-font-format.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ stdenv, fetchFromGitHub }: -stdenv.mkDerivation rec { - pname = "artery-font-format"; - version = "1.1"; - src = fetchFromGitHub { - owner = "Chlumsky"; - repo = "artery-font-format"; - tag = "v${version}"; - hash = "sha256-JozvSQJt/62083sGX0jwMC2xmmpYrGvwd9MIfHtnqCs="; - }; - - installPhase = '' - mkdir -p $out/include/ - cp -r artery-font $out/include/ - ''; -} diff --git a/nix/overte.nix b/nix/overte.nix index bcbced2c0de..2c0d06958ab 100644 --- a/nix/overte.nix +++ b/nix/overte.nix @@ -9,7 +9,6 @@ # dependencies glad, gif_creator, - artery-font-format, cgltf, etc2comp, openssl, @@ -103,7 +102,6 @@ stdenv.mkDerivation { etc2comp cgltf gif_creator - artery-font-format openssl libGL glm diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000000..4f14474dc30 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.95.0" +components = ["rustfmt", "clippy", "rust-analyzer"] + diff --git a/tools/ci-scripts/linux-ci/Dockerfile_build_ubuntu-22.04 b/tools/ci-scripts/linux-ci/Dockerfile_build_ubuntu-22.04 index 2eecd26efdf..cc470e045bf 100644 --- a/tools/ci-scripts/linux-ci/Dockerfile_build_ubuntu-22.04 +++ b/tools/ci-scripts/linux-ci/Dockerfile_build_ubuntu-22.04 @@ -4,7 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 # Docker file for building Overte -# Example build: docker build --no-cache --progress=plain -t overte/overte-full-build:2026-02-09-ubuntu-22.04-amd64 -f Dockerfile_build_ubuntu-22.04 . +# Example build: docker build --no-cache --progress=plain -t overte/overte-full-build:2026-06-05-ubuntu-22.04-amd64 -f Dockerfile_build_ubuntu-22.04 . FROM ubuntu:22.04 LABEL maintainer="Julian Groß (julian.gro@overte.org)" LABEL description="Development image for full Overte builds" @@ -140,3 +140,10 @@ ENV CONAN_PROFILE="./tools/conan-profiles/linux" # On macOS the interface target is called "Overte" instead. ENV APP_TARGET_NAME=interface + + +# Install rustup +RUN <