yum-mirror/VkFFT

git clone https://git.yummers.dev/yum-mirror/VkFFT

Tolmachev DmitriiMetal support in VkFFTba7001c

master
10.6 KiB245 linesraw

About

metal-cpp is a low overhead and header only C++ interface for Metal that helps developers add Metal functionality to graphics applications that are written in C++ (such as game engines). metal-cpp removes the need to create a shim and allows developers to call Metal functions directly from anywhere in their existing C++ code.

Highlights

  • Drop in C++ alternative interface to the Metal Objective-C headers.
  • Direct mapping of all Metal Objective-C classes, constants and enums to C++ in the MTL C++ namespace.
  • No measurable overhead compared to calling Metal Objective-C headers, due to inlining of C++ function calls.
  • No usage of wrapper containers that require additional allocations.
  • Requires C++17 due to the usage of constexpr in NS::Object.
  • Identical header files and function/constant/enum availability for iOS, macOS and tvOS.
  • Backwards compatibility: All bool MTL::Device::supports...() functions check if their required selectors exist and automatically return false if not.
  • String (ErrorDomain) constants are weak linked and automatically set to nullptr if not available.

Memory Allocation Policy

metal-cpp follows the object allocation policies of Cocoa and Cocoa Touch. Understanding those rules is especially important when using metal-cpp, as C++ objects are not eligible for automatic reference counting (ARC).

metal-cpp objects are reference counted. To help convey and manage object lifecycles, the following conventions are observed:

  1. You own any object returned by methods whose name begins with alloc , new , copy , or mutableCopy. The method returns these objects with retainCount equals to 1.
  2. You can take ownership of an object by calling its retain() method. A received object is normally guaranteed to remain valid within the method it was received in. You use retain in two situations: (1) In the implementation of an accessor method (a setter) or to take ownership of an object; and (2) To prevent an object from being deallocated as a side-effect of some other operation.
  3. When you no longer need it, you must relinquish ownership of an object you own. You relinquish ownership by calling its release() or autorelease() method.
  4. You must not relinquish ownership of an object you do not own.

When an object's retainCount reaches 0, the object is immediately deallocated. It is illegal to call methods on a deallocated object and it may lead to an application crash.

AutoreleasePools and Objects

Several methods that create temporary objects in metal-cpp add them to an AutoreleasePool to help manage their lifetimes. In these situations, after metal-cpp creates the object, it adds it to an AutoreleasePool, which will release its objects when you release (or drain) it.

By adding temporary objects to an AutoreleasePool, you do not need to explicitly call release() to deallocate them. Instead, you can rely on the AutoreleasePool to implicitly manage those lifetimes.

If you create an object with a method that does not begin with alloc, new, copy, or mutableCopy, the creating method adds the object to an autorelease pool.

The typical scope of an AutoreleasePool is one frame of rendering for the main thread of the program. When the thread returns control to the RunLoop (an object responsible for receiving input and events from the windowing system), the pool is drained, releasing its objects.

You can create and manage additional AutoreleasePools at smaller scopes to reduce your program's working set, and you are required to do so for any additional threads your program creates.

