-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rapier Physics example blocks runtime init #60
Comments
@ian-h-chamberlain about the last message you sent in #56, I want to say it actually would cause a segmentation fault if it weren’t for a check in the code ( |
I don't think it's possible for it to be a stack issue, right? This is happening before the stack even reaches |
Yeah, but I have no idea how a non-executed line could propagate an error all the way to the std, so I just made hypotheses. |
Just for everyone's reference, I made a repo for the method I used to debug the regex case: https://github.com/ian-h-chamberlain/3ds-codegen-crash-repro It's kind of a brain dump of notes but maybe you'll find it useful. I'm going to see if I can reuse it on this example as well. Edit: also, I'm running on macOS so it's possible some parts of the bisect script aren't 100% portable, and it also relies on a prebuild std for the 3DS instead of |
Okay, I was able to bisect down to a single optimization pass on my system, using this code: use std::time::Duration;
use ctru::console::Console;
use ctru::gfx::Gfx;
use rapier2d::prelude::*;
fn main() {
ctru::init();
let gfx = Gfx::init().expect("Couldn't obtain GFX controller");
let _console = Console::init(gfx.top_screen.borrow_mut());
let mut collider_set = ColliderSet::new();
/* Create the ground. */
let collider = ColliderBuilder::cuboid(100.0, 0.1).build();
collider_set.insert(collider);
println!("Done!");
gfx.flush_buffers();
gfx.swap_buffers();
// So we can see the program got past the crash
std::thread::sleep(Duration::from_secs(2));
} Attempting 37953 passes
+ RUSTFLAGS='-C opt-level=1 -C debuginfo=0 -C codegen-units=1 -C lto=off -C llvm-args=-opt-bisect-limit=37953'
+ cargo -v rustc --target armv6k-nintendo-3ds --target-dir=target-repro
Sending crash-repro.3dsx, 567352 bytes
266001 sent (46.88%), 24 blocks
Did it crash? [y/n] n
Attempting 37952 passes
+ RUSTFLAGS='-C opt-level=1 -C debuginfo=0 -C codegen-units=1 -C lto=off -C llvm-args=-opt-bisect-limit=37952'
+ cargo -v rustc --target armv6k-nintendo-3ds --target-dir=target-repro
Sending crash-repro.3dsx, 588584 bytes
275711 sent (46.84%), 24 blocks
Did it crash? [y/n] y
Final pass was: 37952 These are the candidate passes that actually had that number associated with them:
Anything look suspicious on first glance? My first though is maybe something to do with the drop impls, or perhaps the The next step, I suppose, would be to try and look at the LLVM IR before + after this pass for each of those affected functions, and try to see if any of them look like they might be related to the crash. |
Thanks for taking a look at this. I'm interested in exploring another thread, which is if the static memory for the thread info gets set correctly. Last I looked at the LLVM IR, it was set correctly, but wasn't set correctly by the time of the crash (it was zeroed out, but it should have a non-zero byte to indicate the Option is None). |
Pushed a fix in rust-lang/rust@836146c. Now it's back to the original issue. |
@ian-h-chamberlain are you sure those are the correct opt passes to cause the crash? Using your same parameters the app did’t crash to me… |
@Meziu the exact number will probably depend on your rustc + std (I had a prebuilt std in my case). You might be able to find the pass number on your setup by setting the same Also make sure you If none of that works, you could always try the |
I absolutely cannot check this issue the way @ian-h-chamberlain did. It is great how much effort you've put, but sadly my cpu is just way too slow for the building process (build and rebuild on an Intel i5 M520) (yes i live in 2010). I'm not even sure how to tackle the problem honestly. Looking at the whole "idea" of an optimization pass and how they are handled it makes little sense to me the presence of one function changes the machine code of another. Maybe we still aren't understanding this problem correctly, just as the regex was linked to something different (stack size) that was masked only when a specific amount of optimizations were done, maybe something similar is linked to our issue. |
I've just done a simple test I couldn't do before (especially for @AzureMarker). I've tried running the main directly without any of the |
I’ve started looking into the rapier2d code that caused the problem, and the issue seems to go and come if I just cut out some function calls. E.G. I am still confused and unsure. It is a bit irrational so I don’t know if I checked everything, but it looks to be that way. @ian-h-chamberlain how did you check the stack in the |
The biggest clue I noticed was using The crash in that case occurred immediately afterwards, since I think it was attempting to dereference the null frame pointer. I don't fully understand how that would happen, but those were some of the signs of a corrupt / invalid stack.
This is pretty interesting, it would seem to suggest that simply calling the function (modifying the stack pointer, I guess) is the cause of the problem, rather than any specific code in the function? How deep is the call stack if you put a breakpoint there? |
Ok, I looked at this a bit more and found some interesting things. I'm able to pinpoint the crash as coming from a cast of diff --git a/src/shape/shared_shape.rs b/src/shape/shared_shape.rs
index be4792b..1c1f89a 100644
--- a/src/shape/shared_shape.rs
+++ b/src/shape/shared_shape.rs
@@ -103,7 +103,11 @@ impl SharedShape {
/// Initialize a cuboid shape defined by its half-extents.
#[cfg(feature = "dim2")]
pub fn cuboid(hx: Real, hy: Real) -> Self {
- SharedShape(Arc::new(Cuboid::new(Vector::new(hx, hy))))
+ let vec = Vector::new(hx, hy);
+ let cuboid = Cuboid::new(vec);
+ let arc = Arc::new(cuboid);
+ panic!();
+ SharedShape(arc)
}
/// Initialize a round cuboid shape defined by its half-extents and border radius. Removing the I also captured the difference in MIR between the explicit cast before and the implicit cast after the panic (paths shortened for brevity): --- ../parry2d-crashing.mir 2022-05-15 22:31:05.000000000 -0400
+++ ../parry2d-noncrashing.mir 2022-05-15 22:32:14.000000000 -0400
@@ -103078,15 +103078,13 @@
let mut _4: f32; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:106:31: 106:33
let mut _5: f32; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:106:35: 106:37
let mut _7: na::Matrix<f32, na::Const<2_usize>, na::Const<1_usize>, na::ArrayStorage<f32, 2_usize, 1_usize>>; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:107:34: 107:37
- let mut _9: std::sync::Arc<dyn shape::shape::Shape>; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:53
- let mut _10: std::sync::Arc<shape::cuboid::Cuboid>; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:35
- let mut _11: shape::cuboid::Cuboid; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:28: 108:34
+ let mut _9: shape::cuboid::Cuboid; // in scope 0 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:28: 108:34
scope 1 {
debug vec => _3; // in scope 1 at parry/crates/parry2d/../../src/shape/shared_shape.rs:106:13: 106:16
let _6: shape::cuboid::Cuboid; // in scope 1 at parry/crates/parry2d/../../src/shape/shared_shape.rs:107:13: 107:19
scope 2 {
debug cuboid => _6; // in scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:107:13: 107:19
- let _8: std::sync::Arc<dyn shape::shape::Shape>; // in scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:13: 108:16
+ let _8: std::sync::Arc<shape::cuboid::Cuboid>; // in scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:13: 108:16
scope 3 {
debug arc => _8; // in scope 3 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:13: 108:16
}
@@ -103112,8 +103110,8 @@
}
bb2: {
- _11 = _6; // scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:28: 108:34
- _10 = Arc::<Cuboid>::new(move _11) -> bb3; // scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:35
+ _9 = _6; // scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:28: 108:34
+ _8 = Arc::<Cuboid>::new(move _9) -> bb3; // scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:35
// mir::Constant
// + span: parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:27
// + user_ty: UserType(1)
@@ -103121,8 +103119,6 @@
}
bb3: {
- _9 = move _10 as std::sync::Arc<dyn shape::shape::Shape> (Pointer(Unsize)); // scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:35
- _8 = move _9; // scope 2 at parry/crates/parry2d/../../src/shape/shared_shape.rs:108:19: 108:53
panic(const "explicit panic") -> bb4; // scope 3 at /Users/ianchamberlain/Documents/Development/3ds/rust-horizon/library/core/src/panic.rs:50:9: 50:51
// mir::Constant
// + span: /Users/ianchamberlain/Documents/Development/3ds/rust-horizon/library/core/src/panic.rs:50:9: 50:33 The changes are fairly small but it is surprising to me that a simple trait cast like this would actually change the behavior of the entire program. There's still some mystery to solve about why the crash happens in a different part of the code, but it's interesting that the trait cast itself seems to have an effect. I don't know too much about how |
Looking at the initial thread local storage in both debug and release, they both set the @ian-h-chamberlain you were able to get it into |
@AzureMarker this is the minimum reproduction I've got now: use std::sync::Arc;
use ctru::services::soc::Soc;
use parry2d::na::Vector2;
use parry2d::shape::{Cuboid, Shape};
fn main() {
ctru::init();
let mut soc = Soc::init().expect("failed to init SOC");
soc.redirect_to_3dslink(true, true)
.expect("failed to redirect stdio");
eprintln!("creating vec");
let v = Vector2::new(1.0, 1.0);
eprintln!("creating cuboid");
let c = Cuboid::new(v);
eprintln!("creating Arc");
let a = Arc::new(c);
// eprintln!("casting Arc");
// let b = a as Arc<dyn Shape>; // uncomment this, and crash happens before main
eprintln!("Done!");
} Interestingly, if I use Edit: stack trace of the exception below. I guess I might actually need to pull out some of the panic handler stuff too, let me see... (gdb) bt
#0 0x001656a0 in core::sync::atomic::atomic_add<usize> (dst=0x0, val=1,
order=core::sync::atomic::Ordering::Relaxed)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/core/src/sync/atomic.rs:2604
#1 0x00165d20 in core::sync::atomic::AtomicUsize::fetch_add (self=0x0, val=1,
order=core::sync::atomic::Ordering::Relaxed)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/core/src/sync/atomic.rs:1957
#2 0x0015d644 in alloc::sync::{impl#22}::clone<std::thread::Inner> (self=0x2b1c20)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/alloc/src/sync.rs:1343
#3 0x001b8f44 in std::thread::{impl#22}::clone (self=0x2b1c20)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/thread/mod.rs:1099
#4 0x00160038 in std::sys_common::thread_info::current_thread::{closure#0} (info=0x2b1c14)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/sys_common/thread_info.rs:34
#5 0x0015ff68 in std::sys_common::thread_info::{impl#0}::with::{closure#0}<std::thread::Thread, std::sys_common::thread_info::current_thread::{closure_env#0}> (thread_info=0x2b1c10)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/sys_common/thread_info.rs:27
#6 0x0015e648 in std::thread::local::LocalKey<core::cell::RefCell<core::option::Option<std::sys_common::thread_info::ThreadInfo>>>::try_with<core::cell::RefCell<core::option::Option<std::sys_common::thread_info::ThreadInfo>>, std::sys_common::thread_info::{impl#0}::with::{closure_env#0}<std::thread::Thread, std::sys_common::thread_info::current_thread::{closure_env#0}>, std::thread::Thread> (self=0x2830cc, f=...)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/thread/local.rs:442
#7 0x0015fe90 in std::sys_common::thread_info::ThreadInfo::with<std::thread::Thread, std::sys_common::thread_info::current_thread::{closure_env#0}> (f=...)
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/sys_common/thread_info.rs:20
#8 0x00160010 in std::sys_common::thread_info::current_thread ()
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/sys_common/thread_info.rs:34
#9 0x001b7d20 in std::thread::current ()
at /Users/ianchamberlain/.rustup/toolchains/horizon/lib/rustlib/src/rust/library/std/src/thread/mod.rs:676
#10 0x0012a5c0 in ctru::panic_hook_setup () at ctru-rs/src/lib.rs:42
#11 0x0012a5a8 in ctru::init () at ctru-rs/src/lib.rs:34
#12 0x00101244 in rapier_physics::main (_argc=1, _argv=0x8000000) at ctru-rs/examples/rapier-physics.rs:13
(gdb) |
Does it need the SOC setup? Is it possible to trim it down to just the Arc casting (possibly with a simpler to construct data type)? |
The destination pointer is null, which is causing the crash. |
Ok, I tried to remove more, I so far haven't been able to find a set of simpler types to reproduce but this is a fairly small program that shows it: #![feature(start)]
use std::sync::Arc;
use parry2d::na::Vector2;
use parry2d::shape::{Cuboid, Shape};
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
linker_fix_3ds::init();
pthread_3ds::init();
let _ = std::thread::current(); // crashes here
let v = Vector2::new(1.0, 1.0);
let c = Cuboid::new(v);
let a = Arc::new(c);
// but only if this line is present:
let b = a as Arc<dyn Shape>;
0
} I still can't quite track down where the value is getting initialized improperly, and it's getting late so I think I'll have to pause for tonight. Something I wonder is if we might be hitting this path prematurely? https://github.com/rust-lang/rust/blob/master/library/std/src/thread/local.rs#L249 |
That's a good point about the state value. I haven't checked that during my debugging, but I'll take a look at it soon. |
I did more debugging and I saw that Unfortunately gdb says that I might try disabling Here's some screenshots of the debug and release executions. Notice that both runs start off with the thread info as |
Yeah, it works if I disable |
I don’t like this solution. This means that LocalKey instances aren’t being dropped, and that we use our slower version with We should search more in the tests me and @ian-h-chamberlain have done. That flag works flawlessly everytime but with this one specific crate (and its specific functions). |
Yeah we can continue investigating, but to fix it for now (especially when merging to std - it should be stable) disabling it is the right step. Edit: they're still dropped right? They should use the pthread key destructors. Though I don't think we finished implementing them. |
I've spent a bit more time trying to come up with a minimal reproduction (using some methodology from https://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns), and I've pushed the results to https://github.com/ian-h-chamberlain/3ds-codegen-crash-repro Unfortunately I might try to look at the IR generated for different cases (like why |
Okay, wow! I found something super interesting relating to the enum discriminant / memory layout. Correct behavior (
Bad behavior (
So somehow, it seems adding the extra calls to Another interesting point:
Maybe this means that the address of the thread local is being offset by the presence of those two stack variables, which can be optimized out (or moved, or whatever) if they're not used in the The good news seems to be the thread-local variable is still being initialized properly, the bad news being that it's generated with the wrong address! I'm not really sure how to debug this kind of problem further, maybe we'd need to look at the TLS implementation in LLVM? Or is it possible this is a 3DS kernel bug or something up with the libctru TLS implementation? I started reading https://www.3dbrew.org/wiki/Thread_Local_Storage but I am way out of my depth here... Edit: I also tried to reproduce this with other 24-byte stack variables but so far have not been able to reproduce. Some stuff deep in the bowels of |
Hmm, I don't think it's an OS/kernel issue (and even if it was, the conditions under which it occurs are very very specific).
At what opt-level did you get this info? Does it change with release or debug mode? Edit: |
Both The multiplication definitely is a lot of code... I tried duplicating the logic myself with custom types (copy-pasted from Maybe the call to that function is related to the extra code (and the origin of the weird thread-local offset)? |
The issue is in |
Hmm, that is interesting, especially since those calls are behind an Meanwhile I've been trying to track down the cause of the weird offset, and I found this: in the code that dereferences the thread-local, there is something like this: 1084c0: e59f10dc ldr r1, [pc, #220] ; 1085a4 <crash_repro::repro+0x164>
1084c4: eb0000bc bl 1087bc <__aeabi_read_tp>
1084c8: e0800001 add r0, r0, r1
1084cc: ebffe7ab bl 102380 <core::cell::RefCell<T>::try_borrow_mut> The 10859c: 001794b8 .word 0x001794b8
1085a0: 0017995c .word 0x0017995c
1085a4: 00000020 .word 0x00000020 ; <-- this one
1085a8: 00179994 .word 0x00179994
1085ac: 00179140 .word 0x00179140
1085b0: 00179978 .word 0x00179978 In a correct program, that offset is
.LCPI234_33:
.long .L__unnamed_45
.p2align 2
.LCPI234_34:
.long crash_repro::LOCAL_STATIC(TPOFF) ; <-- this one
.p2align 2
.LCPI234_35:
.long __tls_start
.p2align 2
.LCPI234_36:
.long .L__unnamed_46
.p2align 2 This output looks the same for both correct and incorrect programs, which is why I suspect an LLVM bug, but I'm not too sure what else to look at. I did find this issue which looks to be sort of similar, but only applies for a certain relocation model (not You can get the program to print the value of the offset with something like this: #![feature(asm_sym)]
#![feature(thread_local)]
#[thread_local]
static LOCAL_STATIC: ThreadLocal = ThreadLocal::new(None);
fn main() {
let mut offset: u32;
unsafe {
asm!("
ldr {offset}, ={local_static}(TPOFF)
",
local_static = sym LOCAL_STATIC,
offset = out(reg) offset,
);
}
eprintln!("offset = {offset:#X}");
} With that in mind, I'm going to see if I can reduce the program further, but I imagine that it would still need the |
@ian-h-chamberlain I can confirm the issue happens on the loop at https://github.com/bluss/matrixmultiply/blob/4f841faf50fd61367123b89289dc361f34b1f33e/src/gemm.rs#L299. Yes, those are thread locals that are used... I'll look into the exact reason behind the issue but it's already starting to look like a crate specific issue. |
This find makes it way easier to see the issue: rust3ds/ctru-rs#60 (comment)
@Meziu nice find! With that, the reproduction gets way smaller and actually reproduces without any dependencies (other than I have a feeling it may be related to this issue and that LLVM is generating some weird alignment or something: rust-lang/rust#51828 |
I'd like to note it still makes a difference between |
Good point, I was able to bisect fairly quickly since the program doesn't force a crash + reboot of the device any longer. These are the passes I found: $ egrep '\(99\)' cargo-bisect-output.txt | rustfilt
BISECT: running pass (99) EarlyCSEPass on std::ffi::os_str::<impl core::convert::AsRef<std::ffi::os_str::OsStr> for alloc::string::String>::as_ref
BISECT: running pass (99) EarlyCSEPass on core::ptr::drop_in_place<once_cell::imp::Guard>
BISECT: running pass (99) EarlyCSEPass on <libc::unix::newlib::in_addr as core::clone::Clone>::clone
BISECT: running pass (99) EarlyCSEPass on <&T as core::fmt::Debug>::fmt
BISECT: running pass (99) PromotePass on core::mem::maybe_uninit::MaybeUninit<T>::as_mut_ptr
BISECT: running pass (99) EarlyCSEPass on <core::ops::range::RangeTo<usize> as core::slice::index::SliceIndex<[T]>>::get_unchecked_mut
BISECT: running pass (99) EarlyCSEPass on <core::ops::range::RangeFrom<usize> as core::slice::index::SliceIndex<[T]>>::index
BISECT: running pass (99) IPSCCPPass on [module] I suspect the last one (it's some kind of constant propagation that runs on the whole program), but after looking a bit I fear that this may just be masking the real issue, and the code has moved around enough that the offsets "happen to work" or something like that. When I examined the object file before linking, it has all zeros for the values of the thread-local offsets, so I think it's actually the linker that fills in this information based on the relocation table when it links the final executable. So I think there are two most likely possibilities:
I might play around with some different relocation flags to the linker and see what changes, if anything. Edit: something else worth mentioning that I found, is that I don't think this is the root cause, because I was able to reproduce the issue in Citra using the plain |
Ok, I've gotten some evidence that this is an issue with the linker and/or the linker script, rather than LLVM. We may still end up wanting to work around it with a rustc workaround, but maybe we can just fix the upstream devkitARM toolchain instead. This C program also reproduces the issue:#include <3ds.h>
#include <stdio.h>
#include <string.h>
typedef ALIGN(4) struct {
u8 inner[3];
} Align4;
typedef ALIGN(16) struct {
u8 inner[3];
} Align16;
static __thread Align4 BUF_4 = {.inner = {2, 2, 2}};
static __thread Align16 BUF_16 = {.inner = {1, 1, 1}};
int
main(int argc, char** argv)
{
gfxInitDefault();
consoleInit(GFX_TOP, NULL);
BUF_16.inner[0] = 0;
bool reproduced = false;
printf("[");
for (int i = 0; i < 3; i++) {
if (BUF_4.inner[i] != 2) {
reproduced = true;
}
printf("%d, ", BUF_4.inner[i]);
}
printf("]\n");
if (reproduced) {
printf("reproduced!\n");
}
else {
printf("nope");
}
// Main loop
while (aptMainLoop()) {
gspWaitForVBlank();
hidScanInput();
u32 kDown = hidKeysDown();
if (kDown & KEY_START)
break; // break in order to return to hbmenu
// Flush and swap framebuffers
gfxFlushBuffers();
gfxSwapBuffers();
}
gfxExit();
return 0;
} The symptoms look roughly the same, although of course with a different compiler things seem to get reordered a bit, but if I remove |
Thanks both of you for the deep investigation! |
For example, in the following issue the `thread_info` thread-local is not correctly initialized in debug builds: rust3ds/ctru-rs#60
@AzureMarker we should probably try to close this issue before pushing the thread-related code to Rust’s tree. Having support for |
Btw, since it hasn't been mentioned yet, I'd like to dump this link here about |
In a related note – I've tried to ask about this on the devkitpro.org forums, but it seems my post is still hidden since I am a newly registered user there. Hopefully a mod will approve it soon so the question actually gets posted to the forums... I will link it here if + when it does. |
I've opened an issue on |
Can I open the thread PR in parallel? We can always open another small PR to re-enable the native thread local support. |
Well, neither the post on the forum nor the issue have had any immediate reaction. Opening it already will quicken the merge times (especially for the review). You can open it. |
FYI: I opened the thread PR: |
Until devkitPro/libctru#497 is somehow fixed (pretty much can't be) we can't do anything about this. The fix we already have in place (using a heap-based TLS system) is fine for now, but it hurts to see so much wasted performance. |
We could still leave this issue open, since it is an "open issue". |
Well, yeah, but this is neither a On a side note, can you confirm TLS variables are dropped correctly in our current pthread implementation? I haven’t been able to test it out due to the thread-supporting |
I don't think we call the destructors. Or, at least I didn't find any code that does that. I opened rust3ds/pthread-3ds#19 |
Hooray!! When I use all the following:
The example now works, both emulated and on hardware! Screenshot from Citra (since Luma screenshots still show up blank sometimes): |
Thanks @ian-h-chamberlain 🥳 |
Continuing off #56 discussions.
@ian-h-chamberlain @AzureMarker
The new example in the test/rapier-physics branch doesn’t run correctly.
While the actual panic (which catches a potential ARM Exception) is situated in the
thread_info::set
function instd
, the actual problem is only present when a specific function call from therapier2d
crate is present, and only atopt-level = 0
, in either release or debug mode.We can thus speculate it’s either an issue with optimisations during the compilation process, a stack fault, or an overflow in the
thread_local
storage.The text was updated successfully, but these errors were encountered: