5-3. バッファへのデータ書き込み

バッファが準備できたら今度はそこにデータを書き込んでいきます。


まず前準備として、前回のバッファ作成処理におけるメモリ確保を行う部分にひと手間加える必要があります。

メモリタイプによって、ホスト側から書き込めるメモリと書き込めないメモリがあります。今回はデータを書き込みたいわけなので、ホスト側から書き込めるようなメモリを選ばなければいけません。メモリタイプを選ぶ部分の処理を以下のように書き換えましょう。

for (int i = 0; i < memProps.memoryTypeCount; i++) {
    if (vertexBufMemReq.memoryTypeBits & (1 << i) &&
        (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)) {
        memAllocInfo.memoryTypeIndex = i;
        suitableMemoryTypeFound = true;
        break;
    }
}

ホスト側から見えるようなメモリ(HostVisible)かどうかのチェックを追加しています。このチェックを通るタイプのメモリならばホスト側からデータを読み書きすることができます。

これについては一応3-9節でも軽く解説しました。

これでデータを書き込むための前準備は完了です。頂点のデータを書き込んでいきましょう。


具体的にデバイスメモリに書き込む方法ですが、メモリマッピングというものをします。これは、操作したい対象のデバイスメモリを仮想的にアプリケーションのメモリ空間に対応付けることで操作出来るようにするものです。対象のデバイスメモリを直接操作するわけにはいかないのでこういう形になっています。

これには、デバイスのmapMemoryメソッドを使います。

void* vertexBufMem = device->mapMemory(vertexBufMemory.get(), 0, sizeof(Vertex) * vertices.size());

第1引数はメモリマッピングを行う対象のデバイスメモリ(vk::DeviceMemory)です。

第2、第3引数にはメモリマッピングを行う範囲を指定します。第2引数が範囲の開始地点(先頭から何バイト目)、第3引数が範囲の大きさ(バイト数)です。今回は第2引数に0、第3引数にデバイスメモリの大きさと同じ値を示しているので、確保したメモリの最初から最後までをマッピングしています。

戻り値でマッピングが行われたメモリのアドレスが返ってくるので、この戻り値のアドレスに対して色々操作を行うことでデータを書き込む(読み込む)ことができます。

どんな方法をとっても特に問題はないのですが、ここではmemcpyを使います。頂点のデータをコピーしていきましょう。

std::memcpy(vertexBufMem, vertices.data(), sizeof(Vertex) * vertices.size());

なお、Vulkanではない部分の話になりますがstd::memcpyを使用するには<cstring>をインクルードする必要があることに気を付けてください。

書き込んだらflushMappedMemoryRangesを行うことで、マッピングされたメモリに書き込んだ内容がデバイスメモリに反映されます。マッピングされたメモリはあくまで仮想的にデバイスメモリと対応付けられているだけなので、「同期しておけよ」と念をおさないとデータが同期されない可能性があるのです。

vk::MappedMemoryRange flushMemoryRange;
flushMemoryRange.memory = vertexBufMemory.get();
flushMemoryRange.offset = 0;
flushMemoryRange.size = sizeof(Vertex) * vertices.size();

device->flushMappedMemoryRanges({ flushMemoryRange });

同期を行う対象と範囲はvk::MappedMemoryRangeで表され、複数指定できます。(今回は1つだけですが。)

memoryはデバイスメモリ、offsetは範囲の開始位置、sizeは範囲の大きさです。

上のコードは冗長なのでこのように書いても良いでしょう。

device->flushMappedMemoryRanges({ vk::MappedMemoryRange(vertexBufMemory.get(), 0, sizeof(Vertex) * vertices.size()) });

作業が終わった後はunmapMemoryできちんと後片付けをします。

device->unmapMemory(vertexBufMemory.get());

これでデータを書き込むことができました。


この節ではバッファのメモリに頂点データを書き込みました。次節ではシェーダにこのデータを渡すための段取りとして、デスクリプション(データの読み込み方の情報)を用意します。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です