changeset 28344:a69d51cd4ee3

Allocate a larger backbuffer to allow resizing without reinit. Patch by Georgi Petrov [gogothebee gmail com] and Jim Hauxwell [james (at) dattrax co uk]
author reimar
date Tue, 27 Jan 2009 10:02:47 +0000
parents e45b08f2f5d3
children 3a08f32c8fa1
files libvo/vo_direct3d.c
diffstat 1 files changed, 113 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/libvo/vo_direct3d.c	Mon Jan 26 15:06:44 2009 +0000
+++ b/libvo/vo_direct3d.c	Tue Jan 27 10:02:47 2009 +0000
@@ -78,6 +78,8 @@
                                     cannot lock a normal texture. Uses RGBA */
     IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to
                                     display next frame) */
+    int cur_backbuf_width;          /**< Current backbuffer width */
+    int cur_backbuf_height;         /**< Current backbuffer height */
     int is_osd_populated;           /**< 1 = OSD texture has something to display,
                                     0 = OSD texture is clear */
     int device_caps_power2_only;    /**< 1 = texture sizes have to be power 2
@@ -126,6 +128,10 @@
     float tu, tv;       /* Texture coordinates */
 } struct_vertex;
 
+typedef enum back_buffer_action {
+    BACKBUFFER_CREATE,
+    BACKBUFFER_RESET
+} back_buffer_action_e;
 /****************************************************************************
  *                                                                          *
  *                                                                          *
@@ -224,7 +230,8 @@
     priv->d3d_backbuf = NULL;
 }
 
-/** @brief Create D3D Offscreen and Backbuffer surfaces.
+/** @brief Create D3D Offscreen and Backbuffer surfaces. Each
+ *         surface is created only if it's not already present.
  *  @return 1 on success, 0 on failure
  */
 static int create_d3d_surfaces(void)
@@ -233,7 +240,8 @@
     int tex_width = osd_width, tex_height = osd_height;
     mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d><INFO>create_d3d_surfaces called.\n");
 
-    if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(
+    if (!priv->d3d_surface &&
+        FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface(
                priv->d3d_device, priv->src_width, priv->src_height,
                priv->movie_src_fmt, D3DPOOL_DEFAULT, &priv->d3d_surface, NULL))) {
         mp_msg(MSGT_VO, MSGL_ERR,
@@ -241,7 +249,8 @@
         return 0;
     }
 
-    if (FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0,
+    if (!priv->d3d_backbuf &&
+        FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0,
                                               D3DBACKBUFFER_TYPE_MONO,
                                               &priv->d3d_backbuf))) {
         mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Back Buffer address get failed\n");
@@ -279,7 +288,8 @@
            vo_dwidth, vo_dheight, priv->osd_texture_width, priv->osd_texture_height);
 
     /* create OSD */
