Commit e5385ea9 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Compile shaders at link time

Since line raster emulation was changed to use specialization constants, it has been possible to compile shaders at link time. However, program pipeline objects would have required keeping the shader sources around for recompilation. Now that all necessary decorations are modified directly in SPIR-V, it's possible to compile the shaders at link time and forget about their sources. Program pipeline objects then simply "reconfigure" the generated SPIR-V. A next step could be to also create the Vulkan pipeline object at link time. A number of failures due to gaps in CTS testing prevent that work currently. In particular, in some situations the generated SPIR-V is not per spec, for example it may contain vertex attributes with aliasing locations, or have transform feedback capture of array elements misconfigured. Bug: angleproject:3394 Bug: angleproject:4253 Change-Id: I54c0884cf056b511a4a306225cc6ed2cef84d257 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2023186 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent f1b2c4be
......@@ -186,8 +186,10 @@ class BinaryOutputStream : angle::NonCopyable
template <class IntT>
void writeInt(IntT param)
{
ASSERT(angle::IsValueInRangeForNumericType<int>(param));
int intValue = static_cast<int>(param);
using PromotedIntT =
typename std::conditional<std::is_signed<IntT>::value, int, unsigned>::type;
ASSERT(angle::IsValueInRangeForNumericType<PromotedIntT>(param));
PromotedIntT intValue = static_cast<PromotedIntT>(param);
write(&intValue, 1);
}
......
......@@ -1610,7 +1610,7 @@ angle::Result Program::link(const Context *context)
}
if (!resources->varyingPacking.collectAndPackUserVaryings(
mInfoLog, mergedVaryings, mState.getTransformFeedbackVaryingNames()))
mInfoLog, mergedVaryings, mState.getTransformFeedbackVaryingNames(), isSeparable()))
{
return angle::Result::Continue;
}
......
......@@ -433,7 +433,8 @@ void VaryingPacking::packUserVaryingFieldTF(const ProgramVaryingRef &ref,
bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings)
const std::vector<std::string> &tfVaryings,
const bool isSeparableProgram)
{
VaryingUniqueFullNames uniqueFullNames;
mPackedVaryings.clear();
......@@ -450,7 +451,8 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
// optimizations" may be used to make vertex shader outputs fit.
if ((input && output && output->staticUse) ||
(input && input->isBuiltIn() && input->active) ||
(output && output->isBuiltIn() && output->active))
(output && output->isBuiltIn() && output->active) ||
(isSeparableProgram && ((input && input->active) || (output && output->active))))
{
const sh::ShaderVariable *varying = output ? output : input;
......@@ -485,7 +487,7 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
}
// If the varying is not used in the input, we know it is inactive.
if (!input)
if (!input && !isSeparableProgram)
{
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
continue;
......
......@@ -72,7 +72,11 @@ struct PackedVarying : angle::NonCopyable
PackedVarying &operator=(PackedVarying &&other);
bool isStructField() const { return !frontVarying.parentStructName.empty(); }
bool isStructField() const
{
return frontVarying.varying ? !frontVarying.parentStructName.empty()
: !backVarying.parentStructName.empty();
}
bool isArrayElement() const { return arrayIndex != GL_INVALID_INDEX; }
......@@ -190,7 +194,8 @@ class VaryingPacking final : angle::NonCopyable
bool collectAndPackUserVaryings(gl::InfoLog &infoLog,
const ProgramMergedVaryings &mergedVaryings,
const std::vector<std::string> &tfVaryings);
const std::vector<std::string> &tfVaryings,
const bool isSeparableProgram);
struct Register
{
......
......@@ -964,8 +964,7 @@ bool ValidateSpirv(const std::vector<uint32_t> &spirvBlob)
if (!result)
{
std::string readableSpirv;
result = spirvTools.Disassemble(spirvBlob, &readableSpirv,
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
spirvTools.Disassemble(spirvBlob, &readableSpirv, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
WARN() << "Invalid SPIR-V:\n" << readableSpirv;
}
......
......@@ -132,7 +132,9 @@ class ProgramMtl : public ProgramImpl
void reset(ContextMtl *context);
void linkResources(const gl::ProgramLinkedResources &resources);
angle::Result linkImpl(const gl::Context *glContext, gl::InfoLog &infoLog);
angle::Result linkImpl(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog);
angle::Result convertToMsl(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
......@@ -161,10 +163,6 @@ class ProgramMtl : public ProgramImpl
gl::ShaderBitSet mSamplerBindingsDirty;
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
// We keep the translated linked shader sources to use with shader draw call patching.
gl::ShaderMap<std::string> mShaderSource;
ShaderInterfaceVariableInfoMap mVariableInfoMap;
mtl::RenderPipelineCache mMetalRenderPipelineCache;
};
......
......@@ -295,13 +295,13 @@ std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
// assignment done in that function.
linkResources(resources);
mtl::GlslangGetShaderSource(mState, resources, &mShaderSource, &mVariableInfoMap);
// NOTE(hqle): Parallelize linking.
return std::make_unique<LinkEventDone>(linkImpl(context, infoLog));
return std::make_unique<LinkEventDone>(linkImpl(context, resources, infoLog));
}
angle::Result ProgramMtl::linkImpl(const gl::Context *glContext, gl::InfoLog &infoLog)
angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
// NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
......@@ -310,10 +310,15 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext, gl::InfoLog &in
ANGLE_TRY(initDefaultUniformBlocks(glContext));
// Gather variable info and transform sources.
gl::ShaderMap<std::string> shaderSources;
ShaderInterfaceVariableInfoMap variableInfoMap;
mtl::GlslangGetShaderSource(mState, resources, &shaderSources, &variableInfoMap);
// Convert GLSL to spirv code
gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
ANGLE_TRY(mtl::GlslangGetShaderSpirvCode(contextMtl, contextMtl->getCaps(), mShaderSource,
mVariableInfoMap, &shaderCodes));
ANGLE_TRY(mtl::GlslangGetShaderSpirvCode(contextMtl, contextMtl->getCaps(), shaderSources,
variableInfoMap, &shaderCodes));
// Convert spirv code to MSL
ANGLE_TRY(convertToMsl(glContext, gl::ShaderType::Vertex, infoLog,
......
......@@ -367,13 +367,12 @@ ProgramVk::ShaderInfo::~ShaderInfo() = default;
angle::Result ProgramVk::ShaderInfo::initShaders(
ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
const ShaderInterfaceVariableInfoMap &variableInfoMap)
{
ASSERT(!valid());
ANGLE_TRY(GlslangWrapperVk::GetShaderCode(contextVk, contextVk->getCaps(), shaderSources,
variableInfoMap, spirvBlobsOut));
variableInfoMap, &mSpirvBlobs));
mIsInitialized = true;
return angle::Result::Continue;
......@@ -388,6 +387,34 @@ void ProgramVk::ShaderInfo::release(ContextVk *contextVk)
mIsInitialized = false;
}
void ProgramVk::ShaderInfo::load(gl::BinaryInputStream *stream)
{
// Read in shader codes for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
SpirvBlob *spirvBlob = &mSpirvBlobs[shaderType];
// Read the SPIR-V
stream->readIntVector<uint32_t>(spirvBlob);
}
mIsInitialized = true;
}
void ProgramVk::ShaderInfo::save(gl::BinaryOutputStream *stream)
{
ASSERT(valid());
// Write out shader codes for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
const SpirvBlob &spirvBlob = mSpirvBlobs[shaderType];
// Write the SPIR-V
stream->writeIntVector(spirvBlob);
}
}
// ProgramVk::ProgramInfo implementation.
ProgramVk::ProgramInfo::ProgramInfo() {}
......@@ -432,78 +459,6 @@ void ProgramVk::ProgramInfo::release(ContextVk *contextVk)
}
}
angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStream *stream)
{
// Read in shader codes for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
// Read the shader source
mShaderSources[shaderType] = stream->readString();
SpirvBlob *spirvBlob = &mShaderInfo.getSpirvBlobs()[shaderType];
// Read the SPIR-V
stream->readIntVector<uint32_t>(spirvBlob);
}
// Read the expected bindings
size_t infoCount = stream->readInt<size_t>();
for (size_t i = 0; i < infoCount; ++i)
{
std::string varName = stream->readString();
ShaderInterfaceVariableInfo info;
info.descriptorSet = stream->readInt<uint32_t>();
info.binding = stream->readInt<uint32_t>();
info.activeStages = gl::ShaderBitSet(static_cast<uint8_t>(stream->readInt<uint32_t>()));
info.xfbBuffer = stream->readInt<uint32_t>();
info.xfbOffset = stream->readInt<uint32_t>();
info.xfbStride = stream->readInt<uint32_t>();
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
info.location[shaderType] = stream->readInt<uint32_t>();
info.component[shaderType] = stream->readInt<uint32_t>();
}
mVariableInfoMap[varName] = info;
}
return angle::Result::Continue;
}
void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream)
{
// Write out shader codes for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
// Write the shader source
stream->writeString(mShaderSources[shaderType]);
const SpirvBlob &spirvBlob = mShaderInfo.getSpirvBlobs()[shaderType];
// Write the SPIR-V
stream->writeIntVector(spirvBlob);
}
// Write the expected bindings
stream->writeInt(mVariableInfoMap.size());
for (const auto &nameInfo : mVariableInfoMap)
{
stream->writeString(nameInfo.first);
stream->writeIntOrNegOne(nameInfo.second.descriptorSet);
stream->writeIntOrNegOne(nameInfo.second.binding);
stream->writeIntOrNegOne(nameInfo.second.activeStages.bits());
stream->writeIntOrNegOne(nameInfo.second.xfbBuffer);
stream->writeIntOrNegOne(nameInfo.second.xfbOffset);
stream->writeIntOrNegOne(nameInfo.second.xfbStride);
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
stream->writeIntOrNegOne(nameInfo.second.location[shaderType]);
stream->writeIntOrNegOne(nameInfo.second.component[shaderType]);
}
}
}
// ProgramVk implementation.
ProgramVk::DefaultUniformBlock::DefaultUniformBlock() {}
......@@ -571,11 +526,9 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
angle::Result status = loadSpirvBlob(contextVk, stream);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
reset(contextVk);
mShaderInfo.load(stream);
// Deserializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
......@@ -595,10 +548,8 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
requiredBufferSize[shaderType] = stream->readInt<size_t>();
}
reset(contextVk);
// Initialize and resize the mDefaultUniformBlocks' memory
status = resizeUniformBlockMemory(contextVk, requiredBufferSize);
angle::Result status = resizeUniformBlockMemory(contextVk, requiredBufferSize);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
......@@ -609,9 +560,7 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
void ProgramVk::save(const gl::Context *context, gl::BinaryOutputStream *stream)
{
// (geofflang): Look into saving shader modules in ShaderInfo objects (keep in mind that we
// compile shaders lazily)
saveSpirvBlob(stream);
mShaderInfo.save(stream);
// Serializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
......@@ -652,12 +601,22 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
// assignment done in that function.
linkResources(resources);
reset(contextVk);
// Gather variable info and transform sources.
gl::ShaderMap<std::string> shaderSources;
ShaderInterfaceVariableInfoMap variableInfoMap;
GlslangWrapperVk::GetShaderSource(contextVk->getRenderer()->getFeatures(), mState, resources,
&mShaderSources, &mVariableInfoMap);
&shaderSources, &variableInfoMap);
reset(contextVk);
// Compile the shaders.
angle::Result status = mShaderInfo.initShaders(contextVk, shaderSources, variableInfoMap);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
}
angle::Result status = initDefaultUniformBlocks(context);
status = initDefaultUniformBlocks(context);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
......
......@@ -219,13 +219,6 @@ class ProgramVk : public ProgramImpl
ProgramInfo *programInfo,
vk::ShaderProgramHelper **shaderProgramOut)
{
// Compile shaders if not already. This is done only once regardless of specialization
// constants.
if (!mShaderInfo.valid())
{
ANGLE_TRY(mShaderInfo.initShaders(contextVk, mShaderSources, mVariableInfoMap,
&mShaderInfo.getSpirvBlobs()));
}
ASSERT(mShaderInfo.valid());
// Create the program pipeline. This is done lazily and once per combination of
......@@ -258,10 +251,6 @@ class ProgramVk : public ProgramImpl
return initProgram(contextVk, false, &mDefaultProgramInfo, shaderProgramOut);
}
// Save and load implementation for GLES Program Binary support.
angle::Result loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStream *stream);
void saveSpirvBlob(gl::BinaryOutputStream *stream);
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
{
......@@ -314,15 +303,17 @@ class ProgramVk : public ProgramImpl
angle::Result initShaders(ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
const ShaderInterfaceVariableInfoMap &variableInfoMap);
void release(ContextVk *contextVk);
ANGLE_INLINE bool valid() const { return mIsInitialized; }
gl::ShaderMap<SpirvBlob> &getSpirvBlobs() { return mSpirvBlobs; }
const gl::ShaderMap<SpirvBlob> &getSpirvBlobs() const { return mSpirvBlobs; }
// Save and load implementation for GLES Program Binary support.
void load(gl::BinaryInputStream *stream);
void save(gl::BinaryOutputStream *stream);
private:
gl::ShaderMap<SpirvBlob> mSpirvBlobs;
bool mIsInitialized = false;
......@@ -351,12 +342,6 @@ class ProgramVk : public ProgramImpl
ProgramInfo mDefaultProgramInfo;
ProgramInfo mLineRasterProgramInfo;
// We keep the translated linked shader sources and the expected location/set/binding mapping to
// use with shader draw call compilation.
// TODO(syoussefi): Remove when shader compilation is done at link time.
// http://anglebug.com/3394
gl::ShaderMap<std::string> mShaderSources;
ShaderInterfaceVariableInfoMap mVariableInfoMap;
// We keep the SPIR-V code to use for draw call pipeline creation.
ShaderInfo mShaderInfo;
......
......@@ -534,11 +534,6 @@
// General Vulkan failures
// ANGLEPosition location assignment overlaps other varyings. This only affects platforms that
// don't support the bresenham line raster extension, which in turn also depends on the driver
// versions.
4251 VULKAN NVIDIA : dEQP-GLES3.functional.shaders.linkage.varying.struct.float_uvec2_vec3 = FAIL
// Tests failing due to the "flat" qualifier in the shader:
3677 VULKAN : dEQP-GLES3.functional.fragment_out.basic.int.* = FAIL
3677 VULKAN : dEQP-GLES3.functional.fragment_out.basic.uint.* = FAIL
......
......@@ -73,6 +73,8 @@
// --gtest_filter=dEQP.KHR_GLES31/core_texture_storage_multisample_FunctionalTests_verify_sample_masking_for_non_integer_color_renderable_internalformats* --use-angle=swiftshader
4259 SWIFTSHADER : KHR-GLES31.core.texture_storage_multisample.FunctionalTests.verify_sample_masking_for_non_integer_color_renderable_internalformats = FAIL
// Swiftshader uses "old" sampler rewrite that doesn't support array of arrays
4071 SWIFTSHADER : KHR-GLES31.core.arrays_of_arrays.* = SKIP
////
//// General Vulkan expectations
......@@ -135,3 +137,6 @@
// Fails on Pixel2 and Pixel2 XL
4159 VULKAN PIXEL2ORXL : KHR-GLES31.core.draw_indirect.advanced-twoPass-transformFeedback-elements = FAIL
4159 VULKAN PIXEL2ORXL : KHR-GLES31.core.draw_indirect.advanced-twoPass-transformFeedback-arrays = FAIL
// Android uses "old" sampler rewrite that doesn't support array of arrays
2703 VULKAN ANDROID : KHR-GLES31.core.arrays_of_arrays.* = SKIP
......@@ -2246,20 +2246,21 @@ TEST_P(GLSLTest_ES3, AmbiguousFunctionCall2x2)
TEST_P(GLSLTest_ES3, LargeNumberOfFloat4Parameters)
{
std::stringstream vertexShaderStream;
const unsigned int paramCount = 1024u;
// Note: SPIR-V doesn't allow more than 255 parameters to a function.
const unsigned int paramCount = IsVulkan() ? 255u : 1024u;
vertexShaderStream << "#version 300 es\n"
"precision highp float;\n"
"in vec4 a_vec;\n"
"vec4 lotsOfVec4Parameters(";
for (unsigned int i = 0; i < paramCount; ++i)
for (unsigned int i = 0; i < paramCount - 1; ++i)
{
vertexShaderStream << "vec4 a" << i << ", ";
}
vertexShaderStream << "vec4 aLast)\n"
"{\n"
" vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n";
for (unsigned int i = 0; i < paramCount; ++i)
for (unsigned int i = 0; i < paramCount - 1; ++i)
{
vertexShaderStream << " sum += a" << i << ";\n";
}
......@@ -2269,7 +2270,7 @@ TEST_P(GLSLTest_ES3, LargeNumberOfFloat4Parameters)
"void main()\n"
"{\n"
" gl_Position = lotsOfVec4Parameters(";
for (unsigned int i = 0; i < paramCount; ++i)
for (unsigned int i = 0; i < paramCount - 1; ++i)
{
vertexShaderStream << "a_vec, ";
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment