02-方便的特性

已道心破碎,以后就挑自己觉得重要的抄吧。感觉还不一定能用到我的OpenGL项目上,唉。

命名空间

Slang支持命名空间,且支持三种嵌套方式:

namespace ns1.ns2
{
    int f();
}
// equivalent to:
namespace ns1::ns2
{
    int f();
}
// equivalent to:
namespace ns1
{
    namespace ns2
    {
        int f();
    }
}

和C++一样,能用using来使用命名空间:

using ns1.ns2;
// or:
using namespace ns1.ns2; // alternative syntax.

结构体

成员函数

Slang支持在结构体中定义(静态)成员函数:

struct Foo
{
    int compute(int a, int b)
    {
        return a + b;
    }
}

// ...
Foo foo;
int rs = foo.compute(1,2);

考虑到gpu性能优先,成员函数中的this是不可修改的,如果执意要修改,需要用[mutating]属性修饰该成员函数,否则会报错。

getter&setter

可以用property关键字定义成员变量的getter和setter函数:

struct MyType
{
    uint flag;

    property highBits : uint
    {
        get() { return flag >> 16; }
        set(uint x) { flag = (flag & 0xFF) + (x << 16); }
    }
};

[mutating]

为了GPU性能考虑,成员函数是无法改变结构体内变量的。如果真要这么做,需要[mutating]注解:

struct Foo
{
    int count;
    
    [mutating]
    void setCount(int x) { count = x; }

    // This would fail to compile.
    // void setCount2(int x) { count = x; }
}

运算符重载

Slang也支持运算符重载:

struct MyType
{
    int val;
    __init(int x) { val = x; }
}

MyType operator+(MyType a, MyType b)
{
    return MyType(a.val + b.val);
}

特殊类型

Tuple类型

Tuple类型可以收集不同种类的类型:

Tuple<int, float, bool> t0 = Tuple<int, float, bool>(5, 2.0f, false);
Tuple<int, float, bool> t1 = makeTuple(3, 1.0f, true);

要想访问对应位置的成员变量,需要使用_X

int i = t0._0; 	// 5
bool b = t1._2; // true
t0._0_0_1 		// evaluates to (5, 5, 2.0f)

可以用concat()拼接两个Tuple:

concat(t0, t1) // evaluates to (5, 2.0f, false, 3, 1.0f, true)

可以用countof()获取Tuple内元素个数:

int n = countof(Tuple<int, float>); // 2
int n1 = countof(makeTuple(1,2,3)); // 3

Optional<T>类型

Slang支持Optional<T>类型,代表可能不存在的值:

struct MyType
{
    int val;
}

int useVal(Optional<MyType> p)
{
    // Equivalent to `!p.hasValue`
    if (p == none)        
        return 0;
    return p.value.val;
}

int caller()
{
    MyType v;
    v.val = 0;
    // OK to pass `MyType` to `Optional<MyType>`.
    useVal(v);  
    // OK to pass `none` to `Optional<MyType>`.
    useVal(none);  
    return 0;
}

可以用if let样式获取该类型的值:

Optional<int> getOptInt() { ... }

void test()
{
    if (let x = getOptInt())
    {
        // if we are here, `getOptInt` returns a value `int`.
        // and `x` represents the `int` value.
    }
}

reinterpret<T>类型

在Shader中很难进行类型包装,但Slang提供了相关功能:

float4 myPackedVector = reinterpret<float4>(myVal);

只要目标类型不比原类型的小,就能通过reinterpret<T>()进行类型包装的转换。

DescriptorHandle<T>类型

Slang提供DescriptorHandle<T>类型, 代表一个无绑定的资源句柄. 对于像HLSL, GLSL 和 SPRIV, 资源类型(如texture, samplersbuffers)的句柄是不透明的,DescriptorHandle<T>会将其翻译为一个uint2类型,以便能被定义在任何内存中。该uint2值被视为访问全局描述符堆/数组的索引。

DescriptorHandle<T>的定义如下:

struct DescriptorHandle<T> where T:IOpaqueDescriptor {}

其中IOpaqueDescriptor是大部分资源类型,例如:texturesConstancBufferRaytracingAccelerationStructureSamplerStateSamplerComparisonState和所有类型的StructuredBuffer

使用例如下:

uniform StructuredBuffer<DescriptorHandle<Texture2D>> textures;
uniform int textureIndex;

// define a descriptor handle using builtin convenience typealias:
uniform StructuredBuffer<float4>.Handle output;

[numthreads(1,1,1)]
void main()
{
    output[0] = textures[textureIndex].Load(int3(0));

    // Alternatively, this syntax is also valid:
    (*output)[0] = textures[textureIndex]->Load(int3(0));
}

特殊语法

多层break

Slang支持多层break

outer:
for (int i = 0; i < 5; i++)
{
    inner:
    for (int j = 0; j < 10; j++)
    {
        if (someCondition)
            break outer;
    }
}

参考资料

  • shader-slang/slang: Making it easier to work with shaders (github.com)
  • Basic Convenience Features | slang