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:
- using
std::ptr::copyinstead (I don't expect the source & destination memory to overlap), but still the same SIGSEGV occurred
Assuming I am reading the correct documentation, the issue is you are copying to the stack instead of the data pointer. Since
tvb.datais a*mut u8, the calllet mut data = *tvb.data;dereferences the pointer and copies the value to the stack meaning the type ofdataisu8. Then inptr::copy_nonoverlapping,datais referenced to get the write location. However sincedatais now a single byte on the stack and is not nearly large enough to hold the copied value,copy_nonoverlappingoverwrites a ton of stuff on the stack leading to a segfault.The solution is just cast
tvb.datato a mutable pointer before passing it tocopy_nonoverlapping. Raw pointers can break the mutability rules a bit since they are unsafe to read either way.