By doing a bit more math we can create a sensible schedule for these situations. A 6/3 schedule gives a very nice 150fps with temporal shimmer at 50Hz
179 lines
5.7 KiB
C++
179 lines
5.7 KiB
C++
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <ctime>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
#include "piomatter/piomatter.h"
|
|
|
|
#define _ (0)
|
|
#define r (255 << 16)
|
|
#define g (255 << 8)
|
|
#define b (255)
|
|
#define y (r | g)
|
|
#define c (g | b)
|
|
#define m (r | b)
|
|
#define w (r | g | b)
|
|
|
|
constexpr int width = 64, height = 64;
|
|
|
|
uint32_t pixels[height][width] = {
|
|
{_, w, _, _, r, r, _, _, _, g, _, _, b, b, b, _,
|
|
c, c, _, _, y, _, y, _, m, m, m, _, w, w, w, _}, // 0
|
|
{w, _, w, _, r, _, r, _, g, _, g, _, b, _, _, _,
|
|
c, _, c, _, y, _, y, _, _, m, _, _, _, w, _, _}, // 1
|
|
{w, w, w, _, r, _, r, _, g, g, g, _, b, b, _, _,
|
|
c, c, _, _, y, _, y, _, _, m, _, _, _, w, _, _}, // 2
|
|
{w, _, w, _, r, _, r, _, g, _, g, _, b, _, _, _,
|
|
c, _, c, _, y, _, y, _, _, m, _, _, _, w, _, _}, // 3
|
|
{w, _, w, _, r, r, _, _, g, _, g, _, b, _, _, _,
|
|
c, _, c, _, _, y, _, _, m, m, m, _, _, w, _, _}, // 4
|
|
{_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}, // 5
|
|
{_, c, _, _, y, y, _, _, _, m, _, _, r, r, r, _,
|
|
g, g, _, _, b, _, b, _, w, w, w, _, c, c, c, _}, // 6
|
|
{c, _, c, _, y, _, y, _, m, _, m, _, r, _, _, _,
|
|
g, _, g, _, b, _, b, _, _, w, _, _, _, c, _, _}, // 7
|
|
{c, c, c, _, y, _, y, _, m, m, m, _, r, r, _, _,
|
|
g, g, _, _, b, _, b, _, _, w, _, _, _, c, _, _}, // 8
|
|
{c, _, c, _, y, _, y, _, m, _, m, _, r, _, _, _,
|
|
g, _, g, _, b, _, b, _, _, w, _, _, _, c, _, _}, // 9
|
|
{c, _, c, _, y, y, _, _, m, _, m, _, r, _, _, _,
|
|
g, _, g, _, _, b, _, _, w, w, w, _, _, c, _, _}, // 10
|
|
{_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _,
|
|
_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}, // 11
|
|
{r, y, g, c, b, m, r, y, g, c, b, m, r, y, g, c,
|
|
b, m, r, y, g, c, b, m, r, y, g, c, b, m, r, g}, // 12
|
|
{y, g, c, b, m, r, y, g, c, b, m, r, y, g, c, b,
|
|
m, r, y, g, c, b, m, r, y, g, c, b, m, r, g, y}, // 13
|
|
{g, c, b, m, r, y, g, c, b, m, r, y, g, c, b, m,
|
|
r, y, g, c, b, m, r, y, g, c, b, m, r, g, c, b}, // 14
|
|
{c, b, m, r, y, g, c, b, m, r, y, g, c, b, m, r,
|
|
y, g, c, b, m, r, y, g, c, b, m, r, g, c, b, m}, // 15
|
|
};
|
|
#undef r
|
|
#undef g
|
|
#undef b
|
|
#undef c
|
|
#undef y
|
|
#undef w
|
|
#undef _
|
|
|
|
#define rgb(r, g, b) (((r) << 16) | ((g) << 8) | (b))
|
|
|
|
uint32_t colorwheel(int i) {
|
|
i = i & 0xff;
|
|
if (i < 85) {
|
|
return rgb(255 - i * 3, 0, i * 3);
|
|
}
|
|
if (i < 170) {
|
|
i -= 85;
|
|
return rgb(0, i * 3, 255 - i * 3);
|
|
}
|
|
i -= 170;
|
|
return rgb(i * 3, 255 - i * 3, 0);
|
|
}
|
|
|
|
void test_pattern(int offs) {
|
|
for (int i = 0; i < width; i++) {
|
|
pixels[height - 5][i] = rgb(1 + i * 4, 1 + i * 4, 1 + i * 4);
|
|
pixels[height - 4][i] = colorwheel(2 * i + offs / 3);
|
|
pixels[height - 3][i] = colorwheel(2 * i + 64 + offs / 5);
|
|
pixels[height - 2][i] = colorwheel(2 * i + 128 + offs / 2);
|
|
pixels[height - 1][i] = colorwheel(2 * i + 192 + offs / 7);
|
|
}
|
|
for (int i = 0; i < height; i++) {
|
|
pixels[i][i] = rgb(0xff, 0xff, 0xff);
|
|
}
|
|
}
|
|
|
|
static uint64_t monotonicns64() {
|
|
struct timespec tp;
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
return tp.tv_sec * UINT64_C(1000000000) + tp.tv_nsec;
|
|
}
|
|
|
|
static void print_dither_schedule(const piomatter::schedule_sequence &ss) {
|
|
for (auto s : ss) {
|
|
for (auto i : s) {
|
|
printf("{%d %d} ", i.shift, i.active_time);
|
|
}
|
|
printf("\n");
|
|
}
|
|
printf(" -> ");
|
|
std::map<int, int> sums;
|
|
for (auto s : ss) {
|
|
for (auto i : s) {
|
|
sums[-i.shift] += i.active_time;
|
|
}
|
|
}
|
|
for (auto const &i : sums) {
|
|
printf("{%d %d} ", -i.first, i.second);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void test_simple_dither_schedule(int n_planes, int pixels_across) {
|
|
auto ss = piomatter::make_simple_schedule(n_planes, pixels_across);
|
|
print_dither_schedule(ss);
|
|
printf("\n");
|
|
}
|
|
static void test_temporal_dither_schedule(int n_planes, int pixels_across,
|
|
int n_temporal_frames) {
|
|
auto ss = piomatter::make_temporal_dither_schedule(n_planes, pixels_across,
|
|
n_temporal_frames);
|
|
print_dither_schedule(ss);
|
|
printf("\n");
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
int n = argc > 1 ? atoi(argv[1]) : 0;
|
|
|
|
test_simple_dither_schedule(7, 1);
|
|
test_temporal_dither_schedule(7, 1, 2);
|
|
test_temporal_dither_schedule(7, 1, 3);
|
|
test_temporal_dither_schedule(7, 1, 4);
|
|
test_temporal_dither_schedule(7, 1, 5);
|
|
|
|
return 0;
|
|
test_simple_dither_schedule(6, 1);
|
|
test_temporal_dither_schedule(6, 1, 0);
|
|
test_temporal_dither_schedule(6, 1, 2);
|
|
test_temporal_dither_schedule(6, 1, 4);
|
|
|
|
test_simple_dither_schedule(5, 16);
|
|
test_temporal_dither_schedule(5, 16, 2);
|
|
test_temporal_dither_schedule(5, 16, 3);
|
|
test_temporal_dither_schedule(5, 16, 4);
|
|
|
|
test_simple_dither_schedule(5, 24);
|
|
test_temporal_dither_schedule(5, 24, 2);
|
|
test_temporal_dither_schedule(5, 24, 4);
|
|
|
|
test_simple_dither_schedule(10, 24);
|
|
test_temporal_dither_schedule(10, 24, 8);
|
|
|
|
test_temporal_dither_schedule(5, 128, 3);
|
|
test_temporal_dither_schedule(5, 192, 3);
|
|
test_temporal_dither_schedule(5, 128, 4);
|
|
test_temporal_dither_schedule(5, 192, 4);
|
|
return 0;
|
|
|
|
piomatter::matrix_geometry geometry(128, 4, 10, 0, 64, 64, true,
|
|
piomatter::orientation_normal);
|
|
piomatter::piomatter p(std::span(&pixels[0][0], 64 * 64), geometry);
|
|
|
|
uint64_t start = monotonicns64();
|
|
for (int i = 0; i < n; i++) {
|
|
test_pattern(i);
|
|
p.show();
|
|
}
|
|
uint64_t end = monotonicns64();
|
|
|
|
uint64_t duration = end - start;
|
|
double fps = n * 1e9 / duration;
|
|
printf("%.1f FPS [%d frames in %fs]\n", fps, n, duration / 1e9);
|
|
}
|