Add kmscube
[android/external-libkmsxx.git] / kmscube / kmscube.cpp
1 /*
2  * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
3  * Copyright (c) 2012 Rob Clark <rob@ti.com>
4  * Copyright (c) 2015 Tomi Valkeinen <tomi.valkeinen@ti.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sub license,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial portions
15  * of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
26 /* Based on a egl cube test app originally written by Arvin Schnell */
28 #include <chrono>
29 #include <cstdio>
30 #include <vector>
31 #include <memory>
32 #include <algorithm>
34 #include <xf86drm.h>
35 #include <xf86drmMode.h>
36 #include <gbm.h>
38 #include "esUtil.h"
40 #include <kms++.h>
41 #include "test.h"
43 using namespace kms;
44 using namespace std;
46 static bool s_verbose;
47 static int s_flip_pending;
48 static bool s_need_exit;
50 class GbmDevice
51 {
52 public:
53         GbmDevice(Card& card)
54         {
55                 m_dev = gbm_create_device(card.fd());
56                 FAIL_IF(!m_dev, "failed to create gbm device");
57         }
59         ~GbmDevice()
60         {
61                 gbm_device_destroy(m_dev);
62         }
64         GbmDevice(const GbmDevice& other) = delete;
65         GbmDevice& operator=(const GbmDevice& other) = delete;
67         operator struct gbm_device*() const { return m_dev; }
69 private:
70         struct gbm_device* m_dev;
71 };
73 class GbmSurface
74 {
75 public:
76         GbmSurface(GbmDevice& gdev, int width, int height)
77         {
78                 m_surface = gbm_surface_create(gdev, width, height,
79                                                GBM_FORMAT_XRGB8888,
80                                                GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
81                 FAIL_IF(!m_surface, "failed to create gbm surface");
82         }
84         ~GbmSurface()
85         {
86                 gbm_surface_destroy(m_surface);
87         }
89         GbmSurface(const GbmSurface& other) = delete;
90         GbmSurface& operator=(const GbmSurface& other) = delete;
92         operator struct gbm_surface*() const { return m_surface; }
93 private:
94         struct gbm_surface* m_surface;
95 };
97 struct GLState {
98         GLState(GbmDevice& gdev)
99         {
100                 EGLint major, minor, n;
101                 GLuint vertex_shader, fragment_shader;
102                 GLint ret;
103                 EGLBoolean b;
105 #include "cube.h"
107                 static const EGLint context_attribs[] = {
108                         EGL_CONTEXT_CLIENT_VERSION, 2,
109                         EGL_NONE
110                 };
112                 static const EGLint config_attribs[] = {
113                         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
114                         EGL_RED_SIZE, 8,
115                         EGL_GREEN_SIZE, 8,
116                         EGL_BLUE_SIZE, 8,
117                         EGL_ALPHA_SIZE, 0,
118                         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
119                         EGL_NONE
120                 };
122                 static const char *vertex_shader_source =
123                                 "uniform mat4 modelviewMatrix;      \n"
124                                 "uniform mat4 modelviewprojectionMatrix;\n"
125                                 "uniform mat3 normalMatrix;         \n"
126                                 "                                   \n"
127                                 "attribute vec4 in_position;        \n"
128                                 "attribute vec3 in_normal;          \n"
129                                 "attribute vec4 in_color;           \n"
130                                 "\n"
131                                 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
132                                 "                                   \n"
133                                 "varying vec4 vVaryingColor;        \n"
134                                 "                                   \n"
135                                 "void main()                        \n"
136                                 "{                                  \n"
137                                 "    gl_Position = modelviewprojectionMatrix * in_position;\n"
138                                 "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
139                                 "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
140                                 "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
141                                 "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
142                                 "    float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
143                                 "    vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
144                                 "}                                  \n";
146                 static const char *fragment_shader_source =
147                                 "precision mediump float;           \n"
148                                 "                                   \n"
149                                 "varying vec4 vVaryingColor;        \n"
150                                 "                                   \n"
151                                 "void main()                        \n"
152                                 "{                                  \n"
153                                 "    gl_FragColor = vVaryingColor;  \n"
154                                 "}                                  \n";
156                 m_display = eglGetDisplay(gdev);
157                 FAIL_IF(!m_display, "failed to get egl display");
159                 b = eglInitialize(m_display, &major, &minor);
160                 FAIL_IF(!b, "failed to initialize");
162                 if (s_verbose) {
163                         printf("Using display %p with EGL version %d.%d\n", m_display, major, minor);
165                         printf("EGL_VENDOR:      %s\n", eglQueryString(m_display, EGL_VENDOR));
166                         printf("EGL_VERSION:     %s\n", eglQueryString(m_display, EGL_VERSION));
167                         printf("EGL_EXTENSIONS:  %s\n", eglQueryString(m_display, EGL_EXTENSIONS));
168                         printf("EGL_CLIENT_APIS: %s\n", eglQueryString(m_display, EGL_CLIENT_APIS));
169                 }
171                 b = eglBindAPI(EGL_OPENGL_ES_API);
172                 FAIL_IF(!b, "failed to bind api EGL_OPENGL_ES_API");
174                 b = eglChooseConfig(m_display, config_attribs, &m_config, 1, &n);
175                 FAIL_IF(!b || n != 1, "failed to choose config");
177                 auto getconf = [this](EGLint a) { EGLint v = -1; eglGetConfigAttrib(m_display, m_config, a, &v); return v; };
179                 if (s_verbose) {
180                         printf("EGL Config %d: color buf %d/%d/%d/%d = %d, depth %d, stencil %d\n",
181                                getconf(EGL_CONFIG_ID),
182                                getconf(EGL_ALPHA_SIZE),
183                                getconf(EGL_RED_SIZE),
184                                getconf(EGL_GREEN_SIZE),
185                                getconf(EGL_BLUE_SIZE),
186                                getconf(EGL_BUFFER_SIZE),
187                                getconf(EGL_DEPTH_SIZE),
188                                getconf(EGL_STENCIL_SIZE));
189                 }
191                 m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs);
192                 FAIL_IF(!m_context, "failed to create context");
194                 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context);
196                 if (s_verbose) {
197                         printf("GL_VENDOR:       %s\n", glGetString(GL_VENDOR));
198                         printf("GL_VERSION:      %s\n", glGetString(GL_VERSION));
199                         printf("GL_RENDERER:     %s\n", glGetString(GL_RENDERER));
200                         printf("GL_EXTENSIONS:   %s\n", glGetString(GL_EXTENSIONS));
201                 }
203                 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
205                 glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
206                 glCompileShader(vertex_shader);
208                 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
209                 FAIL_IF(!ret, "vertex shader compilation failed!");
211                 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
213                 glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
214                 glCompileShader(fragment_shader);
216                 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
217                 FAIL_IF(!ret, "fragment shader compilation failed!");
219                 GLuint program = glCreateProgram();
221                 glAttachShader(program, vertex_shader);
222                 glAttachShader(program, fragment_shader);
224                 glBindAttribLocation(program, 0, "in_position");
225                 glBindAttribLocation(program, 1, "in_normal");
226                 glBindAttribLocation(program, 2, "in_color");
228                 glLinkProgram(program);
230                 glGetProgramiv(program, GL_LINK_STATUS, &ret);
231                 FAIL_IF(!ret, "program linking failed!");
233                 glUseProgram(program);
235                 m_modelviewmatrix = glGetUniformLocation(program, "modelviewMatrix");
236                 m_modelviewprojectionmatrix = glGetUniformLocation(program, "modelviewprojectionMatrix");
237                 m_normalmatrix = glGetUniformLocation(program, "normalMatrix");
239                 glEnable(GL_CULL_FACE);
241                 GLintptr positionsoffset = 0;
242                 GLintptr colorsoffset = sizeof(vVertices);
243                 GLintptr normalsoffset = sizeof(vVertices) + sizeof(vColors);
244                 GLuint vbo;
246                 glGenBuffers(1, &vbo);
247                 glBindBuffer(GL_ARRAY_BUFFER, vbo);
248                 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
249                 glBufferSubData(GL_ARRAY_BUFFER, positionsoffset, sizeof(vVertices), &vVertices[0]);
250                 glBufferSubData(GL_ARRAY_BUFFER, colorsoffset, sizeof(vColors), &vColors[0]);
251                 glBufferSubData(GL_ARRAY_BUFFER, normalsoffset, sizeof(vNormals), &vNormals[0]);
252                 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)positionsoffset);
253                 glEnableVertexAttribArray(0);
254                 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)normalsoffset);
255                 glEnableVertexAttribArray(1);
256                 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)colorsoffset);
257                 glEnableVertexAttribArray(2);
258         }
260         ~GLState()
261         {
262                 eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
263                 eglTerminate(m_display);
264         }
266         GLState(const GLState& other) = delete;
267         GLState& operator=(const GLState& other) = delete;
269         EGLDisplay display() const { return m_display; }
270         EGLConfig config() const { return m_config; }
271         EGLContext context() const { return m_context; }
273         GLint modelviewmatrix() const { return m_modelviewmatrix; }
274         GLint modelviewprojectionmatrix() const { return m_modelviewprojectionmatrix; }
275         GLint normalmatrix() const { return m_normalmatrix; }
277 private:
278         EGLDisplay m_display;
279         EGLConfig m_config;
280         EGLContext m_context;
281         GLint m_modelviewmatrix, m_modelviewprojectionmatrix, m_normalmatrix;
282 };
284 struct Surface
286         Surface(Card& card, GbmDevice& gdev, const GLState& gl, int width, int height)
287                 : card(card), gdev(gdev), gl(gl), gsurface(gdev, width, height), m_width(width), m_height(height),
288                   bo_prev(0), bo_next(0)
289         {
290                 esurface = eglCreateWindowSurface(gl.display(), gl.config(), gsurface, NULL);
291                 FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface");
292         }
294         ~Surface()
295         {
296                 if (bo_next)
297                         gbm_surface_release_buffer(gsurface, bo_next);
298                 eglDestroySurface(gl.display(), esurface);
299         }
301         void make_current()
302         {
303                 eglMakeCurrent(gl.display(), esurface, esurface, gl.context());
304                 glViewport(0, 0, m_width, m_height);
305         }
307         void clear()
308         {
309                 glClearColor(0.5, 0.5, 0.5, 1.0);
310                 glClear(GL_COLOR_BUFFER_BIT);
311         }
313         static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
314         {
315                 auto fb = reinterpret_cast<Framebuffer*>(data);
316                 delete fb;
317         }
319         static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card)
320         {
321                 auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo));
322                 if (fb)
323                         return fb;
325                 uint32_t width = gbm_bo_get_width(bo);
326                 uint32_t height = gbm_bo_get_height(bo);
327                 uint32_t stride = gbm_bo_get_stride(bo);
328                 uint32_t handle = gbm_bo_get_handle(bo).u32;
330                 fb = new ExtFramebuffer(card, width, height, 24, 32, stride, handle);
332                 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
334                 return fb;
335         }
337         struct Framebuffer* lock_next()
338         {
339                 bo_prev = bo_next;
340                 eglSwapBuffers(gl.display(), esurface);
341                 bo_next = gbm_surface_lock_front_buffer(gsurface);
342                 FAIL_IF(!bo_next, "could not lock gbm buffer");
343                 return drm_fb_get_from_bo(bo_next, card);
344         }
346         void free_prev()
347         {
348                 if (bo_prev) {
349                         gbm_surface_release_buffer(gsurface, bo_prev);
350                         bo_prev = 0;
351                 }
352         }
354         Card& card;
355         GbmDevice& gdev;
356         const GLState& gl;
358         GbmSurface gsurface;
359         EGLSurface esurface;
361         int m_width;
362         int m_height;
364         struct gbm_bo* bo_prev;
365         struct gbm_bo* bo_next;
366 };
368 static void draw(uint32_t framenum, Surface& surface)
370         const GLState& gl = surface.gl;
372         ESMatrix modelview;
374         esMatrixLoadIdentity(&modelview);
375         esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
376         esRotate(&modelview, 45.0f + (0.75f * framenum), 1.0f, 0.0f, 0.0f);
377         esRotate(&modelview, 45.0f - (0.5f * framenum), 0.0f, 1.0f, 0.0f);
378         esRotate(&modelview, 10.0f + (0.45f * framenum), 0.0f, 0.0f, 1.0f);
380         GLfloat aspect = (GLfloat)(surface.m_height) / (GLfloat)(surface.m_width);
382         ESMatrix projection;
383         esMatrixLoadIdentity(&projection);
384         esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
386         ESMatrix modelviewprojection;
387         esMatrixLoadIdentity(&modelviewprojection);
388         esMatrixMultiply(&modelviewprojection, &modelview, &projection);
390         float normal[9];
391         normal[0] = modelview.m[0][0];
392         normal[1] = modelview.m[0][1];
393         normal[2] = modelview.m[0][2];
394         normal[3] = modelview.m[1][0];
395         normal[4] = modelview.m[1][1];
396         normal[5] = modelview.m[1][2];
397         normal[6] = modelview.m[2][0];
398         normal[7] = modelview.m[2][1];
399         normal[8] = modelview.m[2][2];
401         glUniformMatrix4fv(gl.modelviewmatrix(), 1, GL_FALSE, &modelview.m[0][0]);
402         glUniformMatrix4fv(gl.modelviewprojectionmatrix(), 1, GL_FALSE, &modelviewprojection.m[0][0]);
403         glUniformMatrix3fv(gl.normalmatrix(), 1, GL_FALSE, normal);
405         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
406         glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
407         glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
408         glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
409         glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
410         glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
413 class OutputHandler : private PageFlipHandlerBase
415 public:
416         OutputHandler(Card& card, GbmDevice& gdev, const GLState& gl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult)
417                 : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_plane(plane), m_mode(mode),
418                   m_rotation_mult(rotation_mult)
419         {
420                 m_surface = unique_ptr<Surface>(new Surface(card, gdev, gl, mode.hdisplay, mode.vdisplay));
421                 if (m_plane)
422                         m_surface2 = unique_ptr<Surface>(new Surface(card, gdev, gl, 400, 400));
423         }
425         OutputHandler(const OutputHandler& other) = delete;
426         OutputHandler& operator=(const OutputHandler& other) = delete;
428         void setup()
429         {
430                 int ret;
432                 m_surface->make_current();
433                 m_surface->clear();
434                 struct Framebuffer* fb = m_surface->lock_next();
436                 struct Framebuffer* planefb = 0;
438                 if (m_plane) {
439                         m_surface2->make_current();
440                         m_surface2->clear();
441                         planefb = m_surface2->lock_next();
442                 }
445                 ret = m_crtc->set_mode(m_connector, *fb, m_mode);
446                 FAIL_IF(ret, "failed to set mode");
448                 if (m_plane) {
449                         ret = m_crtc->set_plane(m_plane, *planefb,
450                                                 0, 0, planefb->width(), planefb->height(),
451                                                 0, 0, planefb->width(), planefb->height());
452                         FAIL_IF(ret, "failed to set plane");
453                 }
454         }
456         void start_flipping()
457         {
458                 m_t1 = chrono::steady_clock::now();
459                 queue_next();
460         }
462 private:
463         void handle_page_flip(uint32_t frame, double time)
464         {
465                 ++m_frame_num;
467                 if (m_frame_num  % 100 == 0) {
468                         auto t2 = chrono::steady_clock::now();
469                         chrono::duration<float> fsec = t2 - m_t1;
470                         printf("fps: %f\n", 100.0 / fsec.count());
471                         m_t1 = t2;
472                 }
474                 s_flip_pending--;
476                 m_surface->free_prev();
477                 if (m_plane)
478                         m_surface2->free_prev();
480                 if (s_need_exit)
481                         return;
483                 queue_next();
484         }
486         void queue_next()
487         {
488                 m_surface->make_current();
489                 m_surface->clear();
490                 draw(m_frame_num * m_rotation_mult, *m_surface);
491                 struct Framebuffer* fb = m_surface->lock_next();
493                 struct Framebuffer* planefb = 0;
495                 if (m_plane) {
496                         m_surface2->make_current();
497                         m_surface2->clear();
498                         draw(m_frame_num * m_rotation_mult * 2, *m_surface2);
499                         planefb = m_surface2->lock_next();
500                 }
502                 if (m_crtc->card().has_atomic()) {
503                         int r;
505                         AtomicReq req(m_crtc->card());
507                         req.add(m_crtc, "FB_ID", fb->id());
508                         if (m_plane)
509                                 req.add(m_plane, "FB_ID", planefb->id());
511                         r = req.test();
512                         FAIL_IF(r, "atomic test failed");
514                         r = req.commit(this);
515                         FAIL_IF(r, "atomic commit failed");
516                 } else {
517                         int ret;
519                         ret = m_crtc->page_flip(*fb, this);
520                         FAIL_IF(ret, "failed to queue page flip");
522                         if (m_plane) {
523                                 ret = m_crtc->set_plane(m_plane, *planefb,
524                                                         0, 0, planefb->width(), planefb->height(),
525                                                         0, 0, planefb->width(), planefb->height());
526                                 FAIL_IF(ret, "failed to set plane");
527                         }
528                 }
530                 s_flip_pending++;
531         }
533         int m_frame_num;
534         chrono::steady_clock::time_point m_t1;
536         Connector* m_connector;
537         Crtc* m_crtc;
538         Plane* m_plane;
539         Videomode m_mode;
541         unique_ptr<Surface> m_surface;
542         unique_ptr<Surface> m_surface2;
544         float m_rotation_mult;
545 };
547 int main(int argc, char *argv[])
549         for (int i = 1; i < argc; ++i) {
550                 if (argv[i] == string("-v"))
551                         s_verbose = true;
552         }
554         Card card;
556         GbmDevice gdev(card);
557         const GLState gl(gdev);
559         vector<unique_ptr<OutputHandler>> outputs;
560         vector<Plane*> used_planes;
562         float rot_mult = 1;
564         for (auto pipe : card.get_connected_pipelines()) {
565                 auto connector = pipe.connector;
566                 auto crtc = pipe.crtc;
567                 auto mode = connector->get_default_mode();
569                 Plane* plane = 0;
571                 for (Plane* p : crtc->get_possible_planes()) {
572                         if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end())
573                                 continue;
575                         if (p->plane_type() != PlaneType::Overlay)
576                                 continue;
578                         plane = p;
579                         break;
580                 }
582                 if (plane)
583                         used_planes.push_back(plane);
585                 auto out = new OutputHandler(card, gdev, gl, connector, crtc, mode, plane, rot_mult);
586                 outputs.emplace_back(out);
588                 rot_mult *= 1.33;
589         }
591         for (auto& out : outputs)
592                 out->setup();
594         for (auto& out : outputs)
595                 out->start_flipping();
597         while (!s_need_exit || s_flip_pending) {
598                 fd_set fds;
599                 FD_ZERO(&fds);
600                 if (!s_need_exit)
601                         FD_SET(0, &fds);
602                 FD_SET(card.fd(), &fds);
604                 int ret = select(card.fd() + 1, &fds, NULL, NULL, NULL);
606                 FAIL_IF(ret < 0, "select error: %d", ret);
607                 FAIL_IF(ret == 0, "select timeout");
609                 if (FD_ISSET(0, &fds))
610                         s_need_exit = true;
612                 if (FD_ISSET(card.fd(), &fds))
613                         card.call_page_flip_handlers();
614         }
616         return 0;