繁体   English   中英

使用太多制服时着色器无法编译

[英]Shader fails to compile when using too many uniforms

我会先说这是我第一次接触着色器代码,所以我确信我的措辞会有很多错误。

我正在开发 SpriteKit 游戏,希望能够生成精灵而不是手动创建/更改/添加资产。 我的想法是拥有一个“基础”资产,记下资产中存在的所有不同的 colors,然后以编程方式创建代表资产的 colors 和我想替换它们的 colors 的制服。 从那里,我的着色器可以将每个 rgb 值与代表资产 colors 的制服进行比较,并确定用哪种颜色替换它。 本质上,我正在构建从旧 colors 到新 colors 的映射并执行颜色交换。

只要我在“映射”中使用的制服少于 30 件,这就可以正常工作。 当我超过 30 时,出现以下错误(注意“u_base_”和“u_new_”名称是我生成的制服):

program_source:49:1883: error: 'buffer' 属性参数超出范围:必须介于 0 和 30 片段之间 float4 SKShader_FragFunc( texture2d u_texture [[texture(0)]], const device float *u_time [[buffer(0) ]], const device float *u_path_length [[buffer(1)]], const device float4 * u_base_skinDark [[buffer(2)]], const device float4 * u_base_eyesColor [[buffer(3)]], const device float4 * u_new_shirtMediumLight [[buffer(4)]],const device float4 * u_base_eyesDark [[buffer(5)]],const device float4 * u_new_shoes [[buffer(6)]],const device float4 * u_new_hairDark [[buffer(7)]] ,const device float4 * u_new_eyesWhite [[buffer(8)]],const device float4 * u_new_skinMediumDark [[buffer(9)]],const device float4 * u_base_skinMediumLight [[buffer(10)]],const device float2 * u_sprite_size [[ buffer(11)]],const device float4 * u_base_shirtMediumLight [[buffer(12)]],const device float4 * u_new_eyesColor [[buffer(13)]],const device float4 * u_base_shirtDark [[buffer(14)]],const设备 float4 * u_base_shi rtMediumDark [[buffer(15)]],const device float4 * u_new_hairMedium [[buffer(16)]],const device float4 * u_base_eyesBrow [[buffer(17)]],const device float4 * u_base_hairDark [[buffer(18)] ],const device float4 * u_new_shirtMediumDark [[buffer(19)]],const device float4 * u_new_skinDark [[buffer(20)]],const device float4 * u_new_eyesDark [[buffer(21)]],const device float4 * u_new_eyesBrow [ [buffer(22)]],const device float4 * u_new_hairLight [[buffer(23)]],const device float4 * u_new_skinLight [[buffer(24)]],const device float4 * u_base_skinLight [[buffer(25)]], const device float4 * u_new_skinMediumLight [[buffer(26)]],const device float4 * u_base_hairLight [[buffer(27)]],const device float4 * u_base_shoes [[buffer(28)]],const device float4 * u_base_eyesWhite [[buffer (29)]],const device float4 * u_new_shirtDark [[buffer(30)]],const device float4 * u_base_skinMediumDark [[buffer(31)]],const device float4 * u_base_hairMedium [[buffer(32)]],SKShader_VertexOut插值[[stage_in]]) {

我发现几乎没有对错误消息的引用,因此很难找到修复方法。 从那以后,我发现这并不是使用着色器的正确方法,因为这些类型的比较性能不是很好,但我仍然想了解错误。 无论如何要增加这个缓冲区的大小? 这只是 SpriteKit 的限制吗? 另外,使用“交换纹理”来完成这种颜色交换的唯一方法是什么?

我无法回答您有关 SpriteKit 的具体问题,但我在处理 Metal 项目时遇到了同样的错误。

根据您的硬件,您可以通过使用参数缓冲区来使用超过 31 个缓冲区(即您遇到的限制),例如对于第 1 层(来自about_argument_buffers ):

第 1 层限制

以下资源限制定义为参数缓冲区内设置的资源的最大组合数和单独设置,每个图形或计算 function。例如,如果 kernel function 使用 4 个单独的纹理和一个具有 8 个纹理的参数缓冲区,则总数kernel function 的纹理数是 12。

在 iOS 和 tvOS 中,每个 function 参数表中的最大条目为:

31 个缓冲区(在 A11 和更高版本上,96 个缓冲区)

31 种纹理*(在 A11 和更高版本上,96 种纹理)

16个采样器

*参数缓冲区不支持可写纹理。

在 macOS 中,每个 function 参数表中的最大条目为:

64个缓冲区

128 种纹理

16个采样器

例如,对于 Tier 1 macOS(例如在 M1 上),您应该能够通过使用参数缓冲区来使用总共 64 个缓冲区。

示例.metal着色器代码:

#include <metal_stdlib>
using namespace metal;

struct DataBuffers {
    const device float* buffer0 [[ id(0) ]];
    const device float* buffer1 [[ id(1) ]];
    const device float* buffer2 [[ id(2) ]];
    const device float* buffer3 [[ id(3) ]];
    const device float* buffer4 [[ id(4) ]];
    const device float* buffer5 [[ id(5) ]];
    const device float* buffer6 [[ id(6) ]];
    const device float* buffer7 [[ id(7) ]];
    const device float* buffer8 [[ id(8) ]];
    const device float* buffer9 [[ id(9) ]];
    const device float* buffer10 [[ id(10) ]];
    const device float* buffer11 [[ id(11) ]];
    const device float* buffer12 [[ id(12) ]];
    const device float* buffer13 [[ id(13) ]];
    const device float* buffer14 [[ id(14) ]];
    const device float* buffer15 [[ id(15) ]];
    const device float* buffer16 [[ id(16) ]];
    const device float* buffer17 [[ id(17) ]];
    const device float* buffer18 [[ id(18) ]];
    const device float* buffer19 [[ id(19) ]];
    const device float* buffer20 [[ id(20) ]];
    const device float* buffer21 [[ id(21) ]];
    const device float* buffer22 [[ id(22) ]];
    const device float* buffer23 [[ id(23) ]];
    const device float* buffer24 [[ id(24) ]];
    const device float* buffer25 [[ id(25) ]];
    const device float* buffer26 [[ id(26) ]];
    const device float* buffer27 [[ id(27) ]];
    const device float* buffer28 [[ id(28) ]];
    const device float* buffer29 [[ id(29) ]];
    const device float* buffer30 [[ id(30) ]];
    const device float* buffer31 [[ id(31) ]];
    const device float* buffer32 [[ id(32) ]];
    const device float* buffer33 [[ id(33) ]];
    const device float* buffer34 [[ id(34) ]];
    const device float* buffer35 [[ id(35) ]];
    const device float* buffer36 [[ id(36) ]];
    const device float* buffer37 [[ id(37) ]];
    const device float* buffer38 [[ id(38) ]];
    const device float* buffer39 [[ id(39) ]];
};

kernel void argument_buffer_test(
    device float* out [[ buffer(0) ]],
    const device DataBuffers& data_buffers [[ buffer(1) ]],
    uint id [[ thread_position_in_grid ]]
) {
    // Simply copy one of the input buffers.
    out[id] = data_buffers.buffer11[id];
}

对应.swift文件

import Foundation
import MetalKit

// Number of samples.
let N = 10
// Number of buffers in the struct as defined in the .metal file.
let structBuffers = 40

// Setup.
let device = MTLCreateSystemDefaultDevice()!
let commandQueue = device.makeCommandQueue()!
let library = device.makeDefaultLibrary()!

let function = library.makeFunction(name: "argument_buffer_test")!
let pipeline = try! device.makeComputePipelineState(function: function)

let threadsPerThreadgroup = MTLSize(width: pipeline.threadExecutionWidth, height: 1, depth: 1)

let argumentEncoder = function.makeArgumentEncoder(bufferIndex: 1)
let argumentBuffer = device.makeBuffer(length: argumentEncoder.encodedLength, options: [])
argumentEncoder.setArgumentBuffer(argumentBuffer, offset: 0)

// Dummy data.
var inputBuffer: MTLBuffer
var inputBuffers = [MTLBuffer]()
var dummyData: [CFloat]
for i in 0..<structBuffers {
    dummyData = Array<CFloat>(repeating: Float(i), count: N)
    inputBuffer = device.makeBuffer(
        bytes: &dummyData,
        length: MemoryLayout<CFloat>.stride * dummyData.count,
        options: []
    )!
    inputBuffers.append(inputBuffer)
    argumentEncoder.setBuffer(inputBuffer, offset: 0, index: i)
}

let outputBuffer = device.makeBuffer(length: MemoryLayout<CFloat>.stride * N, options: [])!

// Encode commands.
let commandBuffer = commandQueue.makeCommandBuffer()!
let encoder = commandBuffer.makeComputeCommandEncoder()!
encoder.setComputePipelineState(pipeline)

encoder.setBuffer(outputBuffer, offset: 0, index: 0)
// The argument buffer is represented as a single buffer here,
// with references to the individual input buffers created above.
encoder.setBuffer(argumentBuffer, offset: 0, index: 1)
// When using argument buffers, the underlying buffers have to be marked like so
for i in 0..<inputBuffers.count {
    encoder.useResource(inputBuffers[i], usage: .read)
}

let threadsPerGrid = MTLSize(width: N, height: 1, depth: 1)

encoder.dispatchThreads(threadsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
encoder.endEncoding()

commandBuffer.commit()
commandBuffer.waitUntilCompleted()

// Print output.
print(Array(
    UnsafeBufferPointer<CFloat>(
        start: outputBuffer.contents().bindMemory(to: CFloat.self, capacity: N),
        count: N
    )
))

另一个很好的资源是Metal by Tutorials的第 26 章: https://github.com/raywenderlich/met-materials/tree/editions/3.0

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM