From 33bfb474d1f33bb67f4d41a8e20bd29bc72a4f26 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Wed, 27 Apr 2016 21:50:02 -0400 Subject: [PATCH 01/14] added cmake build to the project --- CMakeLists.txt | 11 +++++++++ README.md | 10 ++++++++ build/.gitignore | 6 +++++ build/build.sh | 13 +++++++++++ cube/CMakeLists.txt | 57 +++++++++++++++++++++++++++++++++++++++++++++ cube/cube.cpp | 9 ++++--- lib/CMakeLists.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++ lib/qvulkanview.cpp | 5 +++- 8 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 build/.gitignore create mode 100755 build/build.sh create mode 100644 cube/CMakeLists.txt create mode 100644 lib/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c8044ff --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +SET(CMAKE_CXX_COMPILER "g++") + +PROJECT(QtVulkan) + +CMAKE_MINIMUM_REQUIRED(VERSION 3.5.0) + +SET(CMAKE_CXX_STANDARD 14) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +ADD_SUBDIRECTORY(lib) +ADD_SUBDIRECTORY(cube) diff --git a/README.md b/README.md index 4865bc5..87c35b5 100644 --- a/README.md +++ b/README.md @@ -13,5 +13,15 @@ Also make sure the include and library paths in cube.pro and lib.pro are correct The plan is to port the rotating cube demo, as well as have a Qt Widget that contains a lot of the boilerplate for vulkan setup. +# building with CMake +If you would like to build the project using CMake instead of qmake, follow the below instructions + +``` + cd build + ./build.sh + cd cube/ + qtvulkan_cube +``` + ## known issues: * resizing is stuck after one resize event diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..fc2a9a1 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,6 @@ +CMakeCache.txt +cmake_install.cmake +Makefile +CMakeFiles/ +cube/ +lib/ diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 0000000..75f6b26 --- /dev/null +++ b/build/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# clean up the old build items +rm -rf CMakeCache.txt CMakeFiles/ cmake_install.cmake cube/ lib/ Makefile + +# build the project +cmake .. +make + +# post work for assets +mkdir cube/shaders +cp ../cube/*.spv cube/shaders +cp ../cube/lunarg.ppm cube diff --git a/cube/CMakeLists.txt b/cube/CMakeLists.txt new file mode 100644 index 0000000..948d214 --- /dev/null +++ b/cube/CMakeLists.txt @@ -0,0 +1,57 @@ +SET(CMAKE_AUTOMOC ON) +SET(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") +SET(CMAKE_CXX_LINK_FLAGS "-lassimp") +SET(CMAKE_CXX_LINK_FLAGS "-lxcb") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR") + +MESSAGE(STATUS ${VULKAN_LIB}) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX -DVK_PROTOTYPES -D_USE_MATH_DEFINES") + +ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + +FIND_PACKAGE(Qt5Widgets REQUIRED) +FIND_PACKAGE(Qt5Gui REQUIRED) +FIND_PACKAGE(Qt5Core REQUIRED) +#FIND_PACKAGE(Qt5Quick REQUIRED) +#FIND_PACKAGE(Qt5Qml REQUIRED) + +SET(qtvulkan_cube_SRCS + cube.cpp +) + +INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + #${Qt5Quick_INCLUDE_DIRS} + #${Qt5Qml_INCLUDE_DIRS} +) + +SET(qtvulkan_cube_LIBS + ${Qt5Widgets_LIBRARIES} + ${Qt5Quick_LIBRARIES} + ${Qt5Core_LIBRARIES} + #${Qt5Gui_LIBRARIES} + #${QT5Qml_LIBRARIES} +) + +ADD_EXECUTABLE(qtvulkan_cube ${qtvulkan_cube_SRCS} ${qtvulkan_cube_MOC_SRCS}) + +SET_TARGET_PROPERTIES(qtvulkan_cube + PROPERTIES + CMAKE_CXX_STANDARD 11 + CMAKE_CXX_STANDARD_REQUIRED ON +) + +TARGET_LINK_LIBRARIES(qtvulkan_cube "-lvulkan" ${qtvulkan_cube_LIBS}) + +# INSTALL +#INSTALL( TARGETS qtvulkan_cube RUNTIME DESTINATION /usr/local/qtvulkan) + +#FILE(GLOB SPIRV_FILES "*.spv") +#INSTALL(FILES ${SPIRV_FILES} DESTINATION /usr/local/qtvulkan/shaders) diff --git a/cube/cube.cpp b/cube/cube.cpp index 3c50806..75e8884 100644 --- a/cube/cube.cpp +++ b/cube/cube.cpp @@ -36,7 +36,10 @@ #include #include -#include +//#include +// TODO - I need to fix this below line so that you can build using the above line +#include + static PFN_vkGetDeviceProcAddr g_gdpa = NULL; @@ -1343,7 +1346,7 @@ VkShaderModule Demo::prepare_vs() { char *vertShaderCode; size_t size; - vertShaderCode = demo_read_spv("cube-vert.spv", &size); + vertShaderCode = demo_read_spv("shaders/cube-vert.spv", &size); assert(vertShaderCode); m_vert_shader_module = @@ -1360,7 +1363,7 @@ VkShaderModule Demo::prepare_fs() { char *fragShaderCode; size_t size; - fragShaderCode = demo_read_spv("cube-frag.spv", &size); + fragShaderCode = demo_read_spv("shaders/cube-frag.spv", &size); assert(fragShaderCode); m_frag_shader_module = prepare_shader_module(fragShaderCode, size); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..07a5433 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,56 @@ +SET(CMAKE_AUTOMOC ON) +SET(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") +SET(CMAKE_CXX_LINK_FLAGS "-lassimp") +SET(CMAKE_CXX_LINK_FLAGS "-lxcb") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR") + +MESSAGE(STATUS ${VULKAN_LIB}) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX -DVK_PROTOTYPES -D_USE_MATH_DEFINES") + +ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + +FIND_PACKAGE(Qt5Widgets REQUIRED) +FIND_PACKAGE(Qt5Gui REQUIRED) +FIND_PACKAGE(Qt5Core REQUIRED) +#FIND_PACKAGE(Qt5Quick REQUIRED) +#FIND_PACKAGE(Qt5Qml REQUIRED) + +SET(qtvulkan_SRCS + qvulkaninstance.cpp + qvulkanview.cpp + main.cpp +) + +INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + #${Qt5Quick_INCLUDE_DIRS} + #${Qt5Qml_INCLUDE_DIRS} +) + +SET(qtvulkan_LIBS + ${Qt5Widgets_LIBRARIES} + ${Qt5Quick_LIBRARIES} + ${Qt5Core_LIBRARIES} + #${Qt5Gui_LIBRARIES} + #${QT5Qml_LIBRARIES} +) + +ADD_EXECUTABLE(qtvulkan ${qtvulkan_SRCS} ${qtvulkan_MOC_SRCS}) + +SET_TARGET_PROPERTIES(qtvulkan + PROPERTIES + CMAKE_CXX_STANDARD 11 + CMAKE_CXX_STANDARD_REQUIRED ON +) + +TARGET_LINK_LIBRARIES(qtvulkan "-lvulkan" ${qtvulkan_LIBS}) + +# INSTALL +#INSTALL( TARGETS qtvulkan RUNTIME DESTINATION /usr/local/qtvulkan) diff --git a/lib/qvulkanview.cpp b/lib/qvulkanview.cpp index 6eb60c4..aae0ed6 100644 --- a/lib/qvulkanview.cpp +++ b/lib/qvulkanview.cpp @@ -5,7 +5,10 @@ #include #include #include "qvulkaninstance.h" -#include +//#include +// TODO - I need to fix this below line so that you can build using the above line +#include + QVulkanInstance* QVulkanView::s_vulkanInstance = nullptr; From 19d6b432476559a5a0818c8d63e97e9ea1894bef Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Wed, 27 Apr 2016 21:52:09 -0400 Subject: [PATCH 02/14] modified the readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 87c35b5..b88b895 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ The plan is to port the rotating cube demo, as well as have a Qt Widget that con If you would like to build the project using CMake instead of qmake, follow the below instructions ``` - cd build - ./build.sh - cd cube/ - qtvulkan_cube + $ cd build + $ ./build.sh + $ cd cube/ + $ qtvulkan_cube ``` ## known issues: From acb86b04b50cd51f1ac48b5ba57bd42a60a8c085 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 20:55:05 -0400 Subject: [PATCH 03/14] moved shaders back into root for build --- build/build.sh | 3 +-- cube/cube.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build/build.sh b/build/build.sh index 75f6b26..7b11bcd 100755 --- a/build/build.sh +++ b/build/build.sh @@ -8,6 +8,5 @@ cmake .. make # post work for assets -mkdir cube/shaders -cp ../cube/*.spv cube/shaders +cp ../cube/*.spv cube cp ../cube/lunarg.ppm cube diff --git a/cube/cube.cpp b/cube/cube.cpp index 75e8884..bf26499 100644 --- a/cube/cube.cpp +++ b/cube/cube.cpp @@ -1346,7 +1346,7 @@ VkShaderModule Demo::prepare_vs() { char *vertShaderCode; size_t size; - vertShaderCode = demo_read_spv("shaders/cube-vert.spv", &size); + vertShaderCode = demo_read_spv("cube-vert.spv", &size); assert(vertShaderCode); m_vert_shader_module = @@ -1363,7 +1363,7 @@ VkShaderModule Demo::prepare_fs() { char *fragShaderCode; size_t size; - fragShaderCode = demo_read_spv("shaders/cube-frag.spv", &size); + fragShaderCode = demo_read_spv("cube-frag.spv", &size); assert(fragShaderCode); m_frag_shader_module = prepare_shader_module(fragShaderCode, size); From 398635ceb7b684ff4875338c4e09dbd99ca083e2 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 21:32:00 -0400 Subject: [PATCH 04/14] rearranged the project files so that qtvulkan could be a used as a library --- CMakeLists.txt | 3 +- build/build.sh | 6 +-- src/CMakeLists.txt | 4 ++ {lib => src/app}/CMakeLists.txt | 0 {lib => src/app}/main.cpp | 0 {cube => src/cube}/CMakeLists.txt | 0 {cube => src/cube}/cube-frag.spv | Bin {cube => src/cube}/cube-vert.spv | Bin {cube => src/cube}/cube.cpp | 0 {cube => src/cube}/cube.h | 0 {cube => src/cube}/cube.pro | 0 {cube => src/cube}/lunarg.ppm | 0 src/lib/CMakeLists.txt | 61 +++++++++++++++++++++++++++ {lib => src/lib}/lib.pro | 0 {lib => src/lib}/qvulkaninstance.cpp | 0 {lib => src/lib}/qvulkaninstance.h | 0 {lib => src/lib}/qvulkanview.cpp | 0 {lib => src/lib}/qvulkanview.h | 0 src/qml/CMakeLists.txt | 7 +++ src/qml/main.qml | 13 ++++++ 20 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/CMakeLists.txt rename {lib => src/app}/CMakeLists.txt (100%) rename {lib => src/app}/main.cpp (100%) rename {cube => src/cube}/CMakeLists.txt (100%) rename {cube => src/cube}/cube-frag.spv (100%) rename {cube => src/cube}/cube-vert.spv (100%) rename {cube => src/cube}/cube.cpp (100%) rename {cube => src/cube}/cube.h (100%) rename {cube => src/cube}/cube.pro (100%) rename {cube => src/cube}/lunarg.ppm (100%) create mode 100644 src/lib/CMakeLists.txt rename {lib => src/lib}/lib.pro (100%) rename {lib => src/lib}/qvulkaninstance.cpp (100%) rename {lib => src/lib}/qvulkaninstance.h (100%) rename {lib => src/lib}/qvulkanview.cpp (100%) rename {lib => src/lib}/qvulkanview.h (100%) create mode 100644 src/qml/CMakeLists.txt create mode 100644 src/qml/main.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index c8044ff..fd1b13d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,5 +7,4 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.5.0) SET(CMAKE_CXX_STANDARD 14) SET(CMAKE_CXX_STANDARD_REQUIRED ON) -ADD_SUBDIRECTORY(lib) -ADD_SUBDIRECTORY(cube) +ADD_SUBDIRECTORY(src) diff --git a/build/build.sh b/build/build.sh index 7b11bcd..9f83910 100755 --- a/build/build.sh +++ b/build/build.sh @@ -1,12 +1,12 @@ #!/bin/bash # clean up the old build items -rm -rf CMakeCache.txt CMakeFiles/ cmake_install.cmake cube/ lib/ Makefile +rm -rf CMakeCache.txt CMakeFiles/ cmake_install.cmake src/ Makefile # build the project cmake .. make # post work for assets -cp ../cube/*.spv cube -cp ../cube/lunarg.ppm cube +#cp ../cube/*.spv cube +#cp ../cube/lunarg.ppm cube diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..bded282 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(lib) +#add_subdirectory(app) +#add_subdirectory(qml) +#add_subdirectory(cube) diff --git a/lib/CMakeLists.txt b/src/app/CMakeLists.txt similarity index 100% rename from lib/CMakeLists.txt rename to src/app/CMakeLists.txt diff --git a/lib/main.cpp b/src/app/main.cpp similarity index 100% rename from lib/main.cpp rename to src/app/main.cpp diff --git a/cube/CMakeLists.txt b/src/cube/CMakeLists.txt similarity index 100% rename from cube/CMakeLists.txt rename to src/cube/CMakeLists.txt diff --git a/cube/cube-frag.spv b/src/cube/cube-frag.spv similarity index 100% rename from cube/cube-frag.spv rename to src/cube/cube-frag.spv diff --git a/cube/cube-vert.spv b/src/cube/cube-vert.spv similarity index 100% rename from cube/cube-vert.spv rename to src/cube/cube-vert.spv diff --git a/cube/cube.cpp b/src/cube/cube.cpp similarity index 100% rename from cube/cube.cpp rename to src/cube/cube.cpp diff --git a/cube/cube.h b/src/cube/cube.h similarity index 100% rename from cube/cube.h rename to src/cube/cube.h diff --git a/cube/cube.pro b/src/cube/cube.pro similarity index 100% rename from cube/cube.pro rename to src/cube/cube.pro diff --git a/cube/lunarg.ppm b/src/cube/lunarg.ppm similarity index 100% rename from cube/lunarg.ppm rename to src/cube/lunarg.ppm diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt new file mode 100644 index 0000000..051672e --- /dev/null +++ b/src/lib/CMakeLists.txt @@ -0,0 +1,61 @@ +SET(CMAKE_AUTOMOC ON) +SET(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") +SET(CMAKE_CXX_LINK_FLAGS "-lassimp") +SET(CMAKE_CXX_LINK_FLAGS "-lxcb") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR") + +MESSAGE(STATUS ${VULKAN_LIB}) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX -DVK_PROTOTYPES -D_USE_MATH_DEFINES") + +ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + +FIND_PACKAGE(Qt5Widgets REQUIRED) +FIND_PACKAGE(Qt5Gui REQUIRED) +FIND_PACKAGE(Qt5Core REQUIRED) +#FIND_PACKAGE(Qt5Quick REQUIRED) +#FIND_PACKAGE(Qt5Qml REQUIRED) + +SET(qtvulkan_SRCS + qvulkaninstance.cpp + qvulkanview.cpp +) + +INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + #${Qt5Quick_INCLUDE_DIRS} + #${Qt5Qml_INCLUDE_DIRS} +) + +ADD_LIBRARY(qtvulkan SHARED ${qtvulkan_SRCS}) + +SET(qtvulkan_LIBS + ${Qt5Widgets_LIBRARIES} + ${Qt5Quick_LIBRARIES} + ${Qt5Core_LIBRARIES} + #${Qt5Gui_LIBRARIES} + #${QT5Qml_LIBRARIES} +) + +TARGET_LINK_LIBRARIES(qtvulkan "-lvulkan" ${qtvulkan_LIBS}) + +SET_TARGET_PROPERTIES(qtvulkan + PROPERTIES + CMAKE_CXX_STANDARD 11 + CMAKE_CXX_STANDARD_REQUIRED ON +) + +SET_TARGET_PROPERTIES(qtvulkan + PROPERTIES + VERSION 0.01 + SOVERSION 1 +) + +# INSTALL +#INSTALL( TARGETS qtvulkan RUNTIME DESTINATION /usr/local/qtvulkan) diff --git a/lib/lib.pro b/src/lib/lib.pro similarity index 100% rename from lib/lib.pro rename to src/lib/lib.pro diff --git a/lib/qvulkaninstance.cpp b/src/lib/qvulkaninstance.cpp similarity index 100% rename from lib/qvulkaninstance.cpp rename to src/lib/qvulkaninstance.cpp diff --git a/lib/qvulkaninstance.h b/src/lib/qvulkaninstance.h similarity index 100% rename from lib/qvulkaninstance.h rename to src/lib/qvulkaninstance.h diff --git a/lib/qvulkanview.cpp b/src/lib/qvulkanview.cpp similarity index 100% rename from lib/qvulkanview.cpp rename to src/lib/qvulkanview.cpp diff --git a/lib/qvulkanview.h b/src/lib/qvulkanview.h similarity index 100% rename from lib/qvulkanview.h rename to src/lib/qvulkanview.h diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt new file mode 100644 index 0000000..4e9dd30 --- /dev/null +++ b/src/qml/CMakeLists.txt @@ -0,0 +1,7 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# INSTALL + +FILE(GLOB QML_FILES "*.qml") + +#INSTALL(FILES ${QML_FILES} DESTINATION /usr/local/qtvulkan/qml) diff --git a/src/qml/main.qml b/src/qml/main.qml new file mode 100644 index 0000000..4f91a22 --- /dev/null +++ b/src/qml/main.qml @@ -0,0 +1,13 @@ +import QtQuick 2.5 +import qtvulkan 1.0 + +Rectangle { + id: window + width: 800 + height: 600 + + QtVulkan { + id: viewport + anchors.fill: parent + } +} From f65f0fe73f1a69e07fcaddb1c5ea1ae6b145aa1e Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 21:46:07 -0400 Subject: [PATCH 05/14] fixed basic application build --- src/CMakeLists.txt | 2 +- src/app/CMakeLists.txt | 31 +++++++++++++++++-------------- src/lib/CMakeLists.txt | 2 ++ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bded282..3501248 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(lib) -#add_subdirectory(app) +add_subdirectory(app) #add_subdirectory(qml) #add_subdirectory(cube) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 07a5433..454c811 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -1,4 +1,4 @@ -SET(CMAKE_AUTOMOC ON) +#SET(CMAKE_AUTOMOC ON) SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") @@ -21,36 +21,39 @@ FIND_PACKAGE(Qt5Core REQUIRED) #FIND_PACKAGE(Qt5Quick REQUIRED) #FIND_PACKAGE(Qt5Qml REQUIRED) -SET(qtvulkan_SRCS - qvulkaninstance.cpp - qvulkanview.cpp +SET(app_SRCS main.cpp ) -INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} +INCLUDE_DIRECTORIES( + ${qtvulkan_SOURCE_DIR} + ${Qt5Gui_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} #${Qt5Quick_INCLUDE_DIRS} #${Qt5Qml_INCLUDE_DIRS} ) -SET(qtvulkan_LIBS - ${Qt5Widgets_LIBRARIES} - ${Qt5Quick_LIBRARIES} - ${Qt5Core_LIBRARIES} - #${Qt5Gui_LIBRARIES} - #${QT5Qml_LIBRARIES} +LINK_LIBRARIES(qtvulkan) + +SET(app_LIBS + ${Qt5Widgets_LIBRARIES} + ${Qt5Quick_LIBRARIES} + ${Qt5Core_LIBRARIES} + #${Qt5Gui_LIBRARIES} + #${QT5Qml_LIBRARIES} ) -ADD_EXECUTABLE(qtvulkan ${qtvulkan_SRCS} ${qtvulkan_MOC_SRCS}) +ADD_EXECUTABLE(app ${app_SRCS} ${app_MOC_SRCS}) -SET_TARGET_PROPERTIES(qtvulkan +SET_TARGET_PROPERTIES(app PROPERTIES CMAKE_CXX_STANDARD 11 CMAKE_CXX_STANDARD_REQUIRED ON ) -TARGET_LINK_LIBRARIES(qtvulkan "-lvulkan" ${qtvulkan_LIBS}) +#TARGET_LINK_LIBRARIES(qtvulkan "-lvulkan" ${qtvulkan_LIBS}) +TARGET_LINK_LIBRARIES(app "-lvulkan" ${app_LIBS}) # INSTALL #INSTALL( TARGETS qtvulkan RUNTIME DESTINATION /usr/local/qtvulkan) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 051672e..3e8d9ef 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,3 +1,5 @@ +PROJECT(qtvulkan) + SET(CMAKE_AUTOMOC ON) SET(CMAKE_INCLUDE_CURRENT_DIR ON) From 0937bb0d6b5be3de2237f79be8bf3f103bc3fca2 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 21:49:55 -0400 Subject: [PATCH 06/14] got rid of the moc build process which is not needed for the main application --- src/app/CMakeLists.txt | 3 +-- src/cube/CMakeLists.txt | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 454c811..2071c6b 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -1,4 +1,3 @@ -#SET(CMAKE_AUTOMOC ON) SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") @@ -44,7 +43,7 @@ SET(app_LIBS #${QT5Qml_LIBRARIES} ) -ADD_EXECUTABLE(app ${app_SRCS} ${app_MOC_SRCS}) +ADD_EXECUTABLE(app ${app_SRCS}) SET_TARGET_PROPERTIES(app PROPERTIES diff --git a/src/cube/CMakeLists.txt b/src/cube/CMakeLists.txt index 948d214..5760b3d 100644 --- a/src/cube/CMakeLists.txt +++ b/src/cube/CMakeLists.txt @@ -1,4 +1,3 @@ -SET(CMAKE_AUTOMOC ON) SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") @@ -21,7 +20,7 @@ FIND_PACKAGE(Qt5Core REQUIRED) #FIND_PACKAGE(Qt5Quick REQUIRED) #FIND_PACKAGE(Qt5Qml REQUIRED) -SET(qtvulkan_cube_SRCS +SET(cube_SRCS cube.cpp ) @@ -32,7 +31,7 @@ INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} #${Qt5Qml_INCLUDE_DIRS} ) -SET(qtvulkan_cube_LIBS +SET(cube_LIBS ${Qt5Widgets_LIBRARIES} ${Qt5Quick_LIBRARIES} ${Qt5Core_LIBRARIES} @@ -40,7 +39,7 @@ SET(qtvulkan_cube_LIBS #${QT5Qml_LIBRARIES} ) -ADD_EXECUTABLE(qtvulkan_cube ${qtvulkan_cube_SRCS} ${qtvulkan_cube_MOC_SRCS}) +ADD_EXECUTABLE(cube ${cube_SRCS}) SET_TARGET_PROPERTIES(qtvulkan_cube PROPERTIES @@ -48,7 +47,7 @@ SET_TARGET_PROPERTIES(qtvulkan_cube CMAKE_CXX_STANDARD_REQUIRED ON ) -TARGET_LINK_LIBRARIES(qtvulkan_cube "-lvulkan" ${qtvulkan_cube_LIBS}) +#TARGET_LINK_LIBRARIES(qtvulkan_cube "-lvulkan" ${qtvulkan_cube_LIBS}) # INSTALL #INSTALL( TARGETS qtvulkan_cube RUNTIME DESTINATION /usr/local/qtvulkan) From bcca54603c14ab48edda24e6ada57d2cddce9fe8 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 21:51:30 -0400 Subject: [PATCH 07/14] fixed .gitignore so it ignores the src folder in the build directory --- build/.gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/.gitignore b/build/.gitignore index fc2a9a1..6dd6702 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -2,5 +2,4 @@ CMakeCache.txt cmake_install.cmake Makefile CMakeFiles/ -cube/ -lib/ +src/ From 909e1b670d27f6de7abc85b5f87aba694d112bc4 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 22:06:50 -0400 Subject: [PATCH 08/14] fixed cube demo and added a snapshot to the README --- README.md | 2 ++ example.png | Bin 0 -> 59692 bytes src/CMakeLists.txt | 2 +- src/app/CMakeLists.txt | 2 +- src/cube/CMakeLists.txt | 10 +++++++--- 5 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 example.png diff --git a/README.md b/README.md index b88b895..71eb423 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # qtvulkan +![Example Cube](https://raw.githubusercontent.com/richardlayman/qtvulkan/master/example.png) + compiles, runs, shows rotating cube! requires setting VK_LAYER_PATH and LD_LIBRARY_PATH, e.g. diff --git a/example.png b/example.png new file mode 100644 index 0000000000000000000000000000000000000000..0e51cfd153da038a25cfe74eac3578990465fe17 GIT binary patch literal 59692 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ຶ< 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 literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3501248..476d2ce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(lib) add_subdirectory(app) +add_subdirectory(cube) #add_subdirectory(qml) -#add_subdirectory(cube) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 2071c6b..97280b5 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -21,7 +21,7 @@ FIND_PACKAGE(Qt5Core REQUIRED) #FIND_PACKAGE(Qt5Qml REQUIRED) SET(app_SRCS - main.cpp + main.cpp ) INCLUDE_DIRECTORIES( diff --git a/src/cube/CMakeLists.txt b/src/cube/CMakeLists.txt index 5760b3d..2b1dc97 100644 --- a/src/cube/CMakeLists.txt +++ b/src/cube/CMakeLists.txt @@ -21,16 +21,20 @@ FIND_PACKAGE(Qt5Core REQUIRED) #FIND_PACKAGE(Qt5Qml REQUIRED) SET(cube_SRCS - cube.cpp + cube.cpp ) -INCLUDE_DIRECTORIES(${Qt5Gui_INCLUDE_DIRS} +INCLUDE_DIRECTORIES( + ${qtvulkan_SOURCE_DIR} + ${Qt5Gui_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} #${Qt5Quick_INCLUDE_DIRS} #${Qt5Qml_INCLUDE_DIRS} ) +LINK_LIBRARIES(qtvulkan) + SET(cube_LIBS ${Qt5Widgets_LIBRARIES} ${Qt5Quick_LIBRARIES} @@ -41,7 +45,7 @@ SET(cube_LIBS ADD_EXECUTABLE(cube ${cube_SRCS}) -SET_TARGET_PROPERTIES(qtvulkan_cube +SET_TARGET_PROPERTIES(cube PROPERTIES CMAKE_CXX_STANDARD 11 CMAKE_CXX_STANDARD_REQUIRED ON From 89c3ff257de624a6ae0d39618f32c189e5283c1b Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Thu, 28 Apr 2016 22:12:26 -0400 Subject: [PATCH 09/14] fixed README --- README.md | 4 ++-- build/build.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 71eb423..5372b01 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ If you would like to build the project using CMake instead of qmake, follow the ``` $ cd build $ ./build.sh - $ cd cube/ - $ qtvulkan_cube + $ cd src/cube/ + $ ./cube ``` ## known issues: diff --git a/build/build.sh b/build/build.sh index 9f83910..89d000b 100755 --- a/build/build.sh +++ b/build/build.sh @@ -8,5 +8,5 @@ cmake .. make # post work for assets -#cp ../cube/*.spv cube -#cp ../cube/lunarg.ppm cube +cp ../src/cube/*.spv src/cube +cp ../src/cube/lunarg.ppm src/cube From 942c7ed1ca17b81230e8d6f828d01a67aad1bb79 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Mon, 2 May 2016 20:52:56 -0400 Subject: [PATCH 10/14] qml code builds fine but doesn't display the vulkan window when ran --- src/CMakeLists.txt | 2 +- src/qml/CMakeLists.txt | 62 +- src/qml/main.cpp | 17 + src/qml/main.qml | 14 +- src/qml/window.cpp | 2333 ++++++++++++++++++++++++++++++++++++++++ src/qml/window.h | 202 ++++ 6 files changed, 2616 insertions(+), 14 deletions(-) create mode 100644 src/qml/main.cpp create mode 100644 src/qml/window.cpp create mode 100644 src/qml/window.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 476d2ce..23c02c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(lib) add_subdirectory(app) add_subdirectory(cube) -#add_subdirectory(qml) +add_subdirectory(qml) diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index 4e9dd30..f6a8cbb 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -1,7 +1,61 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_INCLUDE_CURRENT_DIR ON) -# INSTALL +SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") +SET(CMAKE_CXX_LINK_FLAGS "-lassimp") +SET(CMAKE_CXX_LINK_FLAGS "-lxcb") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR") + +MESSAGE(STATUS ${VULKAN_LIB}) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNOMINMAX -DVK_PROTOTYPES -D_USE_MATH_DEFINES") + +ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + +FIND_PACKAGE(Qt5Widgets REQUIRED) +FIND_PACKAGE(Qt5Gui REQUIRED) +FIND_PACKAGE(Qt5Core REQUIRED) +FIND_PACKAGE(Qt5Quick REQUIRED) +FIND_PACKAGE(Qt5Qml REQUIRED) + +SET(qml_SRCS + window.cpp + main.cpp +) -FILE(GLOB QML_FILES "*.qml") +INCLUDE_DIRECTORIES( + ${qtvulkan_SOURCE_DIR} + ${Qt5Gui_INCLUDE_DIRS} + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Widgets_INCLUDE_DIRS} + ${Qt5Quick_INCLUDE_DIRS} + ${Qt5Qml_INCLUDE_DIRS} +) + +LINK_LIBRARIES(qtvulkan) + +SET(qml_LIBS + ${Qt5Widgets_LIBRARIES} + ${Qt5Quick_LIBRARIES} + ${Qt5Core_LIBRARIES} + ${Qt5Gui_LIBRARIES} + ${QT5Qml_LIBRARIES} +) + +ADD_EXECUTABLE(qml ${qml_SRCS} ${qml_MOC_SRCS}) + +SET_TARGET_PROPERTIES(qml + PROPERTIES + CMAKE_CXX_STANDARD 11 + CMAKE_CXX_STANDARD_REQUIRED ON +) + +TARGET_LINK_LIBRARIES(qml "-lvulkan" ${qml_LIBS}) + +# INSTALL +#INSTALL( TARGETS qtvulkan_cube RUNTIME DESTINATION /usr/local/qtvulkan) -#INSTALL(FILES ${QML_FILES} DESTINATION /usr/local/qtvulkan/qml) +#FILE(GLOB SPIRV_FILES "*.spv") +#INSTALL(FILES ${SPIRV_FILES} DESTINATION /usr/local/qtvulkan/shaders) diff --git a/src/qml/main.cpp b/src/qml/main.cpp new file mode 100644 index 0000000..9fcbd56 --- /dev/null +++ b/src/qml/main.cpp @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include +#include "window.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + qmlRegisterType("qtvulkan", 1, 0, "QtVulkan"); + + QQmlApplicationEngine view("main.qml"); + + return app.exec(); +}; diff --git a/src/qml/main.qml b/src/qml/main.qml index 4f91a22..eeece25 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -1,13 +1,9 @@ import QtQuick 2.5 import qtvulkan 1.0 -Rectangle { - id: window - width: 800 - height: 600 - - QtVulkan { - id: viewport - anchors.fill: parent - } +QtVulkan { + id: vulkan + width: 500 + height: 400 + visible: true } diff --git a/src/qml/window.cpp b/src/qml/window.cpp new file mode 100644 index 0000000..3ae921e --- /dev/null +++ b/src/qml/window.cpp @@ -0,0 +1,2333 @@ +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Chia-I Wu + * Author: Courtney Goeltzenleuchter + * Author: Ian Elliott + * Author: Jon Ashburn + */ + +#define VK_USE_PLATFORM_XCB_KHR 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +//#include +// TODO - I need to fix this below line so that you can build using the above line +#include + + +static PFN_vkGetDeviceProcAddr g_gdpa = NULL; + +static const char *tex_files[] = {"lunarg.ppm"}; + +static int validation_error = 0; + +struct vktexcube_vs_uniform { + // Must start with MVP + float mvp[4][4]; + float position[12 * 3][4]; + float attr[12 * 3][4]; +}; + +static const float g_vertex_buffer_data[] = { + -1.0f,-1.0f,-1.0f, // -X side + -1.0f,-1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + + -1.0f,-1.0f,-1.0f, // -Z side + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + + -1.0f,-1.0f,-1.0f, // -Y side + 1.0f,-1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + + -1.0f, 1.0f,-1.0f, // +Y side + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + + 1.0f, 1.0f,-1.0f, // +X side + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + + -1.0f, 1.0f, 1.0f, // +Z side + -1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, +}; + +static const float g_uv_buffer_data[] = { + 0.0f, 0.0f, // -X side + 1.0f, 0.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + 0.0f, 0.0f, + + 1.0f, 0.0f, // -Z side + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + + 1.0f, 1.0f, // -Y side + 1.0f, 0.0f, + 0.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 1.0f, + + 1.0f, 1.0f, // +Y side + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + + 1.0f, 1.0f, // +X side + 0.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + + 0.0f, 1.0f, // +Z side + 0.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, +}; +// clang-format on + +VKAPI_ATTR VkBool32 VKAPI_CALL +dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, size_t location, int32_t msgCode, + const char *pLayerPrefix, const char *pMsg, void *pUserData) { + + Q_UNUSED(objType) + Q_UNUSED(srcObject) + Q_UNUSED(location) + Q_UNUSED(pUserData) + + QDebug q(qDebug()); + + if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { + q<<"ERROR"; + validation_error = 1; + } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + q<<"WARNING"; + validation_error = 1; + } else { + validation_error = 1; + return false; + } + + q<<": ["<>= 1; + } + // No memory types matched, return failure + return false; +} + +void Demo::flush_init_cmd() { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + + if (m_cmd == VK_NULL_HANDLE) + return; + + err = vkEndCommandBuffer(m_cmd); + assert(!err); + + const VkCommandBuffer cmd_bufs[] = {m_cmd}; + VkFence nullFence = VK_NULL_HANDLE; + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + submit_info.waitSemaphoreCount = 0; + submit_info.pWaitSemaphores = NULL; + submit_info.pWaitDstStageMask = NULL; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = cmd_bufs; + submit_info.signalSemaphoreCount = 0; + submit_info.pSignalSemaphores = NULL; + + err = vkQueueSubmit(m_queue, 1, &submit_info, nullFence); + assert(!err); + + err = vkQueueWaitIdle(m_queue); + assert(!err); + + vkFreeCommandBuffers(m_device, m_cmd_pool, 1, cmd_bufs); + m_cmd = VK_NULL_HANDLE; +} + +void Demo::set_image_layout(VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout old_image_layout, + VkImageLayout new_image_layout, + VkAccessFlagBits srcAccessMask) { + DEBUG_ENTRY; + VkResult U_ASSERT_ONLY err; + + if (m_cmd == VK_NULL_HANDLE) { + VkCommandBufferAllocateInfo cmd_ai = {}; + cmd_ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_ai.pNext = NULL; + cmd_ai.commandPool = m_cmd_pool; + cmd_ai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_ai.commandBufferCount = 1; + + err = vkAllocateCommandBuffers(m_device, &cmd_ai, &m_cmd); + assert(!err); + + VkCommandBufferInheritanceInfo cmd_buf_hinfo = {}; + cmd_buf_hinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + cmd_buf_hinfo.pNext = NULL; + cmd_buf_hinfo.renderPass = VK_NULL_HANDLE; + cmd_buf_hinfo.subpass = 0; + cmd_buf_hinfo.framebuffer = VK_NULL_HANDLE; + cmd_buf_hinfo.occlusionQueryEnable = VK_FALSE; + cmd_buf_hinfo.queryFlags = 0; + cmd_buf_hinfo.pipelineStatistics = 0; + VkCommandBufferBeginInfo cmd_buf_info = {}; + cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_info.pNext = NULL; + cmd_buf_info.flags = 0; + cmd_buf_info.pInheritanceInfo = &cmd_buf_hinfo; + err = vkBeginCommandBuffer(m_cmd, &cmd_buf_info); + assert(!err); + } + + VkImageMemoryBarrier image_memory_barrier = {}; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = srcAccessMask; + image_memory_barrier.dstAccessMask = 0; + image_memory_barrier.oldLayout = old_image_layout; + image_memory_barrier.newLayout = new_image_layout; + image_memory_barrier.image = image; + image_memory_barrier.subresourceRange = {aspectMask, 0, 1, 0, 1}; + + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + /* Make sure anything that was copying from this image has completed */ + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + image_memory_barrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + image_memory_barrier.dstAccessMask = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + /* Make sure any Copy or CPU writes to image are flushed */ + image_memory_barrier.dstAccessMask = + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + } + + VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier; + + VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + vkCmdPipelineBarrier(m_cmd, src_stages, dest_stages, 0, 0, NULL, 0, + NULL, 1, pmemory_barrier); +} + +void Demo::draw_build_cmd(VkCommandBuffer cmd_buf) { + DEBUG_ENTRY; + + VkCommandBufferInheritanceInfo cmd_buf_hinfo = {}; + cmd_buf_hinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; + cmd_buf_hinfo.pNext = NULL; + cmd_buf_hinfo.renderPass = VK_NULL_HANDLE; + cmd_buf_hinfo.subpass = 0; + cmd_buf_hinfo.framebuffer = VK_NULL_HANDLE; + cmd_buf_hinfo.occlusionQueryEnable = VK_FALSE; + cmd_buf_hinfo.queryFlags = 0; + cmd_buf_hinfo.pipelineStatistics = 0; + + VkCommandBufferBeginInfo cmd_buf_info = {}; + cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_info.pNext = NULL; + cmd_buf_info.flags = 0; + cmd_buf_info.pInheritanceInfo = &cmd_buf_hinfo; + + + VkClearValue clear_values[2] = {{},{}}; + clear_values[0].color.float32[0] = 0.2f; + clear_values[0].color.float32[1] = 0.2f; + clear_values[0].color.float32[2] = 0.4f; + clear_values[0].color.float32[3] = 0.2f; + clear_values[1].depthStencil = {1.0f, 0}; + + VkRenderPassBeginInfo rp_begin = {}; + rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rp_begin.pNext = NULL; + rp_begin.renderPass = m_render_pass; + rp_begin.framebuffer = m_framebuffers[m_current_buffer]; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = width(); + rp_begin.renderArea.extent.height = height(); + rp_begin.clearValueCount = 2; + rp_begin.pClearValues = clear_values; + + VkResult U_ASSERT_ONLY err; + + err = vkBeginCommandBuffer(cmd_buf, &cmd_buf_info); + assert(!err); + + vkCmdBeginRenderPass(cmd_buf, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); + vkCmdBindDescriptorSets(cmd_buf, VK_PIPELINE_BIND_POINT_GRAPHICS, + m_pipeline_layout, 0, 1, &m_desc_set, 0, + NULL); + + VkViewport viewport = {}; + memset(&viewport, 0, sizeof(viewport)); + viewport.height = (float)height(); + viewport.width = (float)width(); + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + vkCmdSetViewport(cmd_buf, 0, 1, &viewport); + + VkRect2D scissor {}; + memset(&scissor, 0, sizeof(scissor)); + scissor.extent.width = (uint32_t)qMax(0, width()); + scissor.extent.height = (uint32_t)qMax(0, height()); + scissor.offset.x = 0; + scissor.offset.y = 0; + vkCmdSetScissor(cmd_buf, 0, 1, &scissor); + + vkCmdDraw(cmd_buf, 12 * 3, 1, 0, 0); + vkCmdEndRenderPass(cmd_buf); + + VkImageMemoryBarrier prePresentBarrier = {}; + prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + prePresentBarrier.pNext = NULL; + prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + + prePresentBarrier.image = m_buffers[m_current_buffer].image; + VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier; + vkCmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, + NULL, 1, pmemory_barrier); + + err = vkEndCommandBuffer(cmd_buf); + assert(!err); +} + +void Demo::update_data_buffer() { + DEBUG_ENTRY; + QMatrix4x4 MVP, VP; + int matrixSize = 16 * sizeof(float); + uint8_t *pData; + VkResult U_ASSERT_ONLY err; + + VP = m_projection_matrix * m_view_matrix; + + // Rotate 22.5 degrees around the Y axis + m_model_matrix.rotate(0.1f, QVector3D(0.0f, 1.0f, 0.0f)); + + MVP = VP * m_model_matrix; + + err = vkMapMemory(m_device, m_uniform_data.mem, 0, + m_uniform_data.mem_alloc.allocationSize, 0, + (void **)&pData); + assert(!err); + + memcpy(pData, (const void *)MVP.data(), matrixSize); + + vkUnmapMemory(m_device, m_uniform_data.mem); +} + +void Demo::draw() { + DEBUG_ENTRY; + VkResult U_ASSERT_ONLY err; + VkSemaphore presentCompleteSemaphore = 0; + VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = {}; + presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + presentCompleteSemaphoreCreateInfo.pNext = NULL; + presentCompleteSemaphoreCreateInfo.flags = 0; + + VkFence nullFence = VK_NULL_HANDLE; + + err = vkCreateSemaphore(m_device, &presentCompleteSemaphoreCreateInfo, + NULL, &presentCompleteSemaphore); + assert(!err); + + // Get the index of the next available swapchain image: + err = fpAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, + presentCompleteSemaphore, + (VkFence)0, // TODO: Show use of fence + &m_current_buffer); + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + // swapchain is out of date (e.g. the window was resized) and + // must be recreated: + resize_vk(); + draw(); // FIXME recursive - bleh + vkDestroySemaphore(m_device, presentCompleteSemaphore, NULL); + return; + } else if (err == VK_SUBOPTIMAL_KHR) { + // swapchain is not as optimal as it could be, but the platform's + // presentation engine will still present the image correctly. + } else { + assert(!err); + } + + // Assume the command buffer has been run on current_buffer before so + // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL + set_image_layout(m_buffers[m_current_buffer].image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + (VkAccessFlagBits)0); + flush_init_cmd(); + + // Wait for the present complete semaphore to be signaled to ensure + // that the image won't be rendered to until the presentation + // engine has fully released ownership to the application, and it is + // okay to render to the image. + + // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + + VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &presentCompleteSemaphore; + submit_info.pWaitDstStageMask = &pipe_stage_flags; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &m_buffers[m_current_buffer].cmd; + submit_info.signalSemaphoreCount = 0; + submit_info.pSignalSemaphores = NULL; + + err = vkQueueSubmit(m_queue, 1, &submit_info, nullFence); + assert(!err); + + VkPresentInfoKHR present = {}; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.pNext = NULL; + present.swapchainCount = 1; + present.pSwapchains = &m_swapchain; + present.pImageIndices = &m_current_buffer; + present.waitSemaphoreCount = 0; + present.pResults = 0; + + // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER? + err = fpQueuePresentKHR(m_queue, &present); + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + // swapchain is out of date (e.g. the window was resized) and + // must be recreated: + resize_vk(); + } else if (err == VK_SUBOPTIMAL_KHR) { + // swapchain is not as optimal as it could be, but the platform's + // presentation engine will still present the image correctly. + } else { + assert(!err); + } + + err = vkQueueWaitIdle(m_queue); + assert(err == VK_SUCCESS); + + vkDestroySemaphore(m_device, presentCompleteSemaphore, NULL); +} + +void Demo::prepare_buffers() { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + VkSwapchainKHR oldSwapchain = m_swapchain; + + // Check the surface capabilities and formats + VkSurfaceCapabilitiesKHR surfCapabilities = {}; + err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR( + m_gpu, m_surface, &surfCapabilities); + assert(!err); + + uint32_t presentModeCount = 0; + err = fpGetPhysicalDeviceSurfacePresentModesKHR( + m_gpu, m_surface, &presentModeCount, NULL); + assert(!err); + VkPresentModeKHR *presentModes = + (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); + assert(presentModes); + err = fpGetPhysicalDeviceSurfacePresentModesKHR( + m_gpu, m_surface, &presentModeCount, presentModes); + assert(!err); + + VkExtent2D swapchainExtent = {}; + // width and height are either both -1, or both not -1. + if (surfCapabilities.currentExtent.width == (uint32_t)-1) { + // If the surface size is undefined, the size is set to + // the size of the images requested. + swapchainExtent.width = (uint32_t) width(); + swapchainExtent.height = (uint32_t) height(); //FIXME protect against -1 + } else { + // If the surface size is defined, the swap chain size must match + swapchainExtent = surfCapabilities.currentExtent; + resize(surfCapabilities.currentExtent.width, surfCapabilities.currentExtent.height); // FIXME why + } + + // If mailbox mode is available, use it, as is the lowest-latency non- + // tearing mode. If not, try IMMEDIATE which will usually be available, + // and is fastest (though it tears). If not, fall back to FIFO which is + // always available. + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; + for (size_t i = 0; i < presentModeCount; i++) { + if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; + break; + } + if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && + (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { + swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + } + } + + // Determine the number of VkImage's to use in the swap chain (we desire to + // own only 1 image at a time, besides the images being displayed and + // queued for display): + uint32_t desiredNumberOfSwapchainImages = + surfCapabilities.minImageCount + 1; + if ((surfCapabilities.maxImageCount > 0) && + (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { + // Application must settle for fewer images than desired: + desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; + } + + VkSurfaceTransformFlagBitsKHR preTransform = {}; + if (surfCapabilities.supportedTransforms & + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } else { + preTransform = surfCapabilities.currentTransform; + } + + VkSwapchainCreateInfoKHR swapchain_ci = {}; + swapchain_ci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_ci.pNext = NULL; + swapchain_ci.surface = m_surface; + swapchain_ci.minImageCount = desiredNumberOfSwapchainImages; + swapchain_ci.imageFormat = m_format; + swapchain_ci.imageColorSpace = m_color_space; + swapchain_ci.imageExtent.width = swapchainExtent.width; + swapchain_ci.imageExtent.height = swapchainExtent.height; + swapchain_ci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchain_ci.preTransform = preTransform; + swapchain_ci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_ci.imageArrayLayers = 1; + swapchain_ci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_ci.queueFamilyIndexCount = 0; + swapchain_ci.pQueueFamilyIndices = NULL; + swapchain_ci.presentMode = swapchainPresentMode; + swapchain_ci.oldSwapchain = oldSwapchain; + swapchain_ci.clipped = true; + uint32_t i; + + err = fpCreateSwapchainKHR(m_device, &swapchain_ci, NULL, &m_swapchain); + assert(!err); + + // If we just re-created an existing swapchain, we should destroy the old + // swapchain at this point. + // Note: destroying the swapchain also cleans up all its associated + // presentable images once the platform is done with them. + if (oldSwapchain != VK_NULL_HANDLE) { + fpDestroySwapchainKHR(m_device, oldSwapchain, NULL); + } + + err = fpGetSwapchainImagesKHR(m_device, m_swapchain, + &m_swapchainImageCount, NULL); + assert(!err); + + VkImage *swapchainImages = + (VkImage *)malloc(m_swapchainImageCount * sizeof(VkImage)); + assert(swapchainImages); + err = fpGetSwapchainImagesKHR(m_device, m_swapchain, + &m_swapchainImageCount, + swapchainImages); + assert(!err); + + m_buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) * + m_swapchainImageCount); + assert(m_buffers); + + for (i = 0; i < m_swapchainImageCount; i++) { + VkImageViewCreateInfo color_image_view = {}; + color_image_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + color_image_view.pNext = NULL; + color_image_view.format = m_format; + color_image_view.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A, + }; + color_image_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + color_image_view.subresourceRange.baseMipLevel = 0; + color_image_view.subresourceRange.levelCount = 1; + color_image_view.subresourceRange.baseArrayLayer = 0; + color_image_view.subresourceRange.layerCount = 1; + color_image_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + color_image_view.flags = 0; + + m_buffers[i].image = swapchainImages[i]; + + // Render loop will expect image to have been used before and in + // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image + // to that state + set_image_layout( + m_buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + (VkAccessFlagBits)0); + + color_image_view.image = m_buffers[i].image; + + err = vkCreateImageView(m_device, &color_image_view, NULL, + &m_buffers[i].view); + assert(!err); + } + + if (NULL != presentModes) { + free(presentModes); + } +} + +void Demo::prepare_depth() { + DEBUG_ENTRY; + DEBUG_ENTRY; + + const VkFormat depth_format = VK_FORMAT_D16_UNORM; + VkImageCreateInfo image = {}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = depth_format; + image.extent.width = width(); + image.extent.height = height(); + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image.flags = 0; + + VkImageViewCreateInfo view = {}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = VK_NULL_HANDLE; + view.format = depth_format; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = 1; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; + view.flags = 0; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + + VkMemoryRequirements mem_reqs; + VkResult U_ASSERT_ONLY err; + bool U_ASSERT_ONLY pass; + + m_depth.format = depth_format; + + /* create image */ + err = vkCreateImage(m_device, &image, NULL, &m_depth.image); + assert(!err); + + vkGetImageMemoryRequirements(m_device, m_depth.image, &mem_reqs); + assert(!err); + + m_depth.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + m_depth.mem_alloc.pNext = NULL; + m_depth.mem_alloc.allocationSize = mem_reqs.size; + m_depth.mem_alloc.memoryTypeIndex = 0; + + pass = memory_type_from_properties(mem_reqs.memoryTypeBits, + 0, /* No requirements */ + &m_depth.mem_alloc.memoryTypeIndex); + assert(pass); + + /* allocate memory */ + err = vkAllocateMemory(m_device, &m_depth.mem_alloc, NULL, + &m_depth.mem); + assert(!err); + + /* bind memory */ + err = + vkBindImageMemory(m_device, m_depth.image, m_depth.mem, 0); + assert(!err); + + set_image_layout(m_depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + (VkAccessFlagBits)0); + + /* create image view */ + view.image = m_depth.image; + err = vkCreateImageView(m_device, &view, NULL, &m_depth.view); + assert(!err); +} + +VkFormat QtFormat2vkFormat(QImage::Format f) { + switch (f) { +/* + case QImage::Format_Mono: return VK_FORMAT_UNDEFINED; //FIXME + case QImage::Format_MonoLSB: return VK_FORMAT_UNDEFINED; + case QImage::Format_Indexed8: return VK_FORMAT_UNDEFINED; +*/ + case QImage::Format_RGB32: return VK_FORMAT_R8G8B8A8_UNORM; + case QImage::Format_ARGB32: return VK_FORMAT_R8G8B8A8_UNORM; +/* + case QImage::Format_ARGB32_Premultiplied: return VK_FORMAT_; + case QImage::Format_RGB16: return VK_FORMAT_; + case QImage::Format_ARGB8565_Premultiplied: return VK_FORMAT_; + case QImage::Format_RGB666: return VK_FORMAT_; + case QImage::Format_ARGB6666_Premultiplied: return VK_FORMAT_; + case QImage::Format_RGB555: return VK_FORMAT_; + case QImage::Format_ARGB8555_Premultiplied: return VK_FORMAT_; + case QImage::Format_RGB888: return VK_FORMAT_; + case QImage::Format_RGB444: return VK_FORMAT_; + case QImage::Format_ARGB4444_Premultiplied: return VK_FORMAT_; + case QImage::Format_RGBX8888: return VK_FORMAT_; + case QImage::Format_RGBA8888: return VK_FORMAT_; + case QImage::Format_RGBA8888_Premultiplied: return VK_FORMAT_; + case QImage::Format_BGR30: return VK_FORMAT_; + case QImage::Format_A2BGR30_Premultiplied: return VK_FORMAT_; + case QImage::Format_RGB30: return VK_FORMAT_; + case QImage::Format_A2RGB30_Premultiplied: return VK_FORMAT_; + case QImage::Format_Alpha8: return VK_FORMAT_; + case QImage::Format_Grayscale8: return VK_FORMAT_; +*/ + } +} + +void Demo::prepare_texture_image(const char *filename, + struct texture_object *tex_obj, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkFlags required_props) { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + + QImage img(filename); + const VkFormat tex_format = QtFormat2vkFormat(img.format()); + + if (img.isNull()) { + qFatal("Failed to load textures %s\n", filename); + } + + tex_obj->tex_width = img.width(); + tex_obj->tex_height = img.height(); + + VkImageCreateInfo image_create_info = {}; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = NULL; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = tex_format; + image_create_info.extent.width = img.width(); + image_create_info.extent.height = img.height(); + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = tiling; + image_create_info.usage = usage; + image_create_info.flags = 0; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + VkMemoryRequirements mem_reqs; + + err = vkCreateImage(m_device, &image_create_info, NULL, &tex_obj->image); + assert(!err); + + vkGetImageMemoryRequirements(m_device, tex_obj->image, &mem_reqs); + + tex_obj->mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + tex_obj->mem_alloc.pNext = NULL; + tex_obj->mem_alloc.allocationSize = mem_reqs.size; + tex_obj->mem_alloc.memoryTypeIndex = 0; + + bool U_ASSERT_ONLY pass = memory_type_from_properties(mem_reqs.memoryTypeBits, + required_props, + &tex_obj->mem_alloc.memoryTypeIndex); + assert(pass); + + /* allocate memory */ + err = vkAllocateMemory(m_device, &tex_obj->mem_alloc, NULL, + &(tex_obj->mem)); + assert(!err); + + /* bind memory */ + err = vkBindImageMemory(m_device, tex_obj->image, tex_obj->mem, 0); + assert(!err); + + if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + VkImageSubresource subres = {}; + subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subres.mipLevel = 0; + subres.arrayLayer = 0; + + VkSubresourceLayout layout; + void *data; + + vkGetImageSubresourceLayout(m_device, tex_obj->image, &subres, + &layout); + + err = vkMapMemory(m_device, tex_obj->mem, 0, + tex_obj->mem_alloc.allocationSize, 0, &data); + assert(!err); + + memcpy(data, img.bits(), img.byteCount() ); // FIXME - in place decoding wanted + + vkUnmapMemory(m_device, tex_obj->mem); + } + + tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + set_image_layout(tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout, + VK_ACCESS_HOST_WRITE_BIT); + /* setting the image layout does not reference the actual memory so no need + * to add a mem ref */ +} + +void Demo::destroy_texture_image(struct texture_object *tex_objs) { + DEBUG_ENTRY; + + /* clean up staging resources */ + vkFreeMemory(m_device, tex_objs->mem, NULL); + vkDestroyImage(m_device, tex_objs->image, NULL); +} + +void Demo::prepare_textures() { + DEBUG_ENTRY; + + const VkFormat tex_format = VK_FORMAT_R8G8B8A8_UNORM; // FIXME get from qimage format? + VkFormatProperties props; + uint32_t i; + + vkGetPhysicalDeviceFormatProperties(m_gpu, tex_format, &props); + + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + VkResult U_ASSERT_ONLY err; + + if ((props.linearTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && + !m_use_staging_buffer) { + /* Device can texture using linear textures */ + prepare_texture_image(tex_files[i], &m_textures[i], + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + } else if (props.optimalTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { + /* Must use staging buffer to copy linear texture to optimized */ + struct texture_object staging_texture; + + memset(&staging_texture, 0, sizeof(staging_texture)); + prepare_texture_image(tex_files[i], &staging_texture, + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + + prepare_texture_image( + tex_files[i], &m_textures[i], VK_IMAGE_TILING_OPTIMAL, + (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + set_image_layout(staging_texture.image, + VK_IMAGE_ASPECT_COLOR_BIT, + staging_texture.imageLayout, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + (VkAccessFlagBits)0); + + set_image_layout(m_textures[i].image, + VK_IMAGE_ASPECT_COLOR_BIT, + m_textures[i].imageLayout, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + (VkAccessFlagBits)0); + + VkImageCopy copy_region = {}; + + copy_region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; + copy_region.srcOffset = {0, 0, 0}; + copy_region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; + copy_region.dstOffset = {0, 0, 0}; + copy_region.extent = {staging_texture.tex_width, staging_texture.tex_height, 1}; + + vkCmdCopyImage( + m_cmd, staging_texture.image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_textures[i].image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + + set_image_layout(m_textures[i].image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + m_textures[i].imageLayout, + (VkAccessFlagBits)0); + + flush_init_cmd(); + + destroy_texture_image(&staging_texture); + } else { + /* Can't support VK_FORMAT_R8G8B8A8_UNORM !? */ + assert(!"No support for R8G8B8A8_UNORM as texture image format"); + } + + VkSamplerCreateInfo sampler = {}; + sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler.pNext = NULL; + sampler.magFilter = VK_FILTER_NEAREST; + sampler.minFilter = VK_FILTER_NEAREST; + sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampler.mipLodBias = 0.0f; + sampler.anisotropyEnable = VK_FALSE; + sampler.maxAnisotropy = 1; + sampler.compareOp = VK_COMPARE_OP_NEVER; + sampler.minLod = 0.0f; + sampler.maxLod = 0.0f; + sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + sampler.unnormalizedCoordinates = VK_FALSE; + + VkImageViewCreateInfo view = {}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = VK_NULL_HANDLE; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = tex_format; + // attention: BGRA since that is what QImageReader gives us + // does it depoend on the endian-ness of the platform? + view.components = { + VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_A, + }; + view.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + view.flags = 0; + + /* create sampler */ + err = vkCreateSampler(m_device, &sampler, NULL, + &m_textures[i].sampler); + assert(!err); + + /* create image view */ + view.image = m_textures[i].image; + err = vkCreateImageView(m_device, &view, NULL, + &m_textures[i].view); + assert(!err); + } +} + +void Demo::prepare_cube_data_buffer() { + DEBUG_ENTRY; + + VkBufferCreateInfo buf_info; + VkMemoryRequirements mem_reqs; + uint8_t *pData; + int i; + QMatrix4x4 MVP, VP; + VkResult U_ASSERT_ONLY err; + bool U_ASSERT_ONLY pass; + struct vktexcube_vs_uniform data; + + VP = m_projection_matrix * m_view_matrix; + MVP = VP * m_model_matrix; + memcpy(data.mvp, &MVP, sizeof(data.mvp)); + + for (i = 0; i < 12 * 3; i++) { + data.position[i][0] = g_vertex_buffer_data[i * 3]; + data.position[i][1] = g_vertex_buffer_data[i * 3 + 1]; + data.position[i][2] = g_vertex_buffer_data[i * 3 + 2]; + data.position[i][3] = 1.0f; + data.attr[i][0] = g_uv_buffer_data[2 * i]; + data.attr[i][1] = g_uv_buffer_data[2 * i + 1]; + data.attr[i][2] = 0; + data.attr[i][3] = 0; + } + + memset(&buf_info, 0, sizeof(buf_info)); + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + buf_info.size = sizeof(data); + err = + vkCreateBuffer(m_device, &buf_info, NULL, &m_uniform_data.buf); + assert(!err); + + vkGetBufferMemoryRequirements(m_device, m_uniform_data.buf, + &mem_reqs); + + m_uniform_data.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + m_uniform_data.mem_alloc.pNext = NULL; + m_uniform_data.mem_alloc.allocationSize = mem_reqs.size; + m_uniform_data.mem_alloc.memoryTypeIndex = 0; + + pass = memory_type_from_properties( + mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &m_uniform_data.mem_alloc.memoryTypeIndex); + assert(pass); + + err = vkAllocateMemory(m_device, &m_uniform_data.mem_alloc, NULL, + &(m_uniform_data.mem)); + assert(!err); + + err = vkMapMemory(m_device, m_uniform_data.mem, 0, + m_uniform_data.mem_alloc.allocationSize, 0, + (void **)&pData); + assert(!err); + + memcpy(pData, &data, sizeof data); + + vkUnmapMemory(m_device, m_uniform_data.mem); + + err = vkBindBufferMemory(m_device, m_uniform_data.buf, + m_uniform_data.mem, 0); + assert(!err); + + m_uniform_data.buffer_info.buffer = m_uniform_data.buf; + m_uniform_data.buffer_info.offset = 0; + m_uniform_data.buffer_info.range = sizeof(data); +} + +void Demo::prepare_descriptor_layout() { + DEBUG_ENTRY; + + VkDescriptorSetLayoutBinding layout_bindings[2] = {}; + layout_bindings[0].binding = 0; + layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layout_bindings[0].descriptorCount = 1; + layout_bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layout_bindings[0].pImmutableSamplers = NULL; + layout_bindings[1].binding = 1; + layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layout_bindings[1].descriptorCount = DEMO_TEXTURE_COUNT; + layout_bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layout_bindings[1].pImmutableSamplers = NULL; + + VkDescriptorSetLayoutCreateInfo descriptor_layout = {}; + descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_layout.pNext = NULL; + descriptor_layout.bindingCount = 2; + descriptor_layout.pBindings = layout_bindings; + + VkResult U_ASSERT_ONLY err; + + err = vkCreateDescriptorSetLayout(m_device, &descriptor_layout, NULL, + &m_desc_layout); + assert(!err); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; + pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pPipelineLayoutCreateInfo.pNext = NULL; + pPipelineLayoutCreateInfo.setLayoutCount = 1; + pPipelineLayoutCreateInfo.pSetLayouts = &m_desc_layout; + + err = vkCreatePipelineLayout(m_device, &pPipelineLayoutCreateInfo, NULL, + &m_pipeline_layout); + assert(!err); +} + +void Demo::prepare_render_pass() { + DEBUG_ENTRY; + + VkAttachmentDescription attachments[2] = {{},{}}; + attachments[0].format = m_format; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachments[1].format = m_depth.format; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkAttachmentReference color_reference = {}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_reference = {}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = NULL; + subpass.pDepthStencilAttachment = &depth_reference; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + VkRenderPassCreateInfo rp_info = {}; + rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rp_info.pNext = NULL; + rp_info.attachmentCount = 2; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 0; + rp_info.pDependencies = NULL; + VkResult U_ASSERT_ONLY err; + + err = vkCreateRenderPass(m_device, &rp_info, NULL, &m_render_pass); + assert(!err); +} + +VkShaderModule Demo::prepare_shader_module(const char *code, size_t size) { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + VkShaderModule module = 0; + VkShaderModuleCreateInfo moduleCreateInfo = {}; + + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.pNext = NULL; + + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (uint32_t*)code; + moduleCreateInfo.flags = 0; + err = vkCreateShaderModule(m_device, &moduleCreateInfo, NULL, &module); + assert(!err); + + return module; +} + +char *demo_read_spv(const char *filename, size_t *psize) { + long int size; + size_t U_ASSERT_ONLY retval; + char *shader_code; + + FILE *fp = fopen(filename, "rb"); + if (!fp) + return NULL; + + fseek(fp, 0L, SEEK_END); + size = ftell(fp); + + fseek(fp, 0L, SEEK_SET); + + shader_code = (char*)malloc(size); + retval = fread(shader_code, size, 1, fp); + assert(retval == 1); + + *psize = size; + + fclose(fp); + return shader_code; +} + +VkShaderModule Demo::prepare_vs() { + DEBUG_ENTRY; + + char *vertShaderCode; + size_t size; + + vertShaderCode = demo_read_spv("cube-vert.spv", &size); + + assert(vertShaderCode); + m_vert_shader_module = + prepare_shader_module(vertShaderCode, size); + + free(vertShaderCode); + + return m_vert_shader_module; +} + +VkShaderModule Demo::prepare_fs() { + DEBUG_ENTRY; + + char *fragShaderCode; + size_t size; + + fragShaderCode = demo_read_spv("cube-frag.spv", &size); + assert(fragShaderCode); + m_frag_shader_module = + prepare_shader_module(fragShaderCode, size); + + free(fragShaderCode); + + return m_frag_shader_module; +} + +void Demo::prepare_pipeline() { + DEBUG_ENTRY; + + VkGraphicsPipelineCreateInfo pipeline_ci = {}; + VkPipelineCacheCreateInfo pipelineCache_ci = {}; + VkPipelineVertexInputStateCreateInfo vi = {}; + VkPipelineInputAssemblyStateCreateInfo ia = {}; + VkPipelineRasterizationStateCreateInfo rs = {}; + VkPipelineColorBlendStateCreateInfo cb = {}; + VkPipelineDepthStencilStateCreateInfo ds = {}; + VkPipelineViewportStateCreateInfo vp = {}; + VkPipelineMultisampleStateCreateInfo ms = {}; + VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE]; + VkPipelineDynamicStateCreateInfo dynamicState = {}; + VkResult U_ASSERT_ONLY err; + + memset(dynamicStateEnables, 0, sizeof dynamicStateEnables); + memset(&dynamicState, 0, sizeof dynamicState); + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.pDynamicStates = dynamicStateEnables; + + memset(&pipeline_ci, 0, sizeof(pipeline_ci)); + pipeline_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_ci.layout = m_pipeline_layout; + + memset(&vi, 0, sizeof(vi)); + vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + memset(&ia, 0, sizeof(ia)); + ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + memset(&rs, 0, sizeof(rs)); + rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = VK_CULL_MODE_BACK_BIT; + rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rs.depthClampEnable = VK_FALSE; + rs.rasterizerDiscardEnable = VK_FALSE; + rs.depthBiasEnable = VK_FALSE; + rs.lineWidth = 1.0f; + + memset(&cb, 0, sizeof(cb)); + cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + VkPipelineColorBlendAttachmentState *att_state = (VkPipelineColorBlendAttachmentState*)malloc(sizeof(*att_state)); + memset(att_state, 0x0, sizeof(*att_state)); + att_state[0].colorWriteMask = 0xf; + att_state[0].blendEnable = VK_FALSE; + + cb.attachmentCount = 1; + cb.pAttachments = att_state; + + memset(&vp, 0, sizeof(vp)); + vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vp.viewportCount = 1; + dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT; + vp.scissorCount = 1; + dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR; + + memset(&ds, 0, sizeof(ds)); + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds.depthTestEnable = VK_TRUE; + ds.depthWriteEnable = VK_TRUE; + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + ds.depthBoundsTestEnable = VK_FALSE; + ds.back.failOp = VK_STENCIL_OP_KEEP; + ds.back.passOp = VK_STENCIL_OP_KEEP; + ds.back.compareOp = VK_COMPARE_OP_ALWAYS; + ds.stencilTestEnable = VK_FALSE; + ds.front = ds.back; + + memset(&ms, 0, sizeof(ms)); + ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms.pSampleMask = NULL; + ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + // Two stages: vs and fs + pipeline_ci.stageCount = 2; + VkPipelineShaderStageCreateInfo shaderStages[2]; + memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo)); + + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = prepare_vs(); + shaderStages[0].pName = "main"; + + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = prepare_fs(); + shaderStages[1].pName = "main"; + + memset(&pipelineCache_ci, 0, sizeof(pipelineCache_ci)); + pipelineCache_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + + err = vkCreatePipelineCache(m_device, &pipelineCache_ci, NULL, &m_pipelineCache); + assert(!err); + + pipeline_ci.pVertexInputState = &vi; + pipeline_ci.pInputAssemblyState = &ia; + pipeline_ci.pRasterizationState = &rs; + pipeline_ci.pColorBlendState = &cb; + pipeline_ci.pMultisampleState = &ms; + pipeline_ci.pViewportState = &vp; + pipeline_ci.pDepthStencilState = &ds; + pipeline_ci.pStages = shaderStages; + pipeline_ci.renderPass = m_render_pass; + pipeline_ci.pDynamicState = &dynamicState; + pipeline_ci.renderPass = m_render_pass; + + + err = vkCreateGraphicsPipelines(m_device, m_pipelineCache, 1, + &pipeline_ci, NULL, &m_pipeline); + assert(!err); + + vkDestroyShaderModule(m_device, m_frag_shader_module, NULL); + vkDestroyShaderModule(m_device, m_vert_shader_module, NULL); +} + +void Demo::prepare_descriptor_pool() { + DEBUG_ENTRY; + + VkDescriptorPoolSize type_counts[2] = {{},{}}; + type_counts[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + type_counts[0].descriptorCount = 1; + type_counts[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + type_counts[1].descriptorCount = DEMO_TEXTURE_COUNT; + + VkDescriptorPoolCreateInfo descriptor_pool = {}; + descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptor_pool.pNext = NULL; + descriptor_pool.maxSets = 1; + descriptor_pool.poolSizeCount = 2; + descriptor_pool.pPoolSizes = type_counts; + + VkResult U_ASSERT_ONLY err; + + err = vkCreateDescriptorPool(m_device, &descriptor_pool, NULL, + &m_desc_pool); + assert(!err); +} + +void Demo::prepare_descriptor_set() { + DEBUG_ENTRY; + + VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT]; + VkWriteDescriptorSet writes[2]={{},{}}; + VkResult U_ASSERT_ONLY err; + uint32_t i; + + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.pNext = NULL; + alloc_info.descriptorPool = m_desc_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &m_desc_layout; + + err = vkAllocateDescriptorSets(m_device, &alloc_info, &m_desc_set); + assert(!err); + + memset(&tex_descs, 0, sizeof(tex_descs)); + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + tex_descs[i].sampler = m_textures[i].sampler; + tex_descs[i].imageView = m_textures[i].view; + tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + memset(&writes, 0, sizeof(writes)); + + writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[0].dstSet = m_desc_set; + writes[0].descriptorCount = 1; + writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + writes[0].pBufferInfo = &m_uniform_data.buffer_info; + + writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[1].dstSet = m_desc_set; + writes[1].dstBinding = 1; + writes[1].descriptorCount = DEMO_TEXTURE_COUNT; + writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[1].pImageInfo = tex_descs; + + vkUpdateDescriptorSets(m_device, 2, writes, 0, NULL); +} + +void Demo::prepare_framebuffers() { + DEBUG_ENTRY; + + VkImageView attachments[2] = {{},{}}; + attachments[1] = m_depth.view; + + VkFramebufferCreateInfo fb_info = {}; + fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_info.pNext = NULL; + fb_info.renderPass = m_render_pass; + fb_info.attachmentCount = 2; + fb_info.pAttachments = attachments; + fb_info.width = (uint32_t) width(); //FIXME unsigned cast + fb_info.height = (uint32_t) height(); + fb_info.layers = 1; + + VkResult U_ASSERT_ONLY err; + uint32_t i; + + m_framebuffers = (VkFramebuffer *)malloc(m_swapchainImageCount * + sizeof(VkFramebuffer)); + assert(m_framebuffers); + + for (i = 0; i < m_swapchainImageCount; i++) { + attachments[0] = m_buffers[i].view; + err = vkCreateFramebuffer(m_device, &fb_info, NULL, + &m_framebuffers[i]); + assert(!err); + } +} + +void Demo::redraw() +{ + static uint32_t f = 0; + f++; + if(f == 100) { + qDebug()<<(float)f / (float)m_fpsTimer.elapsed() * 1000.0f; + f=0; + m_fpsTimer.restart(); + + } + + // Wait for work to finish before updating MVP. + vkDeviceWaitIdle(m_device); + update_data_buffer(); + draw(); +} + +void Demo::prepare() { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + + VkCommandPoolCreateInfo cmd_pool_info = {}; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = NULL; + cmd_pool_info.queueFamilyIndex = m_graphics_queue_node_index; + cmd_pool_info.flags = 0; + + err = vkCreateCommandPool(m_device, &cmd_pool_info, NULL, + &m_cmd_pool); + assert(!err); + + VkCommandBufferAllocateInfo cmd = {}; + cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd.pNext = NULL; + cmd.commandPool = m_cmd_pool; + cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd.commandBufferCount = 1; + + prepare_buffers(); + prepare_depth(); + prepare_textures(); + prepare_cube_data_buffer(); + + prepare_descriptor_layout(); + prepare_render_pass(); + prepare_pipeline(); + + for (uint32_t i = 0; i < m_swapchainImageCount; i++) { + err = + vkAllocateCommandBuffers(m_device, &cmd, &m_buffers[i].cmd); + assert(!err); + } + + prepare_descriptor_pool(); + prepare_descriptor_set(); + + prepare_framebuffers(); + + for (uint32_t i = 0; i < m_swapchainImageCount; i++) { + m_current_buffer = i; + draw_build_cmd(m_buffers[i].cmd); + } + + /* + * Prepare functions above may generate pipeline commands + * that need to be flushed before beginning the render loop. + */ + flush_init_cmd(); + + m_current_buffer = 0; + m_prepared = true; +} + +void Demo::cleanup() { + DEBUG_ENTRY; + + uint32_t i; + + m_prepared = false; + + for (i = 0; i < m_swapchainImageCount; i++) { + vkDestroyFramebuffer(m_device, m_framebuffers[i], NULL); + } + free(m_framebuffers); + vkDestroyDescriptorPool(m_device, m_desc_pool, NULL); + + vkDestroyPipeline(m_device, m_pipeline, NULL); + vkDestroyPipelineCache(m_device, m_pipelineCache, NULL); + vkDestroyRenderPass(m_device, m_render_pass, NULL); + vkDestroyPipelineLayout(m_device, m_pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(m_device, m_desc_layout, NULL); + + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + vkDestroyImageView(m_device, m_textures[i].view, NULL); + vkDestroyImage(m_device, m_textures[i].image, NULL); + vkFreeMemory(m_device, m_textures[i].mem, NULL); + vkDestroySampler(m_device, m_textures[i].sampler, NULL); + } + fpDestroySwapchainKHR(m_device, m_swapchain, NULL); + + vkDestroyImageView(m_device, m_depth.view, NULL); + vkDestroyImage(m_device, m_depth.image, NULL); + vkFreeMemory(m_device, m_depth.mem, NULL); + + vkDestroyBuffer(m_device, m_uniform_data.buf, NULL); + vkFreeMemory(m_device, m_uniform_data.mem, NULL); + + for (i = 0; i < m_swapchainImageCount; i++) { + vkDestroyImageView(m_device, m_buffers[i].view, NULL); + vkFreeCommandBuffers(m_device, m_cmd_pool, 1, + &m_buffers[i].cmd); + } + free(m_buffers); + + free(m_queue_props); + + vkDestroyCommandPool(m_device, m_cmd_pool, NULL); + vkDestroyDevice(m_device, NULL); + if (m_validate) { + DestroyDebugReportCallback(m_inst, msg_callback, NULL); + } + vkDestroySurfaceKHR(m_inst, m_surface, NULL); + vkDestroyInstance(m_inst, NULL); +} + +void Demo::resize_vk() { + DEBUG_ENTRY; + + uint32_t i; + + // Don't react to resize until after first initialization. + if (!m_prepared) { + return; + } + // In order to properly resize the window, we must re-create the swapchain + // AND redo the command buffers, etc. + // + // First, perform part of the demo_cleanup() function: + m_prepared = false; + + for (i = 0; i < m_swapchainImageCount; i++) { + vkDestroyFramebuffer(m_device, m_framebuffers[i], NULL); + } + free(m_framebuffers); + vkDestroyDescriptorPool(m_device, m_desc_pool, NULL); + + vkDestroyPipeline(m_device, m_pipeline, NULL); + vkDestroyPipelineCache(m_device, m_pipelineCache, NULL); + vkDestroyRenderPass(m_device, m_render_pass, NULL); + vkDestroyPipelineLayout(m_device, m_pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(m_device, m_desc_layout, NULL); + + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + vkDestroyImageView(m_device, m_textures[i].view, NULL); + vkDestroyImage(m_device, m_textures[i].image, NULL); + vkFreeMemory(m_device, m_textures[i].mem, NULL); + vkDestroySampler(m_device, m_textures[i].sampler, NULL); + } + + vkDestroyImageView(m_device, m_depth.view, NULL); + vkDestroyImage(m_device, m_depth.image, NULL); + vkFreeMemory(m_device, m_depth.mem, NULL); + + vkDestroyBuffer(m_device, m_uniform_data.buf, NULL); + vkFreeMemory(m_device, m_uniform_data.mem, NULL); + + for (i = 0; i < m_swapchainImageCount; i++) { + vkDestroyImageView(m_device, m_buffers[i].view, NULL); + vkFreeCommandBuffers(m_device, m_cmd_pool, 1, + &m_buffers[i].cmd); + } + vkDestroyCommandPool(m_device, m_cmd_pool, NULL); + free(m_buffers); + + // Second, re-perform the demo_prepare() function, which will re-create the + // swapchain: + prepare(); +} + +/* + * Return 1 (true) if all layer names specified in check_names + * can be found in given layer properties. + */ +VkBool32 Demo::check_layers(uint32_t check_count, const char **check_names, + uint32_t layer_count, + VkLayerProperties *layers) { + DEBUG_ENTRY; + + for (uint32_t i = 0; i < check_count; i++) { + VkBool32 found = 0; + for (uint32_t j = 0; j < layer_count; j++) { + if (!strcmp(check_names[i], layers[j].layerName)) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "Cannot find layer: %s\n", check_names[i]); + return 0; + } + } + return 1; +} + +void Demo::resizeEvent(QResizeEvent *e) +{ + qDebug()<accept(); //FIXME? + QWindow::resizeEvent(e); +} + +void Demo::init_vk() { + DEBUG_ENTRY; + + VkResult err; + uint32_t instance_extension_count = 0; + uint32_t instance_layer_count = 0; + uint32_t device_validation_layer_count = 0; + const char **instance_validation_layers = NULL; + m_enabled_extension_count = 0; + m_enabled_layer_count = 0; + + const char *instance_validation_layers_alt1[] = { + "VK_LAYER_LUNARG_standard_validation" + }; + + const char *instance_validation_layers_alt2[] = { + "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", + "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_object_tracker", + "VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation", + "VK_LAYER_LUNARG_swapchain", "VK_LAYER_GOOGLE_unique_objects" + }; + + /* Look for validation layers */ + VkBool32 validation_found = 0; + if (m_validate) { + qDebug()<<"enabling validation"; + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); + assert(!err); + + instance_validation_layers = instance_validation_layers_alt1; + if (instance_layer_count > 0) { + VkLayerProperties *instance_layers = + (VkLayerProperties*) malloc(sizeof (VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, + instance_layers); + assert(!err); + + + validation_found = check_layers( + ARRAY_SIZE(instance_validation_layers_alt1), + instance_validation_layers, instance_layer_count, + instance_layers); + if (validation_found) { + m_enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1); + m_device_validation_layers[0] = "VK_LAYER_LUNARG_standard_validation"; + device_validation_layer_count = 1; + } else { + // use alternative set of validation layers + instance_validation_layers = instance_validation_layers_alt2; + m_enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2); + validation_found = check_layers( + ARRAY_SIZE(instance_validation_layers_alt2), + instance_validation_layers, instance_layer_count, + instance_layers); + device_validation_layer_count = + ARRAY_SIZE(instance_validation_layers_alt2); + for (uint32_t i = 0; i < device_validation_layer_count; i++) { + m_device_validation_layers[i] = + instance_validation_layers[i]; + } + } + free(instance_layers); + } + + if (!validation_found) { + ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find " + "required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + } + + /* Look for instance extensions */ + VkBool32 surfaceExtFound = 0; + VkBool32 platformSurfaceExtFound = 0; + memset(m_extension_names, 0, sizeof(m_extension_names)); + + err = vkEnumerateInstanceExtensionProperties( + NULL, &instance_extension_count, NULL); + assert(!err); + + if (instance_extension_count > 0) { + VkExtensionProperties *instance_extensions = + (VkExtensionProperties*) malloc(sizeof(VkExtensionProperties) * instance_extension_count); + err = vkEnumerateInstanceExtensionProperties( + NULL, &instance_extension_count, instance_extensions); + assert(!err); + for (uint32_t i = 0; i < instance_extension_count; i++) { + if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, + instance_extensions[i].extensionName)) { + surfaceExtFound = 1; + m_extension_names[m_enabled_extension_count++] = + VK_KHR_SURFACE_EXTENSION_NAME; + } + if (!strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, + instance_extensions[i].extensionName)) { + platformSurfaceExtFound = 1; + m_extension_names[m_enabled_extension_count++] = + VK_KHR_XCB_SURFACE_EXTENSION_NAME; + } + if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, + instance_extensions[i].extensionName)) { + if (m_validate) { + m_extension_names[m_enabled_extension_count++] = + VK_EXT_DEBUG_REPORT_EXTENSION_NAME; + } + } + assert(m_enabled_extension_count < 64); + } + + free(instance_extensions); + } + + if (!surfaceExtFound) { + ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " + "the " VK_KHR_SURFACE_EXTENSION_NAME + " extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + if (!platformSurfaceExtFound) { + ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find " + "the " VK_KHR_XCB_SURFACE_EXTENSION_NAME + " extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + VkApplicationInfo app = {}; + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pNext = NULL; + app.pApplicationName = "cube"; + app.applicationVersion = 0; + app.pEngineName = "cubenegine"; + app.engineVersion = 0; + app.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo inst_info = {}; + inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + inst_info.pNext = NULL; + inst_info.pApplicationInfo = &app; + inst_info.enabledLayerCount = m_enabled_layer_count; + inst_info.ppEnabledLayerNames = (const char *const *)instance_validation_layers; + inst_info.enabledExtensionCount = m_enabled_extension_count; + inst_info.ppEnabledExtensionNames = (const char *const *)m_extension_names; + + /* + * This is info for a temp callback to use during CreateInstance. + * After the instance is created, we use the instance-based + * function to register the final callback. + */ + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {}; + if (m_validate) { + dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + dbgCreateInfo.pNext = NULL; + dbgCreateInfo.pfnCallback = m_use_break ? BreakCallback : dbgFunc; + dbgCreateInfo.pUserData = NULL; + dbgCreateInfo.flags = + VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + // | VK_DEBUG_REPORT_INFORMATION_BIT_EXT; + inst_info.pNext = &dbgCreateInfo; + } + + uint32_t gpu_count = 0; + + err = vkCreateInstance(&inst_info, NULL, &m_inst); + if (err == VK_ERROR_INCOMPATIBLE_DRIVER) { + ERR_EXIT("Cannot find a compatible Vulkan installable client driver " + "(ICD).\n\nPlease look at the Getting Started guide for " + "additional information.\n", + "vkCreateInstance Failure"); + } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) { + ERR_EXIT("Cannot find a specified extension library" + ".\nMake sure your layers path is set appropriately.\n", + "vkCreateInstance Failure"); + } else if (err) { + ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan " + "installable client driver (ICD) installed?\nPlease look at " + "the Getting Started guide for additional information.\n", + "vkCreateInstance Failure"); + } + + /* Make initial call to query gpu_count, then second call for gpu info*/ + err = vkEnumeratePhysicalDevices(m_inst, &gpu_count, NULL); + assert(!err && gpu_count > 0); + + if (gpu_count > 0) { + VkPhysicalDevice *physical_devices = (VkPhysicalDevice*)malloc(sizeof(VkPhysicalDevice) * gpu_count); + err = vkEnumeratePhysicalDevices(m_inst, &gpu_count, physical_devices); + assert(!err); + /* For cube demo we just grab the first physical device */ + m_gpu = physical_devices[0]; + free(physical_devices); + } else { + ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices.\n\n" + "Do you have a compatible Vulkan installable client driver (ICD) " + "installed?\nPlease look at the Getting Started guide for " + "additional information.\n", + "vkEnumeratePhysicalDevices Failure"); + } + + /* Look for validation layers */ + validation_found = 0; + m_enabled_layer_count = 0; + uint32_t device_layer_count = 0; + err = + vkEnumerateDeviceLayerProperties(m_gpu, &device_layer_count, NULL); + assert(!err); + + if (device_layer_count > 0) { + VkLayerProperties *device_layers = + (VkLayerProperties*)malloc(sizeof(VkLayerProperties) * device_layer_count); + err = vkEnumerateDeviceLayerProperties(m_gpu, &device_layer_count, + device_layers); + assert(!err); + + if (m_validate) { + validation_found = check_layers(device_validation_layer_count, + m_device_validation_layers, + device_layer_count, + device_layers); + m_enabled_layer_count = device_validation_layer_count; + } + + free(device_layers); + } + + if (m_validate && !validation_found) { + ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find " + "a required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\n", + "vkCreateDevice Failure"); + } + + /* Look for device extensions */ + uint32_t device_extension_count = 0; + VkBool32 swapchainExtFound = 0; + m_enabled_extension_count = 0; + memset(m_extension_names, 0, sizeof(m_extension_names)); + + err = vkEnumerateDeviceExtensionProperties(m_gpu, NULL, + &device_extension_count, NULL); + assert(!err); + + if (device_extension_count > 0) { + VkExtensionProperties *device_extensions = + (VkExtensionProperties*)malloc(sizeof(VkExtensionProperties) * device_extension_count); + err = vkEnumerateDeviceExtensionProperties( + m_gpu, NULL, &device_extension_count, device_extensions); + assert(!err); + + for (uint32_t i = 0; i < device_extension_count; i++) { + if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, + device_extensions[i].extensionName)) { + swapchainExtFound = 1; + m_extension_names[m_enabled_extension_count++] = + VK_KHR_SWAPCHAIN_EXTENSION_NAME; + } + assert(m_enabled_extension_count < 64); + } + + free(device_extensions); + } + + if (!swapchainExtFound) { + ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find " + "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME + " extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + + if (m_validate) { + CreateDebugReportCallback = + (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( + m_inst, "vkCreateDebugReportCallbackEXT"); + DestroyDebugReportCallback = + (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr( + m_inst, "vkDestroyDebugReportCallbackEXT"); + if (!CreateDebugReportCallback) { + ERR_EXIT( + "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n", + "vkGetProcAddr Failure"); + } + if (!DestroyDebugReportCallback) { + ERR_EXIT( + "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n", + "vkGetProcAddr Failure"); + } + DebugReportMessage = + (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr( + m_inst, "vkDebugReportMessageEXT"); + if (!DebugReportMessage) { + ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n", + "vkGetProcAddr Failure"); + } + + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo; + PFN_vkDebugReportCallbackEXT callback; + callback = m_use_break ? BreakCallback : dbgFunc; + dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + dbgCreateInfo.pNext = NULL; + dbgCreateInfo.pfnCallback = callback; + dbgCreateInfo.pUserData = NULL; + dbgCreateInfo.flags = + VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + //| VK_DEBUG_REPORT_INFORMATION_BIT_EXT; + err = CreateDebugReportCallback(m_inst, &dbgCreateInfo, NULL, + &msg_callback); + switch (err) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_EXIT("CreateDebugReportCallback: out of host memory\n", + "CreateDebugReportCallback Failure"); + break; + default: + ERR_EXIT("CreateDebugReportCallback: unknown failure\n", + "CreateDebugReportCallback Failure"); + break; + } + } + vkGetPhysicalDeviceProperties(m_gpu, &m_gpu_props); + + /* Call with NULL data to get count */ + vkGetPhysicalDeviceQueueFamilyProperties(m_gpu, &m_queue_count, + NULL); + assert(m_queue_count >= 1); + + m_queue_props = (VkQueueFamilyProperties *)malloc( + m_queue_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(m_gpu, &m_queue_count, + m_queue_props); + // Find a queue that supports gfx + uint32_t gfx_queue_idx = 0; + for (gfx_queue_idx = 0; gfx_queue_idx < m_queue_count; + gfx_queue_idx++) { + if (m_queue_props[gfx_queue_idx].queueFlags & VK_QUEUE_GRAPHICS_BIT) + break; + } + assert(gfx_queue_idx < m_queue_count); + // Query fine-grained feature support for this device. + // If app has specific feature requirements it should check supported + // features based on this query + VkPhysicalDeviceFeatures physDevFeatures; + vkGetPhysicalDeviceFeatures(m_gpu, &physDevFeatures); + + GET_INSTANCE_PROC_ADDR(m_inst, GetPhysicalDeviceSurfaceSupportKHR); + GET_INSTANCE_PROC_ADDR(m_inst, GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_INSTANCE_PROC_ADDR(m_inst, GetPhysicalDeviceSurfaceFormatsKHR); + GET_INSTANCE_PROC_ADDR(m_inst, GetPhysicalDeviceSurfacePresentModesKHR); + GET_INSTANCE_PROC_ADDR(m_inst, GetSwapchainImagesKHR); +} + +void Demo::create_device() { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + float queue_priorities[1] = {0.0}; + VkDeviceQueueCreateInfo queue = {}; + queue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue.pNext = NULL; + queue.queueFamilyIndex = m_graphics_queue_node_index; + queue.queueCount = 1; + queue.pQueuePriorities = queue_priorities; + + VkDeviceCreateInfo device_ci = {}; + device_ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_ci.pNext = NULL; + device_ci.queueCreateInfoCount = 1; + device_ci.pQueueCreateInfos = &queue; + device_ci.enabledLayerCount = m_enabled_layer_count; + device_ci.ppEnabledLayerNames = + (const char *const *)((m_validate) + ? m_device_validation_layers + : NULL); + device_ci.enabledExtensionCount = m_enabled_extension_count; + device_ci.ppEnabledExtensionNames = (const char *const *)m_extension_names; + device_ci.pEnabledFeatures = + NULL; // If specific features are required, pass them in here + + err = vkCreateDevice(m_gpu, &device_ci, NULL, &m_device); + assert(!err); +} + +void Demo::init_vk_swapchain() { + DEBUG_ENTRY; + + VkResult U_ASSERT_ONLY err; + uint32_t i; + +// Create a WSI surface for the window: +#ifdef _WIN32 + VkWin32SurfaceCreateInfoKHR createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.hinstance = connection; + createInfo.hwnd = window; + + err = + vkCreateWin32SurfaceKHR(inst, &createInfo, NULL, &surface); +#else + + QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); + Q_ASSERT(native); + xcb_connection_t* connection = static_cast(native->nativeResourceForWindow("connection", this)); + Q_ASSERT(connection); + xcb_window_t xcb_window = static_cast(winId()); + Q_ASSERT(xcb_window); + + qDebug()<<"connection and winid:"<= 1); + m_format = surfFormats[0].format; + } + m_color_space = surfFormats[0].colorSpace; + + m_quit = false; + m_curFrame = 0; + + // Get Memory information and properties + vkGetPhysicalDeviceMemoryProperties(m_gpu, &m_memory_properties); +} + diff --git a/src/qml/window.h b/src/qml/window.h new file mode 100644 index 0000000..429050c --- /dev/null +++ b/src/qml/window.h @@ -0,0 +1,202 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include +#include +#include +#include + +#define DEMO_TEXTURE_COUNT 1 + +typedef struct _SwapchainBuffers { + VkImage image; + VkCommandBuffer cmd; + VkImageView view; +} SwapchainBuffers; + +/* + * structure to track all objects related to a texture. + */ +struct texture_object { + VkSampler sampler; + + VkImage image; + VkImageLayout imageLayout; + + VkMemoryAllocateInfo mem_alloc; + VkDeviceMemory mem; + VkImageView view; + uint32_t tex_width, tex_height; +}; + +class Demo : public QWindow { +public: + Demo(); + ~Demo(); + void init_vk(); + void init_vk_swapchain(); + void create_device(); + VkBool32 check_layers(uint32_t check_count, const char **check_names, + uint32_t layer_count, + VkLayerProperties *layers); + + void resizeEvent(QResizeEvent *) override; // QWindow::resizeEvent + void resize_vk(); + void prepare_pipeline(); + void prepare(); + void draw(); + void draw_build_cmd(VkCommandBuffer cmd_buf); + void prepare_texture_image(const char *filename, texture_object *tex_obj, VkImageTiling tiling, VkImageUsageFlags usage, VkFlags required_props); + void prepare_textures(); + void prepare_depth(); + void destroy_texture_image(texture_object *tex_objs); + void prepare_cube_data_buffer(); + void prepare_descriptor_layout(); + void prepare_render_pass(); + void flush_init_cmd(); + void set_image_layout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout, VkAccessFlagBits srcAccessMask); + void update_data_buffer(); + void prepare_buffers(); + void cleanup(); + bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); + void prepare_descriptor_pool(); + void prepare_descriptor_set(); + void prepare_framebuffers(); + + VkShaderModule prepare_shader_module(const char *code, size_t size); + VkShaderModule prepare_vs(); + VkShaderModule prepare_fs(); + +public slots: + void redraw(); + +private: + VkSurfaceKHR m_surface; + bool m_prepared; + bool m_use_staging_buffer; + + VkInstance m_inst; + VkPhysicalDevice m_gpu; + VkDevice m_device; + VkQueue m_queue; + uint32_t m_graphics_queue_node_index; + VkPhysicalDeviceProperties m_gpu_props; + VkQueueFamilyProperties *m_queue_props; + VkPhysicalDeviceMemoryProperties m_memory_properties; + + uint32_t m_enabled_extension_count; + uint32_t m_enabled_layer_count; + const char *m_extension_names[64]; + const char *m_device_validation_layers[64]; + + VkFormat m_format; + VkColorSpaceKHR m_color_space; + + uint32_t m_swapchainImageCount; + VkSwapchainKHR m_swapchain; + SwapchainBuffers *m_buffers; + + VkCommandPool m_cmd_pool; + + struct { + VkFormat format; + VkImage image; + VkMemoryAllocateInfo mem_alloc; + VkDeviceMemory mem; + VkImageView view; + } m_depth; + + struct texture_object m_textures[DEMO_TEXTURE_COUNT]; + + struct { + VkBuffer buf; + VkMemoryAllocateInfo mem_alloc; + VkDeviceMemory mem; + VkDescriptorBufferInfo buffer_info; + } m_uniform_data; + + VkCommandBuffer m_cmd; // Buffer for initialization commands + VkPipelineLayout m_pipeline_layout; + VkDescriptorSetLayout m_desc_layout; + VkPipelineCache m_pipelineCache; + VkRenderPass m_render_pass; + VkPipeline m_pipeline; + + QMatrix4x4 m_projection_matrix; + QMatrix4x4 m_view_matrix; + QMatrix4x4 m_model_matrix; + + float m_spin_angle; + float m_spin_increment; + bool m_pause; + + VkShaderModule m_vert_shader_module; + VkShaderModule m_frag_shader_module; + + VkDescriptorPool m_desc_pool; + VkDescriptorSet m_desc_set; + + VkFramebuffer *m_framebuffers; + + bool m_quit; + int32_t m_curFrame; + int32_t m_frameCount; + bool m_validate; + bool m_use_break; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; + VkDebugReportCallbackEXT msg_callback; + PFN_vkDebugReportMessageEXT DebugReportMessage; + + uint32_t m_current_buffer; + uint32_t m_queue_count; + + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; + PFN_vkQueuePresentKHR fpQueuePresentKHR; + + uint32_t frameCount, curFrame; + QElapsedTimer m_fpsTimer; +}; + +#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ + { \ + fp##entrypoint = \ + (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \ + if (fp##entrypoint == NULL) { \ + ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \ + "vkGetInstanceProcAddr Failure"); \ + } \ + } + +#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ + { \ + if (!g_gdpa) \ + g_gdpa = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr( \ + m_inst, "vkGetDeviceProcAddr"); \ + fp##entrypoint = \ + (PFN_vk##entrypoint)g_gdpa(dev, "vk" #entrypoint); \ + if (fp##entrypoint == NULL) { \ + ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \ + "vkGetDeviceProcAddr Failure"); \ + } \ + } + + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#if defined(NDEBUG) && defined(__GNUC__) +#define U_ASSERT_ONLY __attribute__((unused)) +#else +#define U_ASSERT_ONLY +#endif + +#define ERR_EXIT(err_msg, err_class) qFatal(err_msg) + +#endif From f22b8c1a6d2ab08ad264644997ef1fa34f39059d Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Mon, 2 May 2016 21:07:14 -0400 Subject: [PATCH 11/14] since the qmake projects files are so broken now, I removed them and modified the README --- README.md | 13 ++----------- qtvulkan.pro | 2 -- src/cube/cube.pro | 12 ------------ 3 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 qtvulkan.pro delete mode 100644 src/cube/cube.pro diff --git a/README.md b/README.md index 5372b01..7b53270 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,6 @@ compiles, runs, shows rotating cube! -requires setting VK_LAYER_PATH and LD_LIBRARY_PATH, e.g. - -* LD_LIBRARY_PATH=$HOME/Vulkan-LoaderAndValidationLayers/loader -* VK_LAYER_PATH=$HOME/Vulkan-LoaderAndValidationLayers/dbuild/layers - -or where that lies on your system. - -Also make sure the include and library paths in cube.pro and lib.pro are correct. - -The plan is to port the rotating cube demo, as well as have a Qt Widget that contains a lot of the boilerplate for vulkan setup. - # building with CMake If you would like to build the project using CMake instead of qmake, follow the below instructions @@ -22,8 +11,10 @@ If you would like to build the project using CMake instead of qmake, follow the $ cd build $ ./build.sh $ cd src/cube/ + // run the C++ cube demo $ ./cube ``` ## known issues: * resizing is stuck after one resize event +* qml builds but does not displays a black screen - will add instructions once working diff --git a/qtvulkan.pro b/qtvulkan.pro deleted file mode 100644 index e823065..0000000 --- a/qtvulkan.pro +++ /dev/null @@ -1,2 +0,0 @@ -TEMPLATE= subdirs -SUBDIRS = lib cube diff --git a/src/cube/cube.pro b/src/cube/cube.pro deleted file mode 100644 index 522ce0f..0000000 --- a/src/cube/cube.pro +++ /dev/null @@ -1,12 +0,0 @@ -CONFIG += c++11 -QT += widgets gui gui-private -# FIXME paths... -INCLUDEPATH += $$PWD/../../Vulkan-LoaderAndValidationLayers/include -LIBS += -lvulkan -L$$PWD/../../Vulkan-LoaderAndValidationLayers/dbuild/loader -lxcb - -SOURCES += \ - cube.cpp - -HEADERS += \ - cube.h - From 2d692eb9f42d32b697c7afaf35f055cf721bf325 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Wed, 4 May 2016 22:34:53 -0400 Subject: [PATCH 12/14] the qml can now works --- src/qml/CMakeLists.txt | 5 ++- src/qml/cube-frag.spv | Bin 0 -> 656 bytes src/qml/cube-vert.spv | Bin 0 -> 1584 bytes src/qml/{window.cpp => cube.cpp} | 62 ++++++++++++++----------------- src/qml/{window.h => cube.h} | 32 ++++++++++++---- src/qml/main.cpp | 4 +- src/qml/main.qml | 34 +++++++++++++++-- 7 files changed, 88 insertions(+), 49 deletions(-) create mode 100644 src/qml/cube-frag.spv create mode 100644 src/qml/cube-vert.spv rename src/qml/{window.cpp => cube.cpp} (98%) rename src/qml/{window.h => cube.h} (92%) diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index f6a8cbb..efe800c 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -1,5 +1,7 @@ SET(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_AUTOMOC ON) + SET(CMAKE_CXX_LINK_FLAGS "-lvulkan") SET(CMAKE_CXX_LINK_FLAGS "-lassimp") SET(CMAKE_CXX_LINK_FLAGS "-lxcb") @@ -21,7 +23,8 @@ FIND_PACKAGE(Qt5Quick REQUIRED) FIND_PACKAGE(Qt5Qml REQUIRED) SET(qml_SRCS - window.cpp + #window.cpp + cube.cpp main.cpp ) diff --git a/src/qml/cube-frag.spv b/src/qml/cube-frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..15db12c0b9ca57055631fa2cd18eafffaf08699b GIT binary patch literal 656 zcmY+AUrPc}5XC3gRm;-M{t!WEy^DlJ5Cs+FixAX9kISZuS?0>_3Vn<|ZZARScT=*F zaqcOOV9BdpckIHntkd~UW#rnxPG_^(r6kcVHO4HOE`#vWo za25`upmWlmhW$75)oFRIFNWE(yCfXm#^X3KF`jLR%c2jReYcm+{WwkrqOI$;F*^h( z!03-0`om3O{Bn=Cg}yFV_-ZJ_vi7Bn#$>Ps=H34w&y&C;&5ifyyqCcLR{KI(t# zi;3TFIuqAv&d&M1bLQM;S}NAh8B@^jym?_}YrzzSjhQon-P@g`PGgew8qJ+8JmyWw zI^uIh=U8RQmv}HlzaUwXtVv4x6}A7Ab6J*lCu$wOjV8%BPU9?zCLiNol18JGPf0hM z{O=>vVAzjN<6(aq_mil(y)}-zpG`%P^7$%a$eAT)-O(uRNflF4ZN{8TKd6hc)#2YC z9|#8)Q~!808DxWztvk>u&QNchWvMosYeD;zKo$qKxBh8#oTSS5ZrDrC@C(dM*~K3u z>CA*bx!_sYd^+l5KCHH#j4{<2wD_|0KyuaD=W}>J*H1Yt!LN((g*vgqba|;K0n*bmyP6k% z%$|kMZ!x^f*J9~+SwgQZoutoI3A1B9*zlv*8xm}96ovZ*w-kwf?npR8pZL>TD1rBR z)}`n3)P*@i@7R1E-T}5>icQ{ol28IqKHl4X2{R@i^?>oC2j=xi;_b|m*vw_a*}giw z={SGxv2gwzvElIZK8>6Y_uykcm5$B(JAp7dQX3q;fAo4@O8x@Rx?%AE literal 0 HcmV?d00001 diff --git a/src/qml/window.cpp b/src/qml/cube.cpp similarity index 98% rename from src/qml/window.cpp rename to src/qml/cube.cpp index 3ae921e..5a2cd9e 100644 --- a/src/qml/window.cpp +++ b/src/qml/cube.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include @@ -209,27 +209,6 @@ BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, static void demo_create_xcb_window(struct Demo *demo); static void demo_run_xcb(struct Demo *demo); -/* -int main(int argc, char **argv) { - setvbuf(stdout, NULL, _IONBF, 0); - DEBUG_ENTRY; - QGuiApplication app(argc, argv); - Demo demo; - demo.show(); - - demo.resize(500,500); - demo.init_vk(); - demo.init_vk_swapchain(); - demo.prepare(); - QTimer t; - t.setInterval(16); - QObject::connect(&t, &QTimer::timeout, &demo, &Demo::redraw ); - t.start(); - app.exec(); - - return validation_error; -} -*/ Demo::Demo() : m_surface(0), @@ -497,8 +476,8 @@ void Demo::draw_build_cmd(VkCommandBuffer cmd_buf) { VkRect2D scissor {}; memset(&scissor, 0, sizeof(scissor)); - scissor.extent.width = (uint32_t)qMax(0, width()); - scissor.extent.height = (uint32_t)qMax(0, height()); + scissor.extent.width = (uint32_t)width(); + scissor.extent.height = (uint32_t)height(); scissor.offset.x = 0; scissor.offset.y = 0; vkCmdSetScissor(cmd_buf, 0, 1, &scissor); @@ -677,7 +656,8 @@ void Demo::prepare_buffers() { } else { // If the surface size is defined, the swap chain size must match swapchainExtent = surfCapabilities.currentExtent; - resize(surfCapabilities.currentExtent.width, surfCapabilities.currentExtent.height); // FIXME why + //resize(surfCapabilities.currentExtent.width, surfCapabilities.currentExtent.height); // FIXME why + resize_window(surfCapabilities.currentExtent.width, surfCapabilities.currentExtent.height); // FIXME why } // If mailbox mode is available, use it, as is the lowest-latency non- @@ -815,8 +795,8 @@ void Demo::prepare_depth() { image.pNext = NULL; image.imageType = VK_IMAGE_TYPE_2D; image.format = depth_format; - image.extent.width = width(); - image.extent.height = height(); + image.extent.width = window()->width(); + image.extent.height = window()->height(); image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; @@ -825,6 +805,8 @@ void Demo::prepare_depth() { image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; image.flags = 0; + //std::cout << "prepare_depth() width:" << window()->width() << " height:" << window()->height() << std::endl; + VkImageViewCreateInfo view = {}; view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view.pNext = NULL; @@ -1795,14 +1777,25 @@ VkBool32 Demo::check_layers(uint32_t check_count, const char **check_names, return 1; } -void Demo::resizeEvent(QResizeEvent *e) +//void Demo::resizeEvent(QResizeEvent *e) +void Demo::updateWindow() { - qDebug()<accept(); //FIXME? - QWindow::resizeEvent(e); + //e->accept(); //FIXME? + QQuickItem::update(); + //QWindow::resizeEvent(e); +} + +void Demo::resize_window(int width, int height) { + setHeight(height); + setWidth(width); +} + +void Demo::show_window() { + window()->show(); } void Demo::init_vk() { @@ -2218,9 +2211,9 @@ void Demo::init_vk_swapchain() { QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); Q_ASSERT(native); - xcb_connection_t* connection = static_cast(native->nativeResourceForWindow("connection", this)); + xcb_connection_t* connection = static_cast(native->nativeResourceForWindow("connection", this->window())); Q_ASSERT(connection); - xcb_window_t xcb_window = static_cast(winId()); + xcb_window_t xcb_window = static_cast(this->window()->winId()); Q_ASSERT(xcb_window); qDebug()<<"connection and winid:"< +//#include +#include +#include #include #include +#include #include +#include #define DEMO_TEXTURE_COUNT 1 @@ -29,21 +34,27 @@ struct texture_object { uint32_t tex_width, tex_height; }; -class Demo : public QWindow { +class Demo : public QQuickItem{ + +Q_OBJECT + public: Demo(); ~Demo(); - void init_vk(); - void init_vk_swapchain(); + Q_INVOKABLE void init_vk(); + Q_INVOKABLE void init_vk_swapchain(); void create_device(); VkBool32 check_layers(uint32_t check_count, const char **check_names, uint32_t layer_count, VkLayerProperties *layers); - void resizeEvent(QResizeEvent *) override; // QWindow::resizeEvent + //void resizeEvent(QResizeEvent *) override; // QWindow::resizeEvent + void updateWindow(); + Q_INVOKABLE void resize_window(int width, int height); + Q_INVOKABLE void show_window(); void resize_vk(); void prepare_pipeline(); - void prepare(); + Q_INVOKABLE void prepare(); void draw(); void draw_build_cmd(VkCommandBuffer cmd_buf); void prepare_texture_image(const char *filename, texture_object *tex_obj, VkImageTiling tiling, VkImageUsageFlags usage, VkFlags required_props); @@ -163,6 +174,7 @@ public slots: uint32_t frameCount, curFrame; QElapsedTimer m_fpsTimer; + QTimer m_timer; }; #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ @@ -199,4 +211,8 @@ public slots: #define ERR_EXIT(err_msg, err_class) qFatal(err_msg) -#endif + + + +#endif // CUBE_H + diff --git a/src/qml/main.cpp b/src/qml/main.cpp index 9fcbd56..c00f52e 100644 --- a/src/qml/main.cpp +++ b/src/qml/main.cpp @@ -1,9 +1,11 @@ #include #include +#include #include #include #include -#include "window.h" +#include +#include "cube.h" int main(int argc, char **argv) { diff --git a/src/qml/main.qml b/src/qml/main.qml index eeece25..19d6f9f 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -1,9 +1,35 @@ import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 import qtvulkan 1.0 -QtVulkan { - id: vulkan - width: 500 - height: 400 +ApplicationWindow { + id: window + width: 1200 + height: 700 visible: true + title: "Vulkan Cube" + + QtVulkan { + id: vulkan + anchors.fill: parent + visible: true + } + + Timer { + id: timer + interval: 16 + running: true + repeat: true + onTriggered: vulkan.redraw() + } + + Component.onCompleted: { + vulkan.show_window() + vulkan.resize_window(window.width,window.height) + vulkan.init_vk() + vulkan.init_vk_swapchain() + vulkan.prepare() + } } From d01087e5b35d856817950d64c55095e136d175b0 Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Wed, 4 May 2016 22:38:11 -0400 Subject: [PATCH 13/14] updated README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b53270..1e54cc5 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,11 @@ If you would like to build the project using CMake instead of qmake, follow the $ cd src/cube/ // run the C++ cube demo $ ./cube + // run the Qml cube demo + $ cd src/qml/ + $ ./qml ``` ## known issues: * resizing is stuck after one resize event -* qml builds but does not displays a black screen - will add instructions once working +* In qml I have to resize the window to display the contents From 76169cb4ba3f7014037c5491b5333438ff4bbbbe Mon Sep 17 00:00:00 2001 From: Richard Layman Date: Wed, 4 May 2016 22:42:12 -0400 Subject: [PATCH 14/14] fixed build script --- build/build.sh | 3 +++ src/qml/cube-frag.spv | Bin 656 -> 0 bytes src/qml/cube-vert.spv | Bin 1584 -> 0 bytes 3 files changed, 3 insertions(+) delete mode 100644 src/qml/cube-frag.spv delete mode 100644 src/qml/cube-vert.spv diff --git a/build/build.sh b/build/build.sh index 89d000b..db2030a 100755 --- a/build/build.sh +++ b/build/build.sh @@ -10,3 +10,6 @@ make # post work for assets cp ../src/cube/*.spv src/cube cp ../src/cube/lunarg.ppm src/cube +cp ../src/cube/*.spv src/qml +cp ../src/cube/lunarg.ppm src/qml +cp ../src/qml/main.qml src/qml diff --git a/src/qml/cube-frag.spv b/src/qml/cube-frag.spv deleted file mode 100644 index 15db12c0b9ca57055631fa2cd18eafffaf08699b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 zcmY+AUrPc}5XC3gRm;-M{t!WEy^DlJ5Cs+FixAX9kISZuS?0>_3Vn<|ZZARScT=*F zaqcOOV9BdpckIHntkd~UW#rnxPG_^(r6kcVHO4HOE`#vWo za25`upmWlmhW$75)oFRIFNWE(yCfXm#^X3KF`jLR%c2jReYcm+{WwkrqOI$;F*^h( z!03-0`om3O{Bn=Cg}yFV_-ZJ_vi7Bn#$>Ps=H34w&y&C;&5ifyyqCcLR{KI(t# zi;3TFIuqAv&d&M1bLQM;S}NAh8B@^jym?_}YrzzSjhQon-P@g`PGgew8qJ+8JmyWw zI^uIh=U8RQmv}HlzaUwXtVv4x6}A7Ab6J*lCu$wOjV8%BPU9?zCLiNol18JGPf0hM z{O=>vVAzjN<6(aq_mil(y)}-zpG`%P^7$%a$eAT)-O(uRNflF4ZN{8TKd6hc)#2YC z9|#8)Q~!808DxWztvk>u&QNchWvMosYeD;zKo$qKxBh8#oTSS5ZrDrC@C(dM*~K3u z>CA*bx!_sYd^+l5KCHH#j4{<2wD_|0KyuaD=W}>J*H1Yt!LN((g*vgqba|;K0n*bmyP6k% z%$|kMZ!x^f*J9~+SwgQZoutoI3A1B9*zlv*8xm}96ovZ*w-kwf?npR8pZL>TD1rBR z)}`n3)P*@i@7R1E-T}5>icQ{ol28IqKHl4X2{R@i^?>oC2j=xi;_b|m*vw_a*}giw z={SGxv2gwzvElIZK8>6Y_uykcm5$B(JAp7dQX3q;fAo4@O8x@Rx?%AE