Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Windows Build #88

Closed
2 of 3 tasks
ComFreek opened this issue Feb 28, 2020 · 26 comments
Closed
2 of 3 tasks

Windows Build #88

ComFreek opened this issue Feb 28, 2020 · 26 comments

Comments

@ComFreek
Copy link
Contributor

ComFreek commented Feb 28, 2020

Latest update: curv has now Windows support merged into master!


I would like to give Windows builds a shot. Has there been any previous failure or success stories?

Describe the solution you'd like A native Windows build capable of accessing the GPU on Windows.

Describe alternatives you've considered

  • WSL: apparently there isn't any host's GPU exposure within WSL yet, so we could only software-render here.

    Nonetheless, I am currently trying out this route.

    • Build successful

    • Running CLI successful (e.g. curve -x 2+2)

    • Running GUI not successful:

      $ curv -x circle
      2D shape 2×2
      GLFW error 0x10007: GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable
      ABORT: GLFW create window failed

      Even though I do have an X server on my Windows host, which works fine with other GUI applications within WSL.

  • Browser: I read on the mailing list that someone is working on a wasm build for the web. If that turns out to have good performance, do we actually need a Windows build?

@doug-moen
Copy link
Member

Nobody has mentioned to me that they are working on a Windows port.

A Win32 client would be the easiest for people to install and use.

It should be possible to run Curv under WSL, if your X server supports OpenGL and the GLX protocol. You'll need to read documentation and enable GLX in your X server. I haven't tried this.

If you get it working, please let me know which X server you used. It would be nice to document the setup process.

In the future, Microsoft says that WSL 2 will support direct hardware GPU access, no need to resort to a Win32 X server. There's no schedule for this. https://docs.microsoft.com/en-us/windows/wsl/wsl2-faq

In the future, I plan to have a WASM port of Curv. I imagine we could have a GUI-only WASM demo this year, and a fully functional client with mesh export next year. A WASM client running in a web browser will probably never match the performance of a native client.

Mesh export is hard, because it is unacceptably slow unless you use the -Ojit option. This works by compiling the Curv program into C++, compiling the C++ into a native executable, then loading and running the executable. That strategy isn't possible from inside a web browser. The best I could do is compile the Curv program into a WASM module, then execute the WASM, and that will never be as fast as compiling Curv code to native using the highly optimizing LLVM or g++ compilers. The WASM->native code compilers being developed for use inside web browsers are not expected to produce code that runs as fast as LLVM. Also, I haven't written the code to compile Curv programs into WASM, which seems like a big project.

@ComFreek
Copy link
Contributor Author

ComFreek commented Feb 29, 2020

It should be possible to run Curv under WSL, if your X server supports OpenGL and the GLX protocol. You'll need to read documentation and enable GLX in your X server.

