| /* |
| * Copyright (c) 2015-2025 The Khronos Group Inc. |
| * Copyright (c) 2015-2025 Valve Corporation |
| * Copyright (c) 2015-2025 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. |
| */ |
| |
| #include "shader_helper.h" |
| |
| #include "test_common.h" |
| #include "glslang/SPIRV/GlslangToSpv.h" |
| #include <glslang/Public/ShaderLang.h> |
| |
| #ifdef VVL_USE_SLANG |
| #pragma push_macro("None") |
| #pragma push_macro("Bool") |
| #undef None |
| #undef Bool |
| |
| #include "slang.h" |
| #include "slang-com-ptr.h" |
| |
| #pragma pop_macro("None") |
| #pragma pop_macro("Bool") |
| #endif |
| |
| static void ProcessConfigFile(const VkPhysicalDeviceLimits &device_limits, TBuiltInResource &out_resources) { |
| // These are the default resources for TBuiltInResources. |
| out_resources.maxLights = 32; |
| out_resources.maxClipPlanes = 6; |
| out_resources.maxTextureUnits = 32; |
| out_resources.maxTextureCoords = 32; |
| out_resources.maxVertexAttribs = 64; |
| out_resources.maxVertexUniformComponents = 4096; |
| out_resources.maxVaryingFloats = 64; |
| out_resources.maxVertexTextureImageUnits = 32; |
| out_resources.maxCombinedTextureImageUnits = 80; |
| out_resources.maxTextureImageUnits = 32; |
| out_resources.maxFragmentUniformComponents = 4096; |
| out_resources.maxDrawBuffers = 32; |
| out_resources.maxVertexUniformVectors = 128; |
| out_resources.maxVaryingVectors = 8; |
| out_resources.maxFragmentUniformVectors = 16; |
| out_resources.maxVertexOutputVectors = 16; |
| out_resources.maxFragmentInputVectors = 15; |
| out_resources.minProgramTexelOffset = -8; |
| out_resources.maxProgramTexelOffset = 7; |
| out_resources.maxClipDistances = device_limits.maxClipDistances; |
| out_resources.maxComputeWorkGroupCountX = device_limits.maxComputeWorkGroupCount[0]; |
| out_resources.maxComputeWorkGroupCountY = device_limits.maxComputeWorkGroupCount[1]; |
| out_resources.maxComputeWorkGroupCountZ = device_limits.maxComputeWorkGroupCount[2]; |
| out_resources.maxComputeWorkGroupSizeX = device_limits.maxComputeWorkGroupSize[0]; |
| out_resources.maxComputeWorkGroupSizeY = device_limits.maxComputeWorkGroupSize[1]; |
| out_resources.maxComputeWorkGroupSizeZ = device_limits.maxComputeWorkGroupSize[2]; |
| out_resources.maxComputeUniformComponents = 1024; |
| out_resources.maxComputeTextureImageUnits = 16; |
| out_resources.maxComputeImageUniforms = 8; |
| out_resources.maxComputeAtomicCounters = 8; |
| out_resources.maxComputeAtomicCounterBuffers = 1; |
| out_resources.maxVaryingComponents = 60; |
| out_resources.maxVertexOutputComponents = device_limits.maxVertexOutputComponents; |
| out_resources.maxGeometryInputComponents = device_limits.maxGeometryInputComponents; |
| out_resources.maxGeometryOutputComponents = device_limits.maxGeometryOutputComponents; |
| out_resources.maxFragmentInputComponents = device_limits.maxFragmentInputComponents; |
| out_resources.maxImageUnits = 8; |
| out_resources.maxCombinedImageUnitsAndFragmentOutputs = 8; |
| out_resources.maxCombinedShaderOutputResources = 8; |
| out_resources.maxImageSamples = 0; |
| out_resources.maxVertexImageUniforms = 0; |
| out_resources.maxTessControlImageUniforms = 0; |
| out_resources.maxTessEvaluationImageUniforms = 0; |
| out_resources.maxGeometryImageUniforms = 0; |
| out_resources.maxFragmentImageUniforms = 8; |
| out_resources.maxCombinedImageUniforms = 8; |
| out_resources.maxGeometryTextureImageUnits = 16; |
| out_resources.maxGeometryOutputVertices = device_limits.maxGeometryOutputVertices; |
| out_resources.maxGeometryTotalOutputComponents = device_limits.maxGeometryTotalOutputComponents; |
| out_resources.maxGeometryUniformComponents = 1024; |
| out_resources.maxGeometryVaryingComponents = 64; |
| out_resources.maxTessControlInputComponents = 128; |
| out_resources.maxTessControlOutputComponents = 128; |
| out_resources.maxTessControlTextureImageUnits = 16; |
| out_resources.maxTessControlUniformComponents = 1024; |
| out_resources.maxTessControlTotalOutputComponents = 4096; |
| out_resources.maxTessEvaluationInputComponents = 128; |
| out_resources.maxTessEvaluationOutputComponents = 128; |
| out_resources.maxTessEvaluationTextureImageUnits = 16; |
| out_resources.maxTessEvaluationUniformComponents = 1024; |
| out_resources.maxTessPatchComponents = 120; |
| out_resources.maxPatchVertices = 32; |
| out_resources.maxTessGenLevel = 64; |
| out_resources.maxViewports = device_limits.maxViewports; |
| out_resources.maxVertexAtomicCounters = 0; |
| out_resources.maxTessControlAtomicCounters = 0; |
| out_resources.maxTessEvaluationAtomicCounters = 0; |
| out_resources.maxGeometryAtomicCounters = 0; |
| out_resources.maxFragmentAtomicCounters = 8; |
| out_resources.maxCombinedAtomicCounters = 8; |
| out_resources.maxAtomicCounterBindings = 1; |
| out_resources.maxVertexAtomicCounterBuffers = 0; |
| out_resources.maxTessControlAtomicCounterBuffers = 0; |
| out_resources.maxTessEvaluationAtomicCounterBuffers = 0; |
| out_resources.maxGeometryAtomicCounterBuffers = 0; |
| out_resources.maxFragmentAtomicCounterBuffers = 1; |
| out_resources.maxCombinedAtomicCounterBuffers = 1; |
| out_resources.maxAtomicCounterBufferSize = 16384; |
| out_resources.maxTransformFeedbackBuffers = 4; |
| out_resources.maxTransformFeedbackInterleavedComponents = 64; |
| out_resources.maxCullDistances = device_limits.maxCullDistances; |
| out_resources.maxCombinedClipAndCullDistances = 8; |
| out_resources.maxSamples = 4; |
| out_resources.maxMeshOutputVerticesNV = 256; |
| out_resources.maxMeshOutputPrimitivesNV = 512; |
| out_resources.maxMeshWorkGroupSizeX_NV = 32; |
| out_resources.maxMeshWorkGroupSizeY_NV = 1; |
| out_resources.maxMeshWorkGroupSizeZ_NV = 1; |
| out_resources.maxTaskWorkGroupSizeX_NV = 32; |
| out_resources.maxTaskWorkGroupSizeY_NV = 1; |
| out_resources.maxTaskWorkGroupSizeZ_NV = 1; |
| out_resources.maxMeshViewCountNV = 4; |
| out_resources.maxMeshOutputVerticesEXT = 256; |
| out_resources.maxMeshOutputPrimitivesEXT = 512; |
| out_resources.maxMeshWorkGroupSizeX_EXT = 32; |
| out_resources.maxMeshWorkGroupSizeY_EXT = 1; |
| out_resources.maxMeshWorkGroupSizeZ_EXT = 1; |
| out_resources.maxTaskWorkGroupSizeX_EXT = 32; |
| out_resources.maxTaskWorkGroupSizeY_EXT = 1; |
| out_resources.maxTaskWorkGroupSizeZ_EXT = 1; |
| out_resources.maxMeshViewCountEXT = 4; |
| |
| out_resources.limits.nonInductiveForLoops = 1; |
| out_resources.limits.whileLoops = 1; |
| out_resources.limits.doWhileLoops = 1; |
| out_resources.limits.generalUniformIndexing = 1; |
| out_resources.limits.generalAttributeMatrixVectorIndexing = 1; |
| out_resources.limits.generalVaryingIndexing = 1; |
| out_resources.limits.generalSamplerIndexing = 1; |
| out_resources.limits.generalVariableIndexing = 1; |
| out_resources.limits.generalConstantMatrixVectorIndexing = 1; |
| } |
| |
| // |
| // Convert VK shader type to compiler's |
| // |
| static EShLanguage FindLanguage(const VkShaderStageFlagBits shader_type) { |
| switch (shader_type) { |
| case VK_SHADER_STAGE_VERTEX_BIT: |
| return EShLangVertex; |
| |
| case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: |
| return EShLangTessControl; |
| |
| case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: |
| return EShLangTessEvaluation; |
| |
| case VK_SHADER_STAGE_GEOMETRY_BIT: |
| return EShLangGeometry; |
| |
| case VK_SHADER_STAGE_FRAGMENT_BIT: |
| return EShLangFragment; |
| |
| case VK_SHADER_STAGE_COMPUTE_BIT: |
| return EShLangCompute; |
| |
| case VK_SHADER_STAGE_RAYGEN_BIT_KHR: |
| return EShLangRayGen; |
| |
| case VK_SHADER_STAGE_ANY_HIT_BIT_KHR: |
| return EShLangAnyHit; |
| |
| case VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR: |
| return EShLangClosestHit; |
| |
| case VK_SHADER_STAGE_MISS_BIT_KHR: |
| return EShLangMiss; |
| |
| case VK_SHADER_STAGE_INTERSECTION_BIT_KHR: |
| return EShLangIntersect; |
| |
| case VK_SHADER_STAGE_CALLABLE_BIT_KHR: |
| return EShLangCallable; |
| |
| case VK_SHADER_STAGE_TASK_BIT_EXT: |
| return EShLangTask; |
| |
| case VK_SHADER_STAGE_MESH_BIT_EXT: |
| return EShLangMesh; |
| |
| default: |
| assert(false); |
| return EShLangVertex; |
| } |
| } |
| |
| struct GlslangTargetEnv { |
| GlslangTargetEnv(const spv_target_env env) { |
| switch (env) { |
| case SPV_ENV_UNIVERSAL_1_0: |
| language_version = glslang::EShTargetSpv_1_0; |
| break; |
| case SPV_ENV_UNIVERSAL_1_1: |
| language_version = glslang::EShTargetSpv_1_1; |
| break; |
| case SPV_ENV_UNIVERSAL_1_2: |
| language_version = glslang::EShTargetSpv_1_2; |
| break; |
| case SPV_ENV_UNIVERSAL_1_3: |
| language_version = glslang::EShTargetSpv_1_3; |
| break; |
| case SPV_ENV_UNIVERSAL_1_4: |
| language_version = glslang::EShTargetSpv_1_4; |
| break; |
| case SPV_ENV_UNIVERSAL_1_5: |
| language_version = glslang::EShTargetSpv_1_5; |
| break; |
| case SPV_ENV_UNIVERSAL_1_6: |
| language_version = glslang::EShTargetSpv_1_6; |
| break; |
| case SPV_ENV_VULKAN_1_0: |
| client_version = glslang::EShTargetVulkan_1_0; |
| break; |
| case SPV_ENV_VULKAN_1_1: |
| client_version = glslang::EShTargetVulkan_1_1; |
| language_version = glslang::EShTargetSpv_1_3; |
| break; |
| case SPV_ENV_VULKAN_1_2: |
| client_version = glslang::EShTargetVulkan_1_2; |
| language_version = glslang::EShTargetSpv_1_5; |
| break; |
| case SPV_ENV_VULKAN_1_3: |
| client_version = glslang::EShTargetVulkan_1_3; |
| language_version = glslang::EShTargetSpv_1_6; |
| break; |
| default: |
| assert(false && "Invalid SPIR-V environment"); |
| break; |
| } |
| } |
| |
| operator glslang::EShTargetLanguageVersion() const { return language_version; } |
| |
| operator glslang::EShTargetClientVersion() const { return client_version; } |
| |
| private: |
| glslang::EShTargetLanguageVersion language_version = glslang::EShTargetSpv_1_0; |
| glslang::EShTargetClientVersion client_version = glslang::EShTargetVulkan_1_0; |
| }; |
| |
| // |
| // Compile a given string containing GLSL into SPV for use by VK |
| // Return value of false means an error was encountered. |
| // |
| bool GLSLtoSPV(const VkPhysicalDeviceLimits &device_limits, const VkShaderStageFlagBits shader_type, const char *p_shader, |
| std::vector<uint32_t> &spirv, const spv_target_env spv_env) { |
| TBuiltInResource resources; |
| ProcessConfigFile(device_limits, resources); |
| |
| EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); |
| EShLanguage stage = FindLanguage(shader_type); |
| glslang::TShader shader(stage); |
| GlslangTargetEnv glslang_env(spv_env); |
| shader.setEnvTarget(glslang::EshTargetSpv, glslang_env); |
| shader.setEnvClient(glslang::EShClientVulkan, glslang_env); |
| |
| const char *shader_strings[1]; |
| shader_strings[0] = p_shader; |
| shader.setStrings(shader_strings, 1); |
| |
| if (!shader.parse(&resources, 100, false, messages)) { |
| puts(shader.getInfoLog()); |
| puts(shader.getInfoDebugLog()); |
| return false; // something didn't work |
| } |
| |
| glslang::TProgram program; |
| program.addShader(&shader); |
| |
| if (!program.link(messages)) { |
| puts(shader.getInfoLog()); |
| puts(shader.getInfoDebugLog()); |
| return false; |
| } |
| |
| glslang::SpvOptions spv_options; |
| glslang::GlslangToSpv(*program.getIntermediate(stage), spirv, &spv_options); |
| |
| return true; |
| } |
| |
| // |
| // Compile a given string containing SPIR-V assembly into SPV for use by VK |
| // Return value of false means an error was encountered. |
| // |
| bool ASMtoSPV(const spv_target_env target_env, const uint32_t options, const char *p_asm, std::vector<uint32_t> &spv) { |
| spv_binary binary; |
| spv_diagnostic diagnostic = nullptr; |
| spv_context context = spvContextCreate(target_env); |
| spv_result_t error = spvTextToBinaryWithOptions(context, p_asm, strlen(p_asm), options, &binary, &diagnostic); |
| spvContextDestroy(context); |
| if (error) { |
| spvDiagnosticPrint(diagnostic); |
| spvDiagnosticDestroy(diagnostic); |
| return false; |
| } |
| spv.insert(spv.end(), binary->code, binary->code + binary->wordCount); |
| spvBinaryDestroy(binary); |
| |
| return true; |
| } |
| |
| void CheckSlangSupport() { |
| #ifndef VVL_USE_SLANG |
| GTEST_SKIP() << "Slang not supported on this platform"; |
| #endif |
| } |
| |
| bool SlangToSPV(const spv_target_env target_env, const char* slang_shader, const char* entry_point_name, |
| std::vector<uint8_t>& out_bytes) { |
| #ifndef VVL_USE_SLANG |
| (void)target_env; |
| (void)slang_shader; |
| (void)entry_point_name; |
| (void)out_bytes; |
| return false; |
| #else |
| // Function adapted from |
| // https://github.com/shader-slang/slang/blob/master/examples/hello-world/main.cpp#L114 |
| // Used https://github.com/shader-slang/slang/issues/6678 to figure out how to use |
| // `slang::IModule::loadModuleFromSourceString` |
| |
| using namespace Slang; |
| |
| // First we need to create slang global session with work with the Slang API. |
| ComPtr<slang::IGlobalSession> slang_session; |
| { |
| SlangGlobalSessionDesc slang_session_desc = {}; |
| // slang_session_desc.enableGLSL = true; // Could be needed in the future |
| slang::createGlobalSession(&slang_session_desc, slang_session.writeRef()); |
| } |
| |
| // Next we create a compilation session to generate SPIRV code from Slang source. |
| slang::TargetDesc targetDesc = {}; |
| targetDesc.format = SLANG_SPIRV; |
| // Default currently is spirv_1_3 (vulkan 1.1) as that is what Slang does by default |
| std::string profile = "spirv_1_3"; |
| if (target_env == SPV_ENV_VULKAN_1_2) { |
| profile = "spirv_1_5"; |
| } else if (target_env == SPV_ENV_VULKAN_1_3 || target_env == SPV_ENV_VULKAN_1_4) { |
| profile = "spirv_1_6"; |
| } |
| targetDesc.profile = slang_session->findProfile(profile.c_str()); |
| targetDesc.flags = 0; |
| slang::SessionDesc sessionDesc = {}; |
| sessionDesc.targets = &targetDesc; |
| sessionDesc.targetCount = 1; |
| |
| std::vector<slang::CompilerOptionEntry> options; |
| options.push_back( |
| {slang::CompilerOptionName::EmitSpirvDirectly, {slang::CompilerOptionValueKind::Int, 1, 0, nullptr, nullptr}}); |
| // https://github.com/shader-slang/slang/issues/6248 |
| options.push_back( |
| {slang::CompilerOptionName::VulkanUseEntryPointName, {slang::CompilerOptionValueKind::Int, 1, 0, nullptr, nullptr}}); |
| sessionDesc.compilerOptionEntries = options.data(); |
| sessionDesc.compilerOptionEntryCount = options.size(); |
| |
| ComPtr<slang::ISession> session; |
| SlangResult result = slang_session->createSession(sessionDesc, session.writeRef()); |
| if (result != 0) { |
| ADD_FAILURE() << "Slang failure: slang_session->createSession()"; |
| } |
| |
| // Handle errors |
| Slang::ComPtr<ISlangBlob> diagnostics; |
| |
| // Compile the source code |
| // Once the session has been obtained, we can start loading code into it. |
| // Here we use `loadModuleFromSourceString` and not `loadModule`. |
| // moduleName and path are just placeholders |
| Slang::ComPtr<slang::IModule> slang_module; |
| slang_module = session->loadModuleFromSourceString("my_shader", "my_shader.slang", slang_shader, diagnostics.writeRef()); |
| if (slang_module == NULL) { |
| ADD_FAILURE() << "Slang failure: loadModuleFromSourceString()\n" << ((const char *)diagnostics->getBufferPointer()); |
| return false; |
| } |
| |
| // Loading the module will compile and check all the shader code in it, |
| // including the shader entry points we want to use. Now that the module is loaded |
| // we can look up those entry points by name. |
| // |
| // Note: If you are using this `loadModule` approach to load your shader code it is |
| // important to tag your entry point functions with the `[shader("...")]` attribute |
| // (e.g., `[shader("compute")] void computeMain(...)`). Without that information there |
| // is no unambiguous way for the compiler to know which functions represent entry |
| // points when it parses your code via `loadModule()`. |
| // |
| ComPtr<slang::IEntryPoint> entry_point; |
| result = slang_module->findEntryPointByName(entry_point_name, entry_point.writeRef()); |
| if (result != 0) { |
| ADD_FAILURE() << "Slang failure: loadModuleFromSourceString()\nCould not find entry point \"" << entry_point_name << "\""; |
| return false; |
| } |
| |
| // At this point we have a few different Slang API objects that represent |
| // pieces of our code: `module`, `vertexEntryPoint`, and `fragmentEntryPoint`. |
| // |
| // A single Slang module could contain many different entry points (e.g., |
| // four vertex entry points, three fragment entry points, and two compute |
| // shaders), and before we try to generate output code for our target API |
| // we need to identify which entry points we plan to use together. |
| // |
| // Modules and entry points are both examples of *component types* in the |
| // Slang API. The API also provides a way to build a *composite* out of |
| // other pieces, and that is what we are going to do with our module |
| // and entry points. |
| // |
| std::vector<slang::IComponentType *> componentTypes; |
| componentTypes.emplace_back(slang_module); |
| componentTypes.emplace_back(entry_point); |
| |
| // Actually creating the composite component type is a single operation |
| // on the Slang session, but the operation could potentially fail if |
| // something about the composite was invalid (e.g., you are trying to |
| // combine multiple copies of the same module), so we need to deal |
| // with the possibility of diagnostic output. |
| // |
| ComPtr<slang::IComponentType> composedProgram; |
| { |
| result = session->createCompositeComponentType(componentTypes.data(), (SlangInt)componentTypes.size(), |
| composedProgram.writeRef(), diagnostics.writeRef()); |
| if (result != 0) { |
| ADD_FAILURE() << "Slang failure: createCompositeComponentType()\n" << ((const char *)diagnostics->getBufferPointer()); |
| return false; |
| } |
| } |
| |
| // Now we can call `composedProgram->getEntryPointCode()` to retrieve the |
| // compiled SPIRV code that we will use to create a vulkan compute pipeline. |
| // This will trigger the final Slang compilation and spirv code generation. |
| ComPtr<slang::IBlob> spirvCode; |
| { |
| result = composedProgram->getEntryPointCode(0, 0, spirvCode.writeRef(), diagnostics.writeRef()); |
| if (result != 0) { |
| ADD_FAILURE() << "Slang failure: createCompositeComponentType()\n" << ((const char *)diagnostics->getBufferPointer()); |
| return false; |
| } |
| } |
| |
| out_bytes.resize(spirvCode->getBufferSize()); |
| std::memcpy(out_bytes.data(), spirvCode->getBufferPointer(), spirvCode->getBufferSize()); |
| |
| return true; |
| #endif |
| } |
| |
| VkPipelineShaderStageCreateInfo const &VkShaderObj::GetStageCreateInfo() const { return m_stage_info; } |
| |
| VkShaderObj::VkShaderObj(vkt::Device& device, const char* source, VkShaderStageFlagBits stage, const spv_target_env env, |
| SpvSourceType source_type, const VkSpecializationInfo* spec_info, const char* entry_point, |
| const void* shader_module_ci_pNext, const void* pipeline_shader_stage_ci_pNext) |
| : m_device(&device), m_source(source), m_spv_env(env) { |
| m_stage_info = vku::InitStructHelper(); |
| m_stage_info.flags = 0; |
| m_stage_info.stage = stage; |
| m_stage_info.module = VK_NULL_HANDLE; |
| m_stage_info.pName = entry_point; |
| m_stage_info.pSpecializationInfo = spec_info; |
| m_stage_info.pNext = pipeline_shader_stage_ci_pNext; |
| if (source_type == SPV_SOURCE_GLSL) { |
| InitFromGLSL(shader_module_ci_pNext); |
| } else if (source_type == SPV_SOURCE_ASM) { |
| InitFromASM(); |
| } else if (source_type == SPV_SOURCE_SLANG) { |
| InitFromSlang(); |
| } |
| } |
| |
| bool VkShaderObj::InitFromGLSL(const void* shader_module_ci_pNext) { |
| std::vector<uint32_t> spv; |
| GLSLtoSPV(m_device->Physical().limits_, m_stage_info.stage, m_source, spv, m_spv_env); |
| |
| VkShaderModuleCreateInfo module_ci = vku::InitStructHelper(); |
| module_ci.pNext = shader_module_ci_pNext; |
| module_ci.codeSize = spv.size() * sizeof(uint32_t); |
| module_ci.pCode = spv.data(); |
| |
| Init(*m_device, module_ci); |
| m_stage_info.module = handle(); |
| return VK_NULL_HANDLE != handle(); |
| } |
| |
| // Because shaders are currently validated at pipeline creation time, there are test cases that might fail shader module |
| // creation due to supplying an invalid/unknown SPIR-V capability/operation. This is called after VkShaderObj creation when |
| // tests are found to crash on a CI device |
| VkResult VkShaderObj::InitFromGLSLTry(const vkt::Device *custom_device) { |
| std::vector<uint32_t> spv; |
| // 99% of tests just use the framework's VkDevice, but this allows for tests to use custom device object |
| // Can't set at contructor time since all reference members need to be initialized then. |
| VkPhysicalDeviceLimits limits = (custom_device) ? custom_device->Physical().limits_ : m_device->Physical().limits_; |
| GLSLtoSPV(limits, m_stage_info.stage, m_source, spv, m_spv_env); |
| |
| VkShaderModuleCreateInfo moduleCreateInfo = vku::InitStructHelper(); |
| moduleCreateInfo.codeSize = spv.size() * sizeof(uint32_t); |
| moduleCreateInfo.pCode = spv.data(); |
| |
| const auto result = InitTry(((custom_device) ? *custom_device : *m_device), moduleCreateInfo); |
| m_stage_info.module = handle(); |
| return result; |
| } |
| |
| bool VkShaderObj::InitFromASM() { |
| std::vector<uint32_t> spv; |
| ASMtoSPV(m_spv_env, 0, m_source, spv); |
| |
| VkShaderModuleCreateInfo moduleCreateInfo = vku::InitStructHelper(); |
| moduleCreateInfo.codeSize = spv.size() * sizeof(uint32_t); |
| moduleCreateInfo.pCode = spv.data(); |
| |
| Init(*m_device, moduleCreateInfo); |
| m_stage_info.module = handle(); |
| return VK_NULL_HANDLE != handle(); |
| } |
| |
| VkResult VkShaderObj::InitFromASMTry() { |
| std::vector<uint32_t> spv; |
| ASMtoSPV(m_spv_env, 0, m_source, spv); |
| |
| VkShaderModuleCreateInfo moduleCreateInfo = vku::InitStructHelper(); |
| moduleCreateInfo.codeSize = spv.size() * sizeof(uint32_t); |
| moduleCreateInfo.pCode = spv.data(); |
| |
| const auto result = InitTry(*m_device, moduleCreateInfo); |
| m_stage_info.module = handle(); |
| return result; |
| } |
| |
| bool VkShaderObj::InitFromSlang() { |
| #ifndef VVL_USE_SLANG |
| return false; |
| #else |
| std::vector<uint8_t> bytes; |
| if (!SlangToSPV(m_spv_env, m_source, m_stage_info.pName, bytes)) { |
| return false; |
| } |
| VkShaderModuleCreateInfo module_ci = vku::InitStructHelper(); |
| module_ci.codeSize = bytes.size(); |
| module_ci.pCode = (uint32_t *)bytes.data(); |
| |
| const auto result = InitTry(*m_device, module_ci); |
| m_stage_info.module = handle(); |
| return result == VK_SUCCESS; |
| #endif |
| } |
| |
| // static |
| VkShaderObj VkShaderObj::CreateFromGLSL(VkRenderFramework *framework, const char *source, VkShaderStageFlagBits stage, |
| const spv_target_env spv_env, const VkSpecializationInfo *spec_info, |
| const char *entry_point) { |
| auto shader = VkShaderObj(*framework->DeviceObj(), source, stage, spv_env, SPV_SOURCE_GLSL_TRY, spec_info, entry_point); |
| if (VK_SUCCESS == shader.InitFromGLSLTry()) { |
| return shader; |
| } |
| return {}; |
| } |
| |
| // static |
| VkShaderObj VkShaderObj::CreateFromASM(VkRenderFramework *framework, const char *source, VkShaderStageFlagBits stage, |
| const spv_target_env spv_env, const VkSpecializationInfo *spec_info, |
| const char *entry_point) { |
| auto shader = VkShaderObj(*framework->DeviceObj(), source, stage, spv_env, SPV_SOURCE_ASM_TRY, spec_info, entry_point); |
| if (VK_SUCCESS == shader.InitFromASMTry()) { |
| return shader; |
| } |
| return {}; |
| } |