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