If an object's lifecycle needs to be extended beyond the AutoreleasePool's scope, you can claim ownership of it (avoiding its release beyond the pool's scope) by calling its retain() method before its pool is drained. In these cases, you will be responsible for making the appropriate release() call on the object after you no longer need it.

You can find a more-detailed introduction to the memory management rules here: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html.

For more details about the application's RunLoop, please find its documentation here: https://developer.apple.com/documentation/foundation/nsrunloop

Use and debug AutoreleasePools

When you create an autoreleased object and there is no enclosing AutoreleasePool, the object is leaked.

To prevent this, you normally create an AutoreleasePool in your program's main function, and in the entry function for every thread you create. You may also create additional AutoreleasePools to avoid growing your program's high memory watermark when you create several autoreleased objects, such as when rendering.

Use the Environment Variable OBJC_DEBUG_MISSING_POOLS=YES to print a runtime warning when an autoreleased object is leaked because no enclosing AutoreleasePool is available for its thread.

You can also run leaks --autoreleasePools on a memgraph file or a process ID (macOS only) to view a listing of your program's AutoreleasePools and all objects they contain.

nullptr

Similar to Objective-C, it is legal to call any method, including retain() and release(), on nullptr "objects". While calling methods on nullptr still does incur in function call overhead, the effective result is equivalent of a NOP.

Conversely, do not assume that because calling a method on a pointer did not result in a crash, that the pointed-to object is valid.

Adding metal-cpp to a Project

Simply include Metal/Metal.hpp. To ensure that the selector and class symbols are linked, add to one of your cpp files:

#define NS_PRIVATE_IMPLEMENTATION
#define MTL_PRIVATE_IMPLEMENTATION

#include "Metal/Metal.hpp"

If you want to use the QuartzCore wrapper, add:

#define CA_PRIVATE_IMPLEMENTATION

#include "QuartzCore/QuartzCore.hpp"

Generating a Single Header File

Purely optional: You can generate a single header file that contains all metal-cpp headers via:

./SingleHeader/MakeSingleHeader.py Foundation/Foundation.hpp QuartzCore/QuartzCore.hpp Metal/Metal.hpp

By default the generated header file will be written to ./SingleHeader/Metal.hpp

Examples

Creating the device

Objective-C (with automatic reference counting)
id< MTLDevice > device = MTLCreateSystemDefaultDevice();

// ...
Objective-C
id< MTLDevice > device = MTLCreateSystemDefaultDevice();

// ...

[device release];
C++
MTL::Device* pDevice = MTL::CreateSystemDefaultDevice();

// ...

pDevice->release();

Metal function calls map directly to C++

Objective-C (with automatic reference counting)
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];

[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat];
[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat];
[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat];
[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear];
[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear];
[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear];
[samplerDescriptor setSupportArgumentBuffers: YES];

id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor];
Objective-C
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];

[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat];
[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat];
[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat];
[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear];
[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear];
[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear];
[samplerDescriptor setSupportArgumentBuffers: YES];

id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor];

[samplerDescriptor release];

// ...

[samplerState release];
C++
MTL::SamplerDescriptor* pSamplerDescriptor = MTL::SamplerDescriptor::alloc()->init();

pSamplerDescriptor->setSAddressMode( MTL::SamplerAddressModeRepeat );
pSamplerDescriptor->setTAddressMode( MTL::SamplerAddressModeRepeat );
pSamplerDescriptor->setRAddressMode( MTL::SamplerAddressModeRepeat );
pSamplerDescriptor->setMagFilter( MTL::SamplerMinMagFilterLinear );
pSamplerDescriptor->setMinFilter( MTL::SamplerMinMagFilterLinear );
pSamplerDescriptor->setMipFilter( MTL::SamplerMipFilterLinear );
pSamplerDescriptor->setSupportArgumentBuffers( true );

MTL::SamplerState* pSamplerState = pDevice->newSamplerState( pSamplerDescriptor );

pSamplerDescriptor->release();

// ...

pSamplerState->release();

A subset of bindings for Foundation classes is provided for seamless integration

Objective-C (with automatic reference counting)
NSAutoreleasePool*  pool   = [[NSAutoreleasePool alloc] init];
NSString*           string = [NSString stringWithCString: "Hello World" encoding: NSASCIIStringEncoding];

printf( "string = \"%s\"\n", [string cStringUsingEncoding: NSASCIIStringEncoding] );
Objective-C
NSAutoreleasePool* pool   = [[NSAutoreleasePool alloc] init];
NSString*          string = [NSString stringWithCString: "Hello World" encoding: NSASCIIStringEncoding];
								
printf( "string = \"%s\"\n", [string cStringUsingEncoding: NSASCIIStringEncoding] );

[pool release];
C++
NS::AutoreleasePool* pPool   = NS::AutoreleasePool::alloc()->init();
NS::String*          pString = NS::String::string( "Hello World", NS::ASCIIStringEncoding );

