Reputation: 79
I want to use MTKView on Mac app. After init the MTKView, it appears red instead of the expected clear color on My laptop is MacBook Pro of macOS Catalina 10.15.2, and it render black background on a iMac of macOS Mojave 10.14.6
I try to exchange the metalView's pixelFormat (MTLPixelFormat.bgra8Unorm) to MTLPixelFormat.rgba16Float or other. the background color is incorrect too.
The MTLTexture
create function is MTLTextureDescriptor.texture2DDescriptor(pixelFormat:metalView.colorPixelFormat, width: Int(metalView.frame.size.width), height: Int(metalView.frame.size.height), mipmapped: false)
It could be a problem with the .metal
file, but I don't understand it
I have post the project to Github. here
import Cocoa
import MetalKit
import simd
struct Vertex {
var position: vector_float4
var textCoord: vector_float2
init(position: CGPoint, textCoord: CGPoint) {
self.position = [Float(position.x), Float(position.y), 0 ,1]
self.textCoord = [Float(textCoord.x), Float(textCoord.y)]
}
}
class Matrix {
private(set) var m: [Float]
static var identity = Matrix()
private init() {
m = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
}
@discardableResult
func translation(x: Float, y: Float, z: Float) -> Matrix {
m[12] = x
m[13] = y
m[14] = z
return self
}
@discardableResult
func scaling(x: Float, y: Float, z: Float) -> Matrix {
m[0] = x
m[5] = y
m[10] = z
return self
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, MTKViewDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet weak var metalView: MTKView!
private var commandQueue: MTLCommandQueue?
private var pipelineState: MTLRenderPipelineState!
private var render_target_vertex: MTLBuffer!
private var render_target_uniform: MTLBuffer!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
metalView.device = MTLCreateSystemDefaultDevice()
metalView.delegate = self
commandQueue = metalView.device?.makeCommandQueue()
setupTargetUniforms()
do {
try setupPiplineState()
} catch {
fatalError("Metal initialize failed: \(error.localizedDescription)")
}
}
private func setupTargetUniforms() {
let size = metalView.frame.size
let w = size.width, h = size.height
let vertices = [
Vertex(position: CGPoint(x: 0 , y: 0), textCoord: CGPoint(x: 0, y: 0)),
Vertex(position: CGPoint(x: w , y: 0), textCoord: CGPoint(x: 1, y: 0)),
Vertex(position: CGPoint(x: 0 , y: h), textCoord: CGPoint(x: 0, y: 1)),
Vertex(position: CGPoint(x: w , y: h), textCoord: CGPoint(x: 1, y: 1)),
]
render_target_vertex = metalView.device?.makeBuffer(bytes: vertices, length: MemoryLayout<Vertex>.stride * vertices.count, options: .cpuCacheModeWriteCombined)
let metrix = Matrix.identity
metrix.scaling(x: 2 / Float(size.width), y: -2 / Float(size.height), z: 1)
metrix.translation(x: -1, y: 1, z: 0)
render_target_uniform = metalView.device?.makeBuffer(bytes: metrix.m, length: MemoryLayout<Float>.size * 16, options: [])
}
private func setupPiplineState() throws {
let library = try metalView.device?.makeDefaultLibrary(bundle: Bundle.main)
let vertex_func = library?.makeFunction(name: "vertex_render_target")
let fragment_func = library?.makeFunction(name: "fragment_render_target")
let rpd = MTLRenderPipelineDescriptor()
rpd.vertexFunction = vertex_func
rpd.fragmentFunction = fragment_func
rpd.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
// rpd.colorAttachments[0].isBlendingEnabled = true
// rpd.colorAttachments[0].alphaBlendOperation = .add
// rpd.colorAttachments[0].rgbBlendOperation = .add
// rpd.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
// rpd.colorAttachments[0].sourceAlphaBlendFactor = .one
// rpd.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
// rpd.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
pipelineState = try metalView.device?.makeRenderPipelineState(descriptor: rpd)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
let renderPassDescriptor = MTLRenderPassDescriptor()
let attachment = renderPassDescriptor.colorAttachments[0]
attachment?.clearColor = metalView.clearColor
attachment?.texture = metalView.currentDrawable?.texture
attachment?.loadAction = .clear
attachment?.storeAction = .store
let commandBuffer = commandQueue?.makeCommandBuffer()
let commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
commandEncoder?.setRenderPipelineState(pipelineState)
commandEncoder?.setVertexBuffer(render_target_vertex, offset: 0, index: 0)
commandEncoder?.setVertexBuffer(render_target_uniform, offset: 0, index: 1)
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: metalView.colorPixelFormat,
width: Int(metalView.frame.size.width),
height: Int(metalView.frame.size.height),
mipmapped: false)
textureDescriptor.usage = [.renderTarget, .shaderRead]
let texture = metalView.device?.makeTexture(descriptor: textureDescriptor)
commandEncoder?.setFragmentTexture(texture, index: 0)
commandEncoder?.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
commandEncoder?.endEncoding()
if let drawable = metalView.currentDrawable {
commandBuffer?.present(drawable)
}
commandBuffer?.commit()
}
}
Could someone help me find the problem?
Upvotes: 3
Views: 1425
Reputation: 31782
You're sampling from an uninitialized texture.
In your MetalView
's draw
method, you bind the texture from the screenTarget
variable, which has never been rendered to.
When a Metal texture is created, its contents are undefined. They might be red, black, random noise, or anything else.
Upvotes: 6