如何在C ++中加速平面到打包/交错图形?

时间:2017-07-25 18:53:25

标签: c++ graphics arm inline-assembly

我试图用一个LED矩阵来编程Arduino。我需要在绘制每一行之前准备好数据,但是过程中最内部的循环太慢了。屏幕目前闪烁。循环应该在500us以下完成。 Arduino有一个84MHz的Cortex-M3 ARM处理器。

这是我需要如何重新组合输出位的概念:

5位颜色数据:

R1=12, G1=4, B1=7, R2=0, G2=2, B2=27

下一步是创建一个连续1的32位流。 1的数量由颜色值给出:

r1 = 0b00000000000000000000111111111111
g1 = 0b00000000000000000000000000001111
b1 = 0b00000000000000000000000001111111
r2 = 0b00000000000000000000000000000000
g2 = 0b00000000000000000000000000000011
b2 = 0b00000111111111111111111111111111

最后一步是将每10个像素的第n位(总共30个颜色值)重新组合成32位整数:

pack1 = 0b00 ... 111011
pack2 = 0b00 ... 111011
pack3 = 0b00 ... 111001
pack4 = 0b00 ... 111001
pack5 = 0b00 ... 101001
...

这是代码:

  // In my case scanwidth is 64*2 (64 is the width of the LED matrix and two lines are scanned at once)
  for ( i=0; i<scanwidth/5; i++) { // each run uses 5 upper and 5 lower pixels
      data = *lineptr++; // each int in the line buffer contains 2*15-bit inverted color data (red = 31-red etc.)
      p1uR = 0x7FFFFFFF >> (data >> 26); // pixel 1 of upper line red channel
      p1uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
      p1uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
      p1lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
      p1lG = 0x7FFFFFFF >> (data >> 5  & 0b11111);
      p1lB = 0x7FFFFFFF >> (data  & 0b11111);
      data = *lineptr++;
      p2uR = 0x7FFFFFFF >> (data >> 26);
      p2uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
      p2uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
      p2lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
      p2lG = 0x7FFFFFFF >> (data >> 5  & 0b11111);
      p2lB = 0x7FFFFFFF >> (data  & 0b11111);
      data = *lineptr++;
      p3uR = 0x7FFFFFFF >> (data >> 26);
      p3uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
      p3uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
      p3lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
      p3lG = 0x7FFFFFFF >> (data >> 5  & 0b11111);
      p3lB = 0x7FFFFFFF >> (data  & 0b11111);
      data = *lineptr++;
      p4uR = 0x7FFFFFFF >> (data >> 26);
      p4uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
      p4uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
      p4lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
      p4lG = 0x7FFFFFFF >> (data >> 5  & 0b11111);
      p4lB = 0x7FFFFFFF >> (data  & 0b11111);
      data = *lineptr++;
      p5uR = 0x7FFFFFFF >> (data >> 26);
      p5uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
      p5uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
      p5lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
      p5lG = 0x7FFFFFFF >> (data >> 5  & 0b11111);
      p5lB = 0x7FFFFFFF >> (data  & 0b11111);

      index = i;
      for (j=0; j<31; j++){ // loop over all 30 bits
          index += (scanwidth/5+1);
          scanbuff[index] = (p5uR>>j&1)<<29 | (p5uG>>j&1)<<28 | (p5uB>>j&1)<<27 | (p5lR>>j&1)<<26 | (p5lG>>j&1)<<25 | (p5lB>>j&1)<<24 
                          | (p4uR>>j&1)<<23 | (p4uG>>j&1)<<22 | (p4uB>>j&1)<<21 | (p4lR>>j&1)<<20 | (p4lG>>j&1)<<19 | (p4lB>>j&1)<<18 
                          | (p3uR>>j&1)<<17 | (p3uG>>j&1)<<16 | (p3uB>>j&1)<<15 | (p3lR>>j&1)<<14 | (p3lG>>j&1)<<13 | (p3lB>>j&1)<<12 
                          | (p2uR>>j&1)<<11 | (p2uG>>j&1)<<10 | (p2uB>>j&1)<<9  | (p2lR>>j&1)<<8  | (p2lG>>j&1)<<7  | (p2lB>>j&1)<<6 
                          | (p1uR>>j&1)<<5  | (p1uG>>j&1)<<4  | (p1uB>>j&1)<<3  | (p1lR>>j&1)<<2  | (p1lG>>j&1)<<1  | (p1lB>>j&1);
         }
     }

我认为改善外循环是不必要的。我确实试图展开内循环,但它没有明显改善。

Cortex-M3可以在一个时钟周期内完成移位和逻辑运算。我估计外环和内环需要大约51000个时钟周期(600us)。

我可以用标准C ++代码改进吗?在内联汇编中是否可以进行任何改进?

1 个答案:

答案 0 :(得分:6)

一些Cortex-M 3黑魔法的时间。

#include <cstdint>
#include <memory>
#include <cstring>

volatile char *const bitband_packed = (volatile char*)0x20000000;
volatile uint32_t *const bitband_exploded = (volatile uint32_t*)0x22000000;

static inline void transform_32_32(uint32_t buff[32]) {
    const std::size_t size = sizeof(buff[0])*32;
    volatile char *const tmp = bitband_packed;
    std::memcpy(const_cast<char*>(tmp), buff, size);
    for(std::size_t i = 0; i < 32; i++) {
        for(std::size_t j = i + 1; j < 32; j++) {
            std::swap(bitband_exploded[(32 * i + j)], bitband_exploded[(32 * j + i)]);
        }
    }
    std::memcpy(buff, const_cast<char*>(tmp), size);
}

void transform_pwm_32channel_5bit(const uint8_t input[32], uint32_t output[32]) {
    for(std::size_t i = 0; i < 32; i++) {
        output[i] = 0xffffffff >> input[i];
    }
    transform_32_32(output);
}

Cortex-M系列有一个很好的功能叫Bit-Banding。这样就可以实现非常有效的按位矩阵变换,这恰好就是你有效地进行bitbang所需要的。

变换应该每位约3个周期(compiled on GCC 6.3 with -funroll-loops)执行,因此总共应该只有大约12k个周期,或大约150us。

唯一的收获?这假设您的特定Cortex-M 3实际上支持位带功能。我没有机会在Arduino上测试这个。