金属反馈回路伪影

问题描述

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

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

此处的视频:https://photos.app.goo.gl/ThRJHddo2xKke6CN7

此处的屏幕截图:Picture of artifact

在我的 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,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),vector_float4(0.1,0.3,0.4)]
    var touchData = vector_float4(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,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,alpha: 1.0)
        
        let renderEncoderA = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptorA)!
        renderEncoderA.label = "BufferA"
        renderEncoderA.setRenderPipelinestate(pipelinestateA)
        renderEncoderA.setVertexBuffer(vertexBuffer,index: 0)//this is where the vertex buffer is loaded
        
        renderEncoderA.setFragmentTexture(textureA,index: 0)
        renderEncoderA.setFragmentTexture(textureB,index: 1)
        
        renderEncoderA.setFragmentBytes(&layoutData,index: 1) //this sends the dimentions to the shader
        renderEncoderA.setFragmentBytes(&habitData,index: 2)
        renderEncoderA.setFragmentBytes(&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,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,index: 0)//this is where the vertex buffer is loaded
        
        renderEncoderB.setFragmentTexture(textureA,index: 0)
        renderEncoderB.setFragmentTexture(textureB,index: 1)
        
        renderEncoderB.setFragmentBytes(&layoutData,index: 1) //this sends the dimentions to the shader
        renderEncoderB.setFragmentBytes(&habitData,index: 2)
        renderEncoderB.setFragmentBytes(&touchData,index: 3)
        renderEncoderB.setFragmentBytes(&RCTDIFData,index: 4)
        renderEncoderB.setFragmentBytes(&currentTime,index: 5)
        
        renderEncoderB.drawPrimitives(type: .triangleStrip,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) ]],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,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 &RCTDIFData [[ buffer(4) ]],constant float &currentTime [[ buffer(5)]],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,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;
}

'''

解决方法

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

'''textureDescriptor.usage = [.renderTarget,.shaderRead,.shaderWrite]'''

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...