I see.

  • MobaXTerm's X-server does not support GLX apparently and neither has options to enable it.

  • Xming 6.9.0.31 X-server only supports a lower version of GLX or GLX not at all (couldn't figure out which scenario applies)

    Per the docs the latest Xming release 7.7.0.52 has apparently better support, but it costs money (£10) to obtain it. Unless I am sure it works, I won't buy it. Perhaps I can ask the author to try it if nothing else works.

  • Cygwin/x: will try this and report back. temporary sketched steps:

    1. On your Windows host install Cygwin if you haven't.

    2. Run Cygwin's setup program again and install xinit and xhost.

    3. Within Cygwin edit ~/.bashrc to add export DISPLAY=127.0.0.1:0.0. Then reload via exec bash.

    4. Within Cygwin run xauth add 127.0.0.1:0.0 . 0123456789ABCDEF.

      This will generate/update ~/.XAuthority (unless the path is changed via env variables to something else).

    5. Install WSL

    6. Within WSL run touch ~/.XAuthority if you don't have that file yet

    7. Within WSL cd to your Cygwin's home directory (e.g. /mnt/c/cygwin64/home/ComFreek) and run xauth merge .XAuthority to merge Cygwin's XAuthority file to your WSL's one from step 6.

    8. Within WSL edit ~/.bashrc to add export DISPLAY=127.0.0.1:0.0. Then reload via exec bash.

    9. Within Cygwin run startxwin -- -wgl -listen tcp (see here for the -listen tcp argument)

    10. Within WSL run curv -x circle

      GLFW error 0x10009: GLX: Failed to find a suitable GLXFBConfig
      ABORT: GLFW create window failed

Re native build: I see! What would be the best way to start making adjustments for a native build? Should I just try the commands inside the Makefile on Windows? I guess libraries are dealt with wholly different on Windows.

@doug-moen
Copy link
Member

vcxsrv is another popular choice of X server.
https://sourceforge.net/projects/vcxsrv/

I found a report of somebody getting this same GLFW error using vcxsrv, plus they figured out how to fix it: alecjacobson/computer-graphics-bounding-volume-hierarchy#43

quote: "I fixed it with disabling the native opengl when starting VcXsrv."
microsoft/WSL#2855 (comment)

quote:

in general from a clean Ubuntu install from the store do:

$ sudo apt install ubuntu-desktop mesa-utils
$ export DISPLAY=localhost:0
$ glxgears

On the Windows side, install VcXsrv, choose multiple windows, display 0, start no client, disable native opengl (sic).

@doug-moen
Copy link
Member

Good news! I followed the instructions for installing and configuring vcxsrv, and Curv is working for me in WSL + Ubuntu 18.04.

@doug-moen
Copy link
Member

doug-moen commented Feb 29, 2020

The bad news is, 3D rendering is quite slow. WSL does not provide direct access to the GPU hardware, so Ubuntu Linux defaults to software rendering.

Unfortunately, this can't be fixed until Microsoft updates WSL to support hardware GPU access.

Details

Here's what goes wrong in more detail. Curv uses the X window system, and uses GLX to interface with OpenGL. GLX supports two modes of operation: "direct" and "indirect".

  • Direct mode means that OpenGL rendering is performed by the client process. Normally this is done by direct communication with the GPU, which is fast. With no GPU (which is the case in WSL), Linux defaults to software rendering.
  • Indirect mode means that OpenGL commands are sent over the GLX protocol to the X server, and GPU rendering occurs on the server. This sounds great, because the X server runs as a Win32 process and has direct access to the GPU hardware. Unfortunately, indirect GLX only supports OpenGL 1.4*, and Curv requires OpenGL 3.3. [*Confirmed by personal communication with the author of Xming, Colin Harrison.] This means that the glxgears demo will work, but Curv will fail with an error message when it attempts to open a graphics window.

Running Curv using Direct GLX

To run Curv using direct GLX, you must disable indirect GLX in the X server:

  • for vcxsrv, disable native OpenGL
  • for Xming, disable AIGLX

This works, but rendering is slow. The command curv --version prints this line:

GPU: VMware, Inc., llvmpipe (LLVM 9.0, 256 bits)

which indicates that software rendering is being used (llvmpipe is a software renderer).

Running Curv using Indirect GLX

To run Curv using indirect GLX, you must enable indirect GLX in the X server and in the client.

In the X server, enable indirect GLX like this:

  • for vcxsrv, enable native OpenGL
  • for Xming, enable AIGLX

In the Linux bash shell (the client), set the environment variable

export LIBGL_ALWAYS_INDIRECT=1

If I do this on my machine, then curv --version says this:

Curv: 0.4-508-g6667aedb
Compiler: gcc 7.4.0
Kernel: Linux 4.4.0-18362-Microsoft x86_64
GPU: Intel, Intel(R) HD Graphics 5500
OpenGL: 1.4 (4.4.0 - Build 20.19.15.5058)  

The good news is that the X server is now reporting my GPU hardware (HD Graphics 5500).

If I run glxgears, it works, and it reports a much higher frame rate than in the software rendering (direct) case. If I run curv, it fails when opening a graphics window. The problem is that the indirect GLX protocol only supports OpenGL 1.4 (see the curv --version output above), and Curv requires OpenGL 3.3.

@doug-moen
Copy link
Member

@ComFreek Okay, I'm disappointed by WSL, the performance of software rendering isn't that great. So a WIN32 port looks like it would provide value.

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 2, 2020

Thanks for your further investigations and explanations!

I decided to try native compilation with msys2. At the moment I get some compilation errors, perhaps you could help.

  1. Change Makefile such that the release target calls cmake -DGLEW_INCLUDE_DIR=/mingw64/include/GL/ -DCMAKE_BUILD_TYPE=Release ..

    Of course, this needs to be better handled when integrating that into this repo.

  2. Install msys2 (including upgrading and updating packages as per msys2 docs)

  3. pacman -Sy diffutils mingw-w64-x86_64-clang make mingw-w64-x86_64-cmake git mingw-w64-x86_64-boost pacman -S mingw-w64-x86_64-mesa

    diffutils for cmp used when making

  4. pacman -Sy mingw-w64-x86_64-openexr mingw-w64-x86_64-intel-tbb mingw-w64-x86_64-glm mingw-w64-x86_64-glew mingw-w64-x86_64-dbus

  5. Optionally pacman -Sy doxygen

// Ignore these:
// libxcursor-dev libxinerama-dev libxrandr-dev libglu1-mesa-dev libgles2-mesa-dev libgl1-mesa-dev libxi-dev
// mingw-w64-x86_64-mesa

  1. Then run in MSYS2 shell:
    export CC=clang.exe
    export CXX=clang++.exe
    export CMAKE_GENERATOR="MSYS Makefiles"
    export GLEW_INCLUDE_DIR=/mingw64/include/GL/
    make
    

I get the following compilation errors:

$ make
rm -rf CMakeCache.txt CMakeFiles
mkdir -p release
cd release; cmake -DGLEW_INCLUDE_DIR=/mingw64/include/GL/ -DCMAKE_BUILD_TYPE=Release ..
-- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
-- Using Win32 for window creation
-- Configuring done
-- Generating done
-- Build files have been written to: P:/curv/release
cd release; make
make[1]: Verzeichnis „/p/curv/release“ wird betreten
make[2]: Verzeichnis „/p/curv/release“ wird betreten
make[3]: Verzeichnis „/p/curv/release“ wird betreten
make[3]: Verzeichnis „/p/curv/release“ wird verlassen
make[3]: Verzeichnis „/p/curv/release“ wird betreten
[  2%] Building CXX object CMakeFiles/libcurv.dir/libcurv/analyser.cc.obj
In file included from P:/curv/libcurv/analyser.cc:5:
In file included from P:/curv\libcurv/analyser.h:8:
In file included from P:/curv\libcurv/meaning.h:8:
In file included from P:/curv\libcurv/sc_frame.h:10:
In file included from P:/curv\libcurv/module.h:8:
In file included from P:/curv\libcurv/record.h:9:
In file included from P:/curv\libcurv/symbol.h:8:
P:/curv\libcurv/string.h:145:15: error: use of overloaded operator '<<' is ambiguous (with operand types 'curv::String_Builder' and 'unsigned long long')
        *this << std::forward<First>(first);
        ~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~
P:/curv\libcurv/string.h:146:9: note: in instantiation of function template specialization 'curv::String_Builder::write_all<unsigned long long, char const (&)[2], unsigned long long, char const (&)[21]>' requested here
        write_all(std::forward<Rest>(rest)...);
        ^
P:/curv\libcurv/string.h:215:7: note: in instantiation of function template specialization 'curv::String_Builder::write_all<char const (&)[24], unsigned long long, char const (&)[2], unsigned long long, char const (&)[21]>' requested here
    s.write_all(std::forward<Args>(args)...);
      ^
P:/curv\libcurv/prim.h:311:33: note: in instantiation of function template specialization 'curv::stringify<char const (&)[24], unsigned long long, char const (&)[2], unsigned long long, char const (&)[21]>' requested here
            throw Exception(cx, stringify(
                                ^
P:/curv\libcurv/prim.h:255:32: note: in instantiation of member function 'curv::Binary_Array_Op<curv::Less_Prim>::element_wise_op' requested here
                        return element_wise_op(cx, (List&)rx, (List&)ry);
                               ^
P:/curv\libcurv/prim.h:49:19: note: in instantiation of member function 'curv::Binary_Array_Op<curv::Less_Prim>::call' requested here
      {return Op::call(At_Phrase(*syntax_, f), arg1_->eval(f), arg2_->eval(f));}
                  ^
P:/curv\libcurv/shared.h:57:24: note: in instantiation of member function 'curv::Binary_Op_Expr<curv::Binary_Array_Op<curv::Less_Prim> >::eval' requested here
        ptr = new(raw) T(std::forward<Args>(args)...);
                       ^
P:/curv/libcurv/analyser.cc:716:16: note: in instantiation of function template specialization 'curv::make<curv::Binary_Op_Expr<curv::Binary_Array_Op<curv::Less_Prim> >, curv::Shared<const curv::Binary_Phrase>, curv::Shared<curv::Operation>, curv::Shared<curv::Operation> >' requested here
        return make<Less_Expr>(
               ^

[...]

2 errors generated.
make[3]: *** [CMakeFiles/libcurv.dir/build.make:63: CMakeFiles/libcurv.dir/libcurv/analyser.cc.obj] Fehler 1
make[3]: Verzeichnis „/p/curv/release“ wird verlassen
make[2]: *** [CMakeFiles/Makefile2:271: CMakeFiles/libcurv.dir/all] Fehler 2
make[2]: Verzeichnis „/p/curv/release“ wird verlassen
make[1]: *** [Makefile:130: all] Fehler 2
make[1]: Verzeichnis „/p/curv/release“ wird verlassen
make: *** [Makefile:5: release] Fehler 2

I get similar errors with export CC=gcc.exe, export CXX=g++.exe:

In file included from P:/curv/libcurv/symbol.h:8,
                 from P:/curv/libcurv/record.h:9,
                 from P:/curv/libcurv/module.h:8,
                 from P:/curv/libcurv/sc_frame.h:10,
                 from P:/curv/libcurv/meaning.h:8,
                 from P:/curv/libcurv/analyser.h:8,
                 from P:/curv/libcurv/analyser.cc:5:
P:/curv/libcurv/string.h: In instantiation of 'void curv::String_Builder::write_all(First&&, Rest&& ...) [with First = long long unsigned int; Rest = {const char (&)[2], long long unsigned int, const char (&)[21]}]':
P:/curv/libcurv/string.h:146:9:   required from 'void curv::String_Builder::write_all(First&&, Rest&& ...) [with First = const char (&)[24]; Rest = {long long unsigned int, const char (&)[2], long long unsigned int, const char (&)[21]}]'
P:/curv/libcurv/string.h:215:5:   required from 'curv::Shared<curv::String> curv::stringify(Args&& ...) [with Args = {const char (&)[24], long long unsigned int, const char (&)[2], long long unsigned int, const char (&)[21]}]'
P:/curv/libcurv/prim.h:311:42:   required from 'static curv::Value curv::Binary_Array_Op<PRIM>::element_wise_op(const curv::At_Syntax&, curv::List&, curv::List&) [with PRIM = curv::Power_Prim; curv::List = curv::Tail_Array<curv::List_Base>]'
P:/curv/libcurv/prim.h:255:47:   required from 'static curv::Value curv::Binary_Array_Op<PRIM>::call(const curv::At_Syntax&, curv::Value, curv::Value) [with PRIM = curv::Power_Prim]'
P:/curv/libcurv/prim.h:49:23:   required from 'curv::Value curv::Binary_Op_Expr<Op>::eval(curv::Frame&) const [with Op = curv::Binary_Array_Op<curv::Power_Prim>; curv::Frame = curv::Tail_Array<curv::Frame_Base>]'
P:/curv/libcurv/prim.h:48:19:   required from here
P:/curv/libcurv/string.h:145:15: error: ambiguous overload for 'operator<<' (operand types are 'curv::String_Builder' and 'long long unsigned int')
  145 |         *this << std::forward<First>(first);
      |         ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[...]

make[3]: *** [CMakeFiles/libcurv.dir/build.make:63: CMakeFiles/libcurv.dir/libcurv/analyser.cc.obj] Fehler 1
make[3]: Verzeichnis „/p/curv/release“ wird verlassen
make[2]: *** [CMakeFiles/Makefile2:271: CMakeFiles/libcurv.dir/all] Fehler 2
make[2]: Verzeichnis „/p/curv/release“ wird verlassen
make[1]: *** [Makefile:130: all] Fehler 2
make[1]: Verzeichnis „/p/curv/release“ wird verlassen
make: *** [Makefile:5: release] Fehler 2

@doug-moen
Copy link
Member

The above compiler error should be fixed by commit 1653b19.

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 2, 2020

Thanks, that fixed the error.

I now get some other errors stemming from the fact that boost filesystem path.c_str() returns wchar_t* in my build environment. I naively fixed all these errors by commenting out since I lack knowledge on how to best fix it.

I'll post a link to my fork soon when everything compiles.

Clang: if desired to use Clang, be sure to pass -stdlib=libc++ in CMakeLists.txt and install the libc++ package in msys, see msys2/MINGW-packages#5405.

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 2, 2020

@doug-moen It compiles! (But doesn't link.)

My fork is here: master...ComFreek:master. Apart from the modifications to the submodules below, this changeset highlights all the places which need adjustment for portability. As I lack knowledge, I cannot work on it without further advice from you. Perhaps it would be the fastest if you worked on it (?)

Additionally, you need the following modifications in some submodules:

  • replxx:
    (The only real modification is the commenting out of the define. Everything else is just me documenting/scratching why.)
    image

  • openvdb:
    image

Compiles fine, only with some seemingly straightforward link errors:

[ 95%] Linking CXX executable curv.exe
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lGL
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lboost_iostrea
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lboost_filesys
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lboost_system
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -ldl
collect2.exe: error: ld returned 1 exit status

@doug-moen
Copy link
Member

@ComFreek Thanks for running this experiment.

I can see two paths to porting Curv to Win32.

  1. Use a Cygwin based framework like MSYS2. Since this provides a POSIX interface, then ideally, Curv should simply compile and run with no errors, just as it does under WSL.
  2. Don't use Cygwin, code to the Win32 API. Use portability abstractions or conditional code as necessary.

We have to consider the impact of use-cygwin vs use-win32 on each of my dependencies. For use-cygwin, does each dependency compile and run using Cygwin? For use-win32, does each dependency compile and run on Win32?

Here are my dependencies:

  • replxx -- supports Windows, doesn't compile under Cygwin
  • double-conversion -- supports Windows, compiles on Cygwin
  • glad -- supports Windows, compiles on Cygwin
  • opengl -- a cross-platform API
  • IMGUI -- supports Windows, compiles on Cygwin
  • googletest -- supports Windows, compiles on Cygwin
  • openvdb -- supports Windows, doesn't compile on Cygwin
  • glfw -- supports Windows, compiles on Cygwin
  • Boost -- supports Windows, compiles on Cygwin
  • glm -- supports Windows, compiles on Cygwin
  • ordered-map -- it is just a data structure with no OS deps, so should be fine
  • stb -- supports Windows, compiles on Cygwin

All of my current dependencies support use-win32 and can be compiled against the WIN32 API, based on the fact that they all have #ifdef _WIN32 code in them.

Some of my dependencies fail to compile on Cygwin: replxx, openvdb. So with use-cygwin, we'd have to fix these problems and submit patches to upstream.

One thing that bothers me about Cygwin is that it isn't POSIX and it isn't WIN32. Instead, it is some weird and undocumented hybrid of the two. You don't follow a spec to program against it, you just compile and run your code and debug it until all of the Cygwin weirdness has been fixed. Win32 is a terrible API, but it has the advantage of being stable and well documented.

Can a hybrid approach work? Like, use-cygwin for the Curv source code, and use-win32 for all my dependencies? I don't know. The problem is header files. I'd be mixing use-cygwin and use-win32 header files in the same Curv source files, and there might be conflicts.

It seems like use-win32 is the safe approach, and would avoid heroic reverse engineering efforts to debug conflicts between posix, cygwin and win32 APIs. Like, figuring out what is causing the current compile errors.

The work involved would be identifying Curv code that is posix-dependent, and rewriting it to use portability abstractions. We could use the Boost library for most of the portability abstractions. That one place where I compile a curv program into a native code executable file, then load the file into memory and execute it, is likely resistant to any portability abstraction, so we'll just have a separate Win32 implementation for that.

Then, the next question is what compiler to use on Windows. People who do all their development on Windows use MS Visual C++. But that compiler is weird, and doesn't behave the same as gcc and clang. I think it is safest to use gcc or clang, in terms of compatibility with the Curv source and all my linux-friendly dependency libraries.

I don't have any personal experience doing an open source Windows port. What do you think?

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 3, 2020

I second use-win32, possibly with a wrapper library (e.g. Boost), and with a Linux-style build environment (msys2 + gcc/clang, not MSVC).

I think it is safest to use gcc or clang, in terms of compatibility with the Curv source and all my linux-friendly dependency libraries.

In particular, I would not even know how to install the libraries for compiling on Windows.

The work involved would be identifying Curv code that is posix-dependent, and rewriting it to use portability abstractions.

I already identifier most of this code, I suppose. See my fork changeset. One of the biggest things is that char* <--> wchar_t* issue. Win32 uses wchar_t* everywhere in its API -- if compiled with some "modern" recommended macro defined (was it UNICODE?). I think most of the non-working places can be easily replaced by some invocation of the Boost library. E.g. don't use rename, but use Boost's rename.

I think I'll work on that linker errors first.

@doug-moen
Copy link
Member

I suggest that the first step is to adjust your build environment, revert all the source code changes that you made previously, and then build Curv in Win32 mode. You will get more compile errors than with MSYS2/Cygwin: that is expected.

All of the dependencies that I compile from source are in the extern directory. Ideally there are no Win32 compile errors in any of the extern subdirectories.

The dependency that most worries me is openvdb. It is the biggest and most complex of my source dependencies. It isn't necessary to compile it from source. If we have problems compiling it, the solution is to grab a pre-compiled copy of the openvdb library from the openvdb project, or from a Windows package manager.

The problem with boost::filesystem::path::c_str() returning a wchar_t* is not hard to solve. The short answer is to use path.string().c_str() instead. Documentation is here: https://www.boost.org/doc/libs/1_72_0/libs/filesystem/doc/reference.html#class-path

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 3, 2020

and then build Curv in Win32 mode

I don't understand this. I thought I did this.

@doug-moen
Copy link
Member

I want you to do a native Windows build, not a Cygwin build. This means building Curv on Windows without the Cygwin headers, or the Cygwin library, and without defining __CYGWIN__.

My understanding is that if you download MinGW-w64
https://sourceforge.net/projects/mingw-w64/
and use it to build Curv, then you will be doing a native Windows build.

But if you use MSYS2, then you are doing a Cygwin build. MSYS2 contains MinGW-w64, plus the cygwin libraries and headers, plus other stuff.

I just read some of the MSYS2 documentation, and found this paragraph:

Use msys2 shell for running pacman, makepkg, makepkg-mingw and for building POSIX-dependent software that you don't intend to distribute. Use mingw shells for building native Windows software and other tasks.

So maybe what you need to do is build Curv using the "mingw shell" instead of the "msys2 shell". Does that make sense?

@mkeeter
Copy link

mkeeter commented Mar 3, 2020

My notes on building Antimony as a native Windows app may be relevant – the specific shell that you're looking for is the "MSYS2 MinGW 64-bit" shell.

@ComFreek
Copy link
Contributor Author

Thanks for the replies. Currently, I cannot find the time to do this, so at least from my side this needs to be postponed a few weeks perhaps.

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 17, 2020

Due to the unfortunate situation caused by COVID-19, I had more spare time than expected:

tl;dr: @doug-moen Compilation works, linking fails due to lots of undefined references to OpenVDB stuff. Do you have any ideas?


  1. Set-up curv's and its submodules' source code with the adaptions described above

    I know you said I should start with a clean Curv clone again. However, that one threw exactly the same errors I encountered before and dummy-fixed with my changeset from above.

  2. Within MSYS2*, install the required packaged described above via pacman

    *) I guess it doesn't make a difference if you open the MSYS2 MinGW or other shell for this.

  3. mkdir -p release && cd release && cd release; /mingw64/bin/cmake -G"MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release ..

  4. cd release, VERBOSE=1 make

So far, it compiles fine in step 4, but it fails to link with the same errors as above:

[ 95%] Linking CXX executable curv.exe
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lGL
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lboost_iostream
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lboost_filesystem
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lboost_system
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -ldl
collect2.exe: error: ld returned 1 exit status

Actually, running VERBOSE=1 make gives you the link command line as well, which I tried to adapt to the following:

/C/msys64/mingw64/bin/g++.exe -D_GLIBCXX_USE_CXX11_ABI=0 -g -U_XOPEN_SOURCE -std=c++17 -Wall -Wno-unused-result -O3 -DNDEBUG   -Wl,--whole-archive CMakeFiles/curv.dir/objects.a -Wl,--no-whole-archive  -o curv.exe -Wl,--major-image-version,0,--minor-image-version,0  libcurv_geom.a libcurv.a libimgui.a extern/glfw/src/libglfw3.a libglad.a /mingw64/lib/opengl32.dll.a libreplxx.a libdouble-conversion.a /mingw64/lib/libboost_filesystem-mt.a extern/openvdb/openvdb/liblibopenvdb.a -lHalf -ltbb -lpthread /C/msys64/mingw64/lib/libboost_iostreams-mt.dll.a /C/msys64/mingw64/lib/libboost_system-mt.dll.a -lHalf /C/msys64/mingw64/lib/libz.dll.a -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32

Differences to the actual command line run by make (not posted here) are:

  • -lGL replaced by /mingw64/lib/opengl32.dll.a
  • -lboost_iostream removed as /C/msys64/mingw64/lib/libboost_iostreams-mt.dll.a was already present (where does that come from?)
  • -lboost_filesystem replaced by /mingw64/lib/libboost_filesystem-mt.a (or should it be ....a.dll? that file also exists)
  • -lboost_system removed as /C/msys64/mingw64/lib/libboost_system-mt.dll.a was already present (where does it come from?)
  • -ldl removed for no good reason (trial & error)

That linker command I run in the release subdirectory. As a result, I got a plethora of undefined references starting with:

C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/curv.dir/objects.a(export_mesh.cc.obj): in function `export_mesh(Mesh_Format, curv::Value, curv::Program&, Export_Params const&, std::ostream&)':
P:/curv-modified/curv/export_mesh.cc:210: undefined reference to `__imp__ZN7openvdb4v5_110initializeEv'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/curv.dir/objects.a(export_mesh.cc.obj): in function `openvdb::v5_1::GridBase::GridBase()':
P:/curv-modified/extern/openvdb/openvdb/Grid.h:410: undefined reference to `__imp__ZN7openvdb4v5_14math9Transform21createLinearTransformEd'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/9.3.0/../../../../x86_64-w64-mingw32/bin/ld.exe: P:/curv-modified/extern/openvdb/openvdb/Grid.h:410: undefined reference to `__imp__ZTVN7openvdb4v5_18GridBaseE'

...

If I didn't miss anything, all linker errors are caused by undefined references to OpenVDB stuff.

@doug-moen
Copy link
Member

export_mesh.cc calls openvdb::initialize(). And there is a link error: can't find the symbol __imp__ZN7openvdb4v5_110initializeEv. So what does that mean? Googling...

https://stackoverflow.com/questions/22766540/semantics-of-imp-symbols
This page says that an __imp__ symbol is a reference to the import table of a Windows DLL. This is a concept that doesn't exist in Linux. But, we aren't linking with the OpenVDB DLL. Instead, based on the linker command you gave me, we are linking to extern/openvdb/openvdb/liblibopenvdb.a, which is a static library, not a DLL. This static library does not have an import table, and that's why the symbol isn't defined.

The build file I wrote for Curv (CMakeLists.txt) contains this line:

target_link_libraries(curv PUBLIC libcurv_geom libcurv imgui glfw glad ${LibOpenGL} replxx double-conversion boost_iostreams boost_filesystem boost_system openvdb_static Half tbb dl pthread)                                                   

Note that the dependency is on openvdb_static, which is a static library. So CMake is building the static library and linking to it. This dependency won't work on Windows, we need a DLL. My guess is that replacing this with openvdb will fix the current link errors.

Note that CMakeLists.txt already contains if (APPLE) code to make Curv build on MacOS. I am certain that if (WINDOWS) will also be necessary.

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 18, 2020

Thanks, that was a very good hint!

I now succeeded in building Curv as follows:


  1. Set-up of Dependencies

    1. On Windows install MSYS2

    2. Within "MSYS2 MiNGW 64-bit" run

      1. pacman -Syuu -- reopen the shell if asked to do so

      2. pacman -S --needed diffutils mingw-w64-x86_64-clang make mingw-w64-x86_64-cmake git mingw-w64-x86_64-boost mingw-w64-x86_64-mesa mingw-w64-x86_64-openexr mingw-w64-x86_64-intel-tbb mingw-w64-x86_64-glm mingw-w64-x86_64-glew mingw-w64-x86_64-dbus

        NB: diffutils is required for cmp, which is somewhere used in the build process (in a Makefile)

      3. Optionally run pacman -S --needed doxygen

      4. git clone --recursive https://github.com/ComFreek/curv.git, cd curv (for reference, this is the changeset wrt. original curv)

      5. git submodule update --init

  2. Building

    1. make (will error with not being able to find openvdb.dll.a, that's fine)

    2. pushd release/extern/openvdb/openvdb

    3. VERBOSE=1 make (will error with undefined references to ...tbb...)

    4. Rerun the last command run by the previous make with added -ltbb

      For example, on my set-up the last command was

      cd /P/curv/release/extern/openvdb/openvdb && /C/msys64/mingw64/bin/g++.exe   -g -U_XOPEN_SOURCE -std=c++17 -Wall -Wno-unused-result -O3 -DNDEBUG  -shared -o libopenvdb.dll -Wl,--out-implib,libopenvdb.dll.a -Wl,--major-image-version,5,--minor-image-version,1 -Wl,--whole-archive CMakeFiles/openvdb_shared.dir/objects.a -Wl,--no-whole-archive  -lboost_iostreams-mt -lboost_system-mt -lHalf -lz -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32`.
      

      Hence, I run

      cd /P/curv/release/extern/openvdb/openvdb && /C/msys64/mingw64/bin/g++.exe   -g -U_XOPEN_SOURCE -std=c++17 -Wall -Wno-unused-result -O3 -DNDEBUG  -shared -o libopenvdb.dll -Wl,--out-implib,libopenvdb.dll.a -Wl,--major-image-version,5,--minor-image-version,1 -Wl,--whole-archive CMakeFiles/openvdb_shared.dir/objects.a -Wl,--no-whole-archive  -lboost_iostreams-mt -lboost_system-mt -lHalf -lz -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -ltbb
      

      with -ltbb added at the end.

    5. popd

    6. make

  3. Running: ./release/curv.exe --help

    This successfully prints the help screen.


Caveats:

  • critical: The non-portable features inside Curv are still not ported in my fork, but rather just commented out / trivialized to compilable lines.

    e.g. ./release/curv.exe -x 2+2 errors with ERROR: "": Invalid argument. That probably stems from one of the places where I trivialized something unportable with "".

  • low-priority: We need to manually invoke make inside OpenVDB as for some reason unknown to me, CMake just builds OpenVDB in its static, not shared version. And even whlie doing so, we need to manually link.

@doug-moen
Copy link
Member

doug-moen commented Mar 18, 2020

Thanks, @ComFreek, that's good news. I looked at your changes, so here are some comments.

In general, I think it is okay to use

  #ifdef _WIN32
      /* windows specific code */
  #else
      /* POSIX code */
  #endif

in C++ code, in those few places where no better alternative exists. That's the technique used by most of my dependencies. MinGW is documented to define _WIN32.

The change from uint to unsigned int in libcurv/viewer is fine, I'll accept that change.

There are many code changes related to strings, paths and filename strings. All of the affected lines of code can be changed to be portable between Windows and POSIX, without ifdefs, and without using the wchar_t* type. See https://www.boost.org/doc/libs/1_72_0/libs/filesystem/doc/reference.html#class-path.

  • On Windows, path.c_str() returns wchar_t*, so don't do that. Use path.string() in contexts where either std::string or char* is accepted, and use path.string().c_str() in contexts that require a char*. In Curv, there are a bunch of functions that have a const String_Ref& parameter, and these accept either char* or std::string.
  • On Windows, the filesystem::path(char*) constructor is not defined, so use filesystem::path(std::string) instead. E.g., given const char* str, then instead of path(str) use path(std::string(str)).

The other nonportable C++ Curv code can initially be ported using #ifdef _WIN32 ... #else ... #endif. Some of this code might later be made portable by using portability abstractions from Boost.

I notice you have a private fork of replxx. That library is already portable to Windows, and it uses #ifdef _WIN32 for non-portable code. The goal is to use the replxx git repository directly without maintaining a local fork. I'm using a version locked submodule to reference replxx; I can update to the latest release and that might fix some Windows portability bugs. If there are further problems then the fix can be pushed to the replxx repo: the owner is quite responsive.

I notice you have a private fork of openvdb. This is a large, complex library maintained by a large organization. I don't want to maintain a private fork. They already support a Windows build. Looking at the code and documentation, they use Microsoft Visual C++ to build the library. They don't use #ifdef _WIN32 in their C++ code, they use #ifdef _MSC instead, which tests for the Visual C++ compiler instead of testing for the Windows platform. So I think that coding style is not compatible with MinGW. Since there is a lot of bureaucracy involved in submitting changes (you need to sign a CLA), I think it is easier grab a prebuilt copy of the Windows DLL for openvdb from pacman, and use that.

@ComFreek
Copy link
Contributor Author

Good! I'll work on converting my dirty changes to real portable code following your suggestions.

I notice you have a private fork of replxx.
I notice you have a private fork of openvdb.

Yes, they were just meant to contain my quick & dirty "fixes" as of now. In the long run, I wanted to submit upstream patches, too.

my alternative is to grab a prebuilt copy of the Windows DLL for openvdb from a Windows package manager, and use that.

If I read correctly, this is something you want to look into, it seems from your wording. I hope that'll work without ABI incompatilibities.

@doug-moen
Copy link
Member

To clarify about openvdb: It is a large complex library, and there are bureaucratic barriers to submitting changes (you have to sign a CLA). I think it will be less work for you if you install the openvdb library using pacman (header files and DLL). You'll be installing version 7.x, so you will need to compile Curv using the pacman installed openvdb header files.

The reason I stopped using the preinstalled OpenVDB library on Linux was to work around a problem with Gentoo Linux installing an old and incompatible version of OpenVDB: see #16

@ComFreek
Copy link
Contributor Author

ComFreek commented Mar 23, 2020

Wohoo, Curv now works on Windows! E.g. ./curv.exe -x cylinder gives you:

image

Also, it has access to my "graphics card" (I don't have any dedicated one):

$ ./curv.exe --version
Curv: 0.4-519-gbc78b6ab-dirty
Compiler: gcc 9.3.0
Windows
GPU: Intel, Intel(R) HD Graphics 2500
OpenGL: 4.0.0 - Build 10.18.10.5100

I will now prepare a PR. Would you like to still retain openvdb compiled as a submodule for non-Windows OSes?

@doug-moen
Copy link
Member

Wow, that is fantastic news! I still need to compile openvdb as a submodule in order to build Curv on Gentoo Linux, so the answer to your question is yes.

@ComFreek ComFreek mentioned this issue Mar 23, 2020
@ComFreek
Copy link
Contributor Author

#89 did it!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants