Overv / VulkanTutorial

Tutorial for the Vulkan graphics and compute API
https://vulkan-tutorial.com
Creative Commons Attribution Share Alike 4.0 International
3.06k stars 511 forks source link

Code samples: add CMakeLists.txt #249

Closed kovrov closed 2 years ago

kovrov commented 2 years ago

Calling cmake --build . will build the sample code executables for each chapter in it's own folders, compile corresponding shaders and copy all required resources to appropriate sub-folders. After that any example is ready to be run from it's build sub-folder.

CMake project requires following dependencies:

kovrov commented 2 years ago

I hadn't had a chance to test this on a windows box. Only on linux (ubuntu) and osx.

Overv commented 2 years ago

That is quite cool, nice setup! If this is merged, it should probably also be referenced by the tutorial as a possible example of how to set up Vulkan projects. I can try to test it on Windows sometime this week.

kovrov commented 2 years ago

I just realized I was calling glslangvalidator with --target-env vulkan1.2. Fixed to match the value used in VkApplicationInfo::apiVersion.

Overv commented 2 years ago

I get the following errors when trying to build on Arch Linux:

$ cmake .
CMake Error at CMakeLists.txt:46 (add_executable):
  Target "27_model_loading" links to target "glm::glm" but the target was not
  found.  Perhaps a find_package() call is missing for an IMPORTED target, or
  an ALIAS target is missing?
Call Stack (most recent call first):
  CMakeLists.txt:156 (add_chapter)
...

$ cmake --build .
[  1%] Generating 27_model_loading/shaders
[  2%] Compiling Shaders
[  2%] Built target 27_model_loading_shader
[  3%] Building CXX object CMakeFiles/27_model_loading.dir/27_model_loading.cpp.o
[  4%] Linking CXX executable 27_model_loading/27_model_loading
/usr/bin/ld: cannot find -lglm::glm
/usr/bin/ld: cannot find -ltinyobjloader::tinyobjloader
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/27_model_loading.dir/build.make:106: 27_model_loading/27_model_loading] Error 1
make[1]: *** [CMakeFiles/Makefile2:214: CMakeFiles/27_model_loading.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

When I change all glm::glm references to just glm and tinyobjloader::tinyobjloader to just tinyobjloader everything works just fine. Do you know what might be up with that? This is with cmake version 3.21.1.

kovrov commented 2 years ago

This is interesting, looks like a "downstream" issue. Different distros may provide and maintain its own cmake configs for the dev packages.

On my ubuntu box (20.04) the glm package (0.9.9.7) is installed from ubuntu repo. The library is declared as "glm::glm" (in /usr/lib/cmake/glm/glmConfig.cmake):

add_library(glm::glm INTERFACE IMPORTED)

tinyobjloader is in similar situation - /usr/lib/x86_64-linux-gnu/cmake/tinyobjloader/tinyobjloader-targets.cmake (although libtinyobjloader-dev is not from 20.04, but rather was backported from 20.10):

add_library(tinyobjloader::tinyobjloader SHARED IMPORTED)

What's interesting is that on my macbook, the packages (installed via homebrew formulas) has the same target names. I was hoping this is universal naming convention.

Since glm is a header-only library, the only reason to "findpackage" and "link" it to the executable is to configure include paths (imported targets usually has INTERFACE_INCLUDE_DIRECTORIES property).

tinyobjloader on the other side, is an actual library we need to link against. And as far as I remember on mac, simply linking via -ltinyobjloader was failing (likely because it is not located on standard library paths).

Edit:

For what it worth, the glm manual specifies its cmake module as "glm::glm" (https://github.com/g-truc/glm/blob/master/manual.md#-15-finding-glm-with-cmake):

find_package(glm REQUIRED)
target_link_libraries(<your executable> glm::glm)
kovrov commented 2 years ago

@Overv what are the versions of installed tinyobjloader and glm dev packages?

Overv commented 2 years ago

The versions I have installed are:

I'm not sure why this is happening since I know very little about cmake myself.

The line you mention (add_library) is not in my glmConfig.cmake, but the following line is in my glmTargets.cmake:

add_library(glm INTERFACE IMPORTED)

I'm not sure why they are using a different name in the Arch Linux package.

Update: I've created a bug report to ask about this: https://bugs.archlinux.org/task/71987

kovrov commented 2 years ago

Weird, since glm release 0.9.9.8 cmake target should be glm::glm. On the other hand your tinyobjloader library is indeed old and don't have latest cmake changes.

Anyway. This is not important. We know that correct targets are glm::glm and tinyobjloader::tinyobjloader. And this specific problem is in dev environment. Possible solutions are:

  1. Keep this change as is and rely on users to "correct" their environment if needed.

  2. Add a workaround in our cmake project, e.g:

    if (NOT TARGET tinyobjloader::tinyobjloader) add_library(tinyobjloader::tinyobjloader ALIAS tinyobjloader) endif () if (NOT TARGET glm::glm) add_library(glm::glm ALIAS glm) endif ()

More importantly, I'd like to see if there are any issues on windows.

Overv commented 2 years ago

I just tested it on Windows and ran into a few small issues:

  1. It couldn't find GLFW by itself:
CMake Error at CMakeLists.txt:5 (find_package):
  By not providing "Findglfw3.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "glfw3", but
  CMake did not find one.

  Could not find a package configuration file provided by "glfw3" with any of
  the following names:

    glfw3Config.cmake
    glfw3-config.cmake

  Add the installation prefix of "glfw3" to CMAKE_PREFIX_PATH or set
  "glfw3_DIR" to a directory containing one of the above files.  If "glfw3"
  provides a separate development package or SDK, be sure it has been
  installed.

It looks like it couldn't automatically find glfw3 because it installs into a directory called GLFW by default due to the project name being GLFW rather than glfw3. When I rename the install directory to glfw3 it works just fine. This is a known issue.

  1. It also couldn't find stb_image.h by itself, which makes sense.
CMake Error at CMakeLists.txt:17 (message):
  stb_image.h not found

However, it did find both glm and tinyobjloader automatically without any issues.

After generating all of the projects I could build them just fine with Visual Studio 2017. However, the executables crash because they can't find glfw3.dll. After manually copying glfw3.dll to the directory containing the executable, it starts running but crashes with a runtime exception failed to open file! because it cannot find the compiled shader shaders/vert.spv.

I fixed this by changing the "Working Directory" in the "Debugging" section of the project settings from $(ProjectDir) to $(ProjectDir)$(ProjectName). This can be fixed in CMake by adding the following line to CMakeLists.txt in add_chapter:

set_target_properties (${CHAPTER_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${CHAPTER_NAME}")

I'm not sure what to do about the dll issue, but we should probably just recommend users to add glfw3.dll to their path.

kovrov commented 2 years ago

Thanks!

Where is stb_image.h located on your windows box?

This statement is basically searches for stb_image.h in standard include locations (e.g. /usr/include, /usr/include/ARCH, /usr/ARCH/include, etc.) with additional subdirectory stb to check below each standard directory:

find_path (STB_INCLUDEDIR stb_image.h PATH_SUFFIXES stb)

In my case it is in /usr/include/stb/stb_image.h

Solution would be to set CMAKE_INCLUDE_PATH override in visual studio project (equivalent of -DCMAKE_INCLUDE_PATH=/path/to/stb command line argument).

kovrov commented 2 years ago

I guess the runtime problem finding the dll is caused by changed location of the GLFW binary dir.

Try this - instead of manually renaming GLFW folder to glfw3, configure and build GLFW with CMAKE_INSTALL_PREFIX set to C:/Program Files (x86)/glfw3 and then install. E.g.:

cmake -DCMAKE_INSTALL_PREFIX="C:/Program Files (x86)/glfw3" ../glfw
cmake --build .
cmake --install .

When CMAKE_INSTALL_PREFIX not set, it going be something like c:/Program Files/${PROJECT_NAME} by default, which sounds like your case.

Overv commented 2 years ago

Where is stb_image.h located on your windows box?

In ~/source/libs/stb-master where ~ is my Windows home directory. To my knowledge there is no standard include location in Windows so I wouldn't know where else to put it.

Try this - instead of manually renaming GLFW folder to glfw3, configure and build GLFW with CMAKE_INSTALL_PREFIX set to C:/Program Files (x86)/glfw3 and then install. E.g.:

That didn't make a difference for me. I think that it'll only find glfw3.dll automatically if it's either in the same directory as the executable or in the system path. It doesn't look like cmake is adding any directories (like C:/Program Files (x86)/glfw3) to the system path as part of the install process.

With that said, we could avoid this situation by statically linking glfw3 on Windows.

On a sidenote, the issue with the glm target being named differently in Arch will be fixed soon.

kovrov commented 2 years ago

Configuring with -DCMAKE_INCLUDE_PATH=~/source/libs/stb-master argument (visual studio may have a gui to define cmake variables) should fix the failing check for stb_image.h.

Overv commented 2 years ago

Configuring with -DCMAKE_INCLUDE_PATH=~/source/libs/stb-master argument (visual studio may have a gui to define cmake variables) should fix the failing check for stb_image.h.

Alright, but then you might as well configure STB_INCLUDEDIR directly.

kovrov commented 2 years ago

Configuring with -DCMAKE_INCLUDE_PATH=~/source/libs/stb-master argument (visual studio may have a gui to define cmake variables) should fix the failing check for stb_image.h.

Alright, but then you might as well configure STB_INCLUDEDIR directly.

STB_INCLUDEDIR is meant to be set automatically on environments that either provides stb pkgconf file (e.g. ubuntu) or has the stb headers on standard locations.

Unfortunately upstream STB is not providing any config packages, because it is designed to be project-level drop-in headers.

If there is a well known windows development practice when it comes to 3rd party libraries locations, we could hard-code additional paths argument (called HINTS) to find_path invocation. Otherwise, there is nothing else I can think of but requiring user to setCMAKE_INCLUDE_PATH (which was designed exactly for this purpose).

Overv commented 2 years ago

STB_INCLUDEDIR is meant to be set automatically on environments that either provides stb pkgconf file (e.g. ubuntu) or has the stb headers on standard locations.

Right, but I don't think it's common to have all header files together in a single directory like with /usr/include on Linux. That's why I figured that it made more sense to specify an include directory per library rather than to give CMake one global include path.

If there is a well known windows development practice when it comes to 3rd party libraries locations, we could hard-code additional paths argument (called HINTS) to find_path invocation.

I don't think there is any, unfortunately. I don't know of any myself and I couldn't find any suggestions online either.

kovrov commented 2 years ago

I agree - having headers for all libraries in single default location is not ideal. My point is that additional include paths (plural), if required, meant to be configured by user via well known CMAKE_INCLUDE_PATH variable. STB_INCLUDEDIR is just one of internal variables.

Speaking of glfw3 runtime issue - adding glfw library location to PATH env variable is another alternative to copying the dll to each binary's folder or statically linking.

Overv commented 2 years ago

Would you consider this ready to be merged now?

kovrov commented 2 years ago

Would you consider this ready to be merged now?

Yes, I think it's ready.

Overv commented 2 years ago

Nice, thanks again for the work in putting this together.