2 * Context and render target management in wined3d
4 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wined3d_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
30 #define GLINFO_LOCATION (*gl_info)
32 /* The last used device.
34 * If the application creates multiple devices and switches between them, ActivateContext has to
35 * change the opengl context. This flag allows to keep track which device is active
37 static IWineD3DDeviceImpl
*last_device
;
38 static DWORD wined3d_context_tls_idx
;
40 void context_set_last_device(IWineD3DDeviceImpl
*device
)
45 /* FBO helper functions */
47 /* GL locking is done by the caller */
48 void context_bind_fbo(struct WineD3DContext
*context
, GLenum target
, GLuint
*fbo
)
50 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
61 GL_EXTCALL(glGenFramebuffersEXT(1, fbo
));
62 checkGLcall("glGenFramebuffersEXT()");
63 TRACE("Created FBO %u.\n", *fbo
);
70 case GL_READ_FRAMEBUFFER_EXT
:
71 if (context
->fbo_read_binding
== f
) return;
72 context
->fbo_read_binding
= f
;
75 case GL_DRAW_FRAMEBUFFER_EXT
:
76 if (context
->fbo_draw_binding
== f
) return;
77 context
->fbo_draw_binding
= f
;
80 case GL_FRAMEBUFFER_EXT
:
81 if (context
->fbo_read_binding
== f
82 && context
->fbo_draw_binding
== f
) return;
83 context
->fbo_read_binding
= f
;
84 context
->fbo_draw_binding
= f
;
88 FIXME("Unhandled target %#x.\n", target
);
92 GL_EXTCALL(glBindFramebufferEXT(target
, f
));
93 checkGLcall("glBindFramebuffer()");
96 /* GL locking is done by the caller */
97 static void context_clean_fbo_attachments(const struct wined3d_gl_info
*gl_info
)
101 for (i
= 0; i
< GL_LIMITS(buffers
); ++i
)
103 GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
+ i
, GL_TEXTURE_2D
, 0, 0));
104 checkGLcall("glFramebufferTexture2D()");
106 GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
, GL_DEPTH_ATTACHMENT_EXT
, GL_TEXTURE_2D
, 0, 0));
107 checkGLcall("glFramebufferTexture2D()");
109 GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
, GL_STENCIL_ATTACHMENT_EXT
, GL_TEXTURE_2D
, 0, 0));
110 checkGLcall("glFramebufferTexture2D()");
113 /* GL locking is done by the caller */
114 static void context_destroy_fbo(struct WineD3DContext
*context
, GLuint
*fbo
)
116 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
118 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, fbo
);
119 context_clean_fbo_attachments(gl_info
);
120 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, NULL
);
122 GL_EXTCALL(glDeleteFramebuffersEXT(1, fbo
));
123 checkGLcall("glDeleteFramebuffers()");
126 /* GL locking is done by the caller */
127 static void context_apply_attachment_filter_states(IWineD3DSurface
*surface
, BOOL force_preload
)
129 const IWineD3DSurfaceImpl
*surface_impl
= (IWineD3DSurfaceImpl
*)surface
;
130 IWineD3DDeviceImpl
*device
= surface_impl
->resource
.wineD3DDevice
;
131 IWineD3DBaseTextureImpl
*texture_impl
;
132 BOOL update_minfilter
= FALSE
;
133 BOOL update_magfilter
= FALSE
;
135 /* Update base texture states array */
136 if (SUCCEEDED(IWineD3DSurface_GetContainer(surface
, &IID_IWineD3DBaseTexture
, (void **)&texture_impl
)))
138 if (texture_impl
->baseTexture
.states
[WINED3DTEXSTA_MINFILTER
] != WINED3DTEXF_POINT
139 || texture_impl
->baseTexture
.states
[WINED3DTEXSTA_MIPFILTER
] != WINED3DTEXF_NONE
)
141 texture_impl
->baseTexture
.states
[WINED3DTEXSTA_MINFILTER
] = WINED3DTEXF_POINT
;
142 texture_impl
->baseTexture
.states
[WINED3DTEXSTA_MIPFILTER
] = WINED3DTEXF_NONE
;
143 update_minfilter
= TRUE
;
146 if (texture_impl
->baseTexture
.states
[WINED3DTEXSTA_MAGFILTER
] != WINED3DTEXF_POINT
)
148 texture_impl
->baseTexture
.states
[WINED3DTEXSTA_MAGFILTER
] = WINED3DTEXF_POINT
;
149 update_magfilter
= TRUE
;
152 if (texture_impl
->baseTexture
.bindCount
)
154 WARN("Render targets should not be bound to a sampler\n");
155 IWineD3DDeviceImpl_MarkStateDirty(device
, STATE_SAMPLER(texture_impl
->baseTexture
.sampler
));
158 IWineD3DBaseTexture_Release((IWineD3DBaseTexture
*)texture_impl
);
161 if (update_minfilter
|| update_magfilter
|| force_preload
)
163 GLenum target
, bind_target
;
166 target
= surface_impl
->texture_target
;
167 if (target
== GL_TEXTURE_2D
)
169 bind_target
= GL_TEXTURE_2D
;
170 glGetIntegerv(GL_TEXTURE_BINDING_2D
, &old_binding
);
171 } else if (target
== GL_TEXTURE_RECTANGLE_ARB
) {
172 bind_target
= GL_TEXTURE_RECTANGLE_ARB
;
173 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB
, &old_binding
);
175 bind_target
= GL_TEXTURE_CUBE_MAP_ARB
;
176 glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP_ARB
, &old_binding
);
179 surface_internal_preload(surface
, SRGB_RGB
);
181 glBindTexture(bind_target
, surface_impl
->texture_name
);
182 if (update_minfilter
) glTexParameteri(bind_target
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
183 if (update_magfilter
) glTexParameteri(bind_target
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
184 glBindTexture(bind_target
, old_binding
);
187 checkGLcall("apply_attachment_filter_states()");
190 /* GL locking is done by the caller */
191 void context_attach_depth_stencil_fbo(struct WineD3DContext
*context
,
192 GLenum fbo_target
, IWineD3DSurface
*depth_stencil
, BOOL use_render_buffer
)
194 IWineD3DSurfaceImpl
*depth_stencil_impl
= (IWineD3DSurfaceImpl
*)depth_stencil
;
195 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
197 TRACE("Attach depth stencil %p\n", depth_stencil
);
201 DWORD format_flags
= depth_stencil_impl
->resource
.format_desc
->Flags
;
203 if (use_render_buffer
&& depth_stencil_impl
->current_renderbuffer
)
205 if (format_flags
& WINED3DFMT_FLAG_DEPTH
)
207 GL_EXTCALL(glFramebufferRenderbufferEXT(fbo_target
, GL_DEPTH_ATTACHMENT_EXT
,
208 GL_RENDERBUFFER_EXT
, depth_stencil_impl
->current_renderbuffer
->id
));
209 checkGLcall("glFramebufferRenderbufferEXT()");
212 if (format_flags
& WINED3DFMT_FLAG_STENCIL
)
214 GL_EXTCALL(glFramebufferRenderbufferEXT(fbo_target
, GL_STENCIL_ATTACHMENT_EXT
,
215 GL_RENDERBUFFER_EXT
, depth_stencil_impl
->current_renderbuffer
->id
));
216 checkGLcall("glFramebufferRenderbufferEXT()");
221 context_apply_attachment_filter_states(depth_stencil
, TRUE
);
223 if (format_flags
& WINED3DFMT_FLAG_DEPTH
)
225 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_DEPTH_ATTACHMENT_EXT
,
226 depth_stencil_impl
->texture_target
, depth_stencil_impl
->texture_name
,
227 depth_stencil_impl
->texture_level
));
228 checkGLcall("glFramebufferTexture2DEXT()");
231 if (format_flags
& WINED3DFMT_FLAG_STENCIL
)
233 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_STENCIL_ATTACHMENT_EXT
,
234 depth_stencil_impl
->texture_target
, depth_stencil_impl
->texture_name
,
235 depth_stencil_impl
->texture_level
));
236 checkGLcall("glFramebufferTexture2DEXT()");
240 if (!(format_flags
& WINED3DFMT_FLAG_DEPTH
))
242 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_DEPTH_ATTACHMENT_EXT
, GL_TEXTURE_2D
, 0, 0));
243 checkGLcall("glFramebufferTexture2DEXT()");
246 if (!(format_flags
& WINED3DFMT_FLAG_STENCIL
))
248 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_STENCIL_ATTACHMENT_EXT
, GL_TEXTURE_2D
, 0, 0));
249 checkGLcall("glFramebufferTexture2DEXT()");
254 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_DEPTH_ATTACHMENT_EXT
, GL_TEXTURE_2D
, 0, 0));
255 checkGLcall("glFramebufferTexture2DEXT()");
257 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_STENCIL_ATTACHMENT_EXT
, GL_TEXTURE_2D
, 0, 0));
258 checkGLcall("glFramebufferTexture2DEXT()");
262 /* GL locking is done by the caller */
263 void context_attach_surface_fbo(const struct WineD3DContext
*context
,
264 GLenum fbo_target
, DWORD idx
, IWineD3DSurface
*surface
)
266 const IWineD3DSurfaceImpl
*surface_impl
= (IWineD3DSurfaceImpl
*)surface
;
267 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
269 TRACE("Attach surface %p to %u\n", surface
, idx
);
273 context_apply_attachment_filter_states(surface
, TRUE
);
275 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_COLOR_ATTACHMENT0_EXT
+ idx
, surface_impl
->texture_target
,
276 surface_impl
->texture_name
, surface_impl
->texture_level
));
277 checkGLcall("glFramebufferTexture2DEXT()");
279 GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target
, GL_COLOR_ATTACHMENT0_EXT
+ idx
, GL_TEXTURE_2D
, 0, 0));
280 checkGLcall("glFramebufferTexture2DEXT()");
284 /* GL locking is done by the caller */
285 static void context_check_fbo_status(struct WineD3DContext
*context
)
287 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
290 status
= GL_EXTCALL(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT
));
291 if (status
== GL_FRAMEBUFFER_COMPLETE_EXT
)
293 TRACE("FBO complete\n");
295 IWineD3DSurfaceImpl
*attachment
;
297 FIXME("FBO status %s (%#x)\n", debug_fbostatus(status
), status
);
299 /* Dump the FBO attachments */
300 for (i
= 0; i
< GL_LIMITS(buffers
); ++i
)
302 attachment
= (IWineD3DSurfaceImpl
*)context
->current_fbo
->render_targets
[i
];
305 FIXME("\tColor attachment %d: (%p) %s %ux%u\n",
306 i
, attachment
, debug_d3dformat(attachment
->resource
.format_desc
->format
),
307 attachment
->pow2Width
, attachment
->pow2Height
);
310 attachment
= (IWineD3DSurfaceImpl
*)context
->current_fbo
->depth_stencil
;
313 FIXME("\tDepth attachment: (%p) %s %ux%u\n",
314 attachment
, debug_d3dformat(attachment
->resource
.format_desc
->format
),
315 attachment
->pow2Width
, attachment
->pow2Height
);
320 static struct fbo_entry
*context_create_fbo_entry(struct WineD3DContext
*context
)
322 IWineD3DDeviceImpl
*device
= ((IWineD3DSurfaceImpl
*)context
->surface
)->resource
.wineD3DDevice
;
323 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
324 struct fbo_entry
*entry
;
326 entry
= HeapAlloc(GetProcessHeap(), 0, sizeof(*entry
));
327 entry
->render_targets
= HeapAlloc(GetProcessHeap(), 0, GL_LIMITS(buffers
) * sizeof(*entry
->render_targets
));
328 memcpy(entry
->render_targets
, device
->render_targets
, GL_LIMITS(buffers
) * sizeof(*entry
->render_targets
));
329 entry
->depth_stencil
= device
->stencilBufferTarget
;
330 entry
->attached
= FALSE
;
336 /* GL locking is done by the caller */
337 static void context_reuse_fbo_entry(struct WineD3DContext
*context
, struct fbo_entry
*entry
)
339 IWineD3DDeviceImpl
*device
= ((IWineD3DSurfaceImpl
*)context
->surface
)->resource
.wineD3DDevice
;
340 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
342 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, &entry
->id
);
343 context_clean_fbo_attachments(gl_info
);
345 memcpy(entry
->render_targets
, device
->render_targets
, GL_LIMITS(buffers
) * sizeof(*entry
->render_targets
));
346 entry
->depth_stencil
= device
->stencilBufferTarget
;
347 entry
->attached
= FALSE
;
350 /* GL locking is done by the caller */
351 static void context_destroy_fbo_entry(struct WineD3DContext
*context
, struct fbo_entry
*entry
)
355 TRACE("Destroy FBO %d\n", entry
->id
);
356 context_destroy_fbo(context
, &entry
->id
);
358 --context
->fbo_entry_count
;
359 list_remove(&entry
->entry
);
360 HeapFree(GetProcessHeap(), 0, entry
->render_targets
);
361 HeapFree(GetProcessHeap(), 0, entry
);
365 /* GL locking is done by the caller */
366 static struct fbo_entry
*context_find_fbo_entry(struct WineD3DContext
*context
)
368 IWineD3DDeviceImpl
*device
= ((IWineD3DSurfaceImpl
*)context
->surface
)->resource
.wineD3DDevice
;
369 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
370 struct fbo_entry
*entry
;
372 LIST_FOR_EACH_ENTRY(entry
, &context
->fbo_list
, struct fbo_entry
, entry
)
374 if (!memcmp(entry
->render_targets
, device
->render_targets
, GL_LIMITS(buffers
) * sizeof(*entry
->render_targets
))
375 && entry
->depth_stencil
== device
->stencilBufferTarget
)
377 list_remove(&entry
->entry
);
378 list_add_head(&context
->fbo_list
, &entry
->entry
);
383 if (context
->fbo_entry_count
< WINED3D_MAX_FBO_ENTRIES
)
385 entry
= context_create_fbo_entry(context
);
386 list_add_head(&context
->fbo_list
, &entry
->entry
);
387 ++context
->fbo_entry_count
;
391 entry
= LIST_ENTRY(list_tail(&context
->fbo_list
), struct fbo_entry
, entry
);
392 context_reuse_fbo_entry(context
, entry
);
393 list_remove(&entry
->entry
);
394 list_add_head(&context
->fbo_list
, &entry
->entry
);
400 /* GL locking is done by the caller */
401 static void context_apply_fbo_entry(struct WineD3DContext
*context
, struct fbo_entry
*entry
)
403 IWineD3DDeviceImpl
*device
= ((IWineD3DSurfaceImpl
*)context
->surface
)->resource
.wineD3DDevice
;
404 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
407 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, &entry
->id
);
409 if (!entry
->attached
)
411 /* Apply render targets */
412 for (i
= 0; i
< GL_LIMITS(buffers
); ++i
)
414 IWineD3DSurface
*render_target
= device
->render_targets
[i
];
415 context_attach_surface_fbo(context
, GL_FRAMEBUFFER_EXT
, i
, render_target
);
418 /* Apply depth targets */
419 if (device
->stencilBufferTarget
)
421 unsigned int w
= ((IWineD3DSurfaceImpl
*)device
->render_targets
[0])->pow2Width
;
422 unsigned int h
= ((IWineD3DSurfaceImpl
*)device
->render_targets
[0])->pow2Height
;
424 surface_set_compatible_renderbuffer(device
->stencilBufferTarget
, w
, h
);
426 context_attach_depth_stencil_fbo(context
, GL_FRAMEBUFFER_EXT
, device
->stencilBufferTarget
, TRUE
);
428 entry
->attached
= TRUE
;
430 for (i
= 0; i
< GL_LIMITS(buffers
); ++i
)
432 if (device
->render_targets
[i
])
433 context_apply_attachment_filter_states(device
->render_targets
[i
], FALSE
);
435 if (device
->stencilBufferTarget
)
436 context_apply_attachment_filter_states(device
->stencilBufferTarget
, FALSE
);
439 for (i
= 0; i
< GL_LIMITS(buffers
); ++i
)
441 if (device
->render_targets
[i
])
442 device
->draw_buffers
[i
] = GL_COLOR_ATTACHMENT0_EXT
+ i
;
444 device
->draw_buffers
[i
] = GL_NONE
;
448 /* GL locking is done by the caller */
449 static void context_apply_fbo_state(struct WineD3DContext
*context
)
451 IWineD3DDeviceImpl
*device
= ((IWineD3DSurfaceImpl
*)context
->surface
)->resource
.wineD3DDevice
;
453 if (device
->render_offscreen
)
455 context
->current_fbo
= context_find_fbo_entry(context
);
456 context_apply_fbo_entry(context
, context
->current_fbo
);
458 context
->current_fbo
= NULL
;
459 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, NULL
);
462 context_check_fbo_status(context
);
465 void context_resource_released(IWineD3DDevice
*iface
, IWineD3DResource
*resource
, WINED3DRESOURCETYPE type
)
467 IWineD3DDeviceImpl
*This
= (IWineD3DDeviceImpl
*)iface
;
472 case WINED3DRTYPE_SURFACE
:
474 if (This
->activeContext
&& (IWineD3DSurface
*)resource
== This
->activeContext
->current_rt
)
476 IWineD3DSwapChainImpl
*swapchain
;
478 TRACE("Last active render target destroyed.\n");
480 /* Find a replacement surface for the currently active back
481 * buffer. The context manager does not do NULL checks, so
482 * switch to a valid target as long as the currently set
483 * surface is still valid. Use the surface of the implicit
484 * swpchain. If that is the same as the destroyed surface the
485 * device is destroyed and the lastActiveRenderTarget member
486 * shouldn't matter. */
487 swapchain
= This
->swapchains
? (IWineD3DSwapChainImpl
*)This
->swapchains
[0] : NULL
;
490 if (swapchain
->backBuffer
&& swapchain
->backBuffer
[0] != (IWineD3DSurface
*)resource
)
492 TRACE("Activating primary back buffer.\n");
493 ActivateContext(This
, swapchain
->backBuffer
[0], CTXUSAGE_RESOURCELOAD
);
495 else if (!swapchain
->backBuffer
&& swapchain
->frontBuffer
!= (IWineD3DSurface
*)resource
)
497 /* Single buffering environment */
498 TRACE("Activating primary front buffer.\n");
500 ActivateContext(This
, swapchain
->frontBuffer
, CTXUSAGE_RESOURCELOAD
);
504 /* Implicit render target destroyed, that means the
505 * device is being destroyed whatever we set here, it
506 * shouldn't matter. */
507 TRACE("Device is being destroyed, setting current_rt to 0xdeadbabe.\n");
508 This
->activeContext
->current_rt
= (IWineD3DSurface
*)0xdeadbabe;
513 WARN("Render target set, but swapchain does not exist!\n");
515 /* May happen during ddraw uninitialization. */
516 This
->activeContext
->current_rt
= (IWineD3DSurface
*)0xdeadcafe;
519 else if (This
->d3d_initialized
)
521 ActivateContext(This
, NULL
, CTXUSAGE_RESOURCELOAD
);
524 for (i
= 0; i
< This
->numContexts
; ++i
)
526 WineD3DContext
*context
= This
->contexts
[i
];
527 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
528 struct fbo_entry
*entry
, *entry2
;
532 LIST_FOR_EACH_ENTRY_SAFE(entry
, entry2
, &context
->fbo_list
, struct fbo_entry
, entry
)
534 BOOL destroyed
= FALSE
;
537 for (j
= 0; !destroyed
&& j
< GL_LIMITS(buffers
); ++j
)
539 if (entry
->render_targets
[j
] == (IWineD3DSurface
*)resource
)
541 context_destroy_fbo_entry(context
, entry
);
546 if (!destroyed
&& entry
->depth_stencil
== (IWineD3DSurface
*)resource
)
547 context_destroy_fbo_entry(context
, entry
);
561 DWORD
context_get_tls_idx(void)
563 return wined3d_context_tls_idx
;
566 void context_set_tls_idx(DWORD idx
)
568 wined3d_context_tls_idx
= idx
;
571 struct WineD3DContext
*context_get_current(void)
573 return TlsGetValue(wined3d_context_tls_idx
);
576 BOOL
context_set_current(struct WineD3DContext
*ctx
)
578 struct WineD3DContext
*old
= context_get_current();
582 TRACE("Already using D3D context %p.\n", ctx
);
588 TRACE("Switching to D3D context %p, GL context %p, device context %p.\n", ctx
, ctx
->glCtx
, ctx
->hdc
);
589 if (!pwglMakeCurrent(ctx
->hdc
, ctx
->glCtx
))
591 ERR("Failed to make GL context %p current on device context %p.\n", ctx
->glCtx
, ctx
->hdc
);
597 TRACE("Clearing current D3D context.\n");
598 if (!pwglMakeCurrent(NULL
, NULL
))
600 ERR("Failed to clear current GL context.\n");
605 return TlsSetValue(wined3d_context_tls_idx
, ctx
);
608 /*****************************************************************************
609 * Context_MarkStateDirty
611 * Marks a state in a context dirty. Only one context, opposed to
612 * IWineD3DDeviceImpl_MarkStateDirty, which marks the state dirty in all
616 * context: Context to mark the state dirty in
617 * state: State to mark dirty
618 * StateTable: Pointer to the state table in use(for state grouping)
620 *****************************************************************************/
621 static void Context_MarkStateDirty(WineD3DContext
*context
, DWORD state
, const struct StateEntry
*StateTable
) {
622 DWORD rep
= StateTable
[state
].representative
;
626 if(!rep
|| isStateDirty(context
, rep
)) return;
628 context
->dirtyArray
[context
->numDirtyEntries
++] = rep
;
631 context
->isStateDirty
[idx
] |= (1 << shift
);
634 /*****************************************************************************
637 * Adds a context to the context array. Helper function for CreateContext
639 * This method is not called in performance-critical code paths, only when a
640 * new render target or swapchain is created. Thus performance is not an issue
644 * This: Device to add the context for
645 * hdc: device context
646 * glCtx: WGL context to add
647 * pbuffer: optional pbuffer used with this context
649 *****************************************************************************/
650 static WineD3DContext
*AddContextToArray(IWineD3DDeviceImpl
*This
, HWND win_handle
, HDC hdc
, HGLRC glCtx
, HPBUFFERARB pbuffer
) {
651 WineD3DContext
**oldArray
= This
->contexts
;
654 This
->contexts
= HeapAlloc(GetProcessHeap(), 0, sizeof(*This
->contexts
) * (This
->numContexts
+ 1));
655 if(This
->contexts
== NULL
) {
656 ERR("Unable to grow the context array\n");
657 This
->contexts
= oldArray
;
661 memcpy(This
->contexts
, oldArray
, sizeof(*This
->contexts
) * This
->numContexts
);
664 This
->contexts
[This
->numContexts
] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(WineD3DContext
));
665 if(This
->contexts
[This
->numContexts
] == NULL
) {
666 ERR("Unable to allocate a new context\n");
667 HeapFree(GetProcessHeap(), 0, This
->contexts
);
668 This
->contexts
= oldArray
;
672 This
->contexts
[This
->numContexts
]->hdc
= hdc
;
673 This
->contexts
[This
->numContexts
]->glCtx
= glCtx
;
674 This
->contexts
[This
->numContexts
]->pbuffer
= pbuffer
;
675 This
->contexts
[This
->numContexts
]->win_handle
= win_handle
;
676 HeapFree(GetProcessHeap(), 0, oldArray
);
678 /* Mark all states dirty to force a proper initialization of the states on the first use of the context
680 for(state
= 0; state
<= STATE_HIGHEST
; state
++) {
681 Context_MarkStateDirty(This
->contexts
[This
->numContexts
], state
, This
->StateTable
);
685 TRACE("Created context %p\n", This
->contexts
[This
->numContexts
- 1]);
686 return This
->contexts
[This
->numContexts
- 1];
689 /* This function takes care of WineD3D pixel format selection. */
690 static int WineD3D_ChoosePixelFormat(IWineD3DDeviceImpl
*This
, HDC hdc
,
691 const struct GlPixelFormatDesc
*color_format_desc
, const struct GlPixelFormatDesc
*ds_format_desc
,
692 BOOL auxBuffers
, int numSamples
, BOOL pbuffer
, BOOL findCompatible
)
695 unsigned int matchtry
;
696 short redBits
, greenBits
, blueBits
, alphaBits
, colorBits
;
697 short depthBits
=0, stencilBits
=0;
704 /* First, try without alpha match buffers. MacOS supports aux buffers only
705 * on A8R8G8B8, and we prefer better offscreen rendering over an alpha match.
706 * Then try without aux buffers - this is the most common cause for not
707 * finding a pixel format. Also some drivers(the open source ones)
708 * only offer 32 bit ARB pixel formats. First try without an exact alpha
709 * match, then try without an exact alpha and color match.
711 { TRUE
, TRUE
, TRUE
},
712 { TRUE
, FALSE
, TRUE
},
713 { FALSE
, TRUE
, TRUE
},
714 { FALSE
, FALSE
, TRUE
},
715 { TRUE
, FALSE
, FALSE
},
716 { FALSE
, FALSE
, FALSE
},
720 int nCfgs
= This
->adapter
->nCfgs
;
722 TRACE("ColorFormat=%s, DepthStencilFormat=%s, auxBuffers=%d, numSamples=%d, pbuffer=%d, findCompatible=%d\n",
723 debug_d3dformat(color_format_desc
->format
), debug_d3dformat(ds_format_desc
->format
),
724 auxBuffers
, numSamples
, pbuffer
, findCompatible
);
726 if (!getColorBits(color_format_desc
, &redBits
, &greenBits
, &blueBits
, &alphaBits
, &colorBits
))
728 ERR("Unable to get color bits for format %s (%#x)!\n",
729 debug_d3dformat(color_format_desc
->format
), color_format_desc
->format
);
733 /* In WGL both color, depth and stencil are features of a pixel format. In case of D3D they are separate.
734 * You are able to add a depth + stencil surface at a later stage when you need it.
735 * In order to support this properly in WineD3D we need the ability to recreate the opengl context and
736 * drawable when this is required. This is very tricky as we need to reapply ALL opengl states for the new
737 * context, need torecreate shaders, textures and other resources.
739 * The context manager already takes care of the state problem and for the other tasks code from Reset
740 * can be used. These changes are way to risky during the 1.0 code freeze which is taking place right now.
741 * Likely a lot of other new bugs will be exposed. For that reason request a depth stencil surface all the
742 * time. It can cause a slight performance hit but fixes a lot of regressions. A fixme reminds of that this
743 * issue needs to be fixed. */
744 if (ds_format_desc
->format
!= WINED3DFMT_D24S8
)
746 FIXME("Add OpenGL context recreation support to SetDepthStencilSurface\n");
747 ds_format_desc
= getFormatDescEntry(WINED3DFMT_D24S8
, &This
->adapter
->gl_info
);
750 getDepthStencilBits(ds_format_desc
, &depthBits
, &stencilBits
);
752 for(matchtry
= 0; matchtry
< (sizeof(matches
) / sizeof(matches
[0])) && !iPixelFormat
; matchtry
++) {
753 for(i
=0; i
<nCfgs
; i
++) {
754 BOOL exactDepthMatch
= TRUE
;
755 WineD3D_PixelFormat
*cfg
= &This
->adapter
->cfgs
[i
];
757 /* For now only accept RGBA formats. Perhaps some day we will
758 * allow floating point formats for pbuffers. */
759 if(cfg
->iPixelType
!= WGL_TYPE_RGBA_ARB
)
762 /* In window mode (!pbuffer) we need a window drawable format and double buffering. */
763 if(!pbuffer
&& !(cfg
->windowDrawable
&& cfg
->doubleBuffer
))
766 /* We like to have aux buffers in backbuffer mode */
767 if(auxBuffers
&& !cfg
->auxBuffers
&& matches
[matchtry
].require_aux
)
770 /* In pbuffer-mode we need a pbuffer-capable format but we don't want double buffering */
771 if(pbuffer
&& (!cfg
->pbufferDrawable
|| cfg
->doubleBuffer
))
774 if(matches
[matchtry
].exact_color
) {
775 if(cfg
->redSize
!= redBits
)
777 if(cfg
->greenSize
!= greenBits
)
779 if(cfg
->blueSize
!= blueBits
)
782 if(cfg
->redSize
< redBits
)
784 if(cfg
->greenSize
< greenBits
)
786 if(cfg
->blueSize
< blueBits
)
789 if(matches
[matchtry
].exact_alpha
) {
790 if(cfg
->alphaSize
!= alphaBits
)
793 if(cfg
->alphaSize
< alphaBits
)
797 /* We try to locate a format which matches our requirements exactly. In case of
798 * depth it is no problem to emulate 16-bit using e.g. 24-bit, so accept that. */
799 if(cfg
->depthSize
< depthBits
)
801 else if(cfg
->depthSize
> depthBits
)
802 exactDepthMatch
= FALSE
;
804 /* In all cases make sure the number of stencil bits matches our requirements
805 * even when we don't need stencil because it could affect performance EXCEPT
806 * on cards which don't offer depth formats without stencil like the i915 drivers
808 if(stencilBits
!= cfg
->stencilSize
&& !(This
->adapter
->brokenStencil
&& stencilBits
<= cfg
->stencilSize
))
811 /* Check multisampling support */
812 if(cfg
->numSamples
!= numSamples
)
815 /* When we have passed all the checks then we have found a format which matches our
816 * requirements. Note that we only check for a limit number of capabilities right now,
817 * so there can easily be a dozen of pixel formats which appear to be the 'same' but
818 * can still differ in things like multisampling, stereo, SRGB and other flags.
821 /* Exit the loop as we have found a format :) */
822 if(exactDepthMatch
) {
823 iPixelFormat
= cfg
->iPixelFormat
;
825 } else if(!iPixelFormat
) {
826 /* In the end we might end up with a format which doesn't exactly match our depth
827 * requirements. Accept the first format we found because formats with higher iPixelFormat
828 * values tend to have more extended capabilities (e.g. multisampling) which we don't need. */
829 iPixelFormat
= cfg
->iPixelFormat
;
834 /* When findCompatible is set and no suitable format was found, let ChoosePixelFormat choose a pixel format in order not to crash. */
835 if(!iPixelFormat
&& !findCompatible
) {
836 ERR("Can't find a suitable iPixelFormat\n");
838 } else if(!iPixelFormat
) {
839 PIXELFORMATDESCRIPTOR pfd
;
841 TRACE("Falling back to ChoosePixelFormat as we weren't able to find an exactly matching pixel format\n");
842 /* PixelFormat selection */
843 ZeroMemory(&pfd
, sizeof(pfd
));
844 pfd
.nSize
= sizeof(pfd
);
846 pfd
.dwFlags
= PFD_SUPPORT_OPENGL
| PFD_DOUBLEBUFFER
| PFD_DRAW_TO_WINDOW
;/*PFD_GENERIC_ACCELERATED*/
847 pfd
.iPixelType
= PFD_TYPE_RGBA
;
848 pfd
.cAlphaBits
= alphaBits
;
849 pfd
.cColorBits
= colorBits
;
850 pfd
.cDepthBits
= depthBits
;
851 pfd
.cStencilBits
= stencilBits
;
852 pfd
.iLayerType
= PFD_MAIN_PLANE
;
854 iPixelFormat
= ChoosePixelFormat(hdc
, &pfd
);
856 /* If this happens something is very wrong as ChoosePixelFormat barely fails */
857 ERR("Can't find a suitable iPixelFormat\n");
862 TRACE("Found iPixelFormat=%d for ColorFormat=%s, DepthStencilFormat=%s\n",
863 iPixelFormat
, debug_d3dformat(color_format_desc
->format
), debug_d3dformat(ds_format_desc
->format
));
867 /*****************************************************************************
870 * Creates a new context for a window, or a pbuffer context.
873 * This: Device to activate the context for
874 * target: Surface this context will render to
875 * win_handle: handle to the window which we are drawing to
876 * create_pbuffer: tells whether to create a pbuffer or not
877 * pPresentParameters: contains the pixelformats to use for onscreen rendering
879 *****************************************************************************/
880 WineD3DContext
*CreateContext(IWineD3DDeviceImpl
*This
, IWineD3DSurfaceImpl
*target
, HWND win_handle
, BOOL create_pbuffer
, const WINED3DPRESENT_PARAMETERS
*pPresentParms
) {
881 const struct wined3d_gl_info
*gl_info
= &This
->adapter
->gl_info
;
882 HPBUFFERARB pbuffer
= NULL
;
883 WineD3DContext
*ret
= NULL
;
888 TRACE("(%p): Creating a %s context for render target %p\n", This
, create_pbuffer
? "offscreen" : "onscreen", target
);
891 HDC hdc_parent
= GetDC(win_handle
);
892 int iPixelFormat
= 0;
894 IWineD3DSurface
*StencilSurface
= This
->stencilBufferTarget
;
895 const struct GlPixelFormatDesc
*ds_format_desc
= StencilSurface
896 ? ((IWineD3DSurfaceImpl
*)StencilSurface
)->resource
.format_desc
897 : getFormatDescEntry(WINED3DFMT_UNKNOWN
, &This
->adapter
->gl_info
);
899 /* Try to find a pixel format with pbuffer support. */
900 iPixelFormat
= WineD3D_ChoosePixelFormat(This
, hdc_parent
, target
->resource
.format_desc
,
901 ds_format_desc
, FALSE
/* auxBuffers */, 0 /* numSamples */, TRUE
/* PBUFFER */,
902 FALSE
/* findCompatible */);
904 TRACE("Trying to locate a compatible pixel format because an exact match failed.\n");
906 /* For some reason we weren't able to find a format, try to find something instead of crashing.
907 * A reason for failure could have been wglChoosePixelFormatARB strictness. */
908 iPixelFormat
= WineD3D_ChoosePixelFormat(This
, hdc_parent
, target
->resource
.format_desc
,
909 ds_format_desc
, FALSE
/* auxBuffer */, 0 /* numSamples */, TRUE
/* PBUFFER */,
910 TRUE
/* findCompatible */);
913 /* This shouldn't happen as ChoosePixelFormat always returns something */
915 ERR("Unable to locate a pixel format for a pbuffer\n");
916 ReleaseDC(win_handle
, hdc_parent
);
920 TRACE("Creating a pBuffer drawable for the new context\n");
921 pbuffer
= GL_EXTCALL(wglCreatePbufferARB(hdc_parent
, iPixelFormat
, target
->currentDesc
.Width
, target
->currentDesc
.Height
, 0));
923 ERR("Cannot create a pbuffer\n");
924 ReleaseDC(win_handle
, hdc_parent
);
928 /* In WGL a pbuffer is 'wrapped' inside a HDC to 'fool' wglMakeCurrent */
929 hdc
= GL_EXTCALL(wglGetPbufferDCARB(pbuffer
));
931 ERR("Cannot get a HDC for pbuffer (%p)\n", pbuffer
);
932 GL_EXTCALL(wglDestroyPbufferARB(pbuffer
));
933 ReleaseDC(win_handle
, hdc_parent
);
936 ReleaseDC(win_handle
, hdc_parent
);
938 PIXELFORMATDESCRIPTOR pfd
;
941 const struct GlPixelFormatDesc
*color_format_desc
= target
->resource
.format_desc
;
942 const struct GlPixelFormatDesc
*ds_format_desc
= getFormatDescEntry(WINED3DFMT_UNKNOWN
,
943 &This
->adapter
->gl_info
);
944 BOOL auxBuffers
= FALSE
;
947 hdc
= GetDC(win_handle
);
949 ERR("Cannot retrieve a device context!\n");
953 /* In case of ORM_BACKBUFFER, make sure to request an alpha component for X4R4G4B4/X8R8G8B8 as we might need it for the backbuffer. */
954 if(wined3d_settings
.offscreen_rendering_mode
== ORM_BACKBUFFER
) {
957 if (color_format_desc
->format
== WINED3DFMT_X4R4G4B4
)
958 color_format_desc
= getFormatDescEntry(WINED3DFMT_A4R4G4B4
, &This
->adapter
->gl_info
);
959 else if (color_format_desc
->format
== WINED3DFMT_X8R8G8B8
)
960 color_format_desc
= getFormatDescEntry(WINED3DFMT_A8R8G8B8
, &This
->adapter
->gl_info
);
963 /* DirectDraw supports 8bit paletted render targets and these are used by old games like Starcraft and C&C.
964 * Most modern hardware doesn't support 8bit natively so we perform some form of 8bit -> 32bit conversion.
965 * The conversion (ab)uses the alpha component for storing the palette index. For this reason we require
966 * a format with 8bit alpha, so request A8R8G8B8. */
967 if (color_format_desc
->format
== WINED3DFMT_P8
)
968 color_format_desc
= getFormatDescEntry(WINED3DFMT_A8R8G8B8
, &This
->adapter
->gl_info
);
970 /* Retrieve the depth stencil format from the present parameters.
971 * The choice of the proper format can give a nice performance boost
972 * in case of GPU limited programs. */
973 if(pPresentParms
->EnableAutoDepthStencil
) {
974 TRACE("pPresentParms->EnableAutoDepthStencil=enabled; using AutoDepthStencilFormat=%s\n", debug_d3dformat(pPresentParms
->AutoDepthStencilFormat
));
975 ds_format_desc
= getFormatDescEntry(pPresentParms
->AutoDepthStencilFormat
, &This
->adapter
->gl_info
);
978 /* D3D only allows multisampling when SwapEffect is set to WINED3DSWAPEFFECT_DISCARD */
979 if(pPresentParms
->MultiSampleType
&& (pPresentParms
->SwapEffect
== WINED3DSWAPEFFECT_DISCARD
)) {
980 if(!GL_SUPPORT(ARB_MULTISAMPLE
))
981 ERR("The program is requesting multisampling without support!\n");
983 ERR("Requesting MultiSampleType=%d\n", pPresentParms
->MultiSampleType
);
984 numSamples
= pPresentParms
->MultiSampleType
;
988 /* Try to find a pixel format which matches our requirements */
989 iPixelFormat
= WineD3D_ChoosePixelFormat(This
, hdc
, color_format_desc
, ds_format_desc
,
990 auxBuffers
, numSamples
, FALSE
/* PBUFFER */, FALSE
/* findCompatible */);
992 /* Try to locate a compatible format if we weren't able to find anything */
994 TRACE("Trying to locate a compatible pixel format because an exact match failed.\n");
995 iPixelFormat
= WineD3D_ChoosePixelFormat(This
, hdc
, color_format_desc
, ds_format_desc
,
996 auxBuffers
, 0 /* numSamples */, FALSE
/* PBUFFER */, TRUE
/* findCompatible */ );
999 /* If we still don't have a pixel format, something is very wrong as ChoosePixelFormat barely fails */
1001 ERR("Can't find a suitable iPixelFormat\n");
1005 DescribePixelFormat(hdc
, iPixelFormat
, sizeof(pfd
), &pfd
);
1006 res
= SetPixelFormat(hdc
, iPixelFormat
, NULL
);
1008 int oldPixelFormat
= GetPixelFormat(hdc
);
1010 /* By default WGL doesn't allow pixel format adjustments but we need it here.
1011 * For this reason there is a WINE-specific wglSetPixelFormat which allows you to
1012 * set the pixel format multiple times. Only use it when it is really needed. */
1014 if(oldPixelFormat
== iPixelFormat
) {
1015 /* We don't have to do anything as the formats are the same :) */
1016 } else if(oldPixelFormat
&& GL_SUPPORT(WGL_WINE_PIXEL_FORMAT_PASSTHROUGH
)) {
1017 res
= GL_EXTCALL(wglSetPixelFormatWINE(hdc
, iPixelFormat
, NULL
));
1020 ERR("wglSetPixelFormatWINE failed on HDC=%p for iPixelFormat=%d\n", hdc
, iPixelFormat
);
1023 } else if(oldPixelFormat
) {
1024 /* OpenGL doesn't allow pixel format adjustments. Print an error and continue using the old format.
1025 * There's a big chance that the old format works although with a performance hit and perhaps rendering errors. */
1026 ERR("HDC=%p is already set to iPixelFormat=%d and OpenGL doesn't allow changes!\n", hdc
, oldPixelFormat
);
1028 ERR("SetPixelFormat failed on HDC=%p for iPixelFormat=%d\n", hdc
, iPixelFormat
);
1034 ctx
= pwglCreateContext(hdc
);
1035 if (This
->numContexts
)
1037 if (!pwglShareLists(This
->contexts
[0]->glCtx
, ctx
))
1039 DWORD err
= GetLastError();
1040 ERR("wglShareLists(%p, %p) failed, last error %#x.\n",
1041 This
->contexts
[0]->glCtx
, ctx
, err
);
1046 ERR("Failed to create a WGL context\n");
1047 if(create_pbuffer
) {
1048 GL_EXTCALL(wglReleasePbufferDCARB(pbuffer
, hdc
));
1049 GL_EXTCALL(wglDestroyPbufferARB(pbuffer
));
1053 ret
= AddContextToArray(This
, win_handle
, hdc
, ctx
, pbuffer
);
1055 ERR("Failed to add the newly created context to the context list\n");
1056 if (!pwglDeleteContext(ctx
))
1058 DWORD err
= GetLastError();
1059 ERR("wglDeleteContext(%p) failed, last error %#x.\n", ctx
, err
);
1061 if(create_pbuffer
) {
1062 GL_EXTCALL(wglReleasePbufferDCARB(pbuffer
, hdc
));
1063 GL_EXTCALL(wglDestroyPbufferARB(pbuffer
));
1067 ret
->gl_info
= &This
->adapter
->gl_info
;
1068 ret
->surface
= (IWineD3DSurface
*) target
;
1069 ret
->current_rt
= (IWineD3DSurface
*)target
;
1070 ret
->isPBuffer
= create_pbuffer
;
1071 ret
->tid
= GetCurrentThreadId();
1072 if(This
->shader_backend
->shader_dirtifyable_constants((IWineD3DDevice
*) This
)) {
1073 /* Create the dirty constants array and initialize them to dirty */
1074 ret
->vshader_const_dirty
= HeapAlloc(GetProcessHeap(), 0,
1075 sizeof(*ret
->vshader_const_dirty
) * GL_LIMITS(vshader_constantsF
));
1076 ret
->pshader_const_dirty
= HeapAlloc(GetProcessHeap(), 0,
1077 sizeof(*ret
->pshader_const_dirty
) * GL_LIMITS(pshader_constantsF
));
1078 memset(ret
->vshader_const_dirty
, 1,
1079 sizeof(*ret
->vshader_const_dirty
) * GL_LIMITS(vshader_constantsF
));
1080 memset(ret
->pshader_const_dirty
, 1,
1081 sizeof(*ret
->pshader_const_dirty
) * GL_LIMITS(pshader_constantsF
));
1084 TRACE("Successfully created new context %p\n", ret
);
1086 list_init(&ret
->fbo_list
);
1088 /* Set up the context defaults */
1089 if (!context_set_current(ret
))
1091 ERR("Cannot activate context to set up defaults\n");
1097 glGetIntegerv(GL_AUX_BUFFERS
, &ret
->aux_buffers
);
1099 TRACE("Setting up the screen\n");
1100 /* Clear the screen */
1101 glClearColor(1.0f
, 0.0f
, 0.0f
, 0.0f
);
1102 checkGLcall("glClearColor");
1105 glClearStencil(0xffff);
1107 checkGLcall("glClear");
1109 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER
, GL_TRUE
);
1110 checkGLcall("glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);");
1112 glTexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_COMBINE_EXT
);
1113 checkGLcall("glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);");
1115 glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL
, GL_SEPARATE_SPECULAR_COLOR
);
1116 checkGLcall("glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);");
1118 glPixelStorei(GL_PACK_ALIGNMENT
, This
->surface_alignment
);
1119 checkGLcall("glPixelStorei(GL_PACK_ALIGNMENT, This->surface_alignment);");
1120 glPixelStorei(GL_UNPACK_ALIGNMENT
, This
->surface_alignment
);
1121 checkGLcall("glPixelStorei(GL_UNPACK_ALIGNMENT, This->surface_alignment);");
1123 if(GL_SUPPORT(APPLE_CLIENT_STORAGE
)) {
1124 /* Most textures will use client storage if supported. Exceptions are non-native power of 2 textures
1125 * and textures in DIB sections(due to the memory protection).
1127 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE
, GL_TRUE
);
1128 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
1130 if(GL_SUPPORT(ARB_VERTEX_BLEND
)) {
1131 /* Direct3D always uses n-1 weights for n world matrices and uses 1 - sum for the last one
1132 * this is equal to GL_WEIGHT_SUM_UNITY_ARB. Enabling it doesn't do anything unless
1133 * GL_VERTEX_BLEND_ARB isn't enabled too
1135 glEnable(GL_WEIGHT_SUM_UNITY_ARB
);
1136 checkGLcall("glEnable(GL_WEIGHT_SUM_UNITY_ARB)");
1138 if(GL_SUPPORT(NV_TEXTURE_SHADER2
)) {
1139 /* Set up the previous texture input for all shader units. This applies to bump mapping, and in d3d
1140 * the previous texture where to source the offset from is always unit - 1.
1142 for(s
= 1; s
< GL_LIMITS(textures
); s
++) {
1143 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB
+ s
));
1144 glTexEnvi(GL_TEXTURE_SHADER_NV
, GL_PREVIOUS_TEXTURE_INPUT_NV
, GL_TEXTURE0_ARB
+ s
- 1);
1145 checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, ...");
1148 if(GL_SUPPORT(ARB_FRAGMENT_PROGRAM
)) {
1149 /* MacOS(radeon X1600 at least, but most likely others too) refuses to draw if GLSL and ARBFP are
1150 * enabled, but the currently bound arbfp program is 0. Enabling ARBFP with prog 0 is invalid, but
1151 * GLSL should bypass this. This causes problems in programs that never use the fixed function pipeline,
1152 * because the ARBFP extension is enabled by the ARBFP pipeline at context creation, but no program
1155 * So make sure a program is assigned to each context. The first real ARBFP use will set a different
1156 * program and the dummy program is destroyed when the context is destroyed.
1158 const char *dummy_program
=
1160 "MOV result.color, fragment.color.primary;\n"
1162 GL_EXTCALL(glGenProgramsARB(1, &ret
->dummy_arbfp_prog
));
1163 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB
, ret
->dummy_arbfp_prog
));
1164 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB
, GL_PROGRAM_FORMAT_ASCII_ARB
, strlen(dummy_program
), dummy_program
));
1167 for(s
= 0; s
< GL_LIMITS(point_sprite_units
); s
++) {
1168 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB
+ s
));
1169 glTexEnvi(GL_POINT_SPRITE_ARB
, GL_COORD_REPLACE_ARB
, GL_TRUE
);
1170 checkGLcall("glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE)");
1174 context_set_last_device(This
);
1175 This
->frag_pipe
->enable_extension((IWineD3DDevice
*) This
, TRUE
);
1183 /*****************************************************************************
1184 * RemoveContextFromArray
1186 * Removes a context from the context manager. The opengl context is not
1187 * destroyed or unset. context is not a valid pointer after that call.
1189 * Similar to the former call this isn't a performance critical function. A
1190 * helper function for DestroyContext.
1193 * This: Device to activate the context for
1194 * context: Context to remove
1196 *****************************************************************************/
1197 static void RemoveContextFromArray(IWineD3DDeviceImpl
*This
, WineD3DContext
*context
) {
1198 WineD3DContext
**new_array
;
1202 TRACE("Removing ctx %p\n", context
);
1204 for (i
= 0; i
< This
->numContexts
; ++i
)
1206 if (This
->contexts
[i
] == context
)
1208 HeapFree(GetProcessHeap(), 0, context
);
1216 ERR("Context %p doesn't exist in context array\n", context
);
1220 while (i
< This
->numContexts
- 1)
1222 This
->contexts
[i
] = This
->contexts
[i
+ 1];
1226 --This
->numContexts
;
1227 if (!This
->numContexts
)
1229 HeapFree(GetProcessHeap(), 0, This
->contexts
);
1230 This
->contexts
= NULL
;
1234 new_array
= HeapReAlloc(GetProcessHeap(), 0, This
->contexts
, This
->numContexts
* sizeof(*This
->contexts
));
1237 ERR("Failed to shrink context array. Oh well.\n");
1241 This
->contexts
= new_array
;
1244 /*****************************************************************************
1247 * Destroys a wineD3DContext
1250 * This: Device to activate the context for
1251 * context: Context to destroy
1253 *****************************************************************************/
1254 void DestroyContext(IWineD3DDeviceImpl
*This
, WineD3DContext
*context
) {
1255 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
1256 struct fbo_entry
*entry
, *entry2
;
1259 TRACE("Destroying ctx %p\n", context
);
1261 /* The correct GL context needs to be active to cleanup the GL resources below */
1262 has_glctx
= pwglMakeCurrent(context
->hdc
, context
->glCtx
);
1263 context_set_last_device(NULL
);
1265 if (!has_glctx
) WARN("Failed to activate context. Window already destroyed?\n");
1269 LIST_FOR_EACH_ENTRY_SAFE(entry
, entry2
, &context
->fbo_list
, struct fbo_entry
, entry
) {
1270 if (!has_glctx
) entry
->id
= 0;
1271 context_destroy_fbo_entry(context
, entry
);
1275 if (context
->src_fbo
)
1277 TRACE("Destroy src FBO %d\n", context
->src_fbo
);
1278 context_destroy_fbo(context
, &context
->src_fbo
);
1280 if (context
->dst_fbo
)
1282 TRACE("Destroy dst FBO %d\n", context
->dst_fbo
);
1283 context_destroy_fbo(context
, &context
->dst_fbo
);
1285 if (context
->dummy_arbfp_prog
)
1287 GL_EXTCALL(glDeleteProgramsARB(1, &context
->dummy_arbfp_prog
));
1293 if (This
->activeContext
== context
)
1295 This
->activeContext
= NULL
;
1296 TRACE("Destroying the active context.\n");
1299 if (!context_set_current(NULL
))
1301 ERR("Failed to clear current D3D context.\n");
1304 if(context
->isPBuffer
) {
1305 GL_EXTCALL(wglReleasePbufferDCARB(context
->pbuffer
, context
->hdc
));
1306 GL_EXTCALL(wglDestroyPbufferARB(context
->pbuffer
));
1307 } else ReleaseDC(context
->win_handle
, context
->hdc
);
1308 pwglDeleteContext(context
->glCtx
);
1310 HeapFree(GetProcessHeap(), 0, context
->vshader_const_dirty
);
1311 HeapFree(GetProcessHeap(), 0, context
->pshader_const_dirty
);
1312 RemoveContextFromArray(This
, context
);
1315 /* GL locking is done by the caller */
1316 static inline void set_blit_dimension(UINT width
, UINT height
) {
1317 glMatrixMode(GL_PROJECTION
);
1318 checkGLcall("glMatrixMode(GL_PROJECTION)");
1320 checkGLcall("glLoadIdentity()");
1321 glOrtho(0, width
, height
, 0, 0.0, -1.0);
1322 checkGLcall("glOrtho");
1323 glViewport(0, 0, width
, height
);
1324 checkGLcall("glViewport");
1327 /*****************************************************************************
1330 * Sets up a context for DirectDraw blitting.
1331 * All texture units are disabled, texture unit 0 is set as current unit
1332 * fog, lighting, blending, alpha test, z test, scissor test, culling disabled
1333 * color writing enabled for all channels
1334 * register combiners disabled, shaders disabled
1335 * world matrix is set to identity, texture matrix 0 too
1336 * projection matrix is setup for drawing screen coordinates
1339 * This: Device to activate the context for
1340 * context: Context to setup
1341 * width: render target width
1342 * height: render target height
1344 *****************************************************************************/
1345 /* Context activation is done by the caller. */
1346 static inline void SetupForBlit(IWineD3DDeviceImpl
*This
, WineD3DContext
*context
, UINT width
, UINT height
) {
1348 const struct StateEntry
*StateTable
= This
->StateTable
;
1349 const struct wined3d_gl_info
*gl_info
= context
->gl_info
;
1351 TRACE("Setting up context %p for blitting\n", context
);
1352 if(context
->last_was_blit
) {
1353 if(context
->blit_w
!= width
|| context
->blit_h
!= height
) {
1355 set_blit_dimension(width
, height
);
1357 context
->blit_w
= width
; context
->blit_h
= height
;
1358 /* No need to dirtify here, the states are still dirtified because they weren't
1359 * applied since the last SetupForBlit call. Otherwise last_was_blit would not
1363 TRACE("Context is already set up for blitting, nothing to do\n");
1366 context
->last_was_blit
= TRUE
;
1368 /* TODO: Use a display list */
1370 /* Disable shaders */
1372 This
->shader_backend
->shader_select((IWineD3DDevice
*)This
, FALSE
, FALSE
);
1375 Context_MarkStateDirty(context
, STATE_VSHADER
, StateTable
);
1376 Context_MarkStateDirty(context
, STATE_PIXELSHADER
, StateTable
);
1378 /* Call ENTER_GL() once for all gl calls below. In theory we should not call
1379 * helper functions in between gl calls. This function is full of Context_MarkStateDirty
1380 * which can safely be called from here, we only lock once instead locking/unlocking
1381 * after each GL call.
1385 /* Disable all textures. The caller can then bind a texture it wants to blit
1388 * The blitting code uses (for now) the fixed function pipeline, so make sure to reset all fixed
1389 * function texture unit. No need to care for higher samplers
1391 for(i
= GL_LIMITS(textures
) - 1; i
> 0 ; i
--) {
1392 sampler
= This
->rev_tex_unit_map
[i
];
1393 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB
+ i
));
1394 checkGLcall("glActiveTextureARB");
1396 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP
)) {
1397 glDisable(GL_TEXTURE_CUBE_MAP_ARB
);
1398 checkGLcall("glDisable GL_TEXTURE_CUBE_MAP_ARB");
1400 glDisable(GL_TEXTURE_3D
);
1401 checkGLcall("glDisable GL_TEXTURE_3D");
1402 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE
)) {
1403 glDisable(GL_TEXTURE_RECTANGLE_ARB
);
1404 checkGLcall("glDisable GL_TEXTURE_RECTANGLE_ARB");
1406 glDisable(GL_TEXTURE_2D
);
1407 checkGLcall("glDisable GL_TEXTURE_2D");
1409 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
1410 checkGLcall("glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);");
1412 if (sampler
!= -1) {
1413 if (sampler
< MAX_TEXTURES
) {
1414 Context_MarkStateDirty(context
, STATE_TEXTURESTAGE(sampler
, WINED3DTSS_COLOROP
), StateTable
);
1416 Context_MarkStateDirty(context
, STATE_SAMPLER(sampler
), StateTable
);
1419 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB
));
1420 checkGLcall("glActiveTextureARB");
1422 sampler
= This
->rev_tex_unit_map
[0];
1424 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP
)) {
1425 glDisable(GL_TEXTURE_CUBE_MAP_ARB
);
1426 checkGLcall("glDisable GL_TEXTURE_CUBE_MAP_ARB");
1428 glDisable(GL_TEXTURE_3D
);
1429 checkGLcall("glDisable GL_TEXTURE_3D");
1430 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE
)) {
1431 glDisable(GL_TEXTURE_RECTANGLE_ARB
);
1432 checkGLcall("glDisable GL_TEXTURE_RECTANGLE_ARB");
1434 glDisable(GL_TEXTURE_2D
);
1435 checkGLcall("glDisable GL_TEXTURE_2D");
1437 glTexEnvi(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_REPLACE
);
1439 glMatrixMode(GL_TEXTURE
);
1440 checkGLcall("glMatrixMode(GL_TEXTURE)");
1442 checkGLcall("glLoadIdentity()");
1444 if (GL_SUPPORT(EXT_TEXTURE_LOD_BIAS
)) {
1445 glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT
,
1446 GL_TEXTURE_LOD_BIAS_EXT
,
1448 checkGLcall("glTexEnvi GL_TEXTURE_LOD_BIAS_EXT ...");
1451 if (sampler
!= -1) {
1452 if (sampler
< MAX_TEXTURES
) {
1453 Context_MarkStateDirty(context
, STATE_TRANSFORM(WINED3DTS_TEXTURE0
+ sampler
), StateTable
);
1454 Context_MarkStateDirty(context
, STATE_TEXTURESTAGE(sampler
, WINED3DTSS_COLOROP
), StateTable
);
1456 Context_MarkStateDirty(context
, STATE_SAMPLER(sampler
), StateTable
);
1459 /* Other misc states */
1460 glDisable(GL_ALPHA_TEST
);
1461 checkGLcall("glDisable(GL_ALPHA_TEST)");
1462 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_ALPHATESTENABLE
), StateTable
);
1463 glDisable(GL_LIGHTING
);
1464 checkGLcall("glDisable GL_LIGHTING");
1465 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_LIGHTING
), StateTable
);
1466 glDisable(GL_DEPTH_TEST
);
1467 checkGLcall("glDisable GL_DEPTH_TEST");
1468 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_ZENABLE
), StateTable
);
1469 glDisableWINE(GL_FOG
);
1470 checkGLcall("glDisable GL_FOG");
1471 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_FOGENABLE
), StateTable
);
1472 glDisable(GL_BLEND
);
1473 checkGLcall("glDisable GL_BLEND");
1474 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE
), StateTable
);
1475 glDisable(GL_CULL_FACE
);
1476 checkGLcall("glDisable GL_CULL_FACE");
1477 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_CULLMODE
), StateTable
);
1478 glDisable(GL_STENCIL_TEST
);
1479 checkGLcall("glDisable GL_STENCIL_TEST");
1480 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_STENCILENABLE
), StateTable
);
1481 glDisable(GL_SCISSOR_TEST
);
1482 checkGLcall("glDisable GL_SCISSOR_TEST");
1483 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE
), StateTable
);
1484 if(GL_SUPPORT(ARB_POINT_SPRITE
)) {
1485 glDisable(GL_POINT_SPRITE_ARB
);
1486 checkGLcall("glDisable GL_POINT_SPRITE_ARB");
1487 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_POINTSPRITEENABLE
), StateTable
);
1489 glColorMask(GL_TRUE
, GL_TRUE
,GL_TRUE
,GL_TRUE
);
1490 checkGLcall("glColorMask");
1491 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_CLIPPING
), StateTable
);
1492 if (GL_SUPPORT(EXT_SECONDARY_COLOR
)) {
1493 glDisable(GL_COLOR_SUM_EXT
);
1494 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_SPECULARENABLE
), StateTable
);
1495 checkGLcall("glDisable(GL_COLOR_SUM_EXT)");
1498 /* Setup transforms */
1499 glMatrixMode(GL_MODELVIEW
);
1500 checkGLcall("glMatrixMode(GL_MODELVIEW)");
1502 checkGLcall("glLoadIdentity()");
1503 Context_MarkStateDirty(context
, STATE_TRANSFORM(WINED3DTS_WORLDMATRIX(0)), StateTable
);
1505 context
->last_was_rhw
= TRUE
;
1506 Context_MarkStateDirty(context
, STATE_VDECL
, StateTable
); /* because of last_was_rhw = TRUE */
1508 glDisable(GL_CLIP_PLANE0
); checkGLcall("glDisable(clip plane 0)");
1509 glDisable(GL_CLIP_PLANE1
); checkGLcall("glDisable(clip plane 1)");
1510 glDisable(GL_CLIP_PLANE2
); checkGLcall("glDisable(clip plane 2)");
1511 glDisable(GL_CLIP_PLANE3
); checkGLcall("glDisable(clip plane 3)");
1512 glDisable(GL_CLIP_PLANE4
); checkGLcall("glDisable(clip plane 4)");
1513 glDisable(GL_CLIP_PLANE5
); checkGLcall("glDisable(clip plane 5)");
1514 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_CLIPPING
), StateTable
);
1516 set_blit_dimension(width
, height
);
1520 context
->blit_w
= width
; context
->blit_h
= height
;
1521 Context_MarkStateDirty(context
, STATE_VIEWPORT
, StateTable
);
1522 Context_MarkStateDirty(context
, STATE_TRANSFORM(WINED3DTS_PROJECTION
), StateTable
);
1525 This
->frag_pipe
->enable_extension((IWineD3DDevice
*) This
, FALSE
);
1528 /*****************************************************************************
1529 * findThreadContextForSwapChain
1531 * Searches a swapchain for all contexts and picks one for the thread tid.
1532 * If none can be found the swapchain is requested to create a new context
1534 *****************************************************************************/
1535 static WineD3DContext
*findThreadContextForSwapChain(IWineD3DSwapChain
*swapchain
, DWORD tid
) {
1538 for(i
= 0; i
< ((IWineD3DSwapChainImpl
*) swapchain
)->num_contexts
; i
++) {
1539 if(((IWineD3DSwapChainImpl
*) swapchain
)->context
[i
]->tid
== tid
) {
1540 return ((IWineD3DSwapChainImpl
*) swapchain
)->context
[i
];
1545 /* Create a new context for the thread */
1546 return IWineD3DSwapChainImpl_CreateContextForThread(swapchain
);
1549 /*****************************************************************************
1552 * Finds a context for the current render target and thread
1555 * target: Render target to find the context for
1556 * tid: Thread to activate the context for
1558 * Returns: The needed context
1560 *****************************************************************************/
1561 static inline WineD3DContext
*FindContext(IWineD3DDeviceImpl
*This
, IWineD3DSurface
*target
, DWORD tid
) {
1562 IWineD3DSwapChain
*swapchain
= NULL
;
1563 BOOL readTexture
= wined3d_settings
.offscreen_rendering_mode
!= ORM_FBO
&& This
->render_offscreen
;
1564 WineD3DContext
*context
= This
->activeContext
;
1565 BOOL oldRenderOffscreen
= This
->render_offscreen
;
1566 const struct StateEntry
*StateTable
= This
->StateTable
;
1567 const struct GlPixelFormatDesc
*old
, *new;
1569 if (SUCCEEDED(IWineD3DSurface_GetContainer(target
, &IID_IWineD3DSwapChain
, (void **)&swapchain
))) {
1570 TRACE("Rendering onscreen\n");
1572 context
= findThreadContextForSwapChain(swapchain
, tid
);
1574 This
->render_offscreen
= FALSE
;
1575 /* The context != This->activeContext will catch a NOP context change. This can occur
1576 * if we are switching back to swapchain rendering in case of FBO or Back Buffer offscreen
1577 * rendering. No context change is needed in that case
1580 if(wined3d_settings
.offscreen_rendering_mode
== ORM_PBUFFER
) {
1581 if(This
->pbufferContext
&& tid
== This
->pbufferContext
->tid
) {
1582 This
->pbufferContext
->tid
= 0;
1585 IWineD3DSwapChain_Release(swapchain
);
1587 if(oldRenderOffscreen
) {
1588 Context_MarkStateDirty(context
, WINED3DTS_PROJECTION
, StateTable
);
1589 Context_MarkStateDirty(context
, STATE_VDECL
, StateTable
);
1590 Context_MarkStateDirty(context
, STATE_VIEWPORT
, StateTable
);
1591 Context_MarkStateDirty(context
, STATE_SCISSORRECT
, StateTable
);
1592 Context_MarkStateDirty(context
, STATE_FRONTFACE
, StateTable
);
1596 TRACE("Rendering offscreen\n");
1597 This
->render_offscreen
= TRUE
;
1599 switch(wined3d_settings
.offscreen_rendering_mode
) {
1601 /* FBOs do not need a different context. Stay with whatever context is active at the moment */
1602 if (This
->activeContext
&& This
->activeContext
->tid
== tid
)
1604 context
= This
->activeContext
;
1606 /* This may happen if the app jumps straight into offscreen rendering
1607 * Start using the context of the primary swapchain. tid == 0 is no problem
1608 * for findThreadContextForSwapChain.
1610 * Can also happen on thread switches - in that case findThreadContextForSwapChain
1611 * is perfect to call.
1613 context
= findThreadContextForSwapChain(This
->swapchains
[0], tid
);
1619 IWineD3DSurfaceImpl
*targetimpl
= (IWineD3DSurfaceImpl
*) target
;
1620 if(This
->pbufferContext
== NULL
||
1621 This
->pbufferWidth
< targetimpl
->currentDesc
.Width
||
1622 This
->pbufferHeight
< targetimpl
->currentDesc
.Height
) {
1623 if(This
->pbufferContext
) {
1624 DestroyContext(This
, This
->pbufferContext
);
1627 /* The display is irrelevant here, the window is 0. But CreateContext needs a valid X connection.
1628 * Create the context on the same server as the primary swapchain. The primary swapchain is exists at this point.
1630 This
->pbufferContext
= CreateContext(This
, targetimpl
,
1631 ((IWineD3DSwapChainImpl
*) This
->swapchains
[0])->context
[0]->win_handle
,
1632 TRUE
/* pbuffer */, &((IWineD3DSwapChainImpl
*)This
->swapchains
[0])->presentParms
);
1633 This
->pbufferWidth
= targetimpl
->currentDesc
.Width
;
1634 This
->pbufferHeight
= targetimpl
->currentDesc
.Height
;
1637 if(This
->pbufferContext
) {
1638 if(This
->pbufferContext
->tid
!= 0 && This
->pbufferContext
->tid
!= tid
) {
1639 FIXME("The PBuffr context is only supported for one thread for now!\n");
1641 This
->pbufferContext
->tid
= tid
;
1642 context
= This
->pbufferContext
;
1645 ERR("Failed to create a buffer context and drawable, falling back to back buffer offscreen rendering\n");
1646 wined3d_settings
.offscreen_rendering_mode
= ORM_BACKBUFFER
;
1650 case ORM_BACKBUFFER
:
1651 /* Stay with the currently active context for back buffer rendering */
1652 if (This
->activeContext
&& This
->activeContext
->tid
== tid
)
1654 context
= This
->activeContext
;
1656 /* This may happen if the app jumps straight into offscreen rendering
1657 * Start using the context of the primary swapchain. tid == 0 is no problem
1658 * for findThreadContextForSwapChain.
1660 * Can also happen on thread switches - in that case findThreadContextForSwapChain
1661 * is perfect to call.
1663 context
= findThreadContextForSwapChain(This
->swapchains
[0], tid
);
1668 if(!oldRenderOffscreen
) {
1669 Context_MarkStateDirty(context
, WINED3DTS_PROJECTION
, StateTable
);
1670 Context_MarkStateDirty(context
, STATE_VDECL
, StateTable
);
1671 Context_MarkStateDirty(context
, STATE_VIEWPORT
, StateTable
);
1672 Context_MarkStateDirty(context
, STATE_SCISSORRECT
, StateTable
);
1673 Context_MarkStateDirty(context
, STATE_FRONTFACE
, StateTable
);
1677 /* To compensate the lack of format switching with some offscreen rendering methods and on onscreen buffers
1678 * the alpha blend state changes with different render target formats. */
1679 old
= ((IWineD3DSurfaceImpl
*)context
->current_rt
)->resource
.format_desc
;
1680 new = ((IWineD3DSurfaceImpl
*)target
)->resource
.format_desc
;
1681 if (old
->format
!= new->format
)
1683 /* Disable blending when the alpha mask has changed and when a format doesn't support blending. */
1684 if ((old
->alpha_mask
&& !new->alpha_mask
) || (!old
->alpha_mask
&& new->alpha_mask
)
1685 || !(new->Flags
& WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING
))
1687 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE
), StateTable
);
1691 /* When switching away from an offscreen render target, and we're not using FBOs,
1692 * we have to read the drawable into the texture. This is done via PreLoad(and
1693 * SFLAG_INDRAWABLE set on the surface). There are some things that need care though.
1694 * PreLoad needs a GL context, and FindContext is called before the context is activated.
1695 * It also has to be called with the old rendertarget active, otherwise a wrong drawable
1696 * is read. This leads to these possible situations:
1698 * 0) lastActiveRenderTarget == target && oldTid == newTid:
1699 * Nothing to do, we don't even reach this code in this case...
1701 * 1) lastActiveRenderTarget != target && oldTid == newTid:
1702 * The currently active context is OK for readback. Call PreLoad, and it
1705 * 2) lastActiveRenderTarget == target && oldTid != newTid:
1706 * Nothing to do - the drawable is unchanged
1708 * 3) lastActiveRenderTarget != target && oldTid != newTid:
1709 * This is tricky. We have to get a context with the old drawable from somewhere
1710 * before we can switch to the new context. In this case, PreLoad calls
1711 * ActivateContext(lastActiveRenderTarget) from the new(current) thread. This
1712 * is case (2) then. The old drawable is activated for the new thread, and the
1713 * readback can be done. The recursed ActivateContext does *not* call PreLoad again.
1714 * After that, the outer ActivateContext(which calls PreLoad) can activate the new
1715 * target for the new thread
1717 if (readTexture
&& context
->current_rt
!= target
)
1719 BOOL oldInDraw
= This
->isInDraw
;
1721 /* PreLoad requires a context to load the texture, thus it will call ActivateContext.
1722 * Set the isInDraw to true to signal PreLoad that it has a context. Will be tricky
1723 * when using offscreen rendering with multithreading
1725 This
->isInDraw
= TRUE
;
1727 /* Do that before switching the context:
1728 * Read the back buffer of the old drawable into the destination texture
1730 if (((IWineD3DSurfaceImpl
*)context
->current_rt
)->texture_name_srgb
)
1732 surface_internal_preload(context
->current_rt
, SRGB_BOTH
);
1734 surface_internal_preload(context
->current_rt
, SRGB_RGB
);
1737 /* Assume that the drawable will be modified by some other things now */
1738 IWineD3DSurface_ModifyLocation(context
->current_rt
, SFLAG_INDRAWABLE
, FALSE
);
1740 This
->isInDraw
= oldInDraw
;
1746 /* Context activation is done by the caller. */
1747 static void apply_draw_buffer(IWineD3DDeviceImpl
*This
, IWineD3DSurface
*target
, BOOL blit
)
1749 const struct wined3d_gl_info
*gl_info
= This
->activeContext
->gl_info
;
1750 IWineD3DSwapChain
*swapchain
;
1752 if (SUCCEEDED(IWineD3DSurface_GetContainer(target
, &IID_IWineD3DSwapChain
, (void **)&swapchain
)))
1754 IWineD3DSwapChain_Release((IUnknown
*)swapchain
);
1756 glDrawBuffer(surface_get_gl_buffer(target
, swapchain
));
1757 checkGLcall("glDrawBuffers()");
1763 if (wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
)
1767 if (GL_SUPPORT(ARB_DRAW_BUFFERS
))
1769 GL_EXTCALL(glDrawBuffersARB(GL_LIMITS(buffers
), This
->draw_buffers
));
1770 checkGLcall("glDrawBuffers()");
1774 glDrawBuffer(This
->draw_buffers
[0]);
1775 checkGLcall("glDrawBuffer()");
1778 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT
);
1779 checkGLcall("glDrawBuffer()");
1784 glDrawBuffer(This
->offscreenBuffer
);
1785 checkGLcall("glDrawBuffer()");
1791 /*****************************************************************************
1794 * Finds a rendering context and drawable matching the device and render
1795 * target for the current thread, activates them and puts them into the
1799 * This: Device to activate the context for
1800 * target: Requested render target
1801 * usage: Prepares the context for blitting, drawing or other actions
1803 *****************************************************************************/
1804 void ActivateContext(IWineD3DDeviceImpl
*This
, IWineD3DSurface
*target
, ContextUsage usage
) {
1805 DWORD tid
= GetCurrentThreadId();
1806 DWORD i
, dirtyState
, idx
;
1808 WineD3DContext
*context
;
1809 const struct StateEntry
*StateTable
= This
->StateTable
;
1810 const struct wined3d_gl_info
*gl_info
;
1812 TRACE("(%p): Selecting context for render target %p, thread %d\n", This
, target
, tid
);
1814 if (!target
) target
= This
->activeContext
->current_rt
;
1816 if (This
->activeContext
->current_rt
!= target
|| This
->activeContext
->tid
!= tid
)
1818 context
= FindContext(This
, target
, tid
);
1819 context
->draw_buffer_dirty
= TRUE
;
1820 context
->current_rt
= target
;
1822 /* Stick to the old context */
1823 context
= This
->activeContext
;
1826 gl_info
= context
->gl_info
;
1828 /* Activate the opengl context */
1829 if(last_device
!= This
|| context
!= This
->activeContext
) {
1830 if (!context_set_current(context
)) ERR("Failed to activate the new context.\n");
1831 else This
->frag_pipe
->enable_extension((IWineD3DDevice
*)This
, !context
->last_was_blit
);
1833 if(This
->activeContext
->vshader_const_dirty
) {
1834 memset(This
->activeContext
->vshader_const_dirty
, 1,
1835 sizeof(*This
->activeContext
->vshader_const_dirty
) * GL_LIMITS(vshader_constantsF
));
1836 This
->highest_dirty_vs_const
= GL_LIMITS(vshader_constantsF
);
1838 if(This
->activeContext
->pshader_const_dirty
) {
1839 memset(This
->activeContext
->pshader_const_dirty
, 1,
1840 sizeof(*This
->activeContext
->pshader_const_dirty
) * GL_LIMITS(pshader_constantsF
));
1841 This
->highest_dirty_ps_const
= GL_LIMITS(pshader_constantsF
);
1843 This
->activeContext
= context
;
1844 context_set_last_device(This
);
1848 case CTXUSAGE_CLEAR
:
1849 case CTXUSAGE_DRAWPRIM
:
1850 if (wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
) {
1852 context_apply_fbo_state(context
);
1855 if (context
->draw_buffer_dirty
) {
1856 apply_draw_buffer(This
, target
, FALSE
);
1857 context
->draw_buffer_dirty
= FALSE
;
1862 if (wined3d_settings
.offscreen_rendering_mode
== ORM_FBO
) {
1863 if (This
->render_offscreen
) {
1864 FIXME("Activating for CTXUSAGE_BLIT for an offscreen target with ORM_FBO. This should be avoided.\n");
1866 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, &context
->dst_fbo
);
1867 context_attach_surface_fbo(context
, GL_FRAMEBUFFER_EXT
, 0, target
);
1868 context_attach_depth_stencil_fbo(context
, GL_FRAMEBUFFER_EXT
, NULL
, FALSE
);
1872 context_bind_fbo(context
, GL_FRAMEBUFFER_EXT
, NULL
);
1875 context
->draw_buffer_dirty
= TRUE
;
1877 if (context
->draw_buffer_dirty
) {
1878 apply_draw_buffer(This
, target
, TRUE
);
1879 if (wined3d_settings
.offscreen_rendering_mode
!= ORM_FBO
) {
1880 context
->draw_buffer_dirty
= FALSE
;
1890 case CTXUSAGE_RESOURCELOAD
:
1891 /* This does not require any special states to be set up */
1894 case CTXUSAGE_CLEAR
:
1895 if(context
->last_was_blit
) {
1896 This
->frag_pipe
->enable_extension((IWineD3DDevice
*) This
, TRUE
);
1899 /* Blending and clearing should be orthogonal, but tests on the nvidia driver show that disabling
1900 * blending when clearing improves the clearing performance incredibly.
1903 glDisable(GL_BLEND
);
1905 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE
), StateTable
);
1908 glEnable(GL_SCISSOR_TEST
);
1909 checkGLcall("glEnable GL_SCISSOR_TEST");
1911 context
->last_was_blit
= FALSE
;
1912 Context_MarkStateDirty(context
, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE
), StateTable
);
1913 Context_MarkStateDirty(context
, STATE_SCISSORRECT
, StateTable
);
1916 case CTXUSAGE_DRAWPRIM
:
1917 /* This needs all dirty states applied */
1918 if(context
->last_was_blit
) {
1919 This
->frag_pipe
->enable_extension((IWineD3DDevice
*) This
, TRUE
);
1922 IWineD3DDeviceImpl_FindTexUnitMap(This
);
1925 for(i
=0; i
< context
->numDirtyEntries
; i
++) {
1926 dirtyState
= context
->dirtyArray
[i
];
1927 idx
= dirtyState
>> 5;
1928 shift
= dirtyState
& 0x1f;
1929 context
->isStateDirty
[idx
] &= ~(1 << shift
);
1930 StateTable
[dirtyState
].apply(dirtyState
, This
->stateBlock
, context
);
1933 context
->numDirtyEntries
= 0; /* This makes the whole list clean */
1934 context
->last_was_blit
= FALSE
;
1938 SetupForBlit(This
, context
,
1939 ((IWineD3DSurfaceImpl
*)target
)->currentDesc
.Width
,
1940 ((IWineD3DSurfaceImpl
*)target
)->currentDesc
.Height
);
1944 FIXME("Unexpected context usage requested\n");
1948 WineD3DContext
*getActiveContext(void) {
1949 return last_device
->activeContext
;