- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
具有 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 游戏。但是,我想突破界限,以便在游戏中使用更好的效果。重申一下这个问题,以防我还没有说清楚,我想知道两个与性能相关的主要问题。
感谢并抱歉发了这么长的帖子! :)
最佳答案
只是一些想法......
我会使用仪器来了解您在游戏循环中花费最多时间的是什么。但是,“Time Profiler”可能无法在 GPU 方面为您提供太多帮助。
查看 XCode 中的 GPU 报告,它应该会显示 GPU 和 CPU 在每一帧上花费了多少时间。如果 GPU 已经徘徊在 16 毫秒附近,则没有必要将更多工作转移到 GPU。
看看用 memory buffer that is shared across the GPU and CPU 替换 memcpy
.这样,您只需在 Swift 中写入数组,GPU 就可以使用它,而无需复制内存。
您可以在 Metal 计算着色器中重写 SpriteBatch.submit(sprite)
,但如果您只执行几千次,该方法在计算上似乎并不昂贵次。输出 MTLBuffer
将包含所有可以直接送入渲染编码器的 spritesData
。不过,您仍然需要将输入数据从 CPU 获取到 GPU(计算)。
您的第 1 点很有趣。我不认为你想在 CPU 上转换顶点,但这可能是计算着色器的一个很好的候选者。这类似于我刚才做的 boid 模拟。 Metal 计算着色器更新每个 boid 位置和速度,它还创建每个 boid 转换矩阵,然后用于转换构成 boid 视觉表示的 6 个顶点位置(用 2 个三角形绘制的简单鱼)。我的场景是在 SceneKit 中构建的,因此使用实例化绘制调用并不是一个真正的选择。
关于iOS Metal Spritebatch - 更新顶点 VS 更新制服,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34213822/
在 Metal 中,在着色器内部(进出)使用什么坐标系?当我们渲染到纹理时是一样的吗?也有z缓冲区?有没有不一致的地方?最后 Metal ,opengl和directX有什么区别? 最佳答案 Meta
我正在尝试在 Mac 上的 Apple metal 中开发我自己的迷你游戏引擎,但我被困在我想在 GPU 上渲染文本的地方。我没有太多的图形编程经验,因此我不知道该怎么做。我偶然发现了 Warren
我找不到答案的简单问题,在 openGL 上有一个 glDeleteTextures(1, &t) 显然模型有很大的不同,但我想知道 Metal 是否有相同的需要或要求。 MTLTexture 是通过
我是 Metal 新手。我想使用 Metal 计算来做一些数学运算,所以我创建了一个内核函数(着色器?),比方说 kernel void foo(device float *data1,
我假设除了 Metal 之外的其他 API 中存在颜色附件(我肯定知道 OpenGL),但我是图形编程的新手,我想知道颜色附件在概念上到底是什么。我所做的所有绘图都涉及在颜色附件数组中的第一个设置属性
在计算着色器中,我可以看到双三次是一个选项,但前提是定义了 __HAVE_BICUBIC_FILTERING__。当我将 bicubic 设置为过滤选项时,出现语法错误。 Linear 或Neares
这是一个绝对的初学者问题。 背景:我并不是真正的游戏开发者,但我正在努力学习底层 3D 编程的基础知识,因为这是一个有趣且有趣的话题。我选择了 Apple 的 Metal 作为图形框架。我知道 Sce
在 GLSL 中,我只需使用 out vec3 array[10]; 将数组从顶点着色器传递到片段着色器。然而,在 Metal 中,我想这样做: struct FragmentIn { flo
我看到 Apple GPU 硬件和 iOS/MacOS 版本的组合决定了一个功能集。我可以使用下面的快速代码片段查询我的 MTLDevice 支持哪些功能集。 device.supportsFeatu
我想将深度缓冲区保存到 Metal 纹理中,但我尝试过的任何方法似乎都不起作用。 _renderPassDesc.colorAttachments[1].clearColor = MTLClearCo
我想在我的 Metal 应用程序中实现一个 A-Buffer 算法来实现与订单无关的透明度。该技术的描述提到使用原子计数器。我从未使用过其中之一,甚至没有听说过。我刚刚阅读了 Metal Shadin
假设我有一个 N channel MPSImage 或基于 MTLTexture 的纹理数组。 我如何从中裁剪一个区域,复制所有 N 个 channel ,但改变“像素大小”? 最佳答案 我将只讨论裁
TL;DR:Metal 似乎没有检测到我的顶点着色器返回的内容 我有这两个用 MSL 编写的函数: vertex float4 base_image_rect(constant float4 *pos
如何在目标设置为 iOS 模拟器的情况下在 Xcode 6 中编译 iOS «Metal» 游戏? error: can't exec 'metal' (No such file or directo
将一些基本的 OpenGL ES 2.0 着色器移植到 Metal 着色器时,我不知道如何将 glsl 中的 in/inout/out 限定符转换为 Metal 着色器语言 (MSL)。例如, //O
我不想使用texture1d_array。我可以简单地传递一个 float 组吗?我将把它写入我的内核函数中。 最佳答案 为了写入内核函数内的 float 组,您需要向内核提供一个缓冲区参数。该参数应
我不想使用texture1d_array。我可以简单地传递一个 float 组吗?我将把它写入我的内核函数中。 最佳答案 为了写入内核函数内的 float 组,您需要向内核提供一个缓冲区参数。该参数应
我有一组 Metal 纹理作为纹理集存储在 Xcode Assets 目录中。我正在使用 MTKTextureLoader.newTexture(name:scaleFactor:bundle:opt
Apple 系统中似乎至少有六个矩阵库。其中之一是 simd 库,其类型在 CPU 和 GPU 代码中的工作方式相同。 import simd let mat = float3x3(...) let
有谁知道 Apple 的旧版本 Metal Feature Set Table 的可用性?文件? 当前的 Metal 3.0 文档仅引用 beta MTLGPUFamily 和 MTLSoftware
我是一名优秀的程序员,十分优秀!