【问题标题】:This MVP Metal main.swift shows up blank这个 MVP Metal main.swift 显示为空白
【发布时间】:2019-03-17 07:17:47
【问题描述】:

所以在您项目的main.swift 文件中,您可以像这样创建一个窗口(然后从那里开始):

let nsapp = NSApplication.shared
let window = NSWindow(
  contentRect: NSMakeRect(0, 0, 200, 200),
  styleMask: .fullSizeContentView,
  backing: NSWindow.BackingStoreType.buffered,
  defer: false
)
window.cascadeTopLeft(from:NSMakePoint(20,20))
nsapp.run()

我想知道如何用金属三角形做同样的事情。我一直在查看github.com/topics/metalkit,但到目前为止我发现的最接近的东西不在gist 中。

import Cocoa
import MetalKit

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, MTKViewDelegate {
    weak var window: NSWindow!
    weak var metalView: MTKView!
    let device = MTLCreateSystemDefaultDevice()!
    var commandQueue: MTLCommandQueue!
    var pipelineState: MTLRenderPipelineState!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        metalView = MTKView(frame: NSRect(origin: CGPoint.zero, size: window.frame.size), device: device)
        metalView.delegate = self
        window.contentView = metalView
        commandQueue = device.makeCommandQueue()
        let shaders = """
        #include <metal_stdlib>
        using namespace metal;
        struct VertexIn {
            packed_float3 position;
            packed_float3 color;
        };
        struct VertexOut {
            float4 position [[position]];
            float4 color;
        };
        vertex VertexOut vertex_main(device const VertexIn *vertices [[buffer(0)]],
                                     uint vertexId [[vertex_id]]) {
            VertexOut out;
            out.position = float4(vertices[vertexId].position, 1);
            out.color = float4(vertices[vertexId].color, 1);
            return out;
        }
        fragment float4 fragment_main(VertexOut in [[stage_in]]) {
            return in.color;
        }
        """
        do {
            let library = try device.makeLibrary(source: shaders, options: nil)
            let pipelineDescriptor = MTLRenderPipelineDescriptor()
            pipelineDescriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
            pipelineDescriptor.vertexFunction = library.makeFunction(name: "vertex_main")
            pipelineDescriptor.fragmentFunction = library.makeFunction(name: "fragment_main")
            pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
        } catch {}
    }

    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    }

    func draw(in view: MTKView) {
        guard let commandBuffer = commandQueue.makeCommandBuffer() else { return }
        guard let passDescriptor = view.currentRenderPassDescriptor else { return }
        guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return }
        let vertexData: [Float] = [ -0.5, -0.5, 0, 1, 0, 0,
                                     0.5, -0.5, 0, 0, 1, 0,
                                       0,  0.5, 0, 0, 0, 1 ]
        encoder.setVertexBytes(vertexData, length: vertexData.count * MemoryLayout<Float>.stride, index: 0)
        encoder.setRenderPipelineState(pipelineState)
        encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
        encoder.endEncoding()
        commandBuffer.present(view.currentDrawable!)
        commandBuffer.commit()
    }
}

它至少从头开始构建MTKView。但我还不确定让金属物体工作的最低可行产品是什么这可能需要几天的时间,如果有人已经弄清楚了,这可能对其他人有帮助。

我已将两者结合起来,但据我所知,它没有呈现任何内容。

import AVFoundation
import AudioToolbox
import Foundation
import QuartzCore
import Security
import WebKit
import Cocoa
import Metal
import MetalKit
import Swift

let device = MTLCreateSystemDefaultDevice()!
// Our clear color, can be set to any color
let clearColor = MTLClearColor(red: 0.1, green: 0.57, blue: 0.25, alpha: 1)
let nsapp = NSApplication.shared
let appName = ProcessInfo.processInfo.processName
let window = NSWindow(
  contentRect: NSMakeRect(0, 0, 1000, 1000),
  styleMask: .fullSizeContentView,
  backing: NSWindow.BackingStoreType.buffered,
  defer: false
)
window.cascadeTopLeft(from:NSMakePoint(20,20))
window.title = appName;
window.makeKeyAndOrderFront(nil)

struct Vertex {
    var position: float3
    var color: float4
}

let view = MTKView(frame: NSRect(origin: CGPoint.zero, size: window.frame.size), device: device)
window.contentView = view
view.device = device
view.colorPixelFormat = .bgra8Unorm
view.clearColor = clearColor

let queue = device.makeCommandQueue()!
var vertexBuffer: MTLBuffer!
var vertices: [Vertex] = [
  Vertex(position: float3(0,1,0), color: float4(1,0,0,1)),
  Vertex(position: float3(-1,-1,0), color: float4(0,1,0,1)),
  Vertex(position: float3(1,-1,0), color: float4(0,0,1,1))
]

let shaders = """
#include <metal_stdlib>
using namespace metal;

// Basic Struct to match our Swift type
// This is what is passed into the Vertex Shader
struct VertexIn {
    float3 position;
    float4 color;
};
// What is returned by the Vertex Shader
// This is what is passed into the Fragment Shader
struct VertexOut {
    float4 position [[ position ]];
    float4 color;
};
vertex VertexOut basic_vertex_function(const device VertexIn *vertices [[ buffer(0) ]],
uint vertexID [[ vertex_id ]]) {
    VertexOut vOut;
    vOut.position = float4(vertices[vertexID].position,1);
    vOut.color = vertices[vertexID].color;
    return vOut;
}
fragment float4 basic_fragment_function(VertexOut vIn [[ stage_in ]]) {
    return vIn.color;
}
"""
let library = try device.makeLibrary(source: shaders, options: nil)
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
pipelineDescriptor.vertexFunction = library.makeFunction(name: "basic_vertex_function")
pipelineDescriptor.fragmentFunction = library.makeFunction(name: "basic_fragment_function")
let pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)

