blob: 6a68619101949ca46217aba35f6e2cc9a4fa303a [file] [log] [blame]
// Copyright 2018 The Chromium 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 "components/exo/wayland/clients/client_base.h"
#include <linux-explicit-synchronization-unstable-v1-client-protocol.h>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "components/exo/wayland/clients/client_helper.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "ui/gl/gl_bindings.h"
namespace exo {
namespace wayland {
namespace clients {
namespace {
void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
bool* frame_callback_pending = static_cast<bool*>(data);
*frame_callback_pending = false;
}
} // namespace
// This client exercises the zwp_linux_explicit_synchronization_unstable_v1
// wayland protocol, which enables buffer acquire and release synchronization
// between client and server using dma-fences.
class ExplicitSynchronizationClient : public ClientBase {
public:
ExplicitSynchronizationClient() = default;
// Initialize and run client main loop.
void Run();
private:
DISALLOW_COPY_AND_ASSIGN(ExplicitSynchronizationClient);
};
void ExplicitSynchronizationClient::Run() {
wl_callback_listener frame_listener = {FrameCallback};
/* With a 60Hz redraw rate this completes a half-oscillation in 3 seconds */
static const int kMaxDrawStep = 180;
int draw_step = 0;
int draw_step_dir = 1;
// A valid GL context with support for fd fences is required for this client.
CHECK(gr_context_)
<< "A valid GL context is required. Try running with --use-drm.";
CHECK_EQ(egl_sync_type_, static_cast<unsigned>(EGL_SYNC_NATIVE_FENCE_ANDROID))
<< "EGL doesn't support the EGL_ANDROID_native_fence_sync extension.";
// The server needs to support the linux_explicit_synchronization protocol.
CHECK(globals_.linux_explicit_synchronization)
<< "Server doesn't support zwp_linux_explicit_synchronization_v1.";
std::unique_ptr<zwp_linux_surface_synchronization_v1> surface_synchronization(
zwp_linux_explicit_synchronization_v1_get_synchronization(
globals_.linux_explicit_synchronization.get(), surface_.get()));
DCHECK(surface_synchronization);
std::unique_ptr<wl_callback> frame_callback;
bool frame_callback_pending = false;
do {
if (frame_callback_pending)
continue;
Buffer* buffer = DequeueBuffer();
if (!buffer)
continue;
/* Oscillate between 0 and kMaxDrawStep */
draw_step += draw_step_dir;
if (draw_step == 0 || draw_step == kMaxDrawStep)
draw_step_dir = -draw_step_dir;
SkCanvas* canvas = buffer->sk_surface->getCanvas();
float draw_step_percent = static_cast<float>(draw_step) / kMaxDrawStep;
canvas->clear(
SkColor4f{0.0, draw_step_percent, 1.0 - draw_step_percent, 1.0}
.toSkColor());
// Create an EGLSyncKHR object to signal when rendering is done.
gr_context_->flush();
buffer->egl_sync.reset(new ScopedEglSync(
eglCreateSyncKHR(eglGetCurrentDisplay(), egl_sync_type_, nullptr)));
DCHECK(buffer->egl_sync->is_valid());
glFlush();
wl_surface_set_buffer_scale(surface_.get(), scale_);
wl_surface_set_buffer_transform(surface_.get(), transform_);
wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
surface_size_.height());
wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
// TODO(afrantzis): Acquire a dma-fence fd and use
// zwp_surface_synchronization_v1.set_acquire_fence to synchronize once
// the server supports it.
eglClientWaitSyncKHR(eglGetCurrentDisplay(), buffer->egl_sync->get(),
EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
// Set up the frame callback.
frame_callback_pending = true;
frame_callback.reset(wl_surface_frame(surface_.get()));
wl_callback_add_listener(frame_callback.get(), &frame_listener,
&frame_callback_pending);
wl_surface_commit(surface_.get());
wl_display_flush(display_.get());
} while (wl_display_dispatch(display_.get()) != -1);
}
} // namespace clients
} // namespace wayland
} // namespace exo
int main(int argc, char* argv[]) {
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
exo::wayland::clients::ClientBase::InitParams params;
if (!params.FromCommandLine(*command_line))
return 1;
base::MessageLoopForUI message_loop;
exo::wayland::clients::ExplicitSynchronizationClient client;
if (!client.Init(params))
return 1;
client.Run();
return 0;
}