Thursday, 31 March 2016

Vulkan, first steps 03: devices

This post is the third in series of presenting API samples from Lunar's Vulkan SDK recoded to use nVidia's Open-Source Vulkan C++ API. In the first post Vulkan Instance was created. The second post presented Visual Studio project properties, and had little in common with Vulkan but helped organize samples solution. Now it is time to get back to Vulkan!

List of all Vulkan tutorial posts is here.

Second thing to do, in order to use Vulkan, is to create device. Before that, one can enumerate all Vulkan devices, which are present in system running application. The code is shown below.
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>

#include <madvk/instance.h>


using namespace std;
using namespace mad::vk;

int main(int argc, char* argv[]) {
    try {
        auto instance = Instance("vulkan playground 01");
        cout << "Vulkan instance created" << endl;

        cout << "Vulkan devices:" << endl;
        std::vector<::vk::PhysicalDevice> devices;
        instance.enumeratePhysicalDevices(devices);
        for(const auto& dev : devices) {
            const auto& props = dev.getProperties();
            cout
                << std::showbase << std::internal << std::setfill('0')
                << std::hex
                << "\tID: " << props.deviceID()
                << std::noshowbase << std::right << std::setfill(' ')
                << std::dec
                << "\tname: " << props.deviceName()
                << "\ttype: " << to_string(props.deviceType())
                << "\tdriver version: " << props.driverVersion()
                << "\tapi version: "
                << ((props.apiVersion() >> 22) & 0xfff) << '.' // Major.
                << ((props.apiVersion() >> 12) & 0x3ff) << '.' // Minor.
                << (props.apiVersion() & 0xfff)                // Patch.
                << endl;
        }
    }
    catch(const std::system_error& err) {
        cerr << "[ERROR] " << err.what() << endl;
        return 1;
    }
}

There is difference in creating Vulkan instance between this post and the first one. From this post onward, I will use my RAII wrapper around vk::Instance object and not the bare object itself, to make sure that memory will be freed correctly.

Creating display device object requires two steps:
  1. Enumerating all devices and finding one, which is capable of graphical output.
  2. Creating device object with proper parameters and proper queues.
The first step is almost done in the example above and only minor corrections have to be made. The second one is similar to creating Vulkan instance, but requires vk::PhysicalDevice object from step one. Whole code is presented below. In later examples, vk::Device will be hidden in RAII wrapper.
#include <iostream>
#include <vector>
#include <algorithm>

#include <madvk/instance.h>


using namespace std;
using namespace mad::vk;

int main(int argc, char* argv[]) {
    try {
        auto instance = Instance("vulkan playground 01");
        cout << "Vulkan instance created" << endl;

        // Get all Vulkan devices
        std::vector<::vk::PhysicalDevice> devices;
        instance.enumeratePhysicalDevices(devices);

        // Find Vulkan GPU
        const ::vk::PhysicalDevice* gpu = nullptr;
        for(const auto& dev : devices) {
            const auto& qprops = dev.getQueueFamilyProperties();
            const auto it = find_if(
                qprops.cbegin(), qprops.cend(),
                [](auto& qpr) { return qpr.queueFlags() & ::vk::QueueFlagBits::eGraphics; });
            if(it != qprops.cend()) {
                gpu = &dev;
                break;
            }
        }
        if(!gpu)
            throw runtime_error("Unable to find GPU");

        // Create device object
        float priorities[] = {1.0f};
        auto queue_info = ::vk::DeviceQueueCreateInfo().queueCount(1).pQueuePriorities(priorities);
        auto device_info = ::vk::DeviceCreateInfo().queueCreateInfoCount(1).pQueueCreateInfos(&queue_info);
        auto device = gpu->createDevice(device_info, nullptr);
        cout << "Vulkan device created" << endl;

        device.destroy(nullptr);
        cout << "Vulkan device destroyed" << endl;
    }
    catch(const exception& err) {
        cerr << "[ERROR] " << err.what() << endl;
        return 1;
    }
}

If one would use Vulkan device for doing GPU calculations, then she or he should use vk::QueueFlagBits::eCompute flag in searching for proper device.

Vulkan, first steps 02: Visual Studio Project Properties

I have decided to create a Visual Studio solution with API samples from Lunar's Vulkan SDK recoded to use nVidia's Open-Source Vulkan C++ API. But for each project in the solution, I have to add: paths for Vulkan SDK, tools and libraries from it. Doing it by hand would be tedious. It is a place, where project properties come in handy! The solution presented below is inspired by "Sharing project properties in Visual C++" post.

