WineD3D: Use GL_APPLE_client_storage if available

Stefan Dösinger stefan at codeweavers.com
Sat Mar 31 16:12:32 CDT 2007


From reading apple docs and mailing lists, it seems that apple beats everyone 
to using this extension.

The main motivation was to fix an issue with glGetTexImage returning incorrect 
data. I suspect that this is caused by out of vm space issues. It could also 
be a general bug in MacOS' texture management, I have to investigate that.
-------------- next part --------------
From 196da1ee96a9055088d16b2aa80962c734cd5745 Mon Sep 17 00:00:00 2001
From: Stefan Doesinger <stefan at codeweavers.com>
Date: Sat, 31 Mar 2007 23:02:37 +0200
Subject: [PATCH] WineD3D: Use GL_APPLE_client_storage if available

GL_APPLE_client_storage allows applications to tell opengl that they
will keep a backup copy of the texture, and give them a pointer to that
backup copy. This is slightly more efficient with dynamic textures where
wined3d would keep its copy in any way, and it improves the virtual
memory use since the wined3d copy can be in the areas wine has to block
because of library loading.
---
 dlls/wined3d/context.c         |    8 ++++
 dlls/wined3d/device.c          |    1 +
 dlls/wined3d/surface.c         |   71 +++++++++++++++++++++++++++++++++++++--
 dlls/wined3d/volume.c          |    6 +++-
 dlls/wined3d/wined3d_private.h |    5 ++-
 5 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/dlls/wined3d/context.c b/dlls/wined3d/context.c
index 02bc406..d21f5b8 100644
--- a/dlls/wined3d/context.c
+++ b/dlls/wined3d/context.c
@@ -352,6 +352,14 @@ WineD3DContext *CreateContext(IWineD3DDeviceImpl *This, IWineD3DSurfaceImpl *tar
     glPixelStorei(GL_UNPACK_ALIGNMENT, SURFACE_ALIGNMENT);
     checkGLcall("glPixelStorei(GL_UNPACK_ALIGNMENT, SURFACE_ALIGNMENT);");
 
+    if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
+        /* Most textures will use client storage if supported. Exceptions are non-native power of 2 textures
+         * and textures in DIB sections(due to the memory protection).
+         */
+        glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
+        checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
+    }
+
     if(oldDrawable && oldCtx) {
         glXMakeCurrent(display, oldDrawable, oldCtx);
     }
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 7863714..ffabb2e 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -5470,6 +5470,7 @@ static void updateSurfaceDesc(IWineD3DSurfaceImpl *surface, WINED3DPRESENT_PARAM
         glDeleteTextures(1, &surface->glDescription.textureName);
         LEAVE_GL();
         surface->glDescription.textureName = 0;
+        surface->Flags &= ~SFLAG_CLIENT;
     }
     if(surface->pow2Width != pPresentationParameters->BackBufferWidth ||
        surface->pow2Height != pPresentationParameters->BackBufferHeight) {
diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
index 8ac924b..21ac153 100644
--- a/dlls/wined3d/surface.c
+++ b/dlls/wined3d/surface.c
@@ -167,6 +167,11 @@ static void surface_upload_data(IWineD3DSurfaceImpl *This, GLsizei width, GLsize
         if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
             FIXME("Using DXT1/3/5 without advertized support\n");
         } else {
+            if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
+                /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
+                This->Flags |= SFLAG_CLIENT;
+            }
+
             TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
             ENTER_GL();
             /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
@@ -188,6 +193,8 @@ static void surface_upload_data(IWineD3DSurfaceImpl *This, GLsizei width, GLsize
 }
 
 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
+    BOOL enable_client_storage = FALSE;
+
     TRACE("(%p) : Creating surface (target %#x)  level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
             This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
 
@@ -201,9 +208,32 @@ static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal,
 
     ENTER_GL();
 
-    glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, NULL);
+    if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
+        if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
+            /* In some cases we want to disable client storage.
+             * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
+             * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
+             * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
+             * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
+             * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
+             */
+            glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
+            checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
+            This->Flags &= SFLAG_CLIENT;
+            enable_client_storage = TRUE;
+        } else {
+            This->Flags |= SFLAG_CLIENT;
+            /* Below point opengl to our allocated texture memory */
+        }
+    }
+    glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
+                 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
     checkGLcall("glTexImage2D");
 
+    if(enable_client_storage) {
+        glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
+        checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
+    }
     LEAVE_GL();
 }
 
@@ -1203,6 +1233,7 @@ HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
     if(!This->hDC) {
         int extraline = 0;
         SYSTEM_INFO sysInfo;
+        void *oldmem = This->resource.allocatedMemory;
 
         switch (This->bytesPerPixel) {
             case 2:
@@ -1306,7 +1337,6 @@ HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
         if(This->resource.allocatedMemory) {
             memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
             /* We won't need that any more */
-            HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
         } else {
             /* This is to make LockRect read the gl Texture although memory is allocated */
             This->Flags &= ~SFLAG_INSYSMEM;
@@ -1326,6 +1356,11 @@ HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
                       FALSE);
 
         This->Flags |= SFLAG_DIBSECTION;
+
+        if(This->Flags & SFLAG_CLIENT) {
+            IWineD3DSurface_PreLoad(iface);
+        }
+        HeapFree(GetProcessHeap(), 0, oldmem);
     }
 
     /* Lock the surface */
@@ -1874,6 +1909,9 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface) {
             surface_upload_data(This, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
         }
     } else {
+        /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
+         * changed. So also keep track of memory changes. In this case the texture has to be reallocated
+         */
         surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
         if (mem) {
             surface_upload_data(This, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
@@ -2161,6 +2199,7 @@ HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
     }
 
     if(Mem && Mem != This->resource.allocatedMemory) {
+        void *release = NULL;
 
         /* Do I have to copy the old surface content? */
         if(This->Flags & SFLAG_DIBSECTION) {
@@ -2177,14 +2216,31 @@ HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
                 This->hDC = NULL;
                 This->Flags &= ~SFLAG_DIBSECTION;
         } else if(!(This->Flags & SFLAG_USERPTR)) {
-            HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
+            release = This->resource.allocatedMemory;
         }
         This->resource.allocatedMemory = Mem;
-        This->Flags |= SFLAG_USERPTR;
+        This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
+
+        /* Now the surface memory is most up do date. Invalidate drawable and texture */
+        This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
+
+        /* For client textures opengl has to be notified */
+        if(This->Flags & SFLAG_CLIENT) {
+            IWineD3DSurface_PreLoad(iface);
+            /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
+        }
+
+        /* Now free the old memory if any */
+        HeapFree(GetProcessHeap(), 0, release);
     } else if(This->Flags & SFLAG_USERPTR) {
         /* Lockrect and GetDC will re-create the dib section and allocated memory */
         This->resource.allocatedMemory = NULL;
         This->Flags &= ~SFLAG_USERPTR;
+
+        if(This->Flags & SFLAG_CLIENT) {
+            /* This respecifies an empty texture and opengl knows that the old memory is gone */
+            IWineD3DSurface_PreLoad(iface);
+        }
     }
     return WINED3D_OK;
 }
@@ -3200,6 +3256,13 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
         This->glRect.bottom = This->pow2Height;
     }
 
+    if(GL_SUPPORT(APPLE_CLIENT_STORAGE) && This->resource.allocatedMemory == NULL) {
+        /* Make sure that memory is allocated from the start if we are going to use GL_APPLE_client_storage.
+         * Otherwise a glTexImage2D with a NULL pointer may be done, e.g. when blitting or with offscreen render
+         * targets, thus the client storage wouldn't be used for that texture
+         */
+        This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
+    }
     return WINED3D_OK;
 }
 
diff --git a/dlls/wined3d/volume.c b/dlls/wined3d/volume.c
index 3fb3036..9033c8a 100644
--- a/dlls/wined3d/volume.c
+++ b/dlls/wined3d/volume.c
@@ -291,7 +291,11 @@ static HRESULT WINAPI IWineD3DVolumeImpl_LoadTexture(IWineD3DVolume *iface, GLen
         checkGLcall("glTexImage3D");
     } else
         WARN("This OpenGL implementation doesn't support 3D textures\n");
-    
+
+    /* When adding code releasing This->resource.allocatedMemory to save data keep in mind that
+     * GL_UNPACK_CLIENT_STORAGE_APPLE is enabled by default if supported(GL_APPLE_client_storage).
+     * Thus do not release This->resource.allocatedMemory if GL_APPLE_client_storage is supported.
+     */
     return WINED3D_OK;
 
 }
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 3f69171..976b867 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1117,6 +1117,7 @@ HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *S
 #define SFLAG_LOST        0x00002000 /* Surface lost flag for DDraw */
 #define SFLAG_USERPTR     0x00004000 /* The application allocated the memory for this surface */
 #define SFLAG_GLCKEY      0x00008000 /* The gl texture was created with a color key */
+#define SFLAG_CLIENT      0x00010000 /* GL_APPLE_client_storage is used on that texture */
 
 /* In some conditions the surface memory must not be freed:
  * SFLAG_OVERSIZE: Not all data can be kept in GL
@@ -1125,6 +1126,7 @@ HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *S
  * SFLAG_LOCKED: The app requires access to the surface data
  * SFLAG_DYNLOCK: Avoid freeing the data for performance
  * SFLAG_DYNCHANGE: Same reason as DYNLOCK
+ * SFLAG_CLIENT: OpenGL uses our memory as backup
  */
 #define SFLAG_DONOTFREE  (SFLAG_OVERSIZE   | \
                           SFLAG_CONVERTED  | \
@@ -1132,7 +1134,8 @@ HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *S
                           SFLAG_LOCKED     | \
                           SFLAG_DYNLOCK    | \
                           SFLAG_DYNCHANGE  | \
-                          SFLAG_USERPTR)
+                          SFLAG_USERPTR    | \
+                          SFLAG_CLIENT)
 
 BOOL CalculateTexRect(IWineD3DSurfaceImpl *This, RECT *Rect, float glTexCoord[4]);
 
-- 
1.4.4.3



More information about the wine-patches mailing list