[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 <egdaniel@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
f6fb3db1dc
commit
ef8907c69f
@ -118,6 +118,7 @@ enum class SLType {
|
||||
|
||||
kLast = kInput
|
||||
};
|
||||
static const int kSLTypeCount = static_cast<int>(SLType::kLast) + 1;
|
||||
|
||||
enum class CType : unsigned {
|
||||
// Any float/half, vector of floats/half, or matrices of floats/halfs are a tightly
|
||||
|
58
experimental/graphite/src/Uniform.h
Normal file
58
experimental/graphite/src/Uniform.h
Normal file
@ -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<unsigned>(type))
|
||||
, fCount (static_cast<unsigned>(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<SLType>(fType); }
|
||||
constexpr uint32_t count() const { return static_cast<uint32_t>(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
|
@ -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<typename BaseType>
|
||||
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<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
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<BaseType, RowsOrVecLength>::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<float>(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<BaseType>(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<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
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<BaseType, RowsOrVecLength>::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<BaseType>(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<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
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<BaseType, RowsOrVecLength>::Stride(1);
|
||||
}
|
||||
if (count == 0) {
|
||||
// Stride doesn't matter for a non-array.
|
||||
return 0;
|
||||
}
|
||||
return tight_vec_size<BaseType>(RowsOrVecLength == 3 ? 4 : RowsOrVecLength);
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename BaseType, int RowsOrVecLength, int Cols> class Rules>
|
||||
class Writer {
|
||||
private:
|
||||
template<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
static void Write(void *dst, int n, const BaseType v[]) {
|
||||
if (dst) {
|
||||
size_t stride = Rules<BaseType, RowsOrVecLength, Cols>::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<void>(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<float, 3, 3>(SkTAddOffset<void>(d, offset), 1, mt);
|
||||
// Stride() will give us the stride of each column, so mul by 3 to get matrix stride.
|
||||
offset += 3 * Rules<float, 3, 3>::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<int32_t>(d, n, static_cast<const int32_t *>(v));
|
||||
|
||||
case SLType::kInt2:
|
||||
return Write<int32_t, 2>(d, n, static_cast<const int32_t *>(v));
|
||||
|
||||
case SLType::kInt3:
|
||||
return Write<int32_t, 3>(d, n, static_cast<const int32_t *>(v));
|
||||
|
||||
case SLType::kInt4:
|
||||
return Write<int32_t, 4>(d, n, static_cast<const int32_t *>(v));
|
||||
|
||||
case SLType::kHalf:
|
||||
case SLType::kFloat:
|
||||
return Write<float>(d, n, static_cast<const float *>(v));
|
||||
|
||||
case SLType::kHalf2:
|
||||
case SLType::kFloat2:
|
||||
return Write<float, 2>(d, n, static_cast<const float *>(v));
|
||||
|
||||
case SLType::kHalf3:
|
||||
case SLType::kFloat3:
|
||||
return Write<float, 3>(d, n, static_cast<const float *>(v));
|
||||
|
||||
case SLType::kHalf4:
|
||||
case SLType::kFloat4:
|
||||
return Write<float, 4>(d, n, static_cast<const float *>(v));
|
||||
|
||||
case SLType::kHalf2x2:
|
||||
case SLType::kFloat2x2:
|
||||
return Write<float, 2, 2>(d, n, static_cast<const float *>(v));
|
||||
|
||||
case SLType::kHalf3x3:
|
||||
case SLType::kFloat3x3: {
|
||||
switch (ctype) {
|
||||
case CType::kDefault:
|
||||
return Write<float, 3, 3>(d, n, static_cast<const float *>(v));
|
||||
case CType::kSkMatrix:
|
||||
return WriteSkMatrices(d, n, static_cast<const SkMatrix *>(v));
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
case SLType::kHalf4x4:
|
||||
case SLType::kFloat4x4:
|
||||
return Write<float, 4, 4>(d, n, static_cast<const float *>(v));
|
||||
|
||||
default:
|
||||
SK_ABORT("Unexpected uniform type");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool UniformDataManager::UniformManager::writeUniforms(/*const GrProgramInfo &info,*/
|
||||
void *buffer) {
|
||||
decltype(&Writer<Rules140>::WriteUniform) write;
|
||||
switch (fLayout) {
|
||||
case Layout::kStd140:
|
||||
write = Writer<Rules140>::WriteUniform;
|
||||
break;
|
||||
case Layout::kStd430:
|
||||
write = Writer<Rules430>::WriteUniform;
|
||||
break;
|
||||
case Layout::kMetal:
|
||||
write = Writer<RulesMetal>::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
|
@ -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 <vector>
|
||||
#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<Uniform> 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<std::vector<NewUniform>>;
|
||||
|
||||
ProgramUniforms fUniforms;
|
||||
Layout fLayout;
|
||||
};
|
||||
|
||||
UniformManager fUniformManager;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
||||
#endif // skgpu_UniformDataManager_DEFINED
|
494
experimental/graphite/src/UniformManager.cpp
Normal file
494
experimental/graphite/src/UniformManager.cpp
Normal file
@ -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<typename BaseType>
|
||||
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<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
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<BaseType, RowsOrVecLength>::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<float>(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<BaseType>(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<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
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<BaseType, RowsOrVecLength>::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<BaseType>(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<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
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<BaseType, RowsOrVecLength>::Stride(1);
|
||||
}
|
||||
if (count == 0) {
|
||||
// Stride doesn't matter for a non-array.
|
||||
return RowsOrVecLength * sizeof(BaseType);
|
||||
}
|
||||
return tight_vec_size<BaseType>(RowsOrVecLength == 3 ? 4 : RowsOrVecLength);
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename BaseType, int RowsOrVecLength, int Cols> class Rules>
|
||||
class Writer {
|
||||
private:
|
||||
template<typename BaseType, int RowsOrVecLength = 1, int Cols = 1>
|
||||
static uint32_t Write(void *dst, int n, const BaseType src[]) {
|
||||
size_t stride = Rules<BaseType, RowsOrVecLength, Cols>::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<void>(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<float, 3, 3>::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<float, 3, 3>(SkTAddOffset<void>(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<int32_t>(dest, n, static_cast<const int32_t *>(src));
|
||||
|
||||
case SLType::kInt2:
|
||||
return Write<int32_t, 2>(dest, n, static_cast<const int32_t *>(src));
|
||||
|
||||
case SLType::kInt3:
|
||||
return Write<int32_t, 3>(dest, n, static_cast<const int32_t *>(src));
|
||||
|
||||
case SLType::kInt4:
|
||||
return Write<int32_t, 4>(dest, n, static_cast<const int32_t *>(src));
|
||||
|
||||
case SLType::kHalf:
|
||||
case SLType::kFloat:
|
||||
return Write<float>(dest, n, static_cast<const float *>(src));
|
||||
|
||||
case SLType::kHalf2:
|
||||
case SLType::kFloat2:
|
||||
return Write<float, 2>(dest, n, static_cast<const float *>(src));
|
||||
|
||||
case SLType::kHalf3:
|
||||
case SLType::kFloat3:
|
||||
return Write<float, 3>(dest, n, static_cast<const float *>(src));
|
||||
|
||||
case SLType::kHalf4:
|
||||
case SLType::kFloat4:
|
||||
return Write<float, 4>(dest, n, static_cast<const float *>(src));
|
||||
|
||||
case SLType::kHalf2x2:
|
||||
case SLType::kFloat2x2:
|
||||
return Write<float, 2, 2>(dest, n, static_cast<const float *>(src));
|
||||
|
||||
case SLType::kHalf3x3:
|
||||
case SLType::kFloat3x3: {
|
||||
switch (ctype) {
|
||||
case CType::kDefault:
|
||||
return Write<float, 3, 3>(dest, n, static_cast<const float *>(src));
|
||||
case CType::kSkMatrix:
|
||||
return WriteSkMatrices(dest, n, static_cast<const SkMatrix *>(src));
|
||||
}
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
case SLType::kHalf4x4:
|
||||
case SLType::kFloat4x4:
|
||||
return Write<float, 4, 4>(dest, n, static_cast<const float *>(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<const Uniform> uniforms,
|
||||
void** srcs,
|
||||
uint32_t* offsets,
|
||||
void *dst) {
|
||||
decltype(&Writer<Rules140>::WriteUniform) write;
|
||||
switch (fLayout) {
|
||||
case Layout::kStd140:
|
||||
write = Writer<Rules140>::WriteUniform;
|
||||
break;
|
||||
case Layout::kStd430:
|
||||
write = Writer<Rules430>::WriteUniform;
|
||||
break;
|
||||
case Layout::kMetal:
|
||||
write = Writer<RulesMetal>::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
|
47
experimental/graphite/src/UniformManager.h
Normal file
47
experimental/graphite/src/UniformManager.h
Normal file
@ -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<const Uniform> definitions,
|
||||
void** srcs,
|
||||
uint32_t* offsets,
|
||||
void *dst);
|
||||
|
||||
private:
|
||||
Layout fLayout;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
||||
#endif // skgpu_UniformManager_DEFINED
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user