summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/sksl/SkSLMemoryLayout.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/sksl/SkSLMemoryLayout.h')
-rw-r--r--gfx/skia/skia/src/sksl/SkSLMemoryLayout.h211
1 files changed, 211 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/sksl/SkSLMemoryLayout.h b/gfx/skia/skia/src/sksl/SkSLMemoryLayout.h
new file mode 100644
index 0000000000..8389c00346
--- /dev/null
+++ b/gfx/skia/skia/src/sksl/SkSLMemoryLayout.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKIASL_MEMORYLAYOUT
+#define SKIASL_MEMORYLAYOUT
+
+#include <algorithm>
+
+#include "src/sksl/ir/SkSLType.h"
+
+namespace SkSL {
+
+class MemoryLayout {
+public:
+ enum class Standard {
+ // GLSL std140 layout as described in OpenGL Spec v4.5, 7.6.2.2.
+ k140,
+
+ // GLSL std430 layout. This layout is like std140 but with optimizations. This layout can
+ // ONLY be used with shader storage blocks.
+ k430,
+
+ // MSL memory layout.
+ kMetal,
+
+ // WebGPU Shading Language buffer layout constraints for the uniform address space.
+ kWGSLUniform,
+
+ // WebGPU Shading Language buffer layout constraints for the storage address space.
+ kWGSLStorage,
+ };
+
+ MemoryLayout(Standard std)
+ : fStd(std) {}
+
+ bool isWGSL() const { return fStd == Standard::kWGSLUniform || fStd == Standard::kWGSLStorage; }
+
+ bool isMetal() const { return fStd == Standard::kMetal; }
+
+ /**
+ * WGSL and std140 require various types of variables (structs, arrays, and matrices) in the
+ * uniform address space to be rounded up to the nearest multiple of 16. This function performs
+ * the rounding depending on the given `type` and the current memory layout standard.
+ *
+ * (For WGSL, see https://www.w3.org/TR/WGSL/#address-space-layout-constraints).
+ */
+ size_t roundUpIfNeeded(size_t raw, Type::TypeKind type) const {
+ if (fStd == Standard::k140) {
+ return roundUp16(raw);
+ }
+ // WGSL uniform matrix layout is simply the alignment of the matrix columns and
+ // doesn't have a 16-byte multiple alignment constraint.
+ if (fStd == Standard::kWGSLUniform && type != Type::TypeKind::kMatrix) {
+ return roundUp16(raw);
+ }
+ return raw;
+ }
+
+ /**
+ * Rounds up the integer `n` to the smallest multiple of 16 greater than `n`.
+ */
+ size_t roundUp16(size_t n) const { return (n + 15) & ~15; }
+
+ /**
+ * Returns a type's required alignment when used as a standalone variable.
+ */
+ size_t alignment(const Type& type) const {
+ // See OpenGL Spec 7.6.2.2 Standard Uniform Block Layout
+ switch (type.typeKind()) {
+ case Type::TypeKind::kScalar:
+ case Type::TypeKind::kAtomic:
+ return this->size(type);
+ case Type::TypeKind::kVector:
+ return GetVectorAlignment(this->size(type.componentType()), type.columns());
+ case Type::TypeKind::kMatrix:
+ return this->roundUpIfNeeded(
+ GetVectorAlignment(this->size(type.componentType()), type.rows()),
+ type.typeKind());
+ case Type::TypeKind::kArray:
+ return this->roundUpIfNeeded(this->alignment(type.componentType()),
+ type.typeKind());
+ case Type::TypeKind::kStruct: {
+ size_t result = 0;
+ for (const auto& f : type.fields()) {
+ size_t alignment = this->alignment(*f.fType);
+ if (alignment > result) {
+ result = alignment;
+ }
+ }
+ return this->roundUpIfNeeded(result, type.typeKind());
+ }
+ default:
+ SK_ABORT("cannot determine alignment of type %s", type.displayName().c_str());
+ }
+ }
+
+ /**
+ * For matrices and arrays, returns the number of bytes from the start of one entry (row, in
+ * the case of matrices) to the start of the next.
+ */
+ size_t stride(const Type& type) const {
+ switch (type.typeKind()) {
+ case Type::TypeKind::kMatrix:
+ return this->alignment(type);
+ case Type::TypeKind::kArray: {
+ int stride = this->size(type.componentType());
+ if (stride > 0) {
+ int align = this->alignment(type.componentType());
+ stride += align - 1;
+ stride -= stride % align;
+ stride = this->roundUpIfNeeded(stride, type.typeKind());
+ }
+ return stride;
+ }
+ default:
+ SK_ABORT("type does not have a stride");
+ }
+ }
+
+ /**
+ * Returns the size of a type in bytes. Returns 0 if the given type is not supported.
+ */
+ size_t size(const Type& type) const {
+ switch (type.typeKind()) {
+ case Type::TypeKind::kScalar:
+ if (type.isBoolean()) {
+ if (this->isWGSL()) {
+ return 0;
+ }
+ return 1;
+ }
+ if ((this->isMetal() || this->isWGSL()) && !type.highPrecision() &&
+ type.isNumber()) {
+ return 2;
+ }
+ return 4;
+ case Type::TypeKind::kAtomic:
+ // Our atomic types (currently atomicUint) always occupy 4 bytes.
+ return 4;
+ case Type::TypeKind::kVector:
+ if (this->isMetal() && type.columns() == 3) {
+ return 4 * this->size(type.componentType());
+ }
+ return type.columns() * this->size(type.componentType());
+ case Type::TypeKind::kMatrix: // fall through
+ case Type::TypeKind::kArray:
+ return type.isUnsizedArray() ? 0 : (type.columns() * this->stride(type));
+ case Type::TypeKind::kStruct: {
+ size_t total = 0;
+ for (const auto& f : type.fields()) {
+ size_t alignment = this->alignment(*f.fType);
+ if (total % alignment != 0) {
+ total += alignment - total % alignment;
+ }
+ SkASSERT(total % alignment == 0);
+ total += this->size(*f.fType);
+ }
+ size_t alignment = this->alignment(type);
+ SkASSERT(!type.fields().size() ||
+ (0 == alignment % this->alignment(*type.fields()[0].fType)));
+ return (total + alignment - 1) & ~(alignment - 1);
+ }
+ default:
+ SK_ABORT("cannot determine size of type %s", type.displayName().c_str());
+ }
+ }
+
+ /**
+ * Not all types are compatible with memory layout.
+ */
+ size_t isSupported(const Type& type) const {
+ switch (type.typeKind()) {
+ case Type::TypeKind::kAtomic:
+ return true;
+
+ case Type::TypeKind::kScalar:
+ // bool and short are not host-shareable in WGSL.
+ return !this->isWGSL() ||
+ (!type.isBoolean() && (type.isFloat() || type.highPrecision()));
+
+ case Type::TypeKind::kVector:
+ case Type::TypeKind::kMatrix:
+ case Type::TypeKind::kArray:
+ return this->isSupported(type.componentType());
+
+ case Type::TypeKind::kStruct:
+ return std::all_of(
+ type.fields().begin(), type.fields().end(), [this](const Type::Field& f) {
+ return this->isSupported(*f.fType);
+ });
+
+ default:
+ return false;
+ }
+ }
+
+private:
+ static size_t GetVectorAlignment(size_t componentSize, int columns) {
+ return componentSize * (columns + columns % 2);
+ }
+
+ const Standard fStd;
+};
+
+} // namespace SkSL
+
+#endif