Commit ae09e889 by Courtney Goeltzenleuchter Committed by Commit Bot

Fix mismatch issue with precision qualifiers.

GLSL allows varyings passed from one stage to another to not match in precision (e.g. float & half-float). Vulkan doesn't allow that so adjust those mismatches to use the higher precision. To fix we keep track of the precision of varyings and in the Vulkan backend if we see they are different patch up the SPIR-V to make them match. Bug: angleproject:3078 Change-Id: I385d31e082da46ccdd4817b6612f5f9d9cbce17c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2337755 Commit-Queue: Courtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent c5b5cf6c
......@@ -994,12 +994,17 @@ class SpirvTransformer final : angle::NonCopyable
bool transformEntryPoint(const uint32_t *instruction, size_t wordCount);
bool transformDecorate(const uint32_t *instruction, size_t wordCount);
bool transformTypePointer(const uint32_t *instruction, size_t wordCount);
bool transformReturn(const uint32_t *instruction, size_t wordCount);
bool transformVariable(const uint32_t *instruction, size_t wordCount);
bool transformExecutionMode(const uint32_t *instruction, size_t wordCount);
// Any other instructions:
void writeInputPreamble();
size_t copyInstruction(const uint32_t *instruction, size_t wordCount);
uint32_t getNewId();
void writeOpLoad(uint32_t id, uint32_t typeId, uint32_t tempVarId);
void writeOpStore(uint32_t tempVarId, uint32_t destId);
void writeOpVariable(uint32_t id, uint32_t typeId, uint32_t storageClassId);
// SPIR-V to transform:
const std::vector<uint32_t> &mSpirvBlobIn;
......@@ -1017,8 +1022,11 @@ class SpirvTransformer final : angle::NonCopyable
SpirvBlob *mSpirvBlobOut;
// Traversal state:
size_t mCurrentWord = 0;
bool mIsInFunctionSection = false;
size_t mCurrentWord = 0;
bool mIsInFunctionSection = false;
bool mInsertFunctionVariables = false;
uint32_t mEntryPointId = 0;
uint32_t mOpFunctionId = 0;
// Transformation state:
......@@ -1035,7 +1043,14 @@ class SpirvTransformer final : angle::NonCopyable
// duplicated with a similar instruction but which defines a type with the Private storage
// class. If inactive varyings are encountered, its type is changed to the Private one. The
// following vector maps the Output type id to the corresponding Private one.
std::vector<uint32_t> mTypePointerTransformedId;
struct TransformedIDs
{
uint32_t privateID;
uint32_t typeID;
};
std::vector<TransformedIDs> mTypePointerTransformedId;
std::vector<uint32_t> mFixedVaryingId;
std::vector<uint32_t> mFixedVaryingTypeId;
};
bool SpirvTransformer::transform()
......@@ -1053,13 +1068,14 @@ bool SpirvTransformer::transform()
// Make sure the SpirvBlob is not reused.
ASSERT(mSpirvBlobOut->empty());
// Copy the header to SpirvBlob, we need that to be defined for SpirvTransformer::getNewId to
// work.
mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);
// First, find all necessary ids and associate them with the information required to transform
// their decorations.
resolveVariableIds();
// Copy the header to SpirvBlob
mSpirvBlobOut->assign(mSpirvBlobIn.begin(), mSpirvBlobIn.begin() + kHeaderIndexInstructions);
mCurrentWord = kHeaderIndexInstructions;
while (mCurrentWord < mSpirvBlobIn.size())
{
......@@ -1113,7 +1129,10 @@ void SpirvTransformer::resolveVariableIds()
// Allocate storage for Output type pointer map. At index i, this vector holds the identical
// type as %i except for its storage class turned to Private.
mTypePointerTransformedId.resize(indexBound + 1, 0);
// Also store a FunctionID and TypeID for when we need to fix a precision mismatch
mTypePointerTransformedId.resize(indexBound + 1, {0, 0});
mFixedVaryingId.resize(indexBound + 1, {0});
mFixedVaryingTypeId.resize(indexBound + 1, {0});
size_t currentWord = kHeaderIndexInstructions;
......@@ -1155,6 +1174,7 @@ void SpirvTransformer::resolveVariableIds()
void SpirvTransformer::transformInstruction()
{
ASSERT(mCurrentWord < mSpirvBlobIn.size());
const uint32_t *instruction = &mSpirvBlobIn[mCurrentWord];
const uint32_t wordCount = GetSpirvInstructionLength(instruction);
......@@ -1165,9 +1185,16 @@ void SpirvTransformer::transformInstruction()
if (opCode == spv::OpFunction)
{
constexpr size_t kFunctionIdIndex = 2;
mOpFunctionId = instruction[kFunctionIdIndex];
// SPIR-V is structured in sections. Function declarations come last. Only Op*Access*
// opcodes inside functions need to be inspected.
mIsInFunctionSection = true;
// Only write function variables for the EntryPoint function for non-compute shaders
mInsertFunctionVariables =
mOpFunctionId == mEntryPointId && mShaderType != gl::ShaderType::Compute;
}
// Only look at interesting instructions.
......@@ -1175,6 +1202,18 @@ void SpirvTransformer::transformInstruction()
if (mIsInFunctionSection)
{
// After we process an OpFunction instruction and any instructions that must come
// immediately after OpFunction we need to check if there are any precision mismatches that
// need to be handled. If so, output OpVariable for each variable that needed to change from
// a StorageClassOutput to a StorageClassFunction.
if (mInsertFunctionVariables && opCode != spv::OpFunction &&
opCode != spv::OpFunctionParameter && opCode != spv::OpLabel &&
opCode != spv::OpVariable)
{
writeInputPreamble();
mInsertFunctionVariables = false;
}
// Look at in-function opcodes.
switch (opCode)
{
......@@ -1184,6 +1223,10 @@ void SpirvTransformer::transformInstruction()
case spv::OpInBoundsPtrAccessChain:
transformed = transformAccessChain(instruction, wordCount);
break;
case spv::OpReturn:
transformed = transformReturn(instruction, wordCount);
break;
default:
break;
}
......@@ -1241,6 +1284,29 @@ void SpirvTransformer::transformInstruction()
mCurrentWord += wordCount;
}
// Called by transformInstruction to insert necessary instructions for casting varying
void SpirvTransformer::writeInputPreamble()
{
for (uint32_t id = 0; id < mVariableInfoById.size(); id++)
{
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
if (info && info->useRelaxedPrecision && info->activeStages[mShaderType] &&
!info->varyingIsOutput)
{
// This is an input varying, need to cast the mediump value that came from
// the previous stage into a highp value that the code wants to work with.
// Build OpLoad instruction to load the mediump value into a temporary
uint32_t tempVar = getNewId();
writeOpLoad(mFixedVaryingId[id], mFixedVaryingTypeId[id], tempVar);
// Build OpStore instruction to cast the mediump value to highp for use in
// the function
writeOpStore(tempVar, id);
}
}
}
void SpirvTransformer::visitName(const uint32_t *instruction)
{
// We currently don't have any big-endian devices in the list of supported platforms. Literal
......@@ -1361,6 +1427,13 @@ void SpirvTransformer::visitVariable(const uint32_t *instruction)
// Associate the id of this name with its info.
mVariableInfoById[id] = info;
if (info && info->useRelaxedPrecision && info->activeStages[mShaderType] &&
mFixedVaryingId[id] == 0)
{
mFixedVaryingId[id] = getNewId();
mFixedVaryingTypeId[id] = typeId;
}
// Note if the variable is captured by transform feedback. In that case, the TransformFeedback
// capability needs to be added.
if (mShaderType != gl::ShaderType::Fragment &&
......@@ -1380,6 +1453,7 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
uint32_t id = instruction[kIdIndex];
uint32_t decoration = instruction[kDecorationIndex];
ASSERT(id < mVariableInfoById.size());
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
// If variable is not a shader interface variable that needs modification, there's nothing to
......@@ -1408,6 +1482,16 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
case spv::DecorationDescriptorSet:
newDecorationValue = info->descriptorSet;
break;
case spv::DecorationFlat:
if (info->useRelaxedPrecision)
{
// Change the id to replacement variable
ASSERT(mFixedVaryingId[id] != 0);
const size_t instructionOffset = copyInstruction(instruction, wordCount);
(*mSpirvBlobOut)[instructionOffset + kIdIndex] = mFixedVaryingId[id];
return true;
}
break;
default:
break;
}
......@@ -1429,6 +1513,23 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
return true;
}
if (info->useRelaxedPrecision)
{
// Change the id of the location decoration to replacement variable
ASSERT(mFixedVaryingId[id] != 0);
(*mSpirvBlobOut)[instructionOffset + kIdIndex] = mFixedVaryingId[id];
// The replacement variable is always reduced precision so add that decoration to
// fixedVaryingId
constexpr size_t kInstDecorateRelaxedPrecisionWordCount = 3;
uint32_t inst[kInstDecorateRelaxedPrecisionWordCount];
SetSpirvInstructionLength(inst, kInstDecorateRelaxedPrecisionWordCount);
SetSpirvInstructionOp(inst, spv::OpDecorate);
inst[kIdIndex] = mFixedVaryingId[id];
inst[kDecorationIndex] = spv::DecorationRelaxedPrecision;
copyInstruction(inst, kInstDecorateRelaxedPrecisionWordCount);
}
// Add component decoration, if any.
if (info->component != ShaderInterfaceVariableInfo::kInvalid)
{
......@@ -1463,6 +1564,11 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
for (size_t i = 0; i < kXfbDecorationCount; ++i)
{
const size_t xfbInstructionOffset = copyInstruction(instruction, wordCount);
if (info->useRelaxedPrecision)
{
// Change the id to replacement variable
(*mSpirvBlobOut)[xfbInstructionOffset + kIdIndex] = mFixedVaryingId[id];
}
(*mSpirvBlobOut)[xfbInstructionOffset + kDecorationIndex] = xfbDecorations[i];
(*mSpirvBlobOut)[xfbInstructionOffset + kDecorationValueIndex] = xfbDecorationValues[i];
}
......@@ -1518,7 +1624,8 @@ bool SpirvTransformer::transformEntryPoint(const uint32_t *instruction, size_t w
// Remove inactive varyings from the shader interface declaration.
// SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
constexpr size_t kNameIndex = 3;
constexpr size_t kEntryPointIdIndex = 2;
constexpr size_t kNameIndex = 3;
// Calculate the length of entry point name in words. Note that endianness of the string
// doesn't matter, since we are looking for the '\0' character and rounding up to the word size.
......@@ -1529,6 +1636,10 @@ bool SpirvTransformer::transformEntryPoint(const uint32_t *instruction, size_t w
const size_t interfaceStart = kNameIndex + nameLength;
const size_t interfaceCount = instructionLength - interfaceStart;
// Should only have one EntryPoint
ASSERT(mEntryPointId == 0);
mEntryPointId = instruction[kEntryPointIdIndex];
// Create a copy of the entry point for modification.
std::vector<uint32_t> filteredEntryPoint(instruction, instruction + wordCount);
......@@ -1546,6 +1657,11 @@ bool SpirvTransformer::transformEntryPoint(const uint32_t *instruction, size_t w
continue;
}
// If ID is one we had to replace due to varying mismatch, use the fixed ID.
if (mFixedVaryingId[id] != 0)
{
id = mFixedVaryingId[id];
}
filteredEntryPoint[writeIndex] = id;
++writeIndex;
}
......@@ -1563,9 +1679,6 @@ bool SpirvTransformer::transformEntryPoint(const uint32_t *instruction, size_t w
return true;
}
// SPIR-V 1.0 Section 3.32 Instructions, OpEntryPoint
constexpr size_t kEntryPointIdIndex = 2;
// SPIR-V 1.0 Section 3.32 Instructions, OpExecutionMode
constexpr size_t kExecutionModeInstructionLength = 3;
constexpr size_t kExecutionModeIdIndex = 1;
......@@ -1599,38 +1712,66 @@ bool SpirvTransformer::transformTypePointer(const uint32_t *instruction, size_t
// inactive varying, or if that varying is a struct, an Op*AccessChain retrieving a field of
// that inactive varying.
//
// Unfortunately, SPIR-V specifies the storage class both on the type and the variable
// declaration. Otherwise it would have been sufficient to modify the OpVariable instruction.
// For simplicty, copy every "OpTypePointer Output" instruction except with the Private storage
// class, in case it may be necessary later.
// SPIR-V specifies the storage class both on the type and the variable declaration. Otherwise
// it would have been sufficient to modify the OpVariable instruction. For simplicity, copy
// every "OpTypePointer Output" and "OpTypePointer Input" instruction except with the Private
// storage class, in case it may be necessary later.
if (storageClass != spv::StorageClassOutput)
// Cannot create a Private type declaration from builtins such as gl_PerVertex.
if (mNamesById[typeId] != nullptr && angle::BeginsWith(mNamesById[typeId], "gl_"))
{
return false;
}
// Cannot create a Private type declaration from builtins such as gl_PerVertex.
if (mNamesById[typeId] != nullptr && angle::BeginsWith(mNamesById[typeId], "gl_"))
// Precision fixup needs this typeID
mTypePointerTransformedId[id].typeID = typeId;
if (storageClass != spv::StorageClassOutput && storageClass != spv::StorageClassInput)
{
return false;
}
// Copy the type declaration for modification.
// Insert OpTypePointer definition for new PrivateType.
const size_t instructionOffset = copyInstruction(instruction, wordCount);
const uint32_t newTypeId = getNewId();
(*mSpirvBlobOut)[instructionOffset + kIdIndex] = newTypeId;
const uint32_t newPrivateTypeId = getNewId();
(*mSpirvBlobOut)[instructionOffset + kIdIndex] = newPrivateTypeId;
(*mSpirvBlobOut)[instructionOffset + kStorageClassIndex] = spv::StorageClassPrivate;
// Remember the id of the replacement.
ASSERT(id < mTypePointerTransformedId.size());
mTypePointerTransformedId[id] = newTypeId;
mTypePointerTransformedId[id].privateID = newPrivateTypeId;
// The original instruction should still be present as well. At this point, we don't know
// whether we will need the Output or Private type.
return false;
}
bool SpirvTransformer::transformReturn(const uint32_t *instruction, size_t wordCount)
{
if (mOpFunctionId != mEntryPointId)
{
// We only need to process the precision info when returning from the entry point function
return false;
}
for (uint32_t id = 0; id < mVariableInfoById.size(); id++)
{
const ShaderInterfaceVariableInfo *info = mVariableInfoById[id];
if (info && info->useRelaxedPrecision && info->activeStages[mShaderType] &&
info->varyingIsOutput)
{
// Build OpLoad instruction to load the highp value into a temporary
uint32_t tempVar = getNewId();
writeOpLoad(id, mFixedVaryingTypeId[id], tempVar);
// Build OpStore instruction to cast the highp value to mediump for output
writeOpStore(tempVar, mFixedVaryingId[id]);
}
}
return false;
}
bool SpirvTransformer::transformVariable(const uint32_t *instruction, size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpVariable
......@@ -1656,6 +1797,21 @@ bool SpirvTransformer::transformVariable(const uint32_t *instruction, size_t wor
ASSERT(storageClass != spv::StorageClassInput || info->activeStages[mShaderType]);
if (info->activeStages[mShaderType])
{
if (info->useRelaxedPrecision &&
(storageClass == spv::StorageClassOutput || storageClass == spv::StorageClassInput))
{
// Change existing OpVariable to use fixedVaryingId
const size_t instructionOffset = copyInstruction(instruction, wordCount);
ASSERT(mFixedVaryingId[id] != 0);
(*mSpirvBlobOut)[instructionOffset + kIdIndex] = mFixedVaryingId[id];
// Make original variable a private global
ASSERT(mTypePointerTransformedId[typeId].privateID != 0);
writeOpVariable(id, mTypePointerTransformedId[typeId].privateID,
spv::StorageClassPrivate);
return true;
}
return false;
}
......@@ -1667,9 +1823,10 @@ bool SpirvTransformer::transformVariable(const uint32_t *instruction, size_t wor
const size_t instructionOffset = copyInstruction(instruction, wordCount);
ASSERT(typeId < mTypePointerTransformedId.size());
ASSERT(mTypePointerTransformedId[typeId] != 0);
ASSERT(mTypePointerTransformedId[typeId].privateID != 0);
(*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] = mTypePointerTransformedId[typeId];
(*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] =
mTypePointerTransformedId[typeId].privateID;
(*mSpirvBlobOut)[instructionOffset + kStorageClassIndex] = spv::StorageClassPrivate;
return true;
......@@ -1687,18 +1844,23 @@ bool SpirvTransformer::transformAccessChain(const uint32_t *instruction, size_t
// If not accessing an inactive output varying, nothing to do.
const ShaderInterfaceVariableInfo *info = mVariableInfoById[baseId];
if (info == nullptr || info->activeStages[mShaderType])
if (info == nullptr)
{
return false;
}
if (info->activeStages[mShaderType] && !info->useRelaxedPrecision)
{
return false;
}
// Copy the instruction for modification.
const size_t instructionOffset = copyInstruction(instruction, wordCount);
ASSERT(typeId < mTypePointerTransformedId.size());
ASSERT(mTypePointerTransformedId[typeId] != 0);
ASSERT(mTypePointerTransformedId[typeId].privateID != 0);
(*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] = mTypePointerTransformedId[typeId];
(*mSpirvBlobOut)[instructionOffset + kTypeIdIndex] =
mTypePointerTransformedId[typeId].privateID;
return true;
}
......@@ -1730,6 +1892,57 @@ uint32_t SpirvTransformer::getNewId()
{
return (*mSpirvBlobOut)[kHeaderIndexIndexBound]++;
}
void SpirvTransformer::writeOpStore(uint32_t tempVarId, uint32_t destId)
{
constexpr size_t kInstIndex = 0;
constexpr size_t kIdIndex = 1;
constexpr size_t kObjectIndex = 2;
constexpr size_t kOpStoreInstructionLength = 3;
uint32_t opStore[kOpStoreInstructionLength];
SetSpirvInstructionLength(&opStore[kInstIndex], kOpStoreInstructionLength);
SetSpirvInstructionOp(&opStore[kInstIndex], spv::OpStore);
opStore[kIdIndex] = destId;
opStore[kObjectIndex] = tempVarId;
copyInstruction(opStore, kOpStoreInstructionLength);
}
void SpirvTransformer::writeOpLoad(uint32_t id, uint32_t typeId, uint32_t tempVarId)
{
constexpr size_t kResultTypeIndex = 1;
constexpr size_t kResultIdIndex = 2;
constexpr size_t kLoadIdIndex = 3;
constexpr size_t kOpLoadInstructionLength = 4;
// Build OpLoad instruction
ASSERT(typeId != 0);
ASSERT(mTypePointerTransformedId[typeId].typeID != 0);
uint32_t opLoad[kOpLoadInstructionLength];
SetSpirvInstructionOp(opLoad, spv::OpLoad);
SetSpirvInstructionLength(opLoad, kOpLoadInstructionLength);
opLoad[kResultTypeIndex] = mTypePointerTransformedId[typeId].typeID;
opLoad[kResultIdIndex] = tempVarId;
opLoad[kLoadIdIndex] = id;
copyInstruction(opLoad, kOpLoadInstructionLength);
}
void SpirvTransformer::writeOpVariable(uint32_t id, uint32_t typeId, uint32_t storageClassId)
{
constexpr size_t kResultTypeIndex = 1;
constexpr size_t kResultIdIndex = 2;
constexpr size_t kStorageClassIdIndex = 3;
constexpr size_t kOpVariableInstructionLength = 4;
uint32_t opVariable[kOpVariableInstructionLength];
SetSpirvInstructionOp(opVariable, spv::OpVariable);
SetSpirvInstructionLength(opVariable, kOpVariableInstructionLength);
opVariable[kResultTypeIndex] = typeId;
opVariable[kResultIdIndex] = id;
opVariable[kStorageClassIdIndex] = storageClassId;
copyInstruction(opVariable, kOpVariableInstructionLength);
}
} // anonymous namespace
const uint32_t ShaderInterfaceVariableInfo::kInvalid;
......
......@@ -81,6 +81,13 @@ struct ShaderInterfaceVariableInfo
uint32_t xfbBuffer = kInvalid;
uint32_t xfbOffset = kInvalid;
uint32_t xfbStride = kInvalid;
// Indicates that the precision needs to be modified in the generated SPIR-V
// to support only transferring medium precision data when there's a precision
// mismatch between the shaders. For example, either the VS casts highp->mediump
// or the FS casts mediump->highp.
bool useRelaxedPrecision = false;
// Indicate if varying is input or output
bool varyingIsOutput = false;
};
// TODO: http://anglebug.com/4524: Need a different hash key than a string, since
......
......@@ -219,10 +219,12 @@ std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(gl::BinaryInputStream *
info->location = stream->readInt<uint32_t>();
info->component = stream->readInt<uint32_t>();
// PackedEnumBitSet uses uint8_t
info->activeStages = gl::ShaderBitSet(stream->readInt<uint8_t>());
info->xfbBuffer = stream->readInt<uint32_t>();
info->xfbOffset = stream->readInt<uint32_t>();
info->xfbStride = stream->readInt<uint32_t>();
info->activeStages = gl::ShaderBitSet(stream->readInt<uint8_t>());
info->xfbBuffer = stream->readInt<uint32_t>();
info->xfbOffset = stream->readInt<uint32_t>();
info->xfbStride = stream->readInt<uint32_t>();
info->useRelaxedPrecision = stream->readBool();
info->varyingIsOutput = stream->readBool();
}
}
......@@ -246,6 +248,8 @@ void ProgramExecutableVk::save(gl::BinaryOutputStream *stream)
stream->writeInt<uint32_t>(it.second.xfbBuffer);
stream->writeInt<uint32_t>(it.second.xfbOffset);
stream->writeInt<uint32_t>(it.second.xfbStride);
stream->writeInt<uint8_t>(it.second.useRelaxedPrecision);
stream->writeInt<uint8_t>(it.second.varyingIsOutput);
}
}
}
......@@ -866,6 +870,40 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
return angle::Result::Continue;
}
void ProgramExecutableVk::resolvePrecisionMismatch(const gl::ProgramMergedVaryings &mergedVaryings)
{
for (const gl::ProgramVaryingRef &mergedVarying : mergedVaryings)
{
if (mergedVarying.frontShader && mergedVarying.backShader)
{
GLenum frontPrecision = mergedVarying.frontShader->precision;
GLenum backPrecision = mergedVarying.backShader->precision;
if (frontPrecision != backPrecision)
{
ShaderInterfaceVariableInfo *info =
&mVariableInfoMap[mergedVarying.frontShaderStage]
[mergedVarying.frontShader->mappedName];
ASSERT(frontPrecision >= GL_LOW_FLOAT && frontPrecision <= GL_HIGH_INT);
ASSERT(backPrecision >= GL_LOW_FLOAT && backPrecision <= GL_HIGH_INT);
if (frontPrecision > backPrecision)
{
// The output is higher precision than the input
info->varyingIsOutput = true;
info->useRelaxedPrecision = true;
}
else if (backPrecision > frontPrecision)
{
// The output is lower precision than the input, adjust the input
info = &mVariableInfoMap[mergedVarying.backShaderStage]
[mergedVarying.backShader->mappedName];
info->varyingIsOutput = false;
info->useRelaxedPrecision = true;
}
}
}
}
}
void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
const gl::ShaderType shaderType,
const DefaultUniformBlock &defaultUniformBlock,
......
......@@ -197,6 +197,7 @@ class ProgramExecutableVk
const gl::ActiveTextureArray<vk::TextureUnit> *activeTextures,
vk::DescriptorSetLayoutDesc *descOut);
void resolvePrecisionMismatch(const gl::ProgramMergedVaryings &mergedVaryings);
void updateDefaultUniformsDescriptorSet(const gl::ShaderType shaderType,
const DefaultUniformBlock &defaultUniformBlock,
vk::BufferHelper *defaultUniformBuffer,
......
......@@ -53,7 +53,7 @@ void ProgramPipelineVk::fillProgramStateMap(
}
angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
const gl::ProgramMergedVaryings & /*mergedVaryings*/)
const gl::ProgramMergedVaryings &mergedVaryings)
{
ContextVk *contextVk = vk::GetImpl(glContext);
const gl::State &glState = glContext->getState();
......@@ -87,6 +87,11 @@ angle::Result ProgramPipelineVk::link(const gl::Context *glContext,
}
}
if (contextVk->getFeatures().enablePrecisionQualifiers.enabled)
{
mExecutable.resolvePrecisionMismatch(mergedVaryings);
}
return mExecutable.createPipelineLayout(glContext);
}
......
......@@ -255,7 +255,7 @@ void ProgramVk::fillProgramStateMap(gl::ShaderMap<const gl::ProgramState *> *pro
std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog,
const gl::ProgramMergedVaryings & /*mergedVaryings*/)
const gl::ProgramMergedVaryings &mergedVaryings)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::link");
......@@ -288,6 +288,11 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
return std::make_unique<LinkEventDone>(status);
}
if (contextVk->getFeatures().enablePrecisionQualifiers.enabled)
{
mExecutable.resolvePrecisionMismatch(mergedVaryings);
}
// TODO(jie.a.chen@intel.com): Parallelize linking.
// http://crbug.com/849576
status = mExecutable.createPipelineLayout(context);
......
......@@ -5932,6 +5932,220 @@ void main()
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test mismatched precision in varying is handled correctly.
TEST_P(GLSLTest_ES3, MismatchPrecisionFloat)
{
constexpr char kVS[] = R"(#version 300 es
in vec4 position;
uniform highp float inVal;
out highp float myVarying;
void main()
{
myVarying = inVal;
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
in mediump float myVarying;
void main()
{
my_FragColor = vec4(1, 0, 0, 1);
if (myVarying > 1.0)
{
my_FragColor = vec4(0, 1, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glUseProgram(program.get());
GLint positionLocation = glGetAttribLocation(program.get(), "position");
std::array<Vector3, 6> quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.z() = 0.5f;
}
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
GLint inValLoc = glGetUniformLocation(program, "inVal");
ASSERT_NE(-1, inValLoc);
glUniform1f(inValLoc, static_cast<GLfloat>(1.003));
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test mismatched precision in varying is handled correctly.
TEST_P(GLSLTest_ES3, MismatchPrecisionlowpFloat)
{
// Note: SPIRV only has relaxed precision so both lowp and mediump turn into "relaxed
// precision", thus this is the same test as MismatchPrecisionFloat but including it for
// completeness in case something changes.
constexpr char kVS[] = R"(#version 300 es
in vec4 position;
uniform highp float inVal;
out highp float myVarying;
void main()
{
myVarying = inVal;
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
in lowp float myVarying;
void main()
{
my_FragColor = vec4(1, 0, 0, 1);
if (myVarying > 1.0)
{
my_FragColor = vec4(0, 1, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glUseProgram(program.get());
GLint positionLocation = glGetAttribLocation(program.get(), "position");
std::array<Vector3, 6> quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.z() = 0.5f;
}
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
GLint inValLoc = glGetUniformLocation(program, "inVal");
ASSERT_NE(-1, inValLoc);
glUniform1f(inValLoc, static_cast<GLfloat>(1.003));
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test mismatched precision in varying is handled correctly.
TEST_P(GLSLTest_ES3, MismatchPrecisionVec2UnusedVarying)
{
constexpr char kVS[] = R"(#version 300 es
in vec2 position;
uniform highp float inVal;
out highp float myVarying;
out highp vec2 texCoord;
void main()
{
myVarying = inVal;
gl_Position = vec4(position, 0, 1);
texCoord = position * 0.5 + vec2(0.5);
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
in mediump float myVarying;
in mediump vec2 texCoord;
void main()
{
my_FragColor = vec4(1, 0, 0, 1);
if (myVarying > 1.0)
{
my_FragColor = vec4(0, 1, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glUseProgram(program.get());
GLint positionLocation = glGetAttribLocation(program.get(), "position");
std::array<Vector3, 6> quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.z() = 0.5f;
}
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
GLint inValLoc = glGetUniformLocation(program, "inVal");
ASSERT_NE(-1, inValLoc);
glUniform1f(inValLoc, static_cast<GLfloat>(1.003));
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test mismatched precision in varying is handled correctly.
TEST_P(GLSLTest_ES3, MismatchPrecisionMedToHigh)
{
constexpr char kVS[] = R"(#version 300 es
in vec2 position;
uniform highp float inVal;
out mediump float myVarying;
void main()
{
myVarying = inVal;
gl_Position = vec4(position, 0, 1);
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
in highp float myVarying;
void main()
{
my_FragColor = vec4(1, 0, 0, 1);
if (myVarying > 1.0)
{
my_FragColor = vec4(0, 1, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
glUseProgram(program.get());
GLint positionLocation = glGetAttribLocation(program.get(), "position");
std::array<Vector3, 6> quadVertices = GetQuadVertices();
for (Vector3 &vertex : quadVertices)
{
vertex.z() = 0.5f;
}
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
GLint inValLoc = glGetUniformLocation(program, "inVal");
ASSERT_NE(-1, inValLoc);
glUniform1f(inValLoc, static_cast<GLfloat>(1.003));
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test vector/scalar arithmetic (in this case multiplication and addition). Meant to reproduce a
// bug that appeared in NVIDIA OpenGL drivers and that is worked around by
// VectorizeVectorScalarArithmetic AST transform.
......
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