* Context and render target management in wined3d
*
* Copyright 2007-2008 Stefan Dösinger for CodeWeavers
+ * Copyright 2009 Henri Verbeet for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
-#define GLINFO_LOCATION This->adapter->gl_info
+#define GLINFO_LOCATION (*gl_info)
-/* The last used device.
- *
- * If the application creates multiple devices and switches between them, ActivateContext has to
- * change the opengl context. This flag allows to keep track which device is active
- */
-static IWineD3DDeviceImpl *last_device;
+static DWORD wined3d_context_tls_idx;
/* FBO helper functions */
-void context_bind_fbo(IWineD3DDevice *iface, GLenum target, GLuint *fbo)
+/* GL locking is done by the caller */
+void context_bind_fbo(struct wined3d_context *context, GLenum target, GLuint *fbo)
{
- const IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+ GLuint f;
+
+ if (!fbo)
+ {
+ f = 0;
+ }
+ else
+ {
+ if (!*fbo)
+ {
+ GL_EXTCALL(glGenFramebuffersEXT(1, fbo));
+ checkGLcall("glGenFramebuffersEXT()");
+ TRACE("Created FBO %u.\n", *fbo);
+ }
+ f = *fbo;
+ }
- if (!*fbo)
+ switch (target)
{
- GL_EXTCALL(glGenFramebuffersEXT(1, fbo));
- checkGLcall("glGenFramebuffersEXT()");
- TRACE("Created FBO %d\n", *fbo);
+ case GL_READ_FRAMEBUFFER_EXT:
+ if (context->fbo_read_binding == f) return;
+ context->fbo_read_binding = f;
+ break;
+
+ case GL_DRAW_FRAMEBUFFER_EXT:
+ if (context->fbo_draw_binding == f) return;
+ context->fbo_draw_binding = f;
+ break;
+
+ case GL_FRAMEBUFFER_EXT:
+ if (context->fbo_read_binding == f
+ && context->fbo_draw_binding == f) return;
+ context->fbo_read_binding = f;
+ context->fbo_draw_binding = f;
+ break;
+
+ default:
+ FIXME("Unhandled target %#x.\n", target);
+ break;
}
- GL_EXTCALL(glBindFramebufferEXT(target, *fbo));
+ GL_EXTCALL(glBindFramebufferEXT(target, f));
checkGLcall("glBindFramebuffer()");
}
-static void context_destroy_fbo(IWineD3DDeviceImpl *This, const GLuint *fbo)
+/* GL locking is done by the caller */
+static void context_clean_fbo_attachments(const struct wined3d_gl_info *gl_info)
{
unsigned int i;
- GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, *fbo));
- checkGLcall("glBindFramebuffer()");
for (i = 0; i < GL_LIMITS(buffers); ++i)
{
GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + i, GL_TEXTURE_2D, 0, 0));
}
GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0));
checkGLcall("glFramebufferTexture2D()");
- GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
- checkGLcall("glBindFramebuffer()");
+
+ GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0));
+ checkGLcall("glFramebufferTexture2D()");
+}
+
+/* GL locking is done by the caller */
+static void context_destroy_fbo(struct wined3d_context *context, GLuint *fbo)
+{
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, fbo);
+ context_clean_fbo_attachments(gl_info);
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
+
GL_EXTCALL(glDeleteFramebuffersEXT(1, fbo));
checkGLcall("glDeleteFramebuffers()");
}
-static void context_apply_attachment_filter_states(IWineD3DDevice *iface, IWineD3DSurface *surface, BOOL force_preload)
+/* GL locking is done by the caller */
+static void context_apply_attachment_filter_states(IWineD3DSurface *surface, BOOL force_preload)
{
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
const IWineD3DSurfaceImpl *surface_impl = (IWineD3DSurfaceImpl *)surface;
+ IWineD3DDeviceImpl *device = surface_impl->resource.wineD3DDevice;
IWineD3DBaseTextureImpl *texture_impl;
BOOL update_minfilter = FALSE;
BOOL update_magfilter = FALSE;
if (texture_impl->baseTexture.bindCount)
{
WARN("Render targets should not be bound to a sampler\n");
- IWineD3DDeviceImpl_MarkStateDirty(This, STATE_SAMPLER(texture_impl->baseTexture.sampler));
+ IWineD3DDeviceImpl_MarkStateDirty(device, STATE_SAMPLER(texture_impl->baseTexture.sampler));
}
IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture_impl);
GLenum target, bind_target;
GLint old_binding;
- target = surface_impl->glDescription.target;
+ target = surface_impl->texture_target;
if (target == GL_TEXTURE_2D)
{
bind_target = GL_TEXTURE_2D;
surface_internal_preload(surface, SRGB_RGB);
- glBindTexture(bind_target, surface_impl->glDescription.textureName);
+ glBindTexture(bind_target, surface_impl->texture_name);
if (update_minfilter) glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (update_magfilter) glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(bind_target, old_binding);
checkGLcall("apply_attachment_filter_states()");
}
-/* TODO: Handle stencil attachments */
-void context_attach_depth_stencil_fbo(IWineD3DDeviceImpl *This, GLenum fbo_target, IWineD3DSurface *depth_stencil, BOOL use_render_buffer)
+/* GL locking is done by the caller */
+void context_attach_depth_stencil_fbo(struct wined3d_context *context,
+ GLenum fbo_target, IWineD3DSurface *depth_stencil, BOOL use_render_buffer)
{
IWineD3DSurfaceImpl *depth_stencil_impl = (IWineD3DSurfaceImpl *)depth_stencil;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
TRACE("Attach depth stencil %p\n", depth_stencil);
if (depth_stencil)
{
+ DWORD format_flags = depth_stencil_impl->resource.format_desc->Flags;
+
if (use_render_buffer && depth_stencil_impl->current_renderbuffer)
{
- GL_EXTCALL(glFramebufferRenderbufferEXT(fbo_target, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_impl->current_renderbuffer->id));
- checkGLcall("glFramebufferRenderbufferEXT()");
- } else {
- context_apply_attachment_filter_states((IWineD3DDevice *)This, depth_stencil, TRUE);
+ if (format_flags & WINED3DFMT_FLAG_DEPTH)
+ {
+ GL_EXTCALL(glFramebufferRenderbufferEXT(fbo_target, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_stencil_impl->current_renderbuffer->id));
+ checkGLcall("glFramebufferRenderbufferEXT()");
+ }
+
+ if (format_flags & WINED3DFMT_FLAG_STENCIL)
+ {
+ GL_EXTCALL(glFramebufferRenderbufferEXT(fbo_target, GL_STENCIL_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, depth_stencil_impl->current_renderbuffer->id));
+ checkGLcall("glFramebufferRenderbufferEXT()");
+ }
+ }
+ else
+ {
+ context_apply_attachment_filter_states(depth_stencil, TRUE);
+
+ if (format_flags & WINED3DFMT_FLAG_DEPTH)
+ {
+ GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_DEPTH_ATTACHMENT_EXT,
+ depth_stencil_impl->texture_target, depth_stencil_impl->texture_name,
+ depth_stencil_impl->texture_level));
+ checkGLcall("glFramebufferTexture2DEXT()");
+ }
+
+ if (format_flags & WINED3DFMT_FLAG_STENCIL)
+ {
+ GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_STENCIL_ATTACHMENT_EXT,
+ depth_stencil_impl->texture_target, depth_stencil_impl->texture_name,
+ depth_stencil_impl->texture_level));
+ checkGLcall("glFramebufferTexture2DEXT()");
+ }
+ }
- GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_DEPTH_ATTACHMENT_EXT, depth_stencil_impl->glDescription.target,
- depth_stencil_impl->glDescription.textureName, depth_stencil_impl->glDescription.level));
+ if (!(format_flags & WINED3DFMT_FLAG_DEPTH))
+ {
+ GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0));
checkGLcall("glFramebufferTexture2DEXT()");
}
- } else {
+
+ if (!(format_flags & WINED3DFMT_FLAG_STENCIL))
+ {
+ GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0));
+ checkGLcall("glFramebufferTexture2DEXT()");
+ }
+ }
+ else
+ {
GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0));
checkGLcall("glFramebufferTexture2DEXT()");
+
+ GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0));
+ checkGLcall("glFramebufferTexture2DEXT()");
}
}
-void context_attach_surface_fbo(IWineD3DDeviceImpl *This, GLenum fbo_target, DWORD idx, IWineD3DSurface *surface)
+/* GL locking is done by the caller */
+void context_attach_surface_fbo(const struct wined3d_context *context,
+ GLenum fbo_target, DWORD idx, IWineD3DSurface *surface)
{
const IWineD3DSurfaceImpl *surface_impl = (IWineD3DSurfaceImpl *)surface;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
TRACE("Attach surface %p to %u\n", surface, idx);
if (surface)
{
- context_apply_attachment_filter_states((IWineD3DDevice *)This, surface, TRUE);
+ context_apply_attachment_filter_states(surface, TRUE);
- GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_COLOR_ATTACHMENT0_EXT + idx, surface_impl->glDescription.target,
- surface_impl->glDescription.textureName, surface_impl->glDescription.level));
+ GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_COLOR_ATTACHMENT0_EXT + idx, surface_impl->texture_target,
+ surface_impl->texture_name, surface_impl->texture_level));
checkGLcall("glFramebufferTexture2DEXT()");
} else {
GL_EXTCALL(glFramebufferTexture2DEXT(fbo_target, GL_COLOR_ATTACHMENT0_EXT + idx, GL_TEXTURE_2D, 0, 0));
}
}
-static void context_check_fbo_status(IWineD3DDevice *iface)
+/* GL locking is done by the caller */
+static void context_check_fbo_status(struct wined3d_context *context)
{
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
GLenum status;
status = GL_EXTCALL(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
/* Dump the FBO attachments */
for (i = 0; i < GL_LIMITS(buffers); ++i)
{
- attachment = (IWineD3DSurfaceImpl *)This->activeContext->current_fbo->render_targets[i];
+ attachment = (IWineD3DSurfaceImpl *)context->current_fbo->render_targets[i];
if (attachment)
{
FIXME("\tColor attachment %d: (%p) %s %ux%u\n",
attachment->pow2Width, attachment->pow2Height);
}
}
- attachment = (IWineD3DSurfaceImpl *)This->activeContext->current_fbo->depth_stencil;
+ attachment = (IWineD3DSurfaceImpl *)context->current_fbo->depth_stencil;
if (attachment)
{
FIXME("\tDepth attachment: (%p) %s %ux%u\n",
}
}
-static struct fbo_entry *context_create_fbo_entry(IWineD3DDevice *iface)
+static struct fbo_entry *context_create_fbo_entry(struct wined3d_context *context)
{
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+ IWineD3DDeviceImpl *device = ((IWineD3DSurfaceImpl *)context->surface)->resource.wineD3DDevice;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
struct fbo_entry *entry;
entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
entry->render_targets = HeapAlloc(GetProcessHeap(), 0, GL_LIMITS(buffers) * sizeof(*entry->render_targets));
- memcpy(entry->render_targets, This->render_targets, GL_LIMITS(buffers) * sizeof(*entry->render_targets));
- entry->depth_stencil = This->stencilBufferTarget;
+ memcpy(entry->render_targets, device->render_targets, GL_LIMITS(buffers) * sizeof(*entry->render_targets));
+ entry->depth_stencil = device->stencilBufferTarget;
entry->attached = FALSE;
entry->id = 0;
return entry;
}
-static void context_destroy_fbo_entry(IWineD3DDeviceImpl *This, struct fbo_entry *entry)
+/* GL locking is done by the caller */
+static void context_reuse_fbo_entry(struct wined3d_context *context, struct fbo_entry *entry)
+{
+ IWineD3DDeviceImpl *device = ((IWineD3DSurfaceImpl *)context->surface)->resource.wineD3DDevice;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &entry->id);
+ context_clean_fbo_attachments(gl_info);
+
+ memcpy(entry->render_targets, device->render_targets, GL_LIMITS(buffers) * sizeof(*entry->render_targets));
+ entry->depth_stencil = device->stencilBufferTarget;
+ entry->attached = FALSE;
+}
+
+/* GL locking is done by the caller */
+static void context_destroy_fbo_entry(struct wined3d_context *context, struct fbo_entry *entry)
{
if (entry->id)
{
TRACE("Destroy FBO %d\n", entry->id);
- context_destroy_fbo(This, &entry->id);
+ context_destroy_fbo(context, &entry->id);
}
+ --context->fbo_entry_count;
list_remove(&entry->entry);
HeapFree(GetProcessHeap(), 0, entry->render_targets);
HeapFree(GetProcessHeap(), 0, entry);
}
-static struct fbo_entry *context_find_fbo_entry(IWineD3DDevice *iface, WineD3DContext *context)
+/* GL locking is done by the caller */
+static struct fbo_entry *context_find_fbo_entry(struct wined3d_context *context)
{
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+ IWineD3DDeviceImpl *device = ((IWineD3DSurfaceImpl *)context->surface)->resource.wineD3DDevice;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
struct fbo_entry *entry;
LIST_FOR_EACH_ENTRY(entry, &context->fbo_list, struct fbo_entry, entry)
{
- if (!memcmp(entry->render_targets, This->render_targets, GL_LIMITS(buffers) * sizeof(*entry->render_targets))
- && entry->depth_stencil == This->stencilBufferTarget)
+ if (!memcmp(entry->render_targets, device->render_targets, GL_LIMITS(buffers) * sizeof(*entry->render_targets))
+ && entry->depth_stencil == device->stencilBufferTarget)
{
+ list_remove(&entry->entry);
+ list_add_head(&context->fbo_list, &entry->entry);
return entry;
}
}
- entry = context_create_fbo_entry(iface);
- list_add_head(&context->fbo_list, &entry->entry);
+ if (context->fbo_entry_count < WINED3D_MAX_FBO_ENTRIES)
+ {
+ entry = context_create_fbo_entry(context);
+ list_add_head(&context->fbo_list, &entry->entry);
+ ++context->fbo_entry_count;
+ }
+ else
+ {
+ entry = LIST_ENTRY(list_tail(&context->fbo_list), struct fbo_entry, entry);
+ context_reuse_fbo_entry(context, entry);
+ list_remove(&entry->entry);
+ list_add_head(&context->fbo_list, &entry->entry);
+ }
+
return entry;
}
-static void context_apply_fbo_entry(IWineD3DDevice *iface, struct fbo_entry *entry)
+/* GL locking is done by the caller */
+static void context_apply_fbo_entry(struct wined3d_context *context, struct fbo_entry *entry)
{
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+ IWineD3DDeviceImpl *device = ((IWineD3DSurfaceImpl *)context->surface)->resource.wineD3DDevice;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
unsigned int i;
- context_bind_fbo(iface, GL_FRAMEBUFFER_EXT, &entry->id);
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &entry->id);
if (!entry->attached)
{
/* Apply render targets */
for (i = 0; i < GL_LIMITS(buffers); ++i)
{
- IWineD3DSurface *render_target = This->render_targets[i];
- context_attach_surface_fbo(This, GL_FRAMEBUFFER_EXT, i, render_target);
+ IWineD3DSurface *render_target = device->render_targets[i];
+ context_attach_surface_fbo(context, GL_FRAMEBUFFER_EXT, i, render_target);
}
/* Apply depth targets */
- if (This->stencilBufferTarget) {
- unsigned int w = ((IWineD3DSurfaceImpl *)This->render_targets[0])->pow2Width;
- unsigned int h = ((IWineD3DSurfaceImpl *)This->render_targets[0])->pow2Height;
+ if (device->stencilBufferTarget)
+ {
+ unsigned int w = ((IWineD3DSurfaceImpl *)device->render_targets[0])->pow2Width;
+ unsigned int h = ((IWineD3DSurfaceImpl *)device->render_targets[0])->pow2Height;
- surface_set_compatible_renderbuffer(This->stencilBufferTarget, w, h);
+ surface_set_compatible_renderbuffer(device->stencilBufferTarget, w, h);
}
- context_attach_depth_stencil_fbo(This, GL_FRAMEBUFFER_EXT, This->stencilBufferTarget, TRUE);
+ context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER_EXT, device->stencilBufferTarget, TRUE);
entry->attached = TRUE;
} else {
for (i = 0; i < GL_LIMITS(buffers); ++i)
{
- if (This->render_targets[i])
- context_apply_attachment_filter_states(iface, This->render_targets[i], FALSE);
+ if (device->render_targets[i])
+ context_apply_attachment_filter_states(device->render_targets[i], FALSE);
}
- if (This->stencilBufferTarget)
- context_apply_attachment_filter_states(iface, This->stencilBufferTarget, FALSE);
+ if (device->stencilBufferTarget)
+ context_apply_attachment_filter_states(device->stencilBufferTarget, FALSE);
}
for (i = 0; i < GL_LIMITS(buffers); ++i)
{
- if (This->render_targets[i])
- This->draw_buffers[i] = GL_COLOR_ATTACHMENT0_EXT + i;
+ if (device->render_targets[i])
+ device->draw_buffers[i] = GL_COLOR_ATTACHMENT0_EXT + i;
else
- This->draw_buffers[i] = GL_NONE;
+ device->draw_buffers[i] = GL_NONE;
}
}
-static void context_apply_fbo_state(IWineD3DDevice *iface)
+/* GL locking is done by the caller */
+static void context_apply_fbo_state(struct wined3d_context *context)
{
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
- WineD3DContext *context = This->activeContext;
-
- if (This->render_offscreen)
+ if (context->render_offscreen)
{
- context->current_fbo = context_find_fbo_entry(iface, context);
- context_apply_fbo_entry(iface, context->current_fbo);
+ context->current_fbo = context_find_fbo_entry(context);
+ context_apply_fbo_entry(context, context->current_fbo);
} else {
context->current_fbo = NULL;
- GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
}
- context_check_fbo_status(iface);
+ context_check_fbo_status(context);
+}
+
+/* Context activation is done by the caller. */
+void context_alloc_occlusion_query(struct wined3d_context *context, struct wined3d_occlusion_query *query)
+{
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+
+ if (context->free_occlusion_query_count)
+ {
+ query->id = context->free_occlusion_queries[--context->free_occlusion_query_count];
+ }
+ else
+ {
+ if (GL_SUPPORT(ARB_OCCLUSION_QUERY))
+ {
+ ENTER_GL();
+ GL_EXTCALL(glGenQueriesARB(1, &query->id));
+ checkGLcall("glGenQueriesARB");
+ LEAVE_GL();
+
+ TRACE("Allocated occlusion query %u in context %p.\n", query->id, context);
+ }
+ else
+ {
+ WARN("Occlusion queries not supported, not allocating query id.\n");
+ query->id = 0;
+ }
+ }
+
+ query->context = context;
+ list_add_head(&context->occlusion_queries, &query->entry);
+}
+
+void context_free_occlusion_query(struct wined3d_occlusion_query *query)
+{
+ struct wined3d_context *context = query->context;
+
+ list_remove(&query->entry);
+ query->context = NULL;
+
+ if (context->free_occlusion_query_count >= context->free_occlusion_query_size - 1)
+ {
+ UINT new_size = context->free_occlusion_query_size << 1;
+ GLuint *new_data = HeapReAlloc(GetProcessHeap(), 0, context->free_occlusion_queries,
+ new_size * sizeof(*context->free_occlusion_queries));
+
+ if (!new_data)
+ {
+ ERR("Failed to grow free list, leaking query %u in context %p.\n", query->id, context);
+ return;
+ }
+
+ context->free_occlusion_query_size = new_size;
+ context->free_occlusion_queries = new_data;
+ }
+
+ context->free_occlusion_queries[context->free_occlusion_query_count++] = query->id;
+}
+
+/* Context activation is done by the caller. */
+void context_alloc_event_query(struct wined3d_context *context, struct wined3d_event_query *query)
+{
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+
+ if (context->free_event_query_count)
+ {
+ query->id = context->free_event_queries[--context->free_event_query_count];
+ }
+ else
+ {
+ if (GL_SUPPORT(APPLE_FENCE))
+ {
+ ENTER_GL();
+ GL_EXTCALL(glGenFencesAPPLE(1, &query->id));
+ checkGLcall("glGenFencesAPPLE");
+ LEAVE_GL();
+
+ TRACE("Allocated event query %u in context %p.\n", query->id, context);
+ }
+ else if(GL_SUPPORT(NV_FENCE))
+ {
+ ENTER_GL();
+ GL_EXTCALL(glGenFencesNV(1, &query->id));
+ checkGLcall("glGenFencesNV");
+ LEAVE_GL();
+
+ TRACE("Allocated event query %u in context %p.\n", query->id, context);
+ }
+ else
+ {
+ WARN("Event queries not supported, not allocating query id.\n");
+ query->id = 0;
+ }
+ }
+
+ query->context = context;
+ list_add_head(&context->event_queries, &query->entry);
+}
+
+void context_free_event_query(struct wined3d_event_query *query)
+{
+ struct wined3d_context *context = query->context;
+
+ list_remove(&query->entry);
+ query->context = NULL;
+
+ if (context->free_event_query_count >= context->free_event_query_size - 1)
+ {
+ UINT new_size = context->free_event_query_size << 1;
+ GLuint *new_data = HeapReAlloc(GetProcessHeap(), 0, context->free_event_queries,
+ new_size * sizeof(*context->free_event_queries));
+
+ if (!new_data)
+ {
+ ERR("Failed to grow free list, leaking query %u in context %p.\n", query->id, context);
+ return;
+ }
+
+ context->free_event_query_size = new_size;
+ context->free_event_queries = new_data;
+ }
+
+ context->free_event_queries[context->free_event_query_count++] = query->id;
}
void context_resource_released(IWineD3DDevice *iface, IWineD3DResource *resource, WINED3DRESOURCETYPE type)
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
UINT i;
+ if (!This->d3d_initialized) return;
+
switch(type)
{
case WINED3DRTYPE_SURFACE:
{
+ ActivateContext(This, NULL, CTXUSAGE_RESOURCELOAD);
+
for (i = 0; i < This->numContexts; ++i)
{
+ struct wined3d_context *context = This->contexts[i];
+ const struct wined3d_gl_info *gl_info = context->gl_info;
struct fbo_entry *entry, *entry2;
- LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->contexts[i]->fbo_list, struct fbo_entry, entry)
+ if (context->current_rt == (IWineD3DSurface *)resource) context->current_rt = NULL;
+
+ ENTER_GL();
+
+ LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context->fbo_list, struct fbo_entry, entry)
{
BOOL destroyed = FALSE;
UINT j;
{
if (entry->render_targets[j] == (IWineD3DSurface *)resource)
{
- context_destroy_fbo_entry(This, entry);
+ context_destroy_fbo_entry(context, entry);
destroyed = TRUE;
}
}
if (!destroyed && entry->depth_stencil == (IWineD3DSurface *)resource)
- context_destroy_fbo_entry(This, entry);
+ context_destroy_fbo_entry(context, entry);
}
+
+ LEAVE_GL();
}
break;
}
}
+static void context_destroy_gl_resources(struct wined3d_context *context)
+{
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+ struct wined3d_occlusion_query *occlusion_query;
+ struct wined3d_event_query *event_query;
+ struct fbo_entry *entry, *entry2;
+ BOOL has_glctx;
+
+ has_glctx = pwglMakeCurrent(context->hdc, context->glCtx);
+ if (!has_glctx) WARN("Failed to activate context. Window already destroyed?\n");
+
+ ENTER_GL();
+
+ LIST_FOR_EACH_ENTRY(occlusion_query, &context->occlusion_queries, struct wined3d_occlusion_query, entry)
+ {
+ if (has_glctx && GL_SUPPORT(ARB_OCCLUSION_QUERY)) GL_EXTCALL(glDeleteQueriesARB(1, &occlusion_query->id));
+ occlusion_query->context = NULL;
+ }
+
+ LIST_FOR_EACH_ENTRY(event_query, &context->event_queries, struct wined3d_event_query, entry)
+ {
+ if (has_glctx)
+ {
+ if (GL_SUPPORT(APPLE_FENCE)) GL_EXTCALL(glDeleteFencesAPPLE(1, &event_query->id));
+ else if (GL_SUPPORT(NV_FENCE)) GL_EXTCALL(glDeleteFencesNV(1, &event_query->id));
+ }
+ event_query->context = NULL;
+ }
+
+ LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context->fbo_list, struct fbo_entry, entry) {
+ if (!has_glctx) entry->id = 0;
+ context_destroy_fbo_entry(context, entry);
+ }
+ if (has_glctx)
+ {
+ if (context->src_fbo)
+ {
+ TRACE("Destroy src FBO %d\n", context->src_fbo);
+ context_destroy_fbo(context, &context->src_fbo);
+ }
+ if (context->dst_fbo)
+ {
+ TRACE("Destroy dst FBO %d\n", context->dst_fbo);
+ context_destroy_fbo(context, &context->dst_fbo);
+ }
+ if (context->dummy_arbfp_prog)
+ {
+ GL_EXTCALL(glDeleteProgramsARB(1, &context->dummy_arbfp_prog));
+ }
+
+ if (GL_SUPPORT(ARB_OCCLUSION_QUERY))
+ GL_EXTCALL(glDeleteQueriesARB(context->free_occlusion_query_count, context->free_occlusion_queries));
+
+ if (GL_SUPPORT(APPLE_FENCE))
+ GL_EXTCALL(glDeleteFencesAPPLE(context->free_event_query_count, context->free_event_queries));
+ else if (GL_SUPPORT(NV_FENCE))
+ GL_EXTCALL(glDeleteFencesNV(context->free_event_query_count, context->free_event_queries));
+
+ checkGLcall("context cleanup");
+ }
+
+ LEAVE_GL();
+
+ HeapFree(GetProcessHeap(), 0, context->free_occlusion_queries);
+ HeapFree(GetProcessHeap(), 0, context->free_event_queries);
+
+ if (!pwglMakeCurrent(NULL, NULL))
+ {
+ ERR("Failed to disable GL context.\n");
+ }
+
+ if (context->isPBuffer)
+ {
+ GL_EXTCALL(wglReleasePbufferDCARB(context->pbuffer, context->hdc));
+ GL_EXTCALL(wglDestroyPbufferARB(context->pbuffer));
+ }
+ else
+ {
+ ReleaseDC(context->win_handle, context->hdc);
+ }
+
+ if (!pwglDeleteContext(context->glCtx))
+ {
+ DWORD err = GetLastError();
+ ERR("wglDeleteContext(%p) failed, last error %#x.\n", context->glCtx, err);
+ }
+}
+
+DWORD context_get_tls_idx(void)
+{
+ return wined3d_context_tls_idx;
+}
+
+void context_set_tls_idx(DWORD idx)
+{
+ wined3d_context_tls_idx = idx;
+}
+
+struct wined3d_context *context_get_current(void)
+{
+ return TlsGetValue(wined3d_context_tls_idx);
+}
+
+BOOL context_set_current(struct wined3d_context *ctx)
+{
+ struct wined3d_context *old = context_get_current();
+
+ if (old == ctx)
+ {
+ TRACE("Already using D3D context %p.\n", ctx);
+ return TRUE;
+ }
+
+ if (old)
+ {
+ if (old->destroyed)
+ {
+ TRACE("Switching away from destroyed context %p.\n", old);
+ context_destroy_gl_resources(old);
+ HeapFree(GetProcessHeap(), 0, old);
+ }
+ else
+ {
+ old->current = 0;
+ }
+ }
+
+ if (ctx)
+ {
+ TRACE("Switching to D3D context %p, GL context %p, device context %p.\n", ctx, ctx->glCtx, ctx->hdc);
+ if (!pwglMakeCurrent(ctx->hdc, ctx->glCtx))
+ {
+ ERR("Failed to make GL context %p current on device context %p.\n", ctx->glCtx, ctx->hdc);
+ return FALSE;
+ }
+ ctx->current = 1;
+ }
+ else
+ {
+ TRACE("Clearing current D3D context.\n");
+ if (!pwglMakeCurrent(NULL, NULL))
+ {
+ ERR("Failed to clear current GL context.\n");
+ return FALSE;
+ }
+ }
+
+ return TlsSetValue(wined3d_context_tls_idx, ctx);
+}
+
/*****************************************************************************
* Context_MarkStateDirty
*
* StateTable: Pointer to the state table in use(for state grouping)
*
*****************************************************************************/
-static void Context_MarkStateDirty(WineD3DContext *context, DWORD state, const struct StateEntry *StateTable) {
+static void Context_MarkStateDirty(struct wined3d_context *context, DWORD state, const struct StateEntry *StateTable)
+{
DWORD rep = StateTable[state].representative;
DWORD idx;
BYTE shift;
- if(!rep || isStateDirty(context, rep)) return;
+ if (isStateDirty(context, rep)) return;
context->dirtyArray[context->numDirtyEntries++] = rep;
idx = rep >> 5;
* pbuffer: optional pbuffer used with this context
*
*****************************************************************************/
-static WineD3DContext *AddContextToArray(IWineD3DDeviceImpl *This, HWND win_handle, HDC hdc, HGLRC glCtx, HPBUFFERARB pbuffer) {
- WineD3DContext **oldArray = This->contexts;
+static struct wined3d_context *AddContextToArray(IWineD3DDeviceImpl *This,
+ HWND win_handle, HDC hdc, HGLRC glCtx, HPBUFFERARB pbuffer)
+{
+ struct wined3d_context **oldArray = This->contexts;
DWORD state;
This->contexts = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->contexts) * (This->numContexts + 1));
memcpy(This->contexts, oldArray, sizeof(*This->contexts) * This->numContexts);
}
- This->contexts[This->numContexts] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WineD3DContext));
+ This->contexts[This->numContexts] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**This->contexts));
if(This->contexts[This->numContexts] == NULL) {
ERR("Unable to allocate a new context\n");
HeapFree(GetProcessHeap(), 0, This->contexts);
/* Mark all states dirty to force a proper initialization of the states on the first use of the context
*/
for(state = 0; state <= STATE_HIGHEST; state++) {
- Context_MarkStateDirty(This->contexts[This->numContexts], state, This->StateTable);
+ if (This->StateTable[state].representative)
+ Context_MarkStateDirty(This->contexts[This->numContexts], state, This->StateTable);
}
This->numContexts++;
* pPresentParameters: contains the pixelformats to use for onscreen rendering
*
*****************************************************************************/
-WineD3DContext *CreateContext(IWineD3DDeviceImpl *This, IWineD3DSurfaceImpl *target, HWND win_handle, BOOL create_pbuffer, const WINED3DPRESENT_PARAMETERS *pPresentParms) {
- HDC oldDrawable, hdc;
+struct wined3d_context *CreateContext(IWineD3DDeviceImpl *This, IWineD3DSurfaceImpl *target,
+ HWND win_handle, BOOL create_pbuffer, const WINED3DPRESENT_PARAMETERS *pPresentParms)
+{
+ const struct wined3d_gl_info *gl_info = &This->adapter->gl_info;
+ struct wined3d_context *ret = NULL;
HPBUFFERARB pbuffer = NULL;
- HGLRC ctx = NULL, oldCtx;
- WineD3DContext *ret = NULL;
unsigned int s;
+ HGLRC ctx;
+ HDC hdc;
TRACE("(%p): Creating a %s context for render target %p\n", This, create_pbuffer ? "offscreen" : "onscreen", target);
/* If we still don't have a pixel format, something is very wrong as ChoosePixelFormat barely fails */
if(!iPixelFormat) {
ERR("Can't find a suitable iPixelFormat\n");
- return FALSE;
+ return NULL;
}
DescribePixelFormat(hdc, iPixelFormat, sizeof(pfd), &pfd);
if(!res) {
ERR("wglSetPixelFormatWINE failed on HDC=%p for iPixelFormat=%d\n", hdc, iPixelFormat);
- return FALSE;
+ return NULL;
}
} else if(oldPixelFormat) {
/* OpenGL doesn't allow pixel format adjustments. Print an error and continue using the old format.
ERR("HDC=%p is already set to iPixelFormat=%d and OpenGL doesn't allow changes!\n", hdc, oldPixelFormat);
} else {
ERR("SetPixelFormat failed on HDC=%p for iPixelFormat=%d\n", hdc, iPixelFormat);
- return FALSE;
+ return NULL;
}
}
}
ctx = pwglCreateContext(hdc);
- if(This->numContexts) pwglShareLists(This->contexts[0]->glCtx, ctx);
+ if (This->numContexts)
+ {
+ if (!pwglShareLists(This->contexts[0]->glCtx, ctx))
+ {
+ DWORD err = GetLastError();
+ ERR("wglShareLists(%p, %p) failed, last error %#x.\n",
+ This->contexts[0]->glCtx, ctx, err);
+ }
+ }
if(!ctx) {
ERR("Failed to create a WGL context\n");
ret = AddContextToArray(This, win_handle, hdc, ctx, pbuffer);
if(!ret) {
ERR("Failed to add the newly created context to the context list\n");
- pwglDeleteContext(ctx);
+ if (!pwglDeleteContext(ctx))
+ {
+ DWORD err = GetLastError();
+ ERR("wglDeleteContext(%p) failed, last error %#x.\n", ctx, err);
+ }
if(create_pbuffer) {
GL_EXTCALL(wglReleasePbufferDCARB(pbuffer, hdc));
GL_EXTCALL(wglDestroyPbufferARB(pbuffer));
}
goto out;
}
+ ret->gl_info = &This->adapter->gl_info;
ret->surface = (IWineD3DSurface *) target;
+ ret->current_rt = (IWineD3DSurface *)target;
ret->isPBuffer = create_pbuffer;
ret->tid = GetCurrentThreadId();
if(This->shader_backend->shader_dirtifyable_constants((IWineD3DDevice *) This)) {
sizeof(*ret->pshader_const_dirty) * GL_LIMITS(pshader_constantsF));
}
+ ret->free_occlusion_query_size = 4;
+ ret->free_occlusion_queries = HeapAlloc(GetProcessHeap(), 0,
+ ret->free_occlusion_query_size * sizeof(*ret->free_occlusion_queries));
+ if (!ret->free_occlusion_queries) goto out;
+
+ list_init(&ret->occlusion_queries);
+
+ ret->free_event_query_size = 4;
+ ret->free_event_queries = HeapAlloc(GetProcessHeap(), 0,
+ ret->free_event_query_size * sizeof(*ret->free_event_queries));
+ if (!ret->free_event_queries) goto out;
+
+ list_init(&ret->event_queries);
+
TRACE("Successfully created new context %p\n", ret);
list_init(&ret->fbo_list);
/* Set up the context defaults */
- oldCtx = pwglGetCurrentContext();
- oldDrawable = pwglGetCurrentDC();
- if(oldCtx && oldDrawable) {
- /* See comment in ActivateContext context switching */
- This->frag_pipe->enable_extension((IWineD3DDevice *) This, FALSE);
- }
- if(pwglMakeCurrent(hdc, ctx) == FALSE) {
+ if (!context_set_current(ret))
+ {
ERR("Cannot activate context to set up defaults\n");
goto out;
}
TRACE("Setting up the screen\n");
/* Clear the screen */
- glClearColor(1.0, 0.0, 0.0, 0.0);
+ glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
checkGLcall("glClearColor");
glClearIndex(0);
glClearDepth(1);
for(s = 1; s < GL_LIMITS(textures); s++) {
GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB + s));
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB + s - 1);
- checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, ...\n");
+ checkGLcall("glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, ...");
}
}
+ if(GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
+ /* MacOS(radeon X1600 at least, but most likely others too) refuses to draw if GLSL and ARBFP are
+ * enabled, but the currently bound arbfp program is 0. Enabling ARBFP with prog 0 is invalid, but
+ * GLSL should bypass this. This causes problems in programs that never use the fixed function pipeline,
+ * because the ARBFP extension is enabled by the ARBFP pipeline at context creation, but no program
+ * is ever assigned.
+ *
+ * So make sure a program is assigned to each context. The first real ARBFP use will set a different
+ * program and the dummy program is destroyed when the context is destroyed.
+ */
+ const char *dummy_program =
+ "!!ARBfp1.0\n"
+ "MOV result.color, fragment.color.primary;\n"
+ "END\n";
+ GL_EXTCALL(glGenProgramsARB(1, &ret->dummy_arbfp_prog));
+ GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, ret->dummy_arbfp_prog));
+ GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(dummy_program), dummy_program));
+ }
for(s = 0; s < GL_LIMITS(point_sprite_units); s++) {
GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB + s));
glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
- checkGLcall("glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE)\n");
+ checkGLcall("glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE)");
}
- LEAVE_GL();
- /* Never keep GL_FRAGMENT_SHADER_ATI enabled on a context that we switch away from,
- * but enable it for the first context we create, and reenable it on the old context
- */
- if(oldDrawable && oldCtx) {
- pwglMakeCurrent(oldDrawable, oldCtx);
- } else {
- last_device = This;
+ if (GL_SUPPORT(EXT_PROVOKING_VERTEX))
+ {
+ GL_EXTCALL(glProvokingVertexEXT(GL_FIRST_VERTEX_CONVENTION_EXT));
}
+
+ LEAVE_GL();
+
This->frag_pipe->enable_extension((IWineD3DDevice *) This, TRUE);
return ret;
out:
+ if (ret)
+ {
+ HeapFree(GetProcessHeap(), 0, ret->free_event_queries);
+ HeapFree(GetProcessHeap(), 0, ret->free_occlusion_queries);
+ HeapFree(GetProcessHeap(), 0, ret->pshader_const_dirty);
+ HeapFree(GetProcessHeap(), 0, ret->vshader_const_dirty);
+ HeapFree(GetProcessHeap(), 0, ret);
+ }
return NULL;
}
* context: Context to remove
*
*****************************************************************************/
-static void RemoveContextFromArray(IWineD3DDeviceImpl *This, WineD3DContext *context) {
- WineD3DContext **new_array;
+static void RemoveContextFromArray(IWineD3DDeviceImpl *This, struct wined3d_context *context)
+{
+ struct wined3d_context **new_array;
BOOL found = FALSE;
UINT i;
{
if (This->contexts[i] == context)
{
- HeapFree(GetProcessHeap(), 0, context);
found = TRUE;
break;
}
* context: Context to destroy
*
*****************************************************************************/
-void DestroyContext(IWineD3DDeviceImpl *This, WineD3DContext *context) {
- struct fbo_entry *entry, *entry2;
+void DestroyContext(IWineD3DDeviceImpl *This, struct wined3d_context *context)
+{
+ BOOL destroy;
TRACE("Destroying ctx %p\n", context);
- /* The correct GL context needs to be active to cleanup the GL resources below */
- if(pwglGetCurrentContext() != context->glCtx){
- pwglMakeCurrent(context->hdc, context->glCtx);
- last_device = NULL;
- }
-
- ENTER_GL();
+ if (context->tid == GetCurrentThreadId() || !context->current)
+ {
+ context_destroy_gl_resources(context);
+ destroy = TRUE;
- LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &context->fbo_list, struct fbo_entry, entry) {
- context_destroy_fbo_entry(This, entry);
- }
- if (context->src_fbo) {
- TRACE("Destroy src FBO %d\n", context->src_fbo);
- context_destroy_fbo(This, &context->src_fbo);
- }
- if (context->dst_fbo) {
- TRACE("Destroy dst FBO %d\n", context->dst_fbo);
- context_destroy_fbo(This, &context->dst_fbo);
+ if (!context_set_current(NULL))
+ {
+ ERR("Failed to clear current D3D context.\n");
+ }
}
-
- LEAVE_GL();
-
- if (This->activeContext == context)
+ else
{
- This->activeContext = NULL;
- TRACE("Destroying the active context.\n");
+ context->destroyed = 1;
+ destroy = FALSE;
}
- /* Cleanup the GL context */
- pwglMakeCurrent(NULL, NULL);
- if(context->isPBuffer) {
- GL_EXTCALL(wglReleasePbufferDCARB(context->pbuffer, context->hdc));
- GL_EXTCALL(wglDestroyPbufferARB(context->pbuffer));
- } else ReleaseDC(context->win_handle, context->hdc);
- pwglDeleteContext(context->glCtx);
-
HeapFree(GetProcessHeap(), 0, context->vshader_const_dirty);
HeapFree(GetProcessHeap(), 0, context->pshader_const_dirty);
RemoveContextFromArray(This, context);
+ if (destroy) HeapFree(GetProcessHeap(), 0, context);
}
+/* GL locking is done by the caller */
static inline void set_blit_dimension(UINT width, UINT height) {
glMatrixMode(GL_PROJECTION);
checkGLcall("glMatrixMode(GL_PROJECTION)");
* height: render target height
*
*****************************************************************************/
-static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *context, UINT width, UINT height) {
- int i, sampler;
+/* Context activation is done by the caller. */
+static inline void SetupForBlit(IWineD3DDeviceImpl *This, struct wined3d_context *context, UINT width, UINT height)
+{
+ int i;
const struct StateEntry *StateTable = This->StateTable;
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+ DWORD sampler;
TRACE("Setting up context %p for blitting\n", context);
if(context->last_was_blit) {
if(context->blit_w != width || context->blit_h != height) {
+ ENTER_GL();
set_blit_dimension(width, height);
+ LEAVE_GL();
context->blit_w = width; context->blit_h = height;
/* No need to dirtify here, the states are still dirtified because they weren't
* applied since the last SetupForBlit call. Otherwise last_was_blit would not
/* TODO: Use a display list */
/* Disable shaders */
- This->shader_backend->shader_select((IWineD3DDevice *)This, FALSE, FALSE);
+ ENTER_GL();
+ This->shader_backend->shader_select(context, FALSE, FALSE);
+ LEAVE_GL();
+
Context_MarkStateDirty(context, STATE_VSHADER, StateTable);
Context_MarkStateDirty(context, STATE_PIXELSHADER, StateTable);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
checkGLcall("glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);");
- if (sampler != -1) {
+ if (sampler != WINED3D_UNMAPPED_STAGE)
+ {
if (sampler < MAX_TEXTURES) {
Context_MarkStateDirty(context, STATE_TEXTURESTAGE(sampler, WINED3DTSS_COLOROP), StateTable);
}
if (GL_SUPPORT(EXT_TEXTURE_LOD_BIAS)) {
glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT,
GL_TEXTURE_LOD_BIAS_EXT,
- 0.0);
+ 0.0f);
checkGLcall("glTexEnvi GL_TEXTURE_LOD_BIAS_EXT ...");
}
- if (sampler != -1) {
+ if (sampler != WINED3D_UNMAPPED_STAGE)
+ {
if (sampler < MAX_TEXTURES) {
Context_MarkStateDirty(context, STATE_TRANSFORM(WINED3DTS_TEXTURE0 + sampler), StateTable);
Context_MarkStateDirty(context, STATE_TEXTURESTAGE(sampler, WINED3DTSS_COLOROP), StateTable);
}
glColorMask(GL_TRUE, GL_TRUE,GL_TRUE,GL_TRUE);
checkGLcall("glColorMask");
- Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_CLIPPING), StateTable);
+ Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_COLORWRITEENABLE), StateTable);
if (GL_SUPPORT(EXT_SECONDARY_COLOR)) {
glDisable(GL_COLOR_SUM_EXT);
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_SPECULARENABLE), StateTable);
glDisable(GL_CLIP_PLANE4); checkGLcall("glDisable(clip plane 4)");
glDisable(GL_CLIP_PLANE5); checkGLcall("glDisable(clip plane 5)");
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_CLIPPING), StateTable);
- LEAVE_GL();
set_blit_dimension(width, height);
+
+ LEAVE_GL();
+
context->blit_w = width; context->blit_h = height;
Context_MarkStateDirty(context, STATE_VIEWPORT, StateTable);
Context_MarkStateDirty(context, STATE_TRANSFORM(WINED3DTS_PROJECTION), StateTable);
* If none can be found the swapchain is requested to create a new context
*
*****************************************************************************/
-static WineD3DContext *findThreadContextForSwapChain(IWineD3DSwapChain *swapchain, DWORD tid) {
+static struct wined3d_context *findThreadContextForSwapChain(IWineD3DSwapChain *swapchain, DWORD tid)
+{
unsigned int i;
for(i = 0; i < ((IWineD3DSwapChainImpl *) swapchain)->num_contexts; i++) {
* Returns: The needed context
*
*****************************************************************************/
-static inline WineD3DContext *FindContext(IWineD3DDeviceImpl *This, IWineD3DSurface *target, DWORD tid) {
+static inline struct wined3d_context *FindContext(IWineD3DDeviceImpl *This, IWineD3DSurface *target, DWORD tid)
+{
IWineD3DSwapChain *swapchain = NULL;
- BOOL readTexture = wined3d_settings.offscreen_rendering_mode != ORM_FBO && This->render_offscreen;
- WineD3DContext *context = This->activeContext;
- BOOL oldRenderOffscreen = This->render_offscreen;
- const struct GlPixelFormatDesc *old = ((IWineD3DSurfaceImpl *)This->lastActiveRenderTarget)->resource.format_desc;
- const struct GlPixelFormatDesc *new = ((IWineD3DSurfaceImpl *)target)->resource.format_desc;
+ struct wined3d_context *current_context = context_get_current();
const struct StateEntry *StateTable = This->StateTable;
+ struct wined3d_context *context;
+ BOOL old_render_offscreen;
- /* To compensate the lack of format switching with some offscreen rendering methods and on onscreen buffers
- * the alpha blend state changes with different render target formats
- */
- if (old->format != new->format)
+ if (current_context && current_context->destroyed) current_context = NULL;
+
+ if (!target)
{
- /* Disable blending when the alpha mask has changed and when a format doesn't support blending */
- if ((old->alpha_mask && !new->alpha_mask) || (!old->alpha_mask && new->alpha_mask)
- || !(new->Flags & WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING))
+ if (current_context
+ && current_context->current_rt
+ && ((IWineD3DSurfaceImpl *)current_context->surface)->resource.wineD3DDevice == This)
{
- Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE), StateTable);
+ target = current_context->current_rt;
+ }
+ else
+ {
+ IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)This->swapchains[0];
+ if (swapchain->backBuffer) target = swapchain->backBuffer[0];
+ else target = swapchain->frontBuffer;
}
}
+ if (current_context && current_context->current_rt == target)
+ {
+ return current_context;
+ }
+
if (SUCCEEDED(IWineD3DSurface_GetContainer(target, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
TRACE("Rendering onscreen\n");
context = findThreadContextForSwapChain(swapchain, tid);
- This->render_offscreen = FALSE;
+ old_render_offscreen = context->render_offscreen;
+ context->render_offscreen = FALSE;
/* The context != This->activeContext will catch a NOP context change. This can occur
* if we are switching back to swapchain rendering in case of FBO or Back Buffer offscreen
* rendering. No context change is needed in that case
}
}
IWineD3DSwapChain_Release(swapchain);
-
- if(oldRenderOffscreen) {
- Context_MarkStateDirty(context, WINED3DTS_PROJECTION, StateTable);
- Context_MarkStateDirty(context, STATE_VDECL, StateTable);
- Context_MarkStateDirty(context, STATE_VIEWPORT, StateTable);
- Context_MarkStateDirty(context, STATE_SCISSORRECT, StateTable);
- Context_MarkStateDirty(context, STATE_FRONTFACE, StateTable);
- }
-
- } else {
+ }
+ else
+ {
TRACE("Rendering offscreen\n");
- This->render_offscreen = TRUE;
- switch(wined3d_settings.offscreen_rendering_mode) {
- case ORM_FBO:
- /* FBOs do not need a different context. Stay with whatever context is active at the moment */
- if(This->activeContext && tid == This->lastThread) {
- context = This->activeContext;
- } else {
- /* This may happen if the app jumps straight into offscreen rendering
- * Start using the context of the primary swapchain. tid == 0 is no problem
- * for findThreadContextForSwapChain.
- *
- * Can also happen on thread switches - in that case findThreadContextForSwapChain
- * is perfect to call.
- */
- context = findThreadContextForSwapChain(This->swapchains[0], tid);
- }
- break;
-
- case ORM_PBUFFER:
+retry:
+ if (wined3d_settings.offscreen_rendering_mode == ORM_PBUFFER)
+ {
+ IWineD3DSurfaceImpl *targetimpl = (IWineD3DSurfaceImpl *)target;
+ if (!This->pbufferContext
+ || This->pbufferWidth < targetimpl->currentDesc.Width
+ || This->pbufferHeight < targetimpl->currentDesc.Height)
{
- IWineD3DSurfaceImpl *targetimpl = (IWineD3DSurfaceImpl *) target;
- if(This->pbufferContext == NULL ||
- This->pbufferWidth < targetimpl->currentDesc.Width ||
- This->pbufferHeight < targetimpl->currentDesc.Height) {
- if(This->pbufferContext) {
- DestroyContext(This, This->pbufferContext);
- }
-
- /* The display is irrelevant here, the window is 0. But CreateContext needs a valid X connection.
- * Create the context on the same server as the primary swapchain. The primary swapchain is exists at this point.
- */
- This->pbufferContext = CreateContext(This, targetimpl,
- ((IWineD3DSwapChainImpl *) This->swapchains[0])->context[0]->win_handle,
- TRUE /* pbuffer */, &((IWineD3DSwapChainImpl *)This->swapchains[0])->presentParms);
- This->pbufferWidth = targetimpl->currentDesc.Width;
- This->pbufferHeight = targetimpl->currentDesc.Height;
- }
-
- if(This->pbufferContext) {
- if(This->pbufferContext->tid != 0 && This->pbufferContext->tid != tid) {
- FIXME("The PBuffr context is only supported for one thread for now!\n");
- }
- This->pbufferContext->tid = tid;
- context = This->pbufferContext;
- break;
- } else {
- ERR("Failed to create a buffer context and drawable, falling back to back buffer offscreen rendering\n");
- wined3d_settings.offscreen_rendering_mode = ORM_BACKBUFFER;
- }
+ if (This->pbufferContext) DestroyContext(This, This->pbufferContext);
+
+ /* The display is irrelevant here, the window is 0. But
+ * CreateContext needs a valid X connection. Create the context
+ * on the same server as the primary swapchain. The primary
+ * swapchain is exists at this point. */
+ This->pbufferContext = CreateContext(This, targetimpl,
+ ((IWineD3DSwapChainImpl *)This->swapchains[0])->context[0]->win_handle,
+ TRUE /* pbuffer */, &((IWineD3DSwapChainImpl *)This->swapchains[0])->presentParms);
+ This->pbufferWidth = targetimpl->currentDesc.Width;
+ This->pbufferHeight = targetimpl->currentDesc.Height;
}
- case ORM_BACKBUFFER:
- /* Stay with the currently active context for back buffer rendering */
- if(This->activeContext && tid == This->lastThread) {
- context = This->activeContext;
- } else {
- /* This may happen if the app jumps straight into offscreen rendering
- * Start using the context of the primary swapchain. tid == 0 is no problem
- * for findThreadContextForSwapChain.
- *
- * Can also happen on thread switches - in that case findThreadContextForSwapChain
- * is perfect to call.
- */
- context = findThreadContextForSwapChain(This->swapchains[0], tid);
+ if (This->pbufferContext)
+ {
+ if (This->pbufferContext->tid && This->pbufferContext->tid != tid)
+ {
+ FIXME("The PBuffer context is only supported for one thread for now!\n");
}
- break;
+ This->pbufferContext->tid = tid;
+ context = This->pbufferContext;
+ }
+ else
+ {
+ ERR("Failed to create a buffer context and drawable, falling back to back buffer offscreen rendering.\n");
+ wined3d_settings.offscreen_rendering_mode = ORM_BACKBUFFER;
+ goto retry;
+ }
}
-
- if(!oldRenderOffscreen) {
- Context_MarkStateDirty(context, WINED3DTS_PROJECTION, StateTable);
- Context_MarkStateDirty(context, STATE_VDECL, StateTable);
- Context_MarkStateDirty(context, STATE_VIEWPORT, StateTable);
- Context_MarkStateDirty(context, STATE_SCISSORRECT, StateTable);
- Context_MarkStateDirty(context, STATE_FRONTFACE, StateTable);
+ else
+ {
+ /* Stay with the currently active context. */
+ if (current_context
+ && ((IWineD3DSurfaceImpl *)current_context->surface)->resource.wineD3DDevice == This)
+ {
+ context = current_context;
+ }
+ else
+ {
+ /* This may happen if the app jumps straight into offscreen rendering
+ * Start using the context of the primary swapchain. tid == 0 is no problem
+ * for findThreadContextForSwapChain.
+ *
+ * Can also happen on thread switches - in that case findThreadContextForSwapChain
+ * is perfect to call. */
+ context = findThreadContextForSwapChain(This->swapchains[0], tid);
+ }
}
+
+ old_render_offscreen = context->render_offscreen;
+ context->render_offscreen = TRUE;
}
- /* When switching away from an offscreen render target, and we're not using FBOs,
- * we have to read the drawable into the texture. This is done via PreLoad(and
- * SFLAG_INDRAWABLE set on the surface). There are some things that need care though.
- * PreLoad needs a GL context, and FindContext is called before the context is activated.
- * It also has to be called with the old rendertarget active, otherwise a wrong drawable
- * is read. This leads to these possible situations:
- *
- * 0) lastActiveRenderTarget == target && oldTid == newTid:
- * Nothing to do, we don't even reach this code in this case...
- *
- * 1) lastActiveRenderTarget != target && oldTid == newTid:
- * The currently active context is OK for readback. Call PreLoad, and it
- * performs the read
- *
- * 2) lastActiveRenderTarget == target && oldTid != newTid:
- * Nothing to do - the drawable is unchanged
- *
- * 3) lastActiveRenderTarget != target && oldTid != newTid:
- * This is tricky. We have to get a context with the old drawable from somewhere
- * before we can switch to the new context. In this case, PreLoad calls
- * ActivateContext(lastActiveRenderTarget) from the new(current) thread. This
- * is case (2) then. The old drawable is activated for the new thread, and the
- * readback can be done. The recursed ActivateContext does *not* call PreLoad again.
- * After that, the outer ActivateContext(which calls PreLoad) can activate the new
- * target for the new thread
- */
- if (readTexture && This->lastActiveRenderTarget != target) {
- BOOL oldInDraw = This->isInDraw;
+ if (context->render_offscreen != old_render_offscreen)
+ {
+ Context_MarkStateDirty(context, STATE_TRANSFORM(WINED3DTS_PROJECTION), StateTable);
+ Context_MarkStateDirty(context, STATE_VDECL, StateTable);
+ Context_MarkStateDirty(context, STATE_VIEWPORT, StateTable);
+ Context_MarkStateDirty(context, STATE_SCISSORRECT, StateTable);
+ Context_MarkStateDirty(context, STATE_FRONTFACE, StateTable);
+ }
- /* PreLoad requires a context to load the texture, thus it will call ActivateContext.
- * Set the isInDraw to true to signal PreLoad that it has a context. Will be tricky
- * when using offscreen rendering with multithreading
- */
- This->isInDraw = TRUE;
+ /* To compensate the lack of format switching with some offscreen rendering methods and on onscreen buffers
+ * the alpha blend state changes with different render target formats. */
+ if (!context->current_rt)
+ {
+ Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE), StateTable);
+ }
+ else
+ {
+ const struct GlPixelFormatDesc *old = ((IWineD3DSurfaceImpl *)context->current_rt)->resource.format_desc;
+ const struct GlPixelFormatDesc *new = ((IWineD3DSurfaceImpl *)target)->resource.format_desc;
- /* Do that before switching the context:
- * Read the back buffer of the old drawable into the destination texture
- */
- if(((IWineD3DSurfaceImpl *)This->lastActiveRenderTarget)->glDescription.srgbTextureName) {
- surface_internal_preload(This->lastActiveRenderTarget, SRGB_BOTH);
- } else {
- surface_internal_preload(This->lastActiveRenderTarget, SRGB_RGB);
+ if (old->format != new->format)
+ {
+ /* Disable blending when the alpha mask has changed and when a format doesn't support blending. */
+ if ((old->alpha_mask && !new->alpha_mask) || (!old->alpha_mask && new->alpha_mask)
+ || !(new->Flags & WINED3DFMT_FLAG_POSTPIXELSHADER_BLENDING))
+ {
+ Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE), StateTable);
+ }
}
- /* Assume that the drawable will be modified by some other things now */
- IWineD3DSurface_ModifyLocation(This->lastActiveRenderTarget, SFLAG_INDRAWABLE, FALSE);
+ /* When switching away from an offscreen render target, and we're not
+ * using FBOs, we have to read the drawable into the texture. This is
+ * done via PreLoad (and SFLAG_INDRAWABLE set on the surface). There
+ * are some things that need care though. PreLoad needs a GL context,
+ * and FindContext is called before the context is activated. It also
+ * has to be called with the old rendertarget active, otherwise a
+ * wrong drawable is read. */
+ if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
+ && old_render_offscreen && context->current_rt != target)
+ {
+ BOOL oldInDraw = This->isInDraw;
+
+ /* surface_internal_preload() requires a context to load the
+ * texture, so it will call ActivateContext. Set isInDraw to true
+ * to signal surface_internal_preload() that it has a context. */
+
+ /* FIXME: This is just broken. There's no guarantee whatsoever
+ * that the currently active context, if any, is appropriate for
+ * reading back the render target. We should probably call
+ * context_set_current(context) here and then rely on
+ * ActivateContext() doing the right thing. */
+ This->isInDraw = TRUE;
+
+ /* Read the back buffer of the old drawable into the destination texture. */
+ if (((IWineD3DSurfaceImpl *)context->current_rt)->texture_name_srgb)
+ {
+ surface_internal_preload(context->current_rt, SRGB_BOTH);
+ }
+ else
+ {
+ surface_internal_preload(context->current_rt, SRGB_RGB);
+ }
- This->isInDraw = oldInDraw;
+ IWineD3DSurface_ModifyLocation(context->current_rt, SFLAG_INDRAWABLE, FALSE);
+
+ This->isInDraw = oldInDraw;
+ }
}
+ context->draw_buffer_dirty = TRUE;
+ context->current_rt = target;
+
return context;
}
-static void apply_draw_buffer(IWineD3DDeviceImpl *This, IWineD3DSurface *target, BOOL blit)
+/* Context activation is done by the caller. */
+static void context_apply_draw_buffer(struct wined3d_context *context, BOOL blit)
{
+ const struct wined3d_gl_info *gl_info = context->gl_info;
+ IWineD3DSurface *rt = context->current_rt;
IWineD3DSwapChain *swapchain;
+ IWineD3DDeviceImpl *device;
- if (SUCCEEDED(IWineD3DSurface_GetContainer(target, &IID_IWineD3DSwapChain, (void **)&swapchain)))
+ device = ((IWineD3DSurfaceImpl *)rt)->resource.wineD3DDevice;
+ if (SUCCEEDED(IWineD3DSurface_GetContainer(rt, &IID_IWineD3DSwapChain, (void **)&swapchain)))
{
IWineD3DSwapChain_Release((IUnknown *)swapchain);
ENTER_GL();
- glDrawBuffer(surface_get_gl_buffer(target, swapchain));
+ glDrawBuffer(surface_get_gl_buffer(rt, swapchain));
checkGLcall("glDrawBuffers()");
LEAVE_GL();
}
{
if (GL_SUPPORT(ARB_DRAW_BUFFERS))
{
- GL_EXTCALL(glDrawBuffersARB(GL_LIMITS(buffers), This->draw_buffers));
+ GL_EXTCALL(glDrawBuffersARB(GL_LIMITS(buffers), device->draw_buffers));
checkGLcall("glDrawBuffers()");
}
else
{
- glDrawBuffer(This->draw_buffers[0]);
+ glDrawBuffer(device->draw_buffers[0]);
checkGLcall("glDrawBuffer()");
}
} else {
}
else
{
- glDrawBuffer(This->offscreenBuffer);
+ glDrawBuffer(device->offscreenBuffer);
checkGLcall("glDrawBuffer()");
}
LEAVE_GL();
* usage: Prepares the context for blitting, drawing or other actions
*
*****************************************************************************/
-void ActivateContext(IWineD3DDeviceImpl *This, IWineD3DSurface *target, ContextUsage usage) {
+struct wined3d_context *ActivateContext(IWineD3DDeviceImpl *This, IWineD3DSurface *target, enum ContextUsage usage)
+{
+ struct wined3d_context *current_context = context_get_current();
DWORD tid = GetCurrentThreadId();
DWORD i, dirtyState, idx;
BYTE shift;
- WineD3DContext *context;
const struct StateEntry *StateTable = This->StateTable;
+ const struct wined3d_gl_info *gl_info;
+ struct wined3d_context *context;
TRACE("(%p): Selecting context for render target %p, thread %d\n", This, target, tid);
- if(This->lastActiveRenderTarget != target || tid != This->lastThread) {
- context = FindContext(This, target, tid);
- context->draw_buffer_dirty = TRUE;
- This->lastActiveRenderTarget = target;
- This->lastThread = tid;
- } else {
- /* Stick to the old context */
- context = This->activeContext;
- }
- /* Activate the opengl context */
- if(last_device != This || context != This->activeContext) {
- BOOL ret;
+ context = FindContext(This, target, tid);
- /* Prevent an unneeded context switch as those are expensive */
- if(context->glCtx && (context->glCtx == pwglGetCurrentContext())) {
- TRACE("Already using gl context %p\n", context->glCtx);
- }
- else {
- TRACE("Switching gl ctx to %p, hdc=%p ctx=%p\n", context, context->hdc, context->glCtx);
+ gl_info = context->gl_info;
- ret = pwglMakeCurrent(context->hdc, context->glCtx);
- if(ret == FALSE) {
- ERR("Failed to activate the new context\n");
- } else if(!context->last_was_blit) {
- This->frag_pipe->enable_extension((IWineD3DDevice *) This, TRUE);
- } else {
- This->frag_pipe->enable_extension((IWineD3DDevice *) This, FALSE);
- }
- }
- if(This->activeContext->vshader_const_dirty) {
- memset(This->activeContext->vshader_const_dirty, 1,
- sizeof(*This->activeContext->vshader_const_dirty) * GL_LIMITS(vshader_constantsF));
+ /* Activate the opengl context */
+ if (context != current_context)
+ {
+ if (!context_set_current(context)) ERR("Failed to activate the new context.\n");
+ else This->frag_pipe->enable_extension((IWineD3DDevice *)This, !context->last_was_blit);
+
+ if (context->vshader_const_dirty)
+ {
+ memset(context->vshader_const_dirty, 1,
+ sizeof(*context->vshader_const_dirty) * GL_LIMITS(vshader_constantsF));
+ This->highest_dirty_vs_const = GL_LIMITS(vshader_constantsF);
}
- if(This->activeContext->pshader_const_dirty) {
- memset(This->activeContext->pshader_const_dirty, 1,
- sizeof(*This->activeContext->pshader_const_dirty) * GL_LIMITS(pshader_constantsF));
+ if (context->pshader_const_dirty)
+ {
+ memset(context->pshader_const_dirty, 1,
+ sizeof(*context->pshader_const_dirty) * GL_LIMITS(pshader_constantsF));
+ This->highest_dirty_ps_const = GL_LIMITS(pshader_constantsF);
}
- This->activeContext = context;
- last_device = This;
}
switch (usage) {
case CTXUSAGE_CLEAR:
case CTXUSAGE_DRAWPRIM:
if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
- context_apply_fbo_state((IWineD3DDevice *)This);
+ ENTER_GL();
+ context_apply_fbo_state(context);
+ LEAVE_GL();
}
if (context->draw_buffer_dirty) {
- apply_draw_buffer(This, target, FALSE);
+ context_apply_draw_buffer(context, FALSE);
context->draw_buffer_dirty = FALSE;
}
break;
case CTXUSAGE_BLIT:
if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
- if (This->render_offscreen) {
+ if (context->render_offscreen)
+ {
FIXME("Activating for CTXUSAGE_BLIT for an offscreen target with ORM_FBO. This should be avoided.\n");
- context_bind_fbo((IWineD3DDevice *)This, GL_FRAMEBUFFER_EXT, &context->dst_fbo);
- context_attach_surface_fbo(This, GL_FRAMEBUFFER_EXT, 0, target);
-
ENTER_GL();
- GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0));
- checkGLcall("glFramebufferRenderbufferEXT");
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->dst_fbo);
+ context_attach_surface_fbo(context, GL_FRAMEBUFFER_EXT, 0, target);
+ context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER_EXT, NULL, FALSE);
LEAVE_GL();
} else {
ENTER_GL();
- GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
- checkGLcall("glFramebufferRenderbufferEXT");
+ context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
LEAVE_GL();
}
context->draw_buffer_dirty = TRUE;
}
if (context->draw_buffer_dirty) {
- apply_draw_buffer(This, target, TRUE);
+ context_apply_draw_buffer(context, TRUE);
if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
context->draw_buffer_dirty = FALSE;
}
IWineD3DDeviceImpl_FindTexUnitMap(This);
+ ENTER_GL();
for(i=0; i < context->numDirtyEntries; i++) {
dirtyState = context->dirtyArray[i];
idx = dirtyState >> 5;
context->isStateDirty[idx] &= ~(1 << shift);
StateTable[dirtyState].apply(dirtyState, This->stateBlock, context);
}
+ LEAVE_GL();
context->numDirtyEntries = 0; /* This makes the whole list clean */
context->last_was_blit = FALSE;
break;
default:
FIXME("Unexpected context usage requested\n");
}
-}
-WineD3DContext *getActiveContext(void) {
- return last_device->activeContext;
+ return context;
}