Metal - 我不能使用单个缓冲区绘制超过 2048 个点
Metal - I can't draw more than 2048 points using a single buffer
这是我第一次post来这里,所以请原谅任何无意中违反协议或礼节的行为,谢谢!
基本问题:MetalKt 似乎没有画出我要显示的所有线条。
详情:我已经进入学习 Metal Frameworks 的第 3 周(主要是通过 OS X 上的 MetalKit)。到目前为止,我已经设法将一个 MetalView 放在一起,该 MetalView 显示来自磁盘上文件的音频波,并带有一个在播放音频时在屏幕上移动的滑动条。
音频波只是一组代表声级的点,每对点都由一条线连接,最终看起来就像人们在 GarageBand 或 Logic 等中看到的东西。
我遇到的问题是 Metal 没有绘制我认为我要求它绘制的所有点。通过反复试验,我发现它在绘制 2048 个点(计算机编号!)后停止。我可以验证我是否正确地输入了数据——也就是说,我正在收集足够的点来完全绘制波浪,并使用正确的坐标绘制整个波浪,但介于创建缓冲区和要求 Metal 绘制它之间, 它被剪裁到 2048。音频的其余部分没有显示。
所以我想知道我的创作或 Metal 本身是否存在某些缓冲区数据限制,这会导致此问题。我已经通过使用多个缓冲区解决了这个问题,但这感觉就像一个创可贴修复,我不明白原因让我很困扰。
设置相当准系统,没有纹理或缩放(我知道......就像我说的那样我才刚刚开始)
这是我的 类:
// Shaders.metal
#include <metal_stdlib>
using namespace metal;
struct Vertex {
float4 position [[position]];
float4 color;
};
struct Uniforms {
float4x4 modelMatrix;
};
vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]]) {
float4x4 matrix = uniforms.modelMatrix;
Vertex in = vertices[vid];
Vertex out;
out.position = matrix * float4(in.position);
out.color = in.color;
return out;
}
fragment float4 fragment_func(Vertex vert [[stage_in]]) {
return vert.color;
}
这里有一些矩阵实用程序(主要供将来使用,目前返回统一)- 改编自 Marius Horga 的在线 Metal 教程:
// MathUtils.swift
// chapter07
//
// Created by Marius on 3/1/16.
// Copyright © 2016 Marius Horga. All rights reserved.
// adapted for personal use
import simd
struct Vertex {
var position : vector_float4
var color : vector_float4
init(pos: vector_float4, col: vector_float4) {
position = pos
color = col
}
}
struct Matrix {
var m: [Float]
init() {
m = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
}
func modelMatrix(var matrix: Matrix) -> Matrix {
return matrix // for now, just unity
}
}
视图如下:
// MetalView
import MetalKit
public class TesterMetalView: MTKView {
var vert_audio_buffer : MTLBuffer!
var uniform_buffer : MTLBuffer!
var rps : MTLRenderPipelineState! = nil
required public init(coder: NSCoder) {
super.init(coder: coder)
createBuffers()
registerShaders()
}
override public init(frame frameRect: CGRect, device: MTLDevice?) {
super.init(frame: frameRect, device: device)
createBuffers()
registerShaders()
}
override public func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
if let rpd = currentRenderPassDescriptor,
drawable = currentDrawable {
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1.0)
let command_buffer = device!.newCommandQueue().commandBuffer()
let command_encoder = command_buffer.renderCommandEncoderWithDescriptor(rpd)
command_encoder.setRenderPipelineState(rps)
command_encoder.setVertexBuffer(vert_audio_buffer, offset: 0, atIndex: 0)
command_encoder.setVertexBuffer(uniform_buffer, offset: 0, atIndex: 1)
let numVerts = (vert_audio_buffer.length / sizeof(Vertex))
command_encoder.drawPrimitives(.Line, vertexStart: 0, vertexCount: numVerts)
command_encoder.endEncoding()
command_buffer.presentDrawable(drawable)
command_buffer.commit()
}
}
func createBuffers() {
if device == nil { self.device = MTLCreateSystemDefaultDevice() }
// rotation + scaling
uniform_buffer = device!.newBufferWithLength(sizeof(Float) * 16, options: [])
let bufferPointer = uniform_buffer.contents()
memcpy(bufferPointer, Matrix().modelMatrix(Matrix()).m, sizeof(Float) * 16)
}
func registerShaders() {
if device == nil { self.device = MTLCreateSystemDefaultDevice() }
if let library = device!.newDefaultLibrary() {
let vertex_func = library.newFunctionWithName("vertex_func")
let frag_func = library.newFunctionWithName("fragment_func")
let rpld = MTLRenderPipelineDescriptor()
rpld.vertexFunction = vertex_func
rpld.fragmentFunction = frag_func
rpld.colorAttachments[0].pixelFormat = .BGRA8Unorm
do {
try rps = device!.newRenderPipelineStateWithDescriptor(rpld)
} catch {
Swift.print("***ERROR: newRenderPipelineStateWithDescriptor failed ...\r\t\(error)")
}
}
}
}
下面是金属缓冲区的创建方式:
// within Metal View Controller
var verts_audio = [Vertex]()
// ... create Vertex's from audio data
// I can confirm verts_audio contains valid data
// however, Metal draws only the first 2048 of them
let bufferLength = sizeof(Vertex) * verts_audio.count
// metalV() gets a typed reference to the view
metalV().vert_audio_buffer = metalV().device!
.newBufferWithBytes(verts_audio,
length : bufferLength,
options : [])
所以,如果我尝试绘制一个顶点包含 2838 个点的波浪,并使用上面的代码将它们全部放在一个缓冲区中,我会得到:
如果我将保存的顶点分布在多个缓冲区中,每个缓冲区包含 2048 个顶点(代码未显示),我会得到完整的波形(较浅的线条显示额外的缓冲区):
我可能在做一些愚蠢或明显的事情。我肯定会感谢比我更聪明的人对此有所了解。谢谢!
我想我今天也一直在为此奋斗。在搜索答案时,我找到了“Is there a size limit to newBufferWithBytes()?”。
其中,答案提到了Apple文档:Metal Feature Set Tables。
在那里它说“常量地址 space 中着色器或计算函数变量的最大内存分配”对于 iOS 设备是无限的,但对于 [=23= 只有 64KB ] X.
所以在顶点着色器中,我相信 "constant Vertex *vertices" 应该是 "device Vertex *vertices" on OS X 以使用设备地址 space.
这是我第一次post来这里,所以请原谅任何无意中违反协议或礼节的行为,谢谢!
基本问题:MetalKt 似乎没有画出我要显示的所有线条。
详情:我已经进入学习 Metal Frameworks 的第 3 周(主要是通过 OS X 上的 MetalKit)。到目前为止,我已经设法将一个 MetalView 放在一起,该 MetalView 显示来自磁盘上文件的音频波,并带有一个在播放音频时在屏幕上移动的滑动条。
音频波只是一组代表声级的点,每对点都由一条线连接,最终看起来就像人们在 GarageBand 或 Logic 等中看到的东西。
我遇到的问题是 Metal 没有绘制我认为我要求它绘制的所有点。通过反复试验,我发现它在绘制 2048 个点(计算机编号!)后停止。我可以验证我是否正确地输入了数据——也就是说,我正在收集足够的点来完全绘制波浪,并使用正确的坐标绘制整个波浪,但介于创建缓冲区和要求 Metal 绘制它之间, 它被剪裁到 2048。音频的其余部分没有显示。
所以我想知道我的创作或 Metal 本身是否存在某些缓冲区数据限制,这会导致此问题。我已经通过使用多个缓冲区解决了这个问题,但这感觉就像一个创可贴修复,我不明白原因让我很困扰。
设置相当准系统,没有纹理或缩放(我知道......就像我说的那样我才刚刚开始)
这是我的 类:
// Shaders.metal
#include <metal_stdlib>
using namespace metal;
struct Vertex {
float4 position [[position]];
float4 color;
};
struct Uniforms {
float4x4 modelMatrix;
};
vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]]) {
float4x4 matrix = uniforms.modelMatrix;
Vertex in = vertices[vid];
Vertex out;
out.position = matrix * float4(in.position);
out.color = in.color;
return out;
}
fragment float4 fragment_func(Vertex vert [[stage_in]]) {
return vert.color;
}
这里有一些矩阵实用程序(主要供将来使用,目前返回统一)- 改编自 Marius Horga 的在线 Metal 教程:
// MathUtils.swift
// chapter07
//
// Created by Marius on 3/1/16.
// Copyright © 2016 Marius Horga. All rights reserved.
// adapted for personal use
import simd
struct Vertex {
var position : vector_float4
var color : vector_float4
init(pos: vector_float4, col: vector_float4) {
position = pos
color = col
}
}
struct Matrix {
var m: [Float]
init() {
m = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
}
func modelMatrix(var matrix: Matrix) -> Matrix {
return matrix // for now, just unity
}
}
视图如下:
// MetalView
import MetalKit
public class TesterMetalView: MTKView {
var vert_audio_buffer : MTLBuffer!
var uniform_buffer : MTLBuffer!
var rps : MTLRenderPipelineState! = nil
required public init(coder: NSCoder) {
super.init(coder: coder)
createBuffers()
registerShaders()
}
override public init(frame frameRect: CGRect, device: MTLDevice?) {
super.init(frame: frameRect, device: device)
createBuffers()
registerShaders()
}
override public func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
if let rpd = currentRenderPassDescriptor,
drawable = currentDrawable {
rpd.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1.0)
let command_buffer = device!.newCommandQueue().commandBuffer()
let command_encoder = command_buffer.renderCommandEncoderWithDescriptor(rpd)
command_encoder.setRenderPipelineState(rps)
command_encoder.setVertexBuffer(vert_audio_buffer, offset: 0, atIndex: 0)
command_encoder.setVertexBuffer(uniform_buffer, offset: 0, atIndex: 1)
let numVerts = (vert_audio_buffer.length / sizeof(Vertex))
command_encoder.drawPrimitives(.Line, vertexStart: 0, vertexCount: numVerts)
command_encoder.endEncoding()
command_buffer.presentDrawable(drawable)
command_buffer.commit()
}
}
func createBuffers() {
if device == nil { self.device = MTLCreateSystemDefaultDevice() }
// rotation + scaling
uniform_buffer = device!.newBufferWithLength(sizeof(Float) * 16, options: [])
let bufferPointer = uniform_buffer.contents()
memcpy(bufferPointer, Matrix().modelMatrix(Matrix()).m, sizeof(Float) * 16)
}
func registerShaders() {
if device == nil { self.device = MTLCreateSystemDefaultDevice() }
if let library = device!.newDefaultLibrary() {
let vertex_func = library.newFunctionWithName("vertex_func")
let frag_func = library.newFunctionWithName("fragment_func")
let rpld = MTLRenderPipelineDescriptor()
rpld.vertexFunction = vertex_func
rpld.fragmentFunction = frag_func
rpld.colorAttachments[0].pixelFormat = .BGRA8Unorm
do {
try rps = device!.newRenderPipelineStateWithDescriptor(rpld)
} catch {
Swift.print("***ERROR: newRenderPipelineStateWithDescriptor failed ...\r\t\(error)")
}
}
}
}
下面是金属缓冲区的创建方式:
// within Metal View Controller
var verts_audio = [Vertex]()
// ... create Vertex's from audio data
// I can confirm verts_audio contains valid data
// however, Metal draws only the first 2048 of them
let bufferLength = sizeof(Vertex) * verts_audio.count
// metalV() gets a typed reference to the view
metalV().vert_audio_buffer = metalV().device!
.newBufferWithBytes(verts_audio,
length : bufferLength,
options : [])
所以,如果我尝试绘制一个顶点包含 2838 个点的波浪,并使用上面的代码将它们全部放在一个缓冲区中,我会得到:
如果我将保存的顶点分布在多个缓冲区中,每个缓冲区包含 2048 个顶点(代码未显示),我会得到完整的波形(较浅的线条显示额外的缓冲区):
我可能在做一些愚蠢或明显的事情。我肯定会感谢比我更聪明的人对此有所了解。谢谢!
我想我今天也一直在为此奋斗。在搜索答案时,我找到了“Is there a size limit to newBufferWithBytes()?”。
其中,答案提到了Apple文档:Metal Feature Set Tables。
在那里它说“常量地址 space 中着色器或计算函数变量的最大内存分配”对于 iOS 设备是无限的,但对于 [=23= 只有 64KB ] X.
所以在顶点着色器中,我相信 "constant Vertex *vertices" 应该是 "device Vertex *vertices" on OS X 以使用设备地址 space.