April 4-7, 2016 | Silicon Valley VKCPP Markus Tavenrath Senior Developer Technology Engineer mtavenrath@nvidia.com 4/4/2016
INTRODUCTION Who am I? Senior Dev Tech Software Engineer - Professional Visualization Joined NVIDIA 8 years ago to work on middleware Goal: Make GPU programming easy and efficient Working with CAD ISVs to optimizing their graphics pipelines Working with driver team on Vulkan and OpenGL performance 2
INTRODUCTION What is Vulkan? Clean, modern and consistent C-API without cruft Low CPU overhead Scales well with multiple threads Provides ‚ low level ‘ control over GPU Moves a lot of responsibility from driver to developer Developer essentialy writes part of the driver 4/4/2016 3
HELLO VULKAN Simple HelloVulkan is ~750 lines of code So much to do, hard to start Easy to make errors Even harder to find those errors Is there a way to simplify Vulkan usage? Two projects started VKCPP (low level C++ API) NVK* (high level C++ API) 4/4/2016 4
VKCPP http://github.com/nvpro-pipeline/vkcpp VKCPP is a port of the Vulkan API for C++11 Simplifies logic where possible without changing concepts/behaviour Generated from the official Vulkan spec file, vk.xml Header only with inline functions to minimize additional cost Open source project contains generated header and generator 4/4/2016 5
VKCPP GOALS Reduce code size & risk of errors Initialization ‚ vertical ‘ VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pNext = NULL ; appInfo.pApplicationName = appName; -> reduced clearness due to #lines on screen appInfo.applicationVersion = 1; appInfo.pEngineName = engineName; appInfo.engineVersion = 1; appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 5); VkInstanceCreateInfo instInfo = {}; Potential issues instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instInfo.pNext = NULL ; instInfo.flags = 0; Struct/sType enums mismatch instInfo.pApplicationInfo = &appInfo; instInfo.enabledLayerCount = layerNames. size (); instInfo.ppEnabledLayerNames = layerNames. data () No type safety for enums and flags instInfo.enabledExtensionCount = extensionNames. size (); instInfo.ppEnabledExtensionNames = extensionNames. data (); VkResult res = vkCreateInstance(&instInfo, NULL , &info.inst); Risk of uninitialized fields assert (res == VK_SUCCESS); 6
VULKAN NAMESPACE // Strip Vk prefix of all functions and structs VkResult vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); avoid symbol collisions // Introduce new vk namespace for all vkcpp symbols namespace vk { Result createInstance(const InstanceCreateInfo* pCreateInfo, const AllocationCallbacks* pAllocator, Instance* pInstance); }; 7
TYPE SAFETY Enums namespace vk { // Use scoped enums for type safety enum class ImageType { Strip VK_ prefix + enum name e1D = VK_IMAGE_TYPE_1D, e2D = VK_IMAGE_TYPE_2D, Use upper camel case of enum type as name e3D = VK_IMAGE_TYPE_3D }; } ‘e‘ + name prefix is required only for numbers, used everywhere for consistency reasons 8
TYPE SAFETY Flags // Introduce class for typesafe flags template <typename BitType, typename MaskType = VkFlags> class Flags { ... }; // BitType is scoped enum enum class QueueFlagBits { eGraphics = VK_QUEUE_GRAPHICS_BIT, eCompute = VK_QUEUE_COMPUTE_BIT, eTransfer = VK_QUEUE_TRANSFER_BIT, eSparseBinding = VK_QUEUE_SPARSE_BINDING_BIT }; // Define typesafe flags typedef Flags<QueueFlagBits, VkQueueFlags> QueueFlags; 4/4/2016 9
TYPE SAFETY Flags template <typename BitType, typename MaskType = VkFlags> class Flags { public: Flags(); // No flags set. Use QueueFlags() or {} as value Flags(BitType bit); // QueueFlags qf(QueueFlagBits::eGraphics) Flags<BitType> & operator|=(Flags<BitType> const& rhs); // qf |= QueueFlagBits::eCompute; Flags<BitType> & operator&=(Flags<BitType> const& rhs); // qf &= QueueFlagBits::eGraphics; Flags<BitType> & operator^=(Flags<BitType> const& rhs); // qf ^= QueueFlagBits::eGraphics; Flags<BitType> operator|(Flags<BitType> const& rhs) const; // qf = QueueFlagBits::eCompute | QueueFlagBits::eGraphics Flags<BitType> operator&(Flags<BitType> const& rhs) const; // qf = qf & QueueFlagBits::eGraphics Flags<BitType> operator^(Flags<BitType> const& rhs) const; // qf = qf ^ QueueFlagBits::eGraphics explicit operator bool() const; // if (qf) Is any bit set? bool operator!() const; // if (!qf) Is no bit set? bool operator==(Flags<BitType> const& rhs) const; // if (qt == QueueFlagBits::eCompute) bool operator!=(Flags<BitType> const& rhs) const; // if (qt != QueueFlagBits::eCompute) explicit operator MaskType() const; // VkFlags flags = static_cast<VkFlags>(qf); }; 10
INITIALIZATION CreateInfos and Structs class EventCreateInfo { public: // All constructors initialize sType/pNext EventCreateInfo(); // Initialize all fields with default values (0 currently) EventCreateInfo(EventCreateFlags flags); // Create with all parameters specified EventCreateInfo(VkEventCreateInfo const & rhs); // Construct from native Vulkan type // get parameter object const EventCreateFlags& flags() const; EventCreateFlags& flags(); // get parameter from non-cost object // set parameter EventCreateInfo& flags(EventCreateFlags flags); ... operator const VkEventCreateInfo&() const; // cast operator to native vulkan type 4/4/2016 11 };
RESULTS CODE SIZE & TYPE SAFETY VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pNext = NULL ; appInfo.pApplicationName = appName; appInfo.applicationVersion = 1; appInfo.pEngineName = engineName; appInfo.engineVersion = 1; vk::ApplicationInfo appInfo(appName, 1, appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 5); engineName, 1, VK_MAKE_VERSION(1,0,5)); VkInstanceCreateInfo i = {}; vk::InstanceCreateInfo i({}, &appInfo, i.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; layerNames. size (), layerNames. data (), i.pNext = NULL ; extNames. size (), extNames. data ()); i.flags = 0; vk::Instance instance; i.pApplicationInfo = &appInfo; vk::Result res = vk::createInstance(&i, nullptr, &instance); i.enabledLayerCount = layerNames. size (); assert (res == vk::Result::eSuccess); i.ppEnabledLayerNames = layerNames. data () i.enabledExtensionCount = extNames. size (); i.ppEnabledExtensionNames = extNames. data (); VkInstance instance; VkResult res = vkCreateInstance(&i, NULL , &instance); 12 assert (res == VK_SUCCESS);
RESULTS CODE SIZE & TYPE SAFETY std::vector<std::string>? Bad idea vk::ApplicationInfo appInfo(appName, 1, vk::InstanceCreateInfo i({}, &appInfo, engineName, 1, {“ layer_xyz ”} , VK_MAKE_VERSION(1,0,5)); extNames. size (), extNames. data ()); vk::InstanceCreateInfo i({}, &appInfo, Lifetime of struct != Lifetime of temporary layerNames. size (), layerNames. data (), extNames. size (), extNames. data ()); Temporary array will be destroyed after vk::Instance instance; constructor vk::Result res = vk::createInstance(&i, nullptr, &instance); assert (res == vk::Result::eSuccess); CreateInfos would have to copy data 4/4/2016 13
DESIGNATED INITIALIZER LIST VkApplicationInfo appInfo = vk::ApplicationInfo appInfo = vk::ApplicationInfo() { .pApplicationName(appName) .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .applicationVersion(1) .pNext = NULL , .pEngineName(engineName) .pApplicationName = appName, .engineVersion(1) .applicationVersion = 1, .apiVersion(VK_MAKE_VERSION(1,0,5)); .pEngineName = engineName, .engineVersion = 1, .apiVersion = VK_MAKE_VERSION(1,0,5) Names explicit, but no guarantee to set all fields }; Designated initializer list not part of C++11 4/4/2016 14
C++ CODING STYLE HANDLES class CommandBuffer VkCommandBuffer cmd = ... { VkRect2D scissor; // conversion from/to native handle scissor.offset.x = 0; CommandBuffer(VkCommandBuffer commandBuffer); scissor.offset.y = 0; CommandBuffer& operator=(VkCommandBuffer scissor.extent.width = width; commandBuffer); scissor.extent.height = height; operator VkCommandBuffer() const; vkCmdSetScissor(cmd, 0, 1, &scissor); // boolean tests if handle is valid explicit operator bool() const; Convert C-Style OO bool operator!() const; to C++ Style OO // functions void setScissor(uint32_t firstScissor, uint32_t scissorCount, const Rect2D* pScissors) const; }; vk::CommandBuffer cmd; cmd .setScissor(0, 1, &scissor); 4/4/2016 15
C++ CODING STYLE TEMPORARY STRUCTS AND EXCEPTIONS class Device { Result createFence(const FenceCreateInfo * createInfo, AllocationCallbacks const * allocator, Fence * fence) const; }; Change from pointer to reference allows passing temporaries class Device { Fence createFence(const FenceCreateInfo & createInfo, Optional<AllocationCallbacks const> const & allocator) const; }; 4/4/2016 16
Recommend
More recommend