List of all Vulkan tutorial posts is here.

In solution's directory, I have created vulkan.props file intended for storing all changes in project properties corresponding to using Vulkan SDK. Setting include directory and the additional link-time library was easy because these are shared among all configurations. But libraries directory is different for Win32 and x64 builds. There I had to use a choose construct with proper condition. The whole file is presented below.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ImportGroup Label="PropertySheets" />
  
  <Choose>
    <When Condition="'$(Platform)'!='x64'">
      <PropertyGroup Label="UserMacros">
        <VULKAN_BIN_DIR>$(VULKAN_SDK)\Bin32</VULKAN_BIN_DIR>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup Label="UserMacros">
        <VULKAN_BIN_DIR>$(VULKAN_SDK)\Bin</VULKAN_BIN_DIR>
      </PropertyGroup>
    </Otherwise>
  </Choose>

  <PropertyGroup>
    <IncludePath>$(VULKAN_SDK)\Include;$(IncludePath)</IncludePath>
    <LibraryPath>$(VULKAN_BIN_DIR);$(LibraryPath)</LibraryPath>
  </PropertyGroup>
  <ItemDefinitionGroup>
    <Link>
      <AdditionalDependencies>vulkan-1.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <BuildMacro Include="VULKAN_BIN_DIR">
      <Value>$(VULKAN_BIN_DIR)</Value>
      <EnvironmentVariable>true</EnvironmentVariable>
    </BuildMacro>
  </ItemGroup>
</Project>


The $(VULKAN_SDK) is a system variable created by Vulkan SDK installer. Now, to reference Vulkan SDK I have only to add vulkan.props property file to each project once. Thanks to the choose construct my property file is working in all project configurations.

Wednesday, 30 March 2016

Vulkan, first steps 01

When I was at university, I was interested in games and algorithms of computer graphics. I have taken lectures on it and OpenGL. My work does not require graphics programming, and skills gained during the lectures are used mainly in photography. When The Khronos Group Inc. released Vulkan, I have decided to try it and refresh my graphics skills. Because I am c++ programmer, I have decided to use nVidia's Open-Source Vulkan C++ API instead of original c API. This post is an introduction to Open-Source Vulkan C++ API. List of all Vulkan tutorial posts is here.


The first thing, which is necessary to use Vulkan, is to create its instance. The instance is a representation of Vulkan itself. It is the main object, which allows us to interact with Vulkan and do something useful. The minimal example of creating an instance is shown below. There is only one custom param: app title Vulkan APP in line 13th. Most of the time, the default parameters from Open-Source Vulkan C++ API are used. There is one exception, which is the API version (set in line 14th). The default value is 0, which on my machine resulted in an error of missing compatible device. After setting the API version to VK_MAKE_VERSION(1, 0, 0), Vulkan instance is successfully created and destroyed.
#include <iostream>
#include <string>

// Use more c++ friendly version of Vulkan cpp
#define VKCPP_ENHANCED_MODE
#include <vulkan/vk_cpp.h>

using namespace std;

int main(int argc, char* argv[]) {
 try {
  auto app_info = vk::ApplicationInfo()
   .pApplicationName("Vulkan APP")
   .apiVersion(VK_MAKE_VERSION(1, 0, 0));
  auto instance_info = vk::InstanceCreateInfo().pApplicationInfo(&app_info);

  auto instance = vk::createInstance(instance_info, nullptr);
  cout << "Vulkan instance created" << endl;

  instance.destroy(nullptr);
  cout << "Vulkan instance destroyed" << endl;
 }
 catch(const std::system_error& err) {
  cerr << "[ERROR] " << err.what() << endl;
  return 1;
 }
}

Unfortunately, vk::Instance is not an RAII wrapper, and the instance has to be freed manually. After deciding on memory allocation policy (nullptr parameters for vk::createInstance and vk::Instance::destroy), one should create RAII wrapper around vk::Instance.

Friday, 1 January 2016

What annoys c++ programmer in Windows

If You want Your app to be a nice citizen of user's operating system, You typically use system settings. For instance, You could use localization. For instance, if You want to show 106, then You could write a simple program:
#include <iostream>
#include <locale>

using namespace std;

int main()
{
    const int mega = 1'000'000;

    locale systemLocale("");
    cout.imbue(systemLocale);
    cout << "System's locale: " << mega << endl;

}

It is in plain c++11! If You would remove apostrophes from the definition of mega constant, it would be in c++98.

One side note about creating std::locale object. It can be done in many ways, but it is worth to know about three basic ones:
  • with the default constructor, "C" locale is created
  • with single string parameter (const char* or std::string), locale corresponding to given name is created
  • with a single, empty string parameter, system's default locale object is created.
Back, to the program. When run, it should write to the console "1000000" with thousand separators in it, if they are set in the system. On my Ubuntu box, there are no thousand separators, so program's output is
System's locale: 1000000
Why there are no separators, in Polish there should be spaces, I do not know. But the program's output is at least consistent with system settings. On the other hand, in my Windows box, there are a thousand separators set. And so, the program's output should be "1 000 000". But it is not. It is:
System's locale: 1á000á000
It is almost garbage! It is like Windows is saying to You: "Be a naughty programmer: use only C locale and not the user settings.". ;) But, if users use a more recent version of Windows and run the program in PowerShell, there is a solution. He or she should set console output encoding to the system's default:
[System.Console]::OutputEncoding = [System.Text.Encoding]::Default
After doing that, the program's output is alright and consistent with system settings. On the source of the solution, $OutputEncoding to the rescue, authors said (in year 2006!) that most commands do not process UNICODE correctly and that is the reason for doing fallback to ASCII. For me, it is a poor excuse for not forcing programmers to write good software!

Sunday, 31 May 2015

One small and great feature of Visual Studio 2015 RC

I have started playing with an upcoming version of Visual Studio 2015. After creating a new project, I have started coding. And, when I was creating the first c++ class, I have noticed a great feature of Visual Studio 2015 RC! After declaring method for a class in a header file, a programmer may press ctrl + . to invoke quick action. After doing so, she or he can choose to create a method definition in a source file.
If creating definition was the programmer's choice, Visual Studio will show inline editor of the source file for the method.
After providing the method's body, pressing Esc will close the inline editor. Now, we do not have to constantly switch between header and source files! :)

Tuesday, 5 August 2014

Python's in in c++ - Part 3: Testing and finding in one pass

In two parts: 1 and 2 I have shown simple implementation of Python's in keyword in c++. I have mentioned, that there are two issues with the presented solution:
  1. After testing if some object is in a collection, You have to make a second pass over the collection to find searched object's position.
  2. If collection is random access and it is sorted, searching can be done more efficient by std::binary_search algorithm.
The second problem requires compile-time check if the collection is sorted or prior knowledge. Both solutions are trade-offs, so I will not tackle this problem.

To deal with the first inefficiency I had merged finder and in_impl template classes into one template class with two specializations and common base class. These changes lead to the removal of find_impl function, which was no longer needed. To obtain iterator type for any collection I have created iterator_type metafunction.

The iterator_type metafunction is simple metafunction, which for arrays return pointer type and for other types, it uses decltype keyword to calculate the type of iterator for given collection type. Users can specialize iterator_type metafunction for their own classes. Source code of that utility metafunction is presented below.
template<typename Collection>
struct iterator_type {
 typedef decltype(std::end(Collection())) type;
};

template<typename T, size_t S>
struct iterator_type<T[S]> {
 typedef T* type;
};


Common base class for specializations of the new in_impl template class is called in_impl_base, and its purpose is to gather common code for both specializations. It is: storing iterators to a position of searched element and end of the collection, calculating whether the element is in the collection and returning position of the element. The source code presented below.
template<typename Collection>
class in_impl_base {
public:
 typedef typename iterator_type<Collection>::type iterator;

 in_impl_base(const iterator& position, const iterator& end)
  : position_(position), end_(end) {}

 explicit operator bool() const {
  return end_ != position_;
 }

 iterator position() const {
  return position_;
 }
private:
 iterator position_;
 iterator end_;
};


Specializations of the in_impl template class differ only by the value of HasMemberFind template parameter and means of obtaining a position of the searched element. If HasMemberFind equals to true, then Collection::find member function is used. In the other case, the std::find algorithm is applied. To simplify type name used in in_left and in_right classes, there is also template alias in_implementation defined. It is used by both above-mentioned classes instead of old in_impl class. Described code is presented below.
template<typename Element, typename Collection, bool HasMemberFind = false>
class in_impl : public in_impl_base<Collection> {
public:
 in_impl(const Element& e, const Collection& c)
  : in_impl_base<Collection>(std::find(std::begin(c), std::end(c), e), std::end(c)) {}
};

template<typename Element, typename Collection>
class in_impl<Element, Collection, true> : public in_impl_base<Collection> {
public:
 in_impl(const Element& e, const Collection& c)
  : in_impl_base<Collection>(c.find(e), std::end(c)) {}
};

template<typename Element, typename Collection> using in_implementation = in_impl<Element, Collection, has_member_find<Collection>::value>;

To test new features, I have changed the check function. By calling print it uses proper specialization of printer template class and prints value stored in the collection under obtained position. The body of the main function remained unchanged. Testing utilities are presented below.
template<typename ValueType>
struct printer {
 printer(const ValueType& val) {
  std::cout << "\t\tvalue = " << val << std::endl;
 }
};

template<typename Key, typename Value>
struct printer<std::pair<Key, Value>> {
 printer(const std::pair<Key, Value>& val) {
  std::cout << "\t\tvalue = (" << val.first << ", " << val.second << ")" << std::endl;
 }
};

template<typename ValueType> void print(const ValueType& val) {
 printer<ValueType> p = val;
}

template<typename C>
void check(const int e, const C& c) {
 if(auto pos = e in c) {
  std::cout << "\t" << e << " is in given collection: " << typeid(c).name() << std::endl;
  print(*pos.position());
 }
 else {
  std::cout << "\t" << e << " is not in given collection: " << typeid(c).name() << std::endl;
 }
}


These are all changes made to make a port of Python's in keyword into c++. It turned to be even slightly more powerful than its Python's predecessor as it can test element existence and obtain its position in one pass over the collection. Modified source code can be found on GitHub in 1.5.0 tag.

Saturday, 2 August 2014

Python's in in c++ - Part 2: Template Specializations

In part 1 I have presented expression templates which allowed creation of Python's in construct in c++. In this post I will present some template machinery, which made my in implementation quite efficient.

In part 1, it was shown that finding element in collection is done by find_impl function. Its code is presented below:
template<typename Element, typename Collection>
bool find_impl(const Element& e, const Collection& c) {
 return finder<Element, Collection, has_member_find<Collection>::value>()(e, c);
}
It only creates instance of finder template class and calls its function call operator. The finder class is parametrized by:
  • type of object searched in collection
  • type of collection
  • boolean flag, whether collection has efficient member function find
If given collection supports efficient lock-up, then its member function find is used by proper specialization of finder template. In other case, std::find algorithm is used. Code for both specializations is presented below:
template<typename Element, typename Collection, bool HasMemberFind = false>
class finder {
public:
 bool operator()(const Element& e, const Collection& c) const {
  using namespace std;
  return end(c) != find(begin(c), end(c), e);
 }
};

template<typename Element, typename Collection>
class finder {
public:
 bool operator()(const Element& e, const Collection& c) const {
  using namespace std;
  return end(c) != c.find(e);
 }
};

The tricky part was to create has_member_find template, because it has to deal with concrete types and templates - for instance standard collections. For concrete types has_member_find is a metafunction always returning false. User can define specialization for its own concrete types with efficient find member function. For templates has_member_find inherits is value from template_has_member_find template, which handles templates and for most of them is also a metafunction returning false. The template_has_member_find template is specialized for all standard associative containers to be a metafunction returning true. Code for both templates is presented below:
// Has a template for collection member function find?
template<template<typename...> class TCollection>
struct template_has_member_find : std::false_type {};

template<>
struct template_has_member_find<std::set> : std::true_type {};

template<>
struct template_has_member_find<std::multiset> : std::true_type {};

template<>
struct template_has_member_find<std::map> : std::true_type {};

template<>
struct template_has_member_find<std::multimap> : std::true_type {};

template<>
struct template_has_member_find<std::unordered_set> : std::true_type {};

template<>
struct template_has_member_find<std::unordered_multiset> : std::true_type {};

template<>
struct template_has_member_find<std::unordered_map> : std::true_type {};

template<>
struct template_has_member_find<std::unordered_multimap> : std::true_type {};


// Has a concrete type member function find?
template<typename Collection, typename... Args>
struct has_member_find : public std::false_type {};

template<template<typename...> class Collection, typename... Args>
struct has_member_find<Collection<Args...>> : template_has_member_find<Collection> {};



These are all building blocks, which were used to port Python's in keyword into c++. There are two main problems with presented solution:
  1. After testing if some object is in collection, You have to make second pass over collection to find searched object's position.
  2. If collection is a random access and it is sorted, searching can be done more efficient by std::binary_search algorithm.
The first issue will be addressed in the third part of this miniseries.

Presented code can be found on GitHub in 1.0.0 tag.