はじめに
Vulkanを段階的に, 私のために学習していきます.私のための学習ですので, プログラミング, CGの基礎, 概要は省略します.
始めにライブラリのロードを行いました.
IntelのチュートリアルAPI without Secretsを参考にしています.
コードはGitHubに置くことにします.
ライブラリロード
VulkanのAPIはOpenGLと同様に, APIを動的リンクライブラリからロードして使用するスタイルです.Khronos GroupのVulkan Documentsに, API仕様がXMLファイルとして定義されています.
実装は各ベンダが提供します. C関数宣言としてのインターフェイスもXML仕様から生成されます.
とりあえず, Windows環境でデバイスの初期化を試してみます.
Vulkan (Ver. 1)のライブラリ名は"vulkan-1.dll"や"libvulkan.so.1"です.
"LoadLibrary"や"dlopen"でライブラリをロードし,
"GetProcAddress"や"dlsym"で関数アドレス取得,
"FreeLibrary"や"dlclose"でライブラリをアンロード, という流れになります.
#ifdef _WIN32
#define VK_USE_PLATFORM_WIN32_KHR 1
#define VLK_DLHANDLE HMODULE
#define VLK_DLOPEN(path) LoadLibrary((path))
#define VLK_DLSYM(handle, name) GetProcAddress((handle), (name))
#define VLK_DLCLOSE(handle) FreeLibrary((handle))
#define VLK_VULKANLIB ("vulkan-1.dll")
#else
#define VK_USE_PLATFORM_XLIB_KHR 1
#define VLK_DLHANDLE void*
#define VLK_DLOPEN(path) dlopen((path), RTLD_NOW)
#define VLK_DLSYM(handle, name) dlsym((handle), (name))
#define VLK_DLCLOSE(handle) dlclose((handle))
#define VLK_VULKANLIB ("libvulkan.so.1")
#endif
namespace vk
{
//--------------------------------------------------------------
//---
//--- Lib
//---
//--------------------------------------------------------------
class Lib
{
public:
Lib();
~Lib();
inline bool valid() const;
VkResult initialize();
void terminate();
private:
Lib(const Lib&) = delete;
Lib& operator=(const Lib&) = delete;
VLK_DLHANDLE handle_;
};
inline bool Lib::valid() const
{
return VLK_NULL != handle_;
}
Lib::Lib()
:handle_(VLK_NULL)
{
}
Lib::~Lib()
{
terminate();
}
VkResult Lib::initialize()
{
if(VLK_NULL != handle_){
return VK_ERROR_INITIALIZATION_FAILED;
}
handle_ = VLK_DLOPEN(VLK_VULKANLIB);
if(VLK_NULL == handle_){
return VK_ERROR_INITIALIZATION_FAILED;
}
return VK_SUCCESS;
}
void Lib::terminate()
{
if(VLK_NULL != handle_){
VLK_DLCLOSE(handle_);
handle_ = VLK_NULL;
}
}
}
インスタンス, デバイス生成
次に関数ポインタをロードします.関数のプロトタイプは, Vulkan Documentsにヘッダファイルとして宣言があります.
PFN_(name)の型で関数ポインタの型宣言があるため, 例えば以下のように文字列でアドレスを取得します.
#define VLK_GET_FUNCTION(name) name ## _ = (PFN_ ## name)VLK_DLSYM(handle_, #name);APIが変更されることはほとんどないため, 手で全て書いてもよいですが面倒なので, vk.xmlから抽出します.
例えば, "VLK_EXPORTED_FUNCTION(vkCreateInstance)"として"VkFunctions.inc"に出力しておけば,
#define VLK_EXPORTED_FUNCTION(name) extern PFN_ ## name name ## _;
#include "VkFunctions.inc"
#define VLK_EXPORTED_FUNCTION(name) PFN_ ## name name ## _ = VLK_NULL;
#include "VkFunctions.inc
VkResult Lib::initialize()
{
if(VLK_NULL != handle_){
return VK_ERROR_INITIALIZATION_FAILED;
}
handle_ = VLK_DLOPEN(VLK_VULKANLIB);
if(VLK_NULL == handle_){
return VK_ERROR_INITIALIZATION_FAILED;
}
#define VLK_EXPORTED_FUNCTION(name) VLK_GET_FUNCTION(name, "Error: cannot get exported %s\n")
#define VLK_GET_FUNCTION(name, message) name ## _ = (PFN_ ## name)VLK_DLSYM(handle_, #name);\
if(VLK_NULL == name ## _){\
PRINT1_ERR(message, #name);\
return VK_ERROR_INITIALIZATION_FAILED;\
}
#include "VkFunctions.inc"
#undef VLK_GET_FUNCTION
return VK_SUCCESS;
}
のようにしておくと, "(name)_"でグローバル関数として呼び出すことができます.ここまでできると, 後は仕様どおりに初期化して使用するだけです.
とりあえず, デバイスの生成までできました.
クラスでwrapしていますが, vkCreateInstanceやvkCreateDeviceを呼び出しているだけです.
bool selectQueueFamily(vk::PhysicalDevice& selectedDevice, vk::u32& selectedQueueFamilty, vk::Instance& instance);
int main(int /*argc*/, char** /*argv*/)
{
//Load library
vk::Lib lib;
if(VK_SUCCESS != lib.initialize()){
fprintf(stderr, "Fail to initialize lib\n");
return 0;
}
vk::Instance instance;
vk::Device device;
{
// Create instance
//---------------------------------------------------------------------
VkApplicationInfo applicationInfo = {
VK_STRUCTURE_TYPE_APPLICATION_INFO, //structure type
VLK_NULL,
"Tutorial Vulkan", //application name
VK_MAKE_VERSION(1,0,0), //application version
"Tutorial Engine", //engine name
VK_MAKE_VERSION(1,0,0), //engine version
VK_API_VERSION_1_0, //api version
};
VkInstanceCreateInfo instanceCreateInfo = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, //structure type
VLK_NULL,
0, //instance creation flags
&applicationInfo, //application info
0, //enabled layer count
VLK_NULL, //enabled layer names
0, //enabled extension count
VLK_NULL, //enabled extension names
};
if(VK_SUCCESS != lib.createInstance(instance, &instanceCreateInfo, VLK_NULL)){
fprintf(stderr, "Fail to create instance\n");
return 0;
}
// Create Device
//---------------------------------------------------------------------
vk::PhysicalDevice physicalDevice;
vk::u32 queueFamily;
if(!selectQueueFamily(physicalDevice, queueFamily, instance)){
fprintf(stderr, "Fail to find queue family\n");
return 0;
}
const float queuePriorities[] = {0.0f};
VkDeviceQueueCreateInfo queueCreateInfo = {
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //structure type
VLK_NULL,
0, //device queue creation flags
queueFamily, //selected queue family's index
1, //queue count
queuePriorities, //queue priorities
};
VkDeviceCreateInfo deviceCreateInfo = {
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, //structure type
VLK_NULL,
0, //device creation flags
1, //queue create info count,
&queueCreateInfo, //queue create info
0, //enabled layer count
VLK_NULL, //enabled layer names
0, //enabled extension count
VLK_NULL, //enabled extension names
VLK_NULL, //enabled physical device features
};
if(VK_SUCCESS != physicalDevice.createDevice(device, &deviceCreateInfo, VLK_NULL)){
fprintf(stderr, "Fail to create device\n");
return 0;
}
}
device.destroy();
instance.destroy();
lib.terminate();
return 0;
}
bool selectQueueFamily(vk::PhysicalDevice& selectedDevice, vk::u32& selectedQueueFamilty, vk::Instance& instance)
{
static const vk::u32 MaxProperties = 128;
VkQueueFamilyProperties properties[MaxProperties];
vk::PhysicalDevices physicalDevices = instance.enumeratePhysicalDevices();
for(vk::u32 i=0; i<physicalDevices.getNumDevices(); ++i){
vk::PhysicalDevice& physicalDevice = physicalDevices.getDevice(i);
vk::u32 countProperties = 0;
physicalDevice.getPhysicalDeviceQueueFamilyProperties(&countProperties, VLK_NULL);
countProperties = vk::minimum(countProperties, MaxProperties);
physicalDevice.getPhysicalDeviceQueueFamilyProperties(&countProperties, properties);
for(vk::u32 j=0; j<countProperties; ++j){
if(0<properties[j].queueCount
&& (properties[j].queueFlags & VK_QUEUE_GRAPHICS_BIT))
{
selectedDevice = physicalDevice;
selectedQueueFamilty = j;
return true;
}
}
}
return false;
}
まとめ
vk.xmlから関数名を抽出し, デバイスの生成まで行いました.APIの基本設計はOpenGLと変わらないように思います.
XMLで関数仕様が記述されているため, ヘッダから抽出するより楽になったと思います.
0 件のコメント:
コメントを投稿