swapchain.c revision dce32b5ab44d19932db4c40310f864dab2b5df26
/*
*IDirect3DSwapChain9 implementation
*
*Copyright 2002-2003 Jason Edmeades
*Copyright 2002-2003 Raphael Junqueira
*Copyright 2005 Oliver Stieber
*Copyright 2007-2008 Stefan Dösinger for CodeWeavers
*
*modify it under the terms of the GNU Lesser General Public
*License as published by the Free Software Foundation; either
*version 2.1 of the License, or (at your option) any later version.
*
*This library is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*Lesser General Public License for more details.
*
*You should have received a copy of the GNU Lesser General Public
*License along with this library; if not, write to the Free Software
*Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
* other than GPL or LGPL is available it will apply instead, Oracle elects to use only
* the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
* a choice of LGPL license versions is made available with the language indicating
* that LGPLv2 or any later version may be used, or where a choice of which version
* of the LGPL is applied is otherwise unspecified.
*/
#include "config.h"
#include "wined3d_private.h"
/*TODO: some of the additional parameters may be required to
set the gamma ramp (for some weird reason microsoft have left swap gammaramp in device
but it operates on a swapchain, it may be a good idea to move it to IWineD3DSwapChain for IWineD3D)*/
#ifdef VBOX_WITH_WDDM
{
UINT i;
for (i = 0; i < pDevice->NumberOfSwapChains; ++i)
{
{
return pSwapchain;
}
}
return NULL;
}
static VOID swapchain_cleanup_rt_refs(IWineD3DSwapChainImpl *pSwapchain, IWineD3DSurface *rt, int iBb)
{
struct wined3d_context *context;
UINT i;
for (i = 0; i < device->numContexts; ++i)
{
{
}
}
if (device->NumberOfSwapChains)
{
{
{
if (i)
else
newRt = pDefaultSwapchain->backBuffer ? pDefaultSwapchain->backBuffer[0] : pDefaultSwapchain->frontBuffer;
}
}
}
}
{
/* first make sure the swapchain is not used by anyone */
struct wined3d_context *context;
UINT i;
for (i = 0; i < device->numContexts; ++i)
{
/* pretty hacky, @todo: check if the context is acquired and re-acquire it with the new swapchain */
{
}
}
if (pSwapchain->frontBuffer)
if (pSwapchain->backBuffer)
{
UINT j;
{
}
}
}
{
}
#endif
/*IWineD3DSwapChain parts follow: */
{
unsigned int i;
#ifdef VBOX_WITH_WDDM
/* first remove swapchain from a list to ensure context is properly acquired
* & gl resources are properly cleared on last swapchain destruction */
#endif
/* Release the swapchain's draw buffers. Make sure This->backBuffer[0] is
* the last buffer to be destroyed, FindContext() depends on that. */
if (This->frontBuffer)
{
{
WARN("(%p) Something's still holding the front buffer (%p).\n",
}
}
if (This->backBuffer)
{
while (i--)
{
WARN("(%p) Something's still holding back buffer %u (%p).\n",
}
}
#ifndef VBOX_WINE_WITH_SINGLE_CONTEXT
for (i = 0; i < This->num_contexts; ++i)
{
}
#endif
#ifdef VBOX_WITH_WDDM
{
}
if(This->win_handle) {
}
else
{
WARN("null win info");
}
#else
{
/* Restore the screen resolution if we rendered in fullscreen
* This will restore the screen resolution to what it was before creating the swapchain. In case of d3d8 and d3d9
* this will be the original desktop resolution. In case of d3d7 this will be a NOP because ddraw sets the resolution
* before starting up Direct3D, thus orig_width and orig_height will be equal to the modes in the presentation params
*/
mode.RefreshRate = 0;
}
}
#endif
}
/* A GL context is provided by the caller */
{
TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
else
if (0 && gl_info->fbo_ops.glBlitFramebuffer && is_identity_fixup(backbuffer->resource.format_desc->color_fixup))
{
ENTER_GL();
/* Note that the texture is upside down */
gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
checkGLcall("Swapchain present blit(EXT_framebuffer_blit)\n");
LEAVE_GL();
}
else
{
struct wined3d_context *context2;
{
tex_bottom /= src_h;
}
ENTER_GL();
/* Set up the texture. The surface is not in a IWineD3D*Texture container,
* so there are no d3d texture settings to dirtify
*/
/* Set the viewport to the destination rectandle, disable any projection
* transformation set up by CTXUSAGE_BLIT, and draw a (-1,-1)-(1,1) quad.
*
* Back up viewport and matrix to avoid breaking last_was_blit
*
* Note that CTXUSAGE_BLIT set up viewport and ortho to match the surface
* size - we want the GL drawable(=window) size.
*/
glPushMatrix();
/* bottom left */
/* top left */
/* top right */
/* bottom right */
glEnd();
glPopMatrix();
glPopAttrib();
checkGLcall("Swapchain present blit(manual)\n");
LEAVE_GL();
}
}
static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) {
struct wined3d_context *context;
unsigned int sync;
int retval;
#ifdef VBOX_WITH_WDDM
/* quickly sort out invalid swapchains */
{
WARN("Invalid swapchain");
return WINED3D_OK;
}
#endif
{
WARN("Invalid context, skipping present.\n");
return WINED3D_OK;
}
/* Render the cursor onto the back buffer, using our nifty directdraw blitting code :-) */
{
{
};
/* Build a fake surface to call the Blitting code. It is not possible to use the interface passed by
* the application because we are only supposed to copy the information out. Using a fake surface
* allows to use the Blitting engine and avoid copying the whole texture -> render target blitting code.
*/
cursor.texture_level = 0;
/* The cursor must have pow2 sizes */
/* The surface is in the texture */
/* DDBLT_KEYSRC will cause BltOverride to enable the alpha test with GL_NOTEQUAL, 0.0,
* which is exactly what we want :-)
*/
#ifdef VBOX_WITH_WDDM
/* @todo: can we actualy be here? */
#endif
}
}
{
/* Blit the logo into the upper left corner of the drawable. */
IWineD3DSurface_BltFast(This->backBuffer[0], 0, 0, This->device->logo_surface, NULL, WINEDDBLTFAST_SRCCOLORKEY);
}
#ifdef VBOX_WITH_WDDM
#else
#endif
if (pSourceRect)
{
src_rect = *pSourceRect;
{
}
}
else
{
}
#ifndef VBOX_WITH_WDDM
else
{
}
#else
else
{
}
#endif
/* Rendering to a window of different size, presenting partial rectangles,
* or rendering to a different window needs help from FBO_blit or a textured
* draw. Render the swapchain to a FBO in the future.
*
* Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
* all these issues - this fails if the window is smaller than the backbuffer.
*/
{
/* Force the context manager to update the render target configuration next draw. */
}
if(This->render_to_fbo)
{
/* This codepath should only be hit with the COPY swapeffect. Otherwise a backbuffer-
* window size mismatch is impossible(fullscreen) and src and dst rectangles are
* not allowed(they need the COPY swapeffect)
*
* The DISCARD swap effect is ok as well since any backbuffer content is allowed after
* the swap
*/
{
FIXME("Render-to-fbo with WINED3DSWAPEFFECT_FLIP\n");
}
}
#ifdef VBOX_WITH_WDDM
#else
#endif
#if defined(VBOX_WITH_WDDM) && defined(DEBUG)
{
}
#endif
#ifdef VBOX_WITH_WDDM
/* We're directly using wglMakeCurrent calls skipping GDI layer, which causes GDI SwapBuffers to fail trying to
* call glFinish, which doesn't have any context set. So we use wglSwapLayerBuffers directly as well.
*/
#else
# else
# endif
#endif
TRACE("SwapBuffers called, Starting new frame\n");
/* FPS support */
{
/* every 1.5 seconds */
}
}
#if defined(FRAME_DEBUGGING)
{
if (!isOn) {
FIXME("Enabling D3D Trace\n");
#if defined(SHOW_FRAME_MAKEUP)
FIXME("Singe Frame snapshots Starting\n");
ENTER_GL();
LEAVE_GL();
#endif
#if defined(SINGLE_FRAME_DEBUGGING)
} else {
#if defined(SHOW_FRAME_MAKEUP)
FIXME("Singe Frame snapshots Finishing\n");
#endif
FIXME("Singe Frame trace complete\n");
DeleteFileA("C:\\D3DTRACE");
#endif
}
} else {
if (isOn) {
#if defined(SHOW_FRAME_MAKEUP)
FIXME("Single Frame snapshots Finishing\n");
#endif
FIXME("Disabling D3D Trace\n");
}
}
}
#endif
/* This is disabled, but the code left in for debug purposes.
*
* Since we're allowed to modify the new back buffer on a D3DSWAPEFFECT_DISCARD flip,
* we can clear it with some ugly color to make bad drawing visible and ease debugging.
* The Debug runtime does the same on Windows. However, a few games do not redraw the
* screen properly, like Max Payne 2, which leaves a few pixels undefined.
*
* Tests show that the content of the back buffer after a discard flip is indeed not
* reliable, so no game can depend on the exact content. However, it resembles the
* old contents in some way, for example by showing fragments at other locations. In
* general, the color theme is still intact. So Max payne, which draws rather dark scenes
* gets a dark background image. If we clear it with a bright ugly color, the game's
* bug shows up much more than it does on Windows, and the players see single pixels
* with wrong colors.
* (The Max Payne bug has been confirmed on Windows with the debug runtime)
*/
TRACE("Clearing the color buffer with cyan color\n");
}
if(!This->render_to_fbo &&
/* Both memory copies of the surfaces are ok, flip them around too instead of dirtifying
* Doesn't work with render_to_fbo because we're not flipping
*/
/* Tell the front buffer surface that is has been modified. However,
* the other locations were preserved during that, so keep the flags.
* This serves to update the emulated overlay, if any
*/
} else {
}
} else {
/* If the swapeffect is DISCARD, the back buffer is undefined. That means the SYSMEM
* and INTEXTURE copies can keep their old content if they have any defined content.
* If the swapeffect is COPY, the content remains the same. If it is FLIP however,
* the texture / sysmem copy needs to be reloaded from the drawable
*/
}
}
{
{
}
}
{
if(retval != 0) {
}
} else {
}
break;
} else {
}
break;
} else {
}
break;
} else {
}
break;
default:
}
}
TRACE("returning\n");
return WINED3D_OK;
}
static HRESULT WINAPI IWineD3DSwapChainImpl_SetDestWindowOverride(IWineD3DSwapChain *iface, HWND window)
{
#ifndef VBOX_WITH_WDDM
#endif
return WINED3D_OK;
}
#ifdef VBOX_WITH_WDDM
{
{
return hr;
}
{
return hr;
}
return S_OK;
}
{
/* @todo: if we're in PresentRt mode, check whether the current present rt is updated
* and do present to frontbuffer if needed */
return S_OK;
}
{
{
if (surf)
{
if (surface->presentSwapchain)
{
ERR("not expected");
}
}
{
}
}
if (surf)
return S_OK;
}
#endif
static const IWineD3DSwapChainVtbl IWineD3DSwapChain_Vtbl =
{
/* IUnknown */
/* IWineD3DSwapChain */
#ifdef VBOX_WITH_WDDM
#endif
};
{
/* Make sure the window is managed, otherwise we won't get keyboard input. */
return style;
}
{
/* Filter out window decorations. */
return exstyle;
}
{
#ifdef VBOX_WITH_WDDM
ERR("not supported");
#else
{
ERR("Changing the window style for window %p, but another style (%08x, %08x) is already stored.\n",
}
TRACE("Old style was %08x, %08x, setting to %08x, %08x.\n",
#endif
}
{
#ifdef VBOX_WITH_WDDM
ERR("not supported");
#else
TRACE("Restoring window style of window %p to %08x, %08x.\n",
/* Only restore the style if the application didn't modify it during the
* fullscreen phase. Some applications change it before calling Reset()
* when switching between windowed and fullscreen modes (HL2), some
* depend on the original style (Eve Online). */
{
}
/* Delete the old values. */
#endif
}
{
const struct wined3d_format_desc *format_desc;
#ifdef VBOX_WITH_WDDM
#endif
UINT i;
{
ERR("The application requested %u back buffers, this is not supported.\n",
return WINED3DERR_INVALIDCALL;
}
{
FIXME("The application requested more than one back buffer, this is not properly supported.\n"
"Please configure the application to use double buffering (1 back buffer) if possible.\n");
}
switch (surface_type)
{
case SURFACE_GDI:
break;
case SURFACE_OPENGL:
break;
case SURFACE_UNKNOWN:
FIXME("Caller tried to create a SURFACE_UNKNOWN swapchain.\n");
return WINED3DERR_INVALIDCALL;
}
#ifdef VBOX_WITH_WDDM
{
if (!overridenSwapchain)
{
ERR("invalid window handle supplied");
return E_FAIL;
}
}
else
{
hr = VBoxExtWndCreate(present_parameters->BackBufferWidth, present_parameters->BackBufferHeight, &window, &hDC);
{
return hr;
}
}
#else
window = present_parameters->hDeviceWindow ? present_parameters->hDeviceWindow : device->createParms.hFocusWindow;
#endif
#ifndef VBOX_WITH_WDDM
#else
#endif
{
}
#ifndef VBOX_WITH_WDDM
#else
client_rect.left = 0;
client_rect.top = 0;
#endif
{
{
}
{
}
{
}
}
{
TRACE("Rendering to FBO. Backbuffer %ux%u, window %ux%u.\n",
}
TRACE("Creating front buffer.\n");
{
goto err;
}
if (surface_type == SURFACE_OPENGL)
{
}
/* MSDN says we're only allowed a single fullscreen swapchain per device,
* so we should really check to see if there is a fullscreen swapchain
* already. Does a single head count as full screen? */
if (!present_parameters->Windowed)
{
/* Change the display settings */
{
goto err;
}
}
#ifndef VBOX_WITH_WDDM
{
ERR("Failed to create the context array.\n");
hr = E_OUTOFMEMORY;
goto err;
}
#endif
if (surface_type == SURFACE_OPENGL)
{
#ifdef VBOX_WITH_WDDM
struct wined3d_context * swapchainContext;
#endif
/* In WGL both color, depth and stencil are features of a pixel format. In case of D3D they are separate.
* You are able to add a depth + stencil surface at a later stage when you need it.
* In order to support this properly in WineD3D we need the ability to recreate the opengl context and
* drawable when this is required. This is very tricky as we need to reapply ALL opengl states for the new
* context, need torecreate shaders, textures and other resources.
*
* The context manager already takes care of the state problem and for the other tasks code from Reset
* can be used. These changes are way to risky during the 1.0 code freeze which is taking place right now.
* Likely a lot of other new bugs will be exposed. For that reason request a depth stencil surface all the
* time. It can cause a slight performance hit but fixes a lot of regressions. A fixme reminds of that this
* issue needs to be fixed. */
{
FIXME("Add OpenGL context recreation support to context_validate_onscreen_formats\n");
}
#ifdef VBOX_WITH_WDDM
swapchainContext = context_find_create(device, swapchain, (IWineD3DSurfaceImpl *)swapchain->frontBuffer,
if (!swapchainContext)
#else
#endif
{
WARN("Failed to create context.\n");
goto err;
}
#ifdef VBOX_WITH_WDDM
#else
#endif
}
else
{
#ifndef VBOX_WITH_WDDM
#endif
}
{
if (!swapchain->backBuffer)
{
ERR("Failed to allocate backbuffer array memory.\n");
hr = E_OUTOFMEMORY;
goto err;
}
{
TRACE("Creating back buffer %u.\n", i);
{
goto err;
}
}
}
{
if (!device->auto_depth_stencil_buffer)
{
{
goto err;
}
}
}
#ifdef VBOX_WITH_WDDM
if (overridenSwapchain)
{
}
#endif
return WINED3D_OK;
err:
if (displaymode_set)
{
/* Change the display settings */
}
if (swapchain->backBuffer)
{
{
}
}
#ifdef VBOX_WITH_WDDM
if (!device->NumberOfSwapChains)
{
while (device->numContexts)
{
}
}
#else
{
{
swapchain->num_contexts = 0;
}
}
#endif
#ifdef VBOX_WITH_WDDM
{
}
#endif
return hr;
}
{
#ifndef VBOX_WITH_WDDM
struct wined3d_context **newArray;
#endif
struct wined3d_context *ctx;
#ifdef VBOX_WITH_WDDM
ERR("Should not be here");
#endif
#ifdef VBOX_WITH_WDDM
#endif
)))
{
ERR("Failed to create a new context for the swapchain\n");
return NULL;
}
#ifdef VBOX_WITH_WDDM
/* no need to do anything since context gets added to the device context list within the context_create call */
#else
if(!newArray) {
ERR("Out of memory when trying to allocate a new context array\n");
return NULL;
}
This->num_contexts++;
#endif
return ctx;
}
{
/* The drawable size of an onscreen drawable is the surface size.
* (Actually: The window size, but the surface is created in window size) */
}