std::unique_ptr<ir::IRContext> BuildModule(spv_target_env env, MessageConsumer consumer, const uint32_t* binary, const size_t size) { auto context = spvContextCreate(env); libspirv::SetContextMessageConsumer(context, consumer); auto irContext = MakeUnique<ir::IRContext>(env, consumer); ir::IrLoader loader(consumer, irContext->module()); spv_result_t status = spvBinaryParse(context, &loader, binary, size, SetSpvHeader, SetSpvInst, nullptr); loader.EndModule(); spvContextDestroy(context); return status == SPV_SUCCESS ? std::move(irContext) : nullptr; }
// Use the SPIRV-Tools disassembler to print SPIR-V. void SpirvToolsDisassemble(std::ostream& out, const std::vector<unsigned int>& spirv) { // disassemble spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_3); spv_text text; spv_diagnostic diagnostic = nullptr; spvBinaryToText(context, spirv.data(), spirv.size(), SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT, &text, &diagnostic); // dump if (diagnostic == nullptr) out << text->str; else spvDiagnosticPrint(diagnostic); // teardown spvDiagnosticDestroy(diagnostic); spvContextDestroy(context); }
// Apply the SPIRV-Tools validator to generated SPIR-V. void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger) { // validate spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); spv_const_binary_t binary = { spirv.data(), spirv.size() }; spv_diagnostic diagnostic = nullptr; spv_validator_options options = spvValidatorOptionsCreate(); spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets()); spvValidateWithOptions(context, options, &binary, &diagnostic); // report if (diagnostic != nullptr) { logger->error("SPIRV-Tools Validation Errors"); logger->error(diagnostic->error); } // tear down spvValidatorOptionsDestroy(options); spvDiagnosticDestroy(diagnostic); spvContextDestroy(context); }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "SPIR-V Specialization"; const bool depthPresent = true; 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); /* VULKAN_KEY_START */ // Pass in nullptr for fragment shader so we can setup specialization init_shaders(info, vertShaderText, nullptr); // This structure maps constant ids to data locations. // NOTE: Padding bool to 32-bits for simplicity const VkSpecializationMapEntry entries[] = // id, offset, size {{5, 0, sizeof(uint32_t)}, {7, 1 * sizeof(uint32_t), sizeof(uint32_t)}, {8, 2 * sizeof(uint32_t), sizeof(uint32_t)}, {9, 3 * sizeof(uint32_t), sizeof(uint32_t)}}; // Initialize the values we want our mini-ubershader to use const bool drawUserColor = true; const float userColor[] = {0.0f, 0.0f, 1.0f}; // Populate our data entry uint32_t data[4] = {}; data[0] = drawUserColor ? 1 : 0; ((float *)data)[1] = userColor[0]; ((float *)data)[2] = userColor[1]; ((float *)data)[3] = userColor[2]; // Set up the info describing our spec map and data const VkSpecializationInfo specInfo = { 4, // mapEntryCount entries, // pMapEntries 4 * sizeof(float), // dataSize data, // pData }; // Provide the specialization data to fragment stage info.shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; info.shaderStages[1].pNext = NULL; info.shaderStages[1].pSpecializationInfo = &specInfo; info.shaderStages[1].flags = 0; info.shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; info.shaderStages[1].pName = "main"; VkShaderModuleCreateInfo moduleCreateInfo; moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; moduleCreateInfo.pNext = NULL; moduleCreateInfo.flags = 0; if (use_SPIRV_asm) { // Use the hand edited SPIR-V assembly spv_context spvContext = spvContextCreate(SPV_ENV_VULKAN_1_0); spv_binary fragmentBinary = {}; spv_diagnostic fragmentDiag = {}; spv_result_t fragmentResult = spvTextToBinary(spvContext, fragmentSPIRV_specialized.c_str(), fragmentSPIRV_specialized.length(), &fragmentBinary, &fragmentDiag); if (fragmentDiag) { printf("Diagnostic info from fragment shader:\n"); spvDiagnosticPrint(fragmentDiag); } assert(fragmentResult == SPV_SUCCESS); moduleCreateInfo.codeSize = fragmentBinary->wordCount * sizeof(unsigned int); moduleCreateInfo.pCode = fragmentBinary->code; spvDiagnosticDestroy(fragmentDiag); spvContextDestroy(spvContext); } else { // Convert GLSL to SPIR-V init_glslang(); std::vector<unsigned int> fragSpv; bool U_ASSERT_ONLY retVal = GLSLtoSPV(VK_SHADER_STAGE_FRAGMENT_BIT, fragShaderText, fragSpv); assert(retVal); finalize_glslang(); moduleCreateInfo.codeSize = fragSpv.size() * sizeof(unsigned int); moduleCreateInfo.pCode = fragSpv.data(); } res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[1].module); assert(res == VK_SUCCESS); /* VULKAN_KEY_END */ 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); init_presentable_image(info); VkClearValue clear_values[2]; init_clear_color_and_depth(info, clear_values); VkRenderPassBeginInfo rp_begin; init_render_pass_begin_info(info, rp_begin); 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); VkFence drawFence = {}; init_fence(info, drawFence); VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submit_info = {}; init_submit_info(info, submit_info, pipe_stage_flags); /* 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 = {}; init_present_info(info, present); /* 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); if (info.save_images) write_ppm(info, "spirv_specialization"); vkDestroyFence(info.device, drawFence, NULL); vkDestroySemaphore(info.device, info.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; }
TEST(CreateContext, VulkanEnvironment) { auto c = spvContextCreate(SPV_ENV_VULKAN_1_0); EXPECT_THAT(c, NotNull()); spvContextDestroy(c); }
TEST(CreateContext, UniversalEnvironment) { auto c = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); EXPECT_THAT(c, NotNull()); spvContextDestroy(c); }
// clang-format on int main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "SPIR-V Assembly"; const bool depthPresent = true; 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); /* VULKAN_KEY_START */ // Init the assembler context spv_context spvContext = spvContextCreate(); // Convert the vertex assembly into binary format spv_binary vertexBinary = {}; spv_diagnostic vertexDiag = {}; spv_result_t vertexResult = spvTextToBinary(spvContext, vertexSPIRV.c_str(), vertexSPIRV.length(), &vertexBinary, &vertexDiag); if (vertexDiag) { printf("Diagnostic info from vertex shader:\n"); spvDiagnosticPrint(vertexDiag); } assert(vertexResult == SPV_SUCCESS); // Convert the fragment assembly into binary format spv_binary fragmentBinary = {}; spv_diagnostic fragmentDiag = {}; spv_result_t fragmentResult = spvTextToBinary(spvContext, fragmentSPIRV.c_str(), fragmentSPIRV.length(), &fragmentBinary, &fragmentDiag); if (fragmentDiag) { printf("Diagnostic info from fragment shader:\n"); spvDiagnosticPrint(fragmentDiag); } assert(fragmentResult == SPV_SUCCESS); info.shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; info.shaderStages[0].pNext = NULL; info.shaderStages[0].pSpecializationInfo = NULL; info.shaderStages[0].flags = 0; info.shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; info.shaderStages[0].pName = "main"; VkShaderModuleCreateInfo moduleCreateInfo; moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; moduleCreateInfo.pNext = NULL; moduleCreateInfo.flags = 0; // Use wordCount and code pointers from the spv_binary moduleCreateInfo.codeSize = vertexBinary->wordCount * sizeof(unsigned int); moduleCreateInfo.pCode = vertexBinary->code; res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[0].module); assert(res == VK_SUCCESS); info.shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; info.shaderStages[1].pNext = NULL; info.shaderStages[1].pSpecializationInfo = NULL; info.shaderStages[1].flags = 0; info.shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; info.shaderStages[1].pName = "main"; moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; moduleCreateInfo.pNext = NULL; moduleCreateInfo.flags = 0; // Use wordCount and code pointers from the spv_binary moduleCreateInfo.codeSize = fragmentBinary->wordCount * sizeof(unsigned int); moduleCreateInfo.pCode = fragmentBinary->code; res = vkCreateShaderModule(info.device, &moduleCreateInfo, NULL, &info.shaderStages[1].module); assert(res == VK_SUCCESS); // Clean up the diagnostics spvDiagnosticDestroy(vertexDiag); spvDiagnosticDestroy(fragmentDiag); // Clean up the assembler context spvContextDestroy(spvContext); /* VULKAN_KEY_END */ 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); init_presentable_image(info); VkClearValue clear_values[2]; init_clear_color_and_depth(info, clear_values); VkRenderPassBeginInfo rp_begin; init_render_pass_begin_info(info, rp_begin); 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); execute_pre_present_barrier(info); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); VkFence drawFence = {}; init_fence(info, drawFence); VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSubmitInfo submit_info = {}; init_submit_info(info, submit_info, pipe_stage_flags); /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, drawFence); assert(res == VK_SUCCESS); /* Now present the image in the window */ VkPresentInfoKHR present = {}; init_present_info(info, present); /* 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.queue, &present); assert(res == VK_SUCCESS); wait_seconds(1); if (info.save_images) write_ppm(info, "spirv_assembly"); vkDestroyFence(info.device, drawFence, NULL); vkDestroySemaphore(info.device, info.presentCompleteSemaphore, 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; }
ValidateBase<T, OPTIONS>::~ValidateBase() { spvContextDestroy(context_); }
int main(int argc, char** argv) { if (2 > argc) { print_usage(argv[0]); return 1; } const char* inFile = nullptr; uint32_t options = 0; for (int argi = 1; argi < argc; ++argi) { if ('-' == argv[argi][0]) { if (!strcmp("basic", argv[argi] + 1)) { options |= SPV_VALIDATE_BASIC_BIT; } else if (!strcmp("layout", argv[argi] + 1)) { options |= SPV_VALIDATE_LAYOUT_BIT; } else if (!strcmp("id", argv[argi] + 1)) { options |= SPV_VALIDATE_ID_BIT; } else if (!strcmp("rules", argv[argi] + 1)) { options |= SPV_VALIDATE_RULES_BIT; } else { print_usage(argv[0]); return 1; } } else { if (!inFile) { inFile = argv[argi]; } else { print_usage(argv[0]); return 1; } } } if (!inFile) { fprintf(stderr, "error: input file is empty.\n"); return 1; } std::vector<uint32_t> contents; if (FILE* fp = fopen(inFile, "rb")) { uint32_t buf[1024]; while (size_t len = fread(buf, sizeof(uint32_t), sizeof(buf) / sizeof(uint32_t), fp)) { contents.insert(contents.end(), buf, buf + len); } fclose(fp); } else { fprintf(stderr, "error: file does not exist '%s'\n", inFile); return 1; } spv_const_binary_t binary = {contents.data(), contents.size()}; spv_diagnostic diagnostic = nullptr; spv_context context = spvContextCreate(); spv_result_t error = spvValidate(context, &binary, options, &diagnostic); spvContextDestroy(context); if (error) { spvDiagnosticPrint(diagnostic); spvDiagnosticDestroy(diagnostic); return error; } return 0; }