@@ -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( |
@@ -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(); |
@@ -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() |