blob: d16b7fa1706445c0f5c4874f33d2f7ee5c816ebd [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 <algorithm>
#include <cctype>
#include <cstring>
#include <limits>
#include <unordered_map>
#include <unordered_set>
#include "compiler/translator/Symbol.h"
#include "compiler/translator/msl/Layout.h"
using namespace sh;
////////////////////////////////////////////////////////////////////////////////
enum class Language
{
Metal,
GLSL,
};
static size_t RoundUpToMultipleOf(size_t x, size_t multiple)
{
const size_t rem = x % multiple;
return rem == 0 ? x : x + (multiple - rem);
}
void Layout::requireAlignment(size_t align, bool pad)
{
alignOf = std::max(alignOf, align);
if (pad)
{
sizeOf = RoundUpToMultipleOf(sizeOf, align);
}
}
bool Layout::operator==(const Layout &other) const
{
return sizeOf == other.sizeOf && alignOf == other.alignOf;
}
void Layout::operator+=(const Layout &other)
{
requireAlignment(other.alignOf, true);
sizeOf += other.sizeOf;
}
void Layout::operator*=(size_t scale)
{
sizeOf *= scale;
}
Layout Layout::operator+(const Layout &other) const
{
auto self = *this;
self += other;
return self;
}
Layout Layout::operator*(size_t scale) const
{
auto self = *this;
self *= scale;
return self;
}
static Layout ScalarLayoutOf(const TType &type, Language language)
{
const TBasicType basicType = type.getBasicType();
switch (basicType)
{
case TBasicType::EbtBool:
return {1, 1};
case TBasicType::EbtInt:
case TBasicType::EbtUInt:
case TBasicType::EbtFloat:
return {4, 4};
default:
if (IsSampler(basicType))
{
switch (language)
{
case Language::Metal:
return {8, 8};
case Language::GLSL:
return {4, 4};
}
}
UNIMPLEMENTED();
return Layout::Invalid();
}
}
static const size_t innerScalesPacked[] = {0, 1, 2, 3, 4};
static const size_t innerScalesUnpacked[] = {0, 1, 2, 4, 4};
Layout sh::MetalLayoutOf(const TType &type, MetalLayoutOfConfig config)
{
ASSERT(type.getNominalSize() <= 4);
ASSERT(type.getSecondarySize() <= 4);
const TLayoutBlockStorage storage = type.getLayoutQualifier().blockStorage;
const bool isPacked = !config.disablePacking && (storage == TLayoutBlockStorage::EbsPacked ||
storage == TLayoutBlockStorage::EbsShared);
if (type.isArray() && !config.maskArray)
{
config.maskArray = true;
const Layout layout = MetalLayoutOf(type, config);
config.maskArray = false;
const size_t vol = type.getArraySizeProduct();
return layout * vol;
}
if (const TStructure *structure = type.getStruct())
{
ASSERT(type.getNominalSize() == 1);
ASSERT(type.getSecondarySize() == 1);
auto config2 = config;
config2.maskArray = false;
auto layout = Layout::Identity();
const TFieldList &fields = structure->fields();
for (const TField *field : fields)
{
layout += MetalLayoutOf(*field->type(), config2);
}
if (config.assumeStructsAreTailPadded)
{
size_t pad =
(kDefaultStructAlignmentSize - layout.sizeOf) % kDefaultStructAlignmentSize;
layout.sizeOf += pad;
}
layout.sizeOf = RoundUpToMultipleOf(layout.sizeOf, layout.alignOf);
return layout;
}
if (config.treatSamplersAsTextureEnv && IsSampler(type.getBasicType()))
{
return {16, 8}; // pointer{8,8} and metal::sampler{8,8}
}
const Layout scalarLayout = ScalarLayoutOf(type, Language::Metal);
if (type.isRank0())
{
return scalarLayout;
}
else if (type.isVector())
{
if (isPacked)
{
const size_t innerScale = innerScalesPacked[type.getNominalSize()];
auto layout = Layout{scalarLayout.sizeOf * innerScale, scalarLayout.alignOf};
return layout;
}
else
{
const size_t innerScale = innerScalesUnpacked[type.getNominalSize()];
auto layout = Layout::Both(scalarLayout.sizeOf * innerScale);
return layout;
}
}
else
{
ASSERT(type.isMatrix());
ASSERT(type.getBasicType() == TBasicType::EbtFloat);
// typeCxR <=> typeR[C]
const size_t innerScale = innerScalesUnpacked[type.getRows()];
const size_t outerScale = static_cast<size_t>(type.getCols());
const size_t n = scalarLayout.sizeOf * innerScale;
return {n * outerScale, n};
}
}
TLayoutBlockStorage sh::Overlay(TLayoutBlockStorage oldStorage, const TType &type)
{
const TLayoutBlockStorage newStorage = type.getLayoutQualifier().blockStorage;
switch (newStorage)
{
case TLayoutBlockStorage::EbsUnspecified:
return oldStorage == TLayoutBlockStorage::EbsUnspecified ? kDefaultLayoutBlockStorage
: oldStorage;
default:
return newStorage;
}
}
TLayoutMatrixPacking sh::Overlay(TLayoutMatrixPacking oldPacking, const TType &type)
{
const TLayoutMatrixPacking newPacking = type.getLayoutQualifier().matrixPacking;
switch (newPacking)
{
case TLayoutMatrixPacking::EmpUnspecified:
return oldPacking == TLayoutMatrixPacking::EmpUnspecified ? kDefaultLayoutMatrixPacking
: oldPacking;
default:
return newPacking;
}
}
bool sh::CanBePacked(TLayoutBlockStorage storage)
{
switch (storage)
{
case TLayoutBlockStorage::EbsPacked:
case TLayoutBlockStorage::EbsShared:
return true;
case TLayoutBlockStorage::EbsStd140:
case TLayoutBlockStorage::EbsStd430:
return false;
case TLayoutBlockStorage::EbsUnspecified:
UNREACHABLE();
return false;
}
}
bool sh::CanBePacked(TLayoutQualifier layoutQualifier)
{
return CanBePacked(layoutQualifier.blockStorage);
}
bool sh::CanBePacked(const TType &type)
{
if (!type.isVector())
{
return false;
}
return CanBePacked(type.getLayoutQualifier());
}
void sh::SetBlockStorage(TType &type, TLayoutBlockStorage storage)
{
auto qual = type.getLayoutQualifier();
qual.blockStorage = storage;
type.setLayoutQualifier(qual);
}
static Layout CommonGlslStructLayoutOf(TField const *const *begin,
TField const *const *end,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking matrixPacking,
const bool maskArray,
const size_t baseAlignment)
{
const bool isPacked =
storage == TLayoutBlockStorage::EbsPacked || storage == TLayoutBlockStorage::EbsShared;
auto layout = Layout::Identity();
for (auto iter = begin; iter != end; ++iter)
{
layout += GlslLayoutOf(*(*iter)->type(), storage, matrixPacking, false);
}
if (!isPacked) // XXX: Correct?
{
layout.sizeOf = RoundUpToMultipleOf(layout.sizeOf, layout.alignOf);
}
layout.requireAlignment(baseAlignment, true);
return layout;
}
static Layout CommonGlslLayoutOf(const TType &type,
const TLayoutBlockStorage storage,
const TLayoutMatrixPacking matrixPacking,
const bool maskArray,
const size_t baseAlignment)
{
ASSERT(storage != TLayoutBlockStorage::EbsUnspecified);
const bool isPacked =
storage == TLayoutBlockStorage::EbsPacked || storage == TLayoutBlockStorage::EbsShared;
if (type.isArray() && !type.isMatrix() && !maskArray)
{
auto layout = GlslLayoutOf(type, storage, matrixPacking, true);
layout *= type.getArraySizeProduct();
layout.requireAlignment(baseAlignment, true);
return layout;
}
if (const TStructure *structure = type.getStruct())
{
ASSERT(type.getNominalSize() == 1);
ASSERT(type.getSecondarySize() == 1);
const TFieldList &fields = structure->fields();
return CommonGlslStructLayoutOf(fields.data(), fields.data() + fields.size(), storage,
matrixPacking, maskArray, baseAlignment);
}
const auto scalarLayout = ScalarLayoutOf(type, Language::GLSL);
if (type.isRank0())
{
return scalarLayout;
}
else if (type.isVector())
{
if (isPacked)
{
const size_t sizeScale = innerScalesPacked[type.getNominalSize()];
const size_t alignScale = innerScalesUnpacked[type.getNominalSize()];
auto layout =
Layout{scalarLayout.sizeOf * sizeScale, scalarLayout.alignOf * alignScale};
return layout;
}
else
{
const size_t innerScale = innerScalesUnpacked[type.getNominalSize()];
auto layout = Layout::Both(scalarLayout.sizeOf * innerScale);
return layout;
}
}
else
{
ASSERT(type.isMatrix());
size_t innerDim;
size_t outerDim;
switch (matrixPacking)
{
case TLayoutMatrixPacking::EmpColumnMajor:
innerDim = static_cast<size_t>(type.getRows());
outerDim = static_cast<size_t>(type.getCols());
break;
case TLayoutMatrixPacking::EmpRowMajor:
innerDim = static_cast<size_t>(type.getCols());
outerDim = static_cast<size_t>(type.getRows());
break;
case TLayoutMatrixPacking::EmpUnspecified:
UNREACHABLE();
innerDim = 0;
outerDim = 0;
}
outerDim *= type.getArraySizeProduct();
const size_t innerScale = innerScalesUnpacked[innerDim];
const size_t n = innerScale * scalarLayout.sizeOf;
Layout layout = {outerDim * n, n};
layout.requireAlignment(baseAlignment, true);
return layout;
}
}
Layout sh::GlslLayoutOf(const TType &type,
TLayoutBlockStorage storage,
TLayoutMatrixPacking matrixPacking,
bool maskArray)
{
ASSERT(type.getNominalSize() <= 4);
ASSERT(type.getSecondarySize() <= 4);
storage = Overlay(storage, type);
matrixPacking = Overlay(matrixPacking, type);
switch (storage)
{
case TLayoutBlockStorage::EbsPacked:
return CommonGlslLayoutOf(type, storage, matrixPacking, maskArray, 1);
case TLayoutBlockStorage::EbsShared:
return CommonGlslLayoutOf(type, storage, matrixPacking, maskArray, 16);
case TLayoutBlockStorage::EbsStd140:
return CommonGlslLayoutOf(type, storage, matrixPacking, maskArray, 16);
case TLayoutBlockStorage::EbsStd430:
return CommonGlslLayoutOf(type, storage, matrixPacking, maskArray, 1);
case TLayoutBlockStorage::EbsUnspecified:
UNREACHABLE();
return Layout::Invalid();
}
}
[[nodiscard]] Layout sh::GlslStructLayoutOf(TField const *const *begin,
TField const *const *end,
TLayoutBlockStorage storage,
TLayoutMatrixPacking matrixPacking,
bool maskArray)
{
ASSERT(storage != TLayoutBlockStorage::EbsUnspecified);
ASSERT(matrixPacking != TLayoutMatrixPacking::EmpUnspecified);
switch (storage)
{
case TLayoutBlockStorage::EbsPacked:
return CommonGlslStructLayoutOf(begin, end, storage, matrixPacking, maskArray, 1);
case TLayoutBlockStorage::EbsShared:
return CommonGlslStructLayoutOf(begin, end, storage, matrixPacking, maskArray, 16);
case TLayoutBlockStorage::EbsStd140:
return CommonGlslStructLayoutOf(begin, end, storage, matrixPacking, maskArray, 16);
case TLayoutBlockStorage::EbsStd430:
return CommonGlslStructLayoutOf(begin, end, storage, matrixPacking, maskArray, 1);
case TLayoutBlockStorage::EbsUnspecified:
UNREACHABLE();
return Layout::Invalid();
}
}