1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2010 LunarG Inc.
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, sublicense,
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 shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors:
25 *    Chia-I Wu <olv@lunarg.com>
26 */
27
28#include "util/u_memory.h"
29#include "util/u_inlines.h"
30#include "util/u_atomic.h"
31#include "state_tracker/st_gl_api.h" /* for st_gl_api_create */
32#include "pipe/p_state.h"
33
34#include "stw_st.h"
35#include "stw_device.h"
36#include "stw_framebuffer.h"
37#include "stw_pixelformat.h"
38#include "stw_winsys.h"
39
40struct stw_st_framebuffer {
41   struct st_framebuffer_iface base;
42
43   struct stw_framebuffer *fb;
44   struct st_visual stvis;
45
46   struct pipe_resource *textures[ST_ATTACHMENT_COUNT];
47   struct pipe_resource *msaa_textures[ST_ATTACHMENT_COUNT];
48   struct pipe_resource *back_texture;
49   bool needs_fake_front;
50   unsigned texture_width, texture_height;
51   unsigned texture_mask;
52};
53
54static uint32_t stwfb_ID = 0;
55
56/**
57 * Is the given mutex held by the calling thread?
58 */
59bool
60stw_own_mutex(const CRITICAL_SECTION *cs)
61{
62   // We can't compare OwningThread with our thread handle/id (see
63   // http://stackoverflow.com/a/12675635 ) but we can compare with the
64   // OwningThread member of a critical section we know we own.
65   CRITICAL_SECTION dummy;
66   InitializeCriticalSection(&dummy);
67   EnterCriticalSection(&dummy);
68   if (0)
69      _debug_printf("%p %p\n", cs->OwningThread, dummy.OwningThread);
70   bool ret = cs->OwningThread == dummy.OwningThread;
71   LeaveCriticalSection(&dummy);
72   DeleteCriticalSection(&dummy);
73   return ret;
74}
75
76static void
77stw_pipe_blit(struct pipe_context *pipe,
78              struct pipe_resource *dst,
79              struct pipe_resource *src)
80{
81   struct pipe_blit_info blit;
82
83   if (!dst || !src)
84      return;
85
86   /* From the GL spec, version 4.2, section 4.1.11 (Additional Multisample
87    *  Fragment Operations):
88    *
89    *      If a framebuffer object is not bound, after all operations have
90    *      been completed on the multisample buffer, the sample values for
91    *      each color in the multisample buffer are combined to produce a
92    *      single color value, and that value is written into the
93    *      corresponding color buffers selected by DrawBuffer or
94    *      DrawBuffers. An implementation may defer the writing of the color
95    *      buffers until a later time, but the state of the framebuffer must
96    *      behave as if the color buffers were updated as each fragment was
97    *      processed. The method of combination is not specified. If the
98    *      framebuffer contains sRGB values, then it is recommended that the
99    *      an average of sample values is computed in a linearized space, as
100    *      for blending (see section 4.1.7).
101    *
102    * In other words, to do a resolve operation in a linear space, we have
103    * to set sRGB formats if the original resources were sRGB, so don't use
104    * util_format_linear.
105    */
106
107   memset(&blit, 0, sizeof(blit));
108   blit.dst.resource = dst;
109   blit.dst.box.width = dst->width0;
110   blit.dst.box.height = dst->height0;
111   blit.dst.box.depth = 1;
112   blit.dst.format = dst->format;
113   blit.src.resource = src;
114   blit.src.box.width = src->width0;
115   blit.src.box.height = src->height0;
116   blit.src.box.depth = 1;
117   blit.src.format = src->format;
118   blit.mask = PIPE_MASK_RGBA;
119   blit.filter = PIPE_TEX_FILTER_NEAREST;
120
121   pipe->blit(pipe, &blit);
122}
123
124/**
125 * Remove outdated textures and create the requested ones.
126 */
127static void
128stw_st_framebuffer_validate_locked(struct st_context_iface *stctx,
129                                   struct st_framebuffer_iface *stfb,
130                                   unsigned width, unsigned height,
131                                   unsigned mask)
132{
133   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
134   struct pipe_resource templ;
135   unsigned i;
136
137   memset(&templ, 0, sizeof(templ));
138   templ.target = PIPE_TEXTURE_2D;
139   templ.width0 = width;
140   templ.height0 = height;
141   templ.depth0 = 1;
142   templ.array_size = 1;
143   templ.last_level = 0;
144
145   /* As of now, the only stw_winsys_framebuffer implementation is
146    * d3d12_wgl_framebuffer and it doesn't support front buffer
147    * drawing. A fake front texture is needed to handle that scenario */
148   if (mask & ST_ATTACHMENT_FRONT_LEFT_MASK &&
149       stwfb->fb->winsys_framebuffer &&
150       stwfb->stvis.samples <= 1) {
151      stwfb->needs_fake_front = true;
152   }
153
154   /* remove outdated textures */
155   if (stwfb->texture_width != width || stwfb->texture_height != height) {
156      for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
157         pipe_resource_reference(&stwfb->msaa_textures[i], NULL);
158         pipe_resource_reference(&stwfb->textures[i], NULL);
159      }
160      pipe_resource_reference(&stwfb->back_texture, NULL);
161
162      if (stwfb->fb->winsys_framebuffer) {
163         templ.nr_samples = templ.nr_storage_samples = 1;
164         templ.format = stwfb->stvis.color_format;
165         stwfb->fb->winsys_framebuffer->resize(stwfb->fb->winsys_framebuffer, stctx->pipe, &templ);
166      }
167   }
168
169   for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
170      enum pipe_format format;
171      unsigned bind;
172
173      /* the texture already exists or not requested */
174      if (stwfb->textures[i] || !(mask & (1 << i))) {
175         /* remember the texture */
176         if (stwfb->textures[i])
177            mask |= (1 << i);
178         continue;
179      }
180
181      switch (i) {
182      case ST_ATTACHMENT_FRONT_LEFT:
183      case ST_ATTACHMENT_BACK_LEFT:
184         format = stwfb->stvis.color_format;
185         bind = PIPE_BIND_DISPLAY_TARGET |
186                PIPE_BIND_SAMPLER_VIEW |
187                PIPE_BIND_RENDER_TARGET;
188         break;
189      case ST_ATTACHMENT_DEPTH_STENCIL:
190         format = stwfb->stvis.depth_stencil_format;
191         bind = PIPE_BIND_DEPTH_STENCIL;
192         break;
193      default:
194         format = PIPE_FORMAT_NONE;
195         break;
196      }
197
198      if (format != PIPE_FORMAT_NONE) {
199         templ.format = format;
200
201         if (bind != PIPE_BIND_DEPTH_STENCIL && stwfb->stvis.samples > 1) {
202            templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
203            templ.nr_samples = templ.nr_storage_samples =
204               stwfb->stvis.samples;
205
206            stwfb->msaa_textures[i] =
207               stw_dev->screen->resource_create(stw_dev->screen, &templ);
208         }
209
210         templ.bind = bind;
211         templ.nr_samples = templ.nr_storage_samples = 1;
212         if (stwfb->fb->winsys_framebuffer)
213            stwfb->textures[i] = stwfb->fb->winsys_framebuffer->get_resource(
214               stwfb->fb->winsys_framebuffer, i);
215         else
216            stwfb->textures[i] =
217               stw_dev->screen->resource_create(stw_dev->screen, &templ);
218      }
219   }
220
221   /* When a fake front buffer is needed for drawing, we use the back buffer
222    * as it reduces the number of blits needed:
223    *
224    *   - When flushing the front buffer, we only need to swap the real buffers
225    *   - When swapping buffers, we can safely overwrite the fake front buffer
226    *     as it is now invalid anyway.
227    *
228    * A new texture is created to store the back buffer content.
229    */
230   if (stwfb->needs_fake_front) {
231      if (!stwfb->back_texture) {
232         templ.format = stwfb->stvis.color_format;
233         templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
234         templ.nr_samples = templ.nr_storage_samples = 1;
235
236         stwfb->back_texture =
237            stw_dev->screen->resource_create(stw_dev->screen, &templ);
238
239         /* TODO Only blit if there is something currently drawn on the back buffer */
240         stw_pipe_blit(stctx->pipe,
241                       stwfb->back_texture,
242                       stwfb->textures[ST_ATTACHMENT_BACK_LEFT]);
243      }
244
245      /* Copying front texture content to fake front texture (back texture) */
246      stw_pipe_blit(stctx->pipe,
247                    stwfb->textures[ST_ATTACHMENT_BACK_LEFT],
248                    stwfb->textures[ST_ATTACHMENT_FRONT_LEFT]);
249   }
250
251   stwfb->texture_width = width;
252   stwfb->texture_height = height;
253   stwfb->texture_mask = mask;
254}
255
256static bool
257stw_st_framebuffer_validate(struct st_context_iface *stctx,
258                            struct st_framebuffer_iface *stfb,
259                            const enum st_attachment_type *statts,
260                            unsigned count,
261                            struct pipe_resource **out)
262{
263   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
264   unsigned statt_mask, i;
265
266   statt_mask = 0x0;
267   for (i = 0; i < count; i++)
268      statt_mask |= 1 << statts[i];
269
270   stw_framebuffer_lock(stwfb->fb);
271
272   if (stwfb->fb->must_resize || stwfb->needs_fake_front || (statt_mask & ~stwfb->texture_mask)) {
273      stw_st_framebuffer_validate_locked(stctx, &stwfb->base,
274            stwfb->fb->width, stwfb->fb->height, statt_mask);
275      stwfb->fb->must_resize = FALSE;
276   }
277
278   struct pipe_resource **textures =
279      stwfb->stvis.samples > 1 ? stwfb->msaa_textures
280                               : stwfb->textures;
281
282   for (i = 0; i < count; i++) {
283      struct pipe_resource *texture = NULL;
284
285      if (stwfb->needs_fake_front) {
286         if (statts[i] == ST_ATTACHMENT_FRONT_LEFT)
287            texture = textures[ST_ATTACHMENT_BACK_LEFT]; /* Fake front buffer */
288         else if (statts[i] == ST_ATTACHMENT_BACK_LEFT)
289            texture = stwfb->back_texture;
290      } else {
291         texture = textures[statts[i]];
292      }
293      pipe_resource_reference(&out[i], texture);
294   }
295
296   stw_framebuffer_unlock(stwfb->fb);
297
298   return true;
299}
300
301struct notify_before_flush_cb_args {
302   struct st_context_iface *stctx;
303   struct stw_st_framebuffer *stwfb;
304   unsigned flags;
305};
306
307static void
308notify_before_flush_cb(void* _args)
309{
310   struct notify_before_flush_cb_args *args = (struct notify_before_flush_cb_args *) _args;
311   struct st_context_iface *st = args->stctx;
312   struct pipe_context *pipe = st->pipe;
313
314   if (args->stwfb->stvis.samples > 1) {
315      /* Resolve the MSAA back buffer. */
316      stw_pipe_blit(pipe,
317                    args->stwfb->textures[ST_ATTACHMENT_BACK_LEFT],
318                    args->stwfb->msaa_textures[ST_ATTACHMENT_BACK_LEFT]);
319
320      /* FRONT_LEFT is resolved in flush_frontbuffer. */
321   } else if (args->stwfb->back_texture) {
322      /* Fake front texture is dirty ?? */
323      stw_pipe_blit(pipe,
324                    args->stwfb->textures[ST_ATTACHMENT_BACK_LEFT],
325                    args->stwfb->back_texture);
326   }
327
328   if (args->stwfb->textures[ST_ATTACHMENT_BACK_LEFT])
329      pipe->flush_resource(pipe, args->stwfb->textures[ST_ATTACHMENT_BACK_LEFT]);
330}
331
332void
333stw_st_flush(struct st_context_iface *stctx,
334             struct st_framebuffer_iface *stfb,
335             unsigned flags)
336{
337   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
338   struct notify_before_flush_cb_args args;
339   struct pipe_fence_handle **pfence = NULL;
340   struct pipe_fence_handle *fence = NULL;
341
342   args.stctx = stctx;
343   args.stwfb = stwfb;
344   args.flags = flags;
345
346   if (flags & ST_FLUSH_END_OF_FRAME && !stwfb->fb->winsys_framebuffer)
347      flags |= ST_FLUSH_WAIT;
348
349   if (flags & ST_FLUSH_WAIT)
350      pfence = &fence;
351   stctx->flush(stctx, flags, pfence, notify_before_flush_cb, &args);
352}
353
354/**
355 * Present an attachment of the framebuffer.
356 */
357static bool
358stw_st_framebuffer_present_locked(HDC hdc,
359                                  struct st_context_iface *stctx,
360                                  struct st_framebuffer_iface *stfb,
361                                  enum st_attachment_type statt)
362{
363   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
364   struct pipe_resource *resource;
365
366   assert(stw_own_mutex(&stwfb->fb->mutex));
367
368   resource = stwfb->textures[statt];
369   if (resource) {
370      stw_framebuffer_present_locked(hdc, stwfb->fb, resource);
371   }
372   else {
373      stw_framebuffer_unlock(stwfb->fb);
374   }
375
376   assert(!stw_own_mutex(&stwfb->fb->mutex));
377
378   return true;
379}
380
381static bool
382stw_st_framebuffer_flush_front(struct st_context_iface *stctx,
383                               struct st_framebuffer_iface *stfb,
384                               enum st_attachment_type statt)
385{
386   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
387   struct pipe_context *pipe = stctx->pipe;
388   bool ret;
389   HDC hDC;
390
391   if (statt != ST_ATTACHMENT_FRONT_LEFT)
392      return false;
393
394   stw_framebuffer_lock(stwfb->fb);
395
396   /* Resolve the front buffer. */
397   if (stwfb->stvis.samples > 1) {
398      stw_pipe_blit(pipe, stwfb->textures[statt], stwfb->msaa_textures[statt]);
399   } else if (stwfb->needs_fake_front) {
400      struct pipe_resource *ptex;
401
402      /* swap the textures */
403      ptex = stwfb->textures[ST_ATTACHMENT_FRONT_LEFT];
404      stwfb->textures[ST_ATTACHMENT_FRONT_LEFT] = stwfb->textures[ST_ATTACHMENT_BACK_LEFT];
405      stwfb->textures[ST_ATTACHMENT_BACK_LEFT] = ptex;
406
407      /* fake front texture is now invalid */
408      p_atomic_inc(&stwfb->base.stamp);
409   }
410
411   if (stwfb->textures[statt])
412      pipe->flush_resource(pipe, stwfb->textures[statt]);
413
414   pipe->flush(pipe, NULL, 0);
415
416   /* We must not cache HDCs anywhere, as they can be invalidated by the
417    * application, or screen resolution changes. */
418
419   hDC = GetDC(stwfb->fb->hWnd);
420
421   ret = stw_st_framebuffer_present_locked(hDC, stctx, &stwfb->base, statt);
422
423   ReleaseDC(stwfb->fb->hWnd, hDC);
424
425   return ret;
426}
427
428/**
429 * Create a framebuffer interface.
430 */
431struct st_framebuffer_iface *
432stw_st_create_framebuffer(struct stw_framebuffer *fb)
433{
434   struct stw_st_framebuffer *stwfb;
435
436   stwfb = CALLOC_STRUCT(stw_st_framebuffer);
437   if (!stwfb)
438      return NULL;
439
440   stwfb->fb = fb;
441   stwfb->stvis = fb->pfi->stvis;
442   stwfb->base.ID = p_atomic_inc_return(&stwfb_ID);
443   stwfb->base.state_manager = stw_dev->smapi;
444
445   stwfb->base.visual = &stwfb->stvis;
446   p_atomic_set(&stwfb->base.stamp, 1);
447   stwfb->base.flush_front = stw_st_framebuffer_flush_front;
448   stwfb->base.validate = stw_st_framebuffer_validate;
449
450   return &stwfb->base;
451}
452
453/**
454 * Destroy a framebuffer interface.
455 */
456void
457stw_st_destroy_framebuffer_locked(struct st_framebuffer_iface *stfb)
458{
459   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
460   int i;
461
462   for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
463      pipe_resource_reference(&stwfb->msaa_textures[i], NULL);
464      pipe_resource_reference(&stwfb->textures[i], NULL);
465   }
466   pipe_resource_reference(&stwfb->back_texture, NULL);
467
468   /* Notify the st manager that the framebuffer interface is no
469    * longer valid.
470    */
471   stw_dev->stapi->destroy_drawable(stw_dev->stapi, &stwfb->base);
472
473   FREE(stwfb);
474}
475
476/**
477 * Swap the buffers of the given framebuffer.
478 */
479bool
480stw_st_swap_framebuffer_locked(HDC hdc, struct st_context_iface *stctx,
481                               struct st_framebuffer_iface *stfb)
482{
483   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
484   unsigned front = ST_ATTACHMENT_FRONT_LEFT, back = ST_ATTACHMENT_BACK_LEFT;
485   struct pipe_resource *ptex;
486   unsigned mask;
487
488   /* swap the textures */
489   ptex = stwfb->textures[front];
490   stwfb->textures[front] = stwfb->textures[back];
491   stwfb->textures[back] = ptex;
492
493   /* swap msaa_textures */
494   ptex = stwfb->msaa_textures[front];
495   stwfb->msaa_textures[front] = stwfb->msaa_textures[back];
496   stwfb->msaa_textures[back] = ptex;
497
498
499   /* Fake front texture is now dirty */
500   if (stwfb->needs_fake_front)
501      p_atomic_inc(&stwfb->base.stamp);
502
503   /* convert to mask */
504   front = 1 << front;
505   back = 1 << back;
506
507   /* swap the bits in mask */
508   mask = stwfb->texture_mask & ~(front | back);
509   if (stwfb->texture_mask & front)
510      mask |= back;
511   if (stwfb->texture_mask & back)
512      mask |= front;
513   stwfb->texture_mask = mask;
514
515   front = ST_ATTACHMENT_FRONT_LEFT;
516   return stw_st_framebuffer_present_locked(hdc, stctx, &stwfb->base, front);
517}
518
519
520/**
521 * Return the pipe_resource that correspond to given buffer.
522 */
523struct pipe_resource *
524stw_get_framebuffer_resource(struct st_framebuffer_iface *stfb,
525                             enum st_attachment_type att)
526{
527   struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
528   return stwfb->textures[att];
529}
530
531
532/**
533 * Create an st_api of the gallium frontend.
534 */
535struct st_api *
536stw_st_create_api(void)
537{
538   return st_gl_api_create();
539}
540