ダイナミックユニフォーム
- ダイナミックユニフォームを使用する場合、wgpuDeviceCreateBufferで作成するバッファのサイズは2のn乗にする必要がある
- ダイナミックユニフォームを使用する場合、wgpuRenderPassEncoderSetBindGroupのdynamicOffsetCountとdynamicOffsetsはShaderで定義しているbindingの数だけ用意する必要があり、そのサイズはそのbindingが参照しているバッファの値にする必要がある
バッファの作成
webgpu.h(Dawn)でバッファを作成する時、descriptorを以下のように書く。
WGPUBufferDescriptor bufferDesc{};
bufferDesc.nextInChain = nullptr;
bufferDesc.usage = Usage;
bufferDesc.mappedAtCreation = false;
bufferDesc.size = ByteSize;
WGPUBufferDescriptor構造体のメンバ変数には他にもlabelがあるが、宣言によるとこれはnullableなため、これを記述しなくてもC++側のwebgpu.hではバッファが正常に動作する。
typedef struct WGPUBufferDescriptor {
WGPUChainedStruct const * nextInChain;
char const * label; // nullable
WGPUBufferUsageFlags usage;
uint64_t size;
bool mappedAtCreation;
} WGPUBufferDescriptor;
しかし、Emscriptenビルド後のWebAssembly版のWebGPUだとlabelを記述しないと
buffer usages must not be 0. - While calling [Device].CreateBuffer([BufferDescriptor]).
と警告が出てバッファが生成されず、うまく描画されない。なので以下のようにちゃんとlabelも書いた方がいいかも・・・
WGPUBufferDescriptor bufferDesc{};
bufferDesc.nextInChain = nullptr;
bufferDesc.label = "Buffer";
bufferDesc.usage = Usage;
bufferDesc.mappedAtCreation = false;
bufferDesc.size = ByteSize;
[WebGPU, Dawn] min_uniform_buffer_offset_alignment エラーについて
概要
WebGPU(Dawn)でバインドグループを書いていると
UnalignedBufferOffset(64, "min_uniform_buffer_offset_alignment", 256)
のようなエラーが出て動かない時があった。
原因
このエラーは、同じユニフォームバッファの値をオフセットでバインドする際、そのオフセットが256バイトよりも小さい時に発生する
@group(0) @binding(0) var<uniform> color : vec4<f32>;
@group(0) @binding(1) var<uniform> mvp : mat4x4<f32>;
std::vector<WGPUBindGroupEntry> bindingList(2);
{
int Offset = 0;
int Stride = 4;
bindingList[0].nextInChain = nullptr;
bindingList[0].binding = 0;
bindingList[0].buffer = m_UniformBuffer;
bindingList[0].offset = Offset;
bindingList[0].size = Stride * sizeof(float);
}
{
int Offset = 4 * sizeof(float);
int Stride = 16;
bindingList[1].nextInChain = nullptr;
bindingList[1].binding = 1;
bindingList[1].buffer = m_UniformBuffer;
bindingList[1].offset = Offset;
bindingList[1].size = Stride * sizeof(float);
}
なのでGLSLでよく書くようなたくさんuniformを並べた書き方はできない
uniform float _val;
uniform mat4 _mat;
uniform vec4 _v;
APIによるとこの256バイトがデフォルトの値で、もしかするとこれよりも小さい値を別途設定することもできるかもしれないが、おそらく値が小さいほどバインドする回数が増えて効率が悪くなるから256バイトよりは小さくしない方がよさそう
解決策
全部詰める
floatやvec4, mat4x4といった通常のUniformは全て同じbindingに詰めておいてTextureとかDynamicOffsetを使うときだけ別のbindingに詰める。これが一番良き
struct TestBuffer {
val0: f32,
val1: f32,
val2: f32,
val3: f32,
v0: vec4<f32>,
v1: vec4<f32>,
v2: vec4<f32>,
m1 : mat4x4<f32>,
m2 : mat4x4<f32>,
m3 : mat4x4<f32>,
}
@group(0) @binding(0) var<uniform> ubo : TestBuffer;
素直に256バイト単位で分ける
冗長だけど・・・
struct TestBuffer0 {
val0: f32,
val1: f32,
val2: f32,
val3: f32,
v0: vec4<f32>,
v1: vec4<f32>,
v2: vec4<f32>,
m1 : mat4x4<f32>,
m2 : mat4x4<f32>,
m3 : mat4x4<f32>,
}
struct TestBuffer1 {
val0: f32,
val1: f32,
val2: f32,
val3: f32,
v0: vec4<f32>,
v1: vec4<f32>,
v2: vec4<f32>,
m1 : mat4x4<f32>,
m2 : mat4x4<f32>,
m3 : mat4x4<f32>,
}
@group(0) @binding(0) var<uniform> ubo0 : TestBuffer0;
@group(0) @binding(1) var<uniform> ubo1 : TestBuffer1;
ユニフォームバッファをバインド毎に変える
サンプルを見ているとユニフォームバッファを変えてオフセットを0にすると複数の値が使えるみたい(別々に分けたUniformBufferをさらにオフセットで分割するなら256バイトに気を配る必要があるが・・・)
std::vector<WGPUBindGroupEntry> bindingList(2);
{
int Offset = 0;
int Stride = 4;
bindingList[0].nextInChain = nullptr;
bindingList[0].binding = 0;
bindingList[0].buffer = m_UniformBuffer0;
bindingList[0].offset = Offset;
bindingList[0].size = Stride * sizeof(float);
}
{
int Offset = 0;
int Stride = 16;
bindingList[1].nextInChain = nullptr;
bindingList[1].binding = 1;
bindingList[1].buffer = m_UniformBuffer1;
bindingList[1].offset = Offset;
bindingList[1].size = Stride * sizeof(float);
}
@group(0) @binding(0) var<uniform> color : vec4<f32>;
@group(0) @binding(1) var<uniform> mvp : mat4x4<f32>;