[ddraw] Mipmap creation with DDSCAPS_COMPLEX, DDSCAPS_MIPMAP, and no mipmap count

Antoine Chavasse a.chavasse at gmail.com
Sat May 28 16:36:52 CDT 2005


Hi,

There is an undocumented feature that Anarchy Online needs, which is
that when creating a direct draw surface which has both
DDSCAPS_COMPLEX and DDSCAPS_MIPMAP, and without DDSD_MIPMAPCOUNT, a
default number of mipmap levels are created, depending on the
requested size.

Someone already posted a patch (that was adapted from a cedega patch)
back in september:
http://www.winehq.org/hypermail/wine-devel/2004/09/0195.html

Unfortunately, this patch didn't work, because there is a
contradictory piece of code right before the code it added that
prevented it from ever doing anything.

So, here is an (hopefully) much cleaner patch that works.
Since it's an undocumented feature and as there is a statement in the
existing code that contradicts that the api behaves this way (which is
in fact true, but only if DDSCAPS_COMPLEX is not set), I've added a
test case that verify both the case where mipmap should be created and
the case where only one level of mipmap should be created, which is
the case that the code currently handle.
It has also let me find out that the original patch didn't compute the
number of mipmap to create the same way as windows does, which is why
it's done diffferently here.

I don't know if I did this right, any comment is welcome.

ChangeLog:
 - Implemented the implicit creation of mipmaps for surfaces that have
DDSCAPS_COMPLEX, DDSCAPS_MIPMAP and for which the mipmap count is not
specified.
 - Implemented test cases for mipmap textures creation.

Index: dlls/ddraw/ddraw/main.c
===================================================================
RCS file: /home/wine/wine/dlls/ddraw/ddraw/main.c,v
retrieving revision 1.60
diff -u -p -r1.60 main.c
--- dlls/ddraw/ddraw/main.c	27 May 2005 20:17:36 -0000	1.60
+++ dlls/ddraw/ddraw/main.c	28 May 2005 21:13:56 -0000
@@ -430,12 +430,26 @@ create_texture(IDirectDrawImpl* This, co
 	ddsd.dwFlags |= DDSD_PITCH;
     }
 
