Correct way to use compiled spirv shaders in vulkan

2

TL;DR:

Compiled spirv shaders crash where inline runtime-compiled spirvs work, tested with modified sample. Crash happens deep in vulkan code. What gives?

Details:

I am having trouble using compiled spirv shaders. I took the "draw-textured-cube" sample, extracted it to a standalone environment with paired-down cmake and a minimal edit to util.cpp to adjust the path to the data dir. It compiled and worked as expected.

I then extracted the shader codes from the static const cstrings at the beginning of the file and moved them to their own files. I replaced the call to init_shaders with one that just reads the file and builds the shader modules with the same options as init_shaders. I compiled the glsl into spirv with options taken from an unused function in the original sample CMakeLists.txt.

It now crashes with an access violation on the creation of the pipeline (full error text below). Same result in both Debug and Release builds, with or without the validation layer. Stepping through the assembly in visual studio reveals the error is happening at least 16 layers deep into the create pipeline call (where a call or non-local jmp constitutes a layer, as these are most likely function barriers).

Unhandled exception at 0x000000006894A525 (nvoglv64.dll) in main.exe: 0xC0000005: Access violation writing location 0x00000080A3CAA530. occurred

(With the exact addresses varying as one would expect.)

Any of the samples included with the SDK use inline glsl compiled at runtime into spirv, but all have a comment similar to this one (from the textured cube sample):

/* For this sample, we'll start with GLSL so the shader function is plain */
/* and then use the glslang GLSLtoSPV utility to convert it to SPIR-V for */
/* the driver.  We do this for clarity rather than using pre-compiled     */
/* SPIR-V                                                                 */

This would seem to indicate that the "correct" way to do shaders is to use precompiled, and yet we are not given a working sample/example on how to accomplish this. I have looked other places but cannot find a reasonably simple (<10,000 lines) example of how to compile read and display spv shaders.

My best guesses at what the problem is are: 1) glslValidator is compiling them wrong; 2) I'm reading them wrong; 3) using them requires different options (say, to the shader module creation info) then runtime-compiled shaders. In any case, I'm already starting to wish that vulkan had better error trapping.

Obviously using inline glsl everywhere would be a workaround, and if that's what it takes I can just make a bunch of .cpp files with just one big cstring per shader, at the cost of runtime initial load, but there really should be a better solution.

Host system is windows 7 (x64) with excessive resources compared to what should be required by the program. I'm targetting windows linux and (if possible) mac, so any solutions that rule out android, ios, consoles etc. are fine with me.

Main.cpp:

/*
* Vulkan Samples
*
* 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.
*/

/*
VULKAN_SAMPLE_SHORT_DESCRIPTION
Draw Textured Cube
*/

/* This is part of the draw cube progression */

#include <util_init.hpp>
#include <assert.h>
#include <string.h>
#include <cstdlib>
#include "cube_data.h"

FILE* logfile;
#define FAIL {line = __LINE__; goto fail;}

unsigned int * loadSprv(const char* filename, size_t* codelen) {
    struct stat fstat;
    size_t len, offset = 0, read;
    if (stat(filename, &fstat))
        return NULL;
    *codelen = len = fstat.st_size;
    union {
        unsigned int * ret;
        unsigned char * bytes;
    };
    bytes = (unsigned char *)malloc((1 + len / 4) * 4 + 1);//padded to 32 bit barrier to accomodate unsigned int type
    if (!ret) return NULL;
    FILE* src = fopen(filename, "r");
    if (!src) {
        free(ret);
        return NULL;
    }
    read = fread(bytes, 1, len, src);
    while (read > 0 && len >= 0) {
        len -= read;
        offset += read;
        read = fread(bytes + offset, 1, len, src);
    }
    bytes[offset] = 0;//null term, probably unneeded
    fclose(src);
    return ret;
}

int sample_main(int argc, char *argv[]) {
    VkResult U_ASSERT_ONLY res = VK_SUCCESS;
    struct sample_info info = {};
    char sample_title[] = "Draw Textured Cube";
    const bool depthPresent = true;
    logfile = fopen("runtime.log", "w");
    uint64_t line = 0;

    process_command_line_args(info, argc, argv);
    init_global_layer_properties(info);
    init_instance_extension_names(info);
    init_device_extension_names(info);
    init_instance(info, sample_title);
    init_enumerate_device(info);
    init_window_size(info, 500, 500);
    init_connection(info);
    init_window(info);
    init_swapchain_extension(info);
    init_device(info);
    init_command_pool(info);
    init_command_buffer(info);
    execute_begin_command_buffer(info);
    init_device_queue(info);
    init_swap_chain(info);
    init_depth_buffer(info);
    init_texture(info);
    init_uniform_buffer(info);
    init_descriptor_and_pipeline_layouts(info, true);
    init_renderpass(info, depthPresent);
    //init_shaders(info, vertShaderText, fragShaderText);
    {
        //|-X init_glslang (empty function on everything that's not android)
        info.shaderStages[0] = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0,
            VK_SHADER_STAGE_VERTEX_BIT, VK_NULL_HANDLE, "main", NULL};
        VkShaderModuleCreateInfo moduleCreateInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, NULL, 0,
        0, NULL};
        moduleCreateInfo.pCode = loadSprv("shaders\\sample.vert.spv", &moduleCreateInfo.codeSize);
        if(!moduleCreateInfo.pCode) FAIL;
        res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[0].module);
        free((void*)moduleCreateInfo.pCode);
        if (res) FAIL;
        info.shaderStages[1] = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, NULL, 0,
            VK_SHADER_STAGE_FRAGMENT_BIT, VK_NULL_HANDLE, "main", NULL };
        moduleCreateInfo.pCode = loadSprv("shaders\\sample.frag.spv", &moduleCreateInfo.codeSize);
        res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[1].module);
        free((void*)moduleCreateInfo.pCode);
        if (res) FAIL;
    }
    init_framebuffers(info, depthPresent);
    init_vertex_buffer(info, g_vb_texture_Data, sizeof(g_vb_texture_Data), sizeof(g_vb_texture_Data[0]), true);
    init_descriptor_pool(info, true);
    init_descriptor_set(info, true);
    init_pipeline_cache(info);
    init_pipeline(info, depthPresent);

    /* VULKAN_KEY_START */

    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.2f;
    clear_values[0].color.float32[3] = 0.2f;
    clear_values[1].depthStencil.depth = 1.0f;
    clear_values[1].depthStencil.stencil = 0;

    VkSemaphore imageAcquiredSemaphore;
    VkSemaphoreCreateInfo imageAcquiredSemaphoreCreateInfo;
    imageAcquiredSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    imageAcquiredSemaphoreCreateInfo.pNext = NULL;
    imageAcquiredSemaphoreCreateInfo.flags = 0;

    res = vkCreateSemaphore(info.device, &imageAcquiredSemaphoreCreateInfo, NULL, &imageAcquiredSemaphore);
    assert(res == VK_SUCCESS);

    // Get the index of the next available swapchain image:
    res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE,
        &info.current_buffer);
    // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR
    // return codes
    assert(res == VK_SUCCESS);

    VkRenderPassBeginInfo rp_begin;
    rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
    rp_begin.pNext = NULL;
    rp_begin.renderPass = info.render_pass;
    rp_begin.framebuffer = info.framebuffers[info.current_buffer];
    rp_begin.renderArea.offset.x = 0;
    rp_begin.renderArea.offset.y = 0;
    rp_begin.renderArea.extent.width = info.width;
    rp_begin.renderArea.extent.height = info.height;
    rp_begin.clearValueCount = 2;
    rp_begin.pClearValues = clear_values;

    vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);

    vkCmdBindPipeline(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline);
    vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout, 0, NUM_DESCRIPTOR_SETS,
        info.desc_set.data(), 0, NULL);

    const VkDeviceSize offsets[1] = { 0 };
    vkCmdBindVertexBuffers(info.cmd, 0, 1, &info.vertex_buffer.buf, offsets);

    init_viewports(info);
    init_scissors(info);

    vkCmdDraw(info.cmd, 12 * 3, 1, 0, 0);
    vkCmdEndRenderPass(info.cmd);
    res = vkEndCommandBuffer(info.cmd);
    assert(res == VK_SUCCESS);

    const VkCommandBuffer cmd_bufs[] = { info.cmd };
    VkFenceCreateInfo fenceInfo;
    VkFence drawFence;
    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fenceInfo.pNext = NULL;
    fenceInfo.flags = 0;
    vkCreateFence(info.device, &fenceInfo, NULL, &drawFence);

    VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    VkSubmitInfo submit_info[1] = {};
    submit_info[0].pNext = NULL;
    submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submit_info[0].waitSemaphoreCount = 1;
    submit_info[0].pWaitSemaphores = &imageAcquiredSemaphore;
    submit_info[0].pWaitDstStageMask = &pipe_stage_flags;
    submit_info[0].commandBufferCount = 1;
    submit_info[0].pCommandBuffers = cmd_bufs;
    submit_info[0].signalSemaphoreCount = 0;
    submit_info[0].pSignalSemaphores = NULL;

    /* Queue the command buffer for execution */
    res = vkQueueSubmit(info.graphics_queue, 1, submit_info, drawFence);
    assert(res == VK_SUCCESS);

    /* Now present the image in the window */

    VkPresentInfoKHR present;
    present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    present.pNext = NULL;
    present.swapchainCount = 1;
    present.pSwapchains = &info.swap_chain;
    present.pImageIndices = &info.current_buffer;
    present.pWaitSemaphores = NULL;
    present.waitSemaphoreCount = 0;
    present.pResults = NULL;

    /* Make sure command buffer is finished before presenting */
    do {
        res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT);
    } while (res == VK_TIMEOUT);
    assert(res == VK_SUCCESS);
    res = vkQueuePresentKHR(info.present_queue, &present);
    assert(res == VK_SUCCESS);

    wait_seconds(1);
    /* VULKAN_KEY_END */
    if (info.save_images) write_ppm(info, "draw_textured_cube");

    vkDestroyFence(info.device, drawFence, NULL);
    vkDestroySemaphore(info.device, imageAcquiredSemaphore, NULL);
    destroy_pipeline(info);
    destroy_pipeline_cache(info);
    destroy_textures(info);
    destroy_descriptor_pool(info);
    destroy_vertex_buffer(info);
    destroy_framebuffers(info);
    destroy_shaders(info);
    destroy_renderpass(info);
    destroy_descriptor_and_pipeline_layouts(info);
    destroy_uniform_buffer(info);
    destroy_depth_buffer(info);
    destroy_swap_chain(info);
    destroy_command_buffer(info);
    destroy_command_pool(info);
    destroy_device(info);
    destroy_window(info);
    destroy_instance(info);
    return 0;
