状況 #
wgpu error: Validation Error
Caused by:
In Queue::write_buffer
Copy of 0..400 would end up overrunning the bounds of the Destination buffer of size 0
のようなエラーメッセージが🔗wgpu::Queue::write_buffer関数を呼んだ際に出る。
原因 & 解決方法 #
エラーメッセージはバッファの最大サイズを超えた書き込みをしようとしていることを検出して警告してくれている。
wgpu拡張の(wgpu::util::DeviceExt, wgpu::util) 🔗wgpu::Device::create_buffer_init関数でバッファを作成すると、作成する際に渡したデータの大きさでバッファの最大サイズが設定される。
一度作成したバッファの最大サイズは実行時に変更できないようだ。
サンプルコードなどでもcreate_buffer_init
は多用されているが、実際のアプリケーションでは、バッファの書き込みサイズがチマチマ変わることも多いので、最大サイズを自分で設定できる🔗wgpu::Device::create_buffer関数を利用する必要がある。
具体的にcreate_buffer
で作成したバッファにどうやってデータをセットするかだが、create_buffer_init
関数の実装がシンプルなので見てマネをするか、wgpu::Queue::write_buffer
で書き込めば良い。
create_buffer_init
内の書き込み処理。
buffer.slice(..).get_mapped_range_mut()[..unpadded_size as usize]
.copy_from_slice(descriptor.contents);
buffer.unmap();
とQueue::write_buffer
のようにsubmitでまとめて後で書き込むのではなく同期的にGPUバッファをマッピングしてそこにデータを書き込んでいる。
バッファの最大サイズはwgpu::COPY_BUFFER_ALIGNMENT
の倍数にしておく必要がある。
バックエンド問わずcreate_buffer_init
内で勝手にalignしてくれているので例えDirectXやOpenGLを利用していても統一しておいたほうが良いはず。
参考までに該当コードのBeforeAfterを載せておく。
// Before
let cube_instance_buffer =
wgpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("cube instance buffer"),
contents: bytemuck::cast_slice(&cube_instances),// バッファの内容を渡して初期化
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
// After
let cube_instance_buffer = wgpu_device.create_buffer(&wgpu::BufferDescriptor {
label: Some("cube instance buffer"),
size: (pit_size_xyz.0 * pit_size_xyz.1 * pit_size_xyz.2) as u64
* std::mem::size_of::<CubeWGPUInstance>() as u64,// バッファの最大サイズのみ指定して後でwgpu::Queue::write_bufferで内容を書き込んだり更新する。
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});