Metal feedback loop artifacts - ios

Hi am trying to create some feedback loops in metal for an app I am working on. However I am getting some strange artifacts. I suspect this is due to something in my pipeline, or with how I am sampling the previous frame. In the code there are extra pipelines set up for a second buffer, as eventually I will need that to make a turing pattern. But right now I have reduced the code as much as possible to isolate the issue.
Right now it is just drawing a small circle in the top left corner. Once that is drawn the buffer A fragment samples itself with a one pixel displacement. This causes the circle to stretch out down the screen with every loop. The issue is that I am getting coloured lines appearing ahead of the stretching circle. This same artifact appears when I try and do recursive blurs or other types of feedback loops. The Main buffer is only there to mix the two buffers at the end of the process. I think it can be ignored for troubshooting this. Anyone run into this before?
Video here: https://photos.app.goo.gl/ThRJHddo2xKke6CN7
Screen shot here: Picture of artifact
Calls to metal controller in my viewController
'''
override func viewDidLoad() {
super.viewDidLoad()
metal.initMetalLayer(mView: metalView) // make metal layer
metal.initMetalPipeline()//make metal pipeline
metal.commandQueue = metal.device.makeCommandQueue()//make metal command Queue
timer = CADisplayLink(target: self, selector: #selector(gameloop)) //make the timer to trigger the render call
timer.add(to: RunLoop.main, forMode: .default) //ties the screen refresh to the render call
}
#objc func gameloop() {
autoreleasepool {
metal.currentTime = Float(CACurrentMediaTime() - metal.startTime)
//print(metal.currentTime)
// metal.bufferBRender()
metal.bufferARender()
metal.mainRender() //TURNS METAL RENDER OFF OR ON
}
}
'''
Metal declarations
'''
//
// MTL.swift
// Habituate
//
// Created by Brendan Tipney on 2021-01-14.
//
import Foundation
import Metal
import UIKit
import MetalKit
struct MTL {
var device: MTLDevice!
var metalLayer: CAMetalLayer!
let vertexData: [Float] = [
-1.0, 1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0,-1.0,0.0
]
var vertexBuffer: MTLBuffer!
var textureA: MTLTexture!
var textureB: MTLTexture!
//MARK: - AUX data
var layoutData = simd_float4(390,727,0.75,1.0)
var habitData = [vector_float4(0.25,1.0,0.75,1.0),vector_float4(0.1,1.0,0.3,0.4)]
var touchData = vector_float4(0,0,0,0)
var startTime = CACurrentMediaTime()
var currentTime = Float(CACurrentMediaTime())
//MARK: - RCTDIFData
var RCTDIFData = vector_float4(0.055,0.064,2.0,1.0)
var pipelineState: MTLRenderPipelineState! //DO I need more then one pipeline state?
var pipelineStateB: MTLRenderPipelineState!
var pipelineStateA: MTLRenderPipelineState!
var commandQueue: MTLCommandQueue!
var timer: CADisplayLink!
mutating func initMetalLayer(mView : MTKView) {
device = MTLCreateSystemDefaultDevice()
metalLayer = CAMetalLayer()
metalLayer.device = device
metalLayer.pixelFormat = .bgra8Unorm
metalLayer.framebufferOnly = false// Set to false as I intend to read from the frame buffer for feedback loops. Set to true if not needed
metalLayer.frame = mView.layer.frame
mView.layer.addSublayer(metalLayer)
let viewSize = simd_float2(Float(mView.layer.frame.size.width), Float(mView.layer.frame.size.height))
layoutData = simd_float4(viewSize.x, viewSize.y, 0.75, 1.0) //Trying to load the size data.
print("view = \(viewSize)")
let dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0])
vertexBuffer = device.makeBuffer(bytes: vertexData, length: dataSize, options: []) //makes the buffer with the vertex data.
}
mutating func initMetalPipeline(){
let defaultLibrary = device.makeDefaultLibrary()!
let vertexProgram = defaultLibrary.makeFunction(name: "vertex_main")
let fragmentProgram = defaultLibrary.makeFunction(name: "fragment_main")
let fragmentProgramBuffA = defaultLibrary.makeFunction(name: "fragment_BuffA")
let fragmentProgramBuffB = defaultLibrary.makeFunction(name: "fragment_BuffB")
//MARK: - VertexDescriptor
let vertexDescriptor = MTLVertexDescriptor()//I think this is where the buffers are stored
vertexDescriptor.attributes[0].format = .float3
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.layouts[0].stride = MemoryLayout<float3>.stride
//MARK: - Texture Descriptor
let textureDescriptor = MTLTextureDescriptor() //None of this textureDescriptors may be needed
textureDescriptor.textureType = MTLTextureType.type2D
textureDescriptor.width = Int(layoutData.x)
textureDescriptor.height = Int(layoutData.y)
textureDescriptor.pixelFormat = MTLPixelFormat.bgra8Unorm
textureDescriptor.usage = [.renderTarget, .shaderRead]
textureA = device.makeTexture(descriptor: textureDescriptor)
textureB = device.makeTexture(descriptor: textureDescriptor)
//MARK: - Pipeline Descriptor
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
pipelineStateDescriptor.vertexDescriptor = vertexDescriptor
pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
//MARK: - BufferA Pipeline Descriptor
pipelineStateDescriptor.fragmentFunction = fragmentProgramBuffA
pipelineStateA = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
//MARK: - BufferB Pipeline Descriptor
pipelineStateDescriptor.fragmentFunction = fragmentProgramBuffB
pipelineStateB = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
}
//MARK: - Main Render
mutating func mainRender() {
let commandBuffer = commandQueue.makeCommandBuffer()!
guard let drawable = metalLayer?.nextDrawable() else { return }
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear //PERHAPS SETTING THIS TO LOAD CAN MAKE A FB LOOP
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 0.0)
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
renderEncoder.label = "Main"
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)//this is where the vertex buffer is loaded
renderEncoder.setFragmentTexture(textureA, index: 0)
renderEncoder.setFragmentTexture(textureB, index: 1)
renderEncoder.setFragmentBytes(&layoutData, length: MemoryLayout.size(ofValue: layoutData), index: 1) //this sends the dimentions to the shader
renderEncoder.setFragmentBytes(&habitData, length: 4 * MemoryLayout.size(ofValue: habitData), index: 2)
renderEncoder.setFragmentBytes(&touchData, length: MemoryLayout.size(ofValue: touchData), index: 3)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
renderEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
//MARK: - Buffer B Render
mutating func bufferARender(){
let commandBuffer = commandQueue.makeCommandBuffer()!
let renderPassDescriptorA = MTLRenderPassDescriptor()
renderPassDescriptorA.colorAttachments[0].texture = textureA //set back to this after testing
renderPassDescriptorA.colorAttachments[0].loadAction = .load
renderPassDescriptorA.colorAttachments[0].storeAction = .store
renderPassDescriptorA.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
let renderEncoderA = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorA)!
renderEncoderA.label = "BufferA"
renderEncoderA.setRenderPipelineState(pipelineStateA)
renderEncoderA.setVertexBuffer(vertexBuffer, offset: 0, index: 0)//this is where the vertex buffer is loaded
renderEncoderA.setFragmentTexture(textureA, index: 0)
renderEncoderA.setFragmentTexture(textureB, index: 1)
renderEncoderA.setFragmentBytes(&layoutData, length: MemoryLayout.size(ofValue: layoutData), index: 1) //this sends the dimentions to the shader
renderEncoderA.setFragmentBytes(&habitData, length: 4 * MemoryLayout.size(ofValue: habitData), index: 2)
renderEncoderA.setFragmentBytes(&touchData, length: MemoryLayout.size(ofValue: touchData), index: 3)
renderEncoderA.setFragmentBytes(&RCTDIFData, length: MemoryLayout.size(ofValue: RCTDIFData), index: 4)
renderEncoderA.setFragmentBytes(&currentTime, length: MemoryLayout.size(ofValue: currentTime), index: 5)
renderEncoderA.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
renderEncoderA.endEncoding()
//commandBuffer.present(drawable)
commandBuffer.commit()
}
mutating func bufferBRender(){
let commandBuffer = commandQueue.makeCommandBuffer()!
let renderPassDescriptorB = MTLRenderPassDescriptor()
//renderPassDescriptorB.colorAttachments[0].texture = drawable.texture
renderPassDescriptorB.colorAttachments[0].texture = textureB //set back to this after testing
renderPassDescriptorB.colorAttachments[0].loadAction = .load
renderPassDescriptorB.colorAttachments[0].storeAction = .store
renderPassDescriptorB.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
let renderEncoderB = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorB)!
renderEncoderB.label = "BufferB"
renderEncoderB.setRenderPipelineState(pipelineStateB)
renderEncoderB.setVertexBuffer(vertexBuffer, offset: 0, index: 0)//this is where the vertex buffer is loaded
renderEncoderB.setFragmentTexture(textureA, index: 0)
renderEncoderB.setFragmentTexture(textureB, index: 1)
renderEncoderB.setFragmentBytes(&layoutData, length: MemoryLayout.size(ofValue: layoutData), index: 1) //this sends the dimentions to the shader
renderEncoderB.setFragmentBytes(&habitData, length: 4 * MemoryLayout.size(ofValue: habitData), index: 2)
renderEncoderB.setFragmentBytes(&touchData, length: MemoryLayout.size(ofValue: touchData), index: 3)
renderEncoderB.setFragmentBytes(&RCTDIFData, length: MemoryLayout.size(ofValue: RCTDIFData), index: 4)
renderEncoderB.setFragmentBytes(&currentTime, length: MemoryLayout.size(ofValue: currentTime), index: 5)
renderEncoderB.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4, instanceCount: 1)
renderEncoderB.endEncoding()
//commandBuffer.present(drawable)
commandBuffer.commit()
}
mutating func getDeviceDetails(){
let window = UIApplication.shared.windows[0]
let safeViewTop = window.safeAreaInsets.top
}
mutating func getHabitDetails(_ habits: HabitData){
}
}
'''
Vertex and Fragment shaders
'''
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[attribute(0)]];
};
struct VertexOut {
float4 position [[position]];
};
vertex VertexOut vertex_main(VertexIn in [[stage_in]],
const device packed_float3* vertex_array [[buffer(0)]],
unsigned int vid [[ vertex_id ]]) {
VertexOut out;
out.position = float4(vertex_array[vid].xy,1.0);
//out.data = float4(in.data, 1);
return out;
}
fragment float4 fragment_main( VertexOut in [[stage_in]],
constant float4 &layoutData [[ buffer(1) ]],
constant array<float4, 2> &habitData [[ buffer(2) ]],
constant float4 &touchData [[ buffer(3) ]],
texture2d<float, access::sample> BuffA [[ texture(0) ]],
texture2d<float, access::sample> BuffB [[ texture(1) ]]) {
float4 fragIn = in.position;
float2 uv = float2((fragIn.x/layoutData.x), (fragIn.y/layoutData.y));
float4 fragColor = float4(0.0,0.0,0.0,1.0);
constexpr sampler s(coord::normalized, filter::bicubic, address::clamp_to_edge,compare_func:: less);
float4 texA = BuffA.sample(s, uv);
fragColor = texA;
fragColor.w = 1.0;
return fragColor;
}
fragment float4 fragment_BuffA( VertexOut in [[stage_in]],
constant float4 &layoutData [[ buffer(1) ]],
constant array<float4, 2> &habitData [[ buffer(2) ]],
constant float4 &touchData [[ buffer(3) ]],
constant float4 &RCTDIFData [[ buffer(4) ]],
constant float &currentTime [[ buffer(5)]],
texture2d<float, access::sample> BuffA [[ texture(0) ]],
texture2d<float, access::sample> BuffB [[ texture(1) ]]) {
float4 fragIn = in.position;
float2 uv = float2((fragIn.x/layoutData.x), (fragIn.y/layoutData.y));//Need to load the screen size here or it will only look right on my phone.
float2 pxlRatio = float2(1)/layoutData.xy;
float4 fragColor = float4(0.0,0.0,0.0,1.0);
constexpr sampler s(coord::normalized, filter::nearest, address::clamp_to_zero);
float cornerCircle = step(distance(float2(0,0), uv),0.1);
float4 texA = BuffA.sample(s, uv+float2(-1,-1)*pxlRatio);
fragColor = float4(cornerCircle);
fragColor += texA;
fragColor.w = 1.;
return fragColor;
}
'''

