mitsuba-renderer/enoki

How to pybind FloatC and FloatD

Closed this issue · 17 comments

I am rendering a gradient image using enoki CUDA array. Is there any suggestion on how to store the c++ cuda array FloatC and FloatD (or vector) into python so I can call backward in python for optimization? I didn't see there is a binding for that in enoki/python.h

You can compile extensive Python bindings for both, including automatic convertion to PyTorch/NumPy. See the 'refactor' branch which contains considerable additional work to this end. The enoki/python.h file is only applicable to simple CPU-only arrays.

In particular, look at ENOKI_CUDA, ENOKI_AUTODIFF, and ENOKI_PYTHON in CMake.

File "", line 1, in
TypeError: Unable to convert function return value to a Python type! The signature was
(self: xxxx.Render) -> enoki::DiffArray<enoki::CUDAArray >

the c++ code is something like this...
struct Render {
FloatD m_bitmap_d;
Render();
FloatD &run();
const FloatD &run() const { return m_bitmap_d; }
void config_print();
~Render() {};
};

Right. You'll need to load the Enoki python module so that its return value converter knows what to do with these types. So simply run import enoki in your Python code before calling Render::run. Let me say again that you will probably get a lot more out of the bindings in the refactor branch of Enoki. For that branch, you will need to run import enoki.cuda_autodiff.

And FWIW what enoki/python.h does is to convert C++ Enoki types into corresponding Python types (NumPy arrays) by copying them. That's not really a good way of interfacing between C++ <-> Python when a GPU is involved and the arrays are potentially very big.

You can also take a look at the following list:

https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html.
The header enoki/python.h corresponds to variant #3, and in Mitsuba 2 and Enoki's refactor branch we have now completely switched over to variant #1 for doing conversions.

I tried these code and the problem still occur...
C++:
NAMESPACE_BEGIN(xxxx)
struct Render {
Render();
FloatC run(); // return a FloatC
~Render() {};
};
NAMESPACE_END(xxxx)

// pybind
#include <pybind11/pybind11.h>
#include <pybind11/operators.h>
#include <pybind11/complex.h>
#include <pybind11/functional.h>
#include <pybind11/stl.h>
#include <enoki/python.h>
#include <enoki/stl.h>
#include <enoki/cuda.h>
#include <enoki/autodiff.h>
...
pybind11::class_xxxx::Render(m, "Render")
.def(pybind11::init<>())
.def("run", &xxxx::Render::run);

Python:

import enoki
import xxxx
r = xxxx.Render()
r.run()
TypeError: Unable to convert function return value to a Python type! The signature was
(self: xxxx.Render) -> enoki::CUDAArray

enoki
Also there is an error while building the refactor branch

Issue 1: You are likely compiling Enoki & your own project with different compilers (G++ vs Clang, or different versions of them). In this case, pybind11 keeps classes in different namespaces to prevent ABI incompatibilities.

Issue 2: Can you try again with the latest refactor branch?

After running refactor branch:
image
image

For Issue one:
both the enoki/master any my project are using gcc-8
the problem still occur..
Here is the cmake .. output for both
image
image

Can you give Clang a try?

sudo apt install clang-9 lldb-9 clang-tools-9 clang clangd lldb clang-tools \
        libc++abi1-9  libc++abi-9-dev libc++1-9 libc++-9-dev \

I fixed the GCC issue you reported just now. Generally, I would still recommend using Clang. I've run into so many issues with miscompilations with GCC in the last 2 years that I have completely lost trust in it.

ok, also should i write pybind11::module::import("enoki"); in my c++ code?

Yes, that's probably a good idea.

I tried to build both (enoki refactor branch) with clang-9 and still now working...
Cmake output:
image
image

python:
image

c++:
image
image
FloatC means:
using FloatC = enoki::CUDAArray<Float>;
Thanks for helping T_T

So just to clarify: you can compile Enoki including its Python bindings using both GCC and Clang? But returning a CUDAArray<float> from pybind11-bound code does not work?

IMO there are only two possibilities for this:

  1. Your project and Enoki were compiled with different settings. You have to be careful about the following: use the same version of Clang or GCC. Second (important!) use the same C++ standard library. When compiling Enoki with clang, it uses -stdlib=libc++, which means that your project also has to use -stdlib=libc++.

  2. You did not import the Enoki python module that contains the type you are using. Note that there was a change here for the refactor branch: on master, it is enough to write import enoki. On the refactor branch, you need to write: import enoki.autodiff_cuda (or cuda_autodiff, I don't remember). Note that all the mathematical functions remain in enoki, so one would typically do computations as follows:

from enoki.autodiff_cuda import Float32, UInt32, ...
import enoki as ek

x = ek.linspace(Float32, -1, 1, 100)
y = ek.select(x > 0, ek.asin(x), ek.acos(x))
...

Thanks, it works