-    /* Check also for the MIPMAP / MIPMAPCOUNT flags.
-       As checked on Windows, this is the right behaviour. No mipmaps
seem to be generated. */
-    if (((ddsd.dwFlags & DDSD_MIPMAPCOUNT) == 0) &&
-	((ddsd.ddsCaps.dwCaps & DDSCAPS_MIPMAP) != 0)) {
+    if((ddsd.ddsCaps.dwCaps & DDSCAPS_MIPMAP) &&
+        !(ddsd.dwFlags & DDSD_MIPMAPCOUNT))
+    {
+        if(ddsd.ddsCaps.dwCaps & DDSCAPS_COMPLEX)
+        {
+            /* Undocumented feature: if DDSCAPS_MIPMAP and DDSCAPS_COMPLEX are
+             * both set, but mipmap count isn't given, as many mipmap levels
+             * as necessary are created to get down to a size where either
+             * the width or the height of the texture is 1.
+             *
+             * This is needed by Anarchy Online. */
+            DWORD min = ddsd.dwWidth < ddsd.dwHeight ?
+                        ddsd.dwWidth : ddsd.dwHeight;
+            ddsd.u2.dwMipMapCount = round(log(min) / log(2.0)) + 1;
+        }
+        else
+            /* Create a single mipmap. */
+            ddsd.u2.dwMipMapCount = 1;
+ 
         ddsd.dwFlags |= DDSD_MIPMAPCOUNT;
-	ddsd.u2.dwMipMapCount = 1;
     }
     
     ddsd.dwFlags |= DDSD_PIXELFORMAT;
Index: dlls/ddraw/tests/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/ddraw/tests/Makefile.in,v
retrieving revision 1.3
diff -u -p -r1.3 Makefile.in
--- dlls/ddraw/tests/Makefile.in	16 Feb 2004 22:18:59 -0000	1.3
+++ dlls/ddraw/tests/Makefile.in	28 May 2005 21:13:56 -0000
@@ -6,7 +6,8 @@ TESTDLL   = ddraw.dll
 IMPORTS   = ddraw user32 gdi32 kernel32
 
 CTESTS = \
-	ddrawmodes.c
+	ddrawmodes.c \
+	dsurface.c
 
 @MAKE_TEST_RULES@
 
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ dlls/ddraw/tests/dsurface.c	2005-05-28 22:42:43.902006208 +0200
@@ -0,0 +1,162 @@
+/*
+ * Unit tests for (a few) ddraw surface functions
+ *
+ * Copyright (C) 2005 Antoine Chavasse (a.chavasse at gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include "wine/test.h"
+#include "ddraw.h"
+
+static LPDIRECTDRAW lpDD = NULL;
+
+static void CreateDirectDraw()
+{
+    HRESULT rc;
+
+    rc = DirectDrawCreate(NULL, &lpDD, NULL);
+    ok(rc==DD_OK,"DirectDrawCreate returned: %lx\n",rc);
+
+    rc = IDirectDraw_SetCooperativeLevel(lpDD, NULL, DDSCL_NORMAL);
+    ok(rc==DD_OK,"SetCooperativeLevel returned: %lx\n",rc);
+}
+
+
+static void ReleaseDirectDraw()
+{
+    if( lpDD != NULL )
+    {
+        IDirectDraw_Release(lpDD);
+        lpDD = NULL;
+    }
+}
+
+static void MipMapCreationTest()
+{
+    LPDIRECTDRAWSURFACE lpDDSMipMapTest;
+    DDSURFACEDESC ddsd;
+    HRESULT rc;
+
+    /* First mipmap creation test: create a surface with DDSCAPS_COMPLEX,
+       DDSCAPS_MIPMAP, and DDSD_MIPMAPCOUNT. This create the number of
+        requested mipmap levels. */
+    ddsd.dwSize = sizeof(ddsd);
+    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_MIPMAPCOUNT;
+    ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_COMPLEX | DDSCAPS_MIPMAP;
+    ddsd.dwMipMapCount = 3;
+    ddsd.dwWidth = 128;
+    ddsd.dwHeight = 32;
+    rc = IDirectDraw_CreateSurface(lpDD, &ddsd, &lpDDSMipMapTest, NULL);
+    ok(rc==DD_OK,"CreateSurface returned: %lx\n",rc);
+
+    /* Check the number of created mipmaps */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    rc = IDirectDrawSurface_GetSurfaceDesc(lpDDSMipMapTest, &ddsd);
+    ok(rc==DD_OK,"GetSurfaceDesc returned: %lx\n",rc);
+    ok(ddsd.dwFlags & DDSD_MIPMAPCOUNT,
+        "GetSurfaceDesc returned no mipmapcount.\n");
+    ok(ddsd.dwMipMapCount == 3, "Incorrect mipmap count: %ld.\n",
+        ddsd.dwMipMapCount);
+
+    /* Destroy the surface. */
+    IDirectDrawSurface_Release(lpDDSMipMapTest);
+
+
+    /* Second mipmap creation test: create a surface without a mipmap
+       count, with DDSCAPS_MIPMAP and without DDSCAPS_COMPLEX.
+       This creates a single mipmap level. */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_MIPMAP;
+    ddsd.dwWidth = 128;
+    ddsd.dwHeight = 32;
+    rc = IDirectDraw_CreateSurface(lpDD, &ddsd, &lpDDSMipMapTest, NULL);
+    ok(rc==DD_OK,"CreateSurface returned: %lx\n",rc);
+
+    /* Check the number of created mipmaps */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    rc = IDirectDrawSurface_GetSurfaceDesc(lpDDSMipMapTest, &ddsd);
+    ok(rc==DD_OK,"GetSurfaceDesc returned: %lx\n",rc);
+    ok(ddsd.dwFlags & DDSD_MIPMAPCOUNT,
+        "GetSurfaceDesc returned no mipmapcount.\n");
+    ok(ddsd.dwMipMapCount == 1, "Incorrect mipmap count: %ld.\n",
+        ddsd.dwMipMapCount);
+
+
+    /* Third mipmap creation test: create a surface with DDSCAPS_MIPMAP,
+        DDSCAPS_COMPLEX and without DDSD_MIPMAPCOUNT.
+       It's an undocumented features where a chain of mipmaps, starting from
+       he specified size and down to the smallest size, is automatically
+       created.
+       Anarchy Online needs this feature to work. */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_COMPLEX | DDSCAPS_MIPMAP;
+    ddsd.dwWidth = 128;
+    ddsd.dwHeight = 32;
+    rc = IDirectDraw_CreateSurface(lpDD, &ddsd, &lpDDSMipMapTest, NULL);
+    ok(rc==DD_OK,"CreateSurface returned: %lx\n",rc);
+
+    /* Check the number of created mipmaps */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    rc = IDirectDrawSurface_GetSurfaceDesc(lpDDSMipMapTest, &ddsd);
+    ok(rc==DD_OK,"GetSurfaceDesc returned: %lx\n",rc);
+    ok(ddsd.dwFlags & DDSD_MIPMAPCOUNT,
+        "GetSurfaceDesc returned no mipmapcount.\n");
+    ok(ddsd.dwMipMapCount == 6, "Incorrect mipmap count: %ld.\n",
+        ddsd.dwMipMapCount);
+
+
+    /* Fourth mipmap creation test: same as above with a different texture
+       size.
+       The purpose is to verify that the number of generated mipmaps is
+       dependant on the smallest dimension. */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
+    ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_COMPLEX | DDSCAPS_MIPMAP;
+    ddsd.dwWidth = 32;
+    ddsd.dwHeight = 64;
+    rc = IDirectDraw_CreateSurface(lpDD, &ddsd, &lpDDSMipMapTest, NULL);
+    ok(rc==DD_OK,"CreateSurface returned: %lx\n",rc);
+
+    /* Check the number of created mipmaps */
+    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
+    ddsd.dwSize = sizeof(ddsd);
+    rc = IDirectDrawSurface_GetSurfaceDesc(lpDDSMipMapTest, &ddsd);
+    ok(rc==DD_OK,"GetSurfaceDesc returned: %lx\n",rc);
+    ok(ddsd.dwFlags & DDSD_MIPMAPCOUNT,
+        "GetSurfaceDesc returned no mipmapcount.\n");
+    ok(ddsd.dwMipMapCount == 6, "Incorrect mipmap count: %ld.\n",
+        ddsd.dwMipMapCount);
+
+    /* Destroy the surface. */
+    IDirectDrawSurface_Release(lpDDSMipMapTest);
+}
+
+
+START_TEST(dsurface)
+{
+    CreateDirectDraw();
+    MipMapCreationTest();
+    ReleaseDirectDraw();
+}




More information about the wine-patches mailing list