我正在使用 Unity 进行 C# 工作。 我创建了一个计算着色器,想要将 C# 数据结构数组传递给它。 就在那时,我意识到我可能已经过度使用了奇特的数据结构,而不是坚持使用类似 C 的着色器结构。
更具体地说,我遇到了两个障碍:
1。我想要传递到着色器缓冲区的每个对象都包含一个链接列表,这意味着对象之间的大小不同。着色器缓冲区是否可以定义为除数组之外的任何其他值,其大小显式传递?
2。我想要传递给着色器的每个对象都是多态的。即它们都有一个字段“type”,但是如果 type==0 它们有一个字段 value0,而那些 type==1 的对象有一个字段 value1 。尝试用着色器语言实现这一点是否合理(可能使用类似 C 的联合)?
interface IObject {
public int type { get; }
}
class Type0 : IObject {
public int type => 0;
public int field0;
}
class Type1 : IObject {
public int type => 1;
public float field1;
}
class ItemForShader {
public List<IObject> objects { get; set; }
}
...
// Can I pass this to the shader?
var shaderParameters = new List<ItemForShader>() {
new ItemForShader() {
objects = new() { new Type0(), new Type1() }
},
new ItemForShader() {
objects = new() { new Type1(), new Type0(), new Type0() }
}
}
请注意:我不需要您详细说明任何解决方案(我已经可以看到您描述了如何传递链表节点的共享池,然后使用精美的索引系统在所有对象之间共享这些节点)。
我只是想知道什么是合理的以及通常如何完成(或不完成)。也许本质上描述了在该场景中什么C结构与什么C#结构相匹配?
编辑:正如我担心的那样,这不是好兆头: 具有动态长度的GLSL数组统一
制作多个着色器,一个用于每个数量的潜在项目 您想要支持的链接列表。 GLSL 不支持可变大小 数组。唯一的其他解决方案是创建具有最大的数组 您将使用的尺寸。
您问题的字面答案是:您不能。
GPU代码无法分配内存。 所有内存分配都是在着色器编译时计算出来的。 你不能有一个包含 N 个元素的数组。 您不能拥有多态数据类型。
但是您可以拥有预定大小的数据块,并从中构建这些概念。 在这种情况下,由于 int 和 float 的大小相同,因此您可以拥有紧密包装的联合结构。 一般来说,您只需计算出最大尺寸并让所有对象都使用该尺寸。 这是如何构建联合结构的示例:
C#:
[StructLayout(LayoutKind.Explicit)]
public struct IObject {
[FieldOffset(0)]
public uint type;
[FieldOffset(1)]
public int field0;
[FieldOffset(1)]
public float field1;
public static IObject Type0(int field0) {
return new IObject() {
type = 0,
field0 = field0,
};
}
public static IObject Type1(float field1) {
return new IObject() {
type = 1,
field1 = field1,
};
}
}
HLSL:
struct IObject {
uint type;
int unionMember;
};
int GetField0(IObject o) {
return o.unionMember;
}
float GetField1(IObject o) {
return asfloat(o.unionMember);
}
请注意,“asfloat”是 C 风格重新解释转换的 HLSL 等效项。 它不返回与 int 相同数字的浮点数,而是返回那些被解释为浮点数的字节。
我还将指出 GPU 着色器核心在遍历复杂数据结构时效率不高。 看一下这个问题,大致了解原因。