Сглаживание рендеринга SceneKit с помощью металла

Я новичок в металле. Я визуализирую сцену SceneKit с помощью Metal, используя этот пример кода Apple. TL; DR; он вызывает функцию SCNRenderer's render и передает буфер команд. Я собираю для Биг Сур.

Работает, но не сглаживается. Я пробовал несколько способов добиться этого, как вы можете видеть в обновлениях ниже.

Без Металла я бы просто установил isJitteringEnabled в true на SCNRenderer, и я получил бы красивые (и медленные) 96-проходные рендеры. Если я попытаюсь сделать это с помощью Metal, я получу странное несоответствие формата пикселей, поэтому я подозреваю, что они просто несовместимы.

С помощью Metal, насколько я могу судить, самый простой способ добиться сглаживания - включить множественную выборку в конвейере рендеринга (я знаю, как это сделать) - и использовать текстуру множественной выборки (MTLTextureType.type2DMultisample ). Этот частичный ответ подтверждает мое предположение.

И вот в чем проблема. Я не знаю, как изменить тип текстуры, когда я получаю текстуру от CVMetalTextureCache и CVMetalTextureCacheCreateTextureFromImage. Кажется, это ограничение в поддержке Metal в Core Video?

Мой полный источник здесь

Вот и все. Остальная часть этого поста - это более подробная информация о том, что я пробовал.

(Я думаю, что это возможно с помощью шейдера. Я тоже открыт для этого решения, но не знаю, с чего начать. Этот пример не компилируется, и этот пример предназначен для GSLS)


Мои пиксельные буферы выглядят так

        let pixelbufferAttributes = [
            kCVPixelBufferPixelFormatTypeKey : kCVPixelFormatType_32BGRA,
            kCVPixelBufferWidthKey: exportSettings.width,
            kCVPixelBufferHeightKey : exportSettings.height,
        kCVPixelBufferMetalCompatibilityKey: true] as [String: Any]

Для каждого кадра он создает новый буфер пикселей из пула, обертывает его текстурой Metal из кеша, как это

        let pixelFormat = MTLPixelFormat.bgra8Unorm_srgb
        var optionalMetalTexture: CVMetalTexture?
        err = CVMetalTextureCacheCreateTextureFromImage(
            kCFAllocatorDefault,
            metalTextureCache, // object prop
            pixelBuffer,
            nil, // texture attributes
            pixelFormat,
            exportSettings.width,
            exportSettings.height,
            0, // planeIndex
            &optionalMetalTexture)
        guard err == noErr, let metalTexture = optionalMetalTexture else {
            fatalError("Failed to create metal texture wrapper from pixel bufffer \(err)")
        }

Попытка: изменить дескриптор текстуры

Так как я создаю свою металлическую текстуру из CVPixelbuffer с CVMetalTextureCacheCreateTextureFromImage, я не могу понять, как установить ее атрибуты и сделать это мультисэмплом.

Попытка: попробуйте H264

Ничего не менял. Также попытался изменить только качество альфа, с HEVC с альфа, но без изменений.

Попытка: включить множественную выборку

Мне удалось заставить свой конвейер понять, что мне нужна множественная выборка, но он вылетает из-за того, что текстура не настроена для мультисэмплинга (точнее, MTLTexture типа .2DMultisample (docs)

Попытка: скопировать MTLTexture, созданный Core Video

Я попытался использовать MTLBlitCommandEncoder, чтобы скопировать текстуру, которую мне дал Core Video, в текстуру, которую я настроил с правильными атрибутами. Но он вылетает, говоря мне, что атрибуты не совпадают.

Я начинаю думать, что здесь нет решения?


person Morten J    schedule 22.11.2020    source источник
comment
Какое у вас целевое устройство?   -  person Hamid Yusifli    schedule 22.11.2020
comment
Это приложение для macOS, а мое устройство - 16 с AMD Radeon Pro 5500M 8 ГБ.   -  person Morten J    schedule 23.11.2020
comment
Ваш источник github == образец яблока?   -  person Hamid Yusifli    schedule 26.11.2020
comment
@ 0xBFE1A8 да, единственное, что я изменил в этом коммите, это дрожащая строка. Сам образец находится здесь: developer.apple.com/documentation/avfoundation/   -  person Morten J    schedule 26.11.2020


Ответы (1)


Включение мультисэмплинга было правильной идеей. Следующий патч показывает, как его включить.

--- a/HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift
+++ b/HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift
@@ -32,6 +32,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
     let renderer = SCNRenderer(device: nil, options: nil)
     var lampMaterials: SCNNode!
     var metalTextureCache: CVMetalTextureCache!
+    let msaaSampleCount = 1
+    var metalMultisampledTexture: MTLTexture!
     
     // Export
     var frameCounter = 0
@@ -61,6 +63,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
             fatalError("Cannot create metal texture cache: \(err)")
         }
         metalTextureCache = optionalMetalTextureCache
+        
+        if (msaaSampleCount > 1) {
+            let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.bgra8Unorm_srgb,
+                                                                             width: ExportSettings.width,
+                                                                             height: ExportSettings.height,
+                                                                             mipmapped: false)
+            textureDescriptor.usage = .renderTarget
+            textureDescriptor.storageMode = .private
+            textureDescriptor.textureType = .type2DMultisample
+            textureDescriptor.sampleCount = msaaSampleCount
+            metalMultisampledTexture = renderer.device!.makeTexture(descriptor: textureDescriptor)
+        }
     }
     
     /// Render next frame and call the frame completion handler
@@ -106,7 +120,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
         let renderPassDescriptor = MTLRenderPassDescriptor()
         renderPassDescriptor.colorAttachments[0].loadAction = .clear
         renderPassDescriptor.colorAttachments[0].clearColor = clearColor
-        renderPassDescriptor.colorAttachments[0].texture = CVMetalTextureGetTexture(metalTexture)
+        if (msaaSampleCount > 1) {
+            renderPassDescriptor.colorAttachments[0].texture = metalMultisampledTexture
+            renderPassDescriptor.colorAttachments[0].resolveTexture = CVMetalTextureGetTexture(metalTexture)
+            renderPassDescriptor.colorAttachments[0].storeAction = .multisampleResolve
+        }
+        else {
+            renderPassDescriptor.colorAttachments[0].texture = CVMetalTextureGetTexture(metalTexture)
+        }
         renderer.render(atTime: currentPresentationTime.seconds,
                         viewport: ExportSettings.viewport,
                         commandBuffer: commandBuffer,

person mnuages    schedule 27.11.2020
comment
Это было! Спасибо. Это был resolveTexture, который сбил меня с толку - person Morten J; 28.11.2020
comment
Для тех, кому это понадобится в будущем, установите для параметра sampleCount значение 4 и рассмотрите возможность тестирования, поддерживает ли устройство 8 образцов с supportsTextureSampleCount(_:) - person Morten J; 28.11.2020