【问题标题】:AGAL seq opcode works on hardware, but doesn't on software emulation (float number comparison is different on both?)AGAL seq 操作码适用于硬件,但不适用于软件仿真(两者的浮点数比较不同?)
【发布时间】:2025-12-02 04:40:01
【问题描述】:

来自文档:seq set-if-equal destination = source1 == source2 ? 1 : 0,逐个组件

我还没有彻底测试它,但到目前为止,我的片段着色器在两台机器(台式电脑)上都工作,其中 context3D 初始化成功作为 DirectX,但不适用于闪存回退到软件渲染的机器。

seq ft2.x, ft0.x, fc0.x

ft.x在硬件上设置为1,当当前像素红色值,存储在ft0.x等于常量fc0.x,它存储50/255。所以我想要发生的事情确实发生在硬件上的#32???? (50 == 0x32) 彩色像素上,但不会发生在软件上。

我已经测试了一种解决方法,我可以用更复杂的算法替换 seq 操作码,该算法涉及slt(如果小于则设置)或sge(如果大于或等于则设置)。

因此,问题似乎在于我提供给 GPU 的常数 (50/255) 与实际红色值(纹理中为 50)的比较。如果是其他内容(例如 RGBA 值的顺序不同),sltsge 也会失败。

我在这里做错了吗?我是否应该以某种方式对比较值进行舍入(例如,乘以 255 然后删除小数)以确保它适用于所有设备和模式?

更新: 其中一台具有软件渲染后备功能的机器设置为 16 位图形,但将其更改为 32 位并不能解决问题。我还盲目尝试将颜色值除以 256、128 和 127 而不是 255,希望如果浮点数具有不同的精度(并且更高和更低的数字只要它们等于256px 长渐变内的像素之一),但我的希望没有得到回报。

然后我尝试了将常量存储为整数的解决方法,并在着色器内部将值乘以 255 并删除小数,令我惊讶的是,虽然它在 GPU 上工作,但在软件渲染上却失败了:

mul ft0.x, ft0.x, fc0.yft0.x(红色通道)乘以常数 255

将其转换为整数

frc ft4.x, ft0.x 得到一个小数

sub ft0.x, ft0.x, ft4.x去掉小数,截断整数

现在进行比较,例如seq ft2.x, ft0.x, fc0.x

add ft0.x, ft0.x, ft4.x后面加小数,这一步可能不需要

div ft0.x, ft0.x, fc0.y 将整数值除以 255 以将其转换回浮点数(我的意思是 0..1 范围内的数字)

接下来我要尝试的解决方法是简单地进行一系列小于比较,将临时寄存器设置为 1,然后将其添加到另一个临时寄存器(计数器),以便通过检查我可以看到该值在哪个范围内的计数器。

【问题讨论】:

    标签: shader fragment-shader stage3d agal


    【解决方案1】:

    这是最终对我有用的解决方法。

    我在红色 Alpha 通道上有 4 种颜色,它们告诉着色器要做什么。如果红色值为 50,着色器将左像素作为源,如果为 100,它将采用顶部像素,依此类推。所以我所要做的就是 4 个seq 命令来为寄存器的 4 个组件设置 0 或 1 个偏移量,我以后可以使用采样器的位置从寄存器中添加或删除。

    因为seq 未能将第一次采样的像素的红色值与提供的常数进行比较,所以我制作了一个设置如果大于或等于操作码的“阶梯”:

    "mov ft3.x, fc0.x \n" + //ft3 = 49/0xFF
    
    "sge ft2.x, ft0.x, ft3.x \n" + //if red > 49/0xFF, set 1 to ft2.x
    
    "add ft3.x, ft3.x, fc0.x \n" + //ft3 = 98/0xFF
    "sge ft4.x, ft0.x, ft3.x \n" + //if red > 98/0xFF, set 1 to ft4.x
    "add ft2.x, ft2.x, ft4.x \n" + //if 49 < red < 98, ft2.x = 1, if red > 98, ft2.x = 2
    
    "add ft3.x, ft3.x, fc0.x \n" + //ft3 = 147/0xFF
    "sge ft4.x, ft0.x, ft3.x \n" + //if red > 147/0xFF, set 1 to ft4.x
    "add ft2.x, ft2.x, ft4.x \n" + //if 49 < red < 98, ft2.x = 1, if 98 < red < 147, ft2.x = 2, if red > 147, ft2.x = 3
    
    "add ft3.x, ft3.x, fc0.x \n" + //ft3 = 196/0xFF
    "sge ft4.x, ft0.x, ft3.x \n" + //if red > 196/0xFF, set 1 to ft4.x
    "add ft2.x, ft2.x, ft4.x \n" + //ft2.x is between 0 and 4 including, where 0 means no control color
    

    现在我有一个寄存器 ft2 存储:

    0 for red below 49(实际上所有这些红色值都除以 255,如上面代码中的 cmets 所示)

    1 代表4998 之间的红色

    2 代表98147 之间的红色

    3 代表147196 之间的红色

    4 上面的红色为196

    然后,我不会将像素颜色与常数进行比较,而是将ft2.x 计数器状态与常数进行比较(常数将是 1,2,3,4 而不是 50,100,150,200)。

    不幸的是,这意味着上面的整个代码是额外的开销,我可以节省 GPU,但在 CPU 上无法避免,除非我能找到seq 操作码在比较像素颜色时总是在 CPU 上返回 0 的解决方案和一个常数。

    【讨论】:

    • 请注意:虽然 AS3 文档说 GPU 渲染的软件模拟仍然比标准(旧)CPU 渲染更快,但这可能意味着仅渲染矢量,同时通过循环手动逐像素操作 bitmapData通过 getPixel 和 setPixel 比使用 AGAL 着色器快得多。