realitix / vulkan

The ultimate Python binding for Vulkan API
Apache License 2.0
494 stars 44 forks source link

Cython/C bindings #32

Closed Berserker66 closed 5 years ago

Berserker66 commented 6 years ago

In a nutshell, something similar to the numpy <-> cython bridge, which allows you to "cimport" numpy, giving you typed and direct access to numpy.

In this module, this would mean there is a cython api, through which you work directly with the C structures and types (therefore no conversion between C and python - which is, and probably will always be, the slowest part of this module).

I don't know enough about cython to even know how feasible it is to achieve this, or how much work it is.

I can just say, that in my project I am using cython (gil-less) threads to use multiple CPU cores to then feed data into vulkan, the bottleneck in my case is the conversion from cython/c to python back to c structures.

I know that it is possible to bind to vulkan directly from cython, but then you don't get this modules error checking.

realitix commented 6 years ago

Hello @Berserker66, I didn't think about Cython and I don't know much about it. In my opinion (but I may be wrong), you can directly use the Vulkan header in Cython (like you say) and by passing the VkInstance and VkDevice, it should work. What do you mean by modules error checking ? I don't do error checking in the module, that's the purpose of Vulkan layers.

Can you help me to better understand the issue ?

Thanks

Berserker66 commented 6 years ago

If you use cython directly, you lose this section of features: https://i.imgur.com/MpHTagU.png and have to wrap it yourself.

Just - considering this wrapper is already automatically generated, I was kind of hoping it might not be too much work to add a cython output.

realitix commented 6 years ago

Indeed @Berserker66, Since the model is generated, it's only a matter of jinja2 template to write a new wrapper. So in theory, it can be done easily, but it must be done by someone who know Cython pretty well. Do you know someone who could help ?

Berserker66 commented 6 years ago

I don't really know anyone - I use cython a lot though, - but never really messed with C libraries directly. If I find a spare week somewhere I might be able to do it.

realitix commented 6 years ago

If you need information to use the generator. You can talk to me by email (in git commit list) or on IRC. I'm connected to the #vulkan channel and I am realitix

realitix commented 6 years ago

I updated the SDK to 1.0.61

Berserker66 commented 6 years ago

I tried for a bit, didn't get very far.

My first shot is to try and just compile the existing template with cython and see if that already gives a benefit or not - sometimes it does.

Well, first snack the cpp command does not exist on windows and I've looked around a short bit for an alternative and it looks like there are, but then I don't know how to translate some of the cmd line arguments and .. ugh anyway, I just grabbed the already compiled parts to skip over that for now.

I got cython to compile "something". Can't be loaded and it clearly fails in the link stage:

C:\Program Files\Python36\lib\distutils\dist.py:261: UserWarning: Unknown distribution option: 'include_package_data'
  warnings.warn(msg)
C:\Program Files\Python36\lib\distutils\dist.py:261: UserWarning: Unknown distribution option: 'install_requires'
  warnings.warn(msg)
C:\Program Files\Python36\lib\distutils\dist.py:261: UserWarning: Unknown distribution option: 'setup_requires'
  warnings.warn(msg)
running build_ext
building 'vulkan.__init__' extension
E:\Visual Studio 2017 Community\VC\Tools\MSVC\14.11.25503\bin\HostX64\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD "-IE:\Visual Studio 2017 Community\VC\Redist\MSVC\14.10.25017\onecore\x64\Microsoft.VC150.CRT" "-IC:\Program Files\Python36\include" "-IC:\Program Files\Python36\include" "-IC:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\ucrt" "-IC:\Program Files (x86)\Visual C++ BuildTools\VC\Tools\MSVC\14.11.25503\include" "-IC:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared" /Tcvulkan\__init__.c /Fobuild\temp.win-amd64-3.6\Release\vulkan\__init__.obj
__init__.c
E:\Visual Studio 2017 Community\VC\Tools\MSVC\14.11.25503\bin\HostX64\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO "/LIBPATH:C:\Program Files\Python36\libs" "/LIBPATH:C:\Program Files\Python36\PCbuild\amd64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\ucrt\x64" "/LIBPATH:E:\Visual Studio 2017 Community\VC\Tools\MSVC\14.11.25503\lib\x64" "/LIBPATH:C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x64" /EXPORT:PyInit___init__ build\temp.win-amd64-3.6\Release\vulkan\__init__.obj /OUT:E:\vulkan\vulkan\__init__.cp36-win_amd64.pyd /IMPLIB:build\temp.win-amd64-3.6\Release\vulkan\__init__.cp36-win_amd64.lib
LINK : error LNK2001: unresolved external symbol PyInit___init__
build\temp.win-amd64-3.6\Release\vulkan\__init__.cp36-win_amd64.lib : fatal error LNK1120: 1 unresolved externals
error: command 'E:\\Visual Studio 2017 Community\\VC\\Tools\\MSVC\\14.11.25503\\bin\\HostX64\\x64\\link.exe' failed with exit status 1120