fail:
    fprintf(logfile, "Fail. Res: %d. Line: %u\n", res, line);
    return 1;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.11)
project (STUPID_CUBE)
# set (CMAKE_VERBOSE_MAKEFILE 1)
set(API_NAME "Vulkan" CACHE STRING "API name to use when building")
string(TOLOWER ${API_NAME} API_LOWERCASE)
include(GNUInstallDirs)
file(TO_CMAKE_PATH $ENV{VULKAN_SDK} VULKAN)

add_definitions("-DVULKAN_SAMPLES_BASE_DIR=\"${CMAKE_SOURCE_DIR}\"")

# The MAJOR number of the version we're building, used in naming
# vulkan-<major>.dll (and other files).
set(MAJOR "1")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set (CMAKE_INSTALL_PREFIX "")
set (UTILS_NAME vsamputils)
if(NOT WIN32)
    include(FindPkgConfig)
    option(BUILD_WSI_XCB_SUPPORT "Build XCB WSI support" ON)
    option(BUILD_WSI_WAYLAND_SUPPORT "Build Wayland WSI support" OFF)
    set(DEMOS_WSI_SELECTION "XCB" CACHE STRING "Select WSI target for demos (XCB, XLIB, WAYLAND, DISPLAY)")
    set(SAMPLES_WSI_SELECTION "XCB" CACHE STRING "Select WSI target for api-samples (XCB, WAYLAND, DISPLAY)")
    if (BUILD_WSI_XCB_SUPPORT)
         find_package(XCB REQUIRED)
    endif()
    if (BUILD_WSI_WAYLAND_SUPPORT)
        find_package(Wayland REQUIRED)
    endif()
    set (BUILDTGT_DIR build)
    set (BINDATA_DIR x86_64/bin)
    set (LIBSOURCE_DIR Lib)
else()
    # For Windows, since 32-bit and 64-bit items can co-exist, we build each in its own build directory.
    # 32-bit target data goes in build32, and 64-bit target data goes into build.  So, include/link the
    # appropriate data at build time.
    if (CMAKE_CL_64)
        set (BUILDTGT_DIR build)
        set (BINDATA_DIR Bin)
        set (LIBSOURCE_DIR Lib)
    else()
        set (BUILDTGT_DIR build32)
        set (BINDATA_DIR Bin32)
        set (LIBSOURCE_DIR Lib32)
    endif()
endif()

if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
    set(COMMON_COMPILE_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers")
    set(COMMON_COMPILE_FLAGS "${COMMON_COMPILE_FLAGS} -fno-strict-aliasing -fno-builtin-memcmp")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 ${COMMON_COMPILE_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_COMPILE_FLAGS} -std=c++11")
    if (UNIX)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
    endif()
endif()
find_program(GLSLANG_VALIDATOR NAMES glslangValidator
             HINTS "${VULKAN}/glslang/${BUILDTGT_DIR}/install/bin"
                   "${VULKAN}/${BINDATA_DIR}" )
find_path(GLSLANG_SPIRV_INCLUDE_DIR SPIRV/spirv.hpp HINTS "${VULKAN}/source/glslang/"
                                                          "${VULKAN}/glslang"
                                                          DOC "Path to SPIRV/spirv.hpp")
find_path(SPIRV_TOOLS_INCLUDE_DIR spirv-tools/libspirv.h HINTS "${VULKAN}/spirv-tools/include"
                                                               "${VULKAN}/source/spirv-tools/include"
                                                               "${VULKAN}/spirv-tools/external/include"
                                                               "${VULKAN}/source/spirv-tools/external/include"
                                                               DOC "Path to spirv-tools/libspirv.h")
find_path(Vulkan_INCLUDE_DIR vulkan/vulkan.h HINTS  "${VULKAN}"
                                                    "${VULKAN}/Include" )

if (WIN32)
    set (GLSLANG_SEARCH_PATH "${VULKAN}/glslang/${BUILDTGT_DIR}/glslang/Release"
                             "${VULKAN}/glslang/${BUILDTGT_DIR}/glslang/OSDependent/Windows/Release"
                             "${VULKAN}/glslang/${BUILDTGT_DIR}/hlsl/Release"
                             "${VULKAN}/glslang/${BUILDTGT_DIR}/OGLCompilersDLL/Release"
                             "${VULKAN}/glslang/${BUILDTGT_DIR}/SPIRV/Release" )
    set (SPIRV_TOOLS_SEARCH_PATH "${VULKAN}/spirv-tools/${BUILDTGT_DIR}/source/Release")
    set (SPIRV_TOOLS_OPT_SEARCH_PATH "${VULKAN}/spirv-tools/${BUILDTGT_DIR}/source/opt/Release")
else()
    set (GLSLANG_SEARCH_PATH "${VULKAN}/glslang/build/install/lib" "${VULKAN}/x86_64/lib/glslang" )
    set (SPIRV_TOOLS_SEARCH_PATH "${VULKAN}/spirv-tools/build" "${VULKAN}/x86_64/lib/spirv-tools" )
    set (SPIRV_TOOLS_OPT_SEARCH_PATH "${SPIRV_TOOLS_SEARCH_PATH}")
endif()

find_library(GLSLANG_LIB NAMES glslang
             HINTS ${GLSLANG_SEARCH_PATH} )

find_library(OGLCompiler_LIB NAMES OGLCompiler
             HINTS ${GLSLANG_SEARCH_PATH} )

find_library(OSDependent_LIB NAMES OSDependent
             HINTS ${GLSLANG_SEARCH_PATH} )

find_library(HLSL_LIB NAMES HLSL
             HINTS ${GLSLANG_SEARCH_PATH} )

find_library(SPIRV_LIB NAMES SPIRV
             HINTS ${GLSLANG_SEARCH_PATH} )

find_library(SPIRV_TOOLS_LIB NAMES SPIRV-Tools
             HINTS ${SPIRV_TOOLS_SEARCH_PATH} )

find_library(SPIRV_TOOLS_OPT_LIB NAMES SPIRV-Tools-opt
             HINTS ${SPIRV_TOOLS_OPT_SEARCH_PATH} )

find_library(SPIRV_REMAPPER_LIB NAMES SPVRemapper
             HINTS ${GLSLANG_SEARCH_PATH} )

# On Windows, we must pair Debug and Release appropriately
if (WIN32)
    set (GLSLANG_DEBUG_SEARCH_PATH "${VULKAN}/glslang/${BUILDTGT_DIR}/glslang/Debug"
                                   "${VULKAN}/glslang/${BUILDTGT_DIR}/glslang/OSDependent/Windows/Debug"
                                   "${VULKAN}/glslang/${BUILDTGT_DIR}/hlsl/Debug"
                                   "${VULKAN}/glslang/${BUILDTGT_DIR}/OGLCompilersDLL/Debug"
                                   "${VULKAN}/glslang/${BUILDTGT_DIR}/SPIRV/Debug")
    set (SPIRV_TOOLS_DEBUG_SEARCH_PATH "${VULKAN}/spirv-tools/${BUILDTGT_DIR}/source/Debug")
    set (SPIRV_TOOLS_OPT_DEBUG_SEARCH_PATH "${VULKAN}/spirv-tools/${BUILDTGT_DIR}/source/opt/Debug")

    add_library(glslang     STATIC IMPORTED)
    add_library(OGLCompiler STATIC IMPORTED)
    add_library(OSDependent STATIC IMPORTED)
    add_library(HLSL        STATIC IMPORTED)
    add_library(SPIRV       STATIC IMPORTED)
    add_library(Loader      STATIC IMPORTED)
    add_library(SPIRV-Tools STATIC IMPORTED)
    add_library(SPIRV-Tools-opt STATIC IMPORTED)
    add_library(SPVRemapper STATIC IMPORTED)

    find_library(GLSLANG_DLIB NAMES glslangd
                 HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
    find_library(OGLCompiler_DLIB NAMES OGLCompilerd
                 HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
    find_library(OSDependent_DLIB NAMES OSDependentd
                 HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
    find_library(HLSL_DLIB NAMES HLSLd
                 HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
    find_library(SPIRV_DLIB NAMES SPIRVd
                 HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )
    find_library(SPIRV_TOOLS_DLIB NAMES SPIRV-Tools
                 HINTS ${SPIRV_TOOLS_DEBUG_SEARCH_PATH} )
    find_library(SPIRV_TOOLS_OPT_DLIB NAMES SPIRV-Tools-opt
                 HINTS ${SPIRV_TOOLS_OPT_DEBUG_SEARCH_PATH} )
    find_library(SPIRV_REMAPPER_DLIB NAMES SPVRemapperd
                 HINTS ${GLSLANG_DEBUG_SEARCH_PATH} )

    set_target_properties(glslang PROPERTIES
                         IMPORTED_LOCATION       "${GLSLANG_LIB}"
                         IMPORTED_LOCATION_DEBUG "${GLSLANG_DLIB}")
    set_target_properties(OGLCompiler PROPERTIES
                         IMPORTED_LOCATION       "${OGLCompiler_LIB}"
                         IMPORTED_LOCATION_DEBUG "${OGLCompiler_DLIB}")
    set_target_properties(OSDependent PROPERTIES
                         IMPORTED_LOCATION       "${OSDependent_LIB}"
                         IMPORTED_LOCATION_DEBUG "${OSDependent_DLIB}")
    set_target_properties(HLSL PROPERTIES
                         IMPORTED_LOCATION       "${HLSL_LIB}"
                         IMPORTED_LOCATION_DEBUG "${HLSL_DLIB}")
    set_target_properties(SPIRV PROPERTIES
                         IMPORTED_LOCATION       "${SPIRV_LIB}"
                         IMPORTED_LOCATION_DEBUG "${SPIRV_DLIB}")
    set_target_properties(SPIRV-Tools PROPERTIES
                         IMPORTED_LOCATION       "${SPIRV_TOOLS_LIB}"
                         IMPORTED_LOCATION_DEBUG "${SPIRV_TOOLS_DLIB}")
    set_target_properties(SPIRV-Tools-opt PROPERTIES
                         IMPORTED_LOCATION       "${SPIRV_TOOLS_OPT_LIB}"
                         IMPORTED_LOCATION_DEBUG "${SPIRV_TOOLS_OPT_DLIB}")
    set_target_properties(SPVRemapper PROPERTIES
                         IMPORTED_LOCATION       "${SPIRV_REMAPPER_LIB}"
                         IMPORTED_LOCATION_DEBUG "${SPIRV_REMAPPER_DLIB}")

    set (SPIRV_TOOLS_LIBRARIES SPIRV-Tools-opt SPIRV-Tools)
    set (GLSLANG_LIBRARIES glslang OGLCompiler OSDependent HLSL SPIRV SPVRemapper ${SPIRV_TOOLS_LIBRARIES})
else ()
    set (SPIRV_TOOLS_LIBRARIES ${SPIRV_TOOLS_OPT_LIB} ${SPIRV_TOOLS_LIB})
    set (GLSLANG_LIBRARIES ${GLSLANG_LIB} ${OGLCompiler_LIB} ${OSDependent_LIB} ${HLSL_LIB} ${SPIRV_LIB} ${SPIRV_REMAPPER_LIB} ${SPIRV_TOOLS_LIBRARIES})
endif()

set (GLMINCLUDES "${CMAKE_SOURCE_DIR}/utils")

# to run with source glslang libs/headers
if(UNIX)
    add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
    if (EXISTS "${VULKAN}/glslang")
         set (GLSLANGDIR "${VULKAN}/glslang")
    else()
         set (GLSLANGDIR "${VULKAN}/source/glslang")
    endif()
else()
    set (GLSLANGDIR "${VULKAN}/glslang")
endif()

get_filename_component(GLMINC_PREFIX "${GLMINCLUDES}" ABSOLUTE)
if(NOT EXISTS ${GLMINC_PREFIX})
    message(FATAL_ERROR "Necessary glm headers do not exist: " ${GLMINC_PREFIX})
endif()

get_filename_component(GLSLANG_PREFIX "${GLSLANGDIR}" ABSOLUTE)
if(NOT EXISTS ${GLSLANG_PREFIX})
    message(FATAL_ERROR "Necessary glslang components do not exist: " ${GLSLANG_PREFIX})
endif()

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/utils)

if(WIN32)
    set (MOVE_CMD "move")
    set (VULKAN_LOADER_NAME "${API_LOWERCASE}-${MAJOR}")
else()
    set (MOVE_CMD "mv")
    set (PTHREAD "pthread")
    set (VULKAN_LOADER_NAME "${API_LOWERCASE}")
endif()

if(EXISTS "${CMAKE_SOURCE_DIR}/layers")
    set (VULKAN_LOADER ${VULKAN_LOADER_NAME})
else()
    find_library(VULKAN_LOADER NAMES ${VULKAN_LOADER_NAME}
                 HINTS "${VULKAN}/${LIBSOURCE_DIR}" "${VULKAN}/x86_64/lib" )
endif()

add_definitions(-DAPI_NAME="${API_NAME}")

# If ANDROID is ON, turn on cross-compiling for it
if(ANDROID)
    set(CMAKE_SYSTEM_NAME "Android")
    set(CMAKE_SYSTEM_VERSION "7")
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
    add_definitions(-DVK_USE_PLATFORM_WIN32_KHR -DWIN32_LEAN_AND_MEAN)
    set(DisplayServer Win32)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
    add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    if (SAMPLES_WSI_SELECTION STREQUAL "XCB")
        if (NOT BUILD_WSI_XCB_SUPPORT)
            message( FATAL_ERROR "Selected XCB for samples build but not building Xcb support" )
        endif()
        add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
    elseif (SAMPLES_WSI_SELECTION STREQUAL "XLIB")
        if (NOT BUILD_WSI_XLIB_SUPPORT)
            message( FATAL_ERROR "Selected XLIB for samples build but not building Xlib support" )
        endif()
        add_definitions(-DVK_USE_PLATFORM_XLIB_KHR)
    elseif (SAMPLES_WSI_SELECTION STREQUAL "WAYLAND")
        if (NOT BUILD_WSI_WAYLAND_SUPPORT)
            message( FATAL_ERROR "Selected Wayland for samples build but not building Wayland support" )
        endif()
        add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
    endif()
else()
    message(FATAL_ERROR "Unsupported Platform!")
endif()

set(SAMPLES_DATA_DIR ${SAMPLES_DATA_DIR} "${CMAKE_SOURCE_DIR}/data")
set(SHADER_FILES ${SHADER_FILES} "")
include_directories( ${SAMPLES_DATA_DIR} ${GLSLANG_SPIRV_INCLUDE_DIR} ${GLMINC_PREFIX} ${Vulkan_INCLUDE_DIR})

# Additional includes for spirv-tools
include_directories(${SPIRV_TOOLS_INCLUDE_DIR})

if(WIN32)
    set (MOVE_CMD "move")
    set (VULKAN_LOADER_NAME "${API_LOWERCASE}-${MAJOR}")
else()
    set (MOVE_CMD "mv")
    set (PTHREAD "pthread")
    set (VULKAN_LOADER_NAME "${API_LOWERCASE}")
endif()

if(EXISTS "${PROJECT_SOURCE_DIR}/${V_LVL_RELATIVE_LOCATION}/loader")
    set (VULKAN_LOADER ${VULKAN_LOADER_NAME})
else()
    find_library(VULKAN_LOADER NAMES ${VULKAN_LOADER_NAME}
    HINTS "${V_LVL_RELATIVE_LOCATION}/${LIBSOURCE_DIR}" "${V_LVL_RELATIVE_LOCATION}/x86_64/lib" )
endif()

set (LIBGLM_INCLUDE_DIR ${V_LVL_RELATIVE_LOCATION}/libs)

if(NOT WIN32 AND NOT ANDROID)
    if(SDK_INCLUDE_PATH)
        include_directories(${SDK_INCLUDE_PATH})
    endif()
    if (BUILD_WSI_XCB_SUPPORT)
        include_directories(${XCB_INCLUDE_DIRS})
        link_libraries(${XCB_LIBRARIES} m )
    endif()
    if (BUILD_WSI_WAYLAND_SUPPORT)
        include_directories(${WAYLAND_CLIENT_INCLUDE_DIR})
        link_libraries(${WAYLAND_CLIENT_LIBRARIES})
    endif()
    link_libraries(${VULKAN_LOADER} m ) 
endif()
if(WIN32)
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES")

    # If MSVC, disable some signed/unsigned mismatch warnings.
    if (MSVC)
        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4267")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267")
    endif()

else()
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-sign-compare")
endif()

if (NOT WIN32)
    # extra setup for out-of-tree builds
    if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
        add_custom_target(samples-binary-dir-symlinks ALL
            COMMAND ln -sf ${CMAKE_CURRENT_SOURCE_DIR}/get-short-descripts.sh
            VERBATIM
            )
    endif()
else()
    if (NOT (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR))
        FILE(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lunarg.ppm LUNARG)
    endif()
endif()


#
# START BUILDING SAMPLES HERE
#

# Generate Android project.
option(ANDROID OFF)

# simple one file sample targets, no additional files
file(GLOB SHADERS RELATIVE "${CMAKE_SOURCE_DIR}" CONFIGURE_DEPENDS shaders/*.frag shaders/*.vert)
foreach(SFILE ${SHADERS})
    add_custom_command (OUTPUT ${SFILE}.spv COMMAND ${GLSLANG_VALIDATOR} -s -V "${CMAKE_SOURCE_DIR}/${SFILE}" -o ${SFILE}.spv DEPENDS ${SFILE} )
endforeach(SFILE)
add_executable( main WIN32 main.cpp shaders/sample.frag.spv shaders/sample.vert.spv )
target_link_libraries(main ${UTILS_NAME} ${GLSLANG_LIBRARIES} ${VULKAN_LOADER} ${WINLIBS} ${SPIRV_TOOLS_LIBRARIES})

if (NOT ANDROID)
    foreach (sample ${S_TARGETS})
        install(TARGETS ${sample} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
    endforeach(sample)
    add_subdirectory(utils)
endif()

diff original util.cpp vs mine:

<     return std::string(VULKAN_SAMPLES_BASE_DIR) + "/API-Samples/data/";
---
>     return std::string(VULKAN_SAMPLES_BASE_DIR) + "/data/";

sample.vert:

#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (std140, binding = 0) uniform buf {
        mat4 mvp;
} ubuf;
layout (location = 0) in vec4 pos;
layout (location = 1) in vec2 inTexCoords;
layout (location = 0) out vec2 texcoord;
void main() {
   texcoord = inTexCoords;
   gl_Position = ubuf.mvp * pos;
}

sample.frag:

#version 400
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
layout (binding = 1) uniform sampler2D tex;
layout (location = 0) in vec2 texcoord;
layout (location = 0) out vec4 outColor;
void main() {
   outColor = textureLod(tex, texcoord, 0.0);
}
c++
glsl
shader
vulkan
asked on Stack Overflow Jul 30, 2019 by memtha

1 Answer

3

On Windows, you'll need to open the binary SPIR-V file in binary mode by specifying the fopen mode flags as "rb". SPIR-V code is a sequence of 32-bit unsigned integers and on Windows you need to suppress any line-ending translations with the "binary" mode flag. The "b" flag is ignored on POSIX conforming systems like Linux and so you can leave it in your code if it is to be compiled on multiple platforms.

answered on Stack Overflow Jul 31, 2019 by Karl Schultz

User contributions licensed under CC BY-SA 3.0