1

嗨,我正在尝试为我正在开发的应用程序创建一些金属反馈循环。但是我得到了一些奇怪的文物。我怀疑这是由于我的管道中的某些东西,或者我对前一帧的采样方式。在代码中,为第二个缓冲区设置了额外的管道,因为最终我需要它来制作图灵模式。但是现在我已经尽可能地减少代码以隔离问题。

现在它只是在左上角画一个小圆圈。绘制完成后,缓冲区 A 片段以一个像素的位移对其自身进行采样。这会导致圆圈随着每个循环在屏幕上延伸。问题是我在拉伸圈之前出现了彩色线条。当我尝试进行递归模糊或其他类型的反馈循环时,也会出现同样的伪影。主缓冲区仅用于在流程结束时混合两个缓冲区。我认为解决这个问题可以忽略它。有人遇到过这个吗?

视频在这里:https ://photos.app.goo.gl/ThRJHddo2xKke6CN7

此处的屏幕截图:工件的图片

在我的 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
        }
   

}

'''

金属声明

'''
//
//  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){
    
    }
    
}

'''

顶点和片段着色器

'''

#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;
}

'''

4

1 回答 1

0

似乎我能够通过将 .shadeWrite 添加到我的纹理描述符使用数组来解决该问题。

'''textureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite]'''
于 2021-03-09T14:29:36.743 回答