Example was taken from https://medium.com/@s1ddok/combine-the-power-of-coregraphics-and-metal-by-sharing-resource-memory-eabb4c1be615. Just changed color space from r8unorm to rgba8unorm.
// CGContext created with aligned data, which shared with MTLTexture
let cgContext = textureContext.cgContext
let clearRect = CGRect(
origin: .zero,
size: CGSize(width: cgContext.width, height: cgContext.height)
)
cgContext.setAllowsAntialiasing(true)
cgContext.setShouldAntialias(true)
cgContext.clear(clearRect)
cgContext.addPath(path)
cgContext.setFillColor(fillColor ?? UIColor.clear.cgColor)
cgContext.fillPath(using: fillRule.cgFillRuleValue())
Rendered path on shared data chunk then used as texture for quad primitive that rendered on MTKView's texture. But there is problem - antialiasing will mix path fill color with clear(black) color on edges like this(hope if difference between this pics is noticeable) -
This is normal overlap, when I prefill context area with red color
If antialiasing is switched off for context - edges will clearly white, but sharpened
Is there are way to overlap two textures with clear and smooth edges without rendering an appropriate chunk of overlapped texture to overlapping texture before drawing path?
UPD: How context is created
func createCGMTLContext(withSize size: CGSize) -> CGMTLContext? {
let width = Int(size.width)
let height = Int(size.height)
let pixelRowAlignment = device.minimumTextureBufferAlignment(for: .rgba8Unorm)
let bytesPerRow = alignUp(size: width, align: pixelRowAlignment) * 4
let pagesize = Int(getpagesize())
let allocationSize = alignUp(size: bytesPerRow * height, align: pagesize)
var data: UnsafeMutableRawPointer? = nil
let result = posix_memalign(&data, pagesize, allocationSize)
if result != noErr {
fatalError("Error during memory allocation")
}
let context = CGContext(data: data,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: bytesPerRow,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0, y: -CGFloat(context.height))
let buffer = device.makeBuffer(
bytesNoCopy: context.data!,
length: allocationSize,
options: .storageModeShared,
deallocator: { pointer, length in free(data) }
)!
let textureDescriptor = MTLTextureDescriptor()
textureDescriptor.pixelFormat = .rgba8Unorm
textureDescriptor.width = context.width
textureDescriptor.height = context.height
textureDescriptor.storageMode = buffer.storageMode
textureDescriptor.usage = .shaderRead
let texture = buffer.makeTexture(descriptor: textureDescriptor,
offset: 0,
bytesPerRow: context.bytesPerRow)
guard let requiredTexture = texture else {
return nil
}
let cgmtlContext = CGMTLContext(
cgContext: context,
buffer: buffer,
texture: requiredTexture
)
return cgmtlContext
}
There is how metal blending setted up
pipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
pipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
After reading https://metalbyexample.com/translucency-and-transparency/ I noticed a comment from Warren Moore:
Since you’re loading your textures with CoreGraphics, you’re getting premultiplied alpha (i.e., the color channels are already multiplied by their respective alpha values). The consequence of using premultiplied alpha with the SourceAlpha, OneMinusSourceAlpha blend factors is that the source alpha value is redundantly multiplied, causing areas with a < 1.0 to become unnaturally dark. If instead you use the One, OneMinusSourceAlpha blend factors, you get a much more pleasing result.Incidentally, this is the main reason I used an opaque texture and translucent vertex colors when demonstrating blending in the article.
Changing sourceAlpha to One helped. Unfortunately, alpha premultiplying in CGContext can't be turned off