【问题标题】:How do I cast a float pointer to a uint8_t pointer in Metal Shader Language?如何在 Metal Shader Language 中将浮点指针转换为 uint8_t 指针?
【发布时间】:2021-06-15 16:58:49
【问题描述】:

我正在尝试在 kernel void 函数内用 Metal Shading Language 编写以下 C 代码:

float f = 2.1;
uint8_t byteArray[sizeof(f)];
for (int a = 0; a < sizeof(f); a++) {
    byteArray[a] = ((uint8_t*)&f)[a];
}

代码应该得到一个浮点值的字节数组。当我尝试用 Metal Shader Language 编写相同的代码时,我收到以下构建时错误:

指针类型必须有明确的地址空间限定符

我了解 Metal 限制指针的使用,并要求内核函数的参数提供明确的地址空间属性,例如 deviceconstant 等。我将如何在 Metal 中执行类型转换着色语言?

【问题讨论】:

    标签: c pointers metal uint8t


    【解决方案1】:

    这不是 C++,所以你必须用另一种方式来做。

    您不能在 MSL 中正确使用联合或 reinterpret_cast

    相反,有一个类型为 4 uint8_ts 的向量,它是 uchar4

    要做你想做的事,你可以这样写。

    float f = 2.1;
    uchar4 ff = as_type<uchar4>(f);
    

    请参阅 MSL spec,第 2.19 节类型转换和重新解释数据。

    关于地址空间限定符:

    Metal Shading Language 中的每个指针都有一个地址限定符。它可以是deviceconstantthreadthreadgroup 等。请参阅规范中的第 4 章。

    这些地址限定符来自不同的内存空间这一事实。您可以在上面的文档中阅读有关它们的更多信息。

    由于f 是局部变量,它位于thread 空间中,因此您的uint8_t 指针的类型将是thread uint8_t* 而不仅仅是uint8_t

    所以你可能会这样做:

    float f = 2.1;
    thread uint8_t* ff = (thread uint8_t*)&f;
    

    但我认为as_type 方法更清晰。

    【讨论】:

      【解决方案2】:

      具体不知道metal,但在普通C语言中,你会想把fbyteArray放在union

      这里有一些示例代码:

      #include <stdio.h>
      #include <stdint.h>
      
      union float_byte {
          float f;
          uint8_t byteArray[sizeof(float)];
      };
      
      union float_byte u;
      
      void
      dotest(float f)
      {
      
          u.f = f;
      
          printf("%.6f",u.f);
          for (int idx = 0;  idx < sizeof(u.byteArray);  ++idx)
              printf(" %2.2X",u.byteArray[idx]);
          printf("\n");
      }
      
      int
      main(void)
      {
      
          dotest(2.1);
          dotest(7.6328);
      
          return 0;
      }
      

      这是程序输出:

      2.100000 66 66 06 40
      7.632800 E6 3F F4 40
      

      更新:

      即使在今天,从技术上讲,阅读一个不是最后一个写给工会成员的工会成员不仍然是 UB 吗?虽然,这听起来现在得到了实现定义的行为的广泛支持。许多相关问题之一:stackoverflow.com/questions/2310483/… – yano

      不,出于多种原因,它不是 UB。

      可能是“实现定义的”行为,但 只是因为 CPU/处理器字节序重新。内存中 [32 位] float 的格式,如果我们希望解释 byteArray 中的字节

      但是,AFAICT,这不会影响 OP 的问题,因为这只是为了获得一个字节缓冲区 [用于数据的二进制/序列化?]。

      如果想要解释数据(例如设计 DIY F.P. S/W 实现),则必须知道 float 的格式和字节序。这 [可能] 是 IEEE 784 格式和处理器字节序。

      但是,使用union 只是得到一个字节指针,没有问题。这不是甚至是“实现定义”的行为。

      差不多就是这样:

      float my_f = f;
      uint8_t *byteArray = (uint8_t *) &my_f;
      

      而且,它之所以起作用,是因为它必须起作用。

      另外,union [as used here] 是一个常见的习惯用法,可以追溯到 1970 年代,因此它必须得到支持。

      而且,它只是工作 [因为它必须按照设计]。

      如果我们有:

      void
      funcA(float f)
      {
      
          u.f = f;
      }
      
      void
      funcB(void)
      {
          for (int idx = 0;  idx < sizeof(u.byteArray);  ++idx)
              printf(" %2.2X",u.byteArray[idx]);
          printf("\n");
      }
      

      为了好玩,假设 funcAfuncB 在单独的 .c 文件中。当funcA 被调用时,它会改变u 的内存[以一种可预测的方式]。

      两者之间没有任何改变u的布局。

      然后,我们调用funcBbyteArray 中的字节布局将是相同/可预测的数据。

      这与将float 作为二进制 数据写入文件类似且工作方式相同:

      #include <unistd.h>
      
      int fd;
      
      void
      writefloat(float f)
      {
          float my_f = f;
      
          write(fd,&my_f,sizeof(float));
      }
      
      void
      writefloat2(float f)
      {
      
          write(fd,&f,sizeof(float));
      }
      
      void
      writefloat3(float f)
      {
      
          write(fd,&f,sizeof(f));
      }
      

      如果我们使用uint32_t 而不是float,也许这会更容易看出。我们可以做一个字节序测试。 [注意:这是粗略的,不考虑像 pdp11/vax 这样的古怪字节序]:

      #include <stdio.h>
      #include <stdint.h>
      
      union uint_byte {
          uint32_t i;
          uint8_t b[sizeof(uint32_t)];
      };
      
      union uint_byte u;
      
      int
      main(void)
      {
      
          u.i = 0x01020304;
          if (u.b[0] == 0x04)
              printf("cpu is little-endian\n");
          else
              printf("cpu is big-endian\n");
      
          return 0;
      }
      

      【讨论】:

      • 即使在今天,从技术上讲,阅读一个不是最后一个写信的工会成员不是仍然是 UB 吗?虽然,这听起来现在得到了实现定义的行为的广泛支持。许多相关问题之一:stackoverflow.com/questions/2310483/…
      • @yano 我在回答中添加了更新以解决您的问题
      【解决方案3】:

      在 c++ (20) 中,您有 bit_cast。 在“注释”部分中,您将看到,它只是作为对 memcpy 的调用实现的。

      所以,为了避免“未定义的行为”并确保安全(除了前面所有的,它也是最快的),只需这样做:

      memcpy(byteArray, &f, sizeof f); //size of byteArray must be at least: sizeof f
      

      【讨论】:

      • 谢谢,但这仅适用于 C++,不适用于 Metal。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多