Commit 12252d05 authored by Stefan Dösinger's avatar Stefan Dösinger Committed by Alexandre Julliard

wined3d: Use the context manager to select the primary render target.

parent 7253fae3
......@@ -30,6 +30,265 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d);
#define GLINFO_LOCATION ((IWineD3DImpl *)(This->wineD3D))->gl_info
/*****************************************************************************
* Context_MarkStateDirty
*
* Marks a state in a context dirty. Only one context, opposed to
* IWineD3DDeviceImpl_MarkStateDirty, which marks the state dirty in all
* contexts
*
* Params:
* context: Context to mark the state dirty in
* state: State to mark dirty
*
*****************************************************************************/
static void Context_MarkStateDirty(WineD3DContext *context, DWORD state) {
DWORD rep = StateTable[state].representative;
DWORD idx;
BYTE shift;
if(!rep || isStateDirty(context, rep)) return;
context->dirtyArray[context->numDirtyEntries++] = rep;
idx = rep >> 5;
shift = rep & 0x1f;
context->isStateDirty[idx] |= (1 << shift);
}
/* Returns an array of compatible FBconfig(s).
* The array must be freed with XFree. Requires ENTER_GL()
*/
static GLXFBConfig* pbuffer_find_fbconfigs(
IWineD3DDeviceImpl* This,
IWineD3DSwapChainImpl* implicitSwapchainImpl,
IWineD3DSurfaceImpl* RenderSurface) {
GLXFBConfig* cfgs = NULL;
int nCfgs = 0;
int attribs[256];
int nAttribs = 0;
IWineD3DSurface *StencilSurface = This->stencilBufferTarget;
WINED3DFORMAT BackBufferFormat = RenderSurface->resource.format;
WINED3DFORMAT StencilBufferFormat = (NULL != StencilSurface) ? ((IWineD3DSurfaceImpl *) StencilSurface)->resource.format : 0;
/* TODO:
* if StencilSurface == NULL && zBufferTarget != NULL then switch the zbuffer off,
* it StencilSurface != NULL && zBufferTarget == NULL switch it on
*/
#define PUSH1(att) attribs[nAttribs++] = (att);
#define PUSH2(att,value) attribs[nAttribs++] = (att); attribs[nAttribs++] = (value);
/* PUSH2(GLX_BIND_TO_TEXTURE_RGBA_ATI, True); examples of this are few and far between (but I've got a nice working one!)*/
PUSH2(GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT);
PUSH2(GLX_X_RENDERABLE, TRUE);
PUSH2(GLX_DOUBLEBUFFER, TRUE);
TRACE("calling makeglcfg\n");
D3DFmtMakeGlCfg(BackBufferFormat, StencilBufferFormat, attribs, &nAttribs, FALSE /* alternate */);
PUSH1(None);
TRACE("calling chooseFGConfig\n");
cfgs = glXChooseFBConfig(implicitSwapchainImpl->display,
DefaultScreen(implicitSwapchainImpl->display),
attribs, &nCfgs);
if (cfgs == NULL) {
/* OK we didn't find the exact config, so use any reasonable match */
/* TODO: fill in the 'requested' and 'current' depths, and make sure that's
why we failed. */
static BOOL show_message = TRUE;
if (show_message) {
ERR("Failed to find exact match, finding alternative but you may "
"suffer performance issues, try changing xfree's depth to match the requested depth\n");
show_message = FALSE;
}
nAttribs = 0;
PUSH2(GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT);
/* PUSH2(GLX_X_RENDERABLE, TRUE); */
PUSH2(GLX_RENDER_TYPE, GLX_RGBA_BIT);
PUSH2(GLX_DOUBLEBUFFER, FALSE);
TRACE("calling makeglcfg\n");
D3DFmtMakeGlCfg(BackBufferFormat, StencilBufferFormat, attribs, &nAttribs, TRUE /* alternate */);
PUSH1(None);
cfgs = glXChooseFBConfig(implicitSwapchainImpl->display,
DefaultScreen(implicitSwapchainImpl->display),
attribs, &nCfgs);
}
if (cfgs == NULL) {
ERR("Could not get a valid FBConfig for (%u,%s)/(%u,%s)\n",
BackBufferFormat, debug_d3dformat(BackBufferFormat),
StencilBufferFormat, debug_d3dformat(StencilBufferFormat));
} else {
#ifdef EXTRA_TRACES
int i;
for (i = 0; i < nCfgs; ++i) {
TRACE("for (%u,%s)/(%u,%s) found config[%d]@%p\n", BackBufferFormat,
debug_d3dformat(BackBufferFormat), StencilBufferFormat,
debug_d3dformat(StencilBufferFormat), i, cfgs[i]);
}
if (NULL != This->renderTarget) {
glFlush();
vcheckGLcall("glFlush");
/** This is only useful if the old render target was a swapchain,
* we need to supercede this with a function that displays
* the current buffer on the screen. This is easy to do in glx1.3 but
* we need to do copy-write pixels in glx 1.2.
************************************************/
glXSwapBuffers(implicitSwapChainImpl->display,
implicitSwapChainImpl->drawable);
printf("Hit Enter to get next frame ...\n");
getchar();
}
#endif
}
#undef PUSH1
#undef PUSH2
return cfgs;
}
/*****************************************************************************
* CreateContext
*
* Creates a new context for a window, or a pbuffer context.
* TODO: Merge this with AddContext
*
* Params:
* This: Device to activate the context for
* target: Surface this context will render to
* win: Taget window. NULL for a pbuffer
*
*****************************************************************************/
WineD3DContext *CreateContext(IWineD3DDeviceImpl *This, IWineD3DSurfaceImpl *target, Window win) {
Drawable drawable = win, oldDrawable;
XVisualInfo *visinfo = NULL;
GLXFBConfig *cfgs = NULL;
GLXContext ctx = NULL, oldCtx;
WineD3DContext *ret = NULL;
IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->swapchains[0];
TRACE("(%p): Creating a %s context for render target %p\n", This, win ? "onscreen" : "offscreen", target);
if(!win) {
int attribs[256];
int nAttribs = 0;
TRACE("Creating a pBuffer drawable for the new context\n");
cfgs = pbuffer_find_fbconfigs(This, swapchain, target);
if(!cfgs) {
ERR("Cannot find a frame buffer configuration for the pbuffer\n");
goto out;
}
attribs[nAttribs++] = GLX_PBUFFER_WIDTH;
attribs[nAttribs++] = target->currentDesc.Width;
attribs[nAttribs++] = GLX_PBUFFER_HEIGHT;
attribs[nAttribs++] = target->currentDesc.Height;
attribs[nAttribs++] = None;
visinfo = glXGetVisualFromFBConfig(swapchain->display, cfgs[0]);
if(!visinfo) {
ERR("Cannot find a visual for the pbuffer\n");
goto out;
}
drawable = glXCreatePbuffer(swapchain->display, cfgs[0], attribs);
if(!drawable) {
ERR("Cannot create a pbuffer\n");
goto out;
}
XFree(cfgs);
cfgs = NULL;
}
ctx = glXCreateContext(swapchain->display, visinfo,
This->numContexts ? This->contexts[0]->glCtx : NULL,
GL_TRUE);
if(!ctx) {
ERR("Failed to create a glX context\n");
if(drawable != win) glXDestroyPbuffer(swapchain->display, drawable);
goto out;
}
ret = AddContext(This, ctx, drawable);
if(!ret) {
ERR("Failed to add the newly created context to the context list\n");
glXDestroyContext(swapchain->display, ctx);
if(drawable != win) glXDestroyPbuffer(swapchain->display, drawable);
goto out;
}
ret->surface = (IWineD3DSurface *) target;
TRACE("Successfully created new context %p\n", ret);
/* Set up the context defaults */
oldCtx = glXGetCurrentContext();
oldDrawable = glXGetCurrentDrawable();
if(glXMakeCurrent(swapchain->display, drawable, ctx) == FALSE) {
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);
checkGLcall("glClearColor");
glClearIndex(0);
glClearDepth(1);
glClearStencil(0xffff);
checkGLcall("glClear");
glColor3f(1.0, 1.0, 1.0);
checkGLcall("glColor3f");
glEnable(GL_LIGHTING);
checkGLcall("glEnable");
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
checkGLcall("glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);");
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
checkGLcall("glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);");
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
checkGLcall("glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);");
glPixelStorei(GL_PACK_ALIGNMENT, SURFACE_ALIGNMENT);
checkGLcall("glPixelStorei(GL_PACK_ALIGNMENT, SURFACE_ALIGNMENT);");
glPixelStorei(GL_UNPACK_ALIGNMENT, SURFACE_ALIGNMENT);
checkGLcall("glPixelStorei(GL_UNPACK_ALIGNMENT, SURFACE_ALIGNMENT);");
glXMakeCurrent(swapchain->display, oldDrawable, oldCtx);
out:
if(visinfo) XFree(visinfo);
if(cfgs) XFree(cfgs);
return ret;
}
/*****************************************************************************
* DestroyContext
*
* Destroys a wineD3DContext
* TODO: Merge with DeleteContext
*
* Params:
* This: Device to activate the context for
* context: Context to destroy
*
*****************************************************************************/
void DestroyContext(IWineD3DDeviceImpl *This, WineD3DContext *context) {
IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->swapchains[0];
glXDestroyContext(swapchain->display, context->glCtx);
/* Assume pbuffer for now*/
glXDestroyPbuffer(swapchain->display, context->drawable);
DeleteContext(This, context);
}
/*****************************************************************************
* SetupForBlit
*
* Sets up a context for DirectDraw blitting.
......@@ -58,10 +317,12 @@ static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *contex
}
context->last_was_blit = TRUE;
/* TODO: Use a display list */
/* Disable shaders */
This->shader_backend->shader_cleanup((IWineD3DDevice *) This);
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VSHADER);
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_PIXELSHADER);
Context_MarkStateDirty(context, STATE_VSHADER);
Context_MarkStateDirty(context, STATE_PIXELSHADER);
/* Disable all textures. The caller can then bind a texture it wants to blit
* from
......@@ -86,9 +347,9 @@ static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *contex
checkGLcall("glDisable GL_TEXTURE_1D");
if(i < MAX_TEXTURES) {
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_TEXTURESTAGE(i, WINED3DTSS_COLOROP));
Context_MarkStateDirty(context, STATE_TEXTURESTAGE(i, WINED3DTSS_COLOROP));
}
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_SAMPLER(i));
Context_MarkStateDirty(context, STATE_SAMPLER(i));
}
GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
checkGLcall("glActiveTextureARB");
......@@ -108,7 +369,7 @@ static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *contex
checkGLcall("glMatrixMode(GL_TEXTURE)");
glLoadIdentity();
checkGLcall("glLoadIdentity()");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_TRANSFORM(WINED3DTS_TEXTURE0));
Context_MarkStateDirty(context, STATE_TRANSFORM(WINED3DTS_TEXTURE0));
if (GL_SUPPORT(EXT_TEXTURE_LOD_BIAS)) {
glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT,
......@@ -116,46 +377,46 @@ static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *contex
0.0);
checkGLcall("glTexEnvi GL_TEXTURE_LOD_BIAS_EXT ...");
}
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_SAMPLER(0));
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_TEXTURESTAGE(0, WINED3DTSS_COLOROP));
Context_MarkStateDirty(context, STATE_SAMPLER(0));
Context_MarkStateDirty(context, STATE_TEXTURESTAGE(0, WINED3DTSS_COLOROP));
/* Other misc states */
glDisable(GL_ALPHA_TEST);
checkGLcall("glDisable(GL_ALPHA_TEST)");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_ALPHATESTENABLE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_ALPHATESTENABLE));
glDisable(GL_LIGHTING);
checkGLcall("glDisable GL_LIGHTING");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_LIGHTING));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_LIGHTING));
glDisable(GL_DEPTH_TEST);
checkGLcall("glDisable GL_DEPTH_TEST");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_ZENABLE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_ZENABLE));
glDisable(GL_FOG);
checkGLcall("glDisable GL_FOG");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_FOGENABLE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_FOGENABLE));
glDisable(GL_BLEND);
checkGLcall("glDisable GL_BLEND");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_ALPHABLENDENABLE));
glDisable(GL_CULL_FACE);
checkGLcall("glDisable GL_CULL_FACE");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_CULLMODE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_CULLMODE));
glDisable(GL_STENCIL_TEST);
checkGLcall("glDisable GL_STENCIL_TEST");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_STENCILENABLE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_STENCILENABLE));
if(GL_SUPPORT(ARB_POINT_SPRITE)) {
glDisable(GL_POINT_SPRITE_ARB);
checkGLcall("glDisable GL_POINT_SPRITE_ARB");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_POINTSPRITEENABLE));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_POINTSPRITEENABLE));
}
glColorMask(GL_TRUE, GL_TRUE,GL_TRUE,GL_TRUE);
checkGLcall("glColorMask");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_CLIPPING));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_CLIPPING));
/* Setup transforms */
glMatrixMode(GL_MODELVIEW);
checkGLcall("glMatrixMode(GL_MODELVIEW)");
glLoadIdentity();
checkGLcall("glLoadIdentity()");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_TRANSFORM(WINED3DTS_WORLDMATRIX(0)));
Context_MarkStateDirty(context, STATE_TRANSFORM(WINED3DTS_WORLDMATRIX(0)));
glMatrixMode(GL_PROJECTION);
checkGLcall("glMatrixMode(GL_PROJECTION)");
......@@ -163,10 +424,10 @@ static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *contex
checkGLcall("glLoadIdentity()");
glOrtho(0, width, height, 0, 0.0, -1.0);
checkGLcall("glOrtho");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_TRANSFORM(WINED3DTS_PROJECTION));
Context_MarkStateDirty(context, STATE_TRANSFORM(WINED3DTS_PROJECTION));
context->last_was_rhw = TRUE;
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VDECL); /* because of last_was_rhw = TRUE */
Context_MarkStateDirty(context, STATE_VDECL); /* because of last_was_rhw = TRUE */
glDisable(GL_CLIP_PLANE0); checkGLcall("glDisable(clip plane 0)");
glDisable(GL_CLIP_PLANE1); checkGLcall("glDisable(clip plane 1)");
......@@ -174,11 +435,11 @@ static inline void SetupForBlit(IWineD3DDeviceImpl *This, WineD3DContext *contex
glDisable(GL_CLIP_PLANE3); checkGLcall("glDisable(clip plane 3)");
glDisable(GL_CLIP_PLANE4); checkGLcall("glDisable(clip plane 4)");
glDisable(GL_CLIP_PLANE5); checkGLcall("glDisable(clip plane 5)");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(WINED3DRS_CLIPPING));
Context_MarkStateDirty(context, STATE_RENDER(WINED3DRS_CLIPPING));
glViewport(0, 0, width, height);
checkGLcall("glViewport");
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VIEWPORT);
Context_MarkStateDirty(context, STATE_VIEWPORT);
}
/*****************************************************************************
......@@ -199,15 +460,120 @@ void ActivateContext(IWineD3DDeviceImpl *This, IWineD3DSurface *target, ContextU
int i;
DWORD dirtyState, idx;
BYTE shift;
WineD3DContext *context;
WineD3DContext *context = This->activeContext;
BOOL oldRenderOffscreen = This->render_offscreen;
TRACE("(%p): Selecting context for render target %p, thread %d\n", This, target, tid);
/* TODO: Render target selection */
/* TODO: Thread selection */
if(This->lastActiveRenderTarget != target) {
IWineD3DSwapChain *swapchain = NULL;
HRESULT hr;
BOOL readTexture = wined3d_settings.offscreen_rendering_mode != ORM_FBO && This->render_offscreen;
hr = IWineD3DSurface_GetContainer(target, &IID_IWineD3DSwapChain, (void **) &swapchain);
if(hr == WINED3D_OK && swapchain) {
TRACE("Rendering onscreen\n");
context = ((IWineD3DSwapChainImpl *) swapchain)->context;
This->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
*/
if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && oldRenderOffscreen) {
set_render_target_fbo((IWineD3DDevice *) This, 0, target);
}
IWineD3DSwapChain_Release(swapchain);
if(oldRenderOffscreen) {
Context_MarkStateDirty(context, WINED3DRS_CULLMODE);
}
} 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) {
context = This->activeContext;
} else {
/* This may happen if the app jumps streight into offscreen rendering
* Start using the context of the primary swapchain
*/
context = ((IWineD3DSwapChainImpl *) This->swapchains[0])->context;
}
set_render_target_fbo((IWineD3DDevice *) This, 0, target);
break;
/* TODO: Activate the opengl context */
context = This->contexts[This->activeContext];
case ORM_PBUFFER:
{
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);
}
This->pbufferContext = CreateContext(This, targetimpl, 0 /* Window */);
This->pbufferWidth = targetimpl->currentDesc.Width;
This->pbufferHeight = targetimpl->currentDesc.Height;
}
if(This->pbufferContext) {
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;
}
}
case ORM_BACKBUFFER:
/* Stay with the currently active context for back buffer rendering */
if(This->activeContext) {
context = This->activeContext;
} else {
/* This may happen if the app jumps streight into offscreen rendering
* Start using the context of the primary swapchain
*/
context = ((IWineD3DSwapChainImpl *) This->swapchains[0])->context;
}
break;
}
if(!oldRenderOffscreen) {
Context_MarkStateDirty(context, WINED3DRS_CULLMODE);
}
}
if (readTexture) {
/* Do that before switching the context:
* Read the back buffer of the old drawable into the destination texture
*/
IWineD3DSurface_SetPBufferState(This->lastActiveRenderTarget, TRUE /* inPBuffer */, FALSE /* inTexture */);
IWineD3DSurface_AddDirtyRect(This->lastActiveRenderTarget, NULL);
IWineD3DSurface_PreLoad(This->lastActiveRenderTarget);
IWineD3DSurface_SetPBufferState(This->lastActiveRenderTarget, FALSE /* inPBuffer */, FALSE /* inTexture */);
}
This->lastActiveRenderTarget = target;
if(oldRenderOffscreen != This->render_offscreen && This->depth_copy_state != WINED3D_DCS_NO_COPY) {
This->depth_copy_state = WINED3D_DCS_COPY;
}
} else {
/* Stick to the old context */
context = This->activeContext;
}
/* Activate the opengl context */
if(context != This->activeContext) {
Bool ret;
TRACE("Switching gl ctx to %p, drawable=%ld, ctx=%p\n", context, context->drawable, context->glCtx);
ret = glXMakeCurrent(((IWineD3DSwapChainImpl *) This->swapchains[0])->display, context->drawable, context->glCtx);
if(ret == FALSE) {
ERR("Failed to activate the new context\n");
}
This->activeContext = context;
}
switch(usage) {
case CTXUSAGE_RESOURCELOAD:
......@@ -237,3 +603,99 @@ void ActivateContext(IWineD3DDeviceImpl *This, IWineD3DSurface *target, ContextU
FIXME("Unexpected context usage requested\n");
}
}
/*****************************************************************************
* AddContext
*
* Adds a new WineD3DContext keeping track of a glx context and drawable.
* Creating the context and drawable is up to the swapchain or texture. This
* function adds it to the context array to allow ActivateContext to find it
* and keep track of dirty states. (Later on it will also be able to duplicate
* the context for another thread).
*
* This method is not called in performance-critical code paths, only when a
* new render target or swapchain is created. Thus performance is not an issue
* here.
*
* Params:
* This: Device to add the context for
* glCtx: glX context to add
* drawable: drawable used with this context.
*
*****************************************************************************/
WineD3DContext *AddContext(IWineD3DDeviceImpl *This, GLXContext glCtx, Drawable drawable) {
WineD3DContext **oldArray = This->contexts;
DWORD state;
This->contexts = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->contexts) * (This->numContexts + 1));
if(This->contexts == NULL) {
ERR("Unable to grow the context array\n");
This->contexts = oldArray;
return NULL;
}
if(oldArray) {
memcpy(This->contexts, oldArray, sizeof(*This->contexts) * This->numContexts);
}
This->contexts[This->numContexts] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WineD3DContext));
if(This->contexts[This->numContexts] == NULL) {
ERR("Unable to allocate a new context\n");
HeapFree(GetProcessHeap(), 0, This->contexts);
This->contexts = oldArray;
return NULL;
}
This->contexts[This->numContexts]->glCtx = glCtx;
This->contexts[This->numContexts]->drawable = drawable;
HeapFree(GetProcessHeap(), 0, oldArray);
/* 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->numContexts++;
TRACE("Created context %p\n", This->contexts[This->numContexts - 1]);
return This->contexts[This->numContexts - 1];
}
/*****************************************************************************
* DeleteContext
*
* Removes a context from the context manager. The opengl context is not
* destroyed or unset. context is not a valid pointer after that call.
*
* Simmilar to the former call this isn't a performance critical function.
*
* Params:
* This: Device to activate the context for
* context: Context to remove
*
*****************************************************************************/
void DeleteContext(IWineD3DDeviceImpl *This, WineD3DContext *context) {
UINT t, s;
WineD3DContext **oldArray = This->contexts;
TRACE("Removing ctx %p\n", context);
This->numContexts--;
if(This->numContexts) {
This->contexts = HeapAlloc(GetProcessHeap(), 0, sizeof(*This->contexts) * This->numContexts);
if(!This->contexts) {
ERR("Cannot allocate a new context array, PANIC!!!\n");
}
t = 0;
for(s = 0; s < This->numContexts; s++) {
if(oldArray[s] == context) continue;
This->contexts[t] = oldArray[s];
t++;
}
} else {
This->contexts = NULL;
}
HeapFree(GetProcessHeap(), 0, context);
HeapFree(GetProcessHeap(), 0, oldArray);
}
......@@ -350,16 +350,10 @@ static ULONG WINAPI IWineD3DDeviceImpl_Release(IWineD3DDevice *iface) {
TRACE("(%p) : Releasing from %d\n", This, refCount + 1);
if (!refCount) {
UINT i;
if (This->fbo) {
GL_EXTCALL(glDeleteFramebuffersEXT(1, &This->fbo));
}
for(i = 0; i < This->numContexts; i++) {
HeapFree(GetProcessHeap(), 0, This->contexts[i]);
}
HeapFree(GetProcessHeap(), 0, This->contexts);
HeapFree(GetProcessHeap(), 0, This->render_targets);
HeapFree(GetProcessHeap(), 0, This->draw_buffers);
......@@ -372,7 +366,7 @@ static ULONG WINAPI IWineD3DDeviceImpl_Release(IWineD3DDevice *iface) {
if (This->vs_selected_mode == SHADER_GLSL ||
This->ps_selected_mode == SHADER_GLSL)
delete_glsl_shader_list(iface);
/* Release the update stateblock */
if(IWineD3DStateBlock_Release((IWineD3DStateBlock *)This->updateStateBlock) > 0){
if(This->updateStateBlock != This->stateBlock)
......@@ -396,6 +390,7 @@ static ULONG WINAPI IWineD3DDeviceImpl_Release(IWineD3DDevice *iface) {
dumpResources(This->resources);
}
if(This->contexts) ERR("Context array not freed!\n");
IWineD3D_Release(This->wineD3D);
This->wineD3D = NULL;
......@@ -1354,7 +1349,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateAdditionalSwapChain(IWineD3DDevic
IWineD3DSwapChainImpl *object; /** NOTE: impl ref allowed since this is a create function **/
int num;
XVisualInfo template;
GLXContext oldContext;
GLXContext oldContext, newCtx;
Drawable oldDrawable;
HRESULT hr = WINED3D_OK;
......@@ -1467,23 +1462,19 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateAdditionalSwapChain(IWineD3DDevic
}
/* Now choose a similar visual ID*/
}
#ifdef USE_CONTEXT_MANAGER
/** TODO: use a context mamager **/
#endif
{
IWineD3DSwapChain *implSwapChain;
if (WINED3D_OK != IWineD3DDevice_GetSwapChain(iface, 0, &implSwapChain)) {
/* The first time around we create the context that is shared with all other swapchains and render targets */
object->glCtx = glXCreateContext(object->display, object->visInfo, NULL, GL_TRUE);
newCtx = glXCreateContext(object->display, object->visInfo, NULL, GL_TRUE);
TRACE("Creating implicit context for vis %p, hwnd %p\n", object->display, object->visInfo);
} else {
TRACE("Creating context for vis %p, hwnd %p\n", object->display, object->visInfo);
/* TODO: don't use Impl structures outside of create functions! (a context manager will replace the ->glCtx) */
/* and create a new context with the implicit swapchains context as the shared context */
object->glCtx = glXCreateContext(object->display, object->visInfo, ((IWineD3DSwapChainImpl *)implSwapChain)->glCtx, GL_TRUE);
newCtx = glXCreateContext(object->display, object->visInfo, ((IWineD3DSwapChainImpl *)implSwapChain)->context->glCtx, GL_TRUE);
IWineD3DSwapChain_Release(implSwapChain);
}
}
......@@ -1494,12 +1485,12 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateAdditionalSwapChain(IWineD3DDevic
LEAVE_GL();
if (!object->glCtx) {
if (!newCtx) {
ERR("Failed to create GLX context\n");
return WINED3DERR_NOTAVAILABLE;
} else {
TRACE("Context created (HWND=%p, glContext=%p, Window=%ld, VisInfo=%p)\n",
object->win_handle, object->glCtx, object->win, object->visInfo);
object->win_handle, newCtx, object->win, object->visInfo);
}
/*********************
......@@ -1694,10 +1685,12 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateAdditionalSwapChain(IWineD3DDevic
/*********************
* init the default renderTarget management
*******************/
object->drawable = object->win;
object->render_ctx = object->glCtx;
object->render_ctx = newCtx;
if (hr == WINED3D_OK) {
/* Register the context */
object->context = AddContext(This, newCtx, object->win);
if (hr == WINED3D_OK && object->context) {
/*********************
* Setup some defaults and clear down the buffers
*******************/
......@@ -1706,9 +1699,9 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateAdditionalSwapChain(IWineD3DDevic
oldContext = glXGetCurrentContext();
oldDrawable = glXGetCurrentDrawable();
TRACE("Activating context (display %p context %p drawable %ld)!\n", object->display, object->glCtx, object->win);
if (glXMakeCurrent(object->display, object->win, object->glCtx) == False) {
ERR("Error in setting current context (display %p context %p drawable %ld)!\n", object->display, object->glCtx, object->win);
TRACE("Activating context (display %p context %p drawable %ld)!\n", object->display, newCtx, object->win);
if (glXMakeCurrent(object->display, object->win, newCtx) == False) {
ERR("Error in setting current context (display %p context %p drawable %ld)!\n", object->display, newCtx, object->win);
}
checkGLcall("glXMakeCurrent");
......@@ -1779,14 +1772,13 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateAdditionalSwapChain(IWineD3DDevic
/* NOTE: don't clean up the depthstencil buffer because it belongs to the device */
/* Clean up the context */
/* check that we are the current context first (we shouldn't be though!) */
if (object->glCtx != 0) {
if(glXGetCurrentContext() == object->glCtx) {
if (newCtx != 0) {
if(glXGetCurrentContext() == newCtx) {
glXMakeCurrent(object->display, None, NULL);
}
glXDestroyContext(object->display, object->glCtx);
glXDestroyContext(object->display, newCtx);
}
HeapFree(GetProcessHeap(), 0, object);
}
return hr;
......@@ -1967,12 +1959,16 @@ static HRESULT WINAPI IWineD3DDeviceImpl_Init3D(IWineD3DDevice *iface, WINED3DPR
if(swapchain->backBuffer && swapchain->backBuffer[0]) {
TRACE("Setting rendertarget to %p\n", swapchain->backBuffer);
This->render_targets[0] = swapchain->backBuffer[0];
This->lastActiveRenderTarget = swapchain->backBuffer[0];
}
else {
TRACE("Setting rendertarget to %p\n", swapchain->frontBuffer);
This->render_targets[0] = swapchain->frontBuffer;
This->lastActiveRenderTarget = swapchain->frontBuffer;
}
IWineD3DSurface_AddRef(This->render_targets[0]);
This->activeContext = swapchain->context;
/* Depth Stencil support */
This->stencilBufferTarget = This->depthStencilBuffer;
if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
......@@ -2020,15 +2016,6 @@ static HRESULT WINAPI IWineD3DDeviceImpl_Init3D(IWineD3DDevice *iface, WINED3DPR
/* Clear the screen */
IWineD3DDevice_Clear((IWineD3DDevice *) This, 0, NULL, WINED3DCLEAR_STENCIL|WINED3DCLEAR_ZBUFFER|WINED3DCLEAR_TARGET, 0x00, 1.0, 0);
/* Mark all states dirty. The Setters will not mark a state dirty when the new value is equal to the old value
* This might create a problem in 2 situations:
* ->The D3D default value is 0, but the opengl default value is something else
* ->D3D7 unintialized D3D and reinitializes it. This way the context is destroyed, be the stateblock unchanged
*/
for(state = 0; state <= STATE_HIGHEST; state++) {
IWineD3DDeviceImpl_MarkStateDirty(This, state);
}
This->d3d_initialized = TRUE;
return WINED3D_OK;
}
......@@ -2041,6 +2028,9 @@ static HRESULT WINAPI IWineD3DDeviceImpl_Uninit3D(IWineD3DDevice *iface, D3DCB_D
if(!This->d3d_initialized) return WINED3DERR_INVALIDCALL;
/* Delete the pbuffer context if there is any */
if(This->pbufferContext) DestroyContext(This, This->pbufferContext);
/* Delete the mouse cursor texture */
if(This->cursorTexture) {
ENTER_GL();
......@@ -4576,28 +4566,8 @@ static HRESULT WINAPI IWineD3DDeviceImpl_EndScene(IWineD3DDevice *iface) {
/* We only have to do this if we need to read the, swapbuffers performs a flush for us */
glFlush();
checkGLcall("glFlush");
TRACE("End Scene\n");
/* If we're using FBOs this isn't needed */
if (wined3d_settings.offscreen_rendering_mode != ORM_FBO && This->render_targets[0] != NULL) {
/* If the container of the rendertarget is a texture then we need to save the data from the pbuffer */
IUnknown *targetContainer = NULL;
if (WINED3D_OK == IWineD3DSurface_GetContainer(This->render_targets[0], &IID_IWineD3DBaseTexture, (void **)&targetContainer)
|| WINED3D_OK == IWineD3DSurface_GetContainer(This->render_targets[0], &IID_IWineD3DDevice, (void **)&targetContainer)) {
TRACE("(%p) : Texture rendertarget %p\n", This ,This->render_targets[0]);
/** always dirtify for now. we must find a better way to see that surface have been modified
(Modifications should will only occur via draw-primitive, but we do need better locking
switching to render-to-texture should remove the overhead though.
*/
IWineD3DSurface_SetPBufferState(This->render_targets[0], TRUE /* inPBuffer */, FALSE /* inTexture */);
IWineD3DSurface_AddDirtyRect(This->render_targets[0], NULL);
IWineD3DSurface_PreLoad(This->render_targets[0]);
IWineD3DSurface_SetPBufferState(This->render_targets[0], FALSE /* inPBuffer */, FALSE /* inTexture */);
IUnknown_Release(targetContainer);
}
}
LEAVE_GL();
This->inScene = FALSE;
return WINED3D_OK;
}
......@@ -5644,7 +5614,7 @@ static void set_depth_stencil_fbo(IWineD3DDevice *iface, IWineD3DSurface *depth_
}
}
static void set_render_target_fbo(IWineD3DDevice *iface, DWORD idx, IWineD3DSurface *render_target) {
void set_render_target_fbo(IWineD3DDevice *iface, DWORD idx, IWineD3DSurface *render_target) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
IWineD3DSurfaceImpl *rtimpl = (IWineD3DSurfaceImpl *)render_target;
......@@ -5690,13 +5660,8 @@ static void set_render_target_fbo(IWineD3DDevice *iface, DWORD idx, IWineD3DSurf
}
}
/* internal static helper functions */
static HRESULT WINAPI IWineD3DDeviceImpl_ActiveRender(IWineD3DDevice* iface,
IWineD3DSurface *RenderSurface);
static HRESULT WINAPI IWineD3DDeviceImpl_SetRenderTarget(IWineD3DDevice *iface, DWORD RenderTargetIndex, IWineD3DSurface *pRenderTarget) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
HRESULT hr = WINED3D_OK;
WINED3DVIEWPORT viewport;
TRACE("(%p) : Setting rendertarget %d to %p\n", This, RenderTargetIndex, pRenderTarget);
......@@ -5717,51 +5682,23 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetRenderTarget(IWineD3DDevice *iface,
FIXME("Trying to set render target 0 to NULL\n");
return WINED3DERR_INVALIDCALL;
}
/* TODO: replace Impl* usage with interface usage */
if (pRenderTarget && !((IWineD3DSurfaceImpl *)pRenderTarget)->resource.usage & WINED3DUSAGE_RENDERTARGET) {
FIXME("(%p)Trying to set the render target to a surface(%p) that wasn't created with a usage of WINED3DUSAGE_RENDERTARGET\n",This ,pRenderTarget);
return WINED3DERR_INVALIDCALL;
}
/** TODO: check that the depth stencil format matches the render target, this is only done in debug
* builds, but I think wine counts as a 'debug' build for now.
******************************/
/* If we are trying to set what we already have, don't bother */
if (pRenderTarget == This->render_targets[RenderTargetIndex]) {
TRACE("Trying to do a NOP SetRenderTarget operation\n");
} else {
/* Otherwise, set the render target up */
if (This->inScene) {
/* EndScene takes care for loading the pbuffer into the texture. Call EndScene and BeginScene until we have better offscreen handling */
IWineD3DDevice_EndScene(iface);
IWineD3DDevice_BeginScene(iface);
}
TRACE("clearing renderer\n");
/* IWineD3DDeviceImpl_CleanRender(iface); */
/* OpenGL doesn't support 'sharing' of the stencilBuffer so we may incure an extra memory overhead
depending on the renter target implementation being used.
A shared context implementation will share all buffers between all rendertargets (including swapchains),
implementations that use separate pbuffers for different swapchains or rendertargets will have to duplicate the
stencil buffer and incure an extra memory overhead */
if (RenderTargetIndex == 0) {
hr = IWineD3DDeviceImpl_ActiveRender(iface, pRenderTarget);
} else {
hr = WINED3D_OK;
}
/* Replace the render target */
if (This->render_targets[RenderTargetIndex]) IWineD3DSurface_Release(This->render_targets[RenderTargetIndex]);
This->render_targets[RenderTargetIndex] = pRenderTarget;
if (pRenderTarget) IWineD3DSurface_AddRef(pRenderTarget);
if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
set_render_target_fbo(iface, RenderTargetIndex, pRenderTarget);
}
return WINED3D_OK;
}
if(pRenderTarget) IWineD3DSurface_AddRef(pRenderTarget);
if(This->render_targets[RenderTargetIndex]) IWineD3DSurface_Release(This->render_targets[RenderTargetIndex]);
This->render_targets[RenderTargetIndex] = pRenderTarget;
if (SUCCEEDED(hr)) {
/* Render target 0 is special */
if(RenderTargetIndex == 0) {
/* Finally, reset the viewport as the MSDN states. */
/* TODO: Replace impl usage */
viewport.Height = ((IWineD3DSurfaceImpl *)This->render_targets[0])->currentDesc.Height;
viewport.Width = ((IWineD3DSurfaceImpl *)This->render_targets[0])->currentDesc.Width;
viewport.X = 0;
......@@ -5769,10 +5706,18 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetRenderTarget(IWineD3DDevice *iface,
viewport.MaxZ = 1.0f;
viewport.MinZ = 0.0f;
IWineD3DDeviceImpl_SetViewport(iface, &viewport);
/* Activate the new render target for now. This shouldn't stay here, but is needed until all methods using gl activate the
* ctx properly.
* Use resourceload usage, this will just set the drawables and context but not apply any states. The stateblock may be
* incomplete or incorrect when SetRenderTarget is called. DrawPrim() will apply the states when it is called.
*/
ActivateContext(This, This->render_targets[0], CTXUSAGE_RESOURCELOAD);
} else {
FIXME("Unknown error setting the render target\n");
/* We only get more than 1 render target with fbos, so no need to check the offscreen rendering method */
set_render_target_fbo(iface, RenderTargetIndex, pRenderTarget);
}
return hr;
return WINED3D_OK;
}
static HRESULT WINAPI IWineD3DDeviceImpl_SetDepthStencilSurface(IWineD3DDevice *iface, IWineD3DSurface *pNewZStencil) {
......@@ -5810,445 +5755,6 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetDepthStencilSurface(IWineD3DDevice *
return hr;
}
#ifdef GL_VERSION_1_3
/* Internal functions not in DirectX */
/** TODO: move this off to the opengl context manager
*(the swapchain doesn't need to know anything about offscreen rendering!)
****************************************************/
static HRESULT WINAPI IWineD3DDeviceImpl_CleanRender(IWineD3DDevice* iface, IWineD3DSwapChainImpl *swapchain)
{
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
TRACE("(%p), %p\n", This, swapchain);
if (swapchain->win != swapchain->drawable) {
/* Set everything back the way it ws */
swapchain->render_ctx = swapchain->glCtx;
swapchain->drawable = swapchain->win;
}
return WINED3D_OK;
}
/* TODO: move this off into a context manager so that GLX_ATI_render_texture and other types of surface can be used. */
static HRESULT WINAPI IWineD3DDeviceImpl_FindGLContext(IWineD3DDevice *iface, IWineD3DSurface *pSurface, glContext **context) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
int i;
unsigned int width;
unsigned int height;
WINED3DFORMAT format;
WINED3DSURFACE_DESC surfaceDesc;
memset(&surfaceDesc, 0, sizeof(surfaceDesc));
surfaceDesc.Width = &width;
surfaceDesc.Height = &height;
surfaceDesc.Format = &format;
IWineD3DSurface_GetDesc(pSurface, &surfaceDesc);
*context = NULL;
/* I need a get width/height function (and should do something with the format) */
for (i = 0; i < CONTEXT_CACHE; ++i) {
/** NOTE: the contextCache[i].pSurface == pSurface check ceates onepbuffer per surface
ATI cards don't destroy pbuffers, but as soon as resource releasing callbacks are inplace
the pSurface can be set to 0 allowing it to be reused from cache **/
if (This->contextCache[i].Width == width && This->contextCache[i].Height == height
&& (!pbuffer_per_surface || This->contextCache[i].pSurface == pSurface || This->contextCache[i].pSurface == NULL)) {
*context = &This->contextCache[i];
break;
}
if (This->contextCache[i].Width == 0) {
This->contextCache[i].pSurface = pSurface;
This->contextCache[i].Width = width;
This->contextCache[i].Height = height;
*context = &This->contextCache[i];
break;
}
}
if (i == CONTEXT_CACHE) {
int minUsage = 0x7FFFFFFF; /* MAX_INT */
glContext *dropContext = 0;
for (i = 0; i < CONTEXT_CACHE; i++) {
if (This->contextCache[i].usedcount < minUsage) {
dropContext = &This->contextCache[i];
minUsage = This->contextCache[i].usedcount;
}
}
/* clean up the context (this doesn't work for ATI at the moment */
#if 0
glXDestroyContext(swapchain->display, dropContext->context);
glXDestroyPbuffer(swapchain->display, dropContext->drawable);
#endif
FIXME("Leak\n");
dropContext->Width = 0;
dropContext->pSurface = pSurface;
*context = dropContext;
} else {
if (++This->contextCache[i].usedcount == 0x7FFFFFFF /* MAX_INT */ - 1 ) {
for (i = 0; i < CONTEXT_CACHE; i++) {
This->contextCache[i].usedcount = max(0, This->contextCache[i].usedcount - (0x7FFFFFFF /* MAX_INT */ >> 1));
}
}
}
if (*context != NULL)
return WINED3D_OK;
else
return E_OUTOFMEMORY;
}
#endif
/* Reapply the device stateblock */
static void device_reapply_stateblock(IWineD3DDeviceImpl* This) {
BOOL oldRecording;
IWineD3DStateBlockImpl *oldUpdateStateBlock;
DWORD i;
/* Disable recording */
oldUpdateStateBlock = This->updateStateBlock;
oldRecording= This->isRecordingState;
This->isRecordingState = FALSE;
This->updateStateBlock = This->stateBlock;
/* Reapply the state block */
IWineD3DStateBlock_Apply((IWineD3DStateBlock *)This->stateBlock);
/* Temporaryily mark all render states dirty to force reapplication
* until the context management for is integrated with the state management
* The same for the pixel shader, vertex declaration and vertex shader
* Sampler states and texture stage states are marked
* dirty my StateBlock::Apply already.
*/
for(i = 1; i < WINEHIGHEST_RENDER_STATE; i++) {
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_RENDER(i));
}
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_PIXELSHADER);
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VDECL);
IWineD3DDeviceImpl_MarkStateDirty(This, STATE_VSHADER);
/* Restore recording */
This->isRecordingState = oldRecording;
This->updateStateBlock = oldUpdateStateBlock;
}
/* Set offscreen rendering. When rendering offscreen the surface will be
* rendered upside down to compensate for the fact that D3D texture coordinates
* are flipped compared to GL texture coordinates. The cullmode is affected by
* this, so it must be updated. To update the cullmode stateblock recording has
* to be temporarily disabled. The new state management code will hopefully
* make this unnecessary */
static void device_render_to_texture(IWineD3DDeviceImpl* This, BOOL isTexture) {
BOOL oldRecording;
IWineD3DStateBlockImpl *oldUpdateStateBlock;
/* Nothing to update, return. */
if (This->render_offscreen == isTexture) return;
/* Disable recording */
oldUpdateStateBlock = This->updateStateBlock;
oldRecording= This->isRecordingState;
This->isRecordingState = FALSE;
This->updateStateBlock = This->stateBlock;
This->render_offscreen = isTexture;
if (This->depth_copy_state != WINED3D_DCS_NO_COPY) {
This->depth_copy_state = WINED3D_DCS_COPY;
}
This->contexts[0]->last_was_rhw = FALSE;
/* Viewport state will reapply the projection matrix for now */
IWineD3DDeviceImpl_MarkStateDirty(This, WINED3DRS_CULLMODE);
/* Restore recording */
This->isRecordingState = oldRecording;
This->updateStateBlock = oldUpdateStateBlock;
}
/* Returns an array of compatible FBconfig(s).
* The array must be freed with XFree. Requires ENTER_GL() */
static GLXFBConfig* device_find_fbconfigs(
IWineD3DDeviceImpl* This,
IWineD3DSwapChainImpl* implicitSwapchainImpl,
IWineD3DSurface* RenderSurface) {
GLXFBConfig* cfgs = NULL;
int nCfgs = 0;
int attribs[256];
int nAttribs = 0;
IWineD3DSurface *StencilSurface = This->stencilBufferTarget;
WINED3DFORMAT BackBufferFormat = ((IWineD3DSurfaceImpl *) RenderSurface)->resource.format;
WINED3DFORMAT StencilBufferFormat = (NULL != StencilSurface) ? ((IWineD3DSurfaceImpl *) StencilSurface)->resource.format : 0;
/**TODO:
if StencilSurface == NULL && zBufferTarget != NULL then switch the zbuffer off,
it StencilSurface != NULL && zBufferTarget == NULL switch it on
*/
#define PUSH1(att) attribs[nAttribs++] = (att);
#define PUSH2(att,value) attribs[nAttribs++] = (att); attribs[nAttribs++] = (value);
/* PUSH2(GLX_BIND_TO_TEXTURE_RGBA_ATI, True); examples of this are few and far between (but I've got a nice working one!)*/
PUSH2(GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT);
PUSH2(GLX_X_RENDERABLE, TRUE);
PUSH2(GLX_DOUBLEBUFFER, TRUE);
TRACE("calling makeglcfg\n");
D3DFmtMakeGlCfg(BackBufferFormat, StencilBufferFormat, attribs, &nAttribs, FALSE /* alternate */);
PUSH1(None);
TRACE("calling chooseFGConfig\n");
cfgs = glXChooseFBConfig(implicitSwapchainImpl->display,
DefaultScreen(implicitSwapchainImpl->display),
attribs, &nCfgs);
if (cfgs == NULL) {
/* OK we didn't find the exact config, so use any reasonable match */
/* TODO: fill in the 'requested' and 'current' depths, and make sure that's
why we failed. */
static BOOL show_message = TRUE;
if (show_message) {
ERR("Failed to find exact match, finding alternative but you may "
"suffer performance issues, try changing xfree's depth to match the requested depth\n");
show_message = FALSE;
}
nAttribs = 0;
PUSH2(GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT);
/* PUSH2(GLX_X_RENDERABLE, TRUE); */
PUSH2(GLX_RENDER_TYPE, GLX_RGBA_BIT);
PUSH2(GLX_DOUBLEBUFFER, FALSE);
TRACE("calling makeglcfg\n");
D3DFmtMakeGlCfg(BackBufferFormat, StencilBufferFormat, attribs, &nAttribs, TRUE /* alternate */);
PUSH1(None);
cfgs = glXChooseFBConfig(implicitSwapchainImpl->display,
DefaultScreen(implicitSwapchainImpl->display),
attribs, &nCfgs);
}
if (cfgs == NULL) {
ERR("Could not get a valid FBConfig for (%u,%s)/(%u,%s)\n",
BackBufferFormat, debug_d3dformat(BackBufferFormat),
StencilBufferFormat, debug_d3dformat(StencilBufferFormat));
} else {
#ifdef EXTRA_TRACES
int i;
for (i = 0; i < nCfgs; ++i) {
TRACE("for (%u,%s)/(%u,%s) found config[%d]@%p\n", BackBufferFormat,
debug_d3dformat(BackBufferFormat), StencilBufferFormat,
debug_d3dformat(StencilBufferFormat), i, cfgs[i]);
}
if (NULL != This->renderTarget) {
glFlush();
vcheckGLcall("glFlush");
/** This is only useful if the old render target was a swapchain,
* we need to supercede this with a function that displays
* the current buffer on the screen. This is easy to do in glx1.3 but
* we need to do copy-write pixels in glx 1.2.
************************************************/
glXSwapBuffers(implicitSwapChainImpl->display,
implicitSwapChainImpl->drawable);
printf("Hit Enter to get next frame ...\n");
getchar();
}
#endif
}
#undef PUSH1
#undef PUSH2
return cfgs;
}
/** FIXME: This is currently used called whenever SetRenderTarget or SetStencilBuffer are called
* the functionality needs splitting up so that we don't do more than we should do.
* this only seems to impact performance a little.
******************************/
static HRESULT WINAPI IWineD3DDeviceImpl_ActiveRender(IWineD3DDevice* iface,
IWineD3DSurface *RenderSurface) {
/**
* Currently only active for GLX >= 1.3
* for others versions we'll have to use GLXPixmaps
*
* normally we must test GLX_VERSION_1_3 but nvidia headers are not correct
* as they implement GLX 1.3 but only define GLX_VERSION_1_2
* so only check OpenGL version
* ..........................
* I don't believe that it is a problem with NVidia headers,
* XFree only supports GLX1.2, nVidia (and ATI to some extent) provide 1.3 functions
* in GLX 1.2, there is no mention of the correct way to tell if the extensions are provided.
* ATI Note:
* Your application will report GLX version 1.2 on glXQueryVersion.
* However, it is safe to call the GLX 1.3 functions as described below.
*/
#if defined(GL_VERSION_1_3)
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
GLXFBConfig* cfgs = NULL;
IWineD3DSwapChain *currentSwapchain;
IWineD3DSwapChainImpl *currentSwapchainImpl;
IWineD3DSwapChain *implicitSwapchain;
IWineD3DSwapChainImpl *implicitSwapchainImpl;
IWineD3DSwapChain *renderSurfaceSwapchain;
IWineD3DSwapChainImpl *renderSurfaceSwapchainImpl;
/* Obtain a reference to the device implicit swapchain,
* the swapchain of the current render target,
* and the swapchain of the new render target.
* Fallback to device implicit swapchain if the current render target doesn't have one */
IWineD3DDevice_GetSwapChain(iface, 0, &implicitSwapchain);
IWineD3DSurface_GetContainer(RenderSurface, &IID_IWineD3DSwapChain, (void**) &renderSurfaceSwapchain);
IWineD3DSurface_GetContainer(This->render_targets[0], &IID_IWineD3DSwapChain, (void **)&currentSwapchain);
if (currentSwapchain == NULL)
IWineD3DDevice_GetSwapChain(iface, 0, &currentSwapchain);
currentSwapchainImpl = (IWineD3DSwapChainImpl*) currentSwapchain;
implicitSwapchainImpl = (IWineD3DSwapChainImpl*) implicitSwapchain;
renderSurfaceSwapchainImpl = (IWineD3DSwapChainImpl*) renderSurfaceSwapchain;
ENTER_GL();
/**
* TODO: remove the use of IWineD3DSwapChainImpl, a context manager will help since it will replace the
* renderTarget = swapchain->backBuffer[i] bit and anything to do with *glContexts
**********************************************************************/
if (renderSurfaceSwapchain != NULL) {
/* We also need to make sure that the lights &co are also in the context of the swapchains */
/* FIXME: If the render target gets sent to the frontBuffer, should we be presenting it raw? */
TRACE("making swapchain active\n");
if (RenderSurface != This->render_targets[0]) {
BOOL backbuf = FALSE;
int i;
for(i = 0; i < renderSurfaceSwapchainImpl->presentParms.BackBufferCount; i++) {
if(RenderSurface == renderSurfaceSwapchainImpl->backBuffer[i]) {
backbuf = TRUE;
break;
}
}
if (backbuf) {
} else {
/* This could be flagged so that some operations work directly with the front buffer */
FIXME("Attempting to set the renderTarget to the frontBuffer\n");
}
if (glXMakeCurrent(renderSurfaceSwapchainImpl->display,
renderSurfaceSwapchainImpl->win,
renderSurfaceSwapchainImpl->glCtx) == False) {
TRACE("Error in setting current context: context %p drawable %ld !\n",
implicitSwapchainImpl->glCtx, implicitSwapchainImpl->win);
}
checkGLcall("glXMakeContextCurrent");
/* Clean up the old context */
IWineD3DDeviceImpl_CleanRender(iface, currentSwapchainImpl);
/* Reapply the stateblock, and set the device not to render to texture */
device_reapply_stateblock(This);
device_render_to_texture(This, FALSE);
}
/* Offscreen rendering: PBuffers (currently disabled).
* Also note that this path is never reached if FBOs are supported */
} else if (wined3d_settings.offscreen_rendering_mode == ORM_PBUFFER &&
(cfgs = device_find_fbconfigs(This, implicitSwapchainImpl, RenderSurface)) != NULL) {
/** ********************************************************************
* This is a quickly hacked out implementation of offscreen textures.
* It will work in most cases but there may be problems if the client
* modifies the texture directly, or expects the contents of the rendertarget
* to be persistent.
*
* There are some real speed vs compatibility issues here:
* we should really use a new context for every texture, but that eats ram.
* we should also be restoring the texture to the pbuffer but that eats CPU
* we can also 'reuse' the current pbuffer if the size is larger than the requested buffer,
* but if this means reusing the display backbuffer then we need to make sure that
* states are correctly preserved.
* In many cases I would expect that we can 'skip' some functions, such as preserving states,
* and gain a good performance increase at the cost of compatibility.
* I would suggest that, when this is the case, a user configurable flag be made
* available, allowing the user to choose the best emulated experience for them.
*********************************************************************/
XVisualInfo *visinfo;
glContext *newContext;
/* Here were using a shared context model */
if (WINED3D_OK != IWineD3DDeviceImpl_FindGLContext(iface, RenderSurface, &newContext)) {
FIXME("(%p) : Failed to find a context for surface %p\n", iface, RenderSurface);
}
/* If the context doesn't exist then create a new one */
/* TODO: This should really be part of findGlContext */
if (NULL == newContext->context) {
int attribs[256];
int nAttribs = 0;
TRACE("making new buffer\n");
attribs[nAttribs++] = GLX_PBUFFER_WIDTH;
attribs[nAttribs++] = newContext->Width;
attribs[nAttribs++] = GLX_PBUFFER_HEIGHT;
attribs[nAttribs++] = newContext->Height;
attribs[nAttribs++] = None;
newContext->drawable = glXCreatePbuffer(implicitSwapchainImpl->display, cfgs[0], attribs);
/** ****************************************
*GLX1.3 isn't supported by XFree 'yet' until that point ATI emulates pBuffers
*they note:
* In future releases, we may provide the calls glXCreateNewContext,
* glXQueryDrawable and glXMakeContextCurrent.
* so until then we have to use glXGetVisualFromFBConfig &co..
********************************************/
visinfo = glXGetVisualFromFBConfig(implicitSwapchainImpl->display, cfgs[0]);
if (!visinfo) {
ERR("Error: couldn't get an RGBA, double-buffered visual\n");
} else {
newContext->context = glXCreateContext(
implicitSwapchainImpl->display, visinfo,
implicitSwapchainImpl->glCtx, GL_TRUE);
XFree(visinfo);
}
}
if (NULL == newContext || NULL == newContext->context) {
ERR("(%p) : Failed to find a context for surface %p\n", iface, RenderSurface);
} else {
/* Debug logging, (this function leaks), change to a TRACE when the leak is plugged */
if (glXMakeCurrent(implicitSwapchainImpl->display,
newContext->drawable, newContext->context) == False) {
TRACE("Error in setting current context: context %p drawable %ld\n",
newContext->context, newContext->drawable);
}
checkGLcall("glXMakeContextCurrent");
/* Clean up the old context */
IWineD3DDeviceImpl_CleanRender(iface, currentSwapchainImpl);
/* Reapply stateblock, and set device to render to a texture */
device_reapply_stateblock(This);
device_render_to_texture(This, TRUE);
/* Set the current context of the swapchain to the new context */
implicitSwapchainImpl->drawable = newContext->drawable;
implicitSwapchainImpl->render_ctx = newContext->context;
}
} else {
/* Same context, but update render_offscreen and cull mode */
device_render_to_texture(This, TRUE);
}
if (cfgs != NULL) XFree(cfgs);
if (implicitSwapchain != NULL) IWineD3DSwapChain_Release(implicitSwapchain);
if (currentSwapchain != NULL) IWineD3DSwapChain_Release(currentSwapchain);
if (renderSurfaceSwapchain != NULL) IWineD3DSwapChain_Release(renderSurfaceSwapchain);
LEAVE_GL();
#endif
return WINED3D_OK;
}
static HRESULT WINAPI IWineD3DDeviceImpl_SetCursorProperties(IWineD3DDevice* iface, UINT XHotSpot,
UINT YHotSpot, IWineD3DSurface *pCursorBitmap) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *) iface;
......@@ -6988,7 +6494,6 @@ void IWineD3DDeviceImpl_MarkStateDirty(IWineD3DDeviceImpl *This, DWORD state) {
WineD3DContext *context;
if(!rep) return;
for(i = 0; i < This->numContexts; i++) {
context = This->contexts[i];
if(isStateDirty(context, rep)) continue;
......
......@@ -2444,12 +2444,6 @@ static HRESULT WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter,
object->ddraw_format = pixelformat_for_depth(GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES));
ReleaseDC(0, hDC);
/* Allocate one context for now */
object->contexts = HeapAlloc(GetProcessHeap(), 0, sizeof(WineD3DContext *));
object->contexts[0] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WineD3DContext));
object->numContexts = 1;
object->activeContext = 0;
return WINED3D_OK;
create_device_error:
......
......@@ -237,6 +237,10 @@ ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
if(iface == device->ddraw_primary)
device->ddraw_primary = NULL;
if(iface == device->lastActiveRenderTarget) {
device->lastActiveRenderTarget = NULL;
}
TRACE("(%p) Released\n", This);
HeapFree(GetProcessHeap(), 0, This);
......@@ -714,23 +718,17 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED
} else if (swapchain != NULL) {
IWineD3DSwapChainImpl *implSwapChain;
IWineD3DDevice_GetSwapChain((IWineD3DDevice *)myDevice, 0, (IWineD3DSwapChain **)&implSwapChain);
if (swapchain->glCtx == implSwapChain->render_ctx && swapchain->drawable == implSwapChain->win) {
/* This will fail for the implicit swapchain, which is why there needs to be a context manager */
if (backbuf) {
glReadBuffer(GL_BACK);
} else if (iface == swapchain->frontBuffer) {
glReadBuffer(GL_FRONT);
} else if (iface == myDevice->depthStencilBuffer) {
FIXME("Stencil Buffer lock unsupported for now\n");
} else {
FIXME("Should have got here!\n");
glReadBuffer(GL_BACK);
}
/* ActivateContext should have selected the correct opengl context for that */
if (backbuf) {
glReadBuffer(GL_BACK);
} else if (iface == swapchain->frontBuffer) {
glReadBuffer(GL_FRONT);
} else if (iface == myDevice->depthStencilBuffer) {
FIXME("Stencil Buffer lock unsupported for now\n");
} else {
/* We need to switch contexts to be able to read the buffer!!! */
FIXME("The buffer requested isn't in the current openGL context\n");
notInContext = TRUE;
/* TODO: check the contexts, to see if were shared with the current context */
FIXME("Should have got here!\n");
glReadBuffer(GL_BACK);
}
IWineD3DSwapChain_Release((IWineD3DSwapChain *)implSwapChain);
}
......
......@@ -125,10 +125,11 @@ static void WINAPI IWineD3DSwapChainImpl_Destroy(IWineD3DSwapChain *iface, D3DCB
/* Clean up the context */
/* check that we are the current context first */
if(glXGetCurrentContext() == This->glCtx){
if(glXGetCurrentContext() == This->context->glCtx){
glXMakeCurrent(This->display, None, NULL);
}
glXDestroyContext(This->display, This->glCtx);
glXDestroyContext(This->display, This->context->glCtx);
DeleteContext(This->wineD3DDevice, This->context);
/* IUnknown_Release(This->parent); This should only apply to the primary swapchain,
all others are created by the caller, so releasing the parent should cause
the child to be released, not the other way around!
......@@ -183,15 +184,15 @@ static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CO
if (pSourceRect || pDestRect) FIXME("Unhandled present options %p/%p\n", pSourceRect, pDestRect);
/* TODO: If only source rect or dest rect are supplied then clip the window to match */
TRACE("preseting display %p, drawable %ld\n", This->display, This->drawable);
TRACE("preseting display %p, drawable %ld\n", This->display, This->context->drawable);
/* Don't call checkGLcall, as glGetError is not applicable here */
if (hDestWindowOverride && This->win_handle != hDestWindowOverride) {
/* Set this swapchain up to point to the new destination.. */
#ifdef USE_CONTEXT_MANAGER
/* TODO: use a context mamager */
#endif
/* Ok, now we switch the opengl context behind the context manager's back. Tidy this up when
* the ctx manager is completely in place
*/
/* FIXME: Never access */
IWineD3DSwapChainImpl *swapChainImpl;
IWineD3DDevice_GetSwapChain((IWineD3DDevice *)This->wineD3DDevice, 0 , (IWineD3DSwapChain **)&swapChainImpl);
......@@ -204,7 +205,7 @@ static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CO
XVisualInfo template;
int num;
Display *oldDisplay = This->display;
GLXContext oldContext = This->glCtx;
GLXContext oldContext = This->context->glCtx;
IUnknown* tmp;
GLXContext currentContext;
Drawable currentDrawable;
......@@ -234,21 +235,21 @@ static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CO
/* Now we have problems? well not really we just need to know what the implicit context is */
/* now destroy the old context and create a new one (we should really copy the buffers over, and do the whole make current thing! */
/* destroy the active context?*/
TRACE("Creating new context for %p %p %p\n",This->display, This->visInfo, swapChainImpl->glCtx);
This->glCtx = glXCreateContext(This->display, This->visInfo, swapChainImpl->glCtx, GL_TRUE);
TRACE("Creating new context for %p %p %p\n",This->display, This->visInfo, swapChainImpl->context->glCtx);
This->context->glCtx = glXCreateContext(This->display, This->visInfo, swapChainImpl->context->glCtx, GL_TRUE);
if (NULL == This->glCtx) {
if (NULL == This->context->glCtx) {
ERR("cannot create glxContext\n");
}
This->drawable = This->win;
This->render_ctx = This->glCtx;
This->context->drawable = This->win;
This->render_ctx = This->context->glCtx;
/* Setup some default states TODO: apply the stateblock to the new context */
/** save current context and drawable **/
currentContext = glXGetCurrentContext();
currentDrawable = glXGetCurrentDrawable();
if (glXMakeCurrent(This->display, This->win, This->glCtx) == False) {
ERR("Error in setting current context (display %p context %p drawable %ld)!\n", This->display, This->glCtx, This->win);
if (glXMakeCurrent(This->display, This->win, This->context->glCtx) == False) {
ERR("Error in setting current context (display %p context %p drawable %ld)!\n", This->display, This->context->glCtx, This->win);
}
checkGLcall("glXMakeCurrent");
......@@ -301,7 +302,7 @@ static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CO
/* TODO: The slow way, save the data to memory, create a new context for the destination window, transfer the data cleanup, it may be a good idea to the move this swapchain over to the using the target winows context so that it runs faster in feature. */
glXSwapBuffers(This->display, This->drawable); /* TODO: cycle through the swapchain buffers */
glXSwapBuffers(This->display, This->context->drawable); /* TODO: cycle through the swapchain buffers */
TRACE("glXSwapBuffers called, Starting new frame\n");
/* FPS support */
......
......@@ -466,7 +466,7 @@ struct WineD3DContext {
DWORD numDirtyEntries;
DWORD isStateDirty[STATE_HIGHEST/32 + 1]; /* Bitmap to find out quickly if a state is dirty */
/* TODO: Render target / swapchain this ctx belongs to */
IWineD3DSurface *surface;
/* TODO: Thread this ctx belongs to */
/* Stores some inforation about the context state for optimization */
......@@ -478,6 +478,10 @@ struct WineD3DContext {
BOOL lastWasPow2Texture[MAX_TEXTURES];
GLenum tracking_parm; /* Which source is tracking current colour */
BOOL last_was_blit;
/* The actual opengl context */
GLXContext glCtx;
Drawable drawable;
};
typedef enum ContextUsage {
......@@ -487,6 +491,11 @@ typedef enum ContextUsage {
} ContextUsage;
void ActivateContext(IWineD3DDeviceImpl *device, IWineD3DSurface *target, ContextUsage usage);
WineD3DContext *CreateContext(IWineD3DDeviceImpl *This, IWineD3DSurfaceImpl *target, Window win);
WineD3DContext *AddContext(IWineD3DDeviceImpl *This, GLXContext glCtx, Drawable drawable);
void DeleteContext(IWineD3DDeviceImpl *This, WineD3DContext *context);
void DestroyContext(IWineD3DDeviceImpl *This, WineD3DContext *context);
void set_render_target_fbo(IWineD3DDevice *iface, DWORD idx, IWineD3DSurface *render_target);
/* Routine to fill gl caps for swapchains and IWineD3D */
BOOL IWineD3DImpl_FillGLCaps(IWineD3D *iface, Display* display);
......@@ -547,33 +556,12 @@ typedef struct IWineD3DImpl
extern const IWineD3DVtbl IWineD3D_Vtbl;
/** Hacked out start of a context manager!! - Subject to removal **/
typedef struct glContext {
int Width;
int Height;
int usedcount;
GLXContext context;
Drawable drawable;
IWineD3DSurface *pSurface;
#if 0 /* TODO: someway to represent the state of the context */
IWineD3DStateBlock *pStateBlock;
#endif
/* a few other things like format */
} glContext;
/* TODO: setup some flags in the regestry to enable, disable pbuffer support
(since it will break quite a few things until contexts are managed properly!) */
extern BOOL pbuffer_support;
/* allocate one pbuffer per surface */
extern BOOL pbuffer_per_surface;
/* Maximum number of contexts/pbuffers to keep in cache,
set to 100 because ATI's drivers don't support deleting pBuffers properly
this needs to be migrated to a list and some option availalbe for controle the cache size.
*/
#define CONTEXT_CACHE 100
typedef struct ResourceList {
IWineD3DResource *resource;
struct ResourceList *next;
......@@ -633,6 +621,10 @@ struct IWineD3DDeviceImpl
IWineD3DSurface *stencilBufferTarget;
/* Caches to avoid unneeded context changes */
IWineD3DSurface *lastActiveRenderTarget;
IWineD3DSwapChain *lastActiveSwapChain;
/* palettes texture management */
PALETTEENTRY palettes[MAX_PALETTES][256];
UINT currentPalette;
......@@ -662,9 +654,6 @@ struct IWineD3DDeviceImpl
HRESULT state;
BOOL d3d_initialized;
/* Screen buffer resources - subject to removal */
glContext contextCache[CONTEXT_CACHE];
/* A flag to check for proper BeginScene / EndScene call pairs */
BOOL inScene;
......@@ -695,9 +684,11 @@ struct IWineD3DDeviceImpl
BOOL useDrawStridedSlow;
/* Context management */
WineD3DContext **contexts; /* Dynamic array containing pointers to context structures */
UINT activeContext; /* Only 0 for now */
UINT numContexts; /* Always 1 for now */
WineD3DContext **contexts; /* Dynamic array containing pointers to context structures */
WineD3DContext *activeContext; /* Only 0 for now */
UINT numContexts; /* Always 1 for now */
WineD3DContext *pbufferContext; /* The context that has a pbuffer as drawable */
DWORD pbufferWidth, pbufferHeight; /* Size of the buffer drawable */
};
extern const IWineD3DDeviceVtbl IWineD3DDevice_Vtbl;
......@@ -1340,20 +1331,15 @@ typedef struct IWineD3DSwapChainImpl
long prev_time, frames; /* Performance tracking */
/* TODO: move everything up to drawable off into a context manager
and store the 'data' in the contextManagerData interface.
IUnknown *contextManagerData;
*/
WineD3DContext *context; /* Later a array for multithreading */
HWND win_handle;
Window win;
Display *display;
GLXContext glCtx;
XVisualInfo *visInfo;
GLXContext render_ctx;
/* This has been left in device for now, but needs moving off into a rendertarget management class and separated out from swapchains and devices. */
Drawable drawable;
} IWineD3DSwapChainImpl;
extern const IWineD3DSwapChainVtbl IWineD3DSwapChain_Vtbl;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment