Glsl “有意义吗?”;预载;将数据存储到计算着色器的共享存储中以实现更快的读取访问?

Glsl “有意义吗?”;预载;将数据存储到计算着色器的共享存储中以实现更快的读取访问?,glsl,vulkan,compute-shader,Glsl,Vulkan,Compute Shader,我有以下计算着色器: #version 450 layout (local_size_x = 128, local_size_y = 1, local_size_z = 1) in; layout(push_constant) uniform PushConstant { vec2 topLeft; vec2 bottomRight; }; struct Position { float x, y, z; }; layout (set=0, binding=0) bu

我有以下计算着色器:

#version 450

layout (local_size_x = 128, local_size_y = 1, local_size_z = 1) in;

layout(push_constant) uniform PushConstant
{
    vec2 topLeft;
    vec2 bottomRight;
};

struct Position {
  float x, y, z;
};

layout (set=0, binding=0) buffer PositionBuffer
{
    Position positions[];
};

layout (set=0, binding=1) buffer SelectionBuffer
{
    uint selected[];
};

void main()
{
    uint ind = gl_GlobalInvocationID.z * (gl_WorkGroupSize.x * gl_NumWorkGroups.x) * (gl_WorkGroupSize.y * gl_NumWorkGroups.y)
               + gl_GlobalInvocationID.y * (gl_WorkGroupSize.x * gl_NumWorkGroups.x)
               + gl_GlobalInvocationID.x;

    Position pos = positions[ind];

    selected[ind] = 0;

    if(pos.x > topLeft.x && pos.x < bottomRight.x && pos.y > topLeft.y && pos.y < bottomRight.y)
    {
        selected[ind] = 1;
    }
}
#版本450
布局(本地大小x=128,本地大小y=1,本地大小z=1)在中;
布局(推力常数)均匀推力常数
{
vec2左上角;
vec2右下角;
};
结构位置{
浮动x,y,z;
};
布局(设置=0,绑定=0)缓冲区位置缓冲区
{
职位【】;
};
布局(集合=0,绑定=1)缓冲区选择缓冲区
{
uint选定[];
};
void main()
{
uint ind=gl_globalinjournalid.z*(gl_WorkGroupSize.x*gl_NumWorkGroups.x)*(gl_WorkGroupSize.y*gl_NumWorkGroups.y)
+gl_globalinJournalid.y*(gl_WorkGroupSize.x*gl_NumWorkGroups.x)
+gl_globalinovationid.x;
位置pos=位置[ind];
所选[ind]=0;
如果(位置x>topLeft.x和位置xtopLeft.y和位置y
它所做的是检查一个点(来自
位置
缓冲区)是否位于用户提供的矩形内(来自
PushConstant
)。如果是-着色器通过将
1
写入选定的
缓冲区来标记该点

这段代码运行良好。但是,由于我对计算机没有经验,我正在寻找使它更好的方法。我知道整个团队都可以访问共享变量。其思想是创建一个共享位置数组,并将其填充到一个线程中,比如线程编号0。然后,理论上,其他线程不需要读取缓冲内存,而是需要更快的共享内存

值得吗?
如何正确同步?

我可以做一些类似的事情将数据写入所选的
数组吗?

从整个操作的角度来看。为此,您:

  • 读取单个连续内存块
  • 对该内存的每个值执行单个操作
  • 将该操作的结果写入另一个内存块
  • 您的代码在任何时候都不需要多次读取该值。虽然编写的代码可能会写入一个值两次,但没有必要这样做。您可以根据条件轻松地计算一个值,然后将该值写入内存。我假设一个好的编译器会把你的代码准确地翻译成这样

    由于没有线程一次从多个位置读取或写入多个位置,因此对内存的缓存访问只在允许将“读取X字节”转换为更高效的“读取缓存线字节”读取时有所帮助。两个试图读取恰好位于同一缓存线中的地址的调用应该只执行一次内存提取。写作也是如此;写入同一缓存线的多个调用应聚合为一次写入

    当然,这需要合理的硬件

    假设这样一个系统调用同一内存的多个读/写仍然是可能的。这与扭曲/波前中的调用次数有关(即:在锁定步骤中执行的着色器的调用次数)。如果每个扭曲读取的数据大小未与缓存对齐,则两个扭曲可能对同一缓存线发出读取,因为不同的扭曲可能同时执行。写作也是如此。但即使这样,也假定缓存和执行内存提取的决定是基于每个扭曲进行的

    无论如何,如果应该确定是这种情况,正确的解决方案是尽可能地调整您的读取,而不是尝试为其执行缓存的工作


    有时,预缓存数据会很有用,但这主要是在调用经常从相同的地址读取数据的情况下,通常是在它们从彼此的内存读取数据的情况下。即便如此,您也应该对这一点进行分析,而不是先验地试图以这种方式进行编码。

    您可以在这里大大简化地址计算。您希望列表中有一个1D地址,那么为什么不使用1D工作空间并避免计算
    ind
    所需的所有数学运算呢?在某些GPU架构上,使用
    any()
    all()
    内置函数来实现向量比较,而不是使用长标量
    (如果
    的话),您可能会从中获得一些好处。您的位置也是
    vec3
    类型,但您仅使用
    x
    y
    。考虑到这可能是内存带宽有限的情况,从缓冲区中删除
    z
    会有所帮助。谢谢。我简化了条件检查。但是,你能给我一个提示,我如何简化索引计算?事实上,我需要一个1D地址,但由于工作组的数量是一个3D值,我不知道如何缩短它。应用程序控制工作组调度的大小。只需设置分派,使全局y和z维度为1。谢谢。我的担心与您描述的完全一样-同一组中的不同调用可能会从内存中读取,从而覆盖缓存。我是否正确地理解了缓存是按组的,而不是按扭曲的?@nikitablack:缓存几乎肯定不是按组的。多个扭曲可能来自同一缓存,但缓存不会基于本地组大小。