d3d1x: rework DXGI for occlusion testing and default width/height
authorLuca Barbieri <luca@luca-barbieri.com>
Mon, 27 Sep 2010 18:25:17 +0000 (20:25 +0200)
committerLuca Barbieri <luca@luca-barbieri.com>
Mon, 27 Sep 2010 20:20:53 +0000 (22:20 +0200)
src/gallium/state_trackers/d3d1x/dxgi/src/dxgi_native.cpp
src/gallium/state_trackers/d3d1x/gd3dapi/galliumdxgi.idl
src/gallium/state_trackers/d3d1x/winedlls/dxgi/dxgi_dll.c

index a75a953abfb1e7424437562788a96578bc9486bc..e1c34611d141d049476537fa7f3b6676f753d488 100644 (file)
@@ -65,10 +65,12 @@ struct GalliumDXGIObject : public GalliumPrivateDataComObject<Base>
 
 COM_INTERFACE(IGalliumDXGIBackend, IUnknown)
 
+// TODO: somehow check whether the window is fully obscured or not
 struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend>
 {
-       virtual void * STDMETHODCALLTYPE BeginPresent(
+       virtual HRESULT STDMETHODCALLTYPE BeginPresent(
                HWND hwnd,
+               void** present_cookie,
                void** window,
                RECT *rect,
                RGNDATA **rgndata,
@@ -84,7 +86,8 @@ struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend>
 
                // yes, because we like things looking good
                *preserve_aspect_ratio = TRUE;
-               return 0;
+               *present_cookie = 0;
+               return S_OK;
        }
 
        virtual void STDMETHODCALLTYPE EndPresent(
@@ -92,6 +95,45 @@ struct GalliumDXGIIdentityBackend : public GalliumComObject<IGalliumDXGIBackend>
                void* present_cookie
        )
        {}
+
+       virtual HRESULT STDMETHODCALLTYPE TestPresent(HWND hwnd)
+       {
+               return S_OK;
+       }
+
+        virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
+                HWND hwnd,
+                unsigned* width,
+                unsigned* height
+        )
+        {
+                *width = 0;
+                *height = 0;
+                return S_OK;
+        }
+};
+
+// TODO: maybe install an X11 error hook, so we can return errors properly
+struct GalliumDXGIX11IdentityBackend : public GalliumDXGIIdentityBackend
+{
+       Display* dpy;
+
+       GalliumDXGIX11IdentityBackend(Display* dpy)
+       : dpy(dpy)
+       {}
+
+       virtual HRESULT STDMETHODCALLTYPE GetPresentSize(
+               HWND hwnd,
+               unsigned* width,
+               unsigned* height
+       )
+        {
+               XWindowAttributes xwa;
+               XGetWindowAttributes(dpy, (Window)hwnd, &xwa);
+               *width = xwa.width;
+               *height = xwa.height;
+               return S_OK;
+        }
 };
 
 struct GalliumDXGIFactory : public GalliumDXGIObject<IDXGIFactory1, IUnknown>
@@ -107,6 +149,8 @@ struct GalliumDXGIFactory : public GalliumDXGIObject<IDXGIFactory1, IUnknown>
         {
                if(p_backend)
                        backend = p_backend;
+               else if(!strcmp(platform->name, "X11"))
+                       backend.reset(new GalliumDXGIX11IdentityBackend((Display*)display));
                else
                        backend.reset(new GalliumDXGIIdentityBackend());
        }
@@ -887,6 +931,10 @@ struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDX
 
                blitter.reset(new dxgi_blitter(pipe));
                window = 0;
+
+               hr = resolve_zero_width_height(true);
+               if(!SUCCEEDED(hr))
+                       throw hr;
        }
 
        void init_for_window()
@@ -1006,12 +1054,36 @@ struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDX
                return true;
        }
 
+       HRESULT resolve_zero_width_height(bool force = false)
+       {
+               if(!force && desc.BufferDesc.Width && desc.BufferDesc.Height)
+                       return S_OK;
+
+               unsigned width, height;
+               HRESULT hr = parent->backend->GetPresentSize(desc.OutputWindow, &width, &height);
+               if(!SUCCEEDED(hr))
+                       return hr;
+
+               // On Windows, 8 is used, and a debug message saying so gets printed
+               if(!width)
+                       width = 8;
+               if(!height)
+                       height = 8;
+
+               if(!desc.BufferDesc.Width)
+                       desc.BufferDesc.Width = width;
+               if(!desc.BufferDesc.Height)
+                       desc.BufferDesc.Height = height;
+               return S_OK;
+       }
+
        virtual HRESULT STDMETHODCALLTYPE Present(
                UINT sync_interval,
                UINT flags)
        {
+               HRESULT hr;
                if(flags & DXGI_PRESENT_TEST)
-                       return S_OK;
+                       return parent->backend->TestPresent(desc.OutputWindow);
 
                if(!buffer0)
                {
@@ -1030,7 +1102,11 @@ struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDX
                struct pipe_resource* src;
                struct pipe_surface* dst_surface;
 
-               void* present_cookie = parent->backend->BeginPresent(desc.OutputWindow, &cur_window, &rect, &rgndata, &preserve_aspect_ratio);
+               void* present_cookie;
+               hr = parent->backend->BeginPresent(desc.OutputWindow, &present_cookie, &cur_window, &rect, &rgndata, &preserve_aspect_ratio);
+               if(hr != S_OK)
+                       return hr;
+
                if(!cur_window || rect.left >= rect.right || rect.top >= rect.bottom)
                        goto end_present;
 
@@ -1051,6 +1127,9 @@ struct GalliumDXGISwapChain : public GalliumDXGIObject<IDXGISwapChain, GalliumDX
                src = gallium_buffer0;
                dst_surface = 0;
 
+               assert(src);
+               assert(dst);
+
                /* TODO: sharing the context for blitting won't work correctly if queries are active
                 * Hopefully no one is crazy enough to keep queries active while presenting, expecting
                 * sensible results.
@@ -1235,7 +1314,7 @@ end_present:
                desc.BufferDesc.Width = width;
                desc.BufferDesc.Height = height;
                desc.Flags = swap_chain_flags;
-               return S_OK;
+               return resolve_zero_width_height();
        }
 
        virtual HRESULT STDMETHODCALLTYPE ResizeTarget(
index 92fda3385b9f26b33fe6d3ce339b8f0720fb0b92..c6233c85b9921a6ebfa741c400708d3f747f103d 100644 (file)
@@ -65,7 +65,7 @@ import "../d3dapi/dxgi.idl";
 [object, local, uuid("c22d2f85-f7dd-40b0-a50b-5d308f973c5e")]
 interface IGalliumDXGIBackend : IUnknown
 {
-       /* Returns a cookie that is passed to EndPresent
+       /* *present_cookie is set to a cookie that is passed to EndPresent
         *
         * *window and *rect are the window and subrectangle
         * to present in.
@@ -81,16 +81,20 @@ interface IGalliumDXGIBackend : IUnknown
         * *rgndata is valid until EndPresent is called, at which point EndPresent
         * may free the data.
         *
-        * If window is set 0, the window is fully obscured, so don't present
-        * anything, and tell the app of the obscuration.
+        * However, the rect field should still be set as normal if possible (especially
+        * the dimension)..
         *
         * If preserve_aspect_ratio is set, *rgndata will be ignored. This
         * limitation may be lifted in future versions.
         *
-        * EndPresent is still called even if you return 0 in window.
+        * If the window is fully obscured, return DXGI_STATUS_OCCLUDED.
+        * Everything else is ignored in that case.
+        *
+        * EndPresent is only called when S_OK is returned.
         */
-       void* BeginPresent(
+       HRESULT BeginPresent(
                [in] HWND hwnd,
+               [out] void** present_cookie,
                [out] void** window,
                [out] RECT* rect,
                [out] struct _RGNDATA** rgndata,
@@ -101,6 +105,18 @@ interface IGalliumDXGIBackend : IUnknown
                [in] HWND hwnd,
                [out] void* present_cookie
        );
+
+       /* If the window is fully obscured, return DXGI_STATUS_OCCLUDED, else S_OK */
+       HRESULT TestPresent(
+               [in] HWND hwnd
+       );
+
+       /* Get size of rectangle that would be returned by BeginPresent */
+       HRESULT GetPresentSize(
+               [in] HWND hwnd,
+               [out] unsigned* width,
+               [out] unsigned* height
+       );
 }
 
 void GalliumDXGIUseNothing();
index 5a900046df237246adf919d6b1967e10ff52812d..43e2980afdb2abed271624944274fb5c3b23356f 100644 (file)
@@ -58,16 +58,17 @@ struct WineDXGIBackend
                LONG ref;
 };
 
-static void* STDMETHODCALLTYPE WineDXGIBackend_BeginPresent(
+static HRESULT STDMETHODCALLTYPE WineDXGIBackend_BeginPresent(
        IGalliumDXGIBackend* This,
        HWND hwnd,
+       void** ppresent_cookie,
        void** pwindow,
        RECT* prect,
        RGNDATA** prgndata,
        BOOL* ppreserve_aspect_ratio)
 {
        /* this is the parent HWND which actually has an X11 window associated */
-       HWND x11_hwnd = GetAncestor(hwnd, GA_ROOT);
+       HWND x11_hwnd;
        HDC hdc;
        RECT client_rect;
        POINT x11_hwnd_origin_from_screen;
@@ -77,17 +78,14 @@ static void* STDMETHODCALLTYPE WineDXGIBackend_BeginPresent(
        unsigned code = X11DRV_GET_DRAWABLE;
        unsigned rgndata_size;
        RGNDATA* rgndata;
-
-       hdc = GetDC(x11_hwnd);
-       ExtEscape(hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(drawable), (LPTSTR)&drawable);
-
-       GetDCOrgEx(hdc, &x11_hwnd_origin_from_screen);
-       ReleaseDC(x11_hwnd, hdc);
+       RECT rgn_box;
+       int rgn_box_type;
 
        hdc = GetDC(hwnd);
        GetDCOrgEx(hdc, &hwnd_origin_from_screen);
        hrgn = CreateRectRgn(0, 0, 0, 0);
        GetRandomRgn(hdc, hrgn, SYSRGN);
+       rgn_box_type = GetRgnBox(hrgn, &rgn_box);
 
        /* the coordinate system differs depending on whether Wine is
         * pretending to be Win9x or WinNT, so match that behavior.
@@ -96,6 +94,25 @@ static void* STDMETHODCALLTYPE WineDXGIBackend_BeginPresent(
                OffsetRgn(hrgn, -hwnd_origin_from_screen.x, -hwnd_origin_from_screen.y);
        ReleaseDC(hwnd, hdc);
 
+       if(rgn_box_type == NULLREGION)
+       {
+               DeleteObject(hrgn);
+               return DXGI_STATUS_OCCLUDED;
+       }
+
+       rgndata_size = GetRegionData(hrgn, 0, NULL);
+       rgndata = HeapAlloc(GetProcessHeap(), 0, rgndata_size);
+       GetRegionData(hrgn, rgndata_size, rgndata);
+       DeleteObject(hrgn);
+       *prgndata = rgndata;
+
+       x11_hwnd = GetAncestor(hwnd, GA_ROOT);
+       hdc = GetDC(x11_hwnd);
+       ExtEscape(hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(drawable), (LPTSTR)&drawable);
+
+       GetDCOrgEx(hdc, &x11_hwnd_origin_from_screen);
+       ReleaseDC(x11_hwnd, hdc);
+
        *pwindow = (void*)drawable;
        GetClientRect(hwnd, &client_rect);
 
@@ -105,28 +122,59 @@ static void* STDMETHODCALLTYPE WineDXGIBackend_BeginPresent(
        prect->right = prect->left + client_rect.right;
        prect->bottom = prect->top + client_rect.bottom;
 
-       rgndata_size = GetRegionData(hrgn, 0, NULL);
-       rgndata = HeapAlloc(GetProcessHeap(), 0, rgndata_size);
-       GetRegionData(hrgn, rgndata_size, rgndata);
-       *prgndata = rgndata;
-
        // Windows doesn't preserve the aspect ratio
        // TODO: maybe let the user turn this on somehow
        *ppreserve_aspect_ratio = FALSE;
 
-       DeleteObject(hrgn);
+       *ppresent_cookie = rgndata;
 
-       return rgndata;
+       // TODO: check for errors and return them
+       return S_OK;
 }
 
 static void STDMETHODCALLTYPE WineDXGIBackend_EndPresent(
-               IGalliumDXGIBackend* This,
-               HWND hwnd,
-               void *present_cookie)
+       IGalliumDXGIBackend* This,
+       HWND hwnd,
+       void *present_cookie)
 {
        HeapFree(GetProcessHeap(), 0, present_cookie);
 }
 
+static HRESULT STDMETHODCALLTYPE WineDXGIBackend_TestPresent(
+       IGalliumDXGIBackend* This,
+       HWND hwnd)
+{
+       HDC hdc;
+       HRGN hrgn;
+       RECT rgn_box;
+       int rgn_box_type;
+
+       // TODO: is there a simpler way to check this?
+       hdc = GetDC(hwnd);
+       hrgn = CreateRectRgn(0, 0, 0, 0);
+       GetRandomRgn(hdc, hrgn, SYSRGN);
+       rgn_box_type = GetRgnBox(hrgn, &rgn_box);
+       DeleteObject(hrgn);
+       ReleaseDC(hwnd, hdc);
+
+       return rgn_box_type == NULLREGION ? DXGI_STATUS_OCCLUDED : S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE WineDXGIBackend_GetPresentSize(
+       IGalliumDXGIBackend* This,
+       HWND hwnd,
+       unsigned* width,
+       unsigned* height)
+{
+       RECT client_rect;
+       GetClientRect(hwnd, &client_rect);
+       *width = client_rect.right - client_rect.left;
+       *height = client_rect.bottom - client_rect.top;
+
+       // TODO: check for errors and return them
+       return S_OK;
+}
+
 /* Wine should switch to C++ at least to be able to implement COM interfaces in a sensible way,
  * instead of this ridiculous amount of clumsy duplicated code everywhere
  * C++ exists exactly to avoid having to write the following code */
@@ -165,7 +213,9 @@ static IGalliumDXGIBackendVtbl WineDXGIBackend_vtbl =
        WineDXGIBackend_AddRef,
        WineDXGIBackend_Release,
        WineDXGIBackend_BeginPresent,
-       WineDXGIBackend_EndPresent
+       WineDXGIBackend_EndPresent,
+       WineDXGIBackend_TestPresent,
+       WineDXGIBackend_GetPresentSize
 };
 
 IGalliumDXGIBackend* new_WineDXGIBackend()