我正在尝试为某种图形软件创建一个“无限网格”,类似于 Desmos 或 Geogebra 中的网格。我希望使用金属渲染视图。我的问题是,虽然代码看起来工作正常,但它经常闪烁。
如您所见,在第一部分中表现正常,然后开始“闪烁”
请注意,当网格缩放时,由
scrollWheel
事件触发网格重绘时,不会发生这种情况。
draw()
函数中,我计算视图内应该可见的点数drawPrimitives
函数,其中 instanceCount
参数等于应可见的点数instance_id
属性计算每个图元的位置我认为问题应该与视图处理调整大小的方式有关,因为当缩放事件触发重绘时,一切都会按预期工作。另外,不要认为这是性能问题,因为 fps 非常高且稳定。
我尝试弄乱
isPaused
和 enableSetNeedsDisplay
属性,但我无法实现 isPaused = true
和 enableSetNeedsDisplay = false
的版本。
最让我困惑的部分是,这似乎是随机发生的,但同样,在我的情况下,性能似乎不是问题......
如何在调整窗口大小时避免烦人的闪烁效果?
这是我的视图类:
import AppKit
import MetalKit
class MetalView : MTKView {
var queue : MTLCommandQueue!
var pipelineState : MTLRenderPipelineState!
private var spacing : Float = 10.0
private var xoffset : Float = 0
private var yoffset : Float = 0
required init(coder: NSCoder) {
super.init(coder: coder)
self.device = MTLCreateSystemDefaultDevice()
self.queue = device?.makeCommandQueue()
self.clearColor = MTLClearColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
self.colorPixelFormat = .bgra8Unorm
self.isPaused = true; self.enableSetNeedsDisplay = true
makePipeline()
print("costructor")
}
override func draw(_ dirtyRect: NSRect) {
drawGrid()
}
private func drawGrid(){
guard let drawable = self.currentDrawable,
let passDescriptor = self.currentRenderPassDescriptor,
let commandBuffer = queue.makeCommandBuffer(),
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor)
else {
print("errore")
return
}
let infos = [Float(bounds.width), Float(bounds.height), xoffset, yoffset, spacing]
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBytes(infos, length: MemoryLayout<Float>.size * infos.count, index: 0)
renderEncoder.drawPrimitives(type: .point, vertexStart: 0, vertexCount: 1,
instanceCount: Int(
(floor((infos[0] - xoffset) / spacing)+1) *
(floor((infos[1] - yoffset) / spacing)+1)))
renderEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
commandBuffer.waitUntilCompleted()
}
private func makePipeline() {
guard let library = device?.makeDefaultLibrary() else {print("errore makePipeline"); return}
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = library.makeFunction(name: "vertex_function")
pipelineStateDescriptor.fragmentFunction = library.makeFunction(name: "fragment_function")
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
do {
pipelineState = try device?.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
}
catch let error as NSError{
print("Errore nella creazione della pipeline")
print(error)
}
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
这是我的着色器文件:
#include <metal_stdlib>
using namespace metal;
struct Fragment{
float4 position [[position]];
float pointSize [[point_size]] = 5.0f;
float4 color;
};
vertex Fragment vertex_function (unsigned int instanceID [[instance_id]],
constant float *parameters [[buffer(0)]])
{
const float width = parameters[0];
const float height = parameters[1];
const float xoffset = parameters[2];
const float yoffset = parameters[3];
const float spacing = parameters[4];
int xdots = floor((width - xoffset)/spacing) + 1;
float non_normalized_x = xoffset + (instanceID % xdots) * spacing;
float non_normalized_y = yoffset + (instanceID / xdots) * spacing;
float normalized_x = non_normalized_x / width * 2 - 1;
float normalized_y = non_normalized_y / height * 2 - 1;
Fragment f;
f.position = float4(normalized_x, normalized_y, 0, 1);
return f;
}
fragment float4 fragment_function(Fragment in [[stage_in]])
{
return float4(0.8,0.8,0.8,1);
}