mirror of
https://github.com/esphome/esphome.git
synced 2026-09-18 02:28:42 +00:00
[display] Trim the per pixel cost of draw_pixel_at (#19360)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include <utility>
|
||||
#include <numbers>
|
||||
#include "display_color_utils.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -770,10 +771,12 @@ Rect Display::get_clipping() const {
|
||||
|
||||
void Display::clear_clipping_() { this->clipping_rectangle_.clear(); }
|
||||
|
||||
void Display::feed_wdt_pixel_slow_() { App.feed_wdt(); }
|
||||
|
||||
bool Display::clip(int x, int y) {
|
||||
if (x < 0 || x >= this->get_width() || y < 0 || y >= this->get_height())
|
||||
return false;
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -758,6 +758,13 @@ class Display : public PollingComponent {
|
||||
|
||||
bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
|
||||
|
||||
/// Whether (x, y) falls outside the active clipping rectangle. Tests the
|
||||
/// stack top in place: get_clipping() is out of line and returns the Rect
|
||||
/// by value, which per pixel drawing cannot afford.
|
||||
bool ESPHOME_ALWAYS_INLINE is_point_clipped(int x, int y) const {
|
||||
return this->is_clipping() && !this->clipping_rectangle_.back().inside(x, y);
|
||||
}
|
||||
|
||||
/** Check if pixel is within region of display.
|
||||
*/
|
||||
bool clip(int x, int y);
|
||||
@@ -774,6 +781,17 @@ class Display : public PollingComponent {
|
||||
void do_update_();
|
||||
void clear_clipping_();
|
||||
|
||||
/// Watchdog feed for per pixel loops. App.feed_wdt() is already rate
|
||||
/// limited, but every call reads the clock; only every 256th pixel makes
|
||||
/// that call, so the real feeds are unchanged and a pixel costs a counter.
|
||||
/// At 20 us per pixel on the slowest e-paper path that is about 5 ms
|
||||
/// between clock reads.
|
||||
void ESPHOME_ALWAYS_INLINE feed_wdt_per_pixel_() {
|
||||
if (++this->wdt_pixel_counter_ == 0)
|
||||
this->feed_wdt_pixel_slow_();
|
||||
}
|
||||
void feed_wdt_pixel_slow_();
|
||||
|
||||
virtual int get_height_internal() = 0;
|
||||
virtual int get_width_internal() = 0;
|
||||
|
||||
@@ -793,6 +811,7 @@ class Display : public PollingComponent {
|
||||
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
|
||||
bool auto_clear_enabled_{true};
|
||||
std::vector<Rect> clipping_rectangle_;
|
||||
uint8_t wdt_pixel_counter_{0};
|
||||
bool show_test_card_{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::display {
|
||||
@@ -44,7 +43,7 @@ int DisplayBuffer::get_height() {
|
||||
}
|
||||
|
||||
void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return; // NOLINT
|
||||
|
||||
switch (this->rotation_) {
|
||||
@@ -64,7 +63,7 @@ void HOT DisplayBuffer::draw_pixel_at(int x, int y, Color color) {
|
||||
break;
|
||||
}
|
||||
this->draw_absolute_pixel_internal(x, y, color);
|
||||
App.feed_wdt();
|
||||
this->feed_wdt_per_pixel_();
|
||||
}
|
||||
|
||||
} // namespace esphome::display
|
||||
|
||||
@@ -63,16 +63,6 @@ bool Rect::equal(Rect rect) const {
|
||||
return (rect.x == this->x) && (rect.w == this->w) && (rect.y == this->y) && (rect.h == this->h);
|
||||
}
|
||||
|
||||
bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) const { // NOLINT
|
||||
if (!this->is_set()) {
|
||||
return true;
|
||||
}
|
||||
if (absolute) {
|
||||
return test_x >= this->x && test_x < this->x2() && test_y >= this->y && test_y < this->y2();
|
||||
}
|
||||
return test_x >= 0 && test_x < this->w && test_y >= 0 && test_y < this->h;
|
||||
}
|
||||
|
||||
bool Rect::inside(Rect rect) const {
|
||||
if (!this->is_set() || !rect.is_set()) {
|
||||
return true;
|
||||
|
||||
@@ -26,7 +26,15 @@ class Rect {
|
||||
void shrink(Rect rect);
|
||||
|
||||
bool inside(Rect rect) const;
|
||||
bool inside(int16_t test_x, int16_t test_y, bool absolute = true) const;
|
||||
bool ESPHOME_ALWAYS_INLINE inside(int16_t test_x, int16_t test_y, bool absolute = true) const {
|
||||
if (!this->is_set()) {
|
||||
return true;
|
||||
}
|
||||
if (absolute) {
|
||||
return test_x >= this->x && test_x < this->x2() && test_y >= this->y && test_y < this->y2();
|
||||
}
|
||||
return test_x >= 0 && test_x < this->w && test_y >= 0 && test_y < this->h;
|
||||
}
|
||||
bool equal(Rect rect) const;
|
||||
void info(const std::string &prefix = "rect info:");
|
||||
};
|
||||
|
||||
@@ -299,7 +299,7 @@ bool EPaperBase::initialise(bool partial) {
|
||||
* @return false if the coordinates are out of bounds
|
||||
*/
|
||||
bool EPaperBase::rotate_coordinates_(int &x, int &y) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return false;
|
||||
if (this->effective_transform_ & SWAP_XY)
|
||||
std::swap(x, y);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "hub75_component.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
@@ -124,11 +123,11 @@ void HOT HUB75Display::draw_pixel_at(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return;
|
||||
|
||||
driver_->set_pixel(x, y, color.r, color.g, color.b);
|
||||
App.feed_wdt();
|
||||
this->feed_wdt_per_pixel_();
|
||||
}
|
||||
|
||||
void HOT HUB75Display::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order,
|
||||
|
||||
@@ -855,7 +855,7 @@ void IT8951Display::apply_transform_(int &x, int &y) const {
|
||||
}
|
||||
|
||||
bool IT8951Display::rotate_coordinates_(int &x, int &y) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return false;
|
||||
this->apply_transform_(x, y);
|
||||
if (x >= this->width_ || y >= this->height_ || x < 0 || y < 0)
|
||||
@@ -929,7 +929,7 @@ void IT8951Display::fill(Color color) {
|
||||
void HOT IT8951Display::draw_pixel_at(int x, int y, Color color) {
|
||||
if (this->buffer_ == nullptr)
|
||||
return;
|
||||
App.feed_wdt();
|
||||
this->feed_wdt_per_pixel_();
|
||||
if (!this->rotate_coordinates_(x, y))
|
||||
return;
|
||||
this->write_pixel_native_(static_cast<uint16_t>(x), static_cast<uint16_t>(y), color);
|
||||
|
||||
@@ -259,7 +259,7 @@ bool MipiDsi::check_buffer_() {
|
||||
}
|
||||
|
||||
void MipiDsi::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return;
|
||||
|
||||
switch (this->rotation_) {
|
||||
|
||||
@@ -259,7 +259,7 @@ bool MipiRgb::check_buffer_() {
|
||||
}
|
||||
|
||||
void MipiRgb::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y) || this->is_failed())
|
||||
if (this->is_point_clipped(x, y) || this->is_failed())
|
||||
return;
|
||||
|
||||
switch (this->rotation_) {
|
||||
|
||||
@@ -604,7 +604,7 @@ class MipiSpiBuffer
|
||||
|
||||
// Draw a pixel at the given coordinates.
|
||||
void draw_pixel_at(int x, int y, Color color) override {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return;
|
||||
if constexpr (not HAS_HARDWARE_ROTATION) {
|
||||
if (this->rotation_ == display::DISPLAY_ROTATION_180_DEGREES) {
|
||||
|
||||
@@ -120,7 +120,7 @@ void Pixoo::set_pixel_(uint32_t index, Color color) {
|
||||
}
|
||||
|
||||
void HOT Pixoo::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return;
|
||||
const int side = static_cast<int>(this->model_);
|
||||
switch (this->rotation_) {
|
||||
|
||||
@@ -101,7 +101,7 @@ int RpiDpiRgb::get_height() {
|
||||
}
|
||||
|
||||
void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return; // NOLINT
|
||||
|
||||
switch (this->rotation_) {
|
||||
@@ -124,7 +124,7 @@ void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) {
|
||||
|
||||
this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true,
|
||||
0, 0, 0);
|
||||
App.feed_wdt();
|
||||
this->feed_wdt_per_pixel_();
|
||||
}
|
||||
|
||||
void RpiDpiRgb::dump_config() {
|
||||
|
||||
@@ -164,7 +164,7 @@ void Sdl::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *
|
||||
}
|
||||
|
||||
void Sdl::draw_pixel_at(int x, int y, Color color) {
|
||||
if (this->texture_ == nullptr || !this->get_clipping().inside(x, y))
|
||||
if (this->texture_ == nullptr || this->is_point_clipped(x, y))
|
||||
return;
|
||||
|
||||
if (this->rotation_ == display::DISPLAY_ROTATION_180_DEGREES) {
|
||||
|
||||
@@ -84,7 +84,7 @@ void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8
|
||||
}
|
||||
|
||||
void ST7701S::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y))
|
||||
if (this->is_point_clipped(x, y))
|
||||
return; // NOLINT
|
||||
|
||||
switch (this->rotation_) {
|
||||
@@ -107,7 +107,7 @@ void ST7701S::draw_pixel_at(int x, int y, Color color) {
|
||||
|
||||
this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true,
|
||||
0, 0, 0);
|
||||
App.feed_wdt();
|
||||
this->feed_wdt_per_pixel_();
|
||||
}
|
||||
|
||||
void ST7701S::write_command_(uint8_t value) {
|
||||
|
||||
Reference in New Issue
Block a user