🌋
【Vulkan】BufferDeviceAddressを使ってデータアクセスを楽にしたい
はじめに
Vulkan では、通常のバインディングとは別に Buffer Device Address という機能でバッファにアクセスすることができます。これは、ポインタと似たような機能です。
特に、レイトレーシングパイプラインでは、頂点データやインデックスデータをバッファに格納しておき、 Buffer Device Address を使ってシェーダからアクセスすることが多いです。ユーザ定義の構造体にアクセスする際にも便利です。
この記事では、Buffer Device Address を使ってChitシェーダからヒットしたメッシュの頂点バッファとインデックスバッファにアクセスし、頂点位置を取得する方法を紹介します。
GLSL準備
GLSL側では以下の準備をします。
- 必要な拡張機能を有効化する
- 必要なアドレスを持たせる構造体
MeshAddress
の配列を受け取る -
buffer_reference
を使って頂点配列を用意する
sample.rchit
#version 460
#extension GL_EXT_ray_tracing : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
struct MeshAddress
{
uint64_t vertices;
uint64_t indices;
};
struct Vertex
{
vec3 pos;
};
layout(binding = 0) buffer Addresses { MeshAddress addresses[]; };
layout(buffer_reference, scalar) buffer Vertices { Vertex v[]; };
layout(buffer_reference, scalar) buffer Indices { uvec3 i[]; };
Vulkan準備
Vulkan側では以下の準備をします。
- 必要なFeatureを有効化する
-
vk::DeviceAddress
を含むBufferAddress
構造体を作成する - 参照したいバッファのアドレスを入力する
-
BufferAddress
配列のバッファを作る
main.cpp
void createDevice()
{
// Featuresを有効化する
vk::PhysicalDeviceFeatures deviceFeatures;
deviceFeatures.shaderInt64 = true;
vk::PhysicalDeviceBufferDeviceAddressFeatures addressFeatures;
addressFeatures.bufferDeviceAddress = true;
vk::PhysicalDeviceScalarBlockLayoutFeatures scalarBlockFeatures;
scalarBlockFeatures.scalarBlockLayout = true;
// ...
}
struct BufferAddress
{
vk::DeviceAddress vertices;
vk::DeviceAddress indices;
};
void prepare(){
// バッファにアドレスを持たせる
std::vector<BufferAddress> addresses;
for(auto&& mesh: meshes){
BufferAddress address;
address.vertices = mesh.vertexBuffer.deviceAddress;
address.indices = mesh.indexBuffer.deviceAddress;
addresses.push_back(address);
}
// バッファを作成
addressBuffer = createBuffer(
sizeof(BufferAddress) * addresses.size(),
vk::BufferUsageFlagBits::eStorageBuffer |
vk::BufferUsageFlagBits::eShaderDeviceAddress);
descSet.update("Addresses", addressBuffer);
}
@MatchaChoco010さんの助言により、
vk::PhysicalDeviceScalarBlockLayoutFeatures
を追加しました。ありがとうございます。
GLSLで使う
sample.rchit
// ヒットしたメッシュの頂点バッファとインデックスバッファのアドレスを取得する
MeshAddress address = addresses[gl_InstanceID];
// バッファを参照する
Vertices vertices = Vertices(address.vertices);
Indices indices = Indices(address.indices);
// 頂点を取得する
uvec3 index = indices.i[gl_PrimitiveID];
Vertex v0 = vertices.v[index.x];
Vertex v1 = vertices.v[index.y];
Vertex v2 = vertices.v[index.z];
こんな感じでキレイに書けます。おわりです。
Discussion