-    if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device,
+    if (!priv->d3d_texture_system &&
+        FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device,
                                               priv->osd_texture_width,
                                               priv->osd_texture_height,
                                               1,
@@ -295,7 +305,8 @@
 
     if (!priv->device_texture_sys) {
         /* only create if we need a shadow version on the external device */
-        if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device,
+        if (!priv->d3d_texture_osd &&
+            FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device,
                                                   priv->osd_texture_width,
                                                   priv->osd_texture_height,
                                                   1,
@@ -332,27 +343,78 @@
     present_params->SwapEffect             = D3DSWAPEFFECT_COPY;
     present_params->Flags                  = D3DPRESENTFLAG_VIDEO;
     present_params->hDeviceWindow          = vo_w32_window; /* w32_common var */
-    present_params->BackBufferWidth        = 0; /* Fill up window Width */
-    present_params->BackBufferHeight       = 0; /* Fill up window Height */
+    present_params->BackBufferWidth        = priv->cur_backbuf_width;
+    present_params->BackBufferHeight       = priv->cur_backbuf_height;
     present_params->MultiSampleType        = D3DMULTISAMPLE_NONE;
-    /* D3DPRESENT_INTERVAL_ONE = vsync */
     present_params->PresentationInterval   = D3DPRESENT_INTERVAL_ONE;
     present_params->BackBufferFormat       = priv->desktop_fmt;
     present_params->BackBufferCount        = 1;
     present_params->EnableAutoDepthStencil = FALSE;
 }
 
+
+/** @brief Create a new backbuffer. Create or Reset the D3D
+ *         device.
+ *  @return 1 on success, 0 on failure
+ */
+static int change_d3d_backbuffer(back_buffer_action_e action)
+{
+    D3DPRESENT_PARAMETERS present_params;
+
+    destroy_d3d_surfaces();
+
+    /* Grow the backbuffer in the required dimension. */
+    if (vo_dwidth > priv->cur_backbuf_width)
+        priv->cur_backbuf_width = vo_dwidth;
+
+    if (vo_dheight > priv->cur_backbuf_height)
+        priv->cur_backbuf_height = vo_dheight;
+
+    /* The grown backbuffer dimensions are ready and fill_d3d_presentparams
+     * will use them, so we can reset the device.
+     */
+    fill_d3d_presentparams(&present_params);
+
+    /* vo_w32_window is w32_common variable. It's a handle to the window. */
+    if (action == BACKBUFFER_CREATE &&
+        FAILED(IDirect3D9_CreateDevice(priv->d3d_handle,
+                                       D3DADAPTER_DEFAULT,
+                                       D3DDEVTYPE_HAL, vo_w32_window,
+                                       D3DCREATE_SOFTWARE_VERTEXPROCESSING,
+                                       &present_params, &priv->d3d_device))) {
+            mp_msg(MSGT_VO, MSGL_ERR,
+                   "<vo_direct3d><INFO>Could not create the D3D device\n");
+        return 0;
+    }
+
+    if (action == BACKBUFFER_RESET &&
+        FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) {
+            mp_msg(MSGT_VO, MSGL_ERR,
+                   "<vo_direct3d><INFO>Could not reset the D3D device\n");
+        return 0;
+    }
+
+    mp_msg(MSGT_VO, MSGL_V,
+           "<vo_direct3d><INFO>New backbuffer: Width: %d, Height:%d. VO Dest Width:%d, Height: %d\n",
+           present_params.BackBufferWidth, present_params.BackBufferHeight,
+           vo_dwidth, vo_dheight);
+
+    return 1;
+}
+
 /** @brief Configure initial Direct3D context. The first
  *  function called to initialize the D3D context.
  *  @return 1 on success, 0 on failure
  */
 static int configure_d3d(void)
 {
-    D3DPRESENT_PARAMETERS present_params;
     D3DDISPLAYMODE disp_mode;
+    D3DVIEWPORT9 vp = {0, 0, vo_dwidth, vo_dheight, 0, 1};
 
     mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d><INFO>configure_d3d called\n");
 
+    destroy_d3d_surfaces();
+
     /* Get the current desktop display mode, so we can set up a back buffer
      * of the same format. */
     if (FAILED(IDirect3D9_GetAdapterDisplayMode(priv->d3d_handle,
@@ -366,26 +428,17 @@
     /* Write current Desktop's colorspace format in the global storage. */
     priv->desktop_fmt = disp_mode.Format;
 
-    fill_d3d_presentparams(&present_params);
-
-    /* vo_w32_window is w32_common variable. It's a handle to the window. */
-    if (FAILED(IDirect3D9_CreateDevice(priv->d3d_handle,
-                                       D3DADAPTER_DEFAULT,
-                                       D3DDEVTYPE_HAL, vo_w32_window,
-                                       D3DCREATE_SOFTWARE_VERTEXPROCESSING,
-                                       &present_params, &priv->d3d_device))) {
-        mp_msg(MSGT_VO, MSGL_ERR,
-               "<vo_direct3d><INFO>Could not create the D3D device\n");
+    if (!change_d3d_backbuffer(BACKBUFFER_CREATE))
         return 0;
-    }
 
     if (!create_d3d_surfaces())
         return 0;
 
-    mp_msg(MSGT_VO, MSGL_V,
-           "New BackBuffer: Width: %d, Height:%d. VO Dest Width:%d, Height: %d\n",
-            present_params.BackBufferWidth, present_params.BackBufferHeight,
-            vo_dwidth, vo_dheight);
+    if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device,
+                                            &vp))) {
+        mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Unable to set the viewport\n");
+        return VO_ERROR;
+    }
 
     calc_fs_rect();
 
@@ -400,7 +453,7 @@
 {
     mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d><INFO>reconfigure_d3d called.\n");
 
-    /* Destroy the Offscreen and Backbuffer surfaces */
+    /* Destroy the offscreen, OSD and backbuffer surfaces */
     destroy_d3d_surfaces();
 
     /* Destroy the D3D Device */
@@ -425,35 +478,49 @@
     return 1;
 }
 
-
 /** @brief Resize Direct3D context on window resize.
  *  @return 1 on success, 0 on failure
  */
 static int resize_d3d(void)
 {
-    D3DPRESENT_PARAMETERS present_params;
+    D3DVIEWPORT9 vp = {0, 0, vo_dwidth, vo_dheight, 0, 1};
 
     mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d><INFO>resize_d3d called.\n");
 
-    destroy_d3d_surfaces();
+    /* Make sure that backbuffer is large enough to accomodate the new
+       viewport dimensions. Grow it if necessary. */
 
-    /* Reset the D3D Device with all parameters the same except the new
-     * width/height.
+    if (vo_dwidth > priv->cur_backbuf_width ||
+        vo_dheight > priv->cur_backbuf_height) {
+        change_d3d_backbuffer (BACKBUFFER_RESET);
+    }
+
+    /* Destroy the OSD textures. They should always match the new dimensions
+     * of the onscreen window, so on each resize we need new OSD dimensions.
      */
-    fill_d3d_presentparams(&present_params);
-    if (FAILED(IDirect3DDevice9_Reset(priv->d3d_device, &present_params))) {
-        mp_msg(MSGT_VO, MSGL_ERR,
-               "<vo_direct3d><INFO>Could not reset the D3D device\n");
-        return 0;
-    }
+
+    if (priv->d3d_texture_osd)
+        IDirect3DTexture9_Release(priv->d3d_texture_osd);
+    priv->d3d_texture_osd = NULL;
+
+    if (priv->d3d_texture_system)
+        IDirect3DTexture9_Release(priv->d3d_texture_system);
+    priv->d3d_texture_system = NULL;
+
+
+    /* Recreate the OSD. The function will observe that the offscreen plain
+     * surface and the backbuffer are not destroyed and will skip their creation,
+     * effectively recreating only the OSD.
+     */
 
     if (!create_d3d_surfaces())
         return 0;
 
-    mp_msg(MSGT_VO, MSGL_V,
-           "New BackBuffer: Width: %d, Height:%d. VO Dest Width:%d, Height: %d\n",
-           present_params.BackBufferWidth, present_params.BackBufferHeight,
-           vo_dwidth, vo_dheight);
+    if (FAILED(IDirect3DDevice9_SetViewport(priv->d3d_device,
+                                            &vp))) {
+        mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Unable to set the viewport\n");
+        return VO_ERROR;
+    }
 
     calc_fs_rect();
 
@@ -644,8 +711,10 @@
 
     /* Store in priv->desktop_fmt the user desktop's colorspace. Usually XRGB. */
     priv->desktop_fmt = disp_mode.Format;
+    priv->cur_backbuf_width = disp_mode.Width;
+    priv->cur_backbuf_height = disp_mode.Height;
 
-    mp_msg(MSGT_VO, MSGL_V, "disp_mode.Width %d, disp_mode.Height %d\n",
+    mp_msg(MSGT_VO, MSGL_V, "Setting backbuffer to the screen width: %d, height: %d\n",
            disp_mode.Width, disp_mode.Height);
 
     if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle,
@@ -787,7 +856,8 @@
  */
 static void flip_page(void)
 {
-    if (FAILED(IDirect3DDevice9_Present(priv->d3d_device, 0, 0, 0, 0))) {
+    RECT rect = {0, 0, vo_dwidth, vo_dheight};
+    if (FAILED(IDirect3DDevice9_Present(priv->d3d_device, &rect, 0, 0, 0))) {
         mp_msg(MSGT_VO, MSGL_V,
                "<vo_direct3d>Video adapter became uncooperative.\n");
         mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Trying to reinitialize it...\n");
@@ -795,7 +865,7 @@
             mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Reinitialization Failed.\n");
             return;
         }
-        if (FAILED(IDirect3DDevice9_Present(priv->d3d_device, 0, 0, 0, 0))) {
+        if (FAILED(IDirect3DDevice9_Present(priv->d3d_device, &rect, 0, 0, 0))) {
             mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Reinitialization Failed.\n");
             return;
         }