测试托管缓冲区的最佳实践?

Best Practice for testing Managed Buffers?

在配备独立显卡的 Mac 上,应使用托管缓冲区而不是共享缓冲区,但是使用 [MTLBuffer:didModifyRange:] 保持同步还有其他要求。

但是在 Apple Silicon 上,如果我通过假装 [MTLDevice hasUnifiedMemory] returns NO 并删除对 didModifyRange: 的调用来强制使用托管缓冲区,则渲染是工作正常。

在统一 GPU 内存的 Apple Silicon 上测试托管缓冲区的最佳方法是什么,以便我可以确定我的代码可以在旧 Mac 上运行?

测试硬件兼容性的最佳做法是在您测试兼容性的实际硬件上进行。如果您计划支持与 Apple Silicon 本质上 不同的离散 GPU,最好能够访问一个进行测试。

您可能会有近似的行为,但请记住,这只是一种模拟,无法确保实际硬件的工作方式相同。

这类似于仅使用模拟器进行开发,这根本不是一个好的做法。

更新:有许多服务可以租用裸机 Mac。 MacInCloud 服务允许您配置带有外部 GPU 的机器(例如 AMD RX 580)。前 24 小时只需 0.99 美元。

那里有许多类似的服务,但这是我能够验证离散 GPU 是一种选择的第一项服务。

根据我的经验,在渲染 api 时没有测试代码的最佳实践,因为这里有许多不同的因素: GPUCPU 供应商(AppleAMD英特尔)、操作系统、驱动程序。

我同意Jeshua:

The best practice for testing hardware compatibility is on the actual hardware in which you are testing compatibility.

有许多有用的方法可以使开发和测试更容易:


您可以检测 供应商 ID:

id<MTLDevice> device = MTLCreateSystemDefaultDevice();
NSString* appleGPU = [device.name containsString:@"Apple"];
NSString* intelGPU = [device.name containsString:@"Intel"];
NSString* amdGPU = [device.name containsString:@"AMD"];
NSString* nvidiaGPU = [device.name containsString:@"Nvidia"];

使用 following method 你可以找到你的 gpu 类型:

bool externalGPU = [device isRemovable] == true;
bool integratedGPU = [device isLowPower] == true;
bool discreteGPU = [device isLowPower] == false;

注:

[device isLowPower];

On a Mac with an Apple silicon M1 chip, the property is NO because the GPU runs with both high performance and low power.


确定TBDRGPU架构:

if (@available(macOS 10.15, *)) {
    if ([device supportsFamily: MTLGPUFamilyApple4])
    {
        // The GPU does support Tile-Based Deferred Rendering technique
    }
}

Understand the Managed Mode:

In a unified memory model, a resource with a MTLStorageModeManaged mode resides in system memory accessible to both the CPU and the GPU.

行为类似于 MTLStorageModeShared只有一份内容

注:

In a unified memory model, Metal may ignore synchronization calls completely because it only creates a single memory allocation for the resource.


您还可以检查其他开发人员的一些实现:

PixarAnimationStudios/USD:

HgiMetalCapabilities::HgiMetalCapabilities(id<MTLDevice> device)
{
    if (@available(macOS 10.14.5, ios 12.0, *)) {
        _SetFlag(HgiDeviceCapabilitiesBitsConcurrentDispatch, true);
    }

    defaultStorageMode = MTLResourceStorageModeShared;
    bool unifiedMemory = false;
    if (@available(macOS 100.100, ios 12.0, *)) {
        unifiedMemory = true;
    } else if (@available(macOS 10.15, ios 13.0, *)) {
#if defined(ARCH_OS_IOS) || (defined(__MAC_10_15) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15)
        unifiedMemory = [device hasUnifiedMemory];
#else
        unifiedMemory = [device isLowPower];
#endif
    }

    _SetFlag(HgiDeviceCapabilitiesBitsUnifiedMemory, unifiedMemory);

#if defined(ARCH_OS_MACOS)
    if (!unifiedMemory) {
        defaultStorageMode = MTLResourceStorageModeManaged;
    }
#endif
}

KhronosGroup/MoltenVK

// Metal Managed:
//  - applies to both buffers and textures
//  - default mode for textures on macOS
//  - two copies of each buffer or texture when discrete memory available
//  - convenience of shared mode, performance of private mode
//  - on unified systems behaves like shared memory and has only one copy of content
//  - when writing, use:
//      - buffer didModifyRange:
//      - texture replaceRegion:
//  - when reading, use:
//      - encoder synchronizeResource: followed by
//      - cmdbuff waitUntilCompleted (or completion handler)
//      - buffer/texture getBytes: