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 
33 #include "stw_st.h"
34 #include "stw_device.h"
35 #include "stw_framebuffer.h"
36 #include "stw_pixelformat.h"
37 
38 struct stw_st_framebuffer {
39    struct st_framebuffer_iface base;
40 
41    struct stw_framebuffer *fb;
42    struct st_visual stvis;
43 
44    struct pipe_resource *textures[ST_ATTACHMENT_COUNT];
45    struct pipe_resource *msaa_textures[ST_ATTACHMENT_COUNT];
46    unsigned texture_width, texture_height;
47    unsigned texture_mask;
48 };
49 
50 static uint32_t stwfb_ID = 0;
51 
52 /**
53  * Is the given mutex held by the calling thread?
54  */
55 bool
stw_own_mutex(const CRITICAL_SECTION * cs)56 stw_own_mutex(const CRITICAL_SECTION *cs)
57 {
58    // We can't compare OwningThread with our thread handle/id (see
59    // http://stackoverflow.com/a/12675635 ) but we can compare with the
60    // OwningThread member of a critical section we know we own.
61    CRITICAL_SECTION dummy;
62    InitializeCriticalSection(&dummy);
63    EnterCriticalSection(&dummy);
64    if (0)
65       _debug_printf("%p %p\n", cs->OwningThread, dummy.OwningThread);
66    bool ret = cs->OwningThread == dummy.OwningThread;
67    LeaveCriticalSection(&dummy);
68    DeleteCriticalSection(&dummy);
69    return ret;
70 }
71 
72 
73 /**
74  * Remove outdated textures and create the requested ones.
75  */
76 static void
stw_st_framebuffer_validate_locked(struct st_framebuffer_iface * stfb,unsigned width,unsigned height,unsigned mask)77 stw_st_framebuffer_validate_locked(struct st_framebuffer_iface *stfb,
78                                    unsigned width, unsigned height,
79                                    unsigned mask)
80 {
81    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
82    struct pipe_resource templ;
83    unsigned i;
84 
85    /* remove outdated textures */
86    if (stwfb->texture_width != width || stwfb->texture_height != height) {
87       for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
88          pipe_resource_reference(&stwfb->msaa_textures[i], NULL);
89          pipe_resource_reference(&stwfb->textures[i], NULL);
90       }
91    }
92 
93    memset(&templ, 0, sizeof(templ));
94    templ.target = PIPE_TEXTURE_2D;
95    templ.width0 = width;
96    templ.height0 = height;
97    templ.depth0 = 1;
98    templ.array_size = 1;
99    templ.last_level = 0;
100 
101    for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
102       enum pipe_format format;
103       unsigned bind;
104 
105       /* the texture already exists or not requested */
106       if (stwfb->textures[i] || !(mask & (1 << i))) {
107          /* remember the texture */
108          if (stwfb->textures[i])
109             mask |= (1 << i);
110          continue;
111       }
112 
113       switch (i) {
114       case ST_ATTACHMENT_FRONT_LEFT:
115       case ST_ATTACHMENT_BACK_LEFT:
116          format = stwfb->stvis.color_format;
117          bind = PIPE_BIND_DISPLAY_TARGET |
118                 PIPE_BIND_SAMPLER_VIEW |
119                 PIPE_BIND_RENDER_TARGET;
120          break;
121       case ST_ATTACHMENT_DEPTH_STENCIL:
122          format = stwfb->stvis.depth_stencil_format;
123          bind = PIPE_BIND_DEPTH_STENCIL;
124          break;
125       default:
126          format = PIPE_FORMAT_NONE;
127          break;
128       }
129 
130       if (format != PIPE_FORMAT_NONE) {
131          templ.format = format;
132 
133          if (bind != PIPE_BIND_DEPTH_STENCIL && stwfb->stvis.samples > 1) {
134             templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
135             templ.nr_samples = templ.nr_storage_samples =
136                stwfb->stvis.samples;
137 
138             stwfb->msaa_textures[i] =
139                stw_dev->screen->resource_create(stw_dev->screen, &templ);
140          }
141 
142          templ.bind = bind;
143          templ.nr_samples = templ.nr_storage_samples = 1;
144          stwfb->textures[i] =
145             stw_dev->screen->resource_create(stw_dev->screen, &templ);
146       }
147    }
148 
149    stwfb->texture_width = width;
150    stwfb->texture_height = height;
151    stwfb->texture_mask = mask;
152 }
153 
154 static bool
stw_st_framebuffer_validate(struct st_context_iface * stctx,struct st_framebuffer_iface * stfb,const enum st_attachment_type * statts,unsigned count,struct pipe_resource ** out)155 stw_st_framebuffer_validate(struct st_context_iface *stctx,
156                             struct st_framebuffer_iface *stfb,
157                             const enum st_attachment_type *statts,
158                             unsigned count,
159                             struct pipe_resource **out)
160 {
161    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
162    unsigned statt_mask, i;
163 
164    statt_mask = 0x0;
165    for (i = 0; i < count; i++)
166       statt_mask |= 1 << statts[i];
167 
168    stw_framebuffer_lock(stwfb->fb);
169 
170    if (stwfb->fb->must_resize || (statt_mask & ~stwfb->texture_mask)) {
171       stw_st_framebuffer_validate_locked(&stwfb->base,
172             stwfb->fb->width, stwfb->fb->height, statt_mask);
173       stwfb->fb->must_resize = FALSE;
174    }
175 
176    struct pipe_resource **textures =
177       stwfb->stvis.samples > 1 ? stwfb->msaa_textures
178                                : stwfb->textures;
179 
180    for (i = 0; i < count; i++)
181       pipe_resource_reference(&out[i], textures[statts[i]]);
182 
183    stw_framebuffer_unlock(stwfb->fb);
184 
185    return true;
186 }
187 
188 static void
stw_pipe_blit(struct pipe_context * pipe,struct pipe_resource * dst,struct pipe_resource * src)189 stw_pipe_blit(struct pipe_context *pipe,
190               struct pipe_resource *dst,
191               struct pipe_resource *src)
192 {
193    struct pipe_blit_info blit;
194 
195    /* From the GL spec, version 4.2, section 4.1.11 (Additional Multisample
196     *  Fragment Operations):
197     *
198     *      If a framebuffer object is not bound, after all operations have
199     *      been completed on the multisample buffer, the sample values for
200     *      each color in the multisample buffer are combined to produce a
201     *      single color value, and that value is written into the
202     *      corresponding color buffers selected by DrawBuffer or
203     *      DrawBuffers. An implementation may defer the writing of the color
204     *      buffers until a later time, but the state of the framebuffer must
205     *      behave as if the color buffers were updated as each fragment was
206     *      processed. The method of combination is not specified. If the
207     *      framebuffer contains sRGB values, then it is recommended that the
208     *      an average of sample values is computed in a linearized space, as
209     *      for blending (see section 4.1.7).
210     *
211     * In other words, to do a resolve operation in a linear space, we have
212     * to set sRGB formats if the original resources were sRGB, so don't use
213     * util_format_linear.
214     */
215 
216    memset(&blit, 0, sizeof(blit));
217    blit.dst.resource = dst;
218    blit.dst.box.width = dst->width0;
219    blit.dst.box.height = dst->height0;
220    blit.dst.box.depth = 1;
221    blit.dst.format = dst->format;
222    blit.src.resource = src;
223    blit.src.box.width = src->width0;
224    blit.src.box.height = src->height0;
225    blit.src.box.depth = 1;
226    blit.src.format = src->format;
227    blit.mask = PIPE_MASK_RGBA;
228    blit.filter = PIPE_TEX_FILTER_NEAREST;
229 
230    pipe->blit(pipe, &blit);
231 }
232 
233 /**
234  * Present an attachment of the framebuffer.
235  */
236 static bool
stw_st_framebuffer_present_locked(HDC hdc,struct st_context_iface * stctx,struct st_framebuffer_iface * stfb,enum st_attachment_type statt)237 stw_st_framebuffer_present_locked(HDC hdc,
238                                   struct st_context_iface *stctx,
239                                   struct st_framebuffer_iface *stfb,
240                                   enum st_attachment_type statt)
241 {
242    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
243    struct pipe_resource *resource;
244 
245    assert(stw_own_mutex(&stwfb->fb->mutex));
246 
247    if (stwfb->stvis.samples > 1) {
248       stw_pipe_blit(stctx->pipe,
249                     stwfb->textures[statt],
250                     stwfb->msaa_textures[statt]);
251    }
252 
253    resource = stwfb->textures[statt];
254    if (resource) {
255       stw_framebuffer_present_locked(hdc, stwfb->fb, resource);
256    }
257    else {
258       stw_framebuffer_unlock(stwfb->fb);
259    }
260 
261    assert(!stw_own_mutex(&stwfb->fb->mutex));
262 
263    return true;
264 }
265 
266 static bool
stw_st_framebuffer_flush_front(struct st_context_iface * stctx,struct st_framebuffer_iface * stfb,enum st_attachment_type statt)267 stw_st_framebuffer_flush_front(struct st_context_iface *stctx,
268                                struct st_framebuffer_iface *stfb,
269                                enum st_attachment_type statt)
270 {
271    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
272    bool ret;
273    HDC hDC;
274 
275    stw_framebuffer_lock(stwfb->fb);
276 
277    /* We must not cache HDCs anywhere, as they can be invalidated by the
278     * application, or screen resolution changes. */
279 
280    hDC = GetDC(stwfb->fb->hWnd);
281 
282    ret = stw_st_framebuffer_present_locked(hDC, stctx, &stwfb->base, statt);
283 
284    ReleaseDC(stwfb->fb->hWnd, hDC);
285 
286    return ret;
287 }
288 
289 /**
290  * Create a framebuffer interface.
291  */
292 struct st_framebuffer_iface *
stw_st_create_framebuffer(struct stw_framebuffer * fb)293 stw_st_create_framebuffer(struct stw_framebuffer *fb)
294 {
295    struct stw_st_framebuffer *stwfb;
296 
297    stwfb = CALLOC_STRUCT(stw_st_framebuffer);
298    if (!stwfb)
299       return NULL;
300 
301    stwfb->fb = fb;
302    stwfb->stvis = fb->pfi->stvis;
303    stwfb->base.ID = p_atomic_inc_return(&stwfb_ID);
304    stwfb->base.state_manager = stw_dev->smapi;
305 
306    stwfb->base.visual = &stwfb->stvis;
307    p_atomic_set(&stwfb->base.stamp, 1);
308    stwfb->base.flush_front = stw_st_framebuffer_flush_front;
309    stwfb->base.validate = stw_st_framebuffer_validate;
310 
311    return &stwfb->base;
312 }
313 
314 /**
315  * Destroy a framebuffer interface.
316  */
317 void
stw_st_destroy_framebuffer_locked(struct st_framebuffer_iface * stfb)318 stw_st_destroy_framebuffer_locked(struct st_framebuffer_iface *stfb)
319 {
320    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
321    int i;
322 
323    for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
324       pipe_resource_reference(&stwfb->msaa_textures[i], NULL);
325       pipe_resource_reference(&stwfb->textures[i], NULL);
326    }
327 
328    /* Notify the st manager that the framebuffer interface is no
329     * longer valid.
330     */
331    stw_dev->stapi->destroy_drawable(stw_dev->stapi, &stwfb->base);
332 
333    FREE(stwfb);
334 }
335 
336 /**
337  * Swap the buffers of the given framebuffer.
338  */
339 bool
stw_st_swap_framebuffer_locked(HDC hdc,struct st_context_iface * stctx,struct st_framebuffer_iface * stfb)340 stw_st_swap_framebuffer_locked(HDC hdc, struct st_context_iface *stctx,
341                                struct st_framebuffer_iface *stfb)
342 {
343    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
344    unsigned front = ST_ATTACHMENT_FRONT_LEFT, back = ST_ATTACHMENT_BACK_LEFT;
345    struct pipe_resource *ptex;
346    unsigned mask;
347 
348    /* swap the textures */
349    ptex = stwfb->textures[front];
350    stwfb->textures[front] = stwfb->textures[back];
351    stwfb->textures[back] = ptex;
352 
353    /* swap msaa_textures */
354    ptex = stwfb->msaa_textures[front];
355    stwfb->msaa_textures[front] = stwfb->msaa_textures[back];
356    stwfb->msaa_textures[back] = ptex;
357 
358    /* convert to mask */
359    front = 1 << front;
360    back = 1 << back;
361 
362    /* swap the bits in mask */
363    mask = stwfb->texture_mask & ~(front | back);
364    if (stwfb->texture_mask & front)
365       mask |= back;
366    if (stwfb->texture_mask & back)
367       mask |= front;
368    stwfb->texture_mask = mask;
369 
370    front = ST_ATTACHMENT_FRONT_LEFT;
371    return stw_st_framebuffer_present_locked(hdc, stctx, &stwfb->base, front);
372 }
373 
374 
375 /**
376  * Return the pipe_resource that correspond to given buffer.
377  */
378 struct pipe_resource *
stw_get_framebuffer_resource(struct st_framebuffer_iface * stfb,enum st_attachment_type att)379 stw_get_framebuffer_resource(struct st_framebuffer_iface *stfb,
380                              enum st_attachment_type att)
381 {
382    struct stw_st_framebuffer *stwfb = stw_st_framebuffer(stfb);
383    return stwfb->textures[att];
384 }
385 
386 
387 /**
388  * Create an st_api of the gallium frontend.
389  */
390 struct st_api *
stw_st_create_api(void)391 stw_st_create_api(void)
392 {
393    return st_gl_api_create();
394 }
395