rust3ds / citro3d-rs Goto Github PK
View Code? Open in Web Editor NEWRust bindings and safe wrappers for citro3d
Home Page: https://rust3ds.github.io/citro3d-rs
Rust bindings and safe wrappers for citro3d
Home Page: https://rust3ds.github.io/citro3d-rs
I wanted to compile the triangle example, but it didn't seem to work out very well.
Logs:
https://gist.github.com/CenTdemeern1/fca46a415a0ba8953ee68210c469d77e
Let me know if you need any more information.
Citro3D is designed to be mostly used alone (an exception is the use of Citro2D, which is based on the aforementioned). This is a problem because the original implementation makes many assumptions which we cannot prove in a normal environment, but that we need to to ensure memory safety and correct execution.
For example, we cannot have the use of RawFramebuffer
s without taking the gfx::Screen
‘s write lock. Simply passing immutable references and such is not safe, as the underlying C implementation could change the environment regardless.
We need to thoroughly examine Citro3D’s code and check for all of the functions that mutate the environment (like changing the framebuffer’s format, or even just displaying to the screen) and make appropriate changes to ensure a safe library.
Example for things to avoid:
https://github.com/ian-h-chamberlain/citro3d-rs/blob/6ba85149d84aa3bcc879ec16ef79079dc411643f/citro3d/src/render.rs#L61
Because the choice of Screen target is permanent, but only a simple immutable reference is passed. Tracking internal changes would be impossible. The screen’s locked reference (RefMut
) should be held by the struct.
C3D_BindProgram
stores the passed-in pointer to a shader::Program
in the C3D context. Currently, bind_program
takes a reference to a Program
, meaning that it's possible to pass in a Program
that is dropped before the context attempts to use it (at the end of the frame, when it's rendered). This leads to a use-after-free and generally a crash.
This can be avoided by the Instance
taking ownership of the passed-in Program
, and storing it in some kind of data type that can be Pin
ned.
Similar to the cc-rs
crate, it would be nice to compile shaders automatically in one of these ways:
build script helper crate (probably looks very similar to cc-rs
in practice)
include_bytes!(concat!(env!("OUT_DIR"), "/shader.shbin"))
? looks kinda messy but would work
somehow include built shader bin in romfs – maybe hard link / symlink can do this?
Proc macro? Basically would run the shader compiler and spit out bytes... probably would increase compile time poorly
Hi,
I'm new to rust as I'm more used to C and C++, I've been trying to work on a rust engine supporting 3ds however i'm having some issues.
Basically I want to have a struct that contains the data related to graphics of the engine. So the context etc
I originally approached it by trying to reproduce the triangle example but putting the data in a struct etc instead of putting everything in main
however when constructing the struct I realise that due to the self references, i can't make an appropriate constructor
so I'm wondering what's the recommended approach when working with citro3d-rs
Is support for Citro2D on the checklist? I would quite like to use Rust3DS for my homebrew applications, but it doesn't appear to support C2D unfortunately :/
Without these bindings, It is currently impossible to use textures within a rust environment
EDIT: this may technically come under another repo, But its a essential feature
After looking for a reference to C3D_TexSetFilter
inside of the bindings, I've come to find out that C3D_TexSetFilter
is an inlined function inside of c3d/texture.h
which has been missed.
Do keep in mind the missing binding is a static inline, So its missing
Hi. I'm @Jhynjhiruu's friend. Just wanna preface this by saying this library has been incredibly useful as a reference when working out citro3d, I really appreciate the effort that's gone into this. Now onto the problems
The interface exposed by Instance
is fundamentally unsound, what do I mean by this? Well it makes guarantees to/as safe code that it doesn't uphold. Some examples:
draw_arrays
doesn't require the underlying 'vbo
to outlive the frame end (which will cause use-after-free)The main cause of use-after-free's is the lack of lifetimes for things which need to stick around until the frame ends (when they will be read by the GPU). In its current form at the very least draw_arrays
and bind_program
need to be marked unsafe
(bind_program
especially is really nasty because of its pinning requirements).
Alternatively, the lifetimes can be made to work. It just requires moving the frame stuff to a RAII wrapper rather than exclusively render_frame_with
. I've created a prototype/proof of concept here I'm not married to it but it fixes everything but #35 which I left alone because of #38. It also lays the foundation for implementing stuff like textures in a safe way as well (as the lifetimes are now there to enforce it).
Both ctru-sys and citro3d-sys have bindings for the same header. This causes issues when using both libraries in a project as they are not interchangeable. Not an urgent issue as it has workarounds but could be tidied up.
citro3d provides a dedicated
Mtx_PerspStereoTilt
for creating a tilted perspective matrix, and we should usectru_sys::osGetSliderValue
to adjust the strength of the tilt
Originally posted by @Firepal in #19 (comment)
Yeah, I saw that a little while ago. Unfortunately, the main bit of rendering a model it doesn't touch, textures, is the bit I'm having difficulty with. I've successfully got a shape with a texture, but I find myself hopelessly lost trying to understand how the texture pipeline is supposed to work and I had to go a fair bit against my understanding of how it should work in order to get two faces with two different textures.
Please enjoy this square, which took me about 9 hours to get textured:
(Sorry for the terrible recording quality. I should have turned down my screen brightness first.)
The problem I'm having is that the texture pipeline seems to persist state across draw calls (Instance::draw_arrays
calls, really), even when I think it shouldn't be - first, enabling textures for only the first face and resetting the TexEnv pipeline for the second did nothing, giving me a lovely yellow square (texture coordinates (0.0, 0.0) on Bowser's face) on the back; and second, using two separate textures for the two sides gave me Peach on both sides, for some reason (the Peach texture got used for both sides instead of only the second side, as if the GPU were batching the draws and preserving only the last-written texture). I worked around the issue by merging the textures into one, using it for both draw calls, and adjusting the texture coordinates to match.
I suppose I can technically deal with this by merging all the textures for everything that's supposed to be drawn on one frame into one giant texture and using that for every shape that frame, but it feels like I'm misunderstanding something fundamental about how textures are supposed to work. Either that or it's a bug.
(By the way, having a safe wrapper for draw_elements
would be pretty handy in the future for optimising the number of vertices.)
My friend figured it out, and in retrospect it's sort of obvious - textures have to be alive until they're drawn, and if they're freed before the end of the frame, strange things happen. TexEnvs still don't work how I think they do, but at least I can draw more than one texture at a time now.
Originally posted by @Jhynjhiruu in #35 (comment)
Couple things that could fall under this, but mainly
html_root_url
and favicons like in rust3ds/ctru-rs#147ctru-rs
docs in some placesHi there,
I've had a strange crash in my project for the past while, in which the program crashes while dropping the Target
s for the top screen. I wasn't able to figure out exactly what's going wrong, until I noticed the difference between my program and the example triangle.rs
- my code was creating the Instance
object after the Target
s, and triangle.rs
was creating it before. Given what I know about a Target
being bound to an Instance
, this makes a weird kind of sense - initialising the Target
before the Instance
means that Rust will drop the Instance
first, and the instance calls C3D_Fini
when it's dropped, which presumably does something weird. (The crash itself is in the call to free
within the C code, so presumably this also manifests using the C toolchain, though I didn't test that.)
I was able to fix the crash pretty simply, by changing the Instance
to be initialised first. Is there a better fix? Either unlinking the bound Target
on drop somehow, or something fancy with lifetimes to ensure that the Instance
must outlive the Target
attached to it?
(For a simple repro of the crash, move line 61 of triangle.rs
to line 83 (other places may also work), then build and run it on a real 3DS. Citra doesn't care about the crash, since it's on exit, but on a real 3DS, it means exiting to the Homebrew Menu or the HOME Menu isn't possible, which sucks a lot for testing.)
I'm having trouble porting the Normal Mapping example from C to Rust to test the new bindings. The only difference is in image and shader loading and I can't really put my finger on whats wrong. For some reason the 3ds freezes at C3D_FrameBegin
https://gist.github.com/hYdos/c260e0afe72200a4872e7f9000ddae5d - main.rs
https://gist.github.com/hYdos/8f4c4bea934714be008d7157b1512c79 - util.rs
Wondering if you guys would have more knowledge to whats going on
Assets should not be the issue as i use the command from the bitmap example in rust3ds/citru-rs
to generate textures and use picasso to generate the shader bytes
Not much to describe. triangle.rs from the examples
folder runs and displays a triangle on citra but when testing on my n3ds(xl) its a black screen. I'm assuming its waiting/hanging on something as the home menu button is unresponsive. Rosalina is still usable while its like this.
Reproducing code is https://github.com/CenTdemeern1/witness-rs/tree/main/frontend-3ds
Deadlock stack trace seems to be this:
#0 svcArbitrateAddressNoTimeout () at /Users/davem/projects/devkitpro/pacman-packages/libctru/src/libctru-2.3.1/libctru/source/svc.s:253
#1 0x00107bd8 in syncArbitrateAddress (addr=<optimized out>, type=<optimized out>, value=<optimized out>) at /Users/davem/projects/devkitpro/pacman-packages/libctru/src/libctru-2.3.1/libctru/source/synchronization.c:22
#2 0x0010683c in gspWaitForAnyEvent () at /Users/davem/projects/devkitpro/pacman-packages/libctru/src/libctru-2.3.1/libctru/source/services/gspgpu.c:311
#3 0x00136d3c in gxCmdQueueWait (queue=<optimized out>, timeout=<optimized out>) at /Users/davem/projects/devkitpro/pacman-packages/libctru/src/libctru-2.3.1/libctru/source/gpu/gxqueue.c:116
#4 0x00133be0 in C3Di_WaitAndClearQueue (timeout=<optimized out>) at /Users/davem/projects/devkitpro/pacman-packages/citro3d/src/citro3d-1.7.1/source/renderqueue.c:95
#5 0x001342f4 in C3D_RenderTargetSetOutput (target=target@entry=0x0, screen=GFX_TOP, side=GFX_LEFT, transferFlags=transferFlags@entry=0) at /Users/davem/projects/devkitpro/pacman-packages/citro3d/src/citro3d-1.7.1/source/renderqueue.c:381
#6 0x00134318 in C3D_RenderTargetDetachOutput (target=target@entry=0x82048a8) at /Users/davem/projects/devkitpro/pacman-packages/citro3d/src/citro3d-1.7.1/include/c3d/renderqueue.h:69
#7 0x00134348 in C3D_RenderTargetDelete (target=0x82048a8) at /Users/davem/projects/devkitpro/pacman-packages/citro3d/src/citro3d-1.7.1/source/renderqueue.c:366
#8 0x00112bd0 in citro3d::render::{impl#0}::drop (self=0x81ffb6c) at src/render.rs:33
#9 0x001010d8 in core::ptr::drop_in_place<citro3d::render::Target> () at /Users/ianchamberlain/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:514
#10 0x001034e4 in witness_3ds::main () at frontend-3ds/src/main.rs:105
Originally posted by @CenTdemeern1 in rust3ds/ctru-rs#179
Hi, a couple years ago I was tinkering with citro3d-rs and got to the point of being able to load a model from an .obj file (using obj-rs) and display it with stereoscopic 3d.
I think it'd be pretty nice to have as an example, and I could potentially spend some time reimplementing it on modern citro3d-rs if you're interested. I'm not sure if having a example that uses obj-rs would be too heavyweight though. I also had a variant that just renders a cube at one point.
I also pushed my code to https://github.com/vivlim/citro3d-rs-2021 in case it'd be useful, the readme also links to a video of it running on hardware. The code is a mess in part because I was learning as I went, and then burned out.
Right now we require a RefMut<dyn Screen>
to create a render::Target
, but it should be possible to create one based on a texture, or simply based on dimensions -- the screen is only used to automatically set the target's color format. We can have 2 or 3 helpers to create Target
s, probably.
I started this as a fork, but perhaps once it reaches a certain point it can just be moved into the upstream as rust3ds/citro3d-rs
and the https://github.com/rust3ds/citro3d-sys repo can be archived or something like that.
It might make sense to wait for a reasonable level of quality and/or other rust3ds org changes before doing this.
Basically, we should be able to create a BufInfo
object that borrows the backing VBO, so we don't have to pass raw pointers and we can guarantee the data lives as long as needed for it to be rendered.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.