// // Copyright 2016 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ShaderVk.cpp: // Implements the class methods for ShaderVk. // #include "libANGLE/renderer/vulkan/ShaderVk.h" #include "common/debug.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/renderer/vulkan/ContextVk.h" namespace rx { ShaderVk::ShaderVk(const gl::ShaderState &state) : ShaderImpl(state) {} ShaderVk::~ShaderVk() {} std::shared_ptr ShaderVk::compile(const gl::Context *context, ShCompileOptions *options) { ContextVk *contextVk = vk::GetImpl(context); if (context->isWebGL()) { // Only WebGL requires initialization of local variables, others don't. // Extra initialization in spirv shader may affect performance. options->initializeUninitializedLocals = true; // WebGL shaders may contain OOB array accesses which in turn cause undefined behavior, // which may result in security issues. See https://crbug.com/1189110. options->clampIndirectArrayBounds = true; if (mState.getShaderType() != gl::ShaderType::Compute) { options->initOutputVariables = true; } } if (contextVk->getFeatures().supportsSPIRV14.enabled) { options->emitSPIRV14 = true; } if (contextVk->getFeatures().retainSPIRVDebugInfo.enabled) { options->outputDebugInfo = true; } // robustBufferAccess on Vulkan doesn't support bound check on shader local variables // but the GL_EXT_robustness does support. // Enable the flag clampIndirectArrayBounds to ensure out of bounds local variable writes in // shaders are protected when the context has GL_EXT_robustness enabled if (contextVk->getShareGroup()->hasAnyContextWithRobustness()) { options->clampIndirectArrayBounds = true; } if (contextVk->getFeatures().clampPointSize.enabled) { options->clampPointSize = true; } if (contextVk->getFeatures().emulateAdvancedBlendEquations.enabled) { options->addAdvancedBlendEquationsEmulation = true; } if (!contextVk->getFeatures().enablePrecisionQualifiers.enabled) { options->ignorePrecisionQualifiers = true; } if (contextVk->getFeatures().forceFragmentShaderPrecisionHighpToMediump.enabled) { options->forceShaderPrecisionHighpToMediump = true; } // Let compiler use specialized constant for pre-rotation. if (!contextVk->getFeatures().preferDriverUniformOverSpecConst.enabled) { options->useSpecializationConstant = true; } if (contextVk->getFeatures().clampFragDepth.enabled) { options->clampFragDepth = true; } if (!contextVk->getFeatures().supportsDepthClipControl.enabled) { options->addVulkanDepthCorrection = true; } if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled) { options->addVulkanXfbExtensionSupportCode = true; } else if (mState.getShaderType() == gl::ShaderType::Vertex && contextVk->getFeatures().emulateTransformFeedback.enabled) { options->addVulkanXfbEmulationSupportCode = true; } if (contextVk->getFeatures().roundOutputAfterDithering.enabled) { options->roundOutputAfterDithering = true; } if (contextVk->getFeatures().appendAliasedMemoryDecorations.enabled) { options->aliasedUnlessRestrict = true; } if (contextVk->getFeatures().explicitlyCastMediumpFloatTo16Bit.enabled) { options->castMediumpFloatTo16Bit = true; } if (contextVk->getExtensions().shaderPixelLocalStorageANGLE) { options->pls = contextVk->getNativePixelLocalStorageOptions(); } if (contextVk->getFeatures().avoidOpSelectWithMismatchingRelaxedPrecision.enabled) { options->avoidOpSelectWithMismatchingRelaxedPrecision = true; } if (contextVk->getFeatures().wrapSwitchInIfTrue.enabled) { options->wrapSwitchInIfTrue = true; } if (contextVk->getFeatures().emulateR32fImageAtomicExchange.enabled) { options->emulateR32fImageAtomicExchange = true; } // The Vulkan backend needs no post-processing of the translated shader. return std::shared_ptr(new ShaderTranslateTask); } std::shared_ptr ShaderVk::load(const gl::Context *context, gl::BinaryInputStream *stream) { return std::shared_ptr(new ShaderTranslateTask); } std::string ShaderVk::getDebugInfo() const { const sh::BinaryBlob &spirv = mState.getCompiledState()->compiledBinary; if (spirv.empty()) { return ""; } std::ostringstream blob; if (!mState.getCompiledState()->inputVaryings.empty()) { blob << "Inputs:"; for (const sh::ShaderVariable &var : mState.getCompiledState()->inputVaryings) { blob << " " << var.name; } blob << std::endl; } if (!mState.getCompiledState()->activeAttributes.empty()) { blob << "Inputs:"; for (const sh::ShaderVariable &var : mState.getCompiledState()->activeAttributes) { blob << " " << var.name; } blob << std::endl; } if (!mState.getCompiledState()->outputVaryings.empty()) { blob << "Outputs:"; for (const sh::ShaderVariable &var : mState.getCompiledState()->outputVaryings) { blob << " " << var.name; } blob << std::endl; } if (!mState.getCompiledState()->activeOutputVariables.empty()) { blob << "Outputs:"; for (const sh::ShaderVariable &var : mState.getCompiledState()->activeOutputVariables) { blob << " " << var.name; } blob << std::endl; } if (!mState.getCompiledState()->uniforms.empty()) { blob << "Uniforms:"; for (const sh::ShaderVariable &var : mState.getCompiledState()->uniforms) { blob << " " << var.name; } blob << std::endl; } if (!mState.getCompiledState()->uniformBlocks.empty()) { blob << "Uniform blocks:"; for (const sh::InterfaceBlock &block : mState.getCompiledState()->uniformBlocks) { blob << " " << block.name; } blob << std::endl; } if (!mState.getCompiledState()->shaderStorageBlocks.empty()) { blob << "Storage blocks:"; for (const sh::InterfaceBlock &block : mState.getCompiledState()->shaderStorageBlocks) { blob << " " << block.name; } blob << std::endl; } blob << R"( Paste the following SPIR-V binary in https://www.khronos.org/spir/visualizer/ or pass to a recent build of `spirv-dis` (optionally with `--comment --nested-indent`) Setting the environment variable ANGLE_FEATURE_OVERRIDES_ENABLED=retainSPIRVDebugInfo will retain debug info )"; constexpr size_t kIndicesPerRow = 10; size_t rowOffset = 0; for (size_t index = 0; index < spirv.size(); ++index, ++rowOffset) { if (rowOffset == kIndicesPerRow) { blob << std::endl; rowOffset = 0; } blob << "0x" << std::uppercase << std::setfill('0') << std::setw(8) << std::hex << spirv[index] << ","; } return blob.str(); } } // namespace rx