Use GL_ONE for the source in glBendFunc instead of pre-unmultiplying.
[profile/ivi/wayland.git] / egl-compositor.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <i915_drm.h>
6 #include <sys/ioctl.h>
7 #include <sys/mman.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <linux/fb.h>
11
12 #include "wayland.h"
13
14 #include <GL/gl.h>
15 #include <eagle.h>
16
17 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
18
19 struct egl_compositor {
20         struct wl_compositor base;
21         EGLDisplay display;
22         EGLSurface surface;
23         EGLContext context;
24         struct wl_display *wl_display;
25         int gem_fd;
26 };
27
28 struct surface_data {
29         uint32_t handle;
30         int32_t width, height, stride;
31         GLuint texture;
32         struct wl_map map;
33 };
34
35 static void
36 repaint(void *data)
37 {
38         struct egl_compositor *ec = data;
39         struct wl_surface_iterator *iterator;
40         struct wl_surface *surface;
41         struct surface_data *sd;
42         GLint vertices[12];
43         GLint tex_coords[12] = { 0, 0,  0, 1,  1, 0,  1, 1 };
44         GLuint indices[4] = { 0, 1, 2, 3 };
45
46         iterator = wl_surface_iterator_create(ec->wl_display, 0);
47         while (wl_surface_iterator_next(iterator, &surface)) {
48                 sd = wl_surface_get_data(surface);
49                 if (sd == NULL)
50                         continue;
51
52                 vertices[0] = sd->map.x;
53                 vertices[1] = sd->map.y;
54                 vertices[2] = 0;
55
56                 vertices[3] = sd->map.x;
57                 vertices[4] = sd->map.y + sd->map.height;
58                 vertices[5] = 0;
59
60                 vertices[6] = sd->map.x + sd->map.width;
61                 vertices[7] = sd->map.y;
62                 vertices[8] = 0;
63
64                 vertices[9] = sd->map.x + sd->map.width;
65                 vertices[10] = sd->map.y + sd->map.height;
66                 vertices[11] = 0;
67
68                 glBindTexture(GL_TEXTURE_2D, sd->texture);
69                 glEnable(GL_TEXTURE_2D);
70                 glEnable(GL_BLEND);
71                 /* Assume pre-multiplied alpha for now, this probably
72                  * needs to be a wayland visual type of thing. */
73                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
74
75                 glEnableClientState(GL_VERTEX_ARRAY);
76                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
77                 glVertexPointer(3, GL_INT, 0, vertices);
78                 glTexCoordPointer(2, GL_INT, 0, tex_coords);
79                 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, indices);
80         }
81         wl_surface_iterator_destroy(iterator);
82
83         glFlush();
84
85         eglSwapBuffers(ec->display, ec->surface);
86 }
87
88 static void schedule_repaint(struct egl_compositor *ec)
89 {
90         struct wl_event_loop *loop;
91
92         loop = wl_display_get_event_loop(ec->wl_display);
93         wl_event_loop_add_idle(loop, repaint, ec);
94 }
95
96 void notify_surface_create(struct wl_compositor *compositor,
97                            struct wl_surface *surface)
98 {
99         struct surface_data *sd;
100
101         sd = malloc(sizeof *sd);
102         if (sd == NULL)
103                 return;
104
105         sd->handle = 0;
106         wl_surface_set_data(surface, sd);
107
108         glGenTextures(1, &sd->texture);
109 }
110                                    
111 void notify_surface_destroy(struct wl_compositor *compositor,
112                             struct wl_surface *surface)
113 {
114         struct egl_compositor *ec = (struct egl_compositor *) compositor;
115         struct surface_data *sd;
116         struct drm_gem_close close_arg;
117         int ret;
118
119         sd = wl_surface_get_data(surface);
120         if (sd == NULL || sd->handle == 0)
121                 return;
122         
123         close_arg.handle = sd->handle;
124         ret = ioctl(ec->gem_fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
125         if (ret != 0) {
126                 fprintf(stderr, "failed to gem_close handle %d: %m\n", sd->handle);
127         }
128
129         glDeleteTextures(1, &sd->texture);
130
131         free(sd);
132
133         schedule_repaint(ec);
134 }
135
136 void notify_surface_attach(struct wl_compositor *compositor,
137                            struct wl_surface *surface, uint32_t name, 
138                            uint32_t width, uint32_t height, uint32_t stride)
139 {
140         struct egl_compositor *ec = (struct egl_compositor *) compositor;
141         struct surface_data *sd;
142         struct drm_gem_open open_arg;
143         struct drm_gem_close close_arg;
144         struct drm_i915_gem_pread pread;
145         void *data;
146         uint32_t size;
147         int ret;
148
149         sd = wl_surface_get_data(surface);
150         if (sd == NULL)
151                 return;
152
153         if (sd->handle != 0) {
154                 close_arg.handle = sd->handle;
155                 ret = ioctl(ec->gem_fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
156                 if (ret != 0) {
157                         fprintf(stderr, "failed to gem_close name %d: %m\n", name);
158                 }
159         }
160
161         open_arg.name = name;
162         ret = ioctl(ec->gem_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
163         if (ret != 0) {
164                 fprintf(stderr, "failed to gem_open name %d, fd=%d: %m\n", name, ec->gem_fd);
165                 return;
166         }
167
168         sd->handle = open_arg.handle;
169         sd->width = width;
170         sd->height = height;
171         sd->stride = stride;
172
173         size = sd->height * sd->stride;
174         data = malloc(size);
175         if (data == NULL) {
176                 fprintf(stderr, "swap buffers malloc failed\n");
177                 return;
178         }
179
180         pread.handle = sd->handle;
181         pread.pad = 0;
182         pread.offset = 0;
183         pread.size = size;
184         pread.data_ptr = (long) data;
185
186         if (ioctl(ec->gem_fd, DRM_IOCTL_I915_GEM_PREAD, &pread)) {
187                 fprintf(stderr, "gem pread failed\n");
188                 return;
189         }
190
191         glBindTexture(GL_TEXTURE_2D, sd->texture);
192         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
193         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
194         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
195         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
196         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
197                      GL_BGRA, GL_UNSIGNED_BYTE, data);
198
199         free(data);
200
201         schedule_repaint(ec);
202 }
203
204 void notify_surface_map(struct wl_compositor *compositor,
205                         struct wl_surface *surface, struct wl_map *map)
206 {
207         struct egl_compositor *ec = (struct egl_compositor *) compositor;
208         struct surface_data *sd;
209
210         sd = wl_surface_get_data(surface);
211         if (sd == NULL)
212                 return;
213
214         sd->map = *map;
215
216         schedule_repaint(ec);
217 }
218
219 struct wl_compositor_interface interface = {
220         notify_surface_create,
221         notify_surface_destroy,
222         notify_surface_attach,
223         notify_surface_map
224 };
225
226 static const char gem_device[] = "/dev/dri/card0";
227
228 struct wl_compositor *
229 wl_compositor_create(struct wl_display *display)
230 {
231         EGLConfig configs[64];
232         EGLint major, minor, count;
233         struct egl_compositor *ec;
234         const int width = 1024, height = 768;
235
236         ec = malloc(sizeof *ec);
237         if (ec == NULL)
238                 return NULL;
239
240         ec->base.interface = &interface;
241         ec->wl_display = display;
242
243         ec->display = eglCreateDisplayNative(gem_device, "i965");
244         if (ec->display == NULL) {
245                 fprintf(stderr, "failed to create display\n");
246                 return NULL;
247         }
248
249         if (!eglInitialize(ec->display, &major, &minor)) {
250                 fprintf(stderr, "failed to initialize display\n");
251                 return NULL;
252         }
253
254         if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
255                 fprintf(stderr, "failed to get configs\n");
256                 return NULL;
257         }
258
259         ec->surface = eglCreateSurfaceNative(ec->display, configs[24],
260                                              0, 0, width, height);
261         if (ec->surface == NULL) {
262                 fprintf(stderr, "failed to create surface\n");
263                 return NULL;
264         }
265
266         ec->context = eglCreateContext(ec->display, configs[24], NULL, NULL);
267         if (ec->context == NULL) {
268                 fprintf(stderr, "failed to create context\n");
269                 return NULL;
270         }
271
272         if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
273                 fprintf(stderr, "failed to make context current\n");
274                 return NULL;
275         }
276
277         glViewport(0, 0, width, height);
278         glMatrixMode(GL_PROJECTION);
279         glLoadIdentity();
280         glOrtho(0, width, height, 0, 0, 1000.0);
281         glMatrixMode(GL_MODELVIEW);
282         glClearColor(0.0, 0.05, 0.2, 0.0);
283
284         ec->gem_fd = open(gem_device, O_RDWR);
285         if (ec->gem_fd < 0) {
286                 fprintf(stderr, "failed to open drm device\n");
287                 return NULL;
288         }
289
290         schedule_repaint(ec);
291
292         return &ec->base;
293 }