printf( "pString = \"%s\"\n", pString->cString( NS::ASCIIStringEncoding ) );

pPool->release();

Interoperability with CoreFoundation

MTL::AccelerationStructureTriangleGeometryDescriptor* pGeoDescriptor  = MTL::AccelerationStructureTriangleGeometryDescriptor::alloc()->init();
CFTypeRef                                             descriptors[]   = { ( CFTypeRef )( pGeoDescriptor ) };
NS::Array*                                            pGeoDescriptors = ( NS::Array* )( CFArrayCreate( kCFAllocatorDefault, descriptors, SIZEOF_ARRAY( descriptors), &kCFTypeArrayCallBacks ) );

// ...

pGeoDescriptors->release();

Accessing a CAMetalDrawable

#import <QuartzCore/CAMetalLayer.h>
#import <QuartzCore/QuartzCore.hpp>

// ...

CAMetalLayer*         metalLayer         = /* get your layer from your view */;
id< CAMetalDrawable > metalDrawable      = [metalLayer nextDrawable];
CA::MetalDrawable*    pMetalCppDrawable  = ( __bridge CA::MetalDrawable* ) metalDrawable;

// ...
1## About
2
3**metal-cpp** is a low overhead and header only C++ interface for Metal that helps developers add Metal functionality to graphics applications that are written in C++ (such as game engines). **metal-cpp** removes the need to create a shim and allows developers to call Metal functions directly from anywhere in their existing C++ code.
4
5
6## Highlights
7
8- Drop in C++ alternative interface to the Metal Objective-C headers.
9- Direct mapping of all Metal Objective-C classes, constants and enums to C++ in the MTL C++ namespace.
10- No measurable overhead compared to calling Metal Objective-C headers, due to inlining of C++ function calls.
11- No usage of wrapper containers that require additional allocations.
12- Requires C++17 due to the usage of `constexpr` in `NS::Object`.
13- Identical header files and function/constant/enum availability for iOS, macOS and tvOS.
14- Backwards compatibility: All `bool MTL::Device::supports...()` functions check if their required selectors exist and automatically return `false` if not.
15- String (`ErrorDomain`) constants are weak linked and automatically set to `nullptr` if not available.
16
17## Memory Allocation Policy
18
19**metal-cpp** follows the object allocation policies of Cocoa and Cocoa Touch. Understanding those rules is especially important when using metal-cpp, as C++ objects are not eligible for automatic reference counting (ARC).
20
21**metal-cpp** objects are reference counted. To help convey and manage object lifecycles, the following conventions are observed:
22
231. *You own any object returned by methods whose name begins with* `alloc` *,* `new` *,* `copy` *, or* `mutableCopy`. The method returns these objects with `retainCount` equals to `1`.
242. *You can take ownership of an object by calling its* ```retain()``` *method*. A received object is normally guaranteed to remain valid within the method it was received in. You use `retain` in two situations: (1) In the implementation of an accessor method (a setter) or to take ownership of an object; and (2) To prevent an object from being deallocated as a side-effect of some other operation.
253. *When you no longer need it, you must relinquish ownership of an object you own*. You relinquish ownership by calling its `release()` or `autorelease()` method.
264. *You must not relinquish ownership of an object you do not own*.
27
28When an object's `retainCount` reaches `0`, the object is immediately deallocated. It is illegal to call methods on a deallocated object and it may lead to an application crash.
29
30### AutoreleasePools and Objects
31
32Several methods that create temporary objects in **metal-cpp** add them to an `AutoreleasePool` to help manage their lifetimes. In these situations, after **metal-cpp** creates the object, it adds it to an `AutoreleasePool`, which will release its objects when you release (or drain) it.
33
34By adding temporary objects to an AutoreleasePool, you do not need to explicitly call `release()` to deallocate them. Instead, you can rely on the `AutoreleasePool` to implicitly manage those lifetimes.
35
36If you create an object with a method that does not begin with `alloc`, `new`, `copy`, or `mutableCopy`, the creating method adds the object to an autorelease pool.
37
38The typical scope of an `AutoreleasePool` is one frame of rendering for the main thread of the program. When the thread returns control to the RunLoop (an object responsible for receiving input and events from the windowing system), the pool is *drained*, releasing its objects.
39
40You can create and manage additional `AutoreleasePool`s at smaller scopes to reduce your program's working set, and you are required to do so for any additional threads your program creates.
41
42If an object's lifecycle needs to be extended beyond the `AutoreleasePool`'s scope, you can claim ownership of it (avoiding its release beyond the pool's scope) by calling its `retain()` method before its pool is drained. In these cases, you will be responsible for making the appropriate `release()` call on the object after you no longer need it. 
43
44You can find a more-detailed introduction to the memory management rules here: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html.
45
46For more details about the application's RunLoop, please find its documentation here: https://developer.apple.com/documentation/foundation/nsrunloop
47
48### Use and debug AutoreleasePools
49
50When you create an autoreleased object and there is no enclosing `AutoreleasePool`, the object is leaked.
51
52To prevent this, you normally create an `AutoreleasePool` in your program's `main` function, and in the entry function for every thread you create. You may also create additional `AutoreleasePool`s to avoid growing your program's high memory watermark when you create several autoreleased objects, such as when rendering.
53
54Use the Environment Variable `OBJC_DEBUG_MISSING_POOLS=YES` to print a runtime warning when an autoreleased object is leaked because no enclosing `AutoreleasePool` is available for its thread.
55
56You can also run `leaks --autoreleasePools` on a memgraph file or a process ID (macOS only) to view a listing of your program's `AutoreleasePool`s and all objects they contain.
57
58### nullptr
59
60Similar to Objective-C, it is legal to call any method, including `retain()` and `release()`, on `nullptr` "objects". While calling methods on `nullptr` still does incur in function call overhead, the effective result is equivalent of a NOP.
61
62Conversely, do not assume that because calling a method on a pointer did not result in a crash, that the pointed-to object is valid.
63
64## Adding metal-cpp to a Project
65
66Simply include `Metal/Metal.hpp`. To ensure that the selector and class symbols are linked, add to one of your cpp files:
67
68```cpp
69#define NS_PRIVATE_IMPLEMENTATION
70#define MTL_PRIVATE_IMPLEMENTATION
71
72#include "Metal/Metal.hpp"
73```
74
75If you want to use the QuartzCore wrapper, add:
76
77```cpp
78#define CA_PRIVATE_IMPLEMENTATION
79
80#include "QuartzCore/QuartzCore.hpp"
81```
82
83## Generating a Single Header File
84
85Purely optional: You can generate a single header file that contains all **metal-cpp** headers via:
86
87```shell
88./SingleHeader/MakeSingleHeader.py Foundation/Foundation.hpp QuartzCore/QuartzCore.hpp Metal/Metal.hpp
89```
90
91By default the generated header file will be written to `./SingleHeader/Metal.hpp`
92
93## Examples
94
95#### Creating the device
96
97###### Objective-C (with automatic reference counting)
98
99```objc
100id< MTLDevice > device = MTLCreateSystemDefaultDevice();
101
102// ...
103```
104
105###### Objective-C
106
107```objc
108id< MTLDevice > device = MTLCreateSystemDefaultDevice();
109
110// ...
111
112[device release];
113```
114
115###### C++
116
117```cpp
118MTL::Device* pDevice = MTL::CreateSystemDefaultDevice();
119
120// ...
121
122pDevice->release();
123```
124
125#### Metal function calls map directly to C++
126
127###### Objective-C (with automatic reference counting)
128
129```objc
130MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
131
132[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat];
133[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat];
134[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat];
135[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear];
136[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear];
137[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear];
138[samplerDescriptor setSupportArgumentBuffers: YES];
139
140id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor];
141```
142
143###### Objective-C
144
145```objc
146MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
147
148[samplerDescriptor setSAddressMode: MTLSamplerAddressModeRepeat];
149[samplerDescriptor setTAddressMode: MTLSamplerAddressModeRepeat];
150[samplerDescriptor setRAddressMode: MTLSamplerAddressModeRepeat];
151[samplerDescriptor setMagFilter: MTLSamplerMinMagFilterLinear];
152[samplerDescriptor setMinFilter: MTLSamplerMinMagFilterLinear];
153[samplerDescriptor setMipFilter: MTLSamplerMipFilterLinear];
154[samplerDescriptor setSupportArgumentBuffers: YES];
155
156id< MTLSamplerState > samplerState = [device newSamplerStateWithDescriptor:samplerDescriptor];
157
158[samplerDescriptor release];
159
160// ...
161
162[samplerState release];
163```
164
165###### C++
166
167```cpp
168MTL::SamplerDescriptor* pSamplerDescriptor = MTL::SamplerDescriptor::alloc()->init();
169
170pSamplerDescriptor->setSAddressMode( MTL::SamplerAddressModeRepeat );
171pSamplerDescriptor->setTAddressMode( MTL::SamplerAddressModeRepeat );
172pSamplerDescriptor->setRAddressMode( MTL::SamplerAddressModeRepeat );
173pSamplerDescriptor->setMagFilter( MTL::SamplerMinMagFilterLinear );
174pSamplerDescriptor->setMinFilter( MTL::SamplerMinMagFilterLinear );
175pSamplerDescriptor->setMipFilter( MTL::SamplerMipFilterLinear );
176pSamplerDescriptor->setSupportArgumentBuffers( true );
177
178MTL::SamplerState* pSamplerState = pDevice->newSamplerState( pSamplerDescriptor );
179
180pSamplerDescriptor->release();
181
182// ...
183
184pSamplerState->release();
185```
186
187#### A subset of bindings for Foundation classes is provided for seamless integration
188
189###### Objective-C (with automatic reference counting)
190
191```objc
192NSAutoreleasePool*  pool   = [[NSAutoreleasePool alloc] init];
193NSString*           string = [NSString stringWithCString: "Hello World" encoding: NSASCIIStringEncoding];
194
195printf( "string = \"%s\"\n", [string cStringUsingEncoding: NSASCIIStringEncoding] );
196```
197
198###### Objective-C
199
200```objc
201NSAutoreleasePool* pool   = [[NSAutoreleasePool alloc] init];
202NSString*          string = [NSString stringWithCString: "Hello World" encoding: NSASCIIStringEncoding];
203								
204printf( "string = \"%s\"\n", [string cStringUsingEncoding: NSASCIIStringEncoding] );
205
206[pool release];
207```
208
209###### C++
210
211```cpp
212NS::AutoreleasePool* pPool   = NS::AutoreleasePool::alloc()->init();
213NS::String*          pString = NS::String::string( "Hello World", NS::ASCIIStringEncoding );
214
215printf( "pString = \"%s\"\n", pString->cString( NS::ASCIIStringEncoding ) );
216
217pPool->release();
218```
219
220####  Interoperability with CoreFoundation
221
222```cpp
223MTL::AccelerationStructureTriangleGeometryDescriptor* pGeoDescriptor  = MTL::AccelerationStructureTriangleGeometryDescriptor::alloc()->init();
224CFTypeRef                                             descriptors[]   = { ( CFTypeRef )( pGeoDescriptor ) };
225NS::Array*                                            pGeoDescriptors = ( NS::Array* )( CFArrayCreate( kCFAllocatorDefault, descriptors, SIZEOF_ARRAY( descriptors), &kCFTypeArrayCallBacks ) );
226
227// ...
228
229pGeoDescriptors->release();
230```
231
232#### Accessing a CAMetalDrawable
233
234```objc
235#import <QuartzCore/CAMetalLayer.h>
236#import <QuartzCore/QuartzCore.hpp>
237
238// ...
239
240CAMetalLayer*         metalLayer         = /* get your layer from your view */;
241id< CAMetalDrawable > metalDrawable      = [metalLayer nextDrawable];
242CA::MetalDrawable*    pMetalCppDrawable  = ( __bridge CA::MetalDrawable* ) metalDrawable;
243
244// ...
245```