blob: 3d7701362eb2877cf68629116cb08d26e7918244 [file] [log] [blame]
// Copyright 2024 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_display/display.h"
#include <array>
#include <utility>
#include "gtest/gtest.h"
#include "pw_color/color.h"
using pw::color::color_rgb565_t;
using pw::display_driver::DisplayDriver;
using pw::framebuffer::Framebuffer;
using pw::framebuffer::PixelFormat;
using pw::framebuffer_pool::FramebufferPool;
using Size = pw::geometry::Size<uint16_t>;
namespace pw::display {
namespace {
constexpr size_t kMaxSavedParams = 20;
enum class CallFunc {
Unset,
GetFramebuffer,
ReleaseFramebuffer,
WriteRow,
};
struct CallParams {
CallFunc call_func = CallFunc::Unset;
struct {
void* fb_data = nullptr;
} release_framebuffer;
struct {
size_t num_pixels = 0;
uint16_t row_idx = 0;
uint16_t col_idx = 0;
} write_row;
};
class TestDisplayDriver : public DisplayDriver {
public:
TestDisplayDriver(Framebuffer fb) : framebuffer_(std::move(fb)) {}
Status Init() override { return OkStatus(); }
void WriteFramebuffer(Framebuffer framebuffer,
WriteCallback write_callback) override {
if (next_call_param_idx_ < kMaxSavedParams) {
call_params_[next_call_param_idx_].call_func =
CallFunc::ReleaseFramebuffer;
call_params_[next_call_param_idx_].release_framebuffer.fb_data =
framebuffer.data();
next_call_param_idx_++;
}
write_callback(std::move(framebuffer), OkStatus());
}
Status WriteRow(span<uint16_t> pixel_data,
uint16_t row_idx,
uint16_t col_idx) override {
if (next_call_param_idx_ < kMaxSavedParams) {
call_params_[next_call_param_idx_].call_func = CallFunc::WriteRow;
call_params_[next_call_param_idx_].write_row.num_pixels =
pixel_data.size();
call_params_[next_call_param_idx_].write_row.row_idx = row_idx;
call_params_[next_call_param_idx_].write_row.col_idx = col_idx;
next_call_param_idx_++;
}
return OkStatus();
}
uint16_t GetWidth() const override { return framebuffer_.size().width; }
uint16_t GetHeight() const override { return framebuffer_.size().height; }
int GetNumCalls() const {
int count = 0;
for (size_t i = 0;
i < kMaxSavedParams && call_params_[i].call_func != CallFunc::Unset;
i++) {
count++;
}
return count;
}
const CallParams& GetCall(size_t call_idx) {
PW_ASSERT(call_idx <= call_params_.size());
return call_params_[call_idx];
}
private:
size_t next_call_param_idx_ = 0;
std::array<CallParams, kMaxSavedParams> call_params_;
const Framebuffer framebuffer_;
};
TEST(Display, ReleaseNoResize) {
constexpr pw::geometry::Size<uint16_t> kFramebufferSize{2, 1};
constexpr Size kDisplaySize = kFramebufferSize;
constexpr size_t kNumPixels =
kFramebufferSize.width * kFramebufferSize.height;
constexpr uint16_t kFramebufferRowBytes =
sizeof(color_rgb565_t) * kFramebufferSize.width;
color_rgb565_t pixel_data[kNumPixels];
pw::Vector<void*, 1> pixel_buffers{pixel_data};
FramebufferPool fb_pool({
.fb_addr = pixel_buffers,
.dimensions = kFramebufferSize,
.row_bytes = kFramebufferRowBytes,
.pixel_format = PixelFormat::RGB565,
});
TestDisplayDriver test_driver(Framebuffer(
pixel_data, PixelFormat::RGB565, kFramebufferSize, kFramebufferRowBytes));
Display display(test_driver, kDisplaySize, fb_pool);
Framebuffer fb = display.GetFramebuffer();
EXPECT_TRUE(fb.is_valid());
EXPECT_EQ(kFramebufferSize, fb.size());
EXPECT_EQ(0, test_driver.GetNumCalls());
PW_ASSERT(display.ReleaseFramebuffer(std::move(fb)).ok());
ASSERT_EQ(1, test_driver.GetNumCalls());
auto call = test_driver.GetCall(0);
EXPECT_EQ(CallFunc::ReleaseFramebuffer, call.call_func);
EXPECT_EQ(pixel_data, call.release_framebuffer.fb_data);
}
#if DISPLAY_RESIZE
TEST(Display, ReleaseSmallResize) {
constexpr Size kDisplaySize = {8, 4};
constexpr pw::geometry::Size<uint16_t> kFramebufferSize{2, 1};
constexpr size_t kNumPixels =
kFramebufferSize.width * kFramebufferSize.height;
constexpr uint16_t kFramebufferRowBytes =
sizeof(color_rgb565_t) * kFramebufferSize.width;
color_rgb565_t pixel_data[kNumPixels];
pw::Vector<void*, 1> pixel_buffers{pixel_data};
FramebufferPool fb_pool({
.fb_addr = pixel_buffers,
.dimensions = kFramebufferSize,
.row_bytes = kFramebufferRowBytes,
.pixel_format = PixelFormat::RGB565,
});
TestDisplayDriver test_driver(Framebuffer(
pixel_data, PixelFormat::RGB565, kFramebufferSize, kFramebufferRowBytes));
Display display(test_driver, kDisplaySize, fb_pool);
Framebuffer fb = display.GetFramebuffer();
EXPECT_TRUE(fb.is_valid());
EXPECT_EQ(kFramebufferSize, fb.size());
EXPECT_EQ(0, test_driver.GetNumCalls());
PW_ASSERT(display.ReleaseFramebuffer(std::move(fb)).ok());
ASSERT_EQ(4, test_driver.GetNumCalls());
auto call = test_driver.GetCall(0);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(8U, call.write_row.num_pixels);
EXPECT_EQ(0, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(1);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(8U, call.write_row.num_pixels);
EXPECT_EQ(1, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(2);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(8U, call.write_row.num_pixels);
EXPECT_EQ(2, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(3);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(8U, call.write_row.num_pixels);
EXPECT_EQ(3, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
}
TEST(Display, ReleaseWideResize) {
// Display width > resize buffer (80 px.) will cause two writes per row.
constexpr Size kDisplaySize = {90, 4};
constexpr pw::geometry::Size<uint16_t> kFramebufferSize{2, 1};
constexpr size_t kNumPixels =
kFramebufferSize.width * kFramebufferSize.height;
constexpr uint16_t kFramebufferRowBytes =
sizeof(color_rgb565_t) * kFramebufferSize.width;
color_rgb565_t pixel_data[kNumPixels];
pw::Vector<void*, 1> pixel_buffers{pixel_data};
FramebufferPool fb_pool({
.fb_addr = pixel_buffers,
.dimensions = kFramebufferSize,
.row_bytes = kFramebufferRowBytes,
.pixel_format = PixelFormat::RGB565,
});
TestDisplayDriver test_driver(Framebuffer(
pixel_data, PixelFormat::RGB565, kFramebufferSize, kFramebufferRowBytes));
Display display(test_driver, kDisplaySize, fb_pool);
Framebuffer fb = display.GetFramebuffer();
EXPECT_TRUE(fb.is_valid());
EXPECT_EQ(kFramebufferSize, fb.size());
EXPECT_EQ(0, test_driver.GetNumCalls());
PW_ASSERT(display.ReleaseFramebuffer(std::move(fb)).ok());
ASSERT_EQ(8, test_driver.GetNumCalls());
auto call = test_driver.GetCall(0);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(80U, call.write_row.num_pixels);
EXPECT_EQ(0, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(1);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(10U, call.write_row.num_pixels);
EXPECT_EQ(0, call.write_row.row_idx);
EXPECT_EQ(80, call.write_row.col_idx);
call = test_driver.GetCall(2);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(80U, call.write_row.num_pixels);
EXPECT_EQ(1, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(3);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(10U, call.write_row.num_pixels);
EXPECT_EQ(1, call.write_row.row_idx);
EXPECT_EQ(80, call.write_row.col_idx);
call = test_driver.GetCall(4);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(80U, call.write_row.num_pixels);
EXPECT_EQ(2, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(5);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(10U, call.write_row.num_pixels);
EXPECT_EQ(2, call.write_row.row_idx);
EXPECT_EQ(80, call.write_row.col_idx);
call = test_driver.GetCall(6);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(80U, call.write_row.num_pixels);
EXPECT_EQ(3, call.write_row.row_idx);
EXPECT_EQ(0, call.write_row.col_idx);
call = test_driver.GetCall(7);
EXPECT_EQ(CallFunc::WriteRow, call.call_func);
EXPECT_EQ(10U, call.write_row.num_pixels);
EXPECT_EQ(3, call.write_row.row_idx);
EXPECT_EQ(80, call.write_row.col_idx);
}
#endif // if DISPLAY_RESIZE
} // namespace
} // namespace pw::display