blob: 8dc9365bed866d663c7bba89d0767c22990f4726 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lib/framebuffer/framebuffer.h"
#include <fcntl.h>
#include <fuchsia/hardware/display/llcpp/fidl.h>
#include <fuchsia/sysmem/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/image-format-llcpp/image-format-llcpp.h>
#include <zircon/pixelformat.h>
#include <thread>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
#include "src/lib/fsl/handles/object_info.h"
namespace fhd = fuchsia_hardware_display;
namespace sysmem = fuchsia_sysmem;
zx_status_t fb_bind_with_channel(bool single_buffer, const char** err_msg_out,
zx::channel dc_client_channel);
void RunSingleBufferTest() {
fbl::unique_fd dc_fd(open("/dev/class/display-controller/000", O_RDWR));
if (!dc_fd) {
fprintf(stdout, "Skipping test because of no display controller\n");
return;
}
dc_fd.reset();
constexpr uint32_t kIterations = 2;
for (uint32_t i = 0; i < kIterations; i++) {
const char* error;
zx_status_t status = fb_bind(true, &error);
if (status == ZX_ERR_NOT_SUPPORTED) {
// If the simple display driver is being used then sysmem isn't supported
// and libframebuffer isn't either.
fprintf(stderr, "Skipping because received ZX_ERR_NOT_SUPPORTED\n");
return;
}
ASSERT_OK(status);
zx_handle_t buffer_handle = fb_get_single_buffer();
EXPECT_NE(ZX_HANDLE_INVALID, buffer_handle);
uint32_t width, height, linear_stride_px;
zx_pixel_format_t format;
fb_get_config(&width, &height, &linear_stride_px, &format);
EXPECT_LE(width, linear_stride_px);
EXPECT_LT(0u, ZX_PIXEL_FORMAT_BYTES(format));
uint64_t buffer_size;
EXPECT_OK(zx_vmo_get_size(buffer_handle, &buffer_size));
EXPECT_LE(linear_stride_px * ZX_PIXEL_FORMAT_BYTES(format) * height, buffer_size);
fb_release();
}
}
TEST(Framebuffer, SingleBuffer) {
zx::event finished;
zx::event::create(0, &finished);
std::thread execute_thread([&finished]() {
RunSingleBufferTest();
finished.signal(0, ZX_USER_SIGNAL_0);
});
zx_status_t status =
finished.wait_one(ZX_USER_SIGNAL_0, zx::deadline_after(zx::sec(60)), nullptr);
EXPECT_EQ(ZX_OK, status);
if (status != ZX_OK) {
fprintf(stderr, "Test timed out. Maybe no display is connected to device.\n");
execute_thread.detach();
} else {
execute_thread.join();
}
}
namespace {
constexpr uint32_t kBytesPerRowDivisor = 128;
class StubDisplayController : public fidl::WireServer<fhd::Controller> {
public:
StubDisplayController(bool use_ram_domain) : use_ram_domain_(use_ram_domain) {
zx::channel sysmem_server, sysmem_client;
ASSERT_OK(zx::channel::create(0, &sysmem_server, &sysmem_client));
ASSERT_OK(fdio_service_connect("/svc/fuchsia.sysmem.Allocator", sysmem_server.release()));
sysmem_allocator_ =
std::make_unique<fidl::WireSyncClient<sysmem::Allocator>>(std::move(sysmem_client));
sysmem_allocator_->SetDebugClientInfo(
fidl::StringView::FromExternal(fsl::GetCurrentProcessName() + "-debug-client"),
fsl::GetCurrentProcessKoid());
}
~StubDisplayController() { current_buffer_collection_->Close(); }
void ImportVmoImage(ImportVmoImageRequestView request,
ImportVmoImageCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ImportImage(ImportImageRequestView request,
ImportImageCompleter::Sync& _completer) override {
_completer.Reply(ZX_OK, 1);
}
void ReleaseImage(ReleaseImageRequestView request,
ReleaseImageCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ImportEvent(ImportEventRequestView request,
ImportEventCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ReleaseEvent(ReleaseEventRequestView request,
ReleaseEventCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void CreateLayer(CreateLayerRequestView request,
CreateLayerCompleter::Sync& _completer) override {
_completer.Reply(ZX_OK, 1);
}
void DestroyLayer(DestroyLayerRequestView request,
DestroyLayerCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ImportGammaTable(ImportGammaTableRequestView request,
ImportGammaTableCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ReleaseGammaTable(ReleaseGammaTableRequestView request,
ReleaseGammaTableCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetDisplayMode(SetDisplayModeRequestView request,
SetDisplayModeCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetDisplayColorConversion(SetDisplayColorConversionRequestView request,
SetDisplayColorConversionCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetDisplayGammaTable(SetDisplayGammaTableRequestView request,
SetDisplayGammaTableCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetDisplayLayers(SetDisplayLayersRequestView request,
SetDisplayLayersCompleter::Sync& _completer) override {
// Ignore
}
void SetLayerPrimaryConfig(SetLayerPrimaryConfigRequestView request,
SetLayerPrimaryConfigCompleter::Sync& _completer) override {
// Ignore
}
void SetLayerPrimaryPosition(SetLayerPrimaryPositionRequestView request,
SetLayerPrimaryPositionCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetLayerPrimaryAlpha(SetLayerPrimaryAlphaRequestView request,
SetLayerPrimaryAlphaCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetLayerCursorConfig(SetLayerCursorConfigRequestView request,
SetLayerCursorConfigCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetLayerCursorPosition(SetLayerCursorPositionRequestView request,
SetLayerCursorPositionCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetLayerColorConfig(SetLayerColorConfigRequestView request,
SetLayerColorConfigCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetLayerImage(SetLayerImageRequestView request,
SetLayerImageCompleter::Sync& _completer) override {
// Ignore
}
void CheckConfig(CheckConfigRequestView request,
CheckConfigCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ApplyConfig(ApplyConfigRequestView request,
ApplyConfigCompleter::Sync& _completer) override {
// Ignore
}
void EnableVsync(EnableVsyncRequestView request,
EnableVsyncCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetVirtconMode(SetVirtconModeRequestView request,
SetVirtconModeCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ImportBufferCollection(ImportBufferCollectionRequestView request,
ImportBufferCollectionCompleter::Sync& _completer) override {
zx::channel server, client;
ASSERT_OK(zx::channel::create(0, &server, &client));
ASSERT_TRUE(sysmem_allocator_
->BindSharedCollection(std::move(request->collection_token), std::move(server))
.ok());
current_buffer_collection_ =
std::make_unique<fidl::WireSyncClient<sysmem::BufferCollection>>(std::move(client));
_completer.Reply(ZX_OK);
}
void ReleaseBufferCollection(ReleaseBufferCollectionRequestView request,
ReleaseBufferCollectionCompleter::Sync& _completer) override {}
void SetBufferCollectionConstraints(
SetBufferCollectionConstraintsRequestView request,
SetBufferCollectionConstraintsCompleter::Sync& _completer) override {
sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.cpu = sysmem::wire::kCpuUsageWriteOften | sysmem::wire::kCpuUsageRead;
constraints.min_buffer_count = 1;
constraints.image_format_constraints_count = 1;
auto& image_constraints = constraints.image_format_constraints[0];
image_constraints = image_format::GetDefaultImageFormatConstraints();
image_constraints.pixel_format.type = sysmem::wire::PixelFormatType::kBgra32;
image_constraints.pixel_format.has_format_modifier = true;
image_constraints.pixel_format.format_modifier.value = sysmem::wire::kFormatModifierLinear;
image_constraints.color_spaces_count = 1;
image_constraints.color_space[0].type = sysmem::wire::ColorSpaceType::kSrgb;
image_constraints.max_coded_width = 0xffffffff;
image_constraints.max_coded_height = 0xffffffff;
image_constraints.min_bytes_per_row = 0;
image_constraints.max_bytes_per_row = 0xffffffff;
image_constraints.bytes_per_row_divisor = kBytesPerRowDivisor;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = image_format::GetDefaultBufferMemoryConstraints();
constraints.buffer_memory_constraints.ram_domain_supported = use_ram_domain_;
constraints.buffer_memory_constraints.cpu_domain_supported = !use_ram_domain_;
current_buffer_collection_->SetConstraints(true, constraints);
_completer.Reply(ZX_OK);
}
void GetSingleBufferFramebuffer(GetSingleBufferFramebufferRequestView request,
GetSingleBufferFramebufferCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void IsCaptureSupported(IsCaptureSupportedRequestView request,
IsCaptureSupportedCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ImportImageForCapture(ImportImageForCaptureRequestView request,
ImportImageForCaptureCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void StartCapture(StartCaptureRequestView request,
StartCaptureCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void ReleaseCapture(ReleaseCaptureRequestView request,
ReleaseCaptureCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void AcknowledgeVsync(AcknowledgeVsyncRequestView request,
AcknowledgeVsyncCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
void SetMinimumRgb(SetMinimumRgbRequestView request,
SetMinimumRgbCompleter::Sync& _completer) override {
EXPECT_TRUE(false);
}
private:
std::unique_ptr<fidl::WireSyncClient<sysmem::Allocator>> sysmem_allocator_;
std::unique_ptr<fidl::WireSyncClient<sysmem::BufferCollection>> current_buffer_collection_;
bool use_ram_domain_;
};
} // namespace
void SendInitialDisplay(const fidl::WireEventSender<fhd::Controller>& event_sender,
fhd::wire::Mode* mode, uint32_t pixel_format) {
fhd::wire::Info info;
info.pixel_format = fidl::VectorView<uint32_t>::FromExternal(&pixel_format, 1);
info.modes = fidl::VectorView<fhd::wire::Mode>::FromExternal(mode, 1);
auto added = fidl::VectorView<fhd::wire::Info>::FromExternal(&info, 1);
fidl::VectorView<uint64_t> removed;
ASSERT_OK(event_sender.OnDisplaysChanged(std::move(added), std::move(removed)));
}
void TestDisplayStride(bool ram_domain) {
zx::channel server_channel, client_channel;
ASSERT_OK(zx::channel::create(0u, &server_channel, &client_channel));
fidl::WireEventSender<fhd::Controller> event_sender(std::move(server_channel));
StubDisplayController controller(ram_domain);
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
fhd::wire::Mode mode;
mode.horizontal_resolution = 301;
mode.vertical_resolution = 250;
constexpr uint32_t kPixelFormat = ZX_PIXEL_FORMAT_ARGB_8888;
SendInitialDisplay(event_sender, &mode, kPixelFormat);
loop.StartThread();
ASSERT_OK(fidl::BindSingleInFlightOnly(loop.dispatcher(), std::move(event_sender.channel()),
&controller));
const char* error;
zx_status_t status = fb_bind_with_channel(true, &error, std::move(client_channel));
EXPECT_OK(status);
zx_handle_t buffer_handle = fb_get_single_buffer();
EXPECT_NE(ZX_HANDLE_INVALID, buffer_handle);
uint32_t width, height, linear_stride_px;
zx_pixel_format_t format;
fb_get_config(&width, &height, &linear_stride_px, &format);
EXPECT_EQ(mode.horizontal_resolution, width);
EXPECT_EQ(mode.vertical_resolution, height);
EXPECT_EQ(kPixelFormat, format);
constexpr uint32_t kBytesPerPixel = 4;
// Round up to be a multiple of kBytesPerRowDivisor bytes.
EXPECT_EQ(fbl::round_up(width * kBytesPerPixel, kBytesPerRowDivisor) / kBytesPerPixel,
linear_stride_px);
uint64_t buffer_size;
EXPECT_OK(zx_vmo_get_size(buffer_handle, &buffer_size));
EXPECT_LE(linear_stride_px * ZX_PIXEL_FORMAT_BYTES(format) * height, buffer_size);
}
// Check that the correct stride is returned when a weird one is used.
TEST(Framebuffer, DisplayStrideCpuDomain) { TestDisplayStride(false); }
TEST(Framebuffer, DisplayStrideRamDomain) { TestDisplayStride(true); }