在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
我希望这有帮助。
西蒙