4

I have a large array with more than 1 million struct instance. The size is actually dynamic. It works fine with MTLBuffer, it appears to be a pointer in Metal shader, and I don't have to write some hardcoded size or length.

The problem is, I don't want to bind or set it for each frame. So, I am looking into ArgumentBuffers and Heap. But I didn't find solution or example for my use case. It looks like everything in array inside ArgumentBuffers will take some slot. That's not a good idea for my large array. And I didn't find good example which have pointer in ArgumentBuffers.

After all, what is best practice for my use case?

iaomw
  • 720
  • 6
  • 21
  • It's a bit unclear what you are asking or where the issue is. Are you trying to pass the large array as a single buffer in the argument buffer? Or do you want each instance of the struct to be a separate field in the argument buffer? Are you trying to save memory or time or both? A code example/snippet can be helpful here. – Itai May 19 '21 at 16:17
  • @Itai Hi, I need the array still work as single buffer while using argumentbuffer. The purpose is reduce the code for [.... setBuffer ... offset ... atIndex ...] , use less code in rendering loop. – iaomw May 19 '21 at 16:33
  • 1
    I mean, as with any array you need a pointer to the beginning of the array, and a number of elements. You can make an single argument buffer that has this pointer and number of elements and bind just that. Then you populate it using `MTLArgumentEncoder`. After that, you only need to mark the buffer that is used indirectly (meaning it's encoded in the AB, but not used) by calling `useResource`. – JustSomeGuy May 19 '21 at 18:23

1 Answers1

3

Let me answer my own question. To support dynamic sized array inside Argument buffer. We need the device supporting Metal 2 Tier 2.

[argumentEncoder setArgumentBuffer:_argumentBuffer startOffset:0 arrayElement:0];
// Then, write some code to fill the data for first element
[argumentEncoder setArgumentBuffer:_argumentBuffer startOffset:0 arrayElement:1];
// Then, write some code to fill the data for second element

While, on the GPU side you can simple use the pointer reference below.

constant TheBufferTypeName* theBuffer [[buffer(0)]]

Then, use it with theBuffer[0] or theBuffer[1].

It's done without any hardcoded size.

iaomw
  • 720
  • 6
  • 21
  • May you please show me how you fill the data of an element? It should be useful to see the structure of your argument on the shader too. In my code my argument is a structure containing a uint_8 totLights and an array, where Light is a structure of float3, float4 and so on. I can't succeed at filling the argument with more than 1 Light. Something is wrong with the bytes alignment. Your solution could help. Thanks. – Leonardo Oct 15 '22 at 14:40
  • @Leonardo Hi, I never really have padding issues. Becuase I use Objective-C++, they should have exactly same code and same memory layout as Metal. However, if you want take a look, here is my code. You can simply search for `setArgumentBuffer` in this file https://github.com/iaomw/Tracer/blob/master/RT_Metal/Tracer/AAPLRenderer.mm – iaomw Dec 11 '22 at 16:30