在 vulkan 中使用已编译的 spirv 着色器的正确方法

Correct way to use compiled spirv shaders in vulkan

TL;DR:

已编译的 spirv 着色器在运行时内联运行时编译的 spirvs 工作时崩溃,已使用修改后的示例进行测试。崩溃发生在 Vulkan 代码深处。给出了什么?

详情:

我在使用编译的 spirv 着色器时遇到问题。我获取了 "draw-textured-cube" 示例,使用配对的 cmake 将其提取到独立环境中,并对 util.cpp 进行了最小编辑以调整数据目录的路径。它按预期编译和工作。

然后我从文件开头的 static const cstrings 中提取着色器代码,并将它们移动到它们自己的文件中。我将对 init_shaders 的调用替换为只读取文件并使用与 init_shaders 相同的选项构建着色器模块的调用。我使用从原始示例 CMakeLists.txt.

中未使用的函数中获取的选项将 glsl 编译成 spirv

它现在因创建管道时出现访问冲突而崩溃(下面是完整的错误文本)。 Debug 和 Release 构建中的结果相同,有或没有验证层。单步执行 visual studio 中的程序集显示错误至少发生在创建管道调用的 16 层深处(其中 call 或非本地 jmp 构成一个层,因为这些是最可能的功能障碍)。

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

(确切地址与预期不同。)

SDK 中包含的任何示例都使用在运行时编译到 spirv 中的内联 glsl,但所有示例都有与此类似的注释(来自纹理立方体示例):

/* 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                                                                 */

这似乎表明 "correct" 执行着色器的方法是使用预编译,但我们没有给出如何实现此目的的工作 sample/example。我看过其他地方,但找不到一个相当简单(<10,000 行)的示例来说明如何编译读取和显示 spv 着色器。

我对问题所在的最佳猜测是:1) glslValidator 编译错误; 2)我读错了; 3) 使用它们需要不同的选项(例如,着色器模块创建信息)然后是运行时编译的着色器。无论如何,我已经开始希望 vulkan 有更好的错误捕获。

显然,在任何地方使用内联 glsl 都是一种解决方法,如果需要这样做,我可以只用每个着色器一个大的 cstring 来制作一堆 .cpp 文件,但代价是运行时初始加载,但确实应该有更好的解决方案。

主机系统是 windows 7 (x64),与程序所需资源相比,资源过多。我的目标是 windows linux 和(如果可能的话)mac,所以任何排除 android、ios、游戏机等的解决方案都适合我.

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()

原始 util.cpp 与我的差异:

<     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);
}

在 Windows 上,您需要通过将 fopen 模式标志指定为 "rb" 以二进制模式打开二进制 SPIR-V 文件。 SPIR-V 代码是一个 32 位无符号整数序列,在 Windows 上,您需要使用 "binary" 模式标志抑制​​任何行结束转换。 "b" 标志在 POSIX 符合 Linux 的系统上被忽略,因此如果要在多个平台上编译,您可以将其保留在代码中。