// Vulkan C++ API:
vk::BufferCopy region_cpp;
command_buffer.copyBuffer(staging_buffer, vk_data->buffer, region_cpp);
// Vulkan C API:
VkBufferCopy region = {0, 0, size};
VKF.vkCmdCopyBuffer(vk::CommandBuffer(command_buffer), staging_buffer, vk_data->buffer, 1, ®ion);
Note that VkBufferCopy
is a struct, so it is easy to make a mistake and initialize it partially (leaving some fields with uninitialized trash):
In such case we initialized size
field, but all other fields (offsets) were left uninitialized:
VkBufferCopy region;
region.size = size;
Improvement 1: This is not the case with C++ API, because this code vk::BufferCopy region_cpp;
will call default constructor initializing all fields with zeros.
Note that in case of C API it is enough to write VkBufferCopy region = {};
(but it is still more error-prone).
// Vulkan C++ API:
vk::raii::Instance temporary_instance = vk::raii::Instance(context, instance_create_info);
std::vector<vk::raii::PhysicalDevice> devices = temporary_instance.enumeratePhysicalDevices();
// Vulkan C API:
VkInstance temporary_instance;
assert(VKF.vkCreateInstance(&instance_create_info, nullptr, &temporary_instance) == VK_SUCCESS);
unsigned int ndevices = 0;
assert(VKF.vkEnumeratePhysicalDevices(vk::Instance(temporary_instance), &ndevices, nullptr) == VK_SUCCESS);
std::vector<VkPhysicalDevice> devices(ndevices);
assert(VKF.vkEnumeratePhysicalDevices(vk::Instance(temporary_instance), &ndevices, devices.data()) == VK_SUCCESS);
VKF.vkDestroyInstance(temporary_instance);
Improvement 2: In case of C++ RAII API we don't need to call vkDestroyInstance(...)
manually. Also it supports std containers like std::vector
- see devices result from vkEnumeratePhysicalDevices
.
These three lines are equal thanks to syntax sugar inside Vulkan C++ API:
vk::raii::DescriptorSet descriptor_sets;
...
descriptor_writes[i] = vk::WriteDescriptorSet(descriptor_sets, binding, 0, 1, vk::DescriptorType::eStorageBuffer);
descriptor_writes[i] = vk::WriteDescriptorSet(descriptor_sets.operator vk::DescriptorSet(), binding, 0, 1, vk::DescriptorType::eStorageBuffer);
descriptor_writes[i] = vk::WriteDescriptorSet(vk::DescriptorSet(descriptor_sets), binding, 0, 1, vk::DescriptorType::eStorageBuffer);
I.e. vk::raii::DescriptorSet
will be implicitly converted into vk::DescriptorSet
. So RAII boxes are-just-works! (without need of manual conversions)
Improvement 3: More pretty-looking and expressive enums: VkDescriptorType::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
-> vk::DescriptorType::eStorageBuffer
.
But there are also a case when some parameter is a pointer to multiple values, f.e.:
// C API function:
void vkCmdBindDescriptorSets(
VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipelineLayout layout,
uint32_t firstSet,
uint32_t descriptorSetCount,
const VkDescriptorSet* pDescriptorSets, // <- this argument is a pointer to multiple VkDescriptorSet values
uint32_t dynamicOffsetCount,
const uint32_t* pDynamicOffsets);
Improvement 4: In such cases you can even pass std::vector<vk::DescriptorSet>
as an argument:
vk::raii::DescriptorSet descriptor_sets_a = ...;
vk::raii::DescriptorSet descriptor_sets_b = ...;
std::vector<vk::DescriptorSet> descriptors;
descriptors.push_back(descriptor_sets_a);
descriptors.push_back(descriptor_sets_b);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeline_layout, 0, descriptors, nullptr);
And you can pass even a single (non-RAII) descriptor (it will be automatically boxed into vk::ArrayProxy
):
vk::raii::DescriptorSet descriptor_sets;
vk::DescriptorSet descriptor_sets_non_raii = descriptor_sets;
// These four lines are equal, but the 4-th one DOESN'T COMPILE
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, vk::PipelineLayout(kernel->pipelineLayout()), 0, descriptor_sets_non_raii, nullptr);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, vk::PipelineLayout(kernel->pipelineLayout()), 0, vk::ArrayProxy<vk::DescriptorSet>(descriptor_sets_non_raii), nullptr);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, vk::PipelineLayout(kernel->pipelineLayout()), 0, vk::ArrayProxy<vk::DescriptorSet>(descriptor_sets), nullptr);
command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, vk::PipelineLayout(kernel->pipelineLayout()), 0, descriptor_sets, nullptr); // this line does not compile!
The last one line doesn't compile, because it requires two implicit conversions: vk::raii::DescriptorSet
-> vk::DescriptorSet
-> vk::ArrayProxy
.