Now, googling for this particular error seems to indicate that cython really does not like /.init files and you should really put your stuff elsewhere. Which might be a good idea for the base vulkan implementation too, dropping it all in init is somewhat odd. We can still get the same behavior by importing into the init. Anyway, I made a fork of the project and my progress for today is on there.

https://github.com/Berserker66/vulkan

mackst commented 6 years ago

I don't know if you guys heard of pybind11. https://github.com/pybind/pybind11

I'm not a C\C++ programmer but I tired it few days ago.


#include <vulkan\vulkan.h>
#include <pybind11\pybind11.h>
#include <pybind11\stl.h>

namespace py = pybind11;

class Instance
{
public:
    Instance();
    ~Instance();
    bool createInstance();

private:
    VkInstance instance;

};

Instance::Instance()
{

}

Instance::~Instance()
{
    vkDestroyInstance(instance, nullptr);
}

bool Instance::createInstance()
{
    VkApplicationInfo appInfo = {};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Test vk";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "no engin";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;

    VkInstanceCreateInfo createInfo = {};
    createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
    createInfo.pApplicationInfo = &appInfo;
    createInfo.enabledExtensionCount = 0;
    createInfo.enabledLayerCount = 0;

    auto result = vkCreateInstance(&createInfo, nullptr, &instance);
    if (result == VK_SUCCESS)
        return true;
    else
        return false;
}

void testList(const std::vector<const char*> &l) {
    for (auto i : l)
        std::cout << i << "\n";
}

PYBIND11_MODULE(pyvk, m) {
    m.doc() = "vulkan python binding.";

    py::class_<Instance>(m, "Instance")
        .def(py::init<>())
        .def("createInstance", &Instance::createInstance);

    py::enum_<VkStructureType>(m, "StructureType")
        .value("STRUCTURE_TYPE_APPLICATION_INFO", VkStructureType::VK_STRUCTURE_TYPE_APPLICATION_INFO)
        .value("STRUCTURE_TYPE_INSTANCE_CREATE_INFO", VkStructureType::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO)
        .value("STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO", VkStructureType::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO)
        .export_values();

    m.def("mylistfunc", &testList);
}
sunbearc22 commented 6 years ago

Thanks for sharing.

Can you tell me what's the diff. btw pybind11 and cffi?

Also, how does C++11 compare with C, which Vulkan is written in?

Thanks.

On 05/10/2017 17:41, mack stone wrote:

I don't know if you guys heard of pybind11. https://github.com/pybind/pybind11

I'm not a C\C++ programmer but I tired it few days ago.

include <vulkan\vulkan.h>

include <pybind11\pybind11.h>

include <pybind11\stl.h>

namespace py = pybind11;

class Instance { public: Instance(); ~Instance(); bool createInstance();

private: VkInstance instance;

};

Instance::Instance() {

}

Instance::~Instance() { vkDestroyInstance(instance,nullptr); }

bool Instance::createInstance() { VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName ="Test vk"; appInfo.applicationVersion =VK_MAKE_VERSION(1,0,0); appInfo.pEngineName ="no engin"; appInfo.engineVersion =VK_MAKE_VERSION(1,0,0); appInfo.apiVersion = VK_API_VERSION_1_0;

VkInstanceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pApplicationInfo = &appInfo; createInfo.enabledExtensionCount =0; createInfo.enabledLayerCount =0;

auto result =vkCreateInstance(&createInfo,nullptr, &instance); if (result == VK_SUCCESS) return true; else return false; }

void testList(const std::vector<const char*> &l) { for (auto i : l) std::cout << i <<"\n"; }

