繁体   English   中英

iOS 如何正确使用 simd_float3?

[英]iOS how to work with simd_float3 correctly?

我遇到了一个奇怪的问题,使用金属着色器代码创建的数据。 我想在 Swift 中做额外的处理,然后将数据注入回由 Metal 渲染。

  • 金属着色器被赋予一个 MetalBuffer
  • 金属着色器一个一个地处理顶点
  • 金属着色器将结果写入提供的缓冲区
  • 金属着色器执行缓冲区的完成块
  • Swift 代码接收缓冲区并对其进行迭代,将一个结构重写为另一个
  • Swift 代码将结果写入用于渲染的共享缓冲区
  • 金属着色器在稍微错误的位置渲染像素,导致闪烁和图形故障

所有结构和数学都使用 simd_float3

如何正确传递来自金属代码的结构数据以在 Swift 中可用并将其传回? 我所看到的是我的值因处理而损坏(某种浮点错误 - 使用新结构绘制时像素未对齐)。

如果我将值直接从一个缓冲区分配给共享缓冲区,则这些点将呈现在正确的位置,并且不会发生闪烁。

//ShaderTypes.h
#include <simd/simd.h>
struct ParticleUniforms {
    simd_float3 position;
    simd_float3 color;
//    float confidence; // I want to get rid of this value after processing in swift
};

struct DisplayPoint {
    simd_float3 position;
    simd_float3 color;
};


/// Vertex shader that takes in a 2D grid-point and infers its 3D position in world-space, along with RGB and confidence.
/// Updates the passed in particleUniforms buffer, one point at a time
vertex void screenSample(uint vertexID [[vertex_id]],
                         constant PointCloudUniforms &uniforms [[buffer(kPointCloudUniforms)]],
                         device ParticleUniforms *particleUniforms [[buffer(kParticleUniforms)]],
                         constant float2 *cameraSamplePatternBuffer [[buffer(kGridPoints)]],
                         texture2d<float, access::sample> capturedImageTextureY [[texture(kTextureY)]],
                         texture2d<float, access::sample> capturedImageTextureCbCr [[texture(kTextureCbCr)]],
                         texture2d<float, access::sample> depthMap [[texture(kTextureDepth)]],
                         texture2d<unsigned int, access::sample> confidenceMap [[texture(kTextureConfidence)]]) {
    
    //this is what combines and parses the data to create each point on the cloud
    const auto sampledCameraPoint = cameraSamplePatternBuffer[vertexID];
    
    const auto texCoord = sampledCameraPoint / uniforms.cameraResolution;

    // Sample the color depth map to get the depth value
    const auto depth = depthMap.sample(colorSampler, texCoord).r;
    // With a 2D point plus depth, we can now get its 3D position
    const auto position = worldPoint(sampledCameraPoint, depth, uniforms.cameraIntrinsicsInversed, uniforms.localToWorld);
    
    // Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate
    const auto ycbcr = float4(capturedImageTextureY.sample(colorSampler, texCoord).r, capturedImageTextureCbCr.sample(colorSampler, texCoord.xy).rg, 1);
    const auto sampledColor = (yCbCrToRGB * ycbcr).rgb;
    // Sample the confidence map to get the confidence value
    const auto confidence = confidenceMap.sample(colorSampler, texCoord).r;
    
    if (confidence > 1.0) {
        particleUniforms[vertexID].position = position.xyz;
        particleUniforms[vertexID].color = sampledColor;
    }
}

这是实际上在屏幕上绘制点的着色器:

vertex ParticleVertexOut particleVertex(uint vertexID [[vertex_id]],
                                        constant PointCloudUniforms &uniforms [[buffer(kPointCloudUniforms)]],
                                        constant ParticleUniforms *particleUniforms [[buffer(kParticleUniforms)]]) {
    // get point data
    const auto particleData = particleUniforms[vertexID];
    const auto position = particleData.position;
    
    // animate and project the point
    float4 projectedPosition = uniforms.viewProjectionMatrix * float4(position, 1.0);
    
    projectedPosition /= projectedPosition.w;
    
    ParticleVertexOut out;
    out.position = projectedPosition;
    
    const auto sampledColor = particleData.color;
    
    out.color = float4(sampledColor, 1);
    out.pointSize = 1.0 ;
    
    return out;
}

在 swift 中(两种转换都会损坏 position 和颜色)。

let particle: ParticleUniforms = particlesBuffer[pointIndex]
let point = DisplayPoint(position: simd_float3(particle.position.x,
                                               particle.position.y,
                                               particle.position.z),
                         color: simd_float3(particle.color.x,
                                            particle.color.y,
                                            particle.color.z))
// -OR -
let point = DisplayPoint(position: particle.position,
                         color: particle.color))

下面的图像损坏 - 当将原始结构从一个着色器传递到另一个着色器时,所有像素都正确对齐并且没有“洞”: 在此处输入图像描述

如果我正确理解了您要做什么,那么您基本上是想暂停 GPU 时间线,直到您修改了 CPU 上 GPU 的中间结果。

基本上,除非有明确的同步,否则在调度命令缓冲区之后,CPU 和 GPU 时间线完全独立于您的程序的观点,这意味着您不能对 CPU 和 GPU 之间首先执行的内容做出任何假设。

如果您尝试仅在命令缓冲区的完成处理程序中执行此操作,那么这将不起作用。 GPU 将继续执行队列中的下一个命令缓冲区,您基本上会在 GPU 到达之前竞相更改 CPU 上的数据。

要正确执行您尝试执行的操作,您需要使用MTLSharedEvent而不是使用完成块,而是使用事件的notifyListener

您的新时间线将如下所示,我将在此处使用 Objective-C:

GPU 时间线:

  • 金属着色器被赋予一个 MetalBuffer
  • 金属着色器一个一个地处理顶点
  • 金属着色器将结果写入提供的缓冲区
  • [commandEncoder encodeSignalEvent:event value:n]
  • [commandEncoder encodeWaitForEvent:event value:n+1]

在 CPU 上:

订阅事件: [event notifyListener:_sharedEventListener atValue:2 block:{your block}

在您的区块中,您将执行以下操作:

  • Block 接收缓冲区并对其进行迭代,将一个结构重写为另一个
  • 块代码将结果写入用于渲染的共享缓冲区
  • event.signaledValue = n + 1

在这种情况下, n应该是一个单调上升的值。 因此,每次您想前进时,您都会将其提高一个。 在这种情况下,由于我们同时发出nn + 1信号,因此您需要将其提高两个。 这完全取决于您的特定用例,只需确保您没有等待低于事件当前signaledValue值的值

这应该有效。 有关详细信息,请参阅 Apple 的以下文章: MTLSSharedEvent同步事件在 GPU 和 CPU 之间

但是您还应该注意,如果您不使用多个命令队列,您的 GPU 将基本上停止,什么也不做,同时等待 CPU 完成它所做的任何修改。 有时您无法避免它,但通常最好尝试将所有可以做的工作转移到 GPU 时间线,以避免 GPU 停顿。 您可以尝试在同一个队列上安排工作以修改您的顶点或使用多个队列并使用本文中的技术: 在单个设备中同步事件

无论如何,我希望这可以帮助您解决问题。

暂无
暂无

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

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