From ef8907c69f77b0c4e7b7a64434fda72ab7611091 Mon Sep 17 00:00:00 2001 From: Robert Phillips Date: Tue, 26 Oct 2021 11:39:20 -0400 Subject: [PATCH] [graphite] Rename UniformDataManager to UniformManager and update Gerrit didn't do so well on this CL. What's changed in the UniformManager is: a lot of unused stuff was pared out of the UniformDataManager the Write methods now return the number of bytes written in the call (incl. padding) all the "#ifdef SK_DEBUG" stuff is new to validate against the prior way of writing uniforms Bug: skia:12466 Change-Id: I172a74273c774257f751750aef37f4bd4c4e7e7b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/463458 Reviewed-by: Greg Daniel Reviewed-by: Jim Van Verth Commit-Queue: Robert Phillips --- .../include/private/GraphiteTypesPriv.h | 1 + experimental/graphite/src/Uniform.h | 58 ++ .../graphite/src/UniformDataManager.cpp | 292 ----------- .../graphite/src/UniformDataManager.h | 81 --- experimental/graphite/src/UniformManager.cpp | 494 ++++++++++++++++++ experimental/graphite/src/UniformManager.h | 47 ++ gn/graphite.gni | 5 +- 7 files changed, 603 insertions(+), 375 deletions(-) create mode 100644 experimental/graphite/src/Uniform.h delete mode 100644 experimental/graphite/src/UniformDataManager.cpp delete mode 100644 experimental/graphite/src/UniformDataManager.h create mode 100644 experimental/graphite/src/UniformManager.cpp create mode 100644 experimental/graphite/src/UniformManager.h diff --git a/experimental/graphite/include/private/GraphiteTypesPriv.h b/experimental/graphite/include/private/GraphiteTypesPriv.h index 8de7e4662b..3b37e577f9 100644 --- a/experimental/graphite/include/private/GraphiteTypesPriv.h +++ b/experimental/graphite/include/private/GraphiteTypesPriv.h @@ -118,6 +118,7 @@ enum class SLType { kLast = kInput }; +static const int kSLTypeCount = static_cast(SLType::kLast) + 1; enum class CType : unsigned { // Any float/half, vector of floats/half, or matrices of floats/halfs are a tightly diff --git a/experimental/graphite/src/Uniform.h b/experimental/graphite/src/Uniform.h new file mode 100644 index 0000000000..17e056130e --- /dev/null +++ b/experimental/graphite/src/Uniform.h @@ -0,0 +1,58 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_Uniform_DEFINED +#define skgpu_Uniform_DEFINED + +#include "experimental/graphite/include/private/GraphiteTypesPriv.h" + +namespace skgpu { + +/** + * Describes a uniform. Uniforms consist of: + * type: The type of the uniform + * count: Number of elements of 'type' in the array or kNonArray if not an array. + */ +class Uniform { +public: + static constexpr int kNonArray = 0; + + constexpr Uniform(const char* name, SLType type) : Uniform(name, type, kNonArray) {} + + constexpr Uniform(const char* name, SLType type, int count) + : fType (static_cast(type)) + , fCount (static_cast(count)) +#ifdef SK_DEBUG + , fName (name) +#endif + { + } + + constexpr Uniform(const Uniform&) = default; + Uniform& operator=(const Uniform&) = default; + + constexpr bool isInitialized() const { return this->type() != SLType::kVoid; } + +#ifdef SK_DEBUG + constexpr const char* name() const { return fName; } +#endif + constexpr SLType type() const { return static_cast(fType); } + constexpr uint32_t count() const { return static_cast(fCount); } + +private: + uint32_t fType : 6; + uint32_t fCount : 26; +#ifdef SK_DEBUG + const char* fName; +#endif + + static_assert(kSLTypeCount <= (1 << 6)); +}; + +} // namespace skgpu + +#endif // skgpu_Uniform_DEFINED diff --git a/experimental/graphite/src/UniformDataManager.cpp b/experimental/graphite/src/UniformDataManager.cpp deleted file mode 100644 index 58867662ee..0000000000 --- a/experimental/graphite/src/UniformDataManager.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "experimental/graphite/src/UniformDataManager.h" -#include "include/core/SkMatrix.h" -#include "include/private/SkTemplates.h" - -// ensure that these types are the sizes the uniform data is expecting -static_assert(sizeof(int32_t) == 4); -static_assert(sizeof(float) == 4); - -static constexpr int kNonArray = 0; - -namespace skgpu { - -////////////////////////////////////////////////////////////////////////////// - -UniformDataManager::UniformManager::UniformManager(Layout layout) : fLayout(layout) {} - -template -static constexpr size_t tight_vec_size(int vecLength) { - return sizeof(BaseType) * vecLength; -} - -/** - * From Section 7.6.2.2 "Standard Uniform Block Layout": - * 1. If the member is a scalar consuming N basic machine units, the base alignment is N. - * 2. If the member is a two- or four-component vector with components consuming N basic machine - * units, the base alignment is 2N or 4N, respectively. - * 3. If the member is a three-component vector with components consuming N - * basic machine units, the base alignment is 4N. - * 4. If the member is an array of scalars or vectors, the base alignment and array - * stride are set to match the base alignment of a single array element, according - * to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The - * array may have padding at the end; the base offset of the member following - * the array is rounded up to the next multiple of the base alignment. - * 5. If the member is a column-major matrix with C columns and R rows, the - * matrix is stored identically to an array of C column vectors with R components each, - * according to rule (4). - * 6. If the member is an array of S column-major matrices with C columns and - * R rows, the matrix is stored identically to a row of S × C column vectors - * with R components each, according to rule (4). - * 7. If the member is a row-major matrix with C columns and R rows, the matrix - * is stored identically to an array of R row vectors with C components each, - * according to rule (4). - * 8. If the member is an array of S row-major matrices with C columns and R - * rows, the matrix is stored identically to a row of S × R row vectors with C - * components each, according to rule (4). - * 9. If the member is a structure, the base alignment of the structure is N, where - * N is the largest base alignment value of any of its members, and rounded - * up to the base alignment of a vec4. The individual members of this substructure are then - * assigned offsets by applying this set of rules recursively, - * where the base offset of the first member of the sub-structure is equal to the - * aligned offset of the structure. The structure may have padding at the end; - * the base offset of the member following the sub-structure is rounded up to - * the next multiple of the base alignment of the structure. - * 10. If the member is an array of S structures, the S elements of the array are laid - * out in order, according to rule (9). - */ -template -struct Rules140 { - /** - * For an array of scalars or vectors this returns the stride between array elements. For - * matrices or arrays of matrices this returns the stride between columns of the matrix. Note - * that for single (non-array) scalars or vectors we don't require a stride. - */ - static constexpr size_t Stride(int count) { - SkASSERT(count >= 1 || count == kNonArray); - static_assert(RowsOrVecLength >= 1 && RowsOrVecLength <= 4); - static_assert(Cols >= 1 && Cols <= 4); - if (Cols != 1) { - // This is a matrix or array of matrices. We return the stride between columns. - SkASSERT(RowsOrVecLength > 1); - return Rules140::Stride(1); - } - if (count == 0) { - // Stride doesn't matter for a non-array. - return 0; - } - - // Rule 4. - - // Alignment of vec4 by Rule 2. - constexpr size_t kVec4Alignment = tight_vec_size(4); - // Get alignment of a single vector of BaseType by Rule 1, 2, or 3 - int n = RowsOrVecLength == 3 ? 4 : RowsOrVecLength; - size_t kElementAlignment = tight_vec_size(n); - // Round kElementAlignment up to multiple of kVec4Alignment. - size_t m = (kElementAlignment + kVec4Alignment - 1) / kVec4Alignment; - return m * kVec4Alignment; - } -}; - -/** - * When using the std430 storage layout, shader storage blocks will be laid out in buffer storage - * identically to uniform and shader storage blocks using the std140 layout, except that the base - * alignment and stride of arrays of scalars and vectors in rule 4 and of structures in rule 9 are - * not rounded up a multiple of the base alignment of a vec4. - */ -template -struct Rules430 { - static constexpr size_t Stride(int count) { - SkASSERT(count >= 1 || count == kNonArray); - static_assert(RowsOrVecLength >= 1 && RowsOrVecLength <= 4); - static_assert(Cols >= 1 && Cols <= 4); - - if (Cols != 1) { - // This is a matrix or array of matrices. We return the stride between columns. - SkASSERT(RowsOrVecLength > 1); - return Rules430::Stride(1); - } - if (count == 0) { - // Stride doesn't matter for a non-array. - return 0; - } - // Rule 4 without the round up to a multiple of align-of vec4. - return tight_vec_size(RowsOrVecLength == 3 ? 4 : RowsOrVecLength); - } -}; - -// The strides used here were derived from the rules we've imposed on ourselves in -// GrMtlPipelineStateDataManger. Everything is tight except 3-component which have the stride of -// their 4-component equivalents. -template -struct RulesMetal { - static constexpr size_t Stride(int count) { - SkASSERT(count >= 1 || count == kNonArray); - static_assert(RowsOrVecLength >= 1 && RowsOrVecLength <= 4); - static_assert(Cols >= 1 && Cols <= 4); - if (Cols != 1) { - // This is a matrix or array of matrices. We return the stride between columns. - SkASSERT(RowsOrVecLength > 1); - return RulesMetal::Stride(1); - } - if (count == 0) { - // Stride doesn't matter for a non-array. - return 0; - } - return tight_vec_size(RowsOrVecLength == 3 ? 4 : RowsOrVecLength); - } -}; - -template class Rules> -class Writer { -private: - template - static void Write(void *dst, int n, const BaseType v[]) { - if (dst) { - size_t stride = Rules::Stride(n); - n = (n == kNonArray) ? 1 : n; - n *= Cols; - if (stride == RowsOrVecLength * sizeof(BaseType)) { - std::memcpy(dst, v, n * stride); - } else { - for (int i = 0; i < n; ++i) { - std::memcpy(dst, v, RowsOrVecLength * sizeof(BaseType)); - v += RowsOrVecLength; - dst = SkTAddOffset(dst, stride); - } - } - } - } - - static void WriteSkMatrices(void *d, int n, const SkMatrix m[]) { - size_t offset = 0; - for (int i = 0; i < std::max(n, 1); ++i) { - float mt[] = { - m[i].get(SkMatrix::kMScaleX), - m[i].get(SkMatrix::kMSkewY), - m[i].get(SkMatrix::kMPersp0), - m[i].get(SkMatrix::kMSkewX), - m[i].get(SkMatrix::kMScaleY), - m[i].get(SkMatrix::kMPersp1), - m[i].get(SkMatrix::kMTransX), - m[i].get(SkMatrix::kMTransY), - m[i].get(SkMatrix::kMPersp2), - }; - Write(SkTAddOffset(d, offset), 1, mt); - // Stride() will give us the stride of each column, so mul by 3 to get matrix stride. - offset += 3 * Rules::Stride(1); - } - } - -public: - static void WriteUniform(SLType type, CType ctype, void *d, int n, const void *v) { - SkASSERT(d); - SkASSERT(n >= 1 || n == kNonArray); - switch (type) { - case SLType::kInt: - return Write(d, n, static_cast(v)); - - case SLType::kInt2: - return Write(d, n, static_cast(v)); - - case SLType::kInt3: - return Write(d, n, static_cast(v)); - - case SLType::kInt4: - return Write(d, n, static_cast(v)); - - case SLType::kHalf: - case SLType::kFloat: - return Write(d, n, static_cast(v)); - - case SLType::kHalf2: - case SLType::kFloat2: - return Write(d, n, static_cast(v)); - - case SLType::kHalf3: - case SLType::kFloat3: - return Write(d, n, static_cast(v)); - - case SLType::kHalf4: - case SLType::kFloat4: - return Write(d, n, static_cast(v)); - - case SLType::kHalf2x2: - case SLType::kFloat2x2: - return Write(d, n, static_cast(v)); - - case SLType::kHalf3x3: - case SLType::kFloat3x3: { - switch (ctype) { - case CType::kDefault: - return Write(d, n, static_cast(v)); - case CType::kSkMatrix: - return WriteSkMatrices(d, n, static_cast(v)); - } - SkUNREACHABLE; - } - - case SLType::kHalf4x4: - case SLType::kFloat4x4: - return Write(d, n, static_cast(v)); - - default: - SK_ABORT("Unexpected uniform type"); - } - } -}; - -bool UniformDataManager::UniformManager::writeUniforms(/*const GrProgramInfo &info,*/ - void *buffer) { - decltype(&Writer::WriteUniform) write; - switch (fLayout) { - case Layout::kStd140: - write = Writer::WriteUniform; - break; - case Layout::kStd430: - write = Writer::WriteUniform; - break; - case Layout::kMetal: - write = Writer::WriteUniform; - break; - } - - bool wrote = false; - - { - // TODO: loop over the GrProgramInfo replacement, writing uniforms. - int src = 22; - int dst[2]; - write(SLType::kInt, CType::kDefault, &dst, kNonArray, &src); - wrote = true; - } - - return wrote; -} - -////////////////////////////////////////////////////////////////////////////// - -UniformDataManager::UniformDataManager(Layout layout, - uint32_t uniformCount, - uint32_t uniformSize) - : fUniformSize(uniformSize) - , fUniformsDirty(false) - , fUniformManager(layout) { - fUniformData.reset(uniformSize); - fUniforms.reserve(uniformCount); -} - -void UniformDataManager::setUniforms(/*const GrProgramInfo &info*/) { - if (fUniformManager.writeUniforms(/*info,*/ fUniformData.get())) { - this->markDirty(); - } -} - -} // namespace skgpu diff --git a/experimental/graphite/src/UniformDataManager.h b/experimental/graphite/src/UniformDataManager.h deleted file mode 100644 index 8c444f447c..0000000000 --- a/experimental/graphite/src/UniformDataManager.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef skgpu_UniformDataManager_DEFINED -#define skgpu_UniformDataManager_DEFINED - -#include -#include "experimental/graphite/include/private/GraphiteTypesPriv.h" -#include "src/core/SkAutoMalloc.h" - -namespace skgpu { - -/** - * Used to store uniforms for a program in a CPU buffer that can be uploaded to a UBO. - */ -class UniformDataManager { -public: - enum class Layout { - kStd140, - kStd430, - kMetal, /** This is our own self-imposed layout we use for Metal. */ - }; - - struct NewUniform { - size_t indexInProcessor = ~0; - SLType type = SLType::kVoid; - int count = 0; - uint32_t offset = 0; - }; - - UniformDataManager(Layout layout, - uint32_t uniformCount, - uint32_t uniformSize); - - // For the uniform data to be dirty so that we will reupload on the next use. - void markDirty() { fUniformsDirty = true; } - - // TODO: replace GrProgramInfo w/ something more generic - void setUniforms(/*const GrProgramInfo &info*/); - -protected: - struct Uniform { - uint32_t fOffset; - SkDEBUGCODE( - SLType fType; - int fArrayCount; - ); - }; - - uint32_t fUniformSize; - - std::vector fUniforms; - - mutable SkAutoMalloc fUniformData; - mutable bool fUniformsDirty; - -private: - class UniformManager { - public: - UniformManager(Layout layout); - - // TODO: replace GrProgramInfo w/ something more generic - bool writeUniforms(/*const GrProgramInfo &info,*/ void *buffer); - - private: - using ProgramUniforms = std::vector>; - - ProgramUniforms fUniforms; - Layout fLayout; - }; - - UniformManager fUniformManager; -}; - -} // namespace skgpu - -#endif // skgpu_UniformDataManager_DEFINED diff --git a/experimental/graphite/src/UniformManager.cpp b/experimental/graphite/src/UniformManager.cpp new file mode 100644 index 0000000000..31ccf554fd --- /dev/null +++ b/experimental/graphite/src/UniformManager.cpp @@ -0,0 +1,494 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "experimental/graphite/src/UniformManager.h" +#include "include/core/SkMatrix.h" +#include "include/private/SkTemplates.h" + +// ensure that these types are the sizes the uniform data is expecting +static_assert(sizeof(int32_t) == 4); +static_assert(sizeof(float) == 4); + +namespace skgpu { + +////////////////////////////////////////////////////////////////////////////// + +UniformManager::UniformManager(Layout layout) : fLayout(layout) {} + +template +static constexpr size_t tight_vec_size(int vecLength) { + return sizeof(BaseType) * vecLength; +} + +/** + * From Section 7.6.2.2 "Standard Uniform Block Layout": + * 1. If the member is a scalar consuming N basic machine units, the base alignment is N. + * 2. If the member is a two- or four-component vector with components consuming N basic machine + * units, the base alignment is 2N or 4N, respectively. + * 3. If the member is a three-component vector with components consuming N + * basic machine units, the base alignment is 4N. + * 4. If the member is an array of scalars or vectors, the base alignment and array + * stride are set to match the base alignment of a single array element, according + * to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The + * array may have padding at the end; the base offset of the member following + * the array is rounded up to the next multiple of the base alignment. + * 5. If the member is a column-major matrix with C columns and R rows, the + * matrix is stored identically to an array of C column vectors with R components each, + * according to rule (4). + * 6. If the member is an array of S column-major matrices with C columns and + * R rows, the matrix is stored identically to a row of S × C column vectors + * with R components each, according to rule (4). + * 7. If the member is a row-major matrix with C columns and R rows, the matrix + * is stored identically to an array of R row vectors with C components each, + * according to rule (4). + * 8. If the member is an array of S row-major matrices with C columns and R + * rows, the matrix is stored identically to a row of S × R row vectors with C + * components each, according to rule (4). + * 9. If the member is a structure, the base alignment of the structure is N, where + * N is the largest base alignment value of any of its members, and rounded + * up to the base alignment of a vec4. The individual members of this substructure are then + * assigned offsets by applying this set of rules recursively, + * where the base offset of the first member of the sub-structure is equal to the + * aligned offset of the structure. The structure may have padding at the end; + * the base offset of the member following the sub-structure is rounded up to + * the next multiple of the base alignment of the structure. + * 10. If the member is an array of S structures, the S elements of the array are laid + * out in order, according to rule (9). + */ +template +struct Rules140 { + /** + * For an array of scalars or vectors this returns the stride between array elements. For + * matrices or arrays of matrices this returns the stride between columns of the matrix. Note + * that for single (non-array) scalars or vectors we don't require a stride. + */ + static constexpr size_t Stride(int count) { + SkASSERT(count >= 1 || count == Uniform::kNonArray); + static_assert(RowsOrVecLength >= 1 && RowsOrVecLength <= 4); + static_assert(Cols >= 1 && Cols <= 4); + if (Cols != 1) { + // This is a matrix or array of matrices. We return the stride between columns. + SkASSERT(RowsOrVecLength > 1); + return Rules140::Stride(1); + } + if (count == 0) { + // Stride doesn't matter for a non-array. + return RowsOrVecLength * sizeof(BaseType); + } + + // Rule 4. + + // Alignment of vec4 by Rule 2. + constexpr size_t kVec4Alignment = tight_vec_size(4); + // Get alignment of a single vector of BaseType by Rule 1, 2, or 3 + int n = RowsOrVecLength == 3 ? 4 : RowsOrVecLength; + size_t kElementAlignment = tight_vec_size(n); + // Round kElementAlignment up to multiple of kVec4Alignment. + size_t m = (kElementAlignment + kVec4Alignment - 1) / kVec4Alignment; + return m * kVec4Alignment; + } +}; + +/** + * When using the std430 storage layout, shader storage blocks will be laid out in buffer storage + * identically to uniform and shader storage blocks using the std140 layout, except that the base + * alignment and stride of arrays of scalars and vectors in rule 4 and of structures in rule 9 are + * not rounded up a multiple of the base alignment of a vec4. + */ +template +struct Rules430 { + static constexpr size_t Stride(int count) { + SkASSERT(count >= 1 || count == Uniform::kNonArray); + static_assert(RowsOrVecLength >= 1 && RowsOrVecLength <= 4); + static_assert(Cols >= 1 && Cols <= 4); + + if (Cols != 1) { + // This is a matrix or array of matrices. We return the stride between columns. + SkASSERT(RowsOrVecLength > 1); + return Rules430::Stride(1); + } + if (count == 0) { + // Stride doesn't matter for a non-array. + return RowsOrVecLength * sizeof(BaseType); + } + // Rule 4 without the round up to a multiple of align-of vec4. + return tight_vec_size(RowsOrVecLength == 3 ? 4 : RowsOrVecLength); + } +}; + +// The strides used here were derived from the rules we've imposed on ourselves in +// GrMtlPipelineStateDataManger. Everything is tight except 3-component which have the stride of +// their 4-component equivalents. +template +struct RulesMetal { + static constexpr size_t Stride(int count) { + SkASSERT(count >= 1 || count == Uniform::kNonArray); + static_assert(RowsOrVecLength >= 1 && RowsOrVecLength <= 4); + static_assert(Cols >= 1 && Cols <= 4); + if (Cols != 1) { + // This is a matrix or array of matrices. We return the stride between columns. + SkASSERT(RowsOrVecLength > 1); + return RulesMetal::Stride(1); + } + if (count == 0) { + // Stride doesn't matter for a non-array. + return RowsOrVecLength * sizeof(BaseType); + } + return tight_vec_size(RowsOrVecLength == 3 ? 4 : RowsOrVecLength); + } +}; + +template class Rules> +class Writer { +private: + template + static uint32_t Write(void *dst, int n, const BaseType src[]) { + size_t stride = Rules::Stride(n); + n = (n == Uniform::kNonArray) ? 1 : n; + n *= Cols; + + if (dst) { + if (stride == RowsOrVecLength * sizeof(BaseType)) { + std::memcpy(dst, src, n * stride); + } else { + for (int i = 0; i < n; ++i) { + std::memcpy(dst, src, RowsOrVecLength * sizeof(BaseType)); + src += RowsOrVecLength; + dst = SkTAddOffset(dst, stride); + } + } + } + + return n * stride; + } + + static uint32_t WriteSkMatrices(void *dst, int n, const SkMatrix m[]) { + // Stride() will give us the stride of each column, so mul by 3 to get matrix stride. + size_t stride = 3 * Rules::Stride(1); + n = std::max(n, 1); + + if (dst) { + size_t offset = 0; + for (int i = 0; i < n; ++i) { + float mt[] = { + m[i].get(SkMatrix::kMScaleX), + m[i].get(SkMatrix::kMSkewY), + m[i].get(SkMatrix::kMPersp0), + m[i].get(SkMatrix::kMSkewX), + m[i].get(SkMatrix::kMScaleY), + m[i].get(SkMatrix::kMPersp1), + m[i].get(SkMatrix::kMTransX), + m[i].get(SkMatrix::kMTransY), + m[i].get(SkMatrix::kMPersp2), + }; + Write(SkTAddOffset(dst, offset), 1, mt); + offset += stride; + } + } + return n * stride; + } + +public: + static uint32_t WriteUniform(SLType type, + CType ctype, + void *dest, + int n, + const void *src) { + SkASSERT(n >= 1 || n == Uniform::kNonArray); + switch (type) { + case SLType::kInt: + return Write(dest, n, static_cast(src)); + + case SLType::kInt2: + return Write(dest, n, static_cast(src)); + + case SLType::kInt3: + return Write(dest, n, static_cast(src)); + + case SLType::kInt4: + return Write(dest, n, static_cast(src)); + + case SLType::kHalf: + case SLType::kFloat: + return Write(dest, n, static_cast(src)); + + case SLType::kHalf2: + case SLType::kFloat2: + return Write(dest, n, static_cast(src)); + + case SLType::kHalf3: + case SLType::kFloat3: + return Write(dest, n, static_cast(src)); + + case SLType::kHalf4: + case SLType::kFloat4: + return Write(dest, n, static_cast(src)); + + case SLType::kHalf2x2: + case SLType::kFloat2x2: + return Write(dest, n, static_cast(src)); + + case SLType::kHalf3x3: + case SLType::kFloat3x3: { + switch (ctype) { + case CType::kDefault: + return Write(dest, n, static_cast(src)); + case CType::kSkMatrix: + return WriteSkMatrices(dest, n, static_cast(src)); + } + SkUNREACHABLE; + } + + case SLType::kHalf4x4: + case SLType::kFloat4x4: + return Write(dest, n, static_cast(src)); + + default: + SK_ABORT("Unexpected uniform type"); + } + } +}; + +#ifdef SK_DEBUG +// To determine whether a current offset is aligned, we can just 'and' the lowest bits with the +// alignment mask. A value of 0 means aligned, any other value is how many bytes past alignment we +// are. This works since all alignments are powers of 2. The mask is always (alignment - 1). +static uint32_t sltype_to_alignment_mask(SLType type) { + switch(type) { + case SLType::kByte: // fall through + case SLType::kUByte: + return 0x0; + case SLType::kByte2: // fall through + case SLType::kUByte2: + return 0x1; + case SLType::kByte3: // fall through + case SLType::kByte4: + case SLType::kUByte3: + case SLType::kUByte4: + return 0x3; + case SLType::kShort: // fall through + case SLType::kUShort: + return 0x1; + case SLType::kShort2: // fall through + case SLType::kUShort2: + return 0x3; + case SLType::kShort3: // fall through + case SLType::kShort4: + case SLType::kUShort3: + case SLType::kUShort4: + return 0x7; + case SLType::kInt: + case SLType::kUint: + return 0x3; + case SLType::kInt2: + case SLType::kUint2: + return 0x7; + case SLType::kInt3: + case SLType::kUint3: + case SLType::kInt4: + case SLType::kUint4: + return 0xF; + case SLType::kHalf: // fall through + case SLType::kFloat: + return 0x3; + case SLType::kHalf2: // fall through + case SLType::kFloat2: + return 0x7; + case SLType::kHalf3: // fall through + case SLType::kFloat3: + return 0xF; + case SLType::kHalf4: // fall through + case SLType::kFloat4: + return 0xF; + case SLType::kHalf2x2: // fall through + case SLType::kFloat2x2: + return 0x7; + case SLType::kHalf3x3: // fall through + case SLType::kFloat3x3: + return 0xF; + case SLType::kHalf4x4: // fall through + case SLType::kFloat4x4: + return 0xF; + + // This query is only valid for certain types. + case SLType::kVoid: + case SLType::kBool: + case SLType::kBool2: + case SLType::kBool3: + case SLType::kBool4: + case SLType::kTexture2DSampler: + case SLType::kTextureExternalSampler: + case SLType::kTexture2DRectSampler: + case SLType::kSampler: + case SLType::kTexture2D: + case SLType::kInput: + break; + } + SK_ABORT("Unexpected type"); +} + +/** Returns the size in bytes taken up in Metal buffers for GrSLTypes. */ +inline uint32_t sltype_to_mtl_size(SLType type) { + switch(type) { + case SLType::kByte: + return sizeof(int8_t); + case SLType::kByte2: + return 2 * sizeof(int8_t); + case SLType::kByte3: + return 4 * sizeof(int8_t); + case SLType::kByte4: + return 4 * sizeof(int8_t); + case SLType::kUByte: + return sizeof(uint8_t); + case SLType::kUByte2: + return 2 * sizeof(uint8_t); + case SLType::kUByte3: + return 4 * sizeof(uint8_t); + case SLType::kUByte4: + return 4 * sizeof(uint8_t); + case SLType::kShort: + return sizeof(int16_t); + case SLType::kShort2: + return 2 * sizeof(int16_t); + case SLType::kShort3: + return 4 * sizeof(int16_t); + case SLType::kShort4: + return 4 * sizeof(int16_t); + case SLType::kUShort: + return sizeof(uint16_t); + case SLType::kUShort2: + return 2 * sizeof(uint16_t); + case SLType::kUShort3: + return 4 * sizeof(uint16_t); + case SLType::kUShort4: + return 4 * sizeof(uint16_t); + case SLType::kHalf: // fall through + case SLType::kFloat: + return sizeof(float); + case SLType::kHalf2: // fall through + case SLType::kFloat2: + return 2 * sizeof(float); + case SLType::kHalf3: // fall through + case SLType::kFloat3: + case SLType::kHalf4: + case SLType::kFloat4: + return 4 * sizeof(float); + case SLType::kInt: // fall through + case SLType::kUint: + return sizeof(int32_t); + case SLType::kInt2: // fall through + case SLType::kUint2: + return 2 * sizeof(int32_t); + case SLType::kInt3: // fall through + case SLType::kUint3: + case SLType::kInt4: + case SLType::kUint4: + return 4 * sizeof(int32_t); + case SLType::kHalf2x2: // fall through + case SLType::kFloat2x2: + return 4 * sizeof(float); + case SLType::kHalf3x3: // fall through + case SLType::kFloat3x3: + return 12 * sizeof(float); + case SLType::kHalf4x4: // fall through + case SLType::kFloat4x4: + return 16 * sizeof(float); + + // This query is only valid for certain types. + case SLType::kVoid: + case SLType::kBool: + case SLType::kBool2: + case SLType::kBool3: + case SLType::kBool4: + case SLType::kTexture2DSampler: + case SLType::kTextureExternalSampler: + case SLType::kTexture2DRectSampler: + case SLType::kSampler: + case SLType::kTexture2D: + case SLType::kInput: + break; + } + SK_ABORT("Unexpected type"); +} + +// Given the current offset into the ubo, calculate the offset for the uniform we're trying to add +// taking into consideration all alignment requirements. The uniformOffset is set to the offset for +// the new uniform, and currentOffset is updated to be the offset to the end of the new uniform. +static uint32_t get_ubo_aligned_offset(uint32_t* currentOffset, + uint32_t* maxAlignment, + SLType type, + int arrayCount) { + uint32_t alignmentMask = sltype_to_alignment_mask(type); + if (alignmentMask > *maxAlignment) { + *maxAlignment = alignmentMask; + } + uint32_t offsetDiff = *currentOffset & alignmentMask; + if (offsetDiff != 0) { + offsetDiff = alignmentMask - offsetDiff + 1; + } + uint32_t uniformOffset = *currentOffset + offsetDiff; + SkASSERT(sizeof(float) == 4); + if (arrayCount) { + *currentOffset = uniformOffset + sltype_to_mtl_size(type) * arrayCount; + } else { + *currentOffset = uniformOffset + sltype_to_mtl_size(type); + } + return uniformOffset; +} +#endif // SK_DEBUG + +uint32_t UniformManager::writeUniforms(SkSpan uniforms, + void** srcs, + uint32_t* offsets, + void *dst) { + decltype(&Writer::WriteUniform) write; + switch (fLayout) { + case Layout::kStd140: + write = Writer::WriteUniform; + break; + case Layout::kStd430: + write = Writer::WriteUniform; + break; + case Layout::kMetal: + write = Writer::WriteUniform; + break; + } + +#ifdef SK_DEBUG + uint32_t curUBOOffset = 0; + uint32_t curUBOMaxAlignment = 0; +#endif // SK_DEBUG + + uint32_t offset = 0; + + for (int i = 0; i < (int) uniforms.size(); ++i) { + const Uniform& u = uniforms[i]; + +#ifdef SK_DEBUG + uint32_t debugOffset = get_ubo_aligned_offset(&curUBOOffset, + &curUBOMaxAlignment, + u.type(), + u.count()); +#endif // SK_DEBUG + + uint32_t bytesWritten = write(u.type(), + CType::kDefault, + dst, + u.count(), + srcs ? srcs[i] : nullptr); + SkASSERT(debugOffset == offset); + + if (offsets) { + offsets[i] = offset; + } + offset += bytesWritten; + } + + return offset; +} + +} // namespace skgpu diff --git a/experimental/graphite/src/UniformManager.h b/experimental/graphite/src/UniformManager.h new file mode 100644 index 0000000000..770079924d --- /dev/null +++ b/experimental/graphite/src/UniformManager.h @@ -0,0 +1,47 @@ +/* + * Copyright 2021 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef skgpu_UniformManager_DEFINED +#define skgpu_UniformManager_DEFINED + +#include "experimental/graphite/include/private/GraphiteTypesPriv.h" +#include "experimental/graphite/src/Uniform.h" +#include "include/core/SkSpan.h" + +namespace skgpu { + +enum class Layout { + kStd140, + kStd430, + kMetal, /** This is our own self-imposed layout we use for Metal. */ +}; + +class UniformManager { +public: + UniformManager(Layout layout); + + /* + * Use the uniform 'definitions' to write the data in 'srcs' into 'dst' (if it is non-null). + * If non-null, 'offsets' is filled in with the offset of each uniform w/in 'dst'. The + * number of bytes that was written (or would've been written) to 'dst' is returned. + * In practice one should call: + * auto bytes = writeUniforms(definitions, nullptr, nullptr, nullptr); + * // allocate dst and offsets memory + * writeUniforms(definitions, src, offsets, dst); + */ + uint32_t writeUniforms(SkSpan definitions, + void** srcs, + uint32_t* offsets, + void *dst); + +private: + Layout fLayout; +}; + +} // namespace skgpu + +#endif // skgpu_UniformManager_DEFINED diff --git a/gn/graphite.gni b/gn/graphite.gni index ff0855a239..ba268d02c2 100644 --- a/gn/graphite.gni +++ b/gn/graphite.gni @@ -66,8 +66,9 @@ skia_graphite_sources = [ "$_src/TextureInfo.cpp", "$_src/TextureProxy.cpp", "$_src/TextureProxy.h", - "$_src/UniformDataManager.cpp", - "$_src/UniformDataManager.h", + "$_src/Uniform.h", + "$_src/UniformManager.cpp", + "$_src/UniformManager.h", "$_src/geom/BoundsManager.h", "$_src/geom/IntersectionTree.cpp", "$_src/geom/IntersectionTree.h",