xref: /aosp_15_r20/external/libkmsxx/kmscube/cube-gbm.cpp (revision f0687c8a10b3e371dbe09214db6664e37c283cca)
1*f0687c8aSRaman Tenneti #include <chrono>
2*f0687c8aSRaman Tenneti #include <cstdio>
3*f0687c8aSRaman Tenneti #include <vector>
4*f0687c8aSRaman Tenneti #include <memory>
5*f0687c8aSRaman Tenneti #include <algorithm>
6*f0687c8aSRaman Tenneti #include <poll.h>
7*f0687c8aSRaman Tenneti 
8*f0687c8aSRaman Tenneti #include <xf86drm.h>
9*f0687c8aSRaman Tenneti #include <xf86drmMode.h>
10*f0687c8aSRaman Tenneti #include <gbm.h>
11*f0687c8aSRaman Tenneti 
12*f0687c8aSRaman Tenneti #include <kms++/kms++.h>
13*f0687c8aSRaman Tenneti #include <kms++util/kms++util.h>
14*f0687c8aSRaman Tenneti #include "cube-egl.h"
15*f0687c8aSRaman Tenneti #include "cube-gles2.h"
16*f0687c8aSRaman Tenneti 
17*f0687c8aSRaman Tenneti using namespace kms;
18*f0687c8aSRaman Tenneti using namespace std;
19*f0687c8aSRaman Tenneti 
20*f0687c8aSRaman Tenneti static int s_flip_pending;
21*f0687c8aSRaman Tenneti static bool s_need_exit;
22*f0687c8aSRaman Tenneti 
23*f0687c8aSRaman Tenneti static bool s_support_planes;
24*f0687c8aSRaman Tenneti 
25*f0687c8aSRaman Tenneti class GbmDevice
26*f0687c8aSRaman Tenneti {
27*f0687c8aSRaman Tenneti public:
GbmDevice(Card & card)28*f0687c8aSRaman Tenneti 	GbmDevice(Card& card)
29*f0687c8aSRaman Tenneti 	{
30*f0687c8aSRaman Tenneti 		m_dev = gbm_create_device(card.fd());
31*f0687c8aSRaman Tenneti 		FAIL_IF(!m_dev, "failed to create gbm device");
32*f0687c8aSRaman Tenneti 	}
33*f0687c8aSRaman Tenneti 
~GbmDevice()34*f0687c8aSRaman Tenneti 	~GbmDevice()
35*f0687c8aSRaman Tenneti 	{
36*f0687c8aSRaman Tenneti 		gbm_device_destroy(m_dev);
37*f0687c8aSRaman Tenneti 	}
38*f0687c8aSRaman Tenneti 
39*f0687c8aSRaman Tenneti 	GbmDevice(const GbmDevice& other) = delete;
40*f0687c8aSRaman Tenneti 	GbmDevice& operator=(const GbmDevice& other) = delete;
41*f0687c8aSRaman Tenneti 
handle() const42*f0687c8aSRaman Tenneti 	struct gbm_device* handle() const { return m_dev; }
43*f0687c8aSRaman Tenneti 
44*f0687c8aSRaman Tenneti private:
45*f0687c8aSRaman Tenneti 	struct gbm_device* m_dev;
46*f0687c8aSRaman Tenneti };
47*f0687c8aSRaman Tenneti 
48*f0687c8aSRaman Tenneti class GbmSurface
49*f0687c8aSRaman Tenneti {
50*f0687c8aSRaman Tenneti public:
GbmSurface(GbmDevice & gdev,int width,int height)51*f0687c8aSRaman Tenneti 	GbmSurface(GbmDevice& gdev, int width, int height)
52*f0687c8aSRaman Tenneti 	{
53*f0687c8aSRaman Tenneti 		m_surface = gbm_surface_create(gdev.handle(), width, height,
54*f0687c8aSRaman Tenneti 					       GBM_FORMAT_XRGB8888,
55*f0687c8aSRaman Tenneti 					       GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
56*f0687c8aSRaman Tenneti 		FAIL_IF(!m_surface, "failed to create gbm surface");
57*f0687c8aSRaman Tenneti 	}
58*f0687c8aSRaman Tenneti 
~GbmSurface()59*f0687c8aSRaman Tenneti 	~GbmSurface()
60*f0687c8aSRaman Tenneti 	{
61*f0687c8aSRaman Tenneti 		gbm_surface_destroy(m_surface);
62*f0687c8aSRaman Tenneti 	}
63*f0687c8aSRaman Tenneti 
64*f0687c8aSRaman Tenneti 	GbmSurface(const GbmSurface& other) = delete;
65*f0687c8aSRaman Tenneti 	GbmSurface& operator=(const GbmSurface& other) = delete;
66*f0687c8aSRaman Tenneti 
has_free()67*f0687c8aSRaman Tenneti 	bool has_free()
68*f0687c8aSRaman Tenneti 	{
69*f0687c8aSRaman Tenneti 		return gbm_surface_has_free_buffers(m_surface);
70*f0687c8aSRaman Tenneti 	}
71*f0687c8aSRaman Tenneti 
lock_front_buffer()72*f0687c8aSRaman Tenneti 	gbm_bo* lock_front_buffer()
73*f0687c8aSRaman Tenneti 	{
74*f0687c8aSRaman Tenneti 		return gbm_surface_lock_front_buffer(m_surface);
75*f0687c8aSRaman Tenneti 	}
76*f0687c8aSRaman Tenneti 
release_buffer(gbm_bo * bo)77*f0687c8aSRaman Tenneti 	void release_buffer(gbm_bo* bo)
78*f0687c8aSRaman Tenneti 	{
79*f0687c8aSRaman Tenneti 		gbm_surface_release_buffer(m_surface, bo);
80*f0687c8aSRaman Tenneti 	}
81*f0687c8aSRaman Tenneti 
handle() const82*f0687c8aSRaman Tenneti 	struct gbm_surface* handle() const { return m_surface; }
83*f0687c8aSRaman Tenneti 
84*f0687c8aSRaman Tenneti private:
85*f0687c8aSRaman Tenneti 	struct gbm_surface* m_surface;
86*f0687c8aSRaman Tenneti };
87*f0687c8aSRaman Tenneti 
88*f0687c8aSRaman Tenneti class GbmEglSurface
89*f0687c8aSRaman Tenneti {
90*f0687c8aSRaman Tenneti public:
GbmEglSurface(Card & card,GbmDevice & gdev,const EglState & egl,int width,int height)91*f0687c8aSRaman Tenneti 	GbmEglSurface(Card& card, GbmDevice& gdev, const EglState& egl, int width, int height)
92*f0687c8aSRaman Tenneti 		: card(card), egl(egl), m_width(width), m_height(height),
93*f0687c8aSRaman Tenneti 		  bo_prev(0), bo_next(0)
94*f0687c8aSRaman Tenneti 	{
95*f0687c8aSRaman Tenneti 		gsurface = unique_ptr<GbmSurface>(new GbmSurface(gdev, width, height));
96*f0687c8aSRaman Tenneti 		esurface = eglCreateWindowSurface(egl.display(), egl.config(), gsurface->handle(), NULL);
97*f0687c8aSRaman Tenneti 		FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
98*f0687c8aSRaman Tenneti 	}
99*f0687c8aSRaman Tenneti 
~GbmEglSurface()100*f0687c8aSRaman Tenneti 	~GbmEglSurface()
101*f0687c8aSRaman Tenneti 	{
102*f0687c8aSRaman Tenneti 		if (bo_next)
103*f0687c8aSRaman Tenneti 			gsurface->release_buffer(bo_next);
104*f0687c8aSRaman Tenneti 		eglDestroySurface(egl.display(), esurface);
105*f0687c8aSRaman Tenneti 	}
106*f0687c8aSRaman Tenneti 
make_current()107*f0687c8aSRaman Tenneti 	void make_current()
108*f0687c8aSRaman Tenneti 	{
109*f0687c8aSRaman Tenneti 		FAIL_IF(!gsurface->has_free(), "No free buffers");
110*f0687c8aSRaman Tenneti 
111*f0687c8aSRaman Tenneti 		eglMakeCurrent(egl.display(), esurface, esurface, egl.context());
112*f0687c8aSRaman Tenneti 	}
113*f0687c8aSRaman Tenneti 
swap_buffers()114*f0687c8aSRaman Tenneti 	void swap_buffers()
115*f0687c8aSRaman Tenneti 	{
116*f0687c8aSRaman Tenneti 		eglSwapBuffers(egl.display(), esurface);
117*f0687c8aSRaman Tenneti 	}
118*f0687c8aSRaman Tenneti 
drm_fb_destroy_callback(struct gbm_bo * bo,void * data)119*f0687c8aSRaman Tenneti 	static void drm_fb_destroy_callback(struct gbm_bo* bo, void* data)
120*f0687c8aSRaman Tenneti 	{
121*f0687c8aSRaman Tenneti 		auto fb = reinterpret_cast<Framebuffer*>(data);
122*f0687c8aSRaman Tenneti 		delete fb;
123*f0687c8aSRaman Tenneti 	}
124*f0687c8aSRaman Tenneti 
drm_fb_get_from_bo(struct gbm_bo * bo,Card & card)125*f0687c8aSRaman Tenneti 	static Framebuffer* drm_fb_get_from_bo(struct gbm_bo* bo, Card& card)
126*f0687c8aSRaman Tenneti 	{
127*f0687c8aSRaman Tenneti 		auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
128*f0687c8aSRaman Tenneti 		if (fb)
129*f0687c8aSRaman Tenneti 			return fb;
130*f0687c8aSRaman Tenneti 
131*f0687c8aSRaman Tenneti 		uint32_t width = gbm_bo_get_width(bo);
132*f0687c8aSRaman Tenneti 		uint32_t height = gbm_bo_get_height(bo);
133*f0687c8aSRaman Tenneti 		uint32_t stride = gbm_bo_get_stride(bo);
134*f0687c8aSRaman Tenneti 		uint32_t handle = gbm_bo_get_handle(bo).u32;
135*f0687c8aSRaman Tenneti 		PixelFormat format = (PixelFormat)gbm_bo_get_format(bo);
136*f0687c8aSRaman Tenneti 
137*f0687c8aSRaman Tenneti 		vector<uint32_t> handles{ handle };
138*f0687c8aSRaman Tenneti 		vector<uint32_t> strides{ stride };
139*f0687c8aSRaman Tenneti 		vector<uint32_t> offsets{ 0 };
140*f0687c8aSRaman Tenneti 
141*f0687c8aSRaman Tenneti 		fb = new ExtFramebuffer(card, width, height, format, handles, strides, offsets);
142*f0687c8aSRaman Tenneti 
143*f0687c8aSRaman Tenneti 		gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
144*f0687c8aSRaman Tenneti 
145*f0687c8aSRaman Tenneti 		return fb;
146*f0687c8aSRaman Tenneti 	}
147*f0687c8aSRaman Tenneti 
lock_next()148*f0687c8aSRaman Tenneti 	Framebuffer* lock_next()
149*f0687c8aSRaman Tenneti 	{
150*f0687c8aSRaman Tenneti 		bo_prev = bo_next;
151*f0687c8aSRaman Tenneti 		bo_next = gsurface->lock_front_buffer();
152*f0687c8aSRaman Tenneti 		FAIL_IF(!bo_next, "could not lock gbm buffer");
153*f0687c8aSRaman Tenneti 		return drm_fb_get_from_bo(bo_next, card);
154*f0687c8aSRaman Tenneti 	}
155*f0687c8aSRaman Tenneti 
free_prev()156*f0687c8aSRaman Tenneti 	void free_prev()
157*f0687c8aSRaman Tenneti 	{
158*f0687c8aSRaman Tenneti 		if (bo_prev) {
159*f0687c8aSRaman Tenneti 			gsurface->release_buffer(bo_prev);
160*f0687c8aSRaman Tenneti 			bo_prev = 0;
161*f0687c8aSRaman Tenneti 		}
162*f0687c8aSRaman Tenneti 	}
163*f0687c8aSRaman Tenneti 
width() const164*f0687c8aSRaman Tenneti 	uint32_t width() const { return m_width; }
height() const165*f0687c8aSRaman Tenneti 	uint32_t height() const { return m_height; }
166*f0687c8aSRaman Tenneti 
167*f0687c8aSRaman Tenneti private:
168*f0687c8aSRaman Tenneti 	Card& card;
169*f0687c8aSRaman Tenneti 	const EglState& egl;
170*f0687c8aSRaman Tenneti 
171*f0687c8aSRaman Tenneti 	unique_ptr<GbmSurface> gsurface;
172*f0687c8aSRaman Tenneti 	EGLSurface esurface;
173*f0687c8aSRaman Tenneti 
174*f0687c8aSRaman Tenneti 	int m_width;
175*f0687c8aSRaman Tenneti 	int m_height;
176*f0687c8aSRaman Tenneti 
177*f0687c8aSRaman Tenneti 	struct gbm_bo* bo_prev;
178*f0687c8aSRaman Tenneti 	struct gbm_bo* bo_next;
179*f0687c8aSRaman Tenneti };
180*f0687c8aSRaman Tenneti 
181*f0687c8aSRaman Tenneti class OutputHandler : private PageFlipHandlerBase
182*f0687c8aSRaman Tenneti {
183*f0687c8aSRaman Tenneti public:
OutputHandler(Card & card,GbmDevice & gdev,const EglState & egl,Connector * connector,Crtc * crtc,Videomode & mode,Plane * root_plane,Plane * plane,float rotation_mult)184*f0687c8aSRaman Tenneti 	OutputHandler(Card& card, GbmDevice& gdev, const EglState& egl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* root_plane, Plane* plane, float rotation_mult)
185*f0687c8aSRaman Tenneti 		: m_frame_num(0), m_connector(connector), m_crtc(crtc), m_root_plane(root_plane), m_plane(plane), m_mode(mode),
186*f0687c8aSRaman Tenneti 		  m_rotation_mult(rotation_mult)
187*f0687c8aSRaman Tenneti 	{
188*f0687c8aSRaman Tenneti 		m_surface1 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, mode.hdisplay, mode.vdisplay));
189*f0687c8aSRaman Tenneti 		m_scene1 = unique_ptr<GlScene>(new GlScene());
190*f0687c8aSRaman Tenneti 		m_scene1->set_viewport(m_surface1->width(), m_surface1->height());
191*f0687c8aSRaman Tenneti 
192*f0687c8aSRaman Tenneti 		if (m_plane) {
193*f0687c8aSRaman Tenneti 			m_surface2 = unique_ptr<GbmEglSurface>(new GbmEglSurface(card, gdev, egl, 400, 400));
194*f0687c8aSRaman Tenneti 			m_scene2 = unique_ptr<GlScene>(new GlScene());
195*f0687c8aSRaman Tenneti 			m_scene2->set_viewport(m_surface2->width(), m_surface2->height());
196*f0687c8aSRaman Tenneti 		}
197*f0687c8aSRaman Tenneti 	}
198*f0687c8aSRaman Tenneti 
199*f0687c8aSRaman Tenneti 	OutputHandler(const OutputHandler& other) = delete;
200*f0687c8aSRaman Tenneti 	OutputHandler& operator=(const OutputHandler& other) = delete;
201*f0687c8aSRaman Tenneti 
setup()202*f0687c8aSRaman Tenneti 	void setup()
203*f0687c8aSRaman Tenneti 	{
204*f0687c8aSRaman Tenneti 		int ret;
205*f0687c8aSRaman Tenneti 
206*f0687c8aSRaman Tenneti 		m_surface1->make_current();
207*f0687c8aSRaman Tenneti 		m_surface1->swap_buffers();
208*f0687c8aSRaman Tenneti 		Framebuffer* fb = m_surface1->lock_next();
209*f0687c8aSRaman Tenneti 
210*f0687c8aSRaman Tenneti 		Framebuffer* planefb = 0;
211*f0687c8aSRaman Tenneti 
212*f0687c8aSRaman Tenneti 		if (m_plane) {
213*f0687c8aSRaman Tenneti 			m_surface2->make_current();
214*f0687c8aSRaman Tenneti 			m_surface2->swap_buffers();
215*f0687c8aSRaman Tenneti 			planefb = m_surface2->lock_next();
216*f0687c8aSRaman Tenneti 		}
217*f0687c8aSRaman Tenneti 
218*f0687c8aSRaman Tenneti 		ret = m_crtc->set_mode(m_connector, *fb, m_mode);
219*f0687c8aSRaman Tenneti 		FAIL_IF(ret, "failed to set mode");
220*f0687c8aSRaman Tenneti 
221*f0687c8aSRaman Tenneti 		if (m_plane) {
222*f0687c8aSRaman Tenneti 			ret = m_crtc->set_plane(m_plane, *planefb,
223*f0687c8aSRaman Tenneti 						0, 0, planefb->width(), planefb->height(),
224*f0687c8aSRaman Tenneti 						0, 0, planefb->width(), planefb->height());
225*f0687c8aSRaman Tenneti 			FAIL_IF(ret, "failed to set plane");
226*f0687c8aSRaman Tenneti 		}
227*f0687c8aSRaman Tenneti 	}
228*f0687c8aSRaman Tenneti 
start_flipping()229*f0687c8aSRaman Tenneti 	void start_flipping()
230*f0687c8aSRaman Tenneti 	{
231*f0687c8aSRaman Tenneti 		m_t1 = chrono::steady_clock::now();
232*f0687c8aSRaman Tenneti 		queue_next();
233*f0687c8aSRaman Tenneti 	}
234*f0687c8aSRaman Tenneti 
235*f0687c8aSRaman Tenneti private:
handle_page_flip(uint32_t frame,double time)236*f0687c8aSRaman Tenneti 	void handle_page_flip(uint32_t frame, double time)
237*f0687c8aSRaman Tenneti 	{
238*f0687c8aSRaman Tenneti 		++m_frame_num;
239*f0687c8aSRaman Tenneti 
240*f0687c8aSRaman Tenneti 		if (m_frame_num % 100 == 0) {
241*f0687c8aSRaman Tenneti 			auto t2 = chrono::steady_clock::now();
242*f0687c8aSRaman Tenneti 			chrono::duration<float> fsec = t2 - m_t1;
243*f0687c8aSRaman Tenneti 			printf("fps: %f\n", 100.0 / fsec.count());
244*f0687c8aSRaman Tenneti 			m_t1 = t2;
245*f0687c8aSRaman Tenneti 		}
246*f0687c8aSRaman Tenneti 
247*f0687c8aSRaman Tenneti 		s_flip_pending--;
248*f0687c8aSRaman Tenneti 
249*f0687c8aSRaman Tenneti 		m_surface1->free_prev();
250*f0687c8aSRaman Tenneti 		if (m_plane)
251*f0687c8aSRaman Tenneti 			m_surface2->free_prev();
252*f0687c8aSRaman Tenneti 
253*f0687c8aSRaman Tenneti 		if (s_need_exit)
254*f0687c8aSRaman Tenneti 			return;
255*f0687c8aSRaman Tenneti 
256*f0687c8aSRaman Tenneti 		queue_next();
257*f0687c8aSRaman Tenneti 	}
258*f0687c8aSRaman Tenneti 
queue_next()259*f0687c8aSRaman Tenneti 	void queue_next()
260*f0687c8aSRaman Tenneti 	{
261*f0687c8aSRaman Tenneti 		m_surface1->make_current();
262*f0687c8aSRaman Tenneti 		m_scene1->draw(m_frame_num * m_rotation_mult);
263*f0687c8aSRaman Tenneti 		m_surface1->swap_buffers();
264*f0687c8aSRaman Tenneti 		Framebuffer* fb = m_surface1->lock_next();
265*f0687c8aSRaman Tenneti 
266*f0687c8aSRaman Tenneti 		Framebuffer* planefb = 0;
267*f0687c8aSRaman Tenneti 
268*f0687c8aSRaman Tenneti 		if (m_plane) {
269*f0687c8aSRaman Tenneti 			m_surface2->make_current();
270*f0687c8aSRaman Tenneti 			m_scene2->draw(m_frame_num * m_rotation_mult * 2);
271*f0687c8aSRaman Tenneti 			m_surface2->swap_buffers();
272*f0687c8aSRaman Tenneti 			planefb = m_surface2->lock_next();
273*f0687c8aSRaman Tenneti 		}
274*f0687c8aSRaman Tenneti 
275*f0687c8aSRaman Tenneti 		int r;
276*f0687c8aSRaman Tenneti 
277*f0687c8aSRaman Tenneti 		AtomicReq req(m_crtc->card());
278*f0687c8aSRaman Tenneti 
279*f0687c8aSRaman Tenneti 		req.add(m_root_plane, "FB_ID", fb->id());
280*f0687c8aSRaman Tenneti 		if (m_plane)
281*f0687c8aSRaman Tenneti 			req.add(m_plane, "FB_ID", planefb->id());
282*f0687c8aSRaman Tenneti 
283*f0687c8aSRaman Tenneti 		r = req.test();
284*f0687c8aSRaman Tenneti 		FAIL_IF(r, "atomic test failed");
285*f0687c8aSRaman Tenneti 
286*f0687c8aSRaman Tenneti 		r = req.commit(this);
287*f0687c8aSRaman Tenneti 		FAIL_IF(r, "atomic commit failed");
288*f0687c8aSRaman Tenneti 
289*f0687c8aSRaman Tenneti 		s_flip_pending++;
290*f0687c8aSRaman Tenneti 	}
291*f0687c8aSRaman Tenneti 
292*f0687c8aSRaman Tenneti 	int m_frame_num;
293*f0687c8aSRaman Tenneti 	chrono::steady_clock::time_point m_t1;
294*f0687c8aSRaman Tenneti 
295*f0687c8aSRaman Tenneti 	Connector* m_connector;
296*f0687c8aSRaman Tenneti 	Crtc* m_crtc;
297*f0687c8aSRaman Tenneti 	Plane* m_root_plane;
298*f0687c8aSRaman Tenneti 	Plane* m_plane;
299*f0687c8aSRaman Tenneti 	Videomode m_mode;
300*f0687c8aSRaman Tenneti 
301*f0687c8aSRaman Tenneti 	unique_ptr<GbmEglSurface> m_surface1;
302*f0687c8aSRaman Tenneti 	unique_ptr<GbmEglSurface> m_surface2;
303*f0687c8aSRaman Tenneti 
304*f0687c8aSRaman Tenneti 	unique_ptr<GlScene> m_scene1;
305*f0687c8aSRaman Tenneti 	unique_ptr<GlScene> m_scene2;
306*f0687c8aSRaman Tenneti 
307*f0687c8aSRaman Tenneti 	float m_rotation_mult;
308*f0687c8aSRaman Tenneti };
309*f0687c8aSRaman Tenneti 
main_gbm()310*f0687c8aSRaman Tenneti void main_gbm()
311*f0687c8aSRaman Tenneti {
312*f0687c8aSRaman Tenneti 	Card card;
313*f0687c8aSRaman Tenneti 
314*f0687c8aSRaman Tenneti 	FAIL_IF(!card.has_atomic(), "No atomic modesetting");
315*f0687c8aSRaman Tenneti 
316*f0687c8aSRaman Tenneti 	GbmDevice gdev(card);
317*f0687c8aSRaman Tenneti 	EglState egl(gdev.handle());
318*f0687c8aSRaman Tenneti 
319*f0687c8aSRaman Tenneti 	ResourceManager resman(card);
320*f0687c8aSRaman Tenneti 
321*f0687c8aSRaman Tenneti 	vector<unique_ptr<OutputHandler>> outputs;
322*f0687c8aSRaman Tenneti 
323*f0687c8aSRaman Tenneti 	float rot_mult = 1;
324*f0687c8aSRaman Tenneti 
325*f0687c8aSRaman Tenneti 	for (Connector* conn : card.get_connectors()) {
326*f0687c8aSRaman Tenneti 		if (!conn->connected())
327*f0687c8aSRaman Tenneti 			continue;
328*f0687c8aSRaman Tenneti 
329*f0687c8aSRaman Tenneti 		resman.reserve_connector(conn);
330*f0687c8aSRaman Tenneti 
331*f0687c8aSRaman Tenneti 		Crtc* crtc = resman.reserve_crtc(conn);
332*f0687c8aSRaman Tenneti 		auto mode = conn->get_default_mode();
333*f0687c8aSRaman Tenneti 
334*f0687c8aSRaman Tenneti 		Plane* root_plane = resman.reserve_generic_plane(crtc);
335*f0687c8aSRaman Tenneti 		FAIL_IF(!root_plane, "Root plane not available");
336*f0687c8aSRaman Tenneti 
337*f0687c8aSRaman Tenneti 		Plane* plane = nullptr;
338*f0687c8aSRaman Tenneti 
339*f0687c8aSRaman Tenneti 		if (s_support_planes)
340*f0687c8aSRaman Tenneti 			plane = resman.reserve_generic_plane(crtc);
341*f0687c8aSRaman Tenneti 
342*f0687c8aSRaman Tenneti 		auto out = new OutputHandler(card, gdev, egl, conn, crtc, mode, root_plane, plane, rot_mult);
343*f0687c8aSRaman Tenneti 		outputs.emplace_back(out);
344*f0687c8aSRaman Tenneti 
345*f0687c8aSRaman Tenneti 		rot_mult *= 1.33;
346*f0687c8aSRaman Tenneti 	}
347*f0687c8aSRaman Tenneti 
348*f0687c8aSRaman Tenneti 	for (auto& out : outputs)
349*f0687c8aSRaman Tenneti 		out->setup();
350*f0687c8aSRaman Tenneti 
351*f0687c8aSRaman Tenneti 	for (auto& out : outputs)
352*f0687c8aSRaman Tenneti 		out->start_flipping();
353*f0687c8aSRaman Tenneti 
354*f0687c8aSRaman Tenneti 	struct pollfd fds[2] = {};
355*f0687c8aSRaman Tenneti 	fds[0].fd = 0;
356*f0687c8aSRaman Tenneti 	fds[0].events = POLLIN;
357*f0687c8aSRaman Tenneti 	fds[1].fd = card.fd();
358*f0687c8aSRaman Tenneti 	fds[1].events = POLLIN;
359*f0687c8aSRaman Tenneti 
360*f0687c8aSRaman Tenneti 	while (!s_need_exit || s_flip_pending) {
361*f0687c8aSRaman Tenneti 		int r = poll(fds, ARRAY_SIZE(fds), -1);
362*f0687c8aSRaman Tenneti 		FAIL_IF(r < 0, "poll error %d", r);
363*f0687c8aSRaman Tenneti 
364*f0687c8aSRaman Tenneti 		if (fds[0].revents)
365*f0687c8aSRaman Tenneti 			s_need_exit = true;
366*f0687c8aSRaman Tenneti 
367*f0687c8aSRaman Tenneti 		if (fds[1].revents)
368*f0687c8aSRaman Tenneti 			card.call_page_flip_handlers();
369*f0687c8aSRaman Tenneti 	}
370*f0687c8aSRaman Tenneti }
371