Seems like I was able to resolve the issue by adding .shadeWrite to my texture descriptor usage array.
'''textureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite]'''

Related

Rotating Metal texture 180 degrees

I added a sample code if someone want's to try fixing it: https://www.dropbox.com/s/6t6neo40qjganra/To%20be%20fixed.zip?dl=1
Im making an AR app using Vuforia SDK. Their sample code contains video background rendering using metal. It works fine for vertical orientation, but I need to change it to portrait. The problem is, that after changing it, the video is rendered upside down. Detected targets are in correct orientation, so I think this should be fixed in Metal rendering class. Could someone help me do that? Below is the code Im using to draw that background. How can I rotate it 180 degrees?
private var mMetalDevice: MTLDevice
private var mVideoBackgroundPipelineState: MTLRenderPipelineState!
private var mUniformColorShaderPipelineState: MTLRenderPipelineState!
private var mTexturedVertexShaderPipelineState: MTLRenderPipelineState!
private var mDefaultSamplerState: MTLSamplerState?
private var mVideoBackgroundVertices: MTLBuffer!
private var mVideoBackgroundIndices: MTLBuffer!
private var mVideoBackgroundTextureCoordinates: MTLBuffer!
/// Initialize the renderer ready for use
init(metalDevice: MTLDevice, layer: CAMetalLayer, library: MTLLibrary?, textureDepth: MTLTexture) {
mMetalDevice = metalDevice
let stateDescriptor = MTLRenderPipelineDescriptor()
//
// Video background
//
stateDescriptor.vertexFunction = library?.makeFunction(name: "texturedVertex")
stateDescriptor.fragmentFunction = library?.makeFunction(name: "texturedFragment")
stateDescriptor.colorAttachments[0].pixelFormat = layer.pixelFormat
stateDescriptor.depthAttachmentPixelFormat = textureDepth.pixelFormat
// And create the pipeline state with the descriptor
do {
try mVideoBackgroundPipelineState = metalDevice.makeRenderPipelineState(descriptor: stateDescriptor)
} catch {
print("Failed to create video background render pipeline state:",error)
}
//
// Augmentations
//
// Create pipeline for transparent object overlays
stateDescriptor.vertexFunction = library?.makeFunction(name: "uniformColorVertex")
stateDescriptor.fragmentFunction = library?.makeFunction(name: "uniformColorFragment")
stateDescriptor.colorAttachments[0].pixelFormat = layer.pixelFormat
stateDescriptor.colorAttachments[0].isBlendingEnabled = true
stateDescriptor.colorAttachments[0].rgbBlendOperation = .add
stateDescriptor.colorAttachments[0].alphaBlendOperation = .add
stateDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha
stateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
stateDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
stateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
stateDescriptor.depthAttachmentPixelFormat = textureDepth.pixelFormat
do {
try mUniformColorShaderPipelineState = metalDevice.makeRenderPipelineState(descriptor: stateDescriptor)
} catch {
print("Failed to create augmentation render pipeline state:",error)
return
}
stateDescriptor.vertexFunction = library?.makeFunction(name: "texturedVertex")
stateDescriptor.fragmentFunction = library?.makeFunction(name: "texturedFragment")
// Create pipeline for rendering textures
do {
try mTexturedVertexShaderPipelineState = metalDevice.makeRenderPipelineState(descriptor: stateDescriptor)
} catch {
print("Failed to create guide view render pipeline state:", error)
return
}
mDefaultSamplerState = MetalRenderer.defaultSampler(device: metalDevice)
// Allocate space for rendering data for Video background
mVideoBackgroundVertices = mMetalDevice.makeBuffer(length: MemoryLayout<Float>.size * 3 * 4, options: [.optionCPUCacheModeWriteCombined])
mVideoBackgroundTextureCoordinates = mMetalDevice.makeBuffer(length: MemoryLayout<Float>.size * 2 * 4, options: [.optionCPUCacheModeWriteCombined])
mVideoBackgroundIndices = mMetalDevice.makeBuffer(length: MemoryLayout<UInt16>.size * 6, options: [.optionCPUCacheModeWriteCombined])
}
/// Render the video background
func renderVideoBackground(encoder: MTLRenderCommandEncoder?, projectionMatrix: MTLBuffer, mesh: VuforiaMesh) {
// Copy mesh data into metal buffers
mVideoBackgroundVertices.contents().copyMemory(from: mesh.vertices, byteCount: MemoryLayout<Float>.size * Int(mesh.numVertices) * 3)
mVideoBackgroundTextureCoordinates.contents().copyMemory(from: mesh.textureCoordinates, byteCount: MemoryLayout<Float>.size * Int(mesh.numVertices) * 2)
mVideoBackgroundIndices.contents().copyMemory(from: mesh.indices, byteCount: MemoryLayout<CShort>.size * Int(mesh.numIndices))
// Set the render pipeline state
encoder?.setRenderPipelineState(mVideoBackgroundPipelineState)
// Set the vertex buffer
encoder?.setVertexBuffer(mVideoBackgroundVertices, offset: 0, index: 0)
// Set the projection matrix
encoder?.setVertexBuffer(projectionMatrix, offset: 0, index: 1)
// Set the texture coordinate buffer
encoder?.setVertexBuffer(mVideoBackgroundTextureCoordinates, offset: 0, index: 2)
encoder?.setFragmentSamplerState(mDefaultSamplerState, index: 0)
// Draw the geometry
encoder?.drawIndexedPrimitives(
type: .triangle,
indexCount: 6,
indexType: .uint16,
indexBuffer: mVideoBackgroundIndices,
indexBufferOffset: 0
)
}
}
extension MetalRenderer {
class func defaultSampler(device: MTLDevice) -> MTLSamplerState? {
let sampler = MTLSamplerDescriptor()
sampler.minFilter = .linear
sampler.magFilter = .linear
sampler.mipFilter = .linear
sampler.maxAnisotropy = 1
sampler.sAddressMode = .clampToEdge
sampler.tAddressMode = .clampToEdge
sampler.rAddressMode = .clampToEdge
sampler.normalizedCoordinates = true
sampler.lodMinClamp = 0
sampler.lodMaxClamp = .greatestFiniteMagnitude
return device.makeSamplerState(descriptor: sampler)
}
}
Adding code from the view that creates renderer:
import UIKit
import MetalKit
protocol VuforiaViewDelegate: AnyObject {
func renderFrame(vuforiaView: VuforiaView)
}
class VuforiaView: UIView {
weak var delegate: VuforiaViewDelegate?
var mVuforiaStarted = false
private var mConfigurationChanged = true
private var mRenderer: MetalRenderer!
private var mMetalDevice: MTLDevice!
private var mMetalCommandQueue: MTLCommandQueue!
private var mCommandExecutingSemaphore: DispatchSemaphore!
private var mDepthStencilState: MTLDepthStencilState!
private var mDepthTexture: MTLTexture!
private var mVideoBackgroundProjectionBuffer: MTLBuffer!
private lazy var metalLayer = layer as! CAMetalLayer
override class var layerClass: AnyClass { CAMetalLayer.self }
// Transformations and variables - constantly updated by vuforia frame updates
private var viewport = MTLViewport()
private var trackableProjection = matrix_float4x4()
private var trackableModelView = matrix_float4x4()
private var trackableScaledModelView = matrix_float4x4()
private(set) var worldOriginProjectionMatrix = matrix_float4x4()
private(set) var worldOriginModelViewMatrix = matrix_float4x4()
private(set) var targetPose = matrix_float4x4()
private(set) var targetSize = simd_float3()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
contentScaleFactor = UIScreen.main.nativeScale
// Get the system default metal device
mMetalDevice = MTLCreateSystemDefaultDevice()
// Metal command queue
mMetalCommandQueue = mMetalDevice.makeCommandQueue()
// Create a dispatch semaphore, used to synchronise command execution
mCommandExecutingSemaphore = DispatchSemaphore(value: 1)
// Create a CAMetalLayer and set its frame to match that of the view
let layer = self.layer as! CAMetalLayer
layer.device = mMetalDevice
layer.pixelFormat = .bgra8Unorm
layer.framebufferOnly = true
layer.contentsScale = contentScaleFactor
// Get the default library from the bundle (Metal shaders)
let library = mMetalDevice.makeDefaultLibrary()
// Create a depth texture that is needed when rendering the augmentation.
let screenSize = UIScreen.main.bounds.size
let depthTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(
pixelFormat: .depth32Float,
width: Int(screenSize.width * contentScaleFactor),
height: Int(screenSize.height * contentScaleFactor),
mipmapped: false
)
depthTextureDescriptor.usage = .renderTarget
mDepthTexture = mMetalDevice.makeTexture(descriptor: depthTextureDescriptor)
// Video background projection matrix buffer
mVideoBackgroundProjectionBuffer = mMetalDevice.makeBuffer(length: MemoryLayout<Float>.size * 16, options: [])
// Fragment depth stencil
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .less
depthStencilDescriptor.isDepthWriteEnabled = true
mDepthStencilState = mMetalDevice.makeDepthStencilState(descriptor: depthStencilDescriptor)
mRenderer = MetalRenderer(
metalDevice: mMetalDevice,
layer: layer,
library: library,
textureDepth: mDepthTexture
)
}
private func configureVuforia() {
let orientationValue: Int32 = {
let orientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation ?? .portrait
switch orientation {
case .portrait: return 0
case .portraitUpsideDown: return 1
case .landscapeLeft: return 2
case .landscapeRight: return 3
case .unknown: return 4
#unknown default: return 4
}
}()
let screenSize = UIScreen.main.bounds.size
configureRendering(
Int32(screenSize.width * contentScaleFactor),
Int32(screenSize.height * contentScaleFactor),
orientationValue
)
}
#objc private func renderFrameVuforia() {
objc_sync_enter(self)
if mVuforiaStarted {
if mConfigurationChanged {
mConfigurationChanged = false
configureVuforia()
}
renderFrameVuforiaInternal()
delegate?.renderFrame(vuforiaView: self)
}
objc_sync_exit(self)
}
private func renderFrameVuforiaInternal() {
//Check if Camera is Started
guard isCameraStarted() else { return }
// ========== Set up ==========
var viewportsValue = Array(arrayLiteral: 0.0, 0.0, Double(metalLayer.drawableSize.width), Double(metalLayer.drawableSize.height), 0.0, 1.0)
// --- Command buffer ---
// Get the command buffer from the command queue
let commandBuffer = mMetalCommandQueue.makeCommandBuffer()
// Get the next drawable from the CAMetalLayer
let drawable = metalLayer.nextDrawable()
// It's possible for nextDrawable to return nil, which means a call to
// renderCommandEncoderWithDescriptor will fail
guard drawable != nil else { return }
// Wait for exclusive access to the GPU
let _ = mCommandExecutingSemaphore.wait(timeout: .distantFuture)
// -- Render pass descriptor ---
// Set up a render pass decriptor
let renderPassDescriptor = MTLRenderPassDescriptor()
// Draw to the drawable's texture
renderPassDescriptor.colorAttachments[0].texture = drawable?.texture
// Clear the colour attachment in case there is no video frame
renderPassDescriptor.colorAttachments[0].loadAction = .clear
// Store the data in the texture when rendering is complete
renderPassDescriptor.colorAttachments[0].storeAction = .store
// Use textureDepth for depth operations.
renderPassDescriptor.depthAttachment.texture = mDepthTexture
// Get a command encoder to encode into the command buffer
let encoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
if prepareToRender(
&viewportsValue,
UnsafeMutableRawPointer(Unmanaged.passRetained(mMetalDevice!).toOpaque()),
UnsafeMutableRawPointer(Unmanaged.passRetained(drawable!.texture).toOpaque()),
UnsafeMutableRawPointer(Unmanaged.passRetained(encoder!).toOpaque())
) {
viewport.originX = viewportsValue[0]
viewport.originY = viewportsValue[1]
viewport.width = viewportsValue[2]
viewport.height = viewportsValue[3]
viewport.znear = viewportsValue[4]
viewport.zfar = viewportsValue[5]
encoder?.setViewport(viewport)
// Once the camera is initialized we can get the video background rendering values
getVideoBackgroundProjection(mVideoBackgroundProjectionBuffer.contents())
// Call the renderer to draw the video background
mRenderer.renderVideoBackground(encoder: encoder, projectionMatrix: mVideoBackgroundProjectionBuffer, mesh: getVideoBackgroundMesh())
encoder?.setDepthStencilState(mDepthStencilState)
getOrigin(
&worldOriginProjectionMatrix.columns,
&worldOriginModelViewMatrix.columns
)
getImageTargetResult(
&trackableProjection.columns,
&trackableModelView.columns,
&trackableScaledModelView.columns,
&targetPose.columns,
&targetSize
)
}
// Pass Metal context data to Vuforia Engine (we may have changed the encoder since
// calling Vuforia::Renderer::begin)
finishRender(
UnsafeMutableRawPointer(Unmanaged.passRetained(drawable!.texture).toOpaque()),
UnsafeMutableRawPointer(Unmanaged.passRetained(encoder!).toOpaque())
)
// ========== Finish Metal rendering ==========
encoder?.endEncoding()
// Commit the rendering commands
// Command completed handler
commandBuffer?.addCompletedHandler { _ in self.mCommandExecutingSemaphore.signal() }
// Present the drawable when the command buffer has been executed (Metal
// calls to CoreAnimation to tell it to put the texture on the display when
// the rendering is complete)
commandBuffer?.present(drawable!)
// Commit the command buffer for execution as soon as possible
commandBuffer?.commit()
}
}
Another problem is that in portrait mode something is wrong with aspect ratio, camera background is drawn distorted. But this is for another subject.
Shaders.metal:
/*===============================================================================
Copyright (c) 2020, PTC Inc. All rights reserved.
Vuforia is a trademark of PTC Inc., registered in the United States and other
countries.
===============================================================================*/
#include <metal_stdlib>
using namespace metal;
// === Texture sampling shader ===
struct VertexTextureOut
{
float4 m_Position [[ position ]];
float2 m_TexCoord;
};
vertex VertexTextureOut texturedVertex(constant packed_float3* pPosition [[ buffer(0) ]],
constant float4x4* pMVP [[ buffer(1) ]],
constant float2* pTexCoords [[ buffer(2) ]],
uint vid [[ vertex_id ]])
{
VertexTextureOut out;
float4 in(pPosition[vid], 1.0f);
out.m_Position = *pMVP * in;
out.m_TexCoord = pTexCoords[vid];
return out;
}
fragment half4 texturedFragment(VertexTextureOut inFrag [[ stage_in ]],
texture2d<half> tex2D [[ texture(0) ]],
sampler sampler2D [[ sampler(0) ]])
{
return tex2D.sample(sampler2D, inFrag.m_TexCoord);
}
// === Uniform color shader ===
struct VertexOut
{
float4 m_Position [[ position ]];
};
vertex VertexOut uniformColorVertex(constant packed_float3* pPosition [[ buffer(0) ]],
constant float4x4* pMVP [[ buffer(1) ]],
uint vid [[ vertex_id ]])
{
VertexOut out;
float4 in(pPosition[vid], 1.0f);
out.m_Position = *pMVP * in;
return out;
}
fragment float4 uniformColorFragment(constant float4 &color [[ buffer(0) ]])
{
return color;
}
// === Vertex color shader ===
struct VertexColorOut
{
float4 m_Position [[ position ]];
float4 m_Color;
};
vertex VertexColorOut vertexColorVertex(constant packed_float3* pPosition [[ buffer(0) ]],
constant float4* pColor [[ buffer(1) ]],
constant float4x4* pMVP [[ buffer(2) ]],
uint vid [[ vertex_id ]])
{
VertexColorOut out;
float4 in(pPosition[vid], 1.0f);
out.m_Position = *pMVP * in;
out.m_Color = pColor[vid];
return out;
}
fragment float4 vertexColorFragment(VertexColorOut inFrag [[ stage_in ]])
{
return inFrag.m_Color;
}

