blob: 4d19bd38091c8751c8e921941096efa716ed991e [file] [log] [blame]
//
// 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<ShaderTranslateTask> 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<ShaderTranslateTask>(new ShaderTranslateTask);
}
std::shared_ptr<ShaderTranslateTask> ShaderVk::load(const gl::Context *context,
gl::BinaryInputStream *stream)
{
return std::shared_ptr<ShaderTranslateTask>(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