如何在macOS Cocoa Objective-C中快照和保存渲染的金属可绘制图形?

问题描述 投票:0回答:2

我有一个使用Metal进行屏幕渲染的应用程序(必要时使用CAMetalLayer,而不是MTKView),我想为用户提供保存快照的选项结果存入磁盘。尝试转换为Objective-C时遵循https://stackoverflow.com/a/47632198/2752221的答案,我首先这样编写了一个commandBuffer完成回调(请注意,这是手动保留/释放代码,而不是ARC;对不起,遗留代码):

[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
    [self performSelectorOnMainThread:@selector(saveImageTakingDrawable:) withObject:[drawable retain] waitUntilDone:NO];
    optionKeyPressedAndUnhandled_ = NO;
}];

我在致电[commandBuffer presentDrawable:drawable]之后立即执行此操作;我的id <CAMetalDrawable> drawable仍在范围内。这是我对saveImageTakingDrawable:的实现:

- (void)saveImageTakingDrawable:(id <CAMetalDrawable>)drawable
{
    // We need to have an image to save
    if (!drawable) { NSBeep(); return; }

    id<MTLTexture> displayTexture = drawable.texture;

    if (!displayTexture) { NSBeep(); return; }

    CIImage *ciImage = [CIImage imageWithMTLTexture:displayTexture options:nil];

    // release the metal texture as soon as we can, to free up the system resources
    [drawable release];

    if (!ciImage) { NSBeep(); return; }

    NSCIImageRep *rep = [NSCIImageRep imageRepWithCIImage:ciImage];

    if (!rep) { NSBeep(); return; }

    NSImage *nsImage = [[[NSImage alloc] initWithSize:rep.size] autorelease];
    [nsImage addRepresentation:rep];

    NSData *tiffData = [nsImage TIFFRepresentation];

    if (!tiffData) { NSBeep(); return; }

    ... filesystem cruft culminating in ...

    if ([tiffData writeToFile:filePath options:NSDataWritingWithoutOverwriting error:nil])
    {
        // play a sound to acknowledge saving
        [[NSSound soundNamed:@"Tink"] play];
        return;
    }

    NSBeep();
    return;
}

结果是“ Tink”声音和大小为7.8 MB(1784x1090)的.tif文件,但它是透明的,并且其中没有可用的图像数据;在Hex Fiend中查看文件显示,除了相当简短的页眉和页脚节以外,整个文件都是零。

我怀疑基本方法由于某种原因存在缺陷。尝试使用此快照时,会收到几个控制台日志:

2020-06-04 18:20:40.203669-0400 MetalTest[37773:1065740] [CAMetalLayerDrawable texture] should not be called after already presenting this drawable. Get a nextDrawable instead.
Input Metal texture was created with a device that does not match the current context device.
Input Metal texture was created with a device that does not match the current context device.
2020-06-04 18:20:40.247637-0400 MetalTest[37773:1065740] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600000297260> F8BB1C28-BAE8-11D6-9C31-00039315CD46
2020-06-04 18:20:40.281161-0400 MetalTest[37773:1065740]  HALC_ShellDriverPlugIn::Open: Can't get a pointer to the Open routine

该第一条日志似乎表明,我什至根本不允许我将纹理从可绘制对象中移除。那么...什么是正确的方法?

更新:

[请注意,我不喜欢saveImageTakingDrawable:代码的后面部分。我很乐意写出PNG而不是TIFF,并且如果有一种无需使用CIImageNSCIImageRepNSImage即可到达目的地的方法,那就更好了。我只想将可绘制的纹理图像另存为PNG或TIFF,以某种方式]。

[我有一个使用Metal进行屏幕渲染的应用程序(必要时使用CAMetalLayer而非MTKView),我想为用户提供保存结果快照的选项。 ..

objective-c macos cocoa metal
2个回答
0
投票

我只想以某种方式将可绘制的纹理图像另存为PNG或TIFF。


0
投票

我认为您应该在命令缓冲区中注入blit(在提交之前),以将纹理复制到您自己的纹理中。 (正如您所发现的那样,可绘制对象的纹理在呈现后并不安全使用。)

© www.soinside.com 2019 - 2024. All rights reserved.