IOS Metal Stencil Buffer project crashes when Metal Validation Enabled

I am trying to learn how to implement Stencil Buffer in Metal IOS. For now It works fine without Metal Validation. But as soon as I turn on Metal Validations, project crashes.
Here is my code:-
Metal View:-
import MetalKit
class DrawingView: MTKView {
//Renderer to handle all the rendering of the Drawing View
public var renderer: Renderer!
override init(frame frameRect: CGRect, device: MTLDevice?) {
super.init(frame: frameRect, device: device)
self.configDrawingView()
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.configDrawingView()
}
private func configDrawingView() {
//Setting system default GPU
self.device = MTLCreateSystemDefaultDevice()
//Setting pixel format for the view
self.colorPixelFormat = MTLPixelFormat.bgra8Unorm
//Setting clear color of the view. View will be cleared in every frame
self.clearColor = MTLClearColorMake(1.0, 1.0, 1.0, 1.0)
//Setting sample count of drawing textures
self.sampleCount = 1
//Setting prefered frame rate
self.preferredFramesPerSecond = 120
//Setting auto resizing of the drawable to false. If the value is true, the currentDrawable object’s texture, depthStencilTexture object, and multisampleColorTexture object automatically resize as the view resizes. If the value is false, drawableSize does not change and neither does the size of these objects.
self.autoResizeDrawable = false
//Stencil
self.clearStencil = 0
self.depthStencilPixelFormat = MTLPixelFormat.depth32Float_stencil8
//Setting up Renderer
self.renderer = Renderer(withDevice: self.device!)
//Using renderer and the MTKView delegate. This will call draw method every frame
self.delegate = renderer
}
}
Renderer:-
import MetalKit
class Renderer: NSObject {
//Device
private var device: MTLDevice!
//CommandQueue
private var commandQueue: MTLCommandQueue!
//Mirror Vertex
private var paintData: [Vertex] = [
Vertex(position: float4(-0.75, -0.75, 0, 1), color: float4(1.0, 1.0, 0.0, 1.0)),
Vertex(position: float4(-0.75, 0.75, 0, 1), color: float4(1.0, 1.0, 0.0, 1.0)),
Vertex(position: float4(0.75, 0.75, 0, 1), color: float4(1.0, 1.0, 0.0, 1.0)),
Vertex(position: float4(0.75, -0.75, 0, 1), color: float4(1.0, 1.0, 0.0, 1.0))
]
//Mirror Indices
private var paintIndicies: [UInt32] = [0, 1, 2, 0, 2, 3]
//Mirror Vertex Buffer
private var paintVertexBuffer: MTLBuffer!
//Mirror Index Buffer
private var paintIndexBuffer: MTLBuffer!
//Paint Vertex
private var stencilData: [Vertex] = [
Vertex(position: float4(-1, 0, 0, 1), color: float4(0.0, 0.0, 1.0, 1.0)),
Vertex(position: float4(0, 1, 0, 1), color: float4(0.0, 0.0, 1.0, 1.0)),
Vertex(position: float4(0, -1, 0, 1), color: float4(0.0, 0.0, 1.0, 1.0)),
Vertex(position: float4(1, 0, 0, 1), color: float4(0.0, 0.0, 1.0, 1.0))
]
//Paint indices
private var stencilIndices: [UInt32] = [0, 1, 2, 2, 1, 3]
//Paint Vertex Buffer
private var stencilVertexBuffer: MTLBuffer!
//Paint Index Buffer
private var stencilIndexBuffer: MTLBuffer!
//Depth Stencil State
private var depthStencilState: MTLDepthStencilState!
private var maskStencilState: MTLDepthStencilState!
private var drawStencilState: MTLDepthStencilState!
private var library: MTLLibrary!
private var vertexFunction: MTLFunction!
private var fragmentFunction: MTLFunction!
private var stencilFragmentFunction: MTLFunction!
private var pipelineState: MTLRenderPipelineState!
private var stencilPipelineState: MTLRenderPipelineState!
init(withDevice device: MTLDevice) {
super.init()
self.device = device
self.configure()
}
private func configure() {
self.commandQueue = self.device.makeCommandQueue()
self.setupPipelines()
self.setupDepthStencil()
self.setupBuffers()
}
private func setupDepthStencil() {
var depthStencilStateDescriptor: MTLDepthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilStateDescriptor.depthCompareFunction = MTLCompareFunction.less
depthStencilStateDescriptor.isDepthWriteEnabled = true
self.depthStencilState = self.device.makeDepthStencilState(descriptor: depthStencilStateDescriptor)
depthStencilStateDescriptor = MTLDepthStencilDescriptor()
depthStencilStateDescriptor.depthCompareFunction = MTLCompareFunction.less
var stencilDescriptor: MTLStencilDescriptor = MTLStencilDescriptor()
stencilDescriptor.stencilCompareFunction = MTLCompareFunction.always
stencilDescriptor.depthStencilPassOperation = MTLStencilOperation.replace
depthStencilStateDescriptor.backFaceStencil = stencilDescriptor
depthStencilStateDescriptor.frontFaceStencil = stencilDescriptor
depthStencilStateDescriptor.isDepthWriteEnabled = false
self.drawStencilState = self.device.makeDepthStencilState(descriptor: depthStencilStateDescriptor)
depthStencilStateDescriptor = MTLDepthStencilDescriptor()
depthStencilStateDescriptor.depthCompareFunction = MTLCompareFunction.less
stencilDescriptor = MTLStencilDescriptor()
stencilDescriptor.stencilCompareFunction = MTLCompareFunction.equal
depthStencilStateDescriptor.frontFaceStencil = stencilDescriptor
depthStencilStateDescriptor.backFaceStencil = stencilDescriptor
depthStencilStateDescriptor.isDepthWriteEnabled = true
self.maskStencilState = self.device.makeDepthStencilState(descriptor: depthStencilStateDescriptor)
}
private func setupPipelines() {
self.library = self.device.makeDefaultLibrary()
self.vertexFunction = self.library.makeFunction(name: "vertexShader")
self.fragmentFunction = self.library.makeFunction(name: "fragmentShader")
self.stencilFragmentFunction = self.library.makeFunction(name: "stencilFragmentShader")
var renderPipelineDescriptor: MTLRenderPipelineDescriptor = MTLRenderPipelineDescriptor()
renderPipelineDescriptor.vertexFunction = self.vertexFunction
renderPipelineDescriptor.fragmentFunction = self.fragmentFunction
renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm
renderPipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormat.depth32Float_stencil8
renderPipelineDescriptor.stencilAttachmentPixelFormat = MTLPixelFormat.depth32Float_stencil8
do {
self.pipelineState = try self.device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
} catch {
print("\(error)")
}
var renderPipelineDescriptorNoDraw: MTLRenderPipelineDescriptor = MTLRenderPipelineDescriptor()
renderPipelineDescriptorNoDraw.vertexFunction = self.vertexFunction
renderPipelineDescriptorNoDraw.fragmentFunction = self.stencilFragmentFunction
renderPipelineDescriptorNoDraw.depthAttachmentPixelFormat = MTLPixelFormat.depth32Float_stencil8
renderPipelineDescriptorNoDraw.stencilAttachmentPixelFormat = MTLPixelFormat.depth32Float_stencil8
renderPipelineDescriptorNoDraw.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm //Problem Here ---- (1)
renderPipelineDescriptorNoDraw.colorAttachments[0].writeMask = []
do {
self.stencilPipelineState = try self.device.makeRenderPipelineState(descriptor: renderPipelineDescriptorNoDraw)
} catch {
print("\(error)")
}
}
private func setupBuffers() {
self.stencilVertexBuffer = self.device.makeBuffer(bytes: self.stencilData, length: MemoryLayout<Vertex>.stride * self.stencilData.count, options: [])
self.stencilIndexBuffer = self.device.makeBuffer(bytes: self.stencilIndices, length: MemoryLayout<UInt32>.size * self.stencilIndices.count, options: [])
self.paintVertexBuffer = self.device.makeBuffer(bytes: self.paintData, length: MemoryLayout<Vertex>.stride * self.paintData.count, options: [])
self.paintIndexBuffer = self.device.makeBuffer(bytes: self.paintIndicies, length: MemoryLayout<UInt32>.size * self.paintIndicies.count, options: [])
}
}
extension Renderer: MTKViewDelegate {
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
let buffer: MTLCommandBuffer = self.commandQueue.makeCommandBuffer()!
let renderPassDescriptor = view.currentRenderPassDescriptor
renderPassDescriptor!.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 1.0, 1.0, 1.0)
let encoderClear = buffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
encoderClear!.endEncoding()
let renderPassDescriptorOther = MTLRenderPassDescriptor()
renderPassDescriptorOther.colorAttachments[0].loadAction = MTLLoadAction.load
renderPassDescriptorOther.colorAttachments[0].storeAction = MTLStoreAction.store
renderPassDescriptorOther.colorAttachments[0].texture = view.currentDrawable?.texture
let commandEncoderOther = buffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorOther)
Plane(withDevice: self.device, sizeOf: float2(100, 100), andPlaneCenterTo: float2(100, 100), withColor: float4(1.0, 0.0, 0.0, 1.0)).render(commandEncoder: commandEncoderOther!)
commandEncoderOther?.endEncoding()
let renderPassDescriptorOther1 = MTLRenderPassDescriptor()
renderPassDescriptorOther1.colorAttachments[0].loadAction = MTLLoadAction.load
renderPassDescriptorOther1.colorAttachments[0].storeAction = MTLStoreAction.store
renderPassDescriptorOther1.colorAttachments[0].texture = view.currentDrawable?.texture
let encoder: MTLRenderCommandEncoder = buffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorOther1)!
//Stencil
encoder.setRenderPipelineState(self.stencilPipelineState)
encoder.setStencilReferenceValue(1)
encoder.setDepthStencilState(self.drawStencilState)
encoder.setVertexBuffer(self.stencilVertexBuffer, offset: 0, index: 0)
encoder.drawIndexedPrimitives(type: MTLPrimitiveType.triangle, indexCount: self.stencilIndices.count, indexType: MTLIndexType.uint32, indexBuffer: self.stencilIndexBuffer, indexBufferOffset: 0)
//Paint
encoder.setRenderPipelineState(self.pipelineState)
encoder.setDepthStencilState(self.maskStencilState)
encoder.setVertexBuffer(self.paintVertexBuffer, offset: 0, index: 0)
encoder.drawIndexedPrimitives(type: MTLPrimitiveType.triangle, indexCount: self.paintIndicies.count, indexType: MTLIndexType.uint32, indexBuffer: self.paintIndexBuffer, indexBufferOffset: 0)
encoder.endEncoding()
buffer.present(view.currentDrawable!)
buffer.commit()
}
}
Here is my shader file:-
#include <metal_stdlib>
using namespace metal;
struct Vertex {
float4 position [[position]];
float4 color;
};
vertex Vertex vertexShader(const device Vertex *vertexArray [[buffer(0)]], unsigned int vid [[vertex_id]]) {
return vertexArray[vid];
}
fragment float4 fragmentShader(Vertex interpolated [[stage_in]]) {
return interpolated.color;
}
fragment float4 fragmentShader_stencil(Vertex v [[stage_in]])
{
return float4(1, 1, 1, 0.0);
}
When I turn on Metal Validation, It gives me this error:-
[MTLDebugRenderCommandEncoder validateFramebufferWithRenderPipelineState:]:1236:
failed assertion `For depth attachment, the renderPipelineState pixelFormat
must be MTLPixelFormatInvalid, as no texture is set.'
Then I changed (1) pixel format in Renderer to MTLPixelFormat.inavlid it gives me another error :-
[MTLDebugRenderCommandEncoder validateFramebufferWithRenderPipelineState:]:1196:
failed assertion `For color attachment 0, the render pipeline's pixelFormat
(MTLPixelFormatInvalid) does not match the framebuffer's pixelFormat
(MTLPixelFormatBGRA8Unorm).'
Is there way to fix this. I want Metal Validation to be enabled. With Validation disabled it works fine.
Since you're manually creating render pass descriptors, you need to ensure that the textures and load/store actions of all relevant attachments are configured.
You've specified to your MTKView that it should manage a depth/stencil texture for you, and configured your render pipeline state to expect a depth/stencil texture, so you need to provide such a texture when creating your render pass descriptor:
renderPassDescriptorOther1.depthAttachment.loadAction = .clear
renderPassDescriptorOther1.depthAttachment.storeAction = .dontCare
renderPassDescriptorOther1.depthAttachment.texture = view.depthStencilTexture
renderPassDescriptorOther1.stencilAttachment.loadAction = .clear
renderPassDescriptorOther1.stencilAttachment.storeAction = .dontCare
renderPassDescriptorOther1.stencilAttachment.texture = view.depthStencilTexture
Incidentally, I don't see the reason for running a separate "clear" pass before drawing. It seems that you could just set the loadAction of renderPassDescriptorOther's color attachment to .clear.