vertexBuffer = device.makeBuffer(
  bytes: vertices,
  length: MemoryLayout<Vertex>.stride * vertices.count,
  options: []
)

enum MetalErrors: Error {
  case commandBuffer
  case passDescriptor
  case encoder
}

guard let drawable = view.currentDrawable else { throw MetalErrors.commandBuffer }
guard let commandBuffer = queue.makeCommandBuffer() else { throw MetalErrors.commandBuffer }
guard let passDescriptor = view.currentRenderPassDescriptor else { throw MetalErrors.passDescriptor }
guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { throw MetalErrors.encoder }

nsapp.run()

// let vertexData: [Float] = [ -0.5, -0.5, 0, 1, 0, 0,
//                                      0.5, -0.5, 0, 0, 1, 0,
//                                        0,  0.5, 0, 0, 0, 1 ]
encoder.setRenderPipelineState(pipelineState)
// encoder.setVertexBytes(vertexData, length: vertexData.count * MemoryLayout<Float>.stride, index: 0)
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertices.count)
encoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()

对我来说它是空白的。我也试过关注this

This 越来越近了。

【问题讨论】:

    标签: swift macos metal


    【解决方案1】:

    这里的主要问题是NSApplicationrun 方法在应用程序终止之前不会返回,因此您的渲染命令编码永远不会发生。您可以继承 MTKView 并覆盖其 draw 方法来进行绘图:

    import Cocoa
    import MetalKit
    
    let device = MTLCreateSystemDefaultDevice()!
    // Our clear color, can be set to any color
    let clearColor = MTLClearColor(red: 0.1, green: 0.57, blue: 0.25, alpha: 1)
    
    let shaders = """
    #include <metal_stdlib>
    using namespace metal;
    
    // Basic Struct to match our Swift type
    // This is what is passed into the Vertex Shader
    struct VertexIn {
    float3 position;
    float4 color;
    };
    // What is returned by the Vertex Shader
    // This is what is passed into the Fragment Shader
    struct VertexOut {
    float4 position [[ position ]];
    float4 color;
    };
    vertex VertexOut basic_vertex_function(const device VertexIn *vertices [[ buffer(0) ]],
    uint vertexID [[ vertex_id ]]) {
    VertexOut vOut;
    vOut.position = float4(vertices[vertexID].position,1);
    vOut.color = vertices[vertexID].color;
    return vOut;
    }
    fragment float4 basic_fragment_function(VertexOut vIn [[ stage_in ]]) {
    return vIn.color;
    }
    """
    let library = try device.makeLibrary(source: shaders, options: nil)
    let pipelineDescriptor = MTLRenderPipelineDescriptor()
    pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
    pipelineDescriptor.vertexFunction = library.makeFunction(name: "basic_vertex_function")
    pipelineDescriptor.fragmentFunction = library.makeFunction(name: "basic_fragment_function")
    let pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
    
    struct Vertex {
        var position: float3
        var color: float4
    }
    
    let queue = device.makeCommandQueue()!
    var vertexBuffer: MTLBuffer!
    var vertices: [Vertex] = [
        Vertex(position: float3(0,1,0), color: float4(1,0,0,1)),
        Vertex(position: float3(-1,-1,0), color: float4(0,1,0,1)),
        Vertex(position: float3(1,-1,0), color: float4(0,0,1,1))
    ]
    
    vertexBuffer = device.makeBuffer(
        bytes: vertices,
        length: MemoryLayout<Vertex>.stride * vertices.count,
        options: []
    )
    
    enum MetalErrors: Error {
        case commandBuffer
        case passDescriptor
        case encoder
    }
    
    class MyMTKView : MTKView {
        override func draw() {
            guard let drawable = currentDrawable else { return }
            guard let passDescriptor = currentRenderPassDescriptor else { return }
            guard let commandBuffer = queue.makeCommandBuffer() else { return }
            guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return }
            encoder.setRenderPipelineState(pipelineState)
             encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0 )
            encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: vertices.count)
            encoder.endEncoding()
            commandBuffer.present(drawable)
            commandBuffer.commit()
        }
    }
    
    let nsapp = NSApplication.shared
    let appName = ProcessInfo.processInfo.processName
    let window = NSWindow(
        contentRect: NSMakeRect(0, 0, 1000, 1000),
        styleMask: [.titled, .closable, .resizable],
        backing: NSWindow.BackingStoreType.buffered,
        defer: false
    )
    window.cascadeTopLeft(from:NSMakePoint(20,20))
    window.title = appName;
    
    let view = MyMTKView(frame: NSRect(origin: CGPoint.zero, size: window.frame.size), device: device)
    window.contentView = view
    view.device = device
    view.colorPixelFormat = .bgra8Unorm
    view.clearColor = clearColor
    
    window.makeKeyAndOrderFront(nil)
    
    nsapp.run()
    

    【讨论】:

    • 太棒了,会试试这个。想知道是否有任何方法可以在不创建任何类/子类的情况下做到这一点。感谢您解释为什么它不起作用,这也有助于解决问题。
    • 你也许可以使用计时器或运行循环源来使绘制代码由 Cocoa 事件循环触发,但问题是,你为什么要如此努力地反对平台约定?
    猜你喜欢
    • 2019-03-26
    • 2018-01-23
    • 2015-04-14
    • 1970-01-01
    • 1970-01-01
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多