mirror of
https://github.com/esphome/esphome.git
synced 2026-10-17 03:32:50 +00:00
The benchmark now draws once at each rotation and requires the rotated frame to be the transpose of the plain one before timing, so a wrong traversal fails the run instead of passing silently.
125 lines
4.6 KiB
C++
125 lines
4.6 KiB
C++
#include <benchmark/benchmark.h>
|
|
|
|
#include <cstdint>
|
|
#include <memory>
|
|
|
|
#include "esphome/components/display/display_buffer.h"
|
|
#include "esphome/components/image/image.h"
|
|
|
|
namespace esphome::image::benchmarks {
|
|
|
|
static constexpr int kWidth = 128;
|
|
static constexpr int kHeight = 96;
|
|
// Square frame so a rotated draw of the image stays inside it.
|
|
static constexpr int kStride = kWidth > kHeight ? kWidth : kHeight;
|
|
|
|
// One byte per pixel frame buffer with no hardware behind it, so the
|
|
// measurement is Image::draw() plus the DisplayBuffer pixel path every
|
|
// buffered display shares. Everything fits the host cache, so this counts
|
|
// the per pixel work; the memory locality win only shows on hardware.
|
|
class BenchDisplay : public display::DisplayBuffer {
|
|
public:
|
|
BenchDisplay() : frame_(std::make_unique<uint8_t[]>(kStride * kStride)) {}
|
|
void update() override {}
|
|
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
|
|
int get_width_internal() override { return kStride; }
|
|
int get_height_internal() override { return kStride; }
|
|
|
|
protected:
|
|
void draw_absolute_pixel_internal(int x, int y, Color color) override { this->frame_[y * kStride + x] = color.r; }
|
|
|
|
std::unique_ptr<uint8_t[]> frame_;
|
|
|
|
public:
|
|
uint8_t pixel(int x, int y) const { return this->frame_[y * kStride + x]; }
|
|
};
|
|
|
|
// Draws the image at rotation 0 and 90 and checks the second frame is the
|
|
// transpose of the first, which is what a wrong traversal order would break.
|
|
static bool frames_match(const Image &image) {
|
|
BenchDisplay plain;
|
|
BenchDisplay rotated;
|
|
rotated.set_rotation(display::DISPLAY_ROTATION_90_DEGREES);
|
|
const_cast<Image &>(image).draw(0, 0, &plain, display::COLOR_ON, display::COLOR_OFF);
|
|
const_cast<Image &>(image).draw(0, 0, &rotated, display::COLOR_ON, display::COLOR_OFF);
|
|
for (int y = 0; y < kHeight; y++) {
|
|
for (int x = 0; x < kWidth; x++) {
|
|
// DisplayBuffer maps (x, y) to (width - y - 1, x) at 90 degrees
|
|
if (plain.pixel(x, y) != rotated.pixel(kStride - y - 1, x))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Deterministic pseudo random pixel bytes so the alpha branch sees a mix of
|
|
// pixels. Sized for the widest format, the image reads only what it needs.
|
|
static std::unique_ptr<uint8_t[]> make_pixels() {
|
|
constexpr size_t bytes = kWidth * kHeight * 4;
|
|
auto data = std::make_unique<uint8_t[]>(bytes);
|
|
uint32_t seed = 11897;
|
|
for (size_t i = 0; i < bytes; i++) {
|
|
seed = seed * 1103515245u + 12345u;
|
|
data[i] = static_cast<uint8_t>(seed >> 16);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static void draw_image(benchmark::State &state, ImageType type, Transparency transparency,
|
|
display::DisplayRotation rotation = display::DISPLAY_ROTATION_0_DEGREES) {
|
|
auto data = make_pixels();
|
|
Image image(data.get(), kWidth, kHeight, type, transparency);
|
|
BenchDisplay display;
|
|
display.set_rotation(rotation);
|
|
if (!frames_match(image)) {
|
|
state.SkipWithError("rotated draw is not the transpose of the plain draw");
|
|
return;
|
|
}
|
|
|
|
for (auto _ : state) {
|
|
image.draw(0, 0, &display, display::COLOR_ON, display::COLOR_OFF);
|
|
}
|
|
state.SetItemsProcessed(state.iterations() * kWidth * kHeight);
|
|
}
|
|
|
|
static void ImageDraw_Binary(benchmark::State &state) { draw_image(state, IMAGE_TYPE_BINARY, TRANSPARENCY_OPAQUE); }
|
|
BENCHMARK(ImageDraw_Binary);
|
|
|
|
static void ImageDraw_Grayscale(benchmark::State &state) {
|
|
draw_image(state, IMAGE_TYPE_GRAYSCALE, TRANSPARENCY_OPAQUE);
|
|
}
|
|
BENCHMARK(ImageDraw_Grayscale);
|
|
|
|
static void ImageDraw_RGB565(benchmark::State &state) { draw_image(state, IMAGE_TYPE_RGB565, TRANSPARENCY_OPAQUE); }
|
|
BENCHMARK(ImageDraw_RGB565);
|
|
|
|
static void ImageDraw_RGB(benchmark::State &state) { draw_image(state, IMAGE_TYPE_RGB, TRANSPARENCY_OPAQUE); }
|
|
BENCHMARK(ImageDraw_RGB);
|
|
|
|
static void ImageDraw_RGB_Rotated(benchmark::State &state) {
|
|
draw_image(state, IMAGE_TYPE_RGB, TRANSPARENCY_OPAQUE, display::DISPLAY_ROTATION_90_DEGREES);
|
|
}
|
|
BENCHMARK(ImageDraw_RGB_Rotated);
|
|
|
|
static void ImageDraw_Binary_Transparent(benchmark::State &state) {
|
|
draw_image(state, IMAGE_TYPE_BINARY, TRANSPARENCY_CHROMA_KEY);
|
|
}
|
|
BENCHMARK(ImageDraw_Binary_Transparent);
|
|
|
|
static void ImageDraw_Grayscale_ChromaKey(benchmark::State &state) {
|
|
draw_image(state, IMAGE_TYPE_GRAYSCALE, TRANSPARENCY_CHROMA_KEY);
|
|
}
|
|
BENCHMARK(ImageDraw_Grayscale_ChromaKey);
|
|
|
|
static void ImageDraw_Grayscale_Alpha(benchmark::State &state) {
|
|
draw_image(state, IMAGE_TYPE_GRAYSCALE, TRANSPARENCY_ALPHA_CHANNEL);
|
|
}
|
|
BENCHMARK(ImageDraw_Grayscale_Alpha);
|
|
|
|
static void ImageDraw_RGB_Alpha(benchmark::State &state) {
|
|
draw_image(state, IMAGE_TYPE_RGB, TRANSPARENCY_ALPHA_CHANNEL);
|
|
}
|
|
BENCHMARK(ImageDraw_RGB_Alpha);
|
|
|
|
} // namespace esphome::image::benchmarks
|