简体   繁体   English

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

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

I'll preface this by saying that this is my first time ever touching shader code so I'm sure there will be plenty wrong with my wording.我会先说这是我第一次接触着色器代码,所以我确信我的措辞会有很多错误。

I'm working on a SpriteKit game and want to be able to generate sprites instead of manually creating/changing/adding assets.我正在开发 SpriteKit 游戏,希望能够生成精灵而不是手动创建/更改/添加资产。 My idea was to have a "base" asset, make note of all the different colors that exist within the asset, then programmatically create uniforms representing both the asset's colors and the colors that I want to replace them with.我的想法是拥有一个“基础”资产,记下资产中存在的所有不同的 colors,然后以编程方式创建代表资产的 colors 和我想替换它们的 colors 的制服。 From there, my shader could compare each rgb value with the uniforms representing the asset's colors and determine which color to replace it with.从那里,我的着色器可以将每个 rgb 值与代表资产 colors 的制服进行比较,并确定用哪种颜色替换它。 Essentially, I'm building a mapping from old colors to new colors and performing a color swap.本质上,我正在构建从旧 colors 到新 colors 的映射并执行颜色交换。

This works fine, as long as I use less than 30 uniforms in my "mapping."只要我在“映射”中使用的制服少于 30 件,这就可以正常工作。 When I exceed 30, I get the following error (note that the "u_base_" and "u_new_" names are my generated uniforms):当我超过 30 时,出现以下错误(注意“u_base_”和“u_new_”名称是我生成的制服):

program_source:49:1883: error: 'buffer' attribute parameter is out of bounds: must be between 0 and 30 fragment 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 device float4 * u_base_shi 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 interpolated [[stage_in]]) { 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]]) {

I've found almost no references to the error message so finding a fix has been difficult.我发现几乎没有对错误消息的引用,因此很难找到修复方法。 I've since discovered that this isn't really the right way to use shaders since these types of comparisons aren't very performant, but I'd still like to understand the error.从那以后,我发现这并不是使用着色器的正确方法,因为这些类型的比较性能不是很好,但我仍然想了解错误。 Is there anyway to increase the size of this buffer?无论如何要增加这个缓冲区的大小? Is this just a limitation of SpriteKit?这只是 SpriteKit 的限制吗? Also, is the only way to accomplish this color swap by using a "swap texture"?另外,使用“交换纹理”来完成这种颜色交换的唯一方法是什么?

I won't be able to answer your specific questions regarding SpriteKit, but I have encountered the same error while working on a Metal project.我无法回答您有关 SpriteKit 的具体问题,但我在处理 Metal 项目时遇到了同样的错误。

Depending on your hardware you may be able to use more than 31 buffers (ie the limit you are running into) by utilising argument buffers, eg for Tier 1 (from about_argument_buffers ):根据您的硬件,您可以通过使用参数缓冲区来使用超过 31 个缓冲区(即您遇到的限制),例如对于第 1 层(来自about_argument_buffers ):

Tier 1 Limits第 1 层限制

The following resource limits are defined as the maximum combined number of resources set within an argument buffer and set individually, per graphics or compute function. For example, if a kernel function uses 4 individual textures and one argument buffer with 8 textures, the total number of textures for that kernel function is 12.以下资源限制定义为参数缓冲区内设置的资源的最大组合数和单独设置,每个图形或计算 function。例如,如果 kernel function 使用 4 个单独的纹理和一个具有 8 个纹理的参数缓冲区,则总数kernel function 的纹理数是 12。

In iOS and tvOS, the maximum entries in each function argument table are:在 iOS 和 tvOS 中,每个 function 参数表中的最大条目为:

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

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

16 samplers 16个采样器

*Writable textures are not supported within an argument buffer. *参数缓冲区不支持可写纹理。

In macOS, the maximum entries in each function argument table are:在 macOS 中,每个 function 参数表中的最大条目为:

64 buffers 64个缓冲区

128 textures 128 种纹理

16 samplers 16个采样器

For Tier 1 macOS for example (eg on an M1), you should be able to use a total of 64 buffers by utilising argument buffers.例如,对于 Tier 1 macOS(例如在 M1 上),您应该能够通过使用参数缓冲区来使用总共 64 个缓冲区。

Example .metal shader code:示例.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];
}

Corresponding .swift file对应.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
    )
))

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

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

相关问题 Xamarin OpenGL片段着色器使用制服时的行为异常 - Xamarin OpenGL fragment shader weird behaviour when working with uniforms 尝试将制服传递给金属着色器时,“SceneKit:错误,缺少缓冲区[-1/2]” - “SceneKit: error, missing buffer [-1/2]” when trying to pass uniforms to a metal shader 无法在swift中编译着色器 - Failed to compile shader in swift 发出过多 AFNetworking 请求时超时 - Timeout when issuing too many AFNetworking requests 在iOS 7 iPhone中使用sizeWithAtrributes时,方法调用错误的参数过多 - Too many arguments to method call error when using sizeWithAtrributes in ios 7 iPhone iOS 6 Map是否使用太多TCP连接? - iOS 6 Map is using too many TCP connections? 金属片段着色器 - 如何根据可以通过绘制功能调整的制服为每个像素分配颜色? - Metal fragment shader - How do I assign each pixel a color based on the uniforms that I can adjust via the draw function? 提交应用程序时出现“符号文件过多”警告 - "Too many symbol files" warning when submitting app 当选择太多图像时,ELC图像选择器出现问题iPhone - Issue with ELC Image Picker when too many images are selected iPhone 创建的AVPlayer实例太多时AVPlayerItemStatusFailed错误 - AVPlayerItemStatusFailed error when too many AVPlayer instances created
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM