[D3D8][D3D9][DDRAW][WINED3D] Sync with Wine Staging 2.9. This work couldn't have...
[reactos.git] / reactos / dll / directx / wine / wined3d / swapchain.c
1 /*
2 * Copyright 2002-2003 Jason Edmeades
3 * Copyright 2002-2003 Raphael Junqueira
4 * Copyright 2005 Oliver Stieber
5 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6 * Copyright 2011 Henri Verbeet for CodeWeavers
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #include "wined3d_private.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
26 WINE_DECLARE_DEBUG_CHANNEL(fps);
27
28 static void wined3d_swapchain_destroy_object(void *object)
29 {
30 swapchain_destroy_contexts(object);
31 }
32
33 static void swapchain_cleanup(struct wined3d_swapchain *swapchain)
34 {
35 HRESULT hr;
36 UINT i;
37
38 TRACE("Destroying swapchain %p.\n", swapchain);
39
40 wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma);
41
42 /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0]
43 * is the last buffer to be destroyed, FindContext() depends on that. */
44 if (swapchain->front_buffer)
45 {
46 wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
47 if (wined3d_texture_decref(swapchain->front_buffer))
48 WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer);
49 swapchain->front_buffer = NULL;
50 }
51
52 if (swapchain->back_buffers)
53 {
54 i = swapchain->desc.backbuffer_count;
55
56 while (i--)
57 {
58 wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
59 if (wined3d_texture_decref(swapchain->back_buffers[i]))
60 WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]);
61 }
62 HeapFree(GetProcessHeap(), 0, swapchain->back_buffers);
63 swapchain->back_buffers = NULL;
64 }
65
66 wined3d_cs_destroy_object(swapchain->device->cs, wined3d_swapchain_destroy_object, swapchain);
67 swapchain->device->cs->ops->finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT);
68
69 /* Restore the screen resolution if we rendered in fullscreen.
70 * This will restore the screen resolution to what it was before creating
71 * the swapchain. In case of d3d8 and d3d9 this will be the original
72 * desktop resolution. In case of d3d7 this will be a NOP because ddraw
73 * sets the resolution before starting up Direct3D, thus orig_width and
74 * orig_height will be equal to the modes in the presentation params. */
75 if (!swapchain->desc.windowed && swapchain->desc.auto_restore_display_mode)
76 {
77 if (FAILED(hr = wined3d_set_adapter_display_mode(swapchain->device->wined3d,
78 swapchain->device->adapter->ordinal, &swapchain->original_mode)))
79 ERR("Failed to restore display mode, hr %#x.\n", hr);
80
81 if (swapchain->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
82 {
83 wined3d_device_restore_fullscreen_window(swapchain->device, swapchain->device_window,
84 &swapchain->original_window_rect);
85 wined3d_device_release_focus_window(swapchain->device);
86 }
87 }
88
89 if (swapchain->backup_dc)
90 {
91 TRACE("Destroying backup wined3d window %p, dc %p.\n", swapchain->backup_wnd, swapchain->backup_dc);
92
93 wined3d_release_dc(swapchain->backup_wnd, swapchain->backup_dc);
94 DestroyWindow(swapchain->backup_wnd);
95 }
96 }
97
98 ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain)
99 {
100 ULONG refcount = InterlockedIncrement(&swapchain->ref);
101
102 TRACE("%p increasing refcount to %u.\n", swapchain, refcount);
103
104 return refcount;
105 }
106
107 ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain)
108 {
109 ULONG refcount = InterlockedDecrement(&swapchain->ref);
110
111 TRACE("%p decreasing refcount to %u.\n", swapchain, refcount);
112
113 if (!refcount)
114 {
115 struct wined3d_device *device = swapchain->device;
116
117 device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
118
119 swapchain_cleanup(swapchain);
120 swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent);
121 HeapFree(GetProcessHeap(), 0, swapchain);
122 }
123
124 return refcount;
125 }
126
127 void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain)
128 {
129 TRACE("swapchain %p.\n", swapchain);
130
131 return swapchain->parent;
132 }
133
134 void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window)
135 {
136 if (!window)
137 window = swapchain->device_window;
138 if (window == swapchain->win_handle)
139 return;
140
141 TRACE("Setting swapchain %p window from %p to %p.\n",
142 swapchain, swapchain->win_handle, window);
143 swapchain->win_handle = window;
144 }
145
146 HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain,
147 const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, DWORD flags)
148 {
149 static DWORD notified_flags = 0;
150 RECT s, d;
151
152 TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, flags %#x.\n",
153 swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect),
154 dst_window_override, flags);
155
156 if (flags & ~notified_flags)
157 {
158 FIXME("Ignoring flags %#x.\n", flags & ~notified_flags);
159 notified_flags |= flags;
160 }
161
162 if (!swapchain->back_buffers)
163 {
164 WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL\n");
165 return WINED3DERR_INVALIDCALL;
166 }
167
168 if (!src_rect)
169 {
170 SetRect(&s, 0, 0, swapchain->desc.backbuffer_width,
171 swapchain->desc.backbuffer_height);
172 src_rect = &s;
173 }
174
175 if (!dst_rect)
176 {
177 GetClientRect(swapchain->win_handle, &d);
178 dst_rect = &d;
179 }
180
181 wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect,
182 dst_rect, dst_window_override, flags);
183
184 return WINED3D_OK;
185 }
186
187 HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain,
188 struct wined3d_texture *dst_texture, unsigned int sub_resource_idx)
189 {
190 RECT src_rect, dst_rect;
191
192 TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx);
193
194 SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height);
195 dst_rect = src_rect;
196
197 if (swapchain->desc.windowed)
198 {
199 MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2);
200 FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n",
201 wine_dbgstr_rect(&dst_rect));
202 }
203
204 return wined3d_texture_blt(dst_texture, sub_resource_idx, &dst_rect,
205 swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT);
206 }
207
208 struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain,
209 UINT back_buffer_idx)
210 {
211 TRACE("swapchain %p, back_buffer_idx %u.\n",
212 swapchain, back_buffer_idx);
213
214 /* Return invalid if there is no backbuffer array, otherwise it will
215 * crash when ddraw is used (there swapchain->back_buffers is always
216 * NULL). We need this because this function is called from
217 * stateblock_init_default_state() to get the default scissorrect
218 * dimensions. */
219 if (!swapchain->back_buffers || back_buffer_idx >= swapchain->desc.backbuffer_count)
220 {
221 WARN("Invalid back buffer index.\n");
222 /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it
223 * here in wined3d to avoid problems in other libs. */
224 return NULL;
225 }
226
227 TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx]);
228
229 return swapchain->back_buffers[back_buffer_idx];
230 }
231
232 HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain,
233 struct wined3d_raster_status *raster_status)
234 {
235 TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status);
236
237 return wined3d_get_adapter_raster_status(swapchain->device->wined3d,
238 swapchain->device->adapter->ordinal, raster_status);
239 }
240
241 HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain,
242 struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation)
243 {
244 HRESULT hr;
245
246 TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation);
247
248 hr = wined3d_get_adapter_display_mode(swapchain->device->wined3d,
249 swapchain->device->adapter->ordinal, mode, rotation);
250
251 TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n",
252 mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id));
253
254 return hr;
255 }
256
257 struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain)
258 {
259 TRACE("swapchain %p.\n", swapchain);
260
261 return swapchain->device;
262 }
263
264 void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain,
265 struct wined3d_swapchain_desc *desc)
266 {
267 TRACE("swapchain %p, desc %p.\n", swapchain, desc);
268
269 *desc = swapchain->desc;
270 }
271
272 HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain,
273 DWORD flags, const struct wined3d_gamma_ramp *ramp)
274 {
275 HDC dc;
276
277 TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp);
278
279 if (flags)
280 FIXME("Ignoring flags %#x.\n", flags);
281
282 dc = GetDCEx(swapchain->device_window, 0, DCX_USESTYLE | DCX_CACHE);
283 SetDeviceGammaRamp(dc, (void *)ramp);
284 ReleaseDC(swapchain->device_window, dc);
285
286 return WINED3D_OK;
287 }
288
289 void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette)
290 {
291 TRACE("swapchain %p, palette %p.\n", swapchain, palette);
292 swapchain->palette = palette;
293 }
294
295 HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain,
296 struct wined3d_gamma_ramp *ramp)
297 {
298 HDC dc;
299
300 TRACE("swapchain %p, ramp %p.\n", swapchain, ramp);
301
302 dc = GetDCEx(swapchain->device_window, 0, DCX_USESTYLE | DCX_CACHE);
303 GetDeviceGammaRamp(dc, ramp);
304 ReleaseDC(swapchain->device_window, dc);
305
306 return WINED3D_OK;
307 }
308
309 /* A GL context is provided by the caller */
310 static void swapchain_blit(const struct wined3d_swapchain *swapchain,
311 struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect)
312 {
313 struct wined3d_texture *texture = swapchain->back_buffers[0];
314 struct wined3d_surface *back_buffer = texture->sub_resources[0].u.surface;
315 struct wined3d_device *device = swapchain->device;
316 enum wined3d_texture_filter_type filter;
317 DWORD location;
318
319 TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
320 swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));
321
322 if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left
323 && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top)
324 || is_complex_fixup(texture->resource.format->color_fixup))
325 filter = WINED3D_TEXF_NONE;
326 else
327 filter = WINED3D_TEXF_LINEAR;
328
329 location = WINED3D_LOCATION_TEXTURE_RGB;
330 if (texture->resource.multisample_type)
331 location = WINED3D_LOCATION_RB_RESOLVED;
332
333 wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
334 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, back_buffer,
335 location, src_rect, back_buffer, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter);
336 wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
337 }
338
339 /* Context activation is done by the caller. */
340 static void wined3d_swapchain_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context)
341 {
342 struct wined3d_texture_sub_resource *sub_resource;
343 struct wined3d_texture *texture, *texture_prev;
344 struct gl_texture tex0;
345 GLuint rb0;
346 DWORD locations0;
347 unsigned int i;
348 static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE;
349
350 if (swapchain->desc.backbuffer_count < 2 || !swapchain->render_to_fbo)
351 return;
352
353 texture_prev = swapchain->back_buffers[0];
354
355 /* Back buffer 0 is already in the draw binding. */
356 tex0 = texture_prev->texture_rgb;
357 rb0 = texture_prev->rb_multisample;
358 locations0 = texture_prev->sub_resources[0].locations;
359
360 for (i = 1; i < swapchain->desc.backbuffer_count; ++i)
361 {
362 texture = swapchain->back_buffers[i];
363 sub_resource = &texture->sub_resources[0];
364
365 if (!(sub_resource->locations & supported_locations))
366 wined3d_texture_load_location(texture, 0, context, texture->resource.draw_binding);
367
368 texture_prev->texture_rgb = texture->texture_rgb;
369 texture_prev->rb_multisample = texture->rb_multisample;
370
371 wined3d_texture_validate_location(texture_prev, 0, sub_resource->locations & supported_locations);
372 wined3d_texture_invalidate_location(texture_prev, 0, ~(sub_resource->locations & supported_locations));
373
374 texture_prev = texture;
375 }
376
377 texture_prev->texture_rgb = tex0;
378 texture_prev->rb_multisample = rb0;
379
380 wined3d_texture_validate_location(texture_prev, 0, locations0 & supported_locations);
381 wined3d_texture_invalidate_location(texture_prev, 0, ~(locations0 & supported_locations));
382
383 device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER);
384 }
385
386 static void swapchain_gl_present(struct wined3d_swapchain *swapchain,
387 const RECT *src_rect, const RECT *dst_rect, DWORD flags)
388 {
389 struct wined3d_texture *back_buffer = swapchain->back_buffers[0];
390 const struct wined3d_fb_state *fb = &swapchain->device->cs->fb;
391 const struct wined3d_gl_info *gl_info;
392 struct wined3d_texture *logo_texture;
393 struct wined3d_context *context;
394 BOOL render_to_fbo;
395
396 context = context_acquire(swapchain->device, back_buffer, 0);
397 if (!context->valid)
398 {
399 context_release(context);
400 WARN("Invalid context, skipping present.\n");
401 return;
402 }
403
404 gl_info = context->gl_info;
405
406 if ((logo_texture = swapchain->device->logo_texture))
407 {
408 RECT rect = {0, 0, logo_texture->resource.width, logo_texture->resource.height};
409
410 /* Blit the logo into the upper left corner of the drawable. */
411 wined3d_texture_blt(back_buffer, 0, &rect, logo_texture, 0, &rect,
412 WINED3D_BLT_SRC_CKEY, NULL, WINED3D_TEXF_POINT);
413 }
414
415 if (swapchain->device->bCursorVisible && swapchain->device->cursor_texture
416 && !swapchain->device->hardwareCursor)
417 {
418 RECT dst_rect =
419 {
420 swapchain->device->xScreenSpace - swapchain->device->xHotSpot,
421 swapchain->device->yScreenSpace - swapchain->device->yHotSpot,
422 swapchain->device->xScreenSpace + swapchain->device->cursorWidth - swapchain->device->xHotSpot,
423 swapchain->device->yScreenSpace + swapchain->device->cursorHeight - swapchain->device->yHotSpot,
424 };
425 RECT src_rect =
426 {
427 0, 0,
428 swapchain->device->cursor_texture->resource.width,
429 swapchain->device->cursor_texture->resource.height
430 };
431 const RECT clip_rect = {0, 0, back_buffer->resource.width, back_buffer->resource.height};
432
433 TRACE("Rendering the software cursor.\n");
434
435 if (swapchain->desc.windowed)
436 MapWindowPoints(NULL, swapchain->win_handle, (POINT *)&dst_rect, 2);
437 if (wined3d_clip_blit(&clip_rect, &dst_rect, &src_rect))
438 wined3d_texture_blt(back_buffer, 0, &dst_rect,
439 swapchain->device->cursor_texture, 0, &src_rect,
440 WINED3D_BLT_ALPHA_TEST, NULL, WINED3D_TEXF_POINT);
441 }
442
443 TRACE("Presenting HDC %p.\n", context->hdc);
444
445 if (!(render_to_fbo = swapchain->render_to_fbo)
446 && (src_rect->left || src_rect->top
447 || src_rect->right != swapchain->desc.backbuffer_width
448 || src_rect->bottom != swapchain->desc.backbuffer_height
449 || dst_rect->left || dst_rect->top
450 || dst_rect->right != swapchain->desc.backbuffer_width
451 || dst_rect->bottom != swapchain->desc.backbuffer_height))
452 render_to_fbo = TRUE;
453
454 /* Rendering to a window of different size, presenting partial rectangles,
455 * or rendering to a different window needs help from FBO_blit or a textured
456 * draw. Render the swapchain to a FBO in the future.
457 *
458 * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
459 * all these issues - this fails if the window is smaller than the backbuffer.
460 */
461 if (!swapchain->render_to_fbo && render_to_fbo && wined3d_settings.offscreen_rendering_mode == ORM_FBO)
462 {
463 wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
464 wined3d_texture_invalidate_location(back_buffer, 0, WINED3D_LOCATION_DRAWABLE);
465 swapchain->render_to_fbo = TRUE;
466 swapchain_update_draw_bindings(swapchain);
467 }
468 else
469 {
470 wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding);
471 }
472
473 if (swapchain->render_to_fbo)
474 {
475 static unsigned int once;
476
477 if (swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_FLIP && !once++)
478 FIXME("WINED3D_SWAP_EFFECT_FLIP not implemented.\n");
479
480 swapchain_blit(swapchain, context, src_rect, dst_rect);
481 }
482
483 #if !defined(STAGING_CSMT)
484 if (swapchain->num_contexts > 1)
485 #else /* STAGING_CSMT */
486 if (swapchain->num_contexts > 1 && !wined3d_settings.cs_multithreaded)
487 #endif /* STAGING_CSMT */
488 gl_info->gl_ops.gl.p_glFinish();
489
490 /* call wglSwapBuffers through the gl table to avoid confusing the Steam overlay */
491 gl_info->gl_ops.wgl.p_wglSwapBuffers(context->hdc);
492
493 wined3d_swapchain_rotate(swapchain, context);
494
495 TRACE("SwapBuffers called, Starting new frame\n");
496 /* FPS support */
497 if (TRACE_ON(fps))
498 {
499 DWORD time = GetTickCount();
500 ++swapchain->frames;
501
502 /* every 1.5 seconds */
503 if (time - swapchain->prev_time > 1500)
504 {
505 TRACE_(fps)("%p @ approx %.2ffps\n",
506 swapchain, 1000.0 * swapchain->frames / (time - swapchain->prev_time));
507 swapchain->prev_time = time;
508 swapchain->frames = 0;
509 }
510 }
511
512 wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
513 wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
514 /* If the swapeffect is DISCARD, the back buffer is undefined. That means the SYSMEM
515 * and INTEXTURE copies can keep their old content if they have any defined content.
516 * If the swapeffect is COPY, the content remains the same.
517 *
518 * The FLIP swap effect is not implemented yet. We could mark WINED3D_LOCATION_DRAWABLE
519 * up to date and hope WGL flipped front and back buffers and read this data into
520 * the FBO. Don't bother about this for now. */
521 if (swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_DISCARD)
522 wined3d_texture_validate_location(swapchain->back_buffers[swapchain->desc.backbuffer_count - 1],
523 0, WINED3D_LOCATION_DISCARDED);
524
525 if (fb->depth_stencil)
526 {
527 struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(fb->depth_stencil);
528
529 if (ds && (swapchain->desc.flags & WINED3D_SWAPCHAIN_DISCARD_DEPTHSTENCIL
530 || ds->container->flags & WINED3D_TEXTURE_DISCARD))
531 wined3d_texture_validate_location(ds->container,
532 fb->depth_stencil->sub_resource_idx, WINED3D_LOCATION_DISCARDED);
533 }
534
535 context_release(context);
536 }
537
538 static void swapchain_gl_frontbuffer_updated(struct wined3d_swapchain *swapchain)
539 {
540 struct wined3d_texture *front_buffer = swapchain->front_buffer;
541 struct wined3d_context *context;
542
543 context = context_acquire(swapchain->device, front_buffer, 0);
544 wined3d_texture_load_location(front_buffer, 0, context, front_buffer->resource.draw_binding);
545 context_release(context);
546 SetRectEmpty(&swapchain->front_buffer_update);
547 }
548
549 static const struct wined3d_swapchain_ops swapchain_gl_ops =
550 {
551 swapchain_gl_present,
552 swapchain_gl_frontbuffer_updated,
553 };
554
555 static void swapchain_gdi_frontbuffer_updated(struct wined3d_swapchain *swapchain)
556 {
557 struct wined3d_surface *front;
558 POINT offset = {0, 0};
559 HDC src_dc, dst_dc;
560 RECT draw_rect;
561 HWND window;
562
563 TRACE("swapchain %p.\n", swapchain);
564
565 front = swapchain->front_buffer->sub_resources[0].u.surface;
566 if (swapchain->palette)
567 wined3d_palette_apply_to_dc(swapchain->palette, front->dc);
568
569 if (front->container->resource.map_count)
570 ERR("Trying to blit a mapped surface.\n");
571
572 TRACE("Copying surface %p to screen.\n", front);
573
574 src_dc = front->dc;
575 window = swapchain->win_handle;
576 dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE);
577
578 /* Front buffer coordinates are screen coordinates. Map them to the
579 * destination window if not fullscreened. */
580 if (swapchain->desc.windowed)
581 ClientToScreen(window, &offset);
582
583 TRACE("offset %s.\n", wine_dbgstr_point(&offset));
584
585 SetRect(&draw_rect, 0, 0, swapchain->front_buffer->resource.width,
586 swapchain->front_buffer->resource.height);
587 IntersectRect(&draw_rect, &draw_rect, &swapchain->front_buffer_update);
588
589 BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y,
590 draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top,
591 src_dc, draw_rect.left, draw_rect.top, SRCCOPY);
592 ReleaseDC(window, dst_dc);
593
594 SetRectEmpty(&swapchain->front_buffer_update);
595 }
596
597 static void swapchain_gdi_present(struct wined3d_swapchain *swapchain,
598 const RECT *src_rect, const RECT *dst_rect, DWORD flags)
599 {
600 struct wined3d_surface *front, *back;
601 HBITMAP bitmap;
602 void *data;
603 HDC dc;
604
605 front = swapchain->front_buffer->sub_resources[0].u.surface;
606 back = swapchain->back_buffers[0]->sub_resources[0].u.surface;
607
608 /* Flip the surface data. */
609 dc = front->dc;
610 bitmap = front->bitmap;
611 data = front->container->resource.heap_memory;
612
613 front->dc = back->dc;
614 front->bitmap = back->bitmap;
615 front->container->resource.heap_memory = back->container->resource.heap_memory;
616
617 back->dc = dc;
618 back->bitmap = bitmap;
619 back->container->resource.heap_memory = data;
620
621 /* FPS support */
622 if (TRACE_ON(fps))
623 {
624 static LONG prev_time, frames;
625 DWORD time = GetTickCount();
626
627 ++frames;
628
629 /* every 1.5 seconds */
630 if (time - prev_time > 1500)
631 {
632 TRACE_(fps)("@ approx %.2ffps\n", 1000.0 * frames / (time - prev_time));
633 prev_time = time;
634 frames = 0;
635 }
636 }
637
638 SetRect(&swapchain->front_buffer_update, 0, 0,
639 swapchain->front_buffer->resource.width,
640 swapchain->front_buffer->resource.height);
641 swapchain_gdi_frontbuffer_updated(swapchain);
642 }
643
644 static const struct wined3d_swapchain_ops swapchain_gdi_ops =
645 {
646 swapchain_gdi_present,
647 swapchain_gdi_frontbuffer_updated,
648 };
649
650 static void swapchain_update_render_to_fbo(struct wined3d_swapchain *swapchain)
651 {
652 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO)
653 return;
654
655 if (!swapchain->desc.backbuffer_count)
656 {
657 TRACE("Single buffered rendering.\n");
658 swapchain->render_to_fbo = FALSE;
659 return;
660 }
661
662 TRACE("Rendering to FBO.\n");
663 swapchain->render_to_fbo = TRUE;
664 }
665
666 static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_swapchain *swapchain,
667 enum wined3d_format_id format_id, enum wined3d_multisample_type *type, DWORD *quality)
668 {
669 const struct wined3d_gl_info *gl_info;
670 const struct wined3d_format *format;
671 enum wined3d_multisample_type t;
672
673 if (wined3d_settings.sample_count == ~0u)
674 return;
675
676 gl_info = &swapchain->device->adapter->gl_info;
677 if (!(format = wined3d_get_format(gl_info, format_id, WINED3DUSAGE_RENDERTARGET)))
678 return;
679
680 if ((t = min(wined3d_settings.sample_count, gl_info->limits.samples)))
681 while (!(format->multisample_types & 1u << (t - 1)))
682 ++t;
683 TRACE("Using sample count %u.\n", t);
684 *type = t;
685 *quality = 0;
686 }
687
688 static void wined3d_swapchain_update_swap_interval_cs(void *object)
689 {
690 struct wined3d_swapchain *swapchain = object;
691 const struct wined3d_gl_info *gl_info;
692 struct wined3d_context *context;
693 int swap_interval;
694
695 context = context_acquire(swapchain->device, swapchain->front_buffer, 0);
696 gl_info = context->gl_info;
697
698 switch (swapchain->desc.swap_interval)
699 {
700 case WINED3DPRESENT_INTERVAL_IMMEDIATE:
701 swap_interval = 0;
702 break;
703 case WINED3DPRESENT_INTERVAL_DEFAULT:
704 case WINED3DPRESENT_INTERVAL_ONE:
705 swap_interval = 1;
706 break;
707 case WINED3DPRESENT_INTERVAL_TWO:
708 swap_interval = 2;
709 break;
710 case WINED3DPRESENT_INTERVAL_THREE:
711 swap_interval = 3;
712 break;
713 case WINED3DPRESENT_INTERVAL_FOUR:
714 swap_interval = 4;
715 break;
716 default:
717 FIXME("Unhandled present interval %#x.\n", swapchain->desc.swap_interval);
718 swap_interval = 1;
719 }
720
721 if (gl_info->supported[WGL_EXT_SWAP_CONTROL])
722 {
723 if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval)))
724 ERR("wglSwapIntervalEXT failed to set swap interval %d for context %p, last error %#x\n",
725 swap_interval, context, GetLastError());
726 }
727
728 context_release(context);
729 }
730
731 static void wined3d_swapchain_cs_init(void *object)
732 {
733 struct wined3d_swapchain *swapchain = object;
734 const struct wined3d_gl_info *gl_info;
735 unsigned int i;
736
737 static const enum wined3d_format_id formats[] =
738 {
739 WINED3DFMT_D24_UNORM_S8_UINT,
740 WINED3DFMT_D32_UNORM,
741 WINED3DFMT_R24_UNORM_X8_TYPELESS,
742 WINED3DFMT_D16_UNORM,
743 WINED3DFMT_S1_UINT_D15_UNORM,
744 };
745
746 gl_info = &swapchain->device->adapter->gl_info;
747
748 /* Without ORM_FBO, switching the depth/stencil format is hard. Always
749 * request a depth/stencil buffer in the likely case it's needed later. */
750 for (i = 0; i < ARRAY_SIZE(formats); ++i)
751 {
752 swapchain->ds_format = wined3d_get_format(gl_info, formats[i], WINED3DUSAGE_DEPTHSTENCIL);
753 if ((swapchain->context[0] = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
754 break;
755 TRACE("Depth stencil format %s is not supported, trying next format.\n", debug_d3dformat(formats[i]));
756 }
757
758 if (!swapchain->context[0])
759 {
760 WARN("Failed to create context.\n");
761 return;
762 }
763 swapchain->num_contexts = 1;
764
765 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
766 && (!swapchain->desc.enable_auto_depth_stencil
767 || swapchain->desc.auto_depth_stencil_format != swapchain->ds_format->id))
768 FIXME("Add OpenGL context recreation support.\n");
769
770 context_release(swapchain->context[0]);
771
772 wined3d_swapchain_update_swap_interval_cs(swapchain);
773 }
774
775 static HRESULT swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device,
776 struct wined3d_swapchain_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops)
777 {
778 const struct wined3d_adapter *adapter = device->adapter;
779 struct wined3d_resource_desc texture_desc;
780 BOOL displaymode_set = FALSE;
781 DWORD texture_flags = 0;
782 RECT client_rect;
783 HWND window;
784 HRESULT hr;
785 UINT i;
786
787 if (desc->backbuffer_count > 1)
788 {
789 FIXME("The application requested more than one back buffer, this is not properly supported.\n"
790 "Please configure the application to use double buffering (1 back buffer) if possible.\n");
791 }
792
793 if (device->wined3d->flags & WINED3D_NO3D)
794 swapchain->swapchain_ops = &swapchain_gdi_ops;
795 else
796 swapchain->swapchain_ops = &swapchain_gl_ops;
797
798 window = desc->device_window ? desc->device_window : device->create_parms.focus_window;
799
800 swapchain->device = device;
801 swapchain->parent = parent;
802 swapchain->parent_ops = parent_ops;
803 swapchain->ref = 1;
804 swapchain->win_handle = window;
805 swapchain->device_window = window;
806
807 if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d,
808 adapter->ordinal, &swapchain->original_mode, NULL)))
809 {
810 ERR("Failed to get current display mode, hr %#x.\n", hr);
811 goto err;
812 }
813 GetWindowRect(window, &swapchain->original_window_rect);
814
815 GetClientRect(window, &client_rect);
816 if (desc->windowed)
817 {
818 if (!desc->backbuffer_width)
819 {
820 desc->backbuffer_width = client_rect.right;
821 TRACE("Updating width to %u.\n", desc->backbuffer_width);
822 }
823
824 if (!desc->backbuffer_height)
825 {
826 desc->backbuffer_height = client_rect.bottom;
827 TRACE("Updating height to %u.\n", desc->backbuffer_height);
828 }
829
830 if (desc->backbuffer_format == WINED3DFMT_UNKNOWN)
831 {
832 desc->backbuffer_format = swapchain->original_mode.format_id;
833 TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->original_mode.format_id));
834 }
835 }
836 swapchain->desc = *desc;
837 wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->desc.backbuffer_format,
838 &swapchain->desc.multisample_type, &swapchain->desc.multisample_quality);
839 swapchain_update_render_to_fbo(swapchain);
840
841 TRACE("Creating front buffer.\n");
842
843 texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
844 texture_desc.format = swapchain->desc.backbuffer_format;
845 texture_desc.multisample_type = swapchain->desc.multisample_type;
846 texture_desc.multisample_quality = swapchain->desc.multisample_quality;
847 texture_desc.usage = 0;
848 texture_desc.pool = WINED3D_POOL_DEFAULT;
849 texture_desc.width = swapchain->desc.backbuffer_width;
850 texture_desc.height = swapchain->desc.backbuffer_height;
851 texture_desc.depth = 1;
852 texture_desc.size = 0;
853
854 if (swapchain->desc.flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE)
855 texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC;
856
857 if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
858 parent, &texture_desc, texture_flags, &swapchain->front_buffer)))
859 {
860 WARN("Failed to create front buffer, hr %#x.\n", hr);
861 goto err;
862 }
863
864 wined3d_texture_set_swapchain(swapchain->front_buffer, swapchain);
865 if (!(device->wined3d->flags & WINED3D_NO3D))
866 {
867 wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
868 wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
869 }
870
871 /* MSDN says we're only allowed a single fullscreen swapchain per device,
872 * so we should really check to see if there is a fullscreen swapchain
873 * already. Does a single head count as full screen? */
874 if (!desc->windowed)
875 {
876 if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
877 {
878 /* Change the display settings */
879 swapchain->d3d_mode.width = desc->backbuffer_width;
880 swapchain->d3d_mode.height = desc->backbuffer_height;
881 swapchain->d3d_mode.format_id = desc->backbuffer_format;
882 swapchain->d3d_mode.refresh_rate = desc->refresh_rate;
883 swapchain->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
884
885 if (FAILED(hr = wined3d_set_adapter_display_mode(device->wined3d,
886 adapter->ordinal, &swapchain->d3d_mode)))
887 {
888 WARN("Failed to set display mode, hr %#x.\n", hr);
889 goto err;
890 }
891 displaymode_set = TRUE;
892 }
893 else
894 {
895 swapchain->d3d_mode = swapchain->original_mode;
896 }
897 }
898
899 if (!(device->wined3d->flags & WINED3D_NO3D))
900 {
901 swapchain->context = HeapAlloc(GetProcessHeap(), 0, sizeof(*swapchain->context));
902 if (!swapchain->context)
903 {
904 ERR("Failed to create the context array.\n");
905 hr = E_OUTOFMEMORY;
906 goto err;
907 }
908
909 wined3d_cs_init_object(device->cs, wined3d_swapchain_cs_init, swapchain);
910 device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
911
912 if (!swapchain->context[0])
913 {
914 hr = WINED3DERR_NOTAVAILABLE;
915 goto err;
916 }
917 }
918
919 if (swapchain->desc.backbuffer_count > 0)
920 {
921 if (!(swapchain->back_buffers = wined3d_calloc(swapchain->desc.backbuffer_count,
922 sizeof(*swapchain->back_buffers))))
923 {
924 ERR("Failed to allocate backbuffer array memory.\n");
925 hr = E_OUTOFMEMORY;
926 goto err;
927 }
928
929 texture_desc.usage |= WINED3DUSAGE_RENDERTARGET;
930 for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
931 {
932 TRACE("Creating back buffer %u.\n", i);
933 if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
934 parent, &texture_desc, texture_flags, &swapchain->back_buffers[i])))
935 {
936 WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
937 swapchain->desc.backbuffer_count = i;
938 goto err;
939 }
940 wined3d_texture_set_swapchain(swapchain->back_buffers[i], swapchain);
941 }
942 }
943
944 /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
945 if (desc->enable_auto_depth_stencil && !(device->wined3d->flags & WINED3D_NO3D))
946 {
947 TRACE("Creating depth/stencil buffer.\n");
948 if (!device->auto_depth_stencil_view)
949 {
950 struct wined3d_view_desc desc;
951 struct wined3d_texture *ds;
952
953 texture_desc.format = swapchain->desc.auto_depth_stencil_format;
954 texture_desc.usage = WINED3DUSAGE_DEPTHSTENCIL;
955
956 if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
957 device->device_parent, &texture_desc, texture_flags, &ds)))
958 {
959 WARN("Failed to create the auto depth/stencil surface, hr %#x.\n", hr);
960 goto err;
961 }
962
963 desc.format_id = ds->resource.format->id;
964 desc.flags = 0;
965 desc.u.texture.level_idx = 0;
966 desc.u.texture.level_count = 1;
967 desc.u.texture.layer_idx = 0;
968 desc.u.texture.layer_count = 1;
969 hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops,
970 &device->auto_depth_stencil_view);
971 wined3d_texture_decref(ds);
972 if (FAILED(hr))
973 {
974 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
975 goto err;
976 }
977 }
978 }
979
980 wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma);
981
982 return WINED3D_OK;
983
984 err:
985 if (displaymode_set)
986 {
987 if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
988 adapter->ordinal, &swapchain->original_mode)))
989 ERR("Failed to restore display mode.\n");
990 ClipCursor(NULL);
991 }
992
993 if (swapchain->back_buffers)
994 {
995 for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
996 {
997 if (swapchain->back_buffers[i])
998 {
999 wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
1000 wined3d_texture_decref(swapchain->back_buffers[i]);
1001 }
1002 }
1003 HeapFree(GetProcessHeap(), 0, swapchain->back_buffers);
1004 }
1005
1006 wined3d_cs_destroy_object(swapchain->device->cs, wined3d_swapchain_destroy_object, swapchain);
1007 swapchain->device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
1008
1009 if (swapchain->front_buffer)
1010 {
1011 wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
1012 wined3d_texture_decref(swapchain->front_buffer);
1013 }
1014
1015 return hr;
1016 }
1017
1018 HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, struct wined3d_swapchain_desc *desc,
1019 void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain)
1020 {
1021 struct wined3d_swapchain *object;
1022 HRESULT hr;
1023
1024 TRACE("device %p, desc %p, parent %p, parent_ops %p, swapchain %p.\n",
1025 device, desc, parent, parent_ops, swapchain);
1026
1027 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1028 if (!object)
1029 return E_OUTOFMEMORY;
1030
1031 hr = swapchain_init(object, device, desc, parent, parent_ops);
1032 if (FAILED(hr))
1033 {
1034 WARN("Failed to initialize swapchain, hr %#x.\n", hr);
1035 HeapFree(GetProcessHeap(), 0, object);
1036 return hr;
1037 }
1038
1039 TRACE("Created swapchain %p.\n", object);
1040 *swapchain = object;
1041
1042 return WINED3D_OK;
1043 }
1044
1045 static struct wined3d_context *swapchain_create_context(struct wined3d_swapchain *swapchain)
1046 {
1047 struct wined3d_context **ctx_array;
1048 struct wined3d_context *ctx;
1049
1050 TRACE("Creating a new context for swapchain %p, thread %u.\n", swapchain, GetCurrentThreadId());
1051
1052 if (!(ctx = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
1053 {
1054 ERR("Failed to create a new context for the swapchain\n");
1055 return NULL;
1056 }
1057 context_release(ctx);
1058
1059 if (!(ctx_array = wined3d_calloc(swapchain->num_contexts + 1, sizeof(*ctx_array))))
1060 {
1061 ERR("Out of memory when trying to allocate a new context array\n");
1062 context_destroy(swapchain->device, ctx);
1063 return NULL;
1064 }
1065 memcpy(ctx_array, swapchain->context, sizeof(*ctx_array) * swapchain->num_contexts);
1066 HeapFree(GetProcessHeap(), 0, swapchain->context);
1067 ctx_array[swapchain->num_contexts] = ctx;
1068 swapchain->context = ctx_array;
1069 swapchain->num_contexts++;
1070
1071 TRACE("Returning context %p\n", ctx);
1072 return ctx;
1073 }
1074
1075 void swapchain_destroy_contexts(struct wined3d_swapchain *swapchain)
1076 {
1077 unsigned int i;
1078
1079 for (i = 0; i < swapchain->num_contexts; ++i)
1080 {
1081 context_destroy(swapchain->device, swapchain->context[i]);
1082 }
1083 HeapFree(GetProcessHeap(), 0, swapchain->context);
1084 swapchain->num_contexts = 0;
1085 swapchain->context = NULL;
1086 }
1087
1088 struct wined3d_context *swapchain_get_context(struct wined3d_swapchain *swapchain)
1089 {
1090 DWORD tid = GetCurrentThreadId();
1091 unsigned int i;
1092
1093 for (i = 0; i < swapchain->num_contexts; ++i)
1094 {
1095 if (swapchain->context[i]->tid == tid)
1096 return swapchain->context[i];
1097 }
1098
1099 /* Create a new context for the thread */
1100 return swapchain_create_context(swapchain);
1101 }
1102
1103 HDC swapchain_get_backup_dc(struct wined3d_swapchain *swapchain)
1104 {
1105 if (!swapchain->backup_dc)
1106 {
1107 TRACE("Creating the backup window for swapchain %p.\n", swapchain);
1108
1109 if (!(swapchain->backup_wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window",
1110 WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL)))
1111 {
1112 ERR("Failed to create a window.\n");
1113 return NULL;
1114 }
1115
1116 if (!(swapchain->backup_dc = GetDC(swapchain->backup_wnd)))
1117 {
1118 ERR("Failed to get a DC.\n");
1119 DestroyWindow(swapchain->backup_wnd);
1120 swapchain->backup_wnd = NULL;
1121 return NULL;
1122 }
1123 }
1124
1125 return swapchain->backup_dc;
1126 }
1127
1128 void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain)
1129 {
1130 UINT i;
1131
1132 wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource);
1133
1134 for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1135 {
1136 wined3d_resource_update_draw_binding(&swapchain->back_buffers[i]->resource);
1137 }
1138 }
1139
1140 void swapchain_update_swap_interval(struct wined3d_swapchain *swapchain)
1141 {
1142 wined3d_cs_init_object(swapchain->device->cs, wined3d_swapchain_update_swap_interval_cs, swapchain);
1143 }
1144
1145 void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate)
1146 {
1147 struct wined3d_device *device = swapchain->device;
1148 BOOL filter_messages = device->filter_messages;
1149
1150 /* This code is not protected by the wined3d mutex, so it may run while
1151 * wined3d_device_reset is active. Testing on Windows shows that changing
1152 * focus during resets and resetting during focus change events causes
1153 * the application to crash with an invalid memory access. */
1154
1155 device->filter_messages = !(device->wined3d->flags & WINED3D_FOCUS_MESSAGES);
1156
1157 if (activate)
1158 {
1159 if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES))
1160 {
1161 /* The d3d versions do not agree on the exact messages here. D3d8 restores
1162 * the window but leaves the size untouched, d3d9 sets the size on an
1163 * invisible window, generates messages but doesn't change the window
1164 * properties. The implementation follows d3d9.
1165 *
1166 * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to
1167 * resume drawing after a focus loss. */
1168 SetWindowPos(swapchain->device_window, NULL, 0, 0,
1169 swapchain->desc.backbuffer_width, swapchain->desc.backbuffer_height,
1170 SWP_NOACTIVATE | SWP_NOZORDER);
1171 }
1172
1173 if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE)
1174 {
1175 if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
1176 device->adapter->ordinal, &swapchain->d3d_mode)))
1177 ERR("Failed to set display mode.\n");
1178 }
1179 }
1180 else
1181 {
1182 if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
1183 device->adapter->ordinal, NULL)))
1184 ERR("Failed to set display mode.\n");
1185
1186 swapchain->reapply_mode = TRUE;
1187
1188 if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES)
1189 && IsWindowVisible(swapchain->device_window))
1190 ShowWindow(swapchain->device_window, SW_MINIMIZE);
1191 }
1192
1193 device->filter_messages = filter_messages;
1194 }
1195
1196 HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count,
1197 unsigned int width, unsigned int height, enum wined3d_format_id format_id,
1198 enum wined3d_multisample_type multisample_type, unsigned int multisample_quality)
1199 {
1200 struct wined3d_device *device = swapchain->device;
1201 BOOL update_desc = FALSE;
1202
1203 TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, "
1204 "multisample_type %#x, multisample_quality %#x.\n",
1205 swapchain, buffer_count, width, height, debug_d3dformat(format_id),
1206 multisample_type, multisample_quality);
1207
1208 wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality);
1209
1210 if (buffer_count && buffer_count != swapchain->desc.backbuffer_count)
1211 FIXME("Cannot change the back buffer count yet.\n");
1212
1213 device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
1214
1215 if (!width || !height)
1216 {
1217 /* The application is requesting that either the swapchain width or
1218 * height be set to the corresponding dimension in the window's
1219 * client rect. */
1220
1221 RECT client_rect;
1222
1223 if (!swapchain->desc.windowed)
1224 return WINED3DERR_INVALIDCALL;
1225
1226 if (!GetClientRect(swapchain->device_window, &client_rect))
1227 {
1228 ERR("Failed to get client rect, last error %#x.\n", GetLastError());
1229 return WINED3DERR_INVALIDCALL;
1230 }
1231
1232 if (!width)
1233 width = client_rect.right;
1234
1235 if (!height)
1236 height = client_rect.bottom;
1237 }
1238
1239 if (width != swapchain->desc.backbuffer_width
1240 || height != swapchain->desc.backbuffer_height)
1241 {
1242 swapchain->desc.backbuffer_width = width;
1243 swapchain->desc.backbuffer_height = height;
1244 update_desc = TRUE;
1245 }
1246
1247 if (format_id == WINED3DFMT_UNKNOWN)
1248 {
1249 if (!swapchain->desc.windowed)
1250 return WINED3DERR_INVALIDCALL;
1251 format_id = swapchain->original_mode.format_id;
1252 }
1253
1254 if (format_id != swapchain->desc.backbuffer_format)
1255 {
1256 swapchain->desc.backbuffer_format = format_id;
1257 update_desc = TRUE;
1258 }
1259
1260 if (multisample_type != swapchain->desc.multisample_type
1261 || multisample_quality != swapchain->desc.multisample_quality)
1262 {
1263 swapchain->desc.multisample_type = multisample_type;
1264 swapchain->desc.multisample_quality = multisample_quality;
1265 update_desc = TRUE;
1266 }
1267
1268 if (update_desc)
1269 {
1270 HRESULT hr;
1271 UINT i;
1272
1273 if (FAILED(hr = wined3d_texture_update_desc(swapchain->front_buffer, swapchain->desc.backbuffer_width,
1274 swapchain->desc.backbuffer_height, swapchain->desc.backbuffer_format,
1275 swapchain->desc.multisample_type, swapchain->desc.multisample_quality, NULL, 0)))
1276 return hr;
1277
1278 for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1279 {
1280 if (FAILED(hr = wined3d_texture_update_desc(swapchain->back_buffers[i], swapchain->desc.backbuffer_width,
1281 swapchain->desc.backbuffer_height, swapchain->desc.backbuffer_format,
1282 swapchain->desc.multisample_type, swapchain->desc.multisample_quality, NULL, 0)))
1283 return hr;
1284 }
1285 }
1286
1287 swapchain_update_render_to_fbo(swapchain);
1288 swapchain_update_draw_bindings(swapchain);
1289
1290 return WINED3D_OK;
1291 }
1292
1293 static HRESULT wined3d_swapchain_set_display_mode(struct wined3d_swapchain *swapchain,
1294 struct wined3d_display_mode *mode)
1295 {
1296 struct wined3d_device *device = swapchain->device;
1297 HRESULT hr;
1298
1299 if (swapchain->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE)
1300 {
1301 if (FAILED(hr = wined3d_find_closest_matching_adapter_mode(device->wined3d,
1302 device->adapter->ordinal, mode)))
1303 {
1304 WARN("Failed to find closest matching mode, hr %#x.\n", hr);
1305 }
1306 }
1307
1308 if (FAILED(hr = wined3d_set_adapter_display_mode(device->wined3d,
1309 device->adapter->ordinal, mode)))
1310 {
1311 WARN("Failed to set display mode, hr %#x.\n", hr);
1312 return WINED3DERR_INVALIDCALL;
1313 }
1314
1315 return WINED3D_OK;
1316 }
1317
1318 HRESULT CDECL wined3d_swapchain_resize_target(struct wined3d_swapchain *swapchain,
1319 const struct wined3d_display_mode *mode)
1320 {
1321 struct wined3d_device *device = swapchain->device;
1322 struct wined3d_display_mode actual_mode;
1323 RECT original_window_rect, window_rect;
1324 HRESULT hr;
1325
1326 TRACE("swapchain %p, mode %p.\n", swapchain, mode);
1327
1328 if (swapchain->desc.windowed)
1329 {
1330 SetRect(&window_rect, 0, 0, mode->width, mode->height);
1331 AdjustWindowRectEx(&window_rect,
1332 GetWindowLongW(swapchain->device_window, GWL_STYLE), FALSE,
1333 GetWindowLongW(swapchain->device_window, GWL_EXSTYLE));
1334 SetRect(&window_rect, 0, 0,
1335 window_rect.right - window_rect.left, window_rect.bottom - window_rect.top);
1336 GetWindowRect(swapchain->device_window, &original_window_rect);
1337 OffsetRect(&window_rect, original_window_rect.left, original_window_rect.top);
1338 }
1339 else if (swapchain->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1340 {
1341 actual_mode = *mode;
1342 if (FAILED(hr = wined3d_swapchain_set_display_mode(swapchain, &actual_mode)))
1343 return hr;
1344 SetRect(&window_rect, 0, 0, actual_mode.width, actual_mode.height);
1345 }
1346 else
1347 {
1348 if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d, device->adapter->ordinal,
1349 &actual_mode, NULL)))
1350 {
1351 ERR("Failed to get display mode, hr %#x.\n", hr);
1352 return WINED3DERR_INVALIDCALL;
1353 }
1354
1355 SetRect(&window_rect, 0, 0, actual_mode.width, actual_mode.height);
1356 }
1357
1358 MoveWindow(swapchain->device_window, window_rect.left, window_rect.top,
1359 window_rect.right - window_rect.left,
1360 window_rect.bottom - window_rect.top, TRUE);
1361
1362 return WINED3D_OK;
1363 }
1364
1365 HRESULT CDECL wined3d_swapchain_set_fullscreen(struct wined3d_swapchain *swapchain,
1366 const struct wined3d_swapchain_desc *swapchain_desc, const struct wined3d_display_mode *mode)
1367 {
1368 struct wined3d_device *device = swapchain->device;
1369 struct wined3d_display_mode actual_mode;
1370 HRESULT hr;
1371
1372 TRACE("swapchain %p, desc %p, mode %p.\n", swapchain, swapchain_desc, mode);
1373
1374 if (swapchain->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1375 {
1376 if (mode)
1377 {
1378 actual_mode = *mode;
1379 }
1380 else
1381 {
1382 if (!swapchain_desc->windowed)
1383 {
1384 actual_mode.width = swapchain_desc->backbuffer_width;
1385 actual_mode.height = swapchain_desc->backbuffer_height;
1386 actual_mode.refresh_rate = swapchain_desc->refresh_rate;
1387 actual_mode.format_id = swapchain_desc->backbuffer_format;
1388 actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
1389 }
1390 else
1391 {
1392 actual_mode = swapchain->original_mode;
1393 }
1394 }
1395
1396 if (FAILED(hr = wined3d_swapchain_set_display_mode(swapchain, &actual_mode)))
1397 return hr;
1398 }
1399 else
1400 {
1401 if (mode)
1402 WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n");
1403
1404 if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d, device->adapter->ordinal,
1405 &actual_mode, NULL)))
1406 {
1407 ERR("Failed to get display mode, hr %#x.\n", hr);
1408 return WINED3DERR_INVALIDCALL;
1409 }
1410 }
1411
1412 if (!swapchain_desc->windowed)
1413 {
1414 unsigned int width = actual_mode.width;
1415 unsigned int height = actual_mode.height;
1416
1417 if (swapchain->desc.windowed)
1418 {
1419 /* Switch from windowed to fullscreen */
1420 HWND focus_window = device->create_parms.focus_window;
1421 if (!focus_window)
1422 focus_window = swapchain->device_window;
1423 if (FAILED(hr = wined3d_device_acquire_focus_window(device, focus_window)))
1424 {
1425 ERR("Failed to acquire focus window, hr %#x.\n", hr);
1426 return hr;
1427 }
1428
1429 wined3d_device_setup_fullscreen_window(device, swapchain->device_window, width, height);
1430 }
1431 else
1432 {
1433 /* Fullscreen -> fullscreen mode change */
1434 BOOL filter_messages = device->filter_messages;
1435 device->filter_messages = TRUE;
1436
1437 MoveWindow(swapchain->device_window, 0, 0, width, height, TRUE);
1438
1439 device->filter_messages = filter_messages;
1440 }
1441 swapchain->d3d_mode = actual_mode;
1442 }
1443 else if (!swapchain->desc.windowed)
1444 {
1445 /* Fullscreen -> windowed switch */
1446 RECT *window_rect = NULL;
1447 if (swapchain->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
1448 window_rect = &swapchain->original_window_rect;
1449 wined3d_device_restore_fullscreen_window(device, swapchain->device_window, window_rect);
1450 wined3d_device_release_focus_window(device);
1451 }
1452
1453 swapchain->desc.windowed = swapchain_desc->windowed;
1454
1455 return WINED3D_OK;
1456 }