I'm trying to port an bgfx application I wrote in C++ to Rust using bgfx-rs. Everything works great, until I try to copy some vertex data from the application to an transient vertex buffer.
let vertices: [f32; 16] = [
0.0f32, 0.0f32, 0.0f32, 0.0f32,
1024.0f32, 0.0f32, 1024.0f32, 0.0f32,
0.0f32, 1024.0f32, 0.0f32, 1024.0f32,
1024.0f32, 1024.0f32, 1024.0f32, 1024.0f32,
];
let builder = bgfx::VertexLayoutBuilder::new();
builder.begin(RendererType::Noop);
builder.add(
bgfx::Attrib::Position,
2,
bgfx::AttribType::Float,
bgfx_rs::static_lib::AddArgs {
normalized: false,
as_int: false,
},
);
builder.add(
bgfx::Attrib::TexCoord0,
2,
bgfx::AttribType::Float,
bgfx_rs::static_lib::AddArgs {
normalized: false,
as_int: false,
},
);
builder.end();
let mut tvb = bgfx::TransientVertexBuffer::new();
if bgfx::get_avail_transient_vertex_buffer(4, &builder) == 4 {
bgfx::alloc_transient_vertex_buffer(&mut tvb, 4, &builder);
unsafe {
let mut data = *tvb.data;
std::ptr::copy_nonoverlapping(
vertices.as_ptr() as *const u8,
&mut data,
std::mem::size_of::<[f32; 16]>(),
); //this line causes SIGSEGV
}
}
When I add this std::ptr::copy_nonoverlapping
operation the program is crashing due to SIGSEGV signal.
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
What is the best way to debug the cause of an SIGSEGV signal? I've tried running the code using GDB, but until the crash happens there is no indication of the reason visible at all (I can step through the signal handler but there is no additional information about the crash). If I copy less bytes (std::mem::size_of::<[f32; 16]>()
resolves to 64 bytes; i.e. if I just copy 32 bytes instead) the application is not crashing (but obviously some of my vertex data is missing).
I'm using the unsafe block, because that is the only way I got my C++ code working fighting the borrow-checker. For reference my code in C++ (which works fine):
const float vertices[16] = {
0.0f, 0.0f, 0.0f, 0.0f,
1024.0f, 0.0f, 1024.0f, 0.0f,
0.0f, 1024.0f, 0.0f, 1024.0f,
1024.0f, 1024.0f, 1024.0f, 1024.0f
};
ms_decl // bgfx::VertexLayout
.begin()
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
.end();
bgfx::TransientVertexBuffer tvb;
if (bgfx::getAvailTransientVertexBuffer(4, ms_decl) == 4) {
bgfx::allocTransientVertexBuffer(&tvb, 4,ms_decl);
bx::memCopy(tvb.data, vertices, 16 * sizeof(float));
}
The reason I have to use std::ptr::copy_nonoverlapping
in the Rust version is that tvb.data
is an uint8_t*
in C++ (reference), while it is const u8*
in Rust (reference).
Any help if there is something wrong in my unsafe block or how to get to the bottom of the SIGSEGV would be highly appreciated.
I have tried:
std::ptr::copy
instead (I don't expect the source & destination memory to overlap), but still the same SIGSEGV occurredAssuming I am reading the correct documentation, the issue is you are copying to the stack instead of the data pointer. Since tvb.data
is a *mut u8
, the call let mut data = *tvb.data;
dereferences the pointer and copies the value to the stack meaning the type of data
is u8
. Then in ptr::copy_nonoverlapping
, data
is referenced to get the write location. However since data
is now a single byte on the stack and is not nearly large enough to hold the copied value, copy_nonoverlapping
overwrites a ton of stuff on the stack leading to a segfault.
The solution is just cast tvb.data
to a mutable pointer before passing it to copy_nonoverlapping
. Raw pointers can break the mutability rules a bit since they are unsafe to read either way.
let mut tvb = bgfx::TransientVertexBuffer::new();
if bgfx::get_avail_transient_vertex_buffer(4, &builder) == 4 {
bgfx::alloc_transient_vertex_buffer(&mut tvb, 4, &builder);
unsafe {
std::ptr::copy_nonoverlapping(
&vertices as *const u8,
tvb.data as *mut u8,
std::mem::size_of::<[f32; 16]>(),
);
}
}