PYBIND11_MODULE(pyvk, m) { m.doc() ="vulkan python binding.";

py::class_(m,"Instance") .def(py::init<>()) .def("createInstance", &Instance::createInstance);

py::enum_(m,"StructureType") .value("STRUCTURE_TYPE_APPLICATION_INFO", VkStructureType::VK_STRUCTURE_TYPE_APPLICATION_INFO) .value("STRUCTURE_TYPE_INSTANCE_CREATE_INFO", VkStructureType::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO) .value("STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO", VkStructureType::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO) .export_values();

m.def("mylistfunc", &testList); }

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/realitix/vulkan/issues/32#issuecomment-334414272, or mute the thread https://github.com/notifications/unsubscribe-auth/AWWLzu5XacPx33YuzN8v2fuWDNGIJKsVks5spKRVgaJpZM4Pn3N_.

mackst commented 6 years ago

Can you tell me what's the diff. btw pybind11 and cffi?

I think the main diff is pybind11 can very easy to create Object-oriented code in C++. cffi can't do that in C correct me if I'm wrong. With pybind11 all your code write in C++. cffi can be python or C+python.

Above using in python

import pyvk
ins = pyvk.Instance()
ins.createInstance()
>>> True

Also, how does C++11 compare with C, which Vulkan is written in?

I'm not a C\C++ programmer. Can't answer that.

realitix commented 6 years ago

Hello, Please open another issue if you need to talk about another subject. This issue is about Cython.

Thanks.

Berserker66 commented 6 years ago

So, I dug some more into this. After I assembled some more experience assimilating a simpler C library with cython. Happened to be this one: https://github.com/smcameron/open-simplex-noise-in-c

Some things I learned -

The speed advantage is worthwhile, compared to python's opensimplex https://pypi.python.org/pypi/opensimplex/0.1 it is over a magnitude faster and multithreads very well.

So, that is the pro, which was the expected outcome.

The con: If you define a cython function, you do so with C types as input and output. As a result, it does not accept CFFI objects, you'd need to peel off the cffi wrapping every time you want to interact between the two and you'd need to cffi wrap anything you return from cython. This would likely smash any hopes of faster performance. The other way, which is also awkward, is to wrap the cython functions; which are also accessible from the C layer with cffi. Cffi was however never meant to access the python C layer, in its own documentation you can find that you will likely mess up reference counting and bleed memory all over the place.

So, bottomline - It is certainly possible to make a cython interface. I don't see a good way to make cython and cffi interact in a reasonable fashion however, so both interfaces would be exclusive to each other, you can use either one or the other, possibly with converter functions to wrap/unwrap with cffi to share a few objects, like logical_device and stuff like that.

realitix commented 6 years ago

Thanks @Berserker66 for this nice explanation. It would be exclusive but do you see a use case where you would want to use both ?

Berserker66 commented 6 years ago

Well, my own project would be such a use case. I can't vouch for how many other projects there might be.

First off, there is the use case of having a replacement/alternative to the cffi module. Mostly useful for CPython. Where, as far as python and the "application programmer" is concerned the API is the same, basically a cython api that mimics the current module completely. Such a module should perform faster on CPython, probably not on PyPy. Most importantly, it could release the GIL earlier. Let's look at a trace of my program, to see where that could be useful

Let's get some actual data in here. callgraph

You may need to download the file and zoom in, I doubt github handles it well. So this is a multithreaded environment. So 100% time spent just means that there is always at least one thread spending time in there. Almost all bottlenecks in my project release the GIL, so as a result they quickly exit.

We can see that such a 100% time spent function is _callAPI. to be fair, we are also spending 109 ms in vkWaitForFences, which this profiler sees as cpu time spent. (I hope it doesn't actually do that, maybe someone should investigate). A cython version of such a function would still need the gil for parts of it - to decrease the reference count on the incoming variables, but past that it can assemble the vulkan structures, call into vulkan and almost completely check the output and assemble it without the GIL and do so in C code, as opposed to using the cffi library. I expect a decent, but not mind blowing speed advantage here.

More importantly, a full cython-c API, that you call from Cython user-code, could just never use the GIL (except to generate exceptions). Completely unlocking the multi-threaded potential of vulkan.

To be fair the cffi version could also be improved, by not handling every argument the same, but having a handler for each argument and in the parser step of this module, dropping all the handlers into the function. Completely erasing the dict comprehension in _call_api and also removing lots of if statements.

realitix commented 5 years ago

Hello @Berserker66 , I close this issue for now. Maybe someone would create a Cython interface one day !