native_gdi.c revision a8e195ec24dc244d975cbd3526debc2c22e717be
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.9
4 *
5 * Copyright (C) 2010 LunarG Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions 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 NONINFRINGEMENT.  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 *
25 * Authors:
26 *    Chia-I Wu <olv@lunarg.com>
27 */
28
29#include <windows.h>
30
31#include "pipe/p_compiler.h"
32#include "util/u_memory.h"
33#include "util/u_format.h"
34#include "util/u_inlines.h"
35#include "target-helpers/wrap_screen.h"
36#include "llvmpipe/lp_public.h"
37#include "softpipe/sp_public.h"
38#include "gdi/gdi_sw_winsys.h"
39
40#include "common/native.h"
41
42struct gdi_display {
43   struct native_display base;
44
45   HDC hDC;
46   struct native_event_handler *event_handler;
47
48   struct native_config *configs;
49   int num_configs;
50};
51
52struct gdi_surface {
53   struct native_surface base;
54
55   HWND hWnd;
56   enum pipe_format color_format;
57
58   struct gdi_display *gdpy;
59
60   unsigned int server_stamp;
61   unsigned int client_stamp;
62   int width, height;
63   uint valid_mask;
64
65   struct pipe_resource *resources[NUM_NATIVE_ATTACHMENTS];
66   struct pipe_surface *present_surface;
67};
68
69static INLINE struct gdi_display *
70gdi_display(const struct native_display *ndpy)
71{
72   return (struct gdi_display *) ndpy;
73}
74
75static INLINE struct gdi_surface *
76gdi_surface(const struct native_surface *nsurf)
77{
78   return (struct gdi_surface *) nsurf;
79}
80
81static boolean
82gdi_surface_alloc_buffer(struct native_surface *nsurf,
83                         enum native_attachment which)
84{
85   struct gdi_surface *gsurf = gdi_surface(nsurf);
86   struct pipe_screen *screen = gsurf->gdpy->base.screen;
87   struct pipe_resource templ;
88
89   pipe_resource_reference(&gsurf->resources[which], NULL);
90
91   memset(&templ, 0, sizeof(templ));
92   templ.target = PIPE_TEXTURE_2D;
93   templ.format = gsurf->color_format;
94   templ.width0 = gsurf->width;
95   templ.height0 = gsurf->height;
96   templ.depth0 = 1;
97   templ.bind = PIPE_BIND_RENDER_TARGET |
98                PIPE_BIND_SCANOUT |
99                PIPE_BIND_DISPLAY_TARGET;
100
101   gsurf->resources[which] = screen->resource_create(screen, &templ);
102
103   return (gsurf->resources[which] != NULL);
104}
105
106/**
107 * Update the geometry of the surface.  Return TRUE if the geometry has changed
108 * since last call.
109 */
110static boolean
111gdi_surface_update_geometry(struct native_surface *nsurf)
112{
113   struct gdi_surface *gsurf = gdi_surface(nsurf);
114   RECT rect;
115   unsigned int w, h;
116   boolean updated = FALSE;
117
118   GetClientRect(gsurf->hWnd, &rect);
119   w = rect.right - rect.left;
120   h = rect.bottom - rect.top;
121
122   if (gsurf->width != w || gsurf->height != h) {
123      gsurf->width = w;
124      gsurf->height = h;
125
126      gsurf->server_stamp++;
127      updated = TRUE;
128   }
129
130   return updated;
131}
132
133/**
134 * Update the buffers of the surface.  It is a slow function due to the
135 * round-trip to the server.
136 */
137static boolean
138gdi_surface_update_buffers(struct native_surface *nsurf, uint buffer_mask)
139{
140   struct gdi_surface *gsurf = gdi_surface(nsurf);
141   boolean updated;
142   uint new_valid;
143   int att;
144
145   updated = gdi_surface_update_geometry(&gsurf->base);
146   if (updated) {
147      /* all buffers become invalid */
148      gsurf->valid_mask = 0x0;
149   }
150   else {
151      buffer_mask &= ~gsurf->valid_mask;
152      /* all requested buffers are valid */
153      if (!buffer_mask) {
154         gsurf->client_stamp = gsurf->server_stamp;
155         return TRUE;
156      }
157   }
158
159   new_valid = 0x0;
160   for (att = 0; att < NUM_NATIVE_ATTACHMENTS; att++) {
161      if (native_attachment_mask_test(buffer_mask, att)) {
162         /* reallocate the texture */
163         if (!gdi_surface_alloc_buffer(&gsurf->base, att))
164            break;
165
166         new_valid |= (1 << att);
167         if (buffer_mask == new_valid)
168            break;
169      }
170   }
171
172   gsurf->valid_mask |= new_valid;
173   gsurf->client_stamp = gsurf->server_stamp;
174
175   return (new_valid == buffer_mask);
176}
177
178static boolean
179gdi_surface_present(struct native_surface *nsurf,
180                    enum native_attachment which)
181{
182   struct gdi_surface *gsurf = gdi_surface(nsurf);
183   struct pipe_screen *screen = gsurf->gdpy->base.screen;
184   struct pipe_resource *pres = gsurf->resources[which];
185   struct pipe_surface *psurf;
186   HDC hDC;
187
188   if (!pres)
189      return TRUE;
190
191   psurf = gsurf->present_surface;
192   if (!psurf || psurf->texture != pres) {
193      pipe_surface_reference(&gsurf->present_surface, NULL);
194
195      psurf = screen->get_tex_surface(screen, pres,
196            0, 0, 0, PIPE_BIND_DISPLAY_TARGET);
197      if (!psurf)
198         return FALSE;
199
200      gsurf->present_surface = psurf;
201   }
202
203   hDC = GetDC(gsurf->hWnd);
204   screen->flush_frontbuffer(screen, psurf, (void *) hDC);
205   ReleaseDC(gsurf->hWnd, hDC);
206
207   return TRUE;
208}
209
210static void
211gdi_surface_notify_invalid(struct native_surface *nsurf)
212{
213   struct gdi_surface *gsurf = gdi_surface(nsurf);
214   struct gdi_display *gdpy = gsurf->gdpy;
215
216   gdpy->event_handler->invalid_surface(&gdpy->base,
217         &gsurf->base, gsurf->server_stamp);
218}
219
220static boolean
221gdi_surface_flush_frontbuffer(struct native_surface *nsurf)
222{
223   struct gdi_surface *gsurf = gdi_surface(nsurf);
224   boolean ret;
225
226   ret = gdi_surface_present(&gsurf->base, NATIVE_ATTACHMENT_FRONT_LEFT);
227   /* force buffers to be updated in next validation call */
228   gsurf->server_stamp++;
229   gdi_surface_notify_invalid(&gsurf->base);
230
231   return ret;
232}
233
234static boolean
235gdi_surface_swap_buffers(struct native_surface *nsurf)
236{
237   struct gdi_surface *gsurf = gdi_surface(nsurf);
238   struct pipe_resource **front, **back, *tmp;
239   boolean ret;
240
241   /* display the back buffer first */
242   ret = gdi_surface_present(&gsurf->base, NATIVE_ATTACHMENT_BACK_LEFT);
243   /* force buffers to be updated in next validation call */
244   gsurf->server_stamp++;
245   gdi_surface_notify_invalid(&gsurf->base);
246
247   front = &gsurf->resources[NATIVE_ATTACHMENT_FRONT_LEFT];
248   back = &gsurf->resources[NATIVE_ATTACHMENT_BACK_LEFT];
249
250   /* skip swapping unless there is a front buffer */
251   if (*front) {
252      tmp = *front;
253      *front = *back;
254      *back = tmp;
255   }
256
257   return ret;
258}
259
260static boolean
261gdi_surface_validate(struct native_surface *nsurf, uint attachment_mask,
262                        unsigned int *seq_num, struct pipe_resource **textures,
263                        int *width, int *height)
264{
265   struct gdi_surface *gsurf = gdi_surface(nsurf);
266
267   if (gsurf->client_stamp != gsurf->server_stamp ||
268       (gsurf->valid_mask & attachment_mask) != attachment_mask) {
269      if (!gdi_surface_update_buffers(&gsurf->base, attachment_mask))
270         return FALSE;
271   }
272
273   if (seq_num)
274      *seq_num = gsurf->client_stamp;
275
276   if (textures) {
277      int att;
278      for (att = 0; att < NUM_NATIVE_ATTACHMENTS; att++) {
279         if (native_attachment_mask_test(attachment_mask, att)) {
280            textures[att] = NULL;
281            pipe_resource_reference(&textures[att], gsurf->resources[att]);
282         }
283      }
284   }
285
286   if (width)
287      *width = gsurf->width;
288   if (height)
289      *height = gsurf->height;
290
291   return TRUE;
292}
293
294static void
295gdi_surface_wait(struct native_surface *nsurf)
296{
297   /* no-op */
298}
299
300static void
301gdi_surface_destroy(struct native_surface *nsurf)
302{
303   struct gdi_surface *gsurf = gdi_surface(nsurf);
304   int i;
305
306   pipe_surface_reference(&gsurf->present_surface, NULL);
307
308   for (i = 0; i < NUM_NATIVE_ATTACHMENTS; i++)
309      pipe_resource_reference(&gsurf->resources[i], NULL);
310
311   FREE(gsurf);
312}
313
314static struct native_surface *
315gdi_display_create_window_surface(struct native_display *ndpy,
316                                  EGLNativeWindowType win,
317                                  const struct native_config *nconf)
318{
319   struct gdi_display *gdpy = gdi_display(ndpy);
320   struct gdi_surface *gsurf;
321
322   gsurf = CALLOC_STRUCT(gdi_surface);
323   if (!gsurf)
324      return NULL;
325
326   gsurf->gdpy = gdpy;
327   gsurf->color_format = nconf->color_format;
328   gsurf->hWnd = (HWND) win;
329
330   /* initialize the geometry */
331   gdi_surface_update_buffers(&gsurf->base, 0x0);
332
333   gsurf->base.destroy = gdi_surface_destroy;
334   gsurf->base.swap_buffers = gdi_surface_swap_buffers;
335   gsurf->base.flush_frontbuffer = gdi_surface_flush_frontbuffer;
336   gsurf->base.validate = gdi_surface_validate;
337   gsurf->base.wait = gdi_surface_wait;
338
339   return &gsurf->base;
340}
341
342static int
343fill_color_formats(struct native_display *ndpy, enum pipe_format formats[8])
344{
345   struct pipe_screen *screen = ndpy->screen;
346   int i, count = 0;
347
348   enum pipe_format candidates[] = {
349      /* 32-bit */
350      PIPE_FORMAT_B8G8R8A8_UNORM,
351      PIPE_FORMAT_A8R8G8B8_UNORM,
352      /* 24-bit */
353      PIPE_FORMAT_B8G8R8X8_UNORM,
354      PIPE_FORMAT_X8R8G8B8_UNORM,
355      /* 16-bit */
356      PIPE_FORMAT_B5G6R5_UNORM
357   };
358
359   assert(Elements(candidates) <= 8);
360
361   for (i = 0; i < Elements(candidates); i++) {
362      if (screen->is_format_supported(screen, candidates[i],
363               PIPE_TEXTURE_2D, 0, PIPE_BIND_RENDER_TARGET, 0))
364         formats[count++] = candidates[i];
365   }
366
367   return count;
368}
369
370static const struct native_config **
371gdi_display_get_configs(struct native_display *ndpy, int *num_configs)
372{
373   struct gdi_display *gdpy = gdi_display(ndpy);
374   const struct native_config **configs;
375   int i;
376
377   /* first time */
378   if (!gdpy->configs) {
379      enum pipe_format formats[8];
380      int i, count;
381
382      count = fill_color_formats(&gdpy->base, formats);
383
384      gdpy->configs = CALLOC(count, sizeof(*gdpy->configs));
385      if (!gdpy->configs)
386         return NULL;
387
388      for (i = 0; i < count; i++) {
389         struct native_config *nconf = &gdpy->configs[i];
390
391         nconf->buffer_mask =
392            (1 << NATIVE_ATTACHMENT_FRONT_LEFT) |
393            (1 << NATIVE_ATTACHMENT_BACK_LEFT);
394         nconf->color_format = formats[i];
395
396         nconf->window_bit = TRUE;
397         nconf->slow_config = TRUE;
398      }
399
400      gdpy->num_configs = count;
401   }
402
403   configs = MALLOC(gdpy->num_configs * sizeof(*configs));
404   if (configs) {
405      for (i = 0; i < gdpy->num_configs; i++)
406         configs[i] = (const struct native_config *) &gdpy->configs[i];
407      if (num_configs)
408         *num_configs = gdpy->num_configs;
409   }
410   return configs;
411}
412
413static int
414gdi_display_get_param(struct native_display *ndpy,
415                         enum native_param_type param)
416{
417   int val;
418
419   switch (param) {
420   case NATIVE_PARAM_USE_NATIVE_BUFFER:
421      /* private buffers are allocated */
422      val = FALSE;
423      break;
424   default:
425      val = 0;
426      break;
427   }
428
429   return val;
430}
431
432static void
433gdi_display_destroy(struct native_display *ndpy)
434{
435   struct gdi_display *gdpy = gdi_display(ndpy);
436
437   if (gdpy->configs)
438      FREE(gdpy->configs);
439
440   gdpy->base.screen->destroy(gdpy->base.screen);
441
442   FREE(gdpy);
443}
444
445static struct native_display *
446gdi_create_display(HDC hDC, struct pipe_screen *screen,
447                   struct native_event_handler *event_handler)
448{
449   struct gdi_display *gdpy;
450
451   gdpy = CALLOC_STRUCT(gdi_display);
452   if (!gdpy)
453      return NULL;
454
455   gdpy->hDC = hDC;
456   gdpy->event_handler = event_handler;
457
458   gdpy->base.screen = screen;
459
460   gdpy->base.destroy = gdi_display_destroy;
461   gdpy->base.get_param = gdi_display_get_param;
462
463   gdpy->base.get_configs = gdi_display_get_configs;
464   gdpy->base.create_window_surface = gdi_display_create_window_surface;
465
466   return &gdpy->base;
467}
468
469static struct pipe_screen *
470gdi_create_screen(void)
471{
472   struct sw_winsys *winsys;
473   struct pipe_screen *screen = NULL;
474
475   winsys = gdi_create_sw_winsys();
476   if (!winsys)
477      return NULL;
478
479#if defined(GALLIUM_LLVMPIPE)
480   if (!screen && !debug_get_bool_option("GALLIUM_NO_LLVM", FALSE))
481      screen = llvmpipe_create_screen(winsys);
482#endif
483   if (!screen)
484      screen = softpipe_create_screen(winsys);
485
486   if (!screen) {
487      if (winsys->destroy)
488         winsys->destroy(winsys);
489      return NULL;
490   }
491
492   return gallium_wrap_screen(screen);
493}
494
495struct native_probe *
496native_create_probe(EGLNativeDisplayType dpy)
497{
498   return NULL;
499}
500
501enum native_probe_result
502native_get_probe_result(struct native_probe *nprobe)
503{
504   return NATIVE_PROBE_UNKNOWN;
505}
506
507const char *
508native_get_name(void)
509{
510   return "GDI";
511}
512
513struct native_display *
514native_create_display(EGLNativeDisplayType dpy,
515                      struct native_event_handler *event_handler)
516{
517   struct pipe_screen *screen;
518
519   screen = gdi_create_screen();
520   if (!screen)
521      return NULL;
522
523   return gdi_create_display((HDC) dpy, screen, event_handler);
524}
525