From 751408ea92268a16a6931862cb00e8dbccb4e47e Mon Sep 17 00:00:00 2001 From: numzero Date: Mon, 26 Oct 2026 18:26:11 +0300 Subject: [PATCH] replace custom FFI with Diplomat --- CMakeLists.txt | 2 ++ Cargo.lock | 87 +++++++++++++++++++++++++++++++++------------ Cargo.toml | 9 +++-- diplomat.cmake | 8 +++++ src/lib.rs | 67 +++++++++++++++++++++++++++++----- ui/CMakeLists.txt | 1 - ui/Cargo.toml | 15 -------- ui/impl.cmake | 29 +++++++++++++-- ui/src/api.cxx | 57 ----------------------------- ui/src/api.hxx | 56 ----------------------------- ui/src/lib.rs | 48 ------------------------- ui/src/viewport.cxx | 2 +- ui/src/viewport.hxx | 4 +-- 13 files changed, 169 insertions(+), 216 deletions(-) create mode 100644 diplomat.cmake delete mode 100644 ui/Cargo.toml delete mode 100644 ui/src/api.cxx delete mode 100644 ui/src/api.hxx delete mode 100644 ui/src/lib.rs diff --git a/CMakeLists.txt b/CMakeLists.txt index fd81298..8a50401 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ find_package(Qt6 REQUIRED COMPONENTS Gui Widgets) find_program(CARGO cargo REQUIRED) set(CARGO_TARGET_DIR "${CMAKE_SOURCE_DIR}/target" CACHE PATH "Path to the Cargo target directory") +include(diplomat.cmake) + qt_standard_project_setup() add_subdirectory(ui) diff --git a/Cargo.lock b/Cargo.lock index 0c5bb18..eb0dee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,23 +7,14 @@ name = "PROJECT-NAME" version = "0.1.0" dependencies = [ "bytemuck", + "diplomat", + "diplomat-runtime", "glam", "pollster", "wgpu", "winit", ] -[[package]] -name = "PROJECT-NAME-impl" -version = "0.1.0" -dependencies = [ - "PROJECT-NAME", - "glam", - "pollster", - "raw-window-handle", - "wgpu", -] - [[package]] name = "ab_glyph" version = "0.2.32" @@ -193,7 +184,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -367,6 +358,38 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "diplomat" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a80510e6103d35c5cd031c0e3f21165b32742c301ee3dafdbb5904c6dca46a" +dependencies = [ + "diplomat_core", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "diplomat-runtime" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d981121ba76150987fda68185b37853c0b928a55dfdc28897348cd068440be" + +[[package]] +name = "diplomat_core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5c594efd1d097105255fc325c430a517c97b7b7c69f8727f653593e1ce726" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "smallvec", + "strck", + "syn 3.0.9", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -455,7 +478,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -904,7 +927,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -1205,7 +1228,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -1459,7 +1482,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -1538,6 +1561,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strck" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42316e70da376f3d113a68d138a60d8a9883c604fe97942721ec2068dab13a9f" +dependencies = [ + "unicode-ident", +] + [[package]] name = "strict-num" version = "0.1.1" @@ -1555,6 +1587,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -1590,7 +1633,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -1601,7 +1644,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -1770,7 +1813,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.114", "wasm-bindgen-shared", ] @@ -2103,7 +2146,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -2114,7 +2157,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] [[package]] @@ -2446,5 +2489,5 @@ checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.114", ] diff --git a/Cargo.toml b/Cargo.toml index c3bb8aa..af7dac3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ -[workspace] -members = ["ui"] - [package] name = "PROJECT-NAME" version = "0.1.0" edition = "2024" +[lib] +crate-type = ["lib", "staticlib"] + [profile.dev] panic = 'abort' @@ -23,3 +23,6 @@ glam = { version = "0.30.9", features = ["bytemuck"] } pollster = "0.4.0" wgpu = "27.0.1" winit = "0.30.12" + +diplomat = "0.17.0" +diplomat-runtime = "0.16.1" diff --git a/diplomat.cmake b/diplomat.cmake new file mode 100644 index 0000000..70a752f --- /dev/null +++ b/diplomat.cmake @@ -0,0 +1,8 @@ +set(crates "${CMAKE_BINARY_DIR}/crates") +set(diplomat "${crates}/bin/diplomat-tool") + +if(NOT IS_EXECUTABLE "${diplomat}") + message(CONFIGURE_LOG "Downloading Diplomat...") + file(MAKE_DIRECTORY "${crates}") + execute_process(COMMAND ${CARGO} install --root "${crates}" diplomat-tool@0.17.0) +endif() diff --git a/src/lib.rs b/src/lib.rs index 68a6b75..3b40c0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,20 +2,13 @@ use std::error::Error; use glam::{Mat4, UVec2, Vec3, Vec4, vec3, vec4}; +pub use crate::ffi::RedrawArgs; + mod camera; mod render; const BACKGROUND: Vec4 = vec4(0.00, 0.39, 0.52, 1.00); -#[derive(Debug, Clone, Copy)] -#[repr(C)] -pub struct RedrawArgs { - pub cam_yaw: f32, - pub cam_pitch: f32, - pub cam_distance: f32, - pub cam_fovy: f32, -} - impl Default for RedrawArgs { fn default() -> Self { Self { @@ -252,3 +245,59 @@ pub async fn init_gpu_inner( surface, }) } + +#[diplomat::bridge] +mod ffi { + use std::{num::NonZero, ptr::NonNull}; + + use crate::init_gpu_inner; + use glam::{UVec2, uvec2}; + use wgpu::rwh::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle}; + + #[derive(Debug, Clone, Copy)] + #[repr(C)] + pub struct RedrawArgs { + pub cam_yaw: f32, + pub cam_pitch: f32, + pub cam_distance: f32, + pub cam_fovy: f32, + } + + unsafe fn create_viewport( + display: impl Into, + window: impl Into, + size: UVec2, + ) -> crate::Core { + let target = wgpu::SurfaceTargetUnsafe::RawHandle { + raw_display_handle: display.into(), + raw_window_handle: window.into(), + }; + let gpu = pollster::block_on(init_gpu_inner(|instance| unsafe { + instance.create_surface_unsafe(target) + })) + .unwrap(); + crate::Core::new(gpu, size) + } + + #[diplomat::opaque_mut] + pub struct Core(crate::Core); + + impl Core { + pub fn from_xcb(connection: usize, window: u32, width: u32, height: u32) -> Box { + let display = + XcbDisplayHandle::new(Some(NonNull::new(connection as *mut _).unwrap()), 0); + let window = XcbWindowHandle::new(NonZero::new(window).unwrap()); + Box::new(Self(unsafe { + create_viewport(display, window, uvec2(width, height)) + })) + } + + pub fn configure(&mut self, width: u32, height: u32) { + self.0.configure(uvec2(width, height)); + } + + pub fn redraw(&mut self, args: &RedrawArgs) { + self.0.redraw(args); + } + } +} diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index a9313c9..5a46b32 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -1,7 +1,6 @@ include(impl.cmake) qt_add_executable(PROJECT-NAME - src/api.cxx src/main.cxx src/main_window.cxx src/main_window.ui diff --git a/ui/Cargo.toml b/ui/Cargo.toml deleted file mode 100644 index 6565f4f..0000000 --- a/ui/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "PROJECT-NAME-impl" -version = "0.1.0" -edition = "2024" - -[lib] -crate-type = ["staticlib"] - -[dependencies] -PROJECT-NAME = {path = "../"} - -glam = { version = "0.30" } -pollster = "0.4.0" -raw-window-handle = "0.6.2" -wgpu = "27.0.1" diff --git a/ui/impl.cmake b/ui/impl.cmake index a0469bf..ee82ee1 100644 --- a/ui/impl.cmake +++ b/ui/impl.cmake @@ -1,19 +1,25 @@ # build the Rust side as a static library -set(impl_basename "${CARGO_TARGET_DIR}/release/libPROJECT_NAME_impl") +set(impl_basename "${CARGO_TARGET_DIR}/release/libPROJECT_NAME") set(cargo_cmake_args) +set(diplomat_cmake_args) if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.27") list(APPEND cargo_cmake_args DEPENDS_EXPLICIT_ONLY) + list(APPEND diplomat_cmake_args DEPENDS_EXPLICIT_ONLY) endif() if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.28") list(APPEND cargo_cmake_args JOB_SERVER_AWARE TRUE) endif() +if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.31") + list(APPEND diplomat_cmake_args CODEGEN) +endif() + add_custom_command( OUTPUT "${impl_basename}.a" - COMMAND env "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}" ${CARGO} build --release --package PROJECT-NAME-impl + COMMAND env "CARGO_TARGET_DIR=${CARGO_TARGET_DIR}" ${CARGO} build --release --package PROJECT-NAME WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" DEPFILE "${impl_basename}.d" USES_TERMINAL @@ -31,3 +37,22 @@ add_library(PROJECT_NAME_impl STATIC IMPORTED) set_target_properties(PROJECT_NAME_impl PROPERTIES IMPORTED_LOCATION "${impl_basename}.a" ) + +# generate the bindings +set(bindings_dir "${CMAKE_CURRENT_BINARY_DIR}/include") +file(MAKE_DIRECTORY "${bindings_dir}") +set(bindings + "${bindings_dir}/Core.hpp" +) + +add_custom_command( + OUTPUT ${bindings} + COMMAND ${diplomat} cpp "${bindings_dir}" + MAIN_DEPENDENCY "${CMAKE_SOURCE_DIR}/src/lib.rs" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + VERBATIM + ${diplomat_cmake_args} +) + +target_sources(PROJECT_NAME_impl INTERFACE ${bindings}) +target_include_directories(PROJECT_NAME_impl INTERFACE "${bindings_dir}") diff --git a/ui/src/api.cxx b/ui/src/api.cxx deleted file mode 100644 index aaa2b64..0000000 --- a/ui/src/api.cxx +++ /dev/null @@ -1,57 +0,0 @@ -#include "api.hxx" - -#include -#include - -namespace ffi { -extern "C" Core* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height); -extern "C" void rt4_viewport_destroy(Core* viewport); -extern "C" void rt4_viewport_configure(Core* viewport, std::uint32_t width, std::uint32_t height); -extern "C" void rt4_viewport_redraw(Core* viewport, const RedrawArgs* args); -} // namespace ffi - -BoxCore::BoxCore(BoxCore&& b) - : ptr(std::exchange(b.ptr, {})) { -} - -BoxCore::~BoxCore() { - reset(); -} - -BoxCore& BoxCore::operator=(BoxCore&& b) { - if (&b == this) - return *this; - std::swap(ptr, b.ptr); - b.reset(); - return *this; -} - -BoxCore BoxCore::from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height) { - if (!connection) - throw std::logic_error("attempt to use a null connection"); - if (!window) - throw std::logic_error("attempt to use a null window"); - BoxCore out; - out.ptr.ptr = ffi::rt4_viewport_create(connection, window, width, height); - return out; -} - -void BoxCore::reset() { - auto viewport = std::exchange(ptr, {}); - if (viewport) - ffi::rt4_viewport_destroy(viewport.use()); -} - -ffi::Core* MutCore::use() const { - if (!ptr) - throw std::logic_error("attempt to use a null Core"); - return ptr; -} - -void MutCore::configure(std::uint32_t width, std::uint32_t height) const { - rt4_viewport_configure(use(), width, height); -} - -void MutCore::redraw(const RedrawArgs& args) const { - rt4_viewport_redraw(use(), &args); -} diff --git a/ui/src/api.hxx b/ui/src/api.hxx deleted file mode 100644 index 82d9880..0000000 --- a/ui/src/api.hxx +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -struct xcb_connection_t; - -struct alignas(16) Vec4 { - float x, y, z, w; -}; - -namespace ffi { -struct Core; - -struct RedrawArgs { - float cam_yaw; - float cam_pitch; - float cam_distance; - float cam_fovy; -}; -} // namespace ffi - -using ffi::RedrawArgs; - -class MutCore { - friend class BoxCore; - -public: - explicit operator bool() const { return ptr; } - - void configure(std::uint32_t width, std::uint32_t height) const; - void redraw(const RedrawArgs& args) const; - -private: - ffi::Core* ptr = nullptr; - ffi::Core* use() const; -}; - -class BoxCore { -public: - BoxCore() = default; - BoxCore(const BoxCore&) = delete; - BoxCore(BoxCore&&); - BoxCore& operator=(const BoxCore&) = delete; - BoxCore& operator=(BoxCore&&); - ~BoxCore(); - - explicit operator bool() const { return !!ptr; } - const MutCore* operator->() const { return &ptr; } - - void reset(); - - static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height); - -private: - MutCore ptr; -}; diff --git a/ui/src/lib.rs b/ui/src/lib.rs deleted file mode 100644 index 571ecd9..0000000 --- a/ui/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::{ffi::c_void, num::NonZero, ptr::NonNull}; - -use PROJECT_NAME::{Core, RedrawArgs, init_gpu_inner}; -use glam::{UVec2, uvec2}; -use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle}; - -unsafe fn create_viewport( - display: impl Into, - window: impl Into, - size: UVec2, -) -> Box { - let target = wgpu::SurfaceTargetUnsafe::RawHandle { - raw_display_handle: display.into(), - raw_window_handle: window.into(), - }; - let gpu = pollster::block_on(init_gpu_inner(|instance| unsafe { - instance.create_surface_unsafe(target) - })) - .unwrap(); - Box::new(Core::new(gpu, size)) -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn rt4_viewport_create( - connection: NonNull, - window: NonZero, - width: u32, - height: u32, -) -> Box { - let display = XcbDisplayHandle::new(Some(connection), 0); - let window = XcbWindowHandle::new(window); - unsafe { create_viewport(display, window, uvec2(width, height)) } -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn rt4_viewport_destroy(viewport: Box) { - drop(viewport); -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn rt4_viewport_configure(viewport: &mut Core, width: u32, height: u32) { - viewport.configure(uvec2(width, height)); -} - -#[unsafe(no_mangle)] -unsafe extern "C" fn rt4_viewport_redraw(viewport: &mut Core, args: &RedrawArgs) { - viewport.redraw(args); -} diff --git a/ui/src/viewport.cxx b/ui/src/viewport.cxx index e681bd9..60d638c 100644 --- a/ui/src/viewport.cxx +++ b/ui/src/viewport.cxx @@ -63,7 +63,7 @@ void Viewport::recreate() try { const QSize device_size = size() * devicePixelRatio(); core.reset(); - core = BoxCore::from_xcb(xcb_connection, x11_window, device_size.width(), device_size.height()); + core = Core::from_xcb((std::uintptr_t)xcb_connection, x11_window, device_size.width(), device_size.height()); } catch (const std::exception& e) { fprintf(stderr, "failed to recreate the viewport: %s", e.what()); } diff --git a/ui/src/viewport.hxx b/ui/src/viewport.hxx index 95839b9..5d1d738 100644 --- a/ui/src/viewport.hxx +++ b/ui/src/viewport.hxx @@ -2,7 +2,7 @@ #include -#include "api.hxx" +#include "Core.hpp" class Viewport : public QWidget { Q_OBJECT @@ -20,7 +20,7 @@ protected: void resizeEvent(QResizeEvent* event) override; private: - BoxCore core; + std::unique_ptr core; RedrawArgs args = {}; void recreate();