use a vtable instead of direct linking

This commit is contained in:
numzero 2026-10-25 13:22:41 +03:00
parent 41e747d9c2
commit 96c75c3571
3 changed files with 92 additions and 90 deletions

View File

@ -4,54 +4,13 @@
#include <utility>
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);
extern "C" ICore* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height);
} // 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);
return BoxCore{ffi::rt4_viewport_create(connection, window, width, height)};
}

View File

@ -2,15 +2,32 @@
#include <cstdint>
#include <memory>
struct xcb_connection_t;
class IBoxed {
friend struct BoxDeleter;
private:
~IBoxed() = delete;
virtual void destroy() = 0;
};
struct BoxDeleter {
void operator()(IBoxed* obj) {
obj->destroy();
}
};
template <typename T>
using Box = std::unique_ptr<T, BoxDeleter>;
struct alignas(16) Vec4 {
float x, y, z, w;
};
namespace ffi {
struct Core;
struct RedrawArgs {
float cam_yaw;
float cam_pitch;
@ -21,36 +38,14 @@ struct RedrawArgs {
using ffi::RedrawArgs;
class MutCore {
friend class BoxCore;
class ICore : public IBoxed {
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;
virtual void configure(std::uint32_t width, std::uint32_t height) = 0;
virtual void redraw(const RedrawArgs& args) = 0;
};
class BoxCore {
class BoxCore : public Box<ICore> {
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();
using Box<ICore>::Box;
static BoxCore from_xcb(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height);
private:
MutCore ptr;
};

View File

@ -4,11 +4,74 @@ use PROJECT_NAME::{Core, RedrawArgs, init_gpu_inner};
use glam::{UVec2, uvec2};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
use crate::vtable::ICoreImpl;
mod vtable {
use std::ops::{Deref, DerefMut};
use PROJECT_NAME::{Core, RedrawArgs};
use glam::uvec2;
#[repr(C)]
struct ICoreVTable {
destroy: unsafe extern "C" fn(this: Box<ICoreImpl>),
configure: unsafe extern "C" fn(this: &mut ICoreImpl, width: u32, height: u32),
redraw: unsafe extern "C" fn(this: &mut ICoreImpl, args: &RedrawArgs),
}
unsafe extern "C" fn destroy(this: Box<ICoreImpl>) {
drop(this);
}
unsafe extern "C" fn configure(this: &mut ICoreImpl, width: u32, height: u32) {
this.core.configure(uvec2(width, height));
}
unsafe extern "C" fn redraw(this: &mut ICoreImpl, args: &RedrawArgs) {
this.core.redraw(args);
}
static VTABLE: ICoreVTable = ICoreVTable {
destroy: destroy,
configure: configure,
redraw: redraw,
};
#[repr(C)]
pub(crate) struct ICoreImpl {
vtable: &'static ICoreVTable,
core: Core,
}
impl ICoreImpl {
pub(crate) fn new(core: Core) -> Box<Self> {
Box::new(ICoreImpl {
vtable: &VTABLE,
core,
})
}
}
impl DerefMut for ICoreImpl {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.core
}
}
impl Deref for ICoreImpl {
type Target = Core;
fn deref(&self) -> &Self::Target {
&self.core
}
}
}
unsafe fn create_viewport(
display: impl Into<RawDisplayHandle>,
window: impl Into<RawWindowHandle>,
size: UVec2,
) -> Box<Core> {
) -> Box<ICoreImpl> {
let target = wgpu::SurfaceTargetUnsafe::RawHandle {
raw_display_handle: display.into(),
raw_window_handle: window.into(),
@ -17,7 +80,7 @@ unsafe fn create_viewport(
instance.create_surface_unsafe(target)
}))
.unwrap();
Box::new(Core::new(gpu, size))
ICoreImpl::new(Core::new(gpu, size))
}
#[unsafe(no_mangle)]
@ -26,23 +89,8 @@ unsafe extern "C" fn rt4_viewport_create(
window: NonZero<u32>,
width: u32,
height: u32,
) -> Box<Core> {
) -> Box<ICoreImpl> {
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<Core>) {
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);
}