Why MTLTexture with 2D Array doesn't work?

I'm trying to replicate splat map technique from Unity tutorial. They use Texture2DArray so I created MTLTexture with this type:
private func createTerrainTexture(_ bundle: Bundle) -> MTLTexture {
guard let device = MTLCreateSystemDefaultDevice() else {
fatalError()
}
let names = ["sand", "grass", "earth", "stone", "snow"]
let loader = MTKTextureLoader(device: device)
let array = names.map { name -> MTLTexture in
do {
return try loader.newTexture(name: name, scaleFactor: 1.0, bundle: bundle, options: nil)
} catch {
fatalError()
}
}
guard let queue = device.makeCommandQueue() else {
fatalError()
}
guard let commandBuffer = queue.makeCommandBuffer() else {
fatalError()
}
guard let encoder = commandBuffer.makeBlitCommandEncoder() else {
fatalError()
}
let descriptor = MTLTextureDescriptor()
descriptor.textureType = .type2DArray
descriptor.pixelFormat = array[0].pixelFormat
descriptor.width = array[0].width
descriptor.height = array[0].height
descriptor.mipmapLevelCount = array[0].mipmapLevelCount
descriptor.arrayLength = 5
guard let texture = device.makeTexture(descriptor: descriptor) else {
fatalError()
}
var slice = 0
array.forEach { item in
encoder.copy(from: item,
sourceSlice: 0,
sourceLevel: 0,
sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
sourceSize: MTLSize(width: item.width, height: item.height, depth: 1),
to: texture,
destinationSlice: slice,
destinationLevel: 0,
destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
slice += 1
}
encoder.endEncoding()
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
return texture
}
Here is my fragment shader function:
fragment half4 terrainFragment(TerrainVertexOutput in [[stage_in]],
texture2d_array<float> terrainTexture [[texture(0)]])
{
constexpr sampler sampler2d(coord::normalized, filter::linear, address::repeat);
float2 uv = in.position.xz * 0.02;
float4 c1 = terrainTexture.sample(sampler2d, uv, 0);
return half4(c1);
}
Here is Unity shader from tutorial:
void surf (Input IN, inout SurfaceOutputStandard o) {
float2 uv = IN.worldPos.xz * 0.02;
fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(uv, 0));
Albedo = c.rgb * _Color;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
For some reason I'm getting wrong result when texture repeated in columns.
Result I want to have is:
Update.
Here is how texture looks like in GPU Frame Debugger:
When I copy mipmaps like this:
var slice = 0
array.forEach { item in
print(item.width, item.height, item.mipmapLevelCount)
for i in 0..<descriptor.mipmapLevelCount {
encoder.copy(from: item,
sourceSlice: 0,
sourceLevel: i,
sourceOrigin: MTLOrigin(x: 0, y: 0, z: 0),
sourceSize: MTLSize(width: item.width, height: item.height, depth: 1),
to: texture,
destinationSlice: slice,
destinationLevel: i,
destinationOrigin: MTLOrigin(x: 0, y: 0, z: 0))
}
slice += 1
}
I'm getting error:
-[MTLDebugBlitCommandEncoder validateCopyFromTexture:sourceSlice:sourceLevel:sourceOrigin:sourceSize:toTexture:destinationSlice:destinationLevel:destinationOrigin:options:]:254: failed assertion `(sourceOrigin.x + sourceSize.width)(512) must be <= width(256).'
Problem was in incorrect porting of fragment shader input variable. In original input worldPos was used, but I used float4 position [[position]] and according to Metal specification this means
Describes the window-relative coordinate (x, y, z, 1/w) values for the fragment.
So position was incorrect. Here is how correct fragment shader input looks like:
struct TerrainVertexOutput
{
float4 position [[position]];
float3 p;
};
And vertex function:
vertex TerrainVertexOutput terrainVertex(TerrainVertexInput in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant MyNodeBuffer& scn_node [[buffer(1)]])
{
TerrainVertexOutput v;
v.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
v.p = (scn_node.modelTransform * float4(in.position, 1.0)).xyz;
return v;
}

Large section of iPad OpenGL ES application not retina

I have an OpengGL ES app using a custom shader to display circles. See image below. (Opening it in a new window might be helpful)
If you look carefully you can see that the display seems to be non retina from about 50% of the width and about 75% of the height. This seems to be the case only on iPad 3 (clients device). Simulator and an other iPad Air 2 behave normally.
I used a the basic OpenGL ES game project bundled with XCode.
Update:
The pixelated areas are the ones highlighted in red:
Please also see closeup:
I must admit I do not know where to start debugging this,
since it only seems to bug on the given device.
Here is the code I used to setup the context.
func setup()
{
initTextures()
self.context = EAGLContext(api: .openGLES2)
if !(self.context != nil) {
print("Failed to create ES context")
}
let view = self.view as! GLKView
// Fix for "good" aspect ratio
var frameSize = view.frame.size
frameSize.height = frameSize.width / 1.43023255813953
view.frame.size = frameSize
// Should force the aspect ratio
print("-------------")
print("width \(view.frame.width) and height \(view.frame.height)")
print("aspect ratio w/h \(view.frame.width / view.frame.height)")
print("-------------")
view.context = self.context!
view.drawableColorFormat = .RGBA8888
view.drawableMultisample = .multisample4X
// Application specific code
self.setupGL()
}
Update
I am drawing the circles with a custom fragment shader:
precision highp float;
uniform vec4 iResolution; // z - texWidth, w - texHeight
uniform sampler2D textureUnit;
uniform sampler2D smallPointsTextureUnit;
uniform vec2 gridSize;
#define SMOOTH(r,R) (1.0-smoothstep(R-0.09,R+0.09, r))
#define black vec3(0.0)
#define white vec3(1.0)
float circle(vec2 st, in float _radius, float pct ){
float l = length(st - vec2(0.5));
return 1.-smoothstep(_radius-(_radius*0.005) * pct,
_radius+(_radius*0.005),
l);
}
float stroke(vec2 uv, vec2 center, float radius, float width)
{
float dist = length(uv-center);
float t = 1.0 + smoothstep(radius, radius+width, dist)
- smoothstep(radius-width, radius, dist);
return t;
}
void main()
{
vec2 resolution = vec2(iResolution.x, iResolution.y);
vec2 uv = gl_FragCoord.xy;
vec2 st = gl_FragCoord.xy/resolution;
float colWidth = iResolution.x / gridSize.x;
float rowHeight = (iResolution.y + 1.0) / gridSize.y;
float smallerSize = min(rowHeight, colWidth);
float largerSize = max(rowHeight, colWidth);
vec2 divider = resolution / smallerSize;
st.x *= divider.x;
st.y *= divider.y;
float pct = largerSize / smallerSize;
float texXPos = (floor(st.x * smallerSize / largerSize) + 0.5) / iResolution.z;
float texYPos = (floor(gridSize.y -st.y) + 0.5) / iResolution.w;
vec4 tex = texture2D(textureUnit, vec2(
texXPos,
texYPos));
vec4 texSmallPoints = texture2D(smallPointsTextureUnit, vec2((floor(st.x * 2.0 * smallerSize / largerSize) + 0.5) / 128.0,
(floor(gridSize.y * 2.0 -st.y * 2.0) + 0.5) / 128.0));
//texSmallPoints.r = 0.5;
vec3 fillColor = vec3(tex.x, tex.y, tex.z);
st.x = mod(st.x, pct);
st.x = step( fract(st.x * 1.0 / pct), 1.0 / pct) * fract(st.x);
st.x *= texSmallPoints.r * 2.0; // subdivide for small circles
st.x = fract(st.x);
// Divide by 4
st.y *= texSmallPoints.r * 2.0;
st.y = fract(st.y);
//float r = 0.425;
float r = 0.4;
float fillPct = circle(st, r, 1.0);
vec2 center = vec2(0.5);
float strokePct = stroke(st, center, r, 0.032 * texSmallPoints.r * 1.8);
vec3 finalColor = vec3(1.0);
vec3 strokeColor = fillColor;
// todo -refactor if slow
// todo - invert
if (tex.a > 0.99) {
strokeColor = black;
}
if (tex.a < 0.01) {
strokeColor = white;
}
finalColor = mix(white, fillColor, fillPct);
finalColor = mix(finalColor, strokeColor, 1. - strokePct);
gl_FragColor = vec4(finalColor, 1.0);
}
And GLKViewController:
//
// HomeOpenGLController.swift
// Kobi
//
// Created by Tibor Udvari on 14/06/16.
// Copyright © 2016 Tibor Udvari. All rights reserved.
//
import GLKit
import OpenGLES
import HEXColor
open class KobiOpenGLControllerBase: GLKViewController
{
// --- Small points texture ---
var gpuSmallColorsTexture = [GLubyte](repeating: 0, count: 0)
var currentSmallPointTextureData: [GLubyte]? = nil
// - allocated size
let smallPointsTextureWidth = 128
let smallPointsTextureHeight = 128
// --- Color texture ---
var gpuColorsTexture = [GLubyte](repeating: 0, count: 0)
var currentColorsTextureData: [GLubyte]? = nil // size of grid
// - allocated size
let texWidth: Int = 256
let texHeight: Int = 256
// Grid - circles
let cols = 31
let rows = 22
open let maxIdx: Int
open let circleCount: Int
// Grid - pixels
var width: CGFloat = 0.0
var height: CGFloat = 0.0
var circleWidth: CGFloat = 0.0
var circleHeight: CGFloat = 0.0
// OpenGL
var program: GLuint = 0
var circleProgram: GLuint = 0
var context: EAGLContext? = nil
required public init?(coder aDecoder: NSCoder) {
maxIdx = cols * rows
circleCount = cols * rows
super.init(coder: aDecoder)
}
// 0 is positive instead of 0
func sign(_ x: Int) -> Int {
let r = x < 0 ? -1 : 1
return r
}
// MARK: - Setup
override open func viewDidLoad() {
setupGridData()
}
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setup() // because width and height is not initiated yet
}
func setupGridData(){
//currentSmallPointTextureData = createCurrentSmallPointsTextureData()
}
func setup()
{
initTextures()
self.context = EAGLContext(api: .openGLES2)
if !(self.context != nil) {
print("Failed to create ES context")
}
let view = self.view as! GLKView
// Fix for "good" aspect ratio
var frameSize = view.frame.size
frameSize.height = frameSize.width / 1.43023255813953
view.frame.size = frameSize
// Should force the aspect ratio
print("-------------")
print("width \(view.frame.width) and height \(view.frame.height)")
print("aspect ratio w/h \(view.frame.width / view.frame.height)")
print("-------------")
view.context = self.context!
view.drawableColorFormat = .RGBA8888
view.drawableMultisample = .multisample4X
//view.drawableMultisample = .MultisampleNone
//view.multipleTouchEnabled = true
width = self.view.frame.size.width * self.view.contentScaleFactor
height = self.view.frame.size.height * self.view.contentScaleFactor
circleWidth = width / CGFloat(cols)
circleHeight = height / CGFloat(rows)
self.setupGL()
}
func initTextures()
{
gpuColorsTexture = [GLubyte](repeating: 0, count: Int(texWidth)*Int(texHeight)*4)
gpuSmallColorsTexture = [GLubyte](repeating: 128, count: Int(smallPointsTextureWidth)*Int(smallPointsTextureHeight))
}
// MARK: - GLKView and GLKViewController delegate methods
func sendTexturesToGPU() {
for i in 0..<currentColorsTextureData!.count / 4 {
let r = Int(i) / Int(cols)
let c = Int(i) % cols
let j = r * texWidth + c
gpuColorsTexture[j*4] = currentColorsTextureData![i * 4]; //= GLubyte(255); // red
gpuColorsTexture[j*4+1] = currentColorsTextureData![i * 4 + 1]; //GLubyte(random() % 255); // green
gpuColorsTexture[j*4+2] = currentColorsTextureData![i * 4 + 2]; //GLubyte(0); // blue
gpuColorsTexture[j*4+3] = currentColorsTextureData![i * 4 + 3]; // used for the stroke color
}
for i in 0..<currentSmallPointTextureData!.count{
let r = Int(i) / Int(31 * 2)
let c = Int(i) % (31 * 2)
let j = r * 128 + c
gpuSmallColorsTexture[j] = currentSmallPointTextureData![i];
}
glActiveTexture(GLenum(GL_TEXTURE1));
glTexImage2D(GLenum(GL_TEXTURE_2D), GLint(0), GL_LUMINANCE, GLsizei(smallPointsTextureWidth), GLsizei(smallPointsTextureHeight), GLint(0), GLenum(GL_LUMINANCE), GLenum(GL_UNSIGNED_BYTE), &gpuSmallColorsTexture)
glActiveTexture(GLenum(GL_TEXTURE0));
glTexImage2D(GLenum(GL_TEXTURE_2D), GLint(0), GL_RGBA, GLsizei(texWidth), GLsizei(texHeight), GLint(0), GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), &gpuColorsTexture);
}
func update() {
print("update")
//todo
}
// todo send a uniform array
override open func glkView(_ view: GLKView, drawIn rect: CGRect) {
glClearColor(1.0, 1.0, 0.0, 1.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
glEnable(GLenum(GL_DEPTH_TEST))
glEnable(GLenum(GL_POINT_SIZE));
glEnable(GLenum(GL_BLEND))
glBlendFunc(GLenum(GL_SRC_ALPHA), GLenum(GL_ONE_MINUS_SRC_ALPHA))
glEnable(GLenum(GL_POINT_SMOOTH))
// 22 x 15
var baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, 0.0)
baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, 0.0, 0.0, 1.0, 0.0)
var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, 1.5)
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, 0.0, 1.0, 1.0, 1.0)
modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix)
modelViewMatrix = GLKMatrix4Identity
glUseProgram(program)
/*
withUnsafePointer(to: &modelViewProjectionMatrix, {
$0.withMemoryRebound(to: Float.self, capacity: 16, {
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, $0)
})
})*/
withUnsafePointer(to: &modelViewMatrix, {
$0.withMemoryRebound(to: Float.self, capacity: 16, {
glUniformMatrix4fv(glGetUniformLocation(program, "modelViewProjectionMatrix"), 1, 0, UnsafePointer($0))
})
})
glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, squareVertices)
glUniform4f(glGetUniformLocation(program, "iResolution"), Float(width), Float(height), Float(texWidth), Float(texHeight))
glUniform2f(glGetUniformLocation(program, "gridSize"), Float(cols), Float(rows))
glDrawArrays(GLenum(GL_TRIANGLE_STRIP) , 0, 4)
glUseProgram(circleProgram)
}
// MARK: - Texture
func setupTextures()
{
let texInfo = try! GLKTextureLoader.texture(with: UIImage(named: "texture256")!.cgImage!, options: nil)
glActiveTexture(GLenum(GL_TEXTURE0))
glBindTexture(GLenum(GL_TEXTURE0), (texInfo.name))
//var dataTexture = (texInfo.name)
glUniform1i(glGetUniformLocation(program, "textureUnit"), 0)
glActiveTexture(GLenum(GL_TEXTURE1))
let _ = createSmallPointsTexture()
glUniform1i(glGetUniformLocation(program, "smallPointsTextureUnit"), 1)
}
func createSmallPointsTexture() -> GLuint {
var texture: GLuint = 1
glGenTextures(GLsizei(1), &texture)
glBindTexture(GLenum(GL_TEXTURE_2D), texture)
glActiveTexture(texture)
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR);
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE);
glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE);
glGenerateMipmap(GLenum(GL_TEXTURE_2D));
return texture
}
// MARK: - OpenGL ES 2 shader compilation
func setupGL() {
EAGLContext.setCurrent(self.context)
let _ = self.loadShaders()
glUseProgram(program)
glEnableVertexAttribArray(0)
self.setupTextures()
}
func tearDownGL() {
EAGLContext.setCurrent(self.context)
if program != 0 {
glDeleteProgram(program)
program = 0
}
}
func loadShaders() -> Bool {
var vertShader: GLuint = 0
var fragShader: GLuint = 0
var vertShaderPathname: String
var fragShaderPathname: String
// Create shader program.
program = glCreateProgram()
// Create and compile vertex shader.
vertShaderPathname = Bundle.main.path(forResource: "Shader", ofType: "vsh")!
if self.compileShader(&vertShader, type: GLenum(GL_VERTEX_SHADER), file: vertShaderPathname) == false {
print("Failed to compile vertex shader")
return false
}
// Create and compile fragment shader.
fragShaderPathname = Bundle.main.path(forResource: "Shader", ofType: "fsh")!
if !self.compileShader(&fragShader, type: GLenum(GL_FRAGMENT_SHADER), file: fragShaderPathname) {
print("Failed to compile fragment shader")
/*
var fragInfoLength: GLint = 0
glGetShaderiv(fragShader, GLenum(GL_INFO_LOG_LENGTH), &fragInfoLength)
//let cstring = UnsafeMutablePointer<GLchar>(allocatingCapacity: Int(fragInfoLength))
var cstring = UnsafeMutablePointer<GLchar>(malloc(Int(fragInfoLength)))
glGetShaderInfoLog(fragShader, fragInfoLength, nil, cstring)
let shaderInfoLog = NSString(utf8String: cstring)
print(shaderInfoLog)
*/
return false
}
// Attach vertex shader to program.
glAttachShader(program, vertShader)
// Attach fragment shader to program.
glAttachShader(program, fragShader)
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation(program, 0, "position")
// Link program.
if !self.linkProgram(program) {
print("Failed to link program: \(program)")
if vertShader != 0 {
glDeleteShader(vertShader)
vertShader = 0
}
if fragShader != 0 {
glDeleteShader(fragShader)
fragShader = 0
}
if program != 0 {
glDeleteProgram(program)
program = 0
}
return false
}
// Release vertex and fragment shaders.
if vertShader != 0 {
glDetachShader(program, vertShader)
glDeleteShader(vertShader)
}
if fragShader != 0 {
glDetachShader(program, fragShader)
glDeleteShader(fragShader)
}
return true
}
func compileShader(_ shader: inout GLuint, type: GLenum, file: String) -> Bool {
var status: GLint = 0
var source: UnsafePointer<Int8>
do {
source = try NSString(contentsOfFile: file, encoding: String.Encoding.utf8.rawValue).utf8String!
} catch {
print("Failed to load vertex shader")
return false
}
//var castSource = UnsafePointer<GLchar>(source)
var castSource: UnsafePointer<GLchar>? = UnsafePointer<GLchar>(source)
shader = glCreateShader(type)
glShaderSource(shader, 1, &castSource, nil)
glCompileShader(shader)
var logLength: GLint = 0
glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
if logLength > 0 {
//var log = UnsafeMutablePointer<GLchar>(malloc(Int(logLength)))
print("Log length gt 0")
/*
var log = UnsafeMutablePointer<GLchar>(malloc(Int(logLength)))
glGetShaderInfoLog(shader, logLength, &logLength, log)
NSLog("Shader compile log: \n%s", log)
free(log)
*/
}
glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &status)
if status == 0 {
glDeleteShader(shader)
return false
}
return true
}
func linkProgram(_ prog: GLuint) -> Bool {
var status: GLint = 0
glLinkProgram(prog)
//#if defined(DEBUG)
// var logLength: GLint = 0
// glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
// if logLength > 0 {
// var log = UnsafeMutablePointer<GLchar>(malloc(Int(logLength)))
// glGetShaderInfoLog(shader, logLength, &logLength, log)
// NSLog("Shader compile log: \n%s", log)
// free(log)
// }
//#endif
glGetProgramiv(prog, GLenum(GL_LINK_STATUS), &status)
if status == 0 {
return false
}
return true
}
func validateProgram(_ prog: GLuint) -> Bool {
var logLength: GLsizei = 0
var status: GLint = 0
glValidateProgram(prog)
glGetProgramiv(prog, GLenum(GL_INFO_LOG_LENGTH), &logLength)
if logLength > 0 {
var log: [GLchar] = [GLchar](repeating: 0, count: Int(logLength))
glGetProgramInfoLog(prog, logLength, &logLength, &log)
print("Program validate log: \n\(log)")
}
glGetProgramiv(prog, GLenum(GL_VALIDATE_STATUS), &status)
var returnVal = true
if status == 0 {
returnVal = false
}
return returnVal
}
// MARK : Cleanup
override open func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
if self.isViewLoaded && (self.view.window != nil) {
self.view = nil
self.tearDownGL()
if EAGLContext.current() === self.context {
EAGLContext.setCurrent(nil)
}
self.context = nil
}
}
deinit {
self.tearDownGL()
if EAGLContext.current() === self.context {
EAGLContext.setCurrent(nil)
}
}
}
var squareVertices: [GLfloat] = [
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
];
Thank you for updating. I am not sure exactly which part caused it but I am sure it must happen in your fragment shader code.
I guess vec2 st's value changing is not steady there while it is being calculated.
I want you to test this to see if it is your fragment shader.
just draw only 1 circle without uniform values except iResolution.
only using iResolution and gl_FragCoord, draw a circle.
I think it's going to properly show up. Then go over your FS.

Metal draw one object with texture one object without texture

I want to render 2 different objects with Metal...one is with texture, the other one is without texture. I have 2 different shaders, 2 different vertex descriptors, is that means i should use 2 different render pipeline? .. There is only one object drawing (the model with out texture)on the screen correct, the other one is wrong, I don't know where I went wrong.... Here is the code:
override func buildPipeline() {
//Model
let library = device!.newDefaultLibrary()!
let pipelineDescriptor = MTLRenderPipelineDescriptor()
buildPipelinForSky(pipelineDescriptor, library: library)
buildPipelineForModel(pipelineDescriptor, library: library)
do {
pipelineSky = try device!.newRenderPipelineStateWithDescriptor(pipelineDescriptor)
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = device!.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
commandQueue = device!.newCommandQueue()
}
func buildPipelineForModel(pipeLineDesc:MTLRenderPipelineDescriptor, library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let vertexFunctionModel = library.newFunctionWithName("vertex_ply")
let fragmentFunctionModel = library.newFunctionWithName("fragment_ply")
let vertexDescriptorModel = MTLVertexDescriptor()
vertexDescriptorModel.attributes[0].offset = 0
vertexDescriptorModel.attributes[0].format = .Float4
vertexDescriptorModel.attributes[0].bufferIndex = 0
vertexDescriptorModel.layouts[0].stepFunction = .PerVertex
vertexDescriptorModel.layouts[0].stride = sizeof(Float) * 4
pipeLineDesc.vertexFunction = vertexFunctionModel
pipeLineDesc.vertexDescriptor = vertexDescriptorModel
pipeLineDesc.fragmentFunction = fragmentFunctionModel
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
return pipeLineDesc
}
func buildPipelinForSky(pipeLineDesc:MTLRenderPipelineDescriptor, library: MTLLibrary ) -> MTLRenderPipelineDescriptor{
let vertexFunctionSky = library.newFunctionWithName("vertex_sky")
let fragmentFunctionSky = library.newFunctionWithName("fragment_sky")
let vertexDescriptorSky = MTLVertexDescriptor()
vertexDescriptorSky.attributes[0].offset = 0
vertexDescriptorSky.attributes[0].format = .Float4
vertexDescriptorSky.attributes[0].bufferIndex = 0
vertexDescriptorSky.attributes[1].offset = sizeof(Float32) * 4
vertexDescriptorSky.attributes[1].format = .Float4
vertexDescriptorSky.attributes[1].bufferIndex = 0
vertexDescriptorSky.attributes[2].offset = sizeof(Float32) * 8
vertexDescriptorSky.attributes[2].format = .Float2
vertexDescriptorSky.attributes[2].bufferIndex = 0
vertexDescriptorSky.layouts[0].stepFunction = .PerVertex
vertexDescriptorSky.layouts[0].stride = sizeof(Vertex)
pipeLineDesc.vertexFunction = vertexFunctionSky
pipeLineDesc.vertexDescriptor = vertexDescriptorSky
pipeLineDesc.fragmentFunction = fragmentFunctionSky
pipeLineDesc.depthAttachmentPixelFormat = .Depth32Float
let samplerDescriptorSky = MTLSamplerDescriptor()
samplerDescriptorSky.minFilter = .Nearest
samplerDescriptorSky.magFilter = .Linear
samplerStateSky = device!.newSamplerStateWithDescriptor(samplerDescriptorSky)
return pipeLineDesc
}
override func buildResources() {
// (vertexBuffer, indexBuffer) = SphereGenerator.sphereWithRadius(1, stacks: 30, slices: 30, device: device!)
//Model
(vertexBufferModel,normalBufferModel,colorBufferModel) = PointCloud.model(device!)
uniformBufferModel = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
//Sky
vertexBufferSky = SkySphere.sphere(device!)
uniformBufferSky = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
diffuseTextureSky = self.textureForImage(UIImage(named: "bluemarble")!, device: device!)
}
override func resize() {
//Model
super.resize()
//Sky
let layerSizeSky = metalLayer.drawableSize
let depthTextureDescriptorSky = MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.Depth32Float,
width: Int(layerSizeSky.width),
height: Int(layerSizeSky.height),
mipmapped: false)
depthTextureSky = device!.newTextureWithDescriptor(depthTextureDescriptorSky)
}
override func draw() {
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)
//Sky
if let drawable = metalLayer.nextDrawable()
{
var modelMatrixTransSky = M4f()
var modelMatrixRotSky = M4f()
var modelMatrixScaleSky = M4f()
modelMatrixTransSky = translate(0, y: 0, z: 0)
modelMatrixRotSky = rotate(90, r: V3f(1,0,0)) * modelMatrixRotSky
modelMatrixScaleSky = scaling(10, y: 10, z: 10)
let modelMatrixSky = modelMatrixTransSky * modelMatrixRotSky * modelMatrixScaleSky
var viewMatrixSky = M4f()
viewMatrixSky = myCamera.setLookAt(viewMatrixSky)
let modelViewMatrixSky = viewMatrixSky * modelMatrixSky
let aspect = Float32(metalLayer.drawableSize.width) / Float32(metalLayer.drawableSize.height)
let kFOVY:Float = 85.0
let projectionMatrix = perspective_fov(kFOVY, aspect: aspect, near: 0.1, far: 180.0)
let matricesSky = [projectionMatrix, modelViewMatrixSky]
memcpy(uniformBufferSky.contents(), matricesSky, Int(sizeof(M4f) * 2))
let commandBufferSky = commandQueue.commandBuffer()
commandBufferSky.addCompletedHandler{ [weak self] commandBufferSky in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//Model
var modelMatrixTransModel = M4f()
var modelMatrixRotModel = M4f()
var modelMatrixScaleModel = M4f()
modelMatrixTransModel = translate(0, y: 0, z: 0)
modelMatrixRotModel = rotate(0, r: V3f(1,0,0))
modelMatrixScaleModel = scaling(10, y: 10, z: 10)
let modelMatrixModel = modelMatrixTransModel * modelMatrixRotModel * modelMatrixScaleModel
var viewMatrixModel = M4f()
viewMatrixModel = myCamera.setLookAt(viewMatrixModel)
let modelViewMatrixModel = viewMatrixModel * modelMatrixModel
let matricesModel = [projectionMatrix, modelViewMatrixModel]
memcpy(uniformBufferModel.contents(), matricesModel, Int(sizeof(M4f) * 2))
//Sky
let passDescriptor = MTLRenderPassDescriptor()
passDescrForSky(passDescriptor, drawable: drawable)
passDescrForModel(passDescriptor, drawable: drawable)
//Sky
let commandEncoderSky = commandBufferSky.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoderSky.setRenderPipelineState(pipelineSky)
commandEncoderSky.setDepthStencilState(depthStencilState)
commandEncoderSky.setFrontFacingWinding(.CounterClockwise)
commandEncoderSky.setCullMode(.Back)
pointCloudDraw(commandEncoderSky)
skyDraw(commandEncoderSky)
commandEncoderSky.endEncoding()
commandBufferSky.presentDrawable(drawable)
// bufferIndex matches the current semaphore controled frame index to ensure writing occurs at the correct region in the vertex buffer
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBufferSky.commit()
}
}
func passDescrForModel(passDescriptor: MTLRenderPassDescriptor, drawable: CAMetalDrawable) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
return passDescriptor
}
func passDescrForSky(passDescriptor: MTLRenderPassDescriptor, drawable: CAMetalDrawable) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
passDescriptor.depthAttachment.texture = depthTextureSky
passDescriptor.depthAttachment.clearDepth = 1
passDescriptor.depthAttachment.loadAction = .Clear
passDescriptor.depthAttachment.storeAction = .DontCare
return passDescriptor
}
func pointCloudDraw(commandencodeModel: MTLRenderCommandEncoder) {
commandencodeModel.setVertexBuffer(vertexBufferModel, offset:0, atIndex:0)
commandencodeModel.setVertexBuffer(normalBufferModel, offset:0, atIndex:1)
commandencodeModel.setVertexBuffer(colorBufferModel, offset:0, atIndex:2)
commandencodeModel.setVertexBuffer(uniformBufferModel, offset:0, atIndex:3)
commandencodeModel.setFragmentBuffer(uniformBufferModel, offset: 0, atIndex: 0)
commandencodeModel.drawPrimitives(.Point, vertexStart: 0, vertexCount: vertextCountModel)
}
func skyDraw(commandencodeSky: MTLRenderCommandEncoder) {
commandencodeSky.setVertexBuffer(vertexBufferSky, offset:0, atIndex:0)
commandencodeSky.setVertexBuffer(uniformBufferSky, offset:0, atIndex:1)
commandencodeSky.setFragmentTexture(diffuseTextureSky, atIndex: 0)
commandencodeSky.setFragmentSamplerState(samplerStateSky, atIndex: 0)
commandencodeSky.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCountSky)
}
here is the vertex buffer for the sky:
struct Vector4
{
var x: Float32
var y: Float32
var z: Float32
var w: Float32
}
struct TexCoords
{
var u: Float32
var v: Float32
}
struct Vertex
{
var position: Vector4
var normal: Vector4
var texCoords: TexCoords
}
var vertexCountSky: Int = 0
struct SkySphere
{
static func sphere(device: MTLDevice) -> (MTLBuffer!)
{
let ply = plyVntReader.init(objFileName: "test")
let vertexBuffer = device.newBufferWithBytes(ply!.vertices, length:sizeof(Vertex) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
print(ply!.vertices)
vertexCountSky = ply!.vertexCount
return (vertexBuffer)
}
}
And here is vertex buffer for the model:
var vertextCountModel: Int = 0
struct PointCloud
{
static func model(device: MTLDevice) -> (MTLBuffer!, MTLBuffer!, MTLBuffer!)
{
let ply = plyVncReader.init(objFileName: "controller_ascii")
vertextCountModel = ply!.vertexCount
let vertexBuffer = device.newBufferWithBytes(ply!.vertices, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
let normalBuffer = device.newBufferWithBytes(ply!.normals, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
let colorBuffer = device.newBufferWithBytes(ply!.colors, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
print(ply!.colors)
return (vertexBuffer, normalBuffer, colorBuffer)
}
}
shaders for the sky
using namespace metal;
//Sky
struct TexturedInVertex
{
packed_float4 position [[attribute(0)]];
packed_float4 normal [[attribute(1)]];
packed_float2 texCoords [[attribute(2)]];
};
struct TexturedColoredOutVertex
{
float4 position [[position]];
float3 normal;
float2 texCoords;
float pointsize[[point_size]];
};
struct UniformsSky
{
float4x4 projectionMatrix;
float4x4 modelViewMatrix;
};
vertex TexturedColoredOutVertex vertex_sky(device TexturedInVertex *vert [[buffer(0)]],
constant UniformsSky &uniforms [[buffer(1)]],
uint vid [[vertex_id]])
{
float4x4 MV = uniforms.modelViewMatrix;
float3x3 normalMatrix(MV[0].xyz, MV[1].xyz, MV[2].xyz);
float4 modelNormal = vert[vid].normal;
TexturedColoredOutVertex outVertex;
outVertex.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(vert[vid].position);
outVertex.normal = normalMatrix * modelNormal.xyz;
outVertex.texCoords = vert[vid].texCoords;
outVertex.pointsize = 10.0;
return outVertex;
};
fragment half4 fragment_sky(TexturedColoredOutVertex vert [[stage_in]],
texture2d<float, access::sample> diffuseTexture [[texture(0)]],
sampler samplr [[sampler(0)]])
{
float4 diffuseColor = diffuseTexture.sample(samplr, vert.texCoords);
return half4(diffuseColor.r, diffuseColor.g, diffuseColor.b, 1);
};
here is shader for the model
//model
struct ColoredVertex
{
float4 position [[position]];
float4 normal;
float4 color;
float pointsize[[point_size]];
};
struct UniformsPoint
{
float4x4 projectionMatrix;
float4x4 modelViewMatrix;
};
vertex ColoredVertex vertex_ply(constant float4 *position [[buffer(0)]],
constant float4 *normal [[buffer(1)]],
constant float4 *color [[buffer(2)]],
constant UniformsPoint &uniforms [[buffer(3)]],
uint vid [[vertex_id]])
{
ColoredVertex vert;
vert.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position[vid];
vert.normal = normal[vid];
vert.color = color[vid];
vert.pointsize = 5.0;
return vert;
}
fragment float4 fragment_ply(ColoredVertex vert [[stage_in]])
{
return vert.color;
}

Resources