Commit dee4d7a5 by Charlie Lao Committed by Commit Bot

Vulkan: Early fragment tests optimization

Checks if early fragment tests as an optimization is feasible and enable it if we can. In the link time, if context state diagrees with optimization (in rare case), then remove the ExecutionModeEarlyFragmentTests sprv op code. Bug: angleproject:4508 Change-Id: Ifbb06c0ffb050a9f3ddb16ab50362e908b4b9cf6 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2136490 Commit-Queue: Charlie Lao <cclao@google.com> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com>
parent 32457326
......@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 227
#define ANGLE_SH_VERSION 228
enum ShShaderSpec
{
......@@ -336,6 +336,9 @@ const ShCompileOptions SH_REWRITE_ROW_MAJOR_MATRICES = UINT64_C(1) << 53;
// Drop any explicit precision qualifiers from shader.
const ShCompileOptions SH_IGNORE_PRECISION_QUALIFIERS = UINT64_C(1) << 54;
// Allow compiler to do early fragment tests as an optimization.
const ShCompileOptions SH_EARLY_FRAGMENT_TESTS_OPTIMIZATION = UINT64_C(1) << 55;
// Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy
{
......@@ -665,6 +668,8 @@ sh::WorkGroupSize GetComputeShaderLocalGroupSize(const ShHandle handle);
// Returns the number of views specified through the num_views layout qualifier. If num_views is
// not set, the function returns -1.
int GetVertexShaderNumViews(const ShHandle handle);
// Returns true if compiler has injected instructions for early fragment tests as an optimization
bool HasEarlyFragmentTestsOptimization(const ShHandle handle);
// Returns true if the passed in variables pack in maxVectors followingthe packing rules from the
// GLSL 1.017 spec, Appendix A, section 7.
......
......@@ -124,6 +124,8 @@ angle_translator_sources = [
"src/compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h",
"src/compiler/translator/tree_ops/DeferGlobalInitializers.cpp",
"src/compiler/translator/tree_ops/DeferGlobalInitializers.h",
"src/compiler/translator/tree_ops/EarlyFragmentTestsOptimization.cpp",
"src/compiler/translator/tree_ops/EarlyFragmentTestsOptimization.h",
"src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.cpp",
"src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h",
"src/compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.cpp",
......
......@@ -26,6 +26,7 @@
#include "compiler/translator/tree_ops/ClampPointSize.h"
#include "compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h"
#include "compiler/translator/tree_ops/DeferGlobalInitializers.h"
#include "compiler/translator/tree_ops/EarlyFragmentTestsOptimization.h"
#include "compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h"
#include "compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.h"
#include "compiler/translator/tree_ops/EmulatePrecision.h"
......@@ -930,6 +931,16 @@ bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
}
}
mEarlyFragmentTestsOptimized = false;
if (compileOptions & SH_EARLY_FRAGMENT_TESTS_OPTIMIZATION)
{
if (mShaderVersion <= 300 && mShaderType == GL_FRAGMENT_SHADER &&
!isEarlyFragmentTestsSpecified())
{
mEarlyFragmentTestsOptimized = CheckEarlyFragmentTestsFeasible(this, root);
}
}
return true;
}
......@@ -1492,7 +1503,7 @@ bool TCompiler::isVaryingDefined(const char *varyingName)
void EmitEarlyFragmentTestsGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isEarlyFragmentTestsSpecified())
if (compiler.isEarlyFragmentTestsSpecified() || compiler.isEarlyFragmentTestsOptimized())
{
sink << "layout (early_fragment_tests) in;\n";
}
......
......@@ -99,6 +99,7 @@ class TCompiler : public TShHandleBase
TInfoSink &getInfoSink() { return mInfoSink; }
bool isEarlyFragmentTestsSpecified() const { return mEarlyFragmentTestsSpecified; }
bool isEarlyFragmentTestsOptimized() const { return mEarlyFragmentTestsOptimized; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; }
......@@ -278,6 +279,7 @@ class TCompiler : public TShHandleBase
// fragment shader early fragment tests
bool mEarlyFragmentTestsSpecified;
bool mEarlyFragmentTestsOptimized;
// compute shader local group size
bool mComputeShaderLocalSizeDeclared;
......
......@@ -499,6 +499,16 @@ int GetVertexShaderNumViews(const ShHandle handle)
return compiler->getNumViews();
}
bool HasEarlyFragmentTestsOptimization(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
if (compiler == nullptr)
{
return false;
}
return compiler->isEarlyFragmentTestsOptimized();
}
bool CheckVariablesWithinPackingLimits(int maxVectors, const std::vector<ShaderVariable> &variables)
{
return CheckVariablesInPackingLimits(maxVectors, variables);
......
//
// Copyright 2020 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.
//
// CheckEarlyFragmentTestOptimization is an AST traverser to check if early fragment
// test as an optimization is feasible.
//
#include "compiler/translator/tree_ops/EarlyFragmentTestsOptimization.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/Symbol.h"
#include "compiler/translator/tree_util/IntermNode_util.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
{
namespace
{
// Traverser that check conditions that would prevent early fragment tests optmization.
class CheckEFTOptimizationTraverser : public TIntermTraverser
{
public:
CheckEFTOptimizationTraverser();
void visitSymbol(TIntermSymbol *node) override;
bool visitBranch(Visit visit, TIntermBranch *node) override;
bool isFragDepthUsed() { return mFragDepthUsed; }
bool isDiscardOpUsed() { return mDiscardOpUsed; }
protected:
bool mFragDepthUsed;
bool mDiscardOpUsed;
};
CheckEFTOptimizationTraverser::CheckEFTOptimizationTraverser()
: TIntermTraverser(true, false, false), mFragDepthUsed(false), mDiscardOpUsed(false)
{}
void CheckEFTOptimizationTraverser::visitSymbol(TIntermSymbol *node)
{
// Check the qualifier from the variable, not from the symbol node. The node may have a
// different qualifier if it's the result of a folded ternary node.
TQualifier qualifier = node->variable().getType().getQualifier();
if (qualifier == EvqFragDepth || qualifier == EvqFragDepthEXT)
{
mFragDepthUsed = true;
}
}
bool CheckEFTOptimizationTraverser::visitBranch(Visit visit, TIntermBranch *node)
{
if (node->getFlowOp() == EOpKill)
{
mDiscardOpUsed = true;
}
return true;
}
} // namespace
bool CheckEarlyFragmentTestsFeasible(TCompiler *compiler, TIntermNode *root)
{
CheckEFTOptimizationTraverser traverser;
root->traverse(&traverser);
if (traverser.isFragDepthUsed() || traverser.isDiscardOpUsed())
{
return false;
}
return true;
}
} // namespace sh
//
// Copyright 2020 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.
//
// CheckEarlyFragmentTestsOptimization is an AST traverser to check if early fragment
// tests as an optimization is feasible.
//
#ifndef COMPILER_TRANSLATOR_TREEOPS_EARLYFRAGMENTTESTSOPTIMIZATION_H_
#define COMPILER_TRANSLATOR_TREEOPS_EARLYFRAGMENTTESTSOPTIMIZATION_H_
#include "common/angleutils.h"
namespace sh
{
class TCompiler;
class TIntermNode;
ANGLE_NO_DISCARD bool CheckEarlyFragmentTestsFeasible(TCompiler *compiler, TIntermNode *root);
} // namespace sh
#endif // COMPILER_TRANSLATOR_TREEOPS_EARLYFRAGMENTTESTSOPTIMIZATION_H_
......@@ -1569,6 +1569,13 @@ angle::Result Program::link(const Context *context)
mState.mNumViews = vertexShader->getNumViews();
}
gl::Shader *fragmentShader = mState.mAttachedShaders[ShaderType::Fragment];
if (fragmentShader)
{
mState.mEarlyFramentTestsOptimization =
fragmentShader->hasEarlyFragmentTestsOptimization();
}
InitUniformBlockLinker(mState, &mResources->uniformBlockLinker);
InitShaderStorageBlockLinker(mState, &mResources->shaderStorageBlockLinker);
......@@ -1824,6 +1831,7 @@ void Program::unlink()
mState.mBaseInstanceLocation = -1;
mState.mCachedBaseVertex = 0;
mState.mCachedBaseInstance = 0;
mState.mEarlyFramentTestsOptimization = false;
mValidated = false;
......@@ -5054,6 +5062,7 @@ angle::Result Program::serialize(const Context *context, angle::MemoryBuffer *bi
stream.writeInt(mState.mGeometryShaderMaxVertices);
stream.writeInt(mState.mNumViews);
stream.writeInt(mState.mEarlyFramentTestsOptimization);
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"All bits of mAttributesTypeMask types and mask fit into 32 bits each");
......@@ -5253,7 +5262,8 @@ angle::Result Program::deserialize(const Context *context,
mState.mGeometryShaderInvocations = stream.readInt<int>();
mState.mGeometryShaderMaxVertices = stream.readInt<int>();
mState.mNumViews = stream.readInt<int>();
mState.mNumViews = stream.readInt<int>();
mState.mEarlyFramentTestsOptimization = stream.readInt<bool>();
static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8,
"Too many vertex attribs for mask: All bits of mAttributesTypeMask types and "
......
......@@ -346,6 +346,7 @@ class ProgramState final : angle::NonCopyable
{
return !getLinkedTransformFeedbackVaryings().empty();
}
bool hasEarlyFragmentTestsOptimization() const { return mEarlyFramentTestsOptimization; }
bool isShaderMarkedForDetach(gl::ShaderType shaderType) const
{
......@@ -428,6 +429,7 @@ class ProgramState final : angle::NonCopyable
bool mBinaryRetrieveableHint;
bool mSeparable;
bool mEarlyFramentTestsOptimization;
// ANGLE_multiview.
int mNumViews;
......
......@@ -317,7 +317,8 @@ void Shader::compile(const Context *context)
mState.mGeometryShaderInputPrimitiveType.reset();
mState.mGeometryShaderOutputPrimitiveType.reset();
mState.mGeometryShaderMaxVertices.reset();
mState.mGeometryShaderInvocations = 1;
mState.mGeometryShaderInvocations = 1;
mState.mEarlyFragmentTestsOptimization = false;
mState.mCompileStatus = CompileStatus::COMPILE_REQUESTED;
mBoundCompiler.set(context, context->getCompiler());
......@@ -490,6 +491,8 @@ void Shader::resolveCompile()
std::sort(mState.mInputVaryings.begin(), mState.mInputVaryings.end(), CompareShaderVar);
mState.mActiveOutputVariables =
GetActiveShaderVariables(sh::GetOutputVariables(compilerHandle));
mState.mEarlyFragmentTestsOptimization =
sh::HasEarlyFragmentTestsOptimization(compilerHandle);
break;
}
case ShaderType::Geometry:
......
......@@ -85,6 +85,8 @@ class ShaderState final : angle::NonCopyable
return mActiveOutputVariables;
}
bool isEarlyFragmentTeststOptimization() const { return mEarlyFragmentTestsOptimization; }
bool compilePending() const { return mCompileStatus == CompileStatus::COMPILE_REQUESTED; }
private:
......@@ -108,6 +110,8 @@ class ShaderState final : angle::NonCopyable
std::vector<sh::ShaderVariable> mActiveAttributes;
std::vector<sh::ShaderVariable> mActiveOutputVariables;
bool mEarlyFragmentTestsOptimization;
// ANGLE_multiview.
int mNumViews;
......@@ -162,6 +166,10 @@ class Shader final : angle::NonCopyable, public LabeledObject
unsigned int getRefCount() const;
bool isFlaggedForDeletion() const;
void flagForDeletion();
bool hasEarlyFragmentTestsOptimization() const
{
return mState.mEarlyFragmentTestsOptimization;
}
int getShaderVersion();
......
......@@ -775,6 +775,8 @@ class State : angle::NonCopyable
return mNoSimultaneousConstantColorAndAlphaBlendFunc;
}
bool isEarlyFragmentTestsOptimizationAllowed() const { return isSampleCoverageEnabled(); }
private:
friend class Context;
......
......@@ -978,6 +978,7 @@ class SpirvTransformer final : angle::NonCopyable
{
public:
SpirvTransformer(const std::vector<uint32_t> &spirvBlobIn,
bool removeEarlyFragmentTestsOptimization,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderType shaderType,
SpirvBlob *spirvBlobOut)
......@@ -989,8 +990,8 @@ class SpirvTransformer final : angle::NonCopyable
{
gl::ShaderBitSet allStages;
allStages.set();
mBuiltinVariableInfo.activeStages = allStages;
mRemoveEarlyFragmentTestsOptimization = removeEarlyFragmentTestsOptimization;
mBuiltinVariableInfo.activeStages = allStages;
}
bool transform();
......@@ -1028,6 +1029,7 @@ class SpirvTransformer final : angle::NonCopyable
bool transformDecorate(const uint32_t *instruction, size_t wordCount);
bool transformTypePointer(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:
size_t copyInstruction(const uint32_t *instruction, size_t wordCount);
......@@ -1038,6 +1040,8 @@ class SpirvTransformer final : angle::NonCopyable
const gl::ShaderType mShaderType;
bool mHasTransformFeedbackOutput;
bool mRemoveEarlyFragmentTestsOptimization;
// Input shader variable info map:
const ShaderInterfaceVariableInfoMap &mVariableInfoMap;
ShaderInterfaceVariableInfo mBuiltinVariableInfo;
......@@ -1237,6 +1241,9 @@ void SpirvTransformer::transformInstruction()
case spv::OpVariable:
transformed = transformVariable(instruction, wordCount);
break;
case spv::OpExecutionMode:
transformed = transformExecutionMode(instruction, wordCount);
break;
default:
break;
}
......@@ -1714,6 +1721,22 @@ bool SpirvTransformer::transformAccessChain(const uint32_t *instruction, size_t
return true;
}
bool SpirvTransformer::transformExecutionMode(const uint32_t *instruction, size_t wordCount)
{
// SPIR-V 1.0 Section 3.32 Instructions, OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain,
// OpInBoundsPtrAccessChain
constexpr size_t kModeIndex = 2;
const uint32_t executionMode = instruction[kModeIndex];
if (executionMode == spv::ExecutionModeEarlyFragmentTests &&
mRemoveEarlyFragmentTestsOptimization)
{
// skip the copy
return true;
}
return false;
}
size_t SpirvTransformer::copyInstruction(const uint32_t *instruction, size_t wordCount)
{
size_t instructionOffset = mSpirvBlobOut->size();
......@@ -1921,6 +1944,7 @@ void GlslangGetShaderSource(GlslangSourceOptions &options,
angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback,
const gl::ShaderType shaderType,
bool removeEarlyFragmentTestsOptimization,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const SpirvBlob &initialSpirvBlob,
SpirvBlob *spirvBlobOut)
......@@ -1931,7 +1955,8 @@ angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback,
}
// Transform the SPIR-V code by assigning location/set/binding values.
SpirvTransformer transformer(initialSpirvBlob, variableInfoMap, shaderType, spirvBlobOut);
SpirvTransformer transformer(initialSpirvBlob, removeEarlyFragmentTestsOptimization,
variableInfoMap, shaderType, spirvBlobOut);
ANGLE_GLSLANG_CHECK(callback, transformer.transform(), GlslangError::InvalidSpirv);
ASSERT(ValidateSpirv(*spirvBlobOut));
......@@ -1950,8 +1975,12 @@ angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
// we pass in false here to skip modifications related to early fragment tests
// optimizations and line rasterization. These are done in the initProgram time since they
// are related to context state. We must keep original untouched spriv blobs here because we
// do not have ability to add back in at initProgram time.
angle::Result status =
GlslangTransformSpirvCode(callback, shaderType, variableInfoMap[shaderType],
GlslangTransformSpirvCode(callback, shaderType, false, variableInfoMap[shaderType],
initialSpirvBlobs[shaderType], &(*spirvBlobsOut)[shaderType]);
if (status != angle::Result::Continue)
{
......
......@@ -118,8 +118,9 @@ void GlslangGetShaderSource(GlslangSourceOptions &options,
angle::Result GlslangTransformSpirvCode(const GlslangErrorCallback &callback,
const gl::ShaderType shaderType,
bool removeEarlyFragmentTestsOptimization,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const std::vector<uint32_t> &initialSpirvBlob,
const SpirvBlob &initialSpirvBlob,
SpirvBlob *spirvBlobOut);
angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
......
......@@ -1189,6 +1189,7 @@ angle::Result ContextVk::handleDirtyGraphicsPipeline(const gl::Context *context,
vk::CommandBuffer *commandBuffer)
{
ASSERT(mExecutable);
mExecutable->updateEarlyFragmentTestsOptimization(this);
if (!mCurrentGraphicsPipeline)
{
......@@ -2706,6 +2707,9 @@ angle::Result ContextVk::syncState(const gl::Context *context,
case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
mGraphicsPipelineDesc->updateAlphaToCoverageEnable(
&mGraphicsPipelineTransition, glState.isSampleAlphaToCoverageEnabled());
ASSERT(gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE >
gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED);
iter.setLaterBit(gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE);
break;
case gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
updateSampleMask(glState);
......
......@@ -85,12 +85,13 @@ angle::Result GlslangWrapperVk::GetShaderCode(
angle::Result GlslangWrapperVk::TransformSpirV(
vk::Context *context,
const gl::ShaderType shaderType,
bool removeEarlyFragmentTestsOptimization,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
std::vector<uint32_t> &initialSpirvBlob,
std::vector<uint32_t> *shaderCodeOut)
const SpirvBlob &initialSpirvBlob,
SpirvBlob *shaderCodeOut)
{
return GlslangTransformSpirvCode(
[context](GlslangError error) { return ErrorHandler(context, error); }, shaderType,
variableInfoMap, initialSpirvBlob, shaderCodeOut);
removeEarlyFragmentTestsOptimization, variableInfoMap, initialSpirvBlob, shaderCodeOut);
}
} // namespace rx
......@@ -45,9 +45,10 @@ class GlslangWrapperVk
static angle::Result TransformSpirV(vk::Context *context,
const gl::ShaderType shaderType,
bool removeEarlyFragmentTestsOptimization,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
std::vector<uint32_t> &initialSpirvBlob,
std::vector<uint32_t> *shaderCodeOut);
const SpirvBlob &initialSpirvBlob,
SpirvBlob *shaderCodeOut);
};
} // namespace rx
......
......@@ -88,7 +88,8 @@ ProgramInfo::~ProgramInfo() = default;
angle::Result ProgramInfo::initProgram(ContextVk *contextVk,
const gl::ShaderType shaderType,
const ShaderInfo &shaderInfo,
bool enableLineRasterEmulation)
const ShaderMapInterfaceVariableInfoMap &variableInfoMap,
ProgramTransformOptionBits optionBits)
{
const gl::ShaderMap<SpirvBlob> &spirvBlobs = shaderInfo.getSpirvBlobs();
......@@ -96,13 +97,28 @@ angle::Result ProgramInfo::initProgram(ContextVk *contextVk,
if (!spirvBlob.empty())
{
ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[shaderType].get(), spirvBlob.data(),
spirvBlob.size() * sizeof(uint32_t)));
if (shaderType == gl::ShaderType::Fragment &&
optionBits[ProgramTransformOption::RemoveEarlyFragmentTestsOptimization])
{
SpirvBlob spirvBlobTransformed;
ANGLE_TRY(GlslangWrapperVk::TransformSpirV(contextVk, shaderType, true,
variableInfoMap[shaderType], spirvBlob,
&spirvBlobTransformed));
ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[shaderType].get(),
spirvBlobTransformed.data(),
spirvBlobTransformed.size() * sizeof(uint32_t)));
}
else
{
ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[shaderType].get(),
spirvBlob.data(),
spirvBlob.size() * sizeof(uint32_t)));
}
mProgramHelper.setShader(shaderType, &mShaders[shaderType]);
}
if (enableLineRasterEmulation)
if (optionBits[ProgramTransformOption::EnableLineRasterEmulation])
{
mProgramHelper.enableSpecializationConstant(
sh::vk::SpecializationConstantId::LineRasterEmulation);
......@@ -148,6 +164,7 @@ void ProgramExecutableVk::reset(ContextVk *contextVk)
mEmptyDescriptorSets.fill(VK_NULL_HANDLE);
mPipelineLayoutCreated = false;
mNumDefaultUniformDescriptors = 0;
mTransformOptionBits.reset();
for (vk::RefCountedDescriptorPoolBinding &binding : mDescriptorPoolBindings)
{
......@@ -162,8 +179,10 @@ void ProgramExecutableVk::reset(ContextVk *contextVk)
mTextureDescriptorsCache.clear();
mDescriptorBuffersCache.clear();
mDefaultProgramInfo.release(contextVk);
mLineRasterProgramInfo.release(contextVk);
for (ProgramInfo &programInfo : mProgramInfos)
{
programInfo.release(contextVk);
}
}
std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(gl::BinaryInputStream *stream)
......@@ -529,6 +548,22 @@ void WriteBufferDescriptorSetBinding(const gl::OffsetBindingPointer<gl::Buffer>
ASSERT(writeInfoOut->pBufferInfo[0].buffer != VK_NULL_HANDLE);
}
void ProgramExecutableVk::updateEarlyFragmentTestsOptimization(ContextVk *contextVk)
{
const gl::State &glState = contextVk->getState();
mTransformOptionBits[ProgramTransformOption::RemoveEarlyFragmentTestsOptimization] = false;
if (!glState.isEarlyFragmentTestsOptimizationAllowed())
{
ProgramVk *programVk = getShaderProgram(glState, gl::ShaderType::Fragment);
if (programVk->getState().hasEarlyFragmentTestsOptimization())
{
mTransformOptionBits[ProgramTransformOption::RemoveEarlyFragmentTestsOptimization] =
true;
}
}
}
angle::Result ProgramExecutableVk::getGraphicsPipeline(
ContextVk *contextVk,
gl::PrimitiveMode mode,
......@@ -537,9 +572,10 @@ angle::Result ProgramExecutableVk::getGraphicsPipeline(
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
{
const gl::State &glState = contextVk->getState();
bool bresenhamEmulationEnabled = contextVk->isBresenhamEmulationEnabled(mode);
ProgramInfo &programInfo = getProgramInfo(bresenhamEmulationEnabled);
const gl::State &glState = contextVk->getState();
mTransformOptionBits[ProgramTransformOption::EnableLineRasterEmulation] =
contextVk->isBresenhamEmulationEnabled(mode);
ProgramInfo &programInfo = getProgramInfo(mTransformOptionBits);
RendererVk *renderer = contextVk->getRenderer();
vk::PipelineCache *pipelineCache = nullptr;
......@@ -552,7 +588,7 @@ angle::Result ProgramExecutableVk::getGraphicsPipeline(
if (programVk)
{
ANGLE_TRY(programVk->initGraphicsShaderProgram(contextVk, shaderType,
bresenhamEmulationEnabled, programInfo));
mTransformOptionBits, programInfo));
}
}
......
......@@ -10,6 +10,7 @@
#ifndef LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_
#define LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_
#include "common/bitset_utils.h"
#include "common/utilities.h"
#include "libANGLE/Context.h"
#include "libANGLE/InfoLog.h"
......@@ -45,6 +46,15 @@ class ShaderInfo final : angle::NonCopyable
bool mIsInitialized = false;
};
enum class ProgramTransformOption : uint8_t
{
EnableLineRasterEmulation = 0,
RemoveEarlyFragmentTestsOptimization = 1,
EnumCount = 2,
PermutationCount = 4,
};
using ProgramTransformOptionBits = angle::PackedEnumBitSet<ProgramTransformOption, uint8_t>;
class ProgramInfo final : angle::NonCopyable
{
public:
......@@ -54,7 +64,8 @@ class ProgramInfo final : angle::NonCopyable
angle::Result initProgram(ContextVk *contextVk,
const gl::ShaderType shaderType,
const ShaderInfo &shaderInfo,
bool enableLineRasterEmulation);
const ShaderMapInterfaceVariableInfoMap &variableInfoMap,
ProgramTransformOptionBits optionBits);
void release(ContextVk *contextVk);
ANGLE_INLINE bool valid(const gl::ShaderType shaderType) const
......@@ -108,10 +119,10 @@ class ProgramExecutableVk
gl::ShaderMap<const gl::ProgramState *> *programStatesOut);
const gl::ProgramExecutable &getGlExecutable();
ProgramInfo &getDefaultProgramInfo() { return mDefaultProgramInfo; }
ProgramInfo &getProgramInfo(bool enableLineRasterEmulation)
ProgramInfo &getDefaultProgramInfo() { return mProgramInfos[0]; }
ProgramInfo &getProgramInfo(ProgramTransformOptionBits optionBits)
{
return enableLineRasterEmulation ? mLineRasterProgramInfo : mDefaultProgramInfo;
return mProgramInfos[optionBits.to_ulong()];
}
angle::Result getGraphicsPipeline(ContextVk *contextVk,
......@@ -137,6 +148,8 @@ class ProgramExecutableVk
angle::Result updateDescriptorSets(ContextVk *contextVk, vk::CommandBuffer *commandBuffer);
void updateEarlyFragmentTestsOptimization(ContextVk *contextVk);
void setProgram(ProgramVk *program)
{
ASSERT(!mProgram && !mProgramPipeline);
......@@ -227,8 +240,9 @@ class ProgramExecutableVk
// since that's slow to calculate.
ShaderMapInterfaceVariableInfoMap mVariableInfoMap;
ProgramInfo mDefaultProgramInfo;
ProgramInfo mLineRasterProgramInfo;
ProgramInfo mProgramInfos[static_cast<int>(ProgramTransformOption::PermutationCount)];
ProgramTransformOptionBits mTransformOptionBits;
ProgramVk *mProgram;
ProgramPipelineVk *mProgramPipeline;
......
......@@ -106,8 +106,10 @@ angle::Result ProgramPipelineVk::transformShaderSpirV(const gl::Context *glConte
mExecutable.mVariableInfoMap[shaderType];
std::vector<uint32_t> transformedSpirvBlob;
// We skip early fragment tests optimization modification here since we need to keep
// original spriv blob here.
ANGLE_TRY(GlslangWrapperVk::TransformSpirV(
contextVk, shaderType, variableInfoMap,
contextVk, shaderType, false, variableInfoMap,
programVk->getShaderInfo().getSpirvBlobs()[shaderType], &transformedSpirvBlob));
// Save the newly transformed SPIR-V
......
......@@ -126,15 +126,16 @@ class ProgramVk : public ProgramImpl
ANGLE_INLINE angle::Result initGraphicsShaderProgram(ContextVk *contextVk,
const gl::ShaderType shaderType,
bool enableLineRasterEmulation,
ProgramTransformOptionBits optionBits,
ProgramInfo &programInfo)
{
return initProgram(contextVk, shaderType, enableLineRasterEmulation, &programInfo);
return initProgram(contextVk, shaderType, optionBits, &programInfo);
}
ANGLE_INLINE angle::Result initComputeProgram(ContextVk *contextVk, ProgramInfo &programInfo)
{
return initProgram(contextVk, gl::ShaderType::Compute, false, &programInfo);
ProgramTransformOptionBits optionBits;
return initProgram(contextVk, gl::ShaderType::Compute, optionBits, &programInfo);
}
ShaderInfo &getShaderInfo() { return mShaderInfo; }
......@@ -169,7 +170,7 @@ class ProgramVk : public ProgramImpl
ANGLE_INLINE angle::Result initProgram(ContextVk *contextVk,
const gl::ShaderType shaderType,
bool enableLineRasterEmulation,
ProgramTransformOptionBits optionBits,
ProgramInfo *programInfo)
{
ASSERT(mShaderInfo.valid());
......@@ -179,7 +180,7 @@ class ProgramVk : public ProgramImpl
if (!programInfo->valid(shaderType))
{
ANGLE_TRY(programInfo->initProgram(contextVk, shaderType, mShaderInfo,
enableLineRasterEmulation));
mExecutable.mVariableInfoMap, optionBits));
}
ASSERT(programInfo->valid(shaderType));
......
......@@ -60,6 +60,10 @@ std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *conte
compileOptions |= SH_IGNORE_PRECISION_QUALIFIERS;
}
// Let compiler detect and emit early fragment test execution mode. We will remove it if
// context state does not allow it
compileOptions |= SH_EARLY_FRAGMENT_TESTS_OPTIMIZATION;
return compileImpl(context, compilerInstance, mData.getSource(), compileOptions | options);
}
......
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