gpt4 book ai didi

iOS Metal Spritebatch - 更新顶点 VS 更新制服

转载 作者:搜寻专家 更新时间:2023-11-01 05:38:17 25 4
gpt4 key购买 nike

具有 2d 渲染器中级经验的每个人都知道 Sprite 批处理器在图形 API 特定缓冲区内有数据需要更新,我们一直在寻找最快的方法来更新它。现在我陷入了两难境地——对于 Metal 和 Swift,什么是最明智的更新,什么是最明智的更新方式?更具体地说,我应该在将它们发送到 GPU 之前更新顶点(在 CPU 上进行顶点和 tex 坐标变换),还是创建变换矩阵,创建 tex 坐标参数,并将其发送到一个实例化的统一缓冲区中(做GPU 上的顶点和纹理坐标变换)。我目前的做法涉及实例化渲染和一个对齐到 8 字节的巨型制服缓冲区。

静态数据

static let spritesPerBatch: Int = 1024
static var spritesData: [Float] = [Float](count: spritesPerBatch * BufferConstants.SIZE_OF_SPRITE_INSTANCE_UNIFORMS / sizeof(Float), repeatedValue: 0.0)

排队 Sprite 数据

方法:SpriteBatch.begin()

spritesInBatch = 0

方法:SpriteBatch.submit(sprite)

let offset: Int = spritesInBatch * BufferConstants.SIZE_OF_SPRITE_INSTANCE_UNIFORMS / sizeof(Float)
// transform matrix (3x2)
spritesData[offset + 0] = wsx * cosMetaRot * xOrtho
spritesData[offset + 1] = wsx * sinMetaRot * yOrtho
spritesData[offset + 2] = -hsy * sinMetaRot * xOrtho
spritesData[offset + 3] = hsy * cosMetaRot * yOrtho
spritesData[offset + 4] = (tx * cosNegCameraRotation - ty * sinNegCameraRotation) * xOrtho
spritesData[offset + 5] = (tx * sinNegCameraRotation + ty * cosNegCameraRotation) * yOrtho

// tex coords and lengths
spritesData[offset + 6] = sprite.getU()
spritesData[offset + 7] = sprite.getV()
spritesData[offset + 8] = sprite.getUVW()
spritesData[offset + 9] = sprite.getUVH()

// which texture to use out of the 16 that could be bound
spritesData[offset + 10] = Float(targetTextureIDIndex)

spritesInBatch++

将 Sprite 数据复制到制服缓冲区

方法:SpriteBatch.end()

instancedUniformsBuffer = device.newBufferWithLength(length: spritesPerBatch * BufferConstants.SIZE_OF_SPRITE_INSTANCE_UNIFORMS, options: MTLResourceOptions.CPUCacheModeWriteCombined)
instancedUniformsPointer = instancedUniformsBuffer.contents()
memcpy(instancedUniformsPointer, spritesData, instancedUniformsBuffer.length)
Renderer.renderSpriteBatch()

Sprite 批量渲染方法

方法:Renderer.renderSpriteBatch()

Shaders.setShaderProgram(Shaders.SPRITE)

let textureIDs: [TextureID] = SpriteBatch.getTextureIDs()
for (var i: Int = 0; i < textureIDs.count; i++) {
renderEncoder.setFragmentTexture(TextureManager.getTexture(textureIDs[i]).texture, atIndex: i)
}

let instancedUniformsBuffer: MTLBuffer = SpriteBatch.getInstancedUniformsBuffer().buffer
renderEncoder.setVertexBuffer(VertexBuffers.SPRITE.buffer, offset: 0, atIndex: 0)
renderEncoder.setVertexBuffer(instancedUniformsBuffer, offset: 0, atIndex: 1)
renderEncoder.drawIndexedPrimitives(MTLPrimitiveType.Triangle, indexCount: BufferConstants.SPRITE_INDEX_COUNT, indexType: MTLIndexType.UInt16, indexBuffer: IndexBuffers.SPRITE.buffer, indexBufferOffset: 0, instanceCount: SpriteBatch.getSpritesInBatch())

我目前能够在 iPhone 5s 上以 60 fps 的速度获得大约 1400 个大小为 32x64 的 Sprite 和 8 个独立的纹理。我对此非常满意,并且能够用这个数字完成我的 iOS 游戏。但是,我想突破界限,以便在游戏中使用更好的效果。重申一下这个问题,以防我还没有说清楚,我想知道两个与性能相关的主要问题。

  1. 拥有一个更大的顶点缓冲区(相对于我目前的方法:为所有 Sprite 共享一个顶点和索引缓冲区)是否更好,我在其中使用内存副本设置每个顶点的位置和纹理坐标CPU端?这也意味着不使用实例化绘制调用。
  2. 如果没有,是否有更快的方法来准备和复制 Sprite 数据?

感谢并抱歉发了这么长的帖子! :)

最佳答案

只是一些想法......

  1. 我会使用仪器来了解您在游戏循环中花费最多时间的是什么。但是,“Time Profiler”可能无法在 GPU 方面为您提供太多帮助。

  2. 查看 XCode 中的 GPU 报告,它应该会显示 GPU 和 CPU 在每一帧上花费了多少时间。如果 GPU 已经徘徊在 16 毫秒附近,则没有必要将更多工作转移到 GPU。

  3. 看看用 memory buffer that is shared across the GPU and CPU 替换 memcpy .这样,您只需在 Swift 中写入数组,GPU 就可以使用它,而无需复制内存。

  4. 您可以在 Metal 计算着色器中重写 SpriteBatch.submit(sprite),但如果您只执行几千次,该方法在计算上似乎并不昂贵次。输出 MTLBuffer 将包含所有可以直接送入渲染编码器的 spritesData。不过,您仍然需要将输入数据从 CPU 获取到 GPU(计算)。

  5. 您的第 1 点很有趣。我不认为你想在 CPU 上转换顶点,但这可能是计算着色器的一个很好的候选者。这类似于我刚才做的 boid 模拟。 Metal 计算着色器更新每个 boid 位置和速度,它还创建每个 boid 转换矩阵,然后用于转换构成 boid 视觉表示的 6 个顶点位置(用 2 个三角形绘制的简单鱼)。我的场景是在 SceneKit 中构建的,因此使用实例化绘制调用并不是一个真正的选择。

关于iOS Metal Spritebatch - 更新顶点 VS 更新制服,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34213822/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com