blob: 00318da8f12950e93db3e3266aebc7b1d032fef6 [file] [log] [blame]
//
// 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.
//
#include "compiler/translator/msl/Pipeline.h"
#include "compiler/translator/tree_util/BuiltIn.h"
using namespace sh;
////////////////////////////////////////////////////////////////////////////////
#define VARIANT_NAME(variant, base) (variant == Variant::Modified ? base "Mod" : base)
bool Pipeline::uses(const TVariable &var) const
{
if (var.symbolType() == SymbolType::Empty)
{
return false;
}
if (globalInstanceVar)
{
return &var == globalInstanceVar;
}
const TType &nodeType = var.getType();
const TQualifier qualifier = nodeType.getQualifier();
switch (type)
{
case Type::VertexIn:
switch (qualifier)
{
case TQualifier::EvqAttribute:
case TQualifier::EvqVertexIn:
return true;
default:
return false;
}
case Type::VertexOut:
switch (qualifier)
{
case TQualifier::EvqVaryingOut:
case TQualifier::EvqVertexOut:
case TQualifier::EvqPosition:
case TQualifier::EvqPointSize:
case TQualifier::EvqClipDistance:
case TQualifier::EvqSmoothOut:
case TQualifier::EvqFlatOut:
case TQualifier::EvqNoPerspectiveOut:
case TQualifier::EvqCentroidOut:
case TQualifier::EvqSampleOut:
case TQualifier::EvqNoPerspectiveCentroidOut:
case TQualifier::EvqNoPerspectiveSampleOut:
return true;
default:
return false;
}
case Type::FragmentIn:
switch (qualifier)
{
case TQualifier::EvqVaryingIn:
case TQualifier::EvqFragmentIn:
case TQualifier::EvqSmoothIn:
case TQualifier::EvqFlatIn:
case TQualifier::EvqNoPerspectiveIn:
case TQualifier::EvqCentroidIn:
case TQualifier::EvqSampleIn:
case TQualifier::EvqNoPerspectiveCentroidIn:
case TQualifier::EvqNoPerspectiveSampleIn:
return true;
default:
return false;
}
case Type::FragmentOut:
switch (qualifier)
{
case TQualifier::EvqFragmentOut:
case TQualifier::EvqFragmentInOut:
case TQualifier::EvqFragColor:
case TQualifier::EvqFragData:
case TQualifier::EvqFragDepth:
case TQualifier::EvqSecondaryFragColorEXT:
case TQualifier::EvqSecondaryFragDataEXT:
return true;
case TQualifier::EvqSampleMask:
return var.symbolType() == SymbolType::AngleInternal;
default:
return false;
}
case Type::UserUniforms:
switch (qualifier)
{
case TQualifier::EvqUniform:
return true;
default:
return false;
}
case Type::NonConstantGlobals:
switch (qualifier)
{
case TQualifier::EvqGlobal:
case TQualifier::EvqSamplePosition:
return true;
case TQualifier::EvqSampleMaskIn:
case TQualifier::EvqSampleMask:
return var.symbolType() == SymbolType::BuiltIn;
case TQualifier::EvqUniform:
return var.name() == "gl_NumSamples";
default:
return false;
}
case Type::InvocationVertexGlobals:
switch (qualifier)
{
case TQualifier::EvqVertexID:
return true;
default:
return false;
}
case Type::InvocationFragmentGlobals:
switch (qualifier)
{
case TQualifier::EvqFragCoord:
case TQualifier::EvqPointCoord:
case TQualifier::EvqFrontFacing:
case TQualifier::EvqSampleID:
return true;
case TQualifier::EvqSampleMaskIn:
return var.symbolType() == SymbolType::AngleInternal;
default:
return false;
}
case Type::UniformBuffer:
switch (qualifier)
{
case TQualifier::EvqBuffer:
return true;
default:
return false;
}
case Type::AngleUniforms:
UNREACHABLE(); // globalInstanceVar should be non-null and thus never reach here.
return false;
case Type::Texture:
return IsSampler(nodeType.getBasicType());
case Type::Image:
return IsImage(nodeType.getBasicType());
case Type::InstanceId:
return Name(var) == Name(*BuiltInVariable::gl_InstanceID());
}
}
Name Pipeline::getStructTypeName(Variant variant) const
{
const char *name;
switch (type)
{
case Type::VertexIn:
name = VARIANT_NAME(variant, "VertexIn");
break;
case Type::VertexOut:
name = VARIANT_NAME(variant, "VertexOut");
break;
case Type::FragmentIn:
name = VARIANT_NAME(variant, "FragmentIn");
break;
case Type::FragmentOut:
name = VARIANT_NAME(variant, "FragmentOut");
break;
case Type::UserUniforms:
name = VARIANT_NAME(variant, "UserUniforms");
break;
case Type::AngleUniforms:
name = VARIANT_NAME(variant, "AngleUniforms");
break;
case Type::NonConstantGlobals:
name = VARIANT_NAME(variant, "NonConstGlobals");
break;
case Type::InvocationVertexGlobals:
name = VARIANT_NAME(variant, "InvocationVertexGlobals");
break;
case Type::InvocationFragmentGlobals:
name = VARIANT_NAME(variant, "InvocationFragmentGlobals");
break;
case Type::Texture:
name = VARIANT_NAME(variant, "TextureEnvs");
break;
case Type::Image:
name = VARIANT_NAME(variant, "Images");
break;
case Type::InstanceId:
name = VARIANT_NAME(variant, "InstanceId");
break;
case Type::UniformBuffer:
name = VARIANT_NAME(variant, "UniformBuffer");
}
return Name(name);
}
Name Pipeline::getStructInstanceName(Variant variant) const
{
const char *name;
switch (type)
{
case Type::VertexIn:
name = VARIANT_NAME(variant, "vertexIn");
break;
case Type::VertexOut:
// Used by name in compiler/translator/tree_ops/msl/RewriteOutArgs.cpp
name = VARIANT_NAME(variant, "vertexOut");
break;
case Type::FragmentIn:
name = VARIANT_NAME(variant, "fragmentIn");
break;
case Type::FragmentOut:
// Used by name in compiler/translator/tree_ops/msl/RewriteOutArgs.cpp
name = VARIANT_NAME(variant, "fragmentOut");
break;
case Type::UserUniforms:
name = VARIANT_NAME(variant, "userUniforms");
break;
case Type::AngleUniforms:
name = VARIANT_NAME(variant, "angleUniforms");
break;
case Type::NonConstantGlobals:
// Used by name in compiler/translator/tree_ops/msl/RewriteOutArgs.cpp
name = VARIANT_NAME(variant, "nonConstGlobals");
break;
case Type::InvocationVertexGlobals:
name = VARIANT_NAME(variant, "invocationVertexGlobals");
break;
case Type::InvocationFragmentGlobals:
name = VARIANT_NAME(variant, "invocationFragmentGlobals");
break;
case Type::Texture:
name = VARIANT_NAME(variant, "textureEnvs");
break;
case Type::Image:
name = VARIANT_NAME(variant, "images");
break;
case Type::InstanceId:
name = VARIANT_NAME(variant, "instanceId");
break;
case Type::UniformBuffer:
name = VARIANT_NAME(variant, "uniformBuffer");
}
return Name(name);
}
static bool AllowPacking(Pipeline::Type type)
{
return false;
}
static bool AllowPadding(Pipeline::Type type)
{
using Type = Pipeline::Type;
switch (type)
{
case Type::VertexIn:
case Type::VertexOut:
case Type::FragmentIn:
case Type::FragmentOut:
case Type::AngleUniforms:
case Type::NonConstantGlobals:
case Type::InvocationVertexGlobals:
case Type::InvocationFragmentGlobals:
return true;
case Type::UserUniforms:
case Type::Texture:
case Type::Image:
case Type::InstanceId:
case Type::UniformBuffer:
return false;
}
}
enum Compare
{
LT,
LTE,
EQ,
GTE,
GT,
};
template <typename T>
static bool CompareBy(Compare op, const T &x, const T &y)
{
switch (op)
{
case LT:
return x < y;
case LTE:
return x <= y;
case EQ:
return x == y;
case GTE:
return x >= y;
case GT:
return x > y;
}
}
template <TBasicType BT, Compare Cmp, uint8_t MatchDim, uint8_t NewDim>
static uint8_t SaturateVectorOf(const TField &field)
{
static_assert(NewDim >= MatchDim, "");
const TType &type = *field.type();
ASSERT(type.isScalar() || type.isVector());
const bool cond = type.getBasicType() == BT && !type.isArray() &&
CompareBy(Cmp, type.getNominalSize(), MatchDim) &&
type.getQualifier() != TQualifier::EvqFragDepth;
if (cond)
{
return NewDim;
}
return 0;
}
ModifyStructConfig Pipeline::externalStructModifyConfig() const
{
using Pred = ModifyStructConfig::Predicate;
using SatVec = ModifyStructConfig::SaturateVector;
ModifyStructConfig config(
isPipelineOut() ? ConvertType::OriginalToModified : ConvertType::ModifiedToOriginal,
AllowPacking(type), AllowPadding(type));
config.externalAddressSpace = externalAddressSpace();
switch (type)
{
case Type::VertexIn:
config.inlineArray = Pred::True;
config.splitMatrixColumns = Pred::True;
config.inlineStruct = Pred::True;
break;
case Type::VertexOut:
config.inlineArray = [](const TField &field) -> bool {
// Clip distance output uses float[n] type instead of metal::array.
return field.type()->getQualifier() != TQualifier::EvqClipDistance;
};
config.splitMatrixColumns = Pred::True;
config.inlineStruct = Pred::True;
break;
case Type::FragmentIn:
config.inlineArray = Pred::True;
config.splitMatrixColumns = Pred::True;
config.inlineStruct = Pred::True;
break;
case Type::FragmentOut:
config.inlineArray = Pred::True;
config.splitMatrixColumns = Pred::True;
config.inlineStruct = Pred::True;
config.saturateScalarOrVector = [](const TField &field) -> uint8_t {
if (field.type()->getQualifier() == TQualifier::EvqSampleMask)
{
return 1;
}
if (uint8_t s = SaturateVectorOf<TBasicType::EbtInt, LT, 4, 4>(field))
{
return s;
}
if (uint8_t s = SaturateVectorOf<TBasicType::EbtUInt, LT, 4, 4>(field))
{
return s;
}
if (uint8_t s = SaturateVectorOf<TBasicType::EbtFloat, LT, 4, 4>(field))
{
return s;
}
return 0;
};
break;
case Type::UserUniforms:
config.promoteBoolToUint = Pred::False;
config.saturateMatrixRows = SatVec::DontSaturate;
config.saturateScalarOrVectorArrays = SatVec::DontSaturate;
config.recurseStruct = Pred::True;
break;
case Type::AngleUniforms:
config.initialBlockStorage = TLayoutBlockStorage::EbsStd430; // XXX: Correct?
break;
case Type::NonConstantGlobals:
break;
case Type::UniformBuffer:
config.promoteBoolToUint = Pred::False;
config.saturateMatrixRows = SatVec::DontSaturate;
config.saturateScalarOrVectorArrays = SatVec::DontSaturate;
config.recurseStruct = Pred::True;
break;
case Type::InvocationVertexGlobals:
case Type::InvocationFragmentGlobals:
case Type::Texture:
case Type::Image:
case Type::InstanceId:
break;
}
return config;
}
bool Pipeline::alwaysRequiresLocalVariableDeclarationInMain() const
{
switch (type)
{
case Type::VertexIn:
case Type::FragmentIn:
case Type::UserUniforms:
case Type::AngleUniforms:
case Type::UniformBuffer:
case Type::Image:
return false;
case Type::VertexOut:
case Type::FragmentOut:
case Type::NonConstantGlobals:
case Type::InvocationVertexGlobals:
case Type::InvocationFragmentGlobals:
case Type::Texture:
case Type::InstanceId:
return true;
}
}
bool Pipeline::isPipelineOut() const
{
switch (type)
{
case Type::VertexIn:
case Type::FragmentIn:
case Type::UserUniforms:
case Type::AngleUniforms:
case Type::NonConstantGlobals:
case Type::InvocationVertexGlobals:
case Type::InvocationFragmentGlobals:
case Type::Texture:
case Type::Image:
case Type::InstanceId:
case Type::UniformBuffer:
return false;
case Type::VertexOut:
case Type::FragmentOut:
return true;
}
}
AddressSpace Pipeline::externalAddressSpace() const
{
switch (type)
{
case Type::VertexIn:
case Type::FragmentIn:
case Type::NonConstantGlobals:
case Type::InvocationVertexGlobals:
case Type::InvocationFragmentGlobals:
case Type::Texture:
case Type::Image:
case Type::InstanceId:
case Type::FragmentOut:
case Type::VertexOut:
return AddressSpace::Thread;
case Type::UserUniforms:
case Type::AngleUniforms:
case Type::UniformBuffer:
return AddressSpace::Constant;
}
}
bool PipelineStructs::matches(const TStructure &s, bool internal, bool external) const
{
PipelineScoped<TStructure> ps[] = {
fragmentIn,
fragmentOut,
vertexIn,
vertexOut,
userUniforms,
/* angleUniforms, */
nonConstantGlobals,
invocationVertexGlobals,
invocationFragmentGlobals,
uniformBuffers,
texture,
instanceId,
};
for (const auto &p : ps)
{
if (internal && p.internal == &s)
{
return true;
}
if (external && p.external == &s)
{
return true;
}
}
return false;
}