在Metal中手动设置一维纹理

我试图用值手动填充一维纹理 ,并将纹理传递给计算着色器(这些是我想通过代码设置的2个像素,它们不代表任何图像)。

由于目前less量的金属例子,我能find的所有例子都处理2D纹理 ,通过将加载的UIImage转换为原始字节数据来加载纹理,但是创build一个虚拟UIImage对我来说就像是黑客。

这是我开始的“天真”的方式 –

 ... var manualTextureData: [Float] = [ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0 ]; let region: MTLRegion = MTLRegionMake1D(0, textureDescriptor.width); myTexture.replaceRegion(region, mipmapLevel: 0, withBytes: &manualTextureData, bytesPerRow: 0); 

但是Metal没有在着色器中识别这些值(除了第一个值,它会得到一个空的纹理)。

我很快意识到,浮点数组可能必须转换成一个字节数组(例如UInt8 ),但无法find一种方法从[Float]转换为[UInt8]

我考虑的另一个可能的select是使用一个CVPixelBuffer对象,但是这也是解决问题的办法。

那么最好的解决方法是什么?

提前致谢。

  • 请注意,我不熟悉Objective-C ,因此我不确定使用CVPixelBuffer / UIImage是否夸大了某些应该是直截了当的事情。

我看不出有什么理由让你使用一维纹理来传递数据。 相反,我会只是通过一个缓冲区。 喜欢这个:

 var dataBuffer:MTLBuffer? = device.newBufferWithBytes(&manualTextureData, length: sizeOf(manualTextureData), options: MTLResourceOptions.OptionCPUCacheModeDefault) 

然后,你把它钩到你的renderCommandEncoder像这样:

 renderCommandEncoder.setFragmentBuffer(dataBuffer, offset: 0, atIndex: 1)//Note that if you want this buffer to be passed to you vertex shader you should use setVertexBuffer 

然后在你的着色器中,你应该像这样const device float* bufferPassed [[ buffer(1) ]]

然后在着色器实现中像这样使用它:

 float firstFloat = bufferPassed[0]; 

这将完成工作。

不是真的回答你的问题,但你可以在你的金属着色器中定义一个数组,而不是将值作为纹理传递。

就像是:

 constant float manualData[8] = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0 }; vertex float4 world_vertex(unsigned int vid[[vertex_id]], ...) { int manualIndex = vid % 8; float manualValue = manualData[manualIndex]; // something deep and meaningful here... return float4(manualValue); } 

如果你想要一个浮点纹理,bytesPerRow应该是4倍的宽度,因为float的大小是4个字节。 金属复制记忆,不关心价值。 那是你的任务;-)

就像是:

 myTexture.replaceRegion(region, mipmapLevel: 0, withBytes: &manualTextureData, bytesPerRow: manualTextureData.count * sizeof(Float)); 

请原谅简短的答复,但你可能会发现看看我的实验与斯威夫特和金属是有用的。 我在Swift中创build了一个粒子系统,它被作为一个粒子结构的一维数组传递给一个金属计算着色器。 通过使用posix_memalign,我能够消除在Metal和Swift之间传递数组所导致的瓶颈。

我已经广泛讨论了这个: http : //flexmonkey.blogspot.co.uk/search/label/Metal

我希望这有帮助。

西蒙