繁体   English   中英

您如何将规范化的设备与Apple的Metal内核功能进行协调?

[英]How do you get normalized devices coordinates into Apple's Metal kernel functions?

我在Metal中有一个内核函数,我将纹理传递给该内核函数,以便可以对图像执行一些操作。 我传入了uint2 gid [[thread_position_in_grid]] ,该像素为我提供了整数的像素坐标。

为了获得标准化的设备坐标,我可以对gid.xgid.y以及我的纹理宽度和gid.y进行一些简单的数学运算。 这是最好的方法吗? 更好的方法?

您的方法是一种很好的方法。 如果您不想在内核函数中查询纹理尺寸或创建仅用于传递它们的缓冲区,则可以使用-[MTLComputeCommandEncoder setBytes:length:atIndex:]方法将纹理尺寸绑定为“临时” Metal处理的各种缓冲区:

[computeEncoder setBytes:&dimensions length:sizeof(dimensions) atIndex:0]

我认为您是对的,这是在GLSL中通常使用相同方法的好方法:

  1. 计算纹理像素大小

float2 texSize = float2(1/outTexture.get_with(),1/outTexture.get_height());

  1. 然后用它来获得标准化的像素位置

constexpr sampler s(address::clamp_to_edge, filter::linear, coord::normalized);

//
//  something to do...
//
float4 color = inTexture.sample(s,float2(gid)*texSize);

//
// something todo with pixel
//

outTexture.write(color,gid);

问题中指定的方法效果很好。 但是为了完成,使用非规范化(和/或规范化的设备坐标)读取纹理的另一种方法是使用采样器。

创建一个采样器:

id<MTLSamplerState> GetSamplerState()
{
    MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] autorelease];
    desc.minFilter = MTLSamplerMinMagFilterNearest;
    desc.magFilter = MTLSamplerMinMagFilterNearest;
    desc.mipFilter = MTLSamplerMipFilterNotMipmapped;
    desc.maxAnisotropy = 1;
    desc.sAddressMode = MTLSamplerAddressModeClampToEdge;
    desc.tAddressMode = MTLSamplerAddressModeClampToEdge;
    desc.rAddressMode = MTLSamplerAddressModeClampToEdge;

    // The key point: specifies that the sampler reads non-normalized coordinates
    desc.normalizedCoordinates = NO;

    desc.lodMinClamp = 0.0f;
    desc.lodMaxClamp = FLT_MAX;

    id <MTLSamplerState> sampler_state = nil;
    sampler_state = [[device_ newSamplerStateWithDescriptor:desc] autorelease];

    // Release the descriptor
    desc = nil;

    return sampler_state;
}

然后将其附加到您的计算命令编码器:

id <MTLComputeCommandEncoder> compute_encoder = [comand_buffer computeCommandEncoder];
id<MTLSamplerState> ss = GetSamplerState();

// Attach the sampler state to the encoder, say at sampler bind point 0
[compute_encoder setSamplerState:ss atIndex:0];

// And set your texture, say at texture bind point 0
[compute_encoder setTexture:my_texture atIndex:0];

最后在内核中使用它:

// An example kernel that samples from a texture,
// writes one component of the sample into an output buffer
kernel void compute_main( 
                texture2d<uint, access::sample> tex_to_sample [[ texture(0) ]],
                sampler smp [[ sampler(0) ]],
                device uint *out [[buffer(0)]],
                uint2 tid [[thread_position_in_grid]])
{
    out[tid] = tex_to_sample.sample(smp, tid).x;
}

使用采样器可以指定采样参数(如过滤)。 您还可以通过使用附加到同一内核的不同采样器以不同方式访问纹理。 采样器还避免了必须通过并检查纹理尺寸的边界。

请注意,也可以从计算内核中设置采样器。 请参阅金属底纹语言规范中的2.6节采样器

最后,读取函数(使用问题中指定的gid)与使用采样器采样之间的一个主要区别是read()采用整数坐标,而sample()采用浮点坐标。 因此,传递给样本的整数坐标将转换为等效的浮点数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM