Этот MVP Metal main.swift обнаруживается пустым - PullRequest
0 голосов
/ 17 марта 2019

Таким образом, в файле 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 / themes / metalkit , но самое близкое, что я нашел до сих пор, было не там, а в сущности .

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()

Это пусто для меня. Я пытался следовать этому .

Это становится ближе.

1 Ответ

1 голос
/ 17 марта 2019

Основная проблема здесь в том, что метод NSApplication run не возвращается до тех пор, пока приложение не завершится, поэтому кодирование команды рендеринга никогда не происходит.Вместо этого вы можете создать подкласс 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()
...