I have some code that works fine on macOS 10.14, here is what I get:
And here is what it looks like on macOS 10.15 (notice how the background is not blue anymore and the text, barely visible, is twice as wide and therefore gets cropped):
Does anyone have an idea what could be the problem?
Here is the code:
AppDelegate.swift
import Cocoa
import MetalKit
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, MTKViewDelegate {
struct Vertex {
var position: vector_float2
var textureCoordinates: vector_float2
init(position: vector_float2, textureCoordinates: vector_float2) {
self.position = position
self.textureCoordinates = textureCoordinates
}
}
@IBOutlet var window: NSWindow!
var commandQueue: MTLCommandQueue!
var texturePipelineState: MTLRenderPipelineState!
var texture: MTLTexture!
var textFrame = NSRect.zero
func applicationWillFinishLaunching(_ aNotification: Notification) {
let mtkView = window.contentView as! MTKView
mtkView.delegate = self
mtkView.clearColor = .init(red: 1, green: 1, blue: 1, alpha: 1)
let device = MTLCreateSystemDefaultDevice()!
mtkView.device = device
mtkView.isPaused = true
mtkView.enableSetNeedsDisplay = true
let defaultLibrary = device.makeDefaultLibrary()!
let texturePipelineDescriptor = MTLRenderPipelineDescriptor()
texturePipelineDescriptor.vertexFunction = defaultLibrary.makeFunction(name: "textureVertexShader")
texturePipelineDescriptor.fragmentFunction = defaultLibrary.makeFunction(name: "textureFragmentShader")
texturePipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat
texturePipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
texturePipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
texturePipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
texturePipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
texturePipelineState = try! device.makeRenderPipelineState(descriptor: texturePipelineDescriptor)
commandQueue = device.makeCommandQueue()
let textureDescriptor = MTLTextureDescriptor()
textureDescriptor.pixelFormat = mtkView.colorPixelFormat
let attributedString = NSAttributedString(string: "asdf", attributes: [.font: NSFontManager.shared.convert(.labelFont(ofSize: 15), toHaveTrait: .boldFontMask), .foregroundColor: NSColor.white])
let frameSize = attributedString.size()
let image = NSImage(size: frameSize)
image.lockFocus()
textFrame = NSRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
if true {
NSColor.red.set()
NSBezierPath(rect: textFrame).fill()
}
attributedString.draw(in: textFrame)
let bitmap = NSBitmapImageRep(focusedViewRect: textFrame)!
image.unlockFocus()
let textureSize = NSSize(width: CGFloat(bitmap.pixelsWide), height: CGFloat(bitmap.pixelsHigh))
textureDescriptor.width = Int(textureSize.width)
textureDescriptor.height = Int(textureSize.height)
texture = device.makeTexture(descriptor: textureDescriptor)!
let region = MTLRegionMake2D(0, 0, Int(textureSize.width), Int(textureSize.height))
texture.replace(region: region, mipmapLevel: 0, withBytes: bitmap.bitmapData!, bytesPerRow: bitmap.bytesPerRow)
}
func draw(in view: MTKView) {
guard let currentDrawable = view.currentDrawable, let renderPassDescriptor = view.currentRenderPassDescriptor else {
return
}
let commandBuffer = commandQueue.makeCommandBuffer()!
let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
let viewSize = view.frame.size
var viewportSize = vector_uint2(x: UInt32(viewSize.width), y: UInt32(viewSize.height))
renderCommandEncoder.setVertexBytes(&viewportSize, length: MemoryLayout<vector_uint2>.size, index: Int(VertexInputIndexViewportSize.rawValue))
renderCommandEncoder.setRenderPipelineState(texturePipelineState)
let vertices = [Vertex(position: vector_float2(Float(textFrame.minX), Float(textFrame.minY)), textureCoordinates: vector_float2(0, 1)),
Vertex(position: vector_float2(Float(textFrame.maxX), Float(textFrame.minY)), textureCoordinates: vector_float2(1, 1)),
Vertex(position: vector_float2(Float(textFrame.minX), Float(textFrame.maxY)), textureCoordinates: vector_float2(0, 0)),
Vertex(position: vector_float2(Float(textFrame.maxX), Float(textFrame.maxY)), textureCoordinates: vector_float2(1, 0))]
renderCommandEncoder.setVertexBytes(vertices, length: MemoryLayout.size * vertices.count, index: Int(VertexInputIndexVertices.rawValue))
renderCommandEncoder.setFragmentTexture(texture, index: Int(FragmentInputIndexTexture.rawValue))
renderCommandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
renderCommandEncoder.endEncoding()
commandBuffer.present(currentDrawable)
commandBuffer.commit()
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
}
ShaderTypes.h
#import
typedefenum VertexInputIndex: int {
VertexInputIndexVertices = 0,
VertexInputIndexViewportSize = 1,
} VertexInputIndex;
typedef enum FragmentInputIndex: int {
FragmentInputIndexTexture = 0,
} FragmentInputIndex;
typedef struct {
vector_float2 position;
vector_float2 textureCoordinates;
} Vertex;
Shaders.metal
#include <metal_stdlib>
#include "ShaderTypes.h"
using namespace metal;
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinates;
} TextureRasterizerData;
vertex TextureRasterizerData textureVertexShader(uint vertexID [[vertex_id]],
constant Vertex *texturedVertices [[buffer(VertexInputIndexVertices)]],
constant vector_uint2 *viewportSizePointer [[buffer(VertexInputIndexViewportSize)]]) {
TextureRasterizerData out;
out.clipSpacePosition = float4(0.0, 0.0, 0.0, 1.0);
float2 pixelSpacePosition = texturedVertices[vertexID].position;
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);
out.textureCoordinates = texturedVertices[vertexID].textureCoordinates;
return out;
}
constexpr sampler textureSampler (mag_filter::linear, min_filter::linear);
fragment float4 textureFragmentShader(TextureRasterizerData in [[stage_in]],
texture2d colorTexture [[texture(FragmentInputIndexTexture)]]) {
half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinates);
return float4(colorSample);
}