dcomp: one compositor per process, SetOffsetY/SetClip, last-presented-buffer readback. Needs 02 + 03. LGPL-2.1-or-later. diff --git a/dlls/dcomp/Makefile.in b/dlls/dcomp/Makefile.in index 721f99d..3234bf0 100644 --- a/dlls/dcomp/Makefile.in +++ b/dlls/dcomp/Makefile.in @@ -1,9 +1,10 @@ MODULE = dcomp.dll -IMPORTS = d2d1 dwmapi user32 gdi32 d3dcompiler +IMPORTS = uuid dwmapi user32 gdi32 VER_PRODUCTVERSION = 10,0,14393,0 SOURCES = \ + compositor.c \ dcomp_private_iface.idl \ device.c \ surface.c \ diff --git a/dlls/dcomp/compositor.c b/dlls/dcomp/compositor.c new file mode 100644 index 0000000..9d84d1f --- /dev/null +++ b/dlls/dcomp/compositor.c @@ -0,0 +1,843 @@ +/* + * DirectComposition window compositor + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* There is no DWM to hand composition trees to, so a single thread per + * process emulates it. Every pass it takes a snapshot of all composition + * targets under the dcomp lock, then, without holding that lock, reads the + * last presented frame of each content object back through D3D11 and draws + * the result of each window's targets into that window with GDI. + * + * All targets of one window are drawn in one pass and in DirectComposition + * order: non-topmost targets below topmost targets, each tree depth-first in + * z-order, with visual offsets and rectangle clips applied. A window is only + * redrawn when one of its layers presented a new frame, the tree changed, or + * the window lost what was drawn into it. */ + +#include +#include + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "dcomp_private.h" +#include "dxgi1_2.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dcomp); + +/* All targets of the process, protected by the dcomp lock. */ +static struct list compositor_targets = LIST_INIT(compositor_targets); +static BOOL compositor_running; +static LONG compositor_generation; + +/* Content read back from a swapchain or a composition surface. Only the + * compositor thread uses these. They hold no references: an entry is dropped + * as soon as its content is no longer part of any composition tree, so that + * the compositor never keeps an application's devices alive. */ +struct layer_frame +{ + struct list entry; + const IUnknown *content; + UINT64 serial; + /* Swapchain description, refreshed when the buffers are resized. GetDesc1() + * is not called every pass because it prints a FIXME each time. */ + BOOL have_desc; + DXGI_SWAP_CHAIN_DESC1 desc; + UINT width, height; + DWORD *bits; + BOOL used; +}; + +/* One layer of a window, as found in the snapshot. */ +struct layer +{ + HWND hwnd; + BOOL topmost; + IUnknown *content; + GUID content_iid; + int x, y; + RECT clip; +}; + +struct layer_key +{ + IUnknown *content; + UINT64 serial; + UINT width, height; + int x, y; + RECT clip; +}; + +struct window_cache +{ + struct list entry; + HWND hwnd; + HDC dc; + HBITMAP bitmap, old_bitmap; + DWORD *bits; /* what was last drawn into the window */ + DWORD *compose; /* the frame being composed */ + UINT width, height; + struct layer_key *keys; + unsigned int key_count; + LONG generation; + DWORD last_blit; + BOOL valid, needs_blit, used; +}; + +/* State owned by one compositor thread. */ +struct compositor +{ + struct list layer_frames; + struct list window_caches; +}; + +void compositor_add_target(struct composition_target *target) +{ + dcomp_lock(); + list_add_tail(&compositor_targets, &target->compositor_entry); + InterlockedIncrement(&compositor_generation); + dcomp_unlock(); +} + +/* Called with the dcomp lock held. */ +void compositor_remove_target(struct composition_target *target) +{ + list_remove(&target->compositor_entry); + InterlockedIncrement(&compositor_generation); +} + +static void snapshot_visual(struct layer **layers, unsigned int *count, unsigned int *capacity, + const struct composition_target *target, const struct composition_visual *visual, + int x, int y, RECT clip) +{ + const struct composition_visual *child; + struct layer *layer; + RECT visual_clip; + + x += (int)visual->offset_x; + y += (int)visual->offset_y; + + if (visual->has_clip) + { + visual_clip.left = x + (int)visual->clip.left; + visual_clip.top = y + (int)visual->clip.top; + visual_clip.right = x + (int)visual->clip.right; + visual_clip.bottom = y + (int)visual->clip.bottom; + if (!IntersectRect(&clip, &clip, &visual_clip)) + return; + } + + if (visual->content) + { + if (*count == *capacity) + { + unsigned int new_capacity = max(16, *capacity * 2); + struct layer *new_layers; + + if (!(new_layers = realloc(*layers, new_capacity * sizeof(**layers)))) + return; + *layers = new_layers; + *capacity = new_capacity; + } + layer = &(*layers)[(*count)++]; + layer->hwnd = target->hwnd; + layer->topmost = !!target->topmost; + layer->content = visual->content; + IUnknown_AddRef(layer->content); + layer->content_iid = visual->content_iid; + layer->x = x; + layer->y = y; + layer->clip = clip; + } + + LIST_FOR_EACH_ENTRY(child, &visual->child_visuals, struct composition_visual, entry) + snapshot_visual(layers, count, capacity, target, child, x, y, clip); +} + +/* Takes the snapshot. Returns FALSE when there are no targets left. */ +static BOOL take_snapshot(struct layer **layers, unsigned int *count, LONG *generation) +{ + unsigned int capacity = 0, pass; + struct composition_target *target; + static const RECT unlimited = {INT_MIN / 2, INT_MIN / 2, INT_MAX / 2, INT_MAX / 2}; + BOOL any; + + *layers = NULL; + *count = 0; + + dcomp_lock(); + any = !list_empty(&compositor_targets); + *generation = compositor_generation; + /* Non-topmost targets are composed below topmost targets. */ + for (pass = 0; pass < 2; ++pass) + { + LIST_FOR_EACH_ENTRY(target, &compositor_targets, struct composition_target, compositor_entry) + { + if (!target->root || target->shared_visual_handle || !!target->topmost != (pass == 1)) + continue; + snapshot_visual(layers, count, &capacity, target, + impl_from_IDCompositionVisual(target->root), 0, 0, unlimited); + } + } + if (!any) + compositor_running = FALSE; + dcomp_unlock(); + + return any; +} + +static struct layer_frame *get_layer_frame(struct compositor *compositor, IUnknown *content) +{ + struct layer_frame *frame; + + LIST_FOR_EACH_ENTRY(frame, &compositor->layer_frames, struct layer_frame, entry) + { + if (frame->content == content) + return frame; + } + + if (!(frame = calloc(1, sizeof(*frame)))) + return NULL; + frame->content = content; + list_add_tail(&compositor->layer_frames, &frame->entry); + return frame; +} + +static void free_layer_frame(struct layer_frame *frame) +{ + list_remove(&frame->entry); + free(frame->bits); + free(frame); +} + +static HRESULT get_swapchain_texture(IDXGISwapChain1 *swapchain, struct layer_frame *frame, + ID3D11Texture2D **texture) +{ + D3D11_TEXTURE2D_DESC texture_desc; + unsigned int attempt; + HRESULT hr = E_FAIL; + + for (attempt = 0; attempt < 2; ++attempt) + { + if (!frame->have_desc) + { + if (FAILED(hr = IDXGISwapChain1_GetDesc1(swapchain, &frame->desc))) + return hr; + frame->have_desc = TRUE; + } + + /* Wine's swapchains rotate their buffers on present: the most recently + * presented frame is in the last buffer, and buffer 0 is the one the + * application draws the next frame into. */ + hr = IDXGISwapChain1_GetBuffer(swapchain, frame->desc.BufferCount ? frame->desc.BufferCount - 1 : 0, + &IID_ID3D11Texture2D, (void **)texture); + if (SUCCEEDED(hr)) + { + ID3D11Texture2D_GetDesc(*texture, &texture_desc); + if (texture_desc.Width == frame->desc.Width && texture_desc.Height == frame->desc.Height) + return hr; + ID3D11Texture2D_Release(*texture); + *texture = NULL; + } + frame->have_desc = FALSE; + } + return FAILED(hr) ? hr : E_FAIL; +} + +/* Finds the texture holding the last completed frame of a layer. Returns + * S_FALSE if there is no frame newer than the one in "frame". */ +static HRESULT get_layer_texture(const struct layer *layer, struct layer_frame *frame, + ID3D11Texture2D **texture, UINT64 *serial, BOOL *opaque) +{ + struct composition_surface *surface_impl; + IDCompositionSurface *surface; + IDXGISwapChain1 *swapchain; + UINT present_count; + HRESULT hr; + + *texture = NULL; + *opaque = FALSE; + + if (IsEqualGUID(&layer->content_iid, &IID_IDXGISwapChain1)) + { + if (FAILED(hr = IUnknown_QueryInterface(layer->content, &IID_IDXGISwapChain1, (void **)&swapchain))) + return hr; + + if (FAILED(hr = IDXGISwapChain1_GetLastPresentCount(swapchain, &present_count)) + || !present_count || (frame->bits && frame->serial == present_count)) + { + IDXGISwapChain1_Release(swapchain); + return FAILED(hr) ? hr : S_FALSE; + } + + hr = get_swapchain_texture(swapchain, frame, texture); + IDXGISwapChain1_Release(swapchain); + *serial = present_count; + *opaque = frame->desc.AlphaMode == DXGI_ALPHA_MODE_IGNORE; + return hr; + } + + if (IsEqualGUID(&layer->content_iid, &IID_IDCompositionSurface)) + { + if (FAILED(hr = IUnknown_QueryInterface(layer->content, &IID_IDCompositionSurface, (void **)&surface))) + return hr; + + surface_impl = unsafe_impl_from_IDCompositionSurface(surface); + *serial = (ULONG)ReadNoFence(&surface_impl->serial); + if (!IsEqualGUID(&surface_impl->physical_surface_iid, &IID_IDXGISurface)) + hr = E_NOTIMPL; + else if (frame->bits && frame->serial == *serial) + hr = S_FALSE; + else + hr = IUnknown_QueryInterface(surface_impl->physical_surface, &IID_ID3D11Texture2D, (void **)texture); + *opaque = surface_impl->alpha_mode == DXGI_ALPHA_MODE_IGNORE; + IDCompositionSurface_Release(surface); + return hr; + } + + return E_NOTIMPL; +} + +static HRESULT read_layer_frame(struct layer_frame *frame, ID3D11Texture2D *texture, BOOL opaque) +{ + static unsigned int warned_format, warned_multisample; + D3D11_TEXTURE2D_DESC desc, staging_desc; + D3D11_MAPPED_SUBRESOURCE map; + ID3D11DeviceContext *context; + ID3D11Texture2D *staging; + ID3D11Device *device; + BOOL swizzle, force_alpha; + const BYTE *src_row; + unsigned int x, y; + DWORD *dst, pixel; + HRESULT hr; + + ID3D11Texture2D_GetDesc(texture, &desc); + switch (desc.Format) + { + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + swizzle = FALSE; + force_alpha = opaque; + break; + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + swizzle = FALSE; + force_alpha = TRUE; + break; + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + swizzle = TRUE; + force_alpha = opaque; + break; + default: + if (!warned_format++) + FIXME("Unsupported content format %#x.\n", desc.Format); + return E_NOTIMPL; + } + if (desc.SampleDesc.Count > 1) + { + if (!warned_multisample++) + FIXME("Multisampled content is not supported.\n"); + return E_NOTIMPL; + } + + ID3D11Texture2D_GetDevice(texture, &device); + + staging_desc = desc; + staging_desc.MipLevels = 1; + staging_desc.ArraySize = 1; + staging_desc.Usage = D3D11_USAGE_STAGING; + staging_desc.BindFlags = 0; + staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + staging_desc.MiscFlags = 0; + if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &staging_desc, NULL, &staging))) + { + ERR("Failed to create a staging texture, hr %#lx.\n", hr); + ID3D11Device_Release(device); + return hr; + } + + if (frame->width != desc.Width || frame->height != desc.Height || !frame->bits) + { + free(frame->bits); + frame->width = frame->height = 0; + if (!(frame->bits = malloc((size_t)desc.Width * desc.Height * sizeof(*frame->bits)))) + { + ID3D11Texture2D_Release(staging); + ID3D11Device_Release(device); + return E_OUTOFMEMORY; + } + frame->width = desc.Width; + frame->height = desc.Height; + } + + /* Each call takes the wined3d lock by itself, and neither call depends on + * or changes pipeline state the application may have bound. */ + ID3D11Device_GetImmediateContext(device, &context); + ID3D11DeviceContext_CopySubresourceRegion(context, (ID3D11Resource *)staging, 0, 0, 0, 0, + (ID3D11Resource *)texture, 0, NULL); + hr = ID3D11DeviceContext_Map(context, (ID3D11Resource *)staging, 0, D3D11_MAP_READ, 0, &map); + TRACE("Read back %ux%u, format %#x, hr %#lx.\n", desc.Width, desc.Height, desc.Format, hr); + if (SUCCEEDED(hr)) + { + for (y = 0; y < desc.Height; ++y) + { + src_row = (const BYTE *)map.pData + (size_t)y * map.RowPitch; + dst = frame->bits + (size_t)y * desc.Width; + if (!swizzle && !force_alpha) + { + memcpy(dst, src_row, desc.Width * sizeof(*dst)); + continue; + } + for (x = 0; x < desc.Width; ++x) + { + pixel = ((const DWORD *)src_row)[x]; + if (swizzle) + pixel = (pixel & 0xff00ff00) | ((pixel & 0xff) << 16) | ((pixel >> 16) & 0xff); + if (force_alpha) + pixel |= 0xff000000; + dst[x] = pixel; + } + } + ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)staging, 0); + } + else + { + ERR("Failed to map the staging texture, hr %#lx.\n", hr); + } + ID3D11DeviceContext_Release(context); + ID3D11Texture2D_Release(staging); + ID3D11Device_Release(device); + return hr; +} + +static struct window_cache *get_window_cache(struct compositor *compositor, HWND hwnd) +{ + struct window_cache *cache; + + LIST_FOR_EACH_ENTRY(cache, &compositor->window_caches, struct window_cache, entry) + { + if (cache->hwnd == hwnd) + return cache; + } + + if (!(cache = calloc(1, sizeof(*cache)))) + return NULL; + cache->hwnd = hwnd; + list_add_tail(&compositor->window_caches, &cache->entry); + return cache; +} + +static void free_window_bitmap(struct window_cache *cache) +{ + if (cache->dc) + { + SelectObject(cache->dc, cache->old_bitmap); + DeleteObject(cache->bitmap); + DeleteDC(cache->dc); + } + free(cache->compose); + cache->dc = NULL; + cache->bitmap = cache->old_bitmap = NULL; + cache->bits = cache->compose = NULL; + cache->width = cache->height = 0; + cache->valid = FALSE; +} + +static void free_window_cache(struct window_cache *cache) +{ + list_remove(&cache->entry); + free_window_bitmap(cache); + free(cache->keys); + free(cache); +} + +static BOOL resize_window_bitmap(struct window_cache *cache, UINT width, UINT height) +{ + BITMAPINFO info = {{sizeof(info.bmiHeader)}}; + + if (cache->dc && cache->width == width && cache->height == height) + return TRUE; + + free_window_bitmap(cache); + if (!width || !height) + return FALSE; + + info.bmiHeader.biWidth = width; + info.bmiHeader.biHeight = -(LONG)height; + info.bmiHeader.biPlanes = 1; + info.bmiHeader.biBitCount = 32; + info.bmiHeader.biCompression = BI_RGB; + if (!(cache->compose = malloc((size_t)width * height * sizeof(*cache->compose)))) + return FALSE; + if (!(cache->dc = CreateCompatibleDC(NULL))) + { + free(cache->compose); + cache->compose = NULL; + return FALSE; + } + if (!(cache->bitmap = CreateDIBSection(cache->dc, &info, DIB_RGB_COLORS, (void **)&cache->bits, NULL, 0))) + { + DeleteDC(cache->dc); + cache->dc = NULL; + free(cache->compose); + cache->compose = NULL; + return FALSE; + } + cache->old_bitmap = SelectObject(cache->dc, cache->bitmap); + cache->width = width; + cache->height = height; + return TRUE; +} + +/* Premultiplied "over": dst = src + dst * (1 - src_alpha). */ +static void blend_layer(struct window_cache *cache, const struct layer_frame *frame, int x, int y, const RECT *clip) +{ + RECT dst_rect, frame_rect; + unsigned int i, j, width; + const DWORD *src; + DWORD *dst, s, d, inv; + + SetRect(&frame_rect, x, y, x + frame->width, y + frame->height); + SetRect(&dst_rect, 0, 0, cache->width, cache->height); + if (!IntersectRect(&dst_rect, &dst_rect, &frame_rect) || !IntersectRect(&dst_rect, &dst_rect, clip)) + return; + + width = dst_rect.right - dst_rect.left; + for (j = dst_rect.top; j < dst_rect.bottom; ++j) + { + src = frame->bits + (size_t)(j - y) * frame->width + (dst_rect.left - x); + dst = cache->compose + (size_t)j * cache->width + dst_rect.left; + for (i = 0; i < width; ++i) + { + s = src[i]; + if ((s >> 24) == 0xff || !dst[i]) + { + dst[i] = s; + continue; + } + if (!s) + continue; + d = dst[i]; + inv = 0xff - (s >> 24); + dst[i] = s + ((((d & 0x00ff00ff) * inv + 0x00800080) >> 8) & 0x00ff00ff) + + ((((d >> 8) & 0x00ff00ff) * inv + 0x00800080) & 0xff00ff00); + } + } +} + +/* Copies the composed frame into the window bitmap. Returns the bounding + * rectangle of the pixels that changed, and whether all of them are opaque. */ +static void update_window_bitmap(struct window_cache *cache, RECT *dirty, BOOL *opaque) +{ + unsigned int x, y, left, right; + const DWORD *src; + DWORD *dst; + + SetRectEmpty(dirty); + *opaque = TRUE; + for (y = 0; y < cache->height; ++y) + { + src = cache->compose + (size_t)y * cache->width; + dst = cache->bits + (size_t)y * cache->width; + if (!memcmp(src, dst, cache->width * sizeof(*dst))) + continue; + for (left = 0; src[left] == dst[left]; ++left); + for (right = cache->width; src[right - 1] == dst[right - 1]; --right); + for (x = left; x < right; ++x) + { + if ((src[x] >> 24) != 0xff) + *opaque = FALSE; + } + memcpy(dst + left, src + left, (right - left) * sizeof(*dst)); + if (IsRectEmpty(dirty)) + SetRect(dirty, left, y, right, y + 1); + else + { + dirty->left = min(dirty->left, (LONG)left); + dirty->right = max(dirty->right, (LONG)right); + dirty->bottom = y + 1; + } + } +} + +static BOOL bitmap_is_opaque(const struct window_cache *cache, const RECT *rect) +{ + unsigned int x, y; + const DWORD *row; + + for (y = rect->top; y < rect->bottom; ++y) + { + row = cache->bits + (size_t)y * cache->width; + for (x = rect->left; x < rect->right; ++x) + { + if ((row[x] >> 24) != 0xff) + return FALSE; + } + } + return TRUE; +} + +static BOOL window_has_pending_paint(HWND hwnd) +{ + RECT rect; + + return GetUpdateRect(hwnd, &rect, FALSE); +} + +static void compose_window(struct compositor *compositor, struct window_cache *cache, + const struct layer *layers, unsigned int count, LONG generation) +{ + static const BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; + struct layer_key *keys = NULL; + DPI_AWARENESS_CONTEXT old_context; + struct layer_frame *frame; + ID3D11Texture2D *texture; + unsigned int i, key_count = 0; + BOOL topmost = FALSE, changed, pending, opaque = FALSE; + DWORD now = GetTickCount(); + UINT64 serial = 0; + RECT client, dirty; + HRESULT hr; + HDC dc; + + if (!IsWindow(cache->hwnd)) + return; + + old_context = SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext(cache->hwnd)); + GetClientRect(cache->hwnd, &client); + + if (!IsWindowVisible(cache->hwnd)) + { + cache->needs_blit = TRUE; + goto done; + } + + if (!(keys = calloc(count ? count : 1, sizeof(*keys)))) + goto done; + + /* Bring the layer frames up to date. */ + for (i = 0; i < count; ++i) + { + if (layers[i].hwnd != cache->hwnd) + continue; + topmost |= layers[i].topmost; + + if (!(frame = get_layer_frame(compositor, layers[i].content))) + continue; + frame->used = TRUE; + + hr = get_layer_texture(&layers[i], frame, &texture, &serial, &opaque); + if (hr == S_OK) + { + if (SUCCEEDED(read_layer_frame(frame, texture, opaque))) + frame->serial = serial; + ID3D11Texture2D_Release(texture); + } + if (!frame->bits || FAILED(hr)) + continue; + + keys[key_count].content = layers[i].content; + keys[key_count].serial = frame->serial; + keys[key_count].width = frame->width; + keys[key_count].height = frame->height; + keys[key_count].x = layers[i].x; + keys[key_count].y = layers[i].y; + keys[key_count].clip = layers[i].clip; + ++key_count; + } + + changed = !cache->valid || cache->generation != generation || cache->key_count != key_count + || memcmp(cache->keys, keys, key_count * sizeof(*keys)) + || cache->width != client.right || cache->height != client.bottom; + + /* Whatever is drawn while a paint is pending may be erased by it, so draw + * everything again once it has been handled. */ + if ((pending = window_has_pending_paint(cache->hwnd))) + cache->needs_blit = TRUE; + + if (!changed && (pending || (!cache->needs_blit && now - cache->last_blit < 1000))) + goto done; + + SetRectEmpty(&dirty); + if (changed) + { + if (cache->width != client.right || cache->height != client.bottom) + { + if (!resize_window_bitmap(cache, client.right, client.bottom)) + goto done; + cache->needs_blit = TRUE; + } + + memset(cache->compose, 0, (size_t)cache->width * cache->height * sizeof(*cache->compose)); + for (i = 0; i < count; ++i) + { + if (layers[i].hwnd != cache->hwnd) + continue; + LIST_FOR_EACH_ENTRY(frame, &compositor->layer_frames, struct layer_frame, entry) + { + if (frame->content == layers[i].content && frame->bits) + { + blend_layer(cache, frame, layers[i].x, layers[i].y, &layers[i].clip); + break; + } + } + } + update_window_bitmap(cache, &dirty, &opaque); + + free(cache->keys); + cache->keys = keys; + keys = NULL; + cache->key_count = key_count; + cache->generation = generation; + cache->valid = TRUE; + } + + if (!cache->dc) + goto done; + + if (cache->needs_blit || now - cache->last_blit >= 1000) + { + SetRect(&dirty, 0, 0, cache->width, cache->height); + opaque = bitmap_is_opaque(cache, &dirty); + } + if (IsRectEmpty(&dirty)) + goto done; + + TRACE("Drawing %s into window %p, %u layers, opaque %d.\n", wine_dbgstr_rect(&dirty), cache->hwnd, + cache->key_count, opaque); + + /* Topmost content is drawn over child windows, the rest below them. */ + if ((dc = GetDCEx(cache->hwnd, 0, DCX_CACHE | (topmost ? 0 : DCX_CLIPCHILDREN)))) + { + GdiFlush(); + if (opaque) + BitBlt(dc, dirty.left, dirty.top, dirty.right - dirty.left, dirty.bottom - dirty.top, + cache->dc, dirty.left, dirty.top, SRCCOPY); + else + GdiAlphaBlend(dc, dirty.left, dirty.top, dirty.right - dirty.left, dirty.bottom - dirty.top, + cache->dc, dirty.left, dirty.top, dirty.right - dirty.left, dirty.bottom - dirty.top, blend); + ReleaseDC(cache->hwnd, dc); + cache->last_blit = now; + cache->needs_blit = pending; + } + +done: + free(keys); + SetThreadDpiAwarenessContext(old_context); +} + +/* Returns FALSE when there are no targets left. */ +static BOOL composite(struct compositor *compositor) +{ + struct window_cache *cache, *next_cache; + struct layer_frame *frame, *next_frame; + struct layer *layers; + unsigned int count, i; + LONG generation; + BOOL any; + + any = take_snapshot(&layers, &count, &generation); + + LIST_FOR_EACH_ENTRY(cache, &compositor->window_caches, struct window_cache, entry) + cache->used = FALSE; + LIST_FOR_EACH_ENTRY(frame, &compositor->layer_frames, struct layer_frame, entry) + frame->used = FALSE; + + for (i = 0; i < count; ++i) + { + if (!(cache = get_window_cache(compositor, layers[i].hwnd)) || cache->used) + continue; + cache->used = TRUE; + compose_window(compositor, cache, layers, count, generation); + } + + LIST_FOR_EACH_ENTRY_SAFE(cache, next_cache, &compositor->window_caches, struct window_cache, entry) + { + if (!cache->used) + free_window_cache(cache); + } + LIST_FOR_EACH_ENTRY_SAFE(frame, next_frame, &compositor->layer_frames, struct layer_frame, entry) + { + if (!frame->used) + free_layer_frame(frame); + } + + for (i = 0; i < count; ++i) + IUnknown_Release(layers[i].content); + free(layers); + return any; +} + +static DWORD WINAPI compositor_thread_proc(void *module) +{ + struct compositor compositor; + unsigned int frequency; + HDC hdc; + + SetThreadDescription(GetCurrentThread(), L"wine_dcomp_compositor"); + list_init(&compositor.layer_frames); + list_init(&compositor.window_caches); + + /* TODO: Wait for vertical blanks instead. */ + hdc = GetDC(0); + frequency = GetDeviceCaps(hdc, VREFRESH); + ReleaseDC(0, hdc); + if (frequency < 24 || frequency > 1000) + frequency = 60; + + /* Checking for new frames is cheap; frames are only read back after a + * present, and windows only redrawn when something changed. */ + while (composite(&compositor)) + Sleep(1000 / frequency); + + /* No targets were left, so composite() released every cache entry. */ + TRACE("Compositor thread exiting.\n"); + FreeLibraryAndExitThread(module, 0); +} + +void compositor_commit(void) +{ + HMODULE module; + HANDLE thread; + + InterlockedIncrement(&compositor_generation); + + dcomp_lock(); + if (!compositor_running && !list_empty(&compositor_targets)) + { + /* Keep dcomp.dll loaded until the thread has exited. */ + if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)compositor_commit, &module)) + { + if ((thread = CreateThread(NULL, 0, compositor_thread_proc, module, 0, NULL))) + { + compositor_running = TRUE; + CloseHandle(thread); + } + else + { + ERR("Failed to create the compositor thread, error %lu.\n", GetLastError()); + FreeLibrary(module); + } + } + } + dcomp_unlock(); +} diff --git a/dlls/dcomp/dcomp_private.h b/dlls/dcomp/dcomp_private.h index ad41d9d..f36c3f8 100644 --- a/dlls/dcomp/dcomp_private.h +++ b/dlls/dcomp/dcomp_private.h @@ -30,9 +30,6 @@ struct composition_device IDCompositionDeviceUnknown IDCompositionDeviceUnknown_iface; IDCompositionSurfaceUnknown *drawing_surface; struct list targets; - HANDLE thread; - BOOL exit_thread; - BOOL thread_exited; int version; LONG ref; }; @@ -58,6 +55,7 @@ struct composition_surface UINT height; DXGI_FORMAT pixel_format; DXGI_ALPHA_MODE alpha_mode; + LONG serial; /* incremented when drawing ends */ LONG ref; }; @@ -70,20 +68,22 @@ struct composition_target HWND hwnd; HANDLE shared_visual_handle; struct list entry; + struct list compositor_entry; LONG ref; }; struct composition_visual { IDCompositionVisualUnknown IDCompositionVisualUnknown_iface; - ID2D1GdiInteropRenderTarget *interop; - ID2D1DeviceContext *device_context; IUnknown *content; GUID content_iid; enum DCOMPOSITION_BITMAP_INTERPOLATION_MODE interpolation_mode; enum DCOMPOSITION_BORDER_MODE border_mode; enum DCOMPOSITION_BACKFACE_VISIBILITY visibility; float offset_x; + float offset_y; + D2D_RECT_F clip; + BOOL has_clip; struct list child_visuals; /* visuals closer to head are lower in z-order */ struct list entry; struct composition_visual *parent; @@ -137,6 +137,9 @@ static inline struct composition_visual *impl_from_IDCompositionVisualUnknown(ID } void dcomp_lock(void); +void compositor_add_target(struct composition_target *target); +void compositor_remove_target(struct composition_target *target); +void compositor_commit(void); void dcomp_unlock(void); HRESULT create_surface(struct composition_surface_factory *factory, UINT width, UINT height, DXGI_FORMAT pixel_format, DXGI_ALPHA_MODE alpha_mode, IDCompositionSurface **dcomp_surface); diff --git a/dlls/dcomp/device.c b/dlls/dcomp/device.c index 2c03aad..cbbd3b1 100644 --- a/dlls/dcomp/device.c +++ b/dlls/dcomp/device.c @@ -34,7 +34,6 @@ #include "dxgi.h" #include "dwmapi.h" #include "d3d11_4.h" -#include "d3dcompiler.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(dcomp); @@ -110,664 +109,17 @@ static ULONG STDMETHODCALLTYPE device_Release(IDCompositionDevice *iface) TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) - { - if (device->thread) - { - WaitForSingleObject(device->thread, INFINITE); - CloseHandle(device->thread); - } free(device); - } return ref; } -/* TODO: Optimize this. There is plenty of room */ -static HRESULT create_bgra_surface_from_rgba(IDXGISurface *rgba_surface, IDXGISurface **bgra_surface) -{ - static const D3D11_INPUT_ELEMENT_DESC layout_desc[] = - { - {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0}, - }; - - static const char vs_code[] = - "void main(in float4 in_position : POSITION,\n" - " in float2 in_texcoord : TEXCOORD,\n" - " out float4 position : SV_Position,\n" - " out float2 texcoord : TEXCOORD)\n" - "{\n" - " position = in_position;\n" - " texcoord = in_texcoord;\n" - "}"; - - static const char ps_code[] = - "Texture2D src_texture : register(t0);\n" - "SamplerState sam_linear : register(s0);\n" - "float4 main(float4 pos : SV_POSITION, float2 uv : TEXCOORD) : SV_TARGET" - "{\n" - " return src_texture.Sample(sam_linear, uv);\n" - "}"; - - struct vec2 - { - float x, y; - }; - - static const struct - { - struct vec2 position; - struct vec2 texcoord; - } quad[] = - { - {{-1.0f, 1.0f}, {0.0f, 0.0f}}, /* Top-Left */ - {{1.0f, 1.0f}, {1.0f, 0.0f}}, /* Top-Right */ - {{-1.0f, -1.0f}, {0.0f, 1.0f}}, /* Bottom-Left */ - {{1.0f, -1.0f}, {1.0f, 1.0f}}, /* Bottom-Right */ - }; - - ID3D11Texture2D *src_texture = NULL, *dst_texture = NULL; - ID3DDeviceContextState *state = NULL, *old_state = NULL; - ID3D11DeviceContext1 *d3d11_device_context1 = NULL; - ID3D11DeviceContext *d3d11_device_context = NULL; - static ID3D10Blob *vs_blob = NULL, *ps_blob = NULL; - ID3D11InputLayout *input_layout = NULL; - D3D11_SAMPLER_DESC sampler_desc = {0}; - D3D11_SUBRESOURCE_DATA resource_data; - ID3D11ShaderResourceView *srv = NULL; - ID3D11Device1 *d3d11_device = NULL; - ID3D11RenderTargetView *rtv = NULL; - ID3D11SamplerState *sampler = NULL; - D3D11_TEXTURE2D_DESC texture_desc; - DXGI_SURFACE_DESC surface_desc; - D3D11_BUFFER_DESC buffer_desc; - ID3D11VertexShader *vs = NULL; - ID3D11PixelShader *ps = NULL; - unsigned int stride, offset; - ID3D11Buffer *vb = NULL; - D3D11_VIEWPORT vp; - HRESULT hr; - - static const D3D_FEATURE_LEVEL feature_levels[] = - { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - }; - - hr = IDXGISurface_QueryInterface(rgba_surface, &IID_ID3D11Texture2D, (void **)&src_texture); - if (FAILED(hr)) - { - ERR("Failed to get a ID3D11Texture2D, hr %#lx.\n", hr); - goto done; - } - - hr = IDXGISurface_GetDevice(rgba_surface, &IID_ID3D11Device1, (void **)&d3d11_device); - if (FAILED(hr)) - { - ERR("Failed to get device, hr %#lx.\n", hr); - goto done; - } - - hr = IDXGISurface_GetDesc(rgba_surface, &surface_desc); - if (FAILED(hr)) - { - ERR("Failed to get the dxgi surface description, hr %#lx.\n", hr); - goto done; - } - - texture_desc.Width = surface_desc.Width; - texture_desc.Height = surface_desc.Height; - texture_desc.MipLevels = 1; - texture_desc.ArraySize = 1; - texture_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - texture_desc.SampleDesc.Count = 1; - texture_desc.SampleDesc.Quality = 0; - texture_desc.Usage = D3D11_USAGE_DEFAULT; - texture_desc.BindFlags = D3D11_BIND_RENDER_TARGET; - texture_desc.CPUAccessFlags = 0; - texture_desc.MiscFlags = D3D11_RESOURCE_MISC_GUARDED | D3D11_RESOURCE_MISC_GDI_COMPATIBLE; - hr = ID3D11Device1_CreateTexture2D(d3d11_device, &texture_desc, NULL, &dst_texture); - if (FAILED(hr)) - { - ERR("Failed to create a ID3D11Texture2D, hr %#lx.\n", hr); - goto done; - } - - hr = ID3D11Device1_CreateShaderResourceView(d3d11_device, (ID3D11Resource *)src_texture, NULL, &srv); - if (FAILED(hr)) - { - ERR("Failed to create a SRV, hr %#lx.\n", hr); - goto done; - } - - hr = ID3D11Device1_CreateRenderTargetView(d3d11_device, (ID3D11Resource *)dst_texture, NULL, &rtv); - if (FAILED(hr)) - { - ERR("Failed to create a RTV, hr %#lx.\n", hr); - goto done; - } - - ID3D11Device1_GetImmediateContext(d3d11_device, &d3d11_device_context); - if (FAILED(hr = ID3D11DeviceContext_QueryInterface(d3d11_device_context, &IID_ID3D11DeviceContext1, - (void **)&d3d11_device_context1))) - { - ERR("Failed to query ID3D11DeviceContext1, hr %#lx.\n", hr); - goto done; - } - - if (FAILED(hr = ID3D11Device1_CreateDeviceContextState(d3d11_device, 0, feature_levels, - ARRAY_SIZE(feature_levels), D3D11_SDK_VERSION, - &IID_ID3D11Device1, NULL, &state))) - { - ERR("Failed to create device context state, hr %#lx.\n", hr); - goto done; - } - ID3D11DeviceContext1_SwapDeviceContextState(d3d11_device_context1, state, &old_state); - - ID3D11DeviceContext1_OMSetRenderTargets(d3d11_device_context1, 1, &rtv, NULL); - ID3D11DeviceContext1_PSSetShaderResources(d3d11_device_context1, 0, 1, &srv); - - /* Move this to dcomp device instance ? */ - if (vs_blob == NULL) - { - hr = D3DCompile(vs_code, sizeof(vs_code) - 1, "vs", NULL, NULL, "main", "vs_4_0", 0, 0, &vs_blob, NULL); - if (FAILED(hr)) - { - ERR("Failed to compile vertex shader, hr %#lx.\n", hr); - goto done; - } - - hr = D3DCompile(ps_code, sizeof(ps_code) - 1, "ps", NULL, NULL, "main", "ps_4_0", 0, 0, &ps_blob, NULL); - if (FAILED(hr)) - { - ERR("Failed to compile pixel shader, hr %#lx.\n", hr); - goto done; - } - } - - hr = ID3D11Device1_CreateVertexShader(d3d11_device, ID3D10Blob_GetBufferPointer(vs_blob), - ID3D10Blob_GetBufferSize(vs_blob), NULL, &vs); - if (FAILED(hr)) - { - ERR("Failed to create vertex shader, hr %#lx.\n", hr); - goto done; - } - - hr = ID3D11Device1_CreatePixelShader(d3d11_device, ID3D10Blob_GetBufferPointer(ps_blob), - ID3D10Blob_GetBufferSize(ps_blob), NULL, &ps); - if (FAILED(hr)) - { - ERR("Failed to create pixel shader, hr %#lx.\n", hr); - goto done; - } - - hr = ID3D11Device1_CreateInputLayout(d3d11_device, layout_desc, ARRAY_SIZE(layout_desc), - ID3D10Blob_GetBufferPointer(vs_blob), - ID3D10Blob_GetBufferSize(vs_blob), &input_layout); - if (FAILED(hr)) - { - ERR("Failed to create input layout, hr %#lx.\n", hr); - goto done; - } - - buffer_desc.ByteWidth = sizeof(quad); - buffer_desc.Usage = D3D11_USAGE_DEFAULT; - buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; - buffer_desc.CPUAccessFlags = 0; - buffer_desc.MiscFlags = 0; - buffer_desc.StructureByteStride = 0; - resource_data.pSysMem = quad; - resource_data.SysMemPitch = 0; - resource_data.SysMemSlicePitch = 0; - hr = ID3D11Device1_CreateBuffer(d3d11_device, &buffer_desc, &resource_data, &vb); - if (FAILED(hr)) - { - ERR("Failed to create vertex buffer, hr %#lx.\n", hr); - goto done; - } - - sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; - sampler_desc.MinLOD = 0; - sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; - - hr = ID3D11Device1_CreateSamplerState(d3d11_device, &sampler_desc, &sampler); - if (FAILED(hr)) - { - ERR("Failed to create sampler state, hr %#lx.\n", hr); - goto done; - } - ID3D11DeviceContext1_PSSetSamplers(d3d11_device_context1, 0, 1, &sampler); - - ID3D11DeviceContext1_IASetInputLayout(d3d11_device_context1, input_layout); - ID3D11DeviceContext1_IASetPrimitiveTopology(d3d11_device_context1, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - - stride = sizeof(quad[0]); - offset = 0; - ID3D11DeviceContext1_IASetVertexBuffers(d3d11_device_context1, 0, 1, &vb, &stride, &offset); - ID3D11DeviceContext1_VSSetShader(d3d11_device_context1, vs, NULL, 0); - ID3D11DeviceContext1_PSSetShader(d3d11_device_context1, ps, NULL, 0); - - vp.TopLeftX = 0; - vp.TopLeftY = 0; - vp.Width = surface_desc.Width; - vp.Height = surface_desc.Height; - vp.MinDepth = 0; - vp.MaxDepth = 1; - ID3D11DeviceContext1_RSSetViewports(d3d11_device_context1, 1, &vp); - ID3D11DeviceContext1_Draw(d3d11_device_context1, 4, 0); - - hr = ID3D11Texture2D_QueryInterface(dst_texture, &IID_IDXGISurface, (void **)bgra_surface); - if (FAILED(hr)) - { - ERR("Failed to get bgra surface, hr %#lx.\n", hr); - goto done; - } - -done: - if (old_state) - { - ID3D11DeviceContext1_SwapDeviceContextState(d3d11_device_context1, old_state, NULL); - ID3DDeviceContextState_Release(old_state); - } - if (state) - ID3DDeviceContextState_Release(state); - if (vb) - ID3D11Buffer_Release(vb); - if (sampler) - ID3D11SamplerState_Release(sampler); - if (input_layout) - ID3D11InputLayout_Release(input_layout); - if (ps) - ID3D11PixelShader_Release(ps); - if (vs) - ID3D11VertexShader_Release(vs); - if (d3d11_device_context1) - ID3D11DeviceContext1_Release(d3d11_device_context1); - if (d3d11_device_context) - ID3D11DeviceContext_Release(d3d11_device_context); - if (rtv) - ID3D11RenderTargetView_Release(rtv); - if (srv) - ID3D11ShaderResourceView_Release(srv); - if (dst_texture) - ID3D11Texture2D_Release(dst_texture); - if (d3d11_device) - ID3D11Device1_Release(d3d11_device); - if (src_texture) - ID3D11Texture2D_Release(src_texture); - return hr; -} - -static void do_composite_dxgi_surface(const struct composition_target *target, - const struct composition_visual *visual, - IDXGISurface *dxgi_surface) -{ - ID3DDeviceContextState *state = NULL, *old_state = NULL; - ID3D11DeviceContext1 *d3d11_device_context1 = NULL; - ID3D11DeviceContext *d3d11_device_context = NULL; - ID3D11Multithread *multithread = NULL; - D2D1_BITMAP_PROPERTIES1 bitmap_desc; - ID2D1Bitmap1 *target_bitmap = NULL; - ID3D11Device1 *d3d11_device = NULL; - IDXGISurface *bgra_surface = NULL; - DXGI_SURFACE_DESC surface_desc; - ID2D1Bitmap *src_bitmap = NULL; - ID2D1Image *old_target = NULL; - HDC dst_dc, src_dc; - D2D1_SIZE_U size; - DWORD style; - HRESULT hr; - - static const BLENDFUNCTION blend_func = - { - AC_SRC_OVER, /* BlendOp */ - 0, /* BlendFlag */ - 255, /* SourceConstantAlpha */ - AC_SRC_ALPHA /* AlphaFormat */ - }; - - static const D3D_FEATURE_LEVEL feature_levels[] = - { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - }; - - if (!target->root) - { - ERR("Target has no root.\n"); - goto done; - } - - hr = IDXGISurface_GetDevice(dxgi_surface, &IID_ID3D11Device1, (void **)&d3d11_device); - if (FAILED(hr)) - { - ERR("Failed to get device, hr %#lx.\n", hr); - goto done; - } - - ID3D11Device1_GetImmediateContext(d3d11_device, &d3d11_device_context); - if (FAILED(hr = ID3D11DeviceContext_QueryInterface(d3d11_device_context, &IID_ID3D11DeviceContext1, - (void **)&d3d11_device_context1))) - { - ERR("Failed to query ID3D11DeviceContext1, hr %#lx.\n", hr); - goto done; - } - - /* Use ID3D11Multithread to avoid conflicts with rendering in an another thread with the same - * immediate ID3D11DeviceContext */ - hr = ID3D11DeviceContext_QueryInterface(d3d11_device_context, &IID_ID3D11Multithread, (void **)&multithread); - if (FAILED(hr)) - { - ERR("Failed to query ID3D11Multithread, hr %#lx.\n", hr); - goto done; - } - - ID3D11Multithread_SetMultithreadProtected(multithread, TRUE); - ID3D11Multithread_Enter(multithread); - - /* Is this really necessary? anyway, just to be safe */ - if (FAILED(hr = ID3D11Device1_CreateDeviceContextState(d3d11_device, 0, feature_levels, - ARRAY_SIZE(feature_levels), D3D11_SDK_VERSION, - &IID_ID3D11Device1, NULL, &state))) - { - ERR("Failed to create device context state, hr %#lx.\n", hr); - goto done; - } - ID3D11DeviceContext1_SwapDeviceContextState(d3d11_device_context1, state, &old_state); - - if (FAILED(hr = IDXGISurface_GetDesc(dxgi_surface, &surface_desc))) - { - ERR("Failed to get the dxgi surface description, hr %#lx.\n", hr); - goto done; - } - - /* Convert DXGI_FORMAT_R8G8B8A8_UNORM to DXGI_FORMAT_B8G8R8A8_UNORM */ - if (surface_desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM) - { - hr = create_bgra_surface_from_rgba(dxgi_surface, &bgra_surface); - if (FAILED(hr)) - { - ERR("Failed to convert DXGI_FORMAT_R8G8B8A8_UNORM surface to DXGI_FORMAT_B8G8R8A8_UNORM, hr %#lx.\n", hr); - goto done; - } - dxgi_surface = bgra_surface; - } - else if (surface_desc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT) - { - ERR("DXGI_FORMAT_R16G16B16A16_FLOAT surface is currently unsupported.\n"); - goto done; - } - - size.width = surface_desc.Width; - size.height = surface_desc.Height; - bitmap_desc.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; - bitmap_desc.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; - bitmap_desc.dpiX = 0; - bitmap_desc.dpiY = 0; - bitmap_desc.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE; - bitmap_desc.colorContext = NULL; - - assert(visual->device_context); - if (FAILED(hr = ID2D1DeviceContext_CreateBitmap(visual->device_context, size, NULL, 0, - &bitmap_desc, &target_bitmap))) - { - ERR("Failed to create a bitmap, hr %#lx.\n", hr); - goto done; - } - - if (FAILED(hr = ID2D1DeviceContext_CreateSharedBitmap(visual->device_context, &IID_IDXGISurface, - dxgi_surface, NULL, &src_bitmap))) - { - ERR("Failed to create a bitmap from dxgi surface, hr %#lx.\n", hr); - goto done; - } - - if (FAILED(hr = ID2D1Bitmap1_CopyFromBitmap(target_bitmap, NULL, src_bitmap, NULL))) - { - ERR("Failed to copy bitmap, hr %#lx.\n", hr); - goto done; - } - - ID2D1DeviceContext_GetTarget(visual->device_context, &old_target); - ID2D1DeviceContext_SetTarget(visual->device_context, (ID2D1Image *)target_bitmap); - if (FAILED(hr = ID2D1GdiInteropRenderTarget_GetDC(visual->interop, D2D1_DC_INITIALIZE_MODE_COPY, - &src_dc))) - { - ERR("GetDC failed, hr %#lx.\n", hr); - ID2D1DeviceContext_SetTarget(visual->device_context, old_target); - if (old_target) - ID2D1Image_Release(old_target); - goto done; - } - - style = DCX_USESTYLE | DCX_CACHE; - if (!target->topmost) - style |= DCX_CLIPCHILDREN; - dst_dc = GetDCEx(target->hwnd, 0, style); - GdiAlphaBlend(dst_dc, 0, 0, size.width, size.height, src_dc, 0, 0, size.width, size.height, blend_func); - ReleaseDC(target->hwnd, dst_dc); - - ID2D1GdiInteropRenderTarget_ReleaseDC(visual->interop, NULL); - ID2D1DeviceContext_SetTarget(visual->device_context, old_target); - if (old_target) - ID2D1Image_Release(old_target); - -done: - if (bgra_surface) - IDXGISurface_Release(bgra_surface); - if (src_bitmap) - ID2D1Bitmap_Release(src_bitmap); - if (target_bitmap) - ID2D1Bitmap1_Release(target_bitmap); - if (d3d11_device_context1 && old_state) - { - ID3D11DeviceContext1_SwapDeviceContextState(d3d11_device_context1, old_state, NULL); - ID3DDeviceContextState_Release(old_state); - } - if (state) - ID3DDeviceContextState_Release(state); - if (multithread) - { - ID3D11Multithread_Leave(multithread); - ID3D11Multithread_Release(multithread); - } - if (d3d11_device_context1) - ID3D11DeviceContext1_Release(d3d11_device_context1); - if (d3d11_device_context) - ID3D11DeviceContext_Release(d3d11_device_context); - if (d3d11_device) - ID3D11Device1_Release(d3d11_device); -} - -static struct composition_visual * shared_visual_target_get_root(HANDLE shared_visual_handle) -{ - /* Shared visual handles need wineserver support, which this build does not carry. */ - FIXME("shared visual handle %p is unsupported.\n", shared_visual_handle); - return NULL; -} - -static HRESULT do_composite(const struct composition_target *target, struct composition_visual *visual) -{ - struct composition_visual *child_visual; - IDXGISurface *dxgi_surface; - HRESULT hr; - - if (visual->shared_visual_handle) - { - struct composition_visual *root = shared_visual_target_get_root(visual->shared_visual_handle); - if (root) - do_composite(target, root); - } - - /* Render content */ - if (visual->content) - { - if (IsEqualGUID(&visual->content_iid, &IID_IDXGISwapChain1)) - { - DXGI_SWAP_CHAIN_DESC swapchain_desc; - IDXGISwapChain1 *swapchain; - - hr = IUnknown_QueryInterface(visual->content, &IID_IDXGISwapChain1, (void **)&swapchain); - if (FAILED(hr)) - { - FIXME("Failed to query IDXGISwapChain1.\n"); - return hr; - } - - hr = IDXGISwapChain1_GetDesc(swapchain, &swapchain_desc); - if (FAILED(hr)) - { - ERR("Failed to get the swapchain description, hr %#lx.\n", hr); - IDXGISwapChain1_Release(swapchain); - return hr; - } - - /* The front buffer is the last back buffer because the composition swapchain must - * be created with DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL */ - hr = IDXGISwapChain1_GetBuffer(swapchain, 0, &IID_IDXGISurface, - (void **)&dxgi_surface); - IDXGISwapChain1_Release(swapchain); - if (FAILED(hr)) - { - ERR("Failed to get the swapchain front buffer, hr %#lx.\n", hr); - return hr; - } - } - else if (IsEqualGUID(&visual->content_iid, &IID_IDCompositionSurface)) - { - struct composition_surface *surface_impl; - IDCompositionSurface *surface; - - hr = IUnknown_QueryInterface(visual->content, &IID_IDCompositionSurface, (void **)&surface); - if (FAILED(hr)) - { - FIXME("Failed to query IDCompositionSurface.\n"); - return hr; - } - - surface_impl = impl_from_IDCompositionSurface(surface); - if (IsEqualGUID(&surface_impl->physical_surface_iid, &IID_IDXGISurface)) - { - dxgi_surface = (IDXGISurface *)surface_impl->physical_surface; - IDXGISurface_AddRef(dxgi_surface); - IDCompositionSurface_Release(surface); - } - else - { - FIXME("IDCompositionSurface physical_surface_iid %s is unsupported.\n", - wine_dbgstr_guid(&surface_impl->physical_surface_iid)); - IDCompositionSurface_Release(surface); - return E_FAIL; - } - } - else - { - FIXME("content_iid %s is unsupported.\n", wine_dbgstr_guid(&visual->content_iid)); - return E_FAIL; - } - - do_composite_dxgi_surface(target, visual, dxgi_surface); - IDXGISurface_Release(dxgi_surface); - } - - LIST_FOR_EACH_ENTRY(child_visual, &visual->child_visuals, struct composition_visual, entry) - { - do_composite(target, child_visual); - } - - return S_OK; -} - - -static DWORD WINAPI composite_thread_proc(void *iface) -{ - struct composition_device *device = impl_from_IDCompositionDevice(iface); - unsigned int count, frequency, refresh_period; - struct composition_target *target; - struct composition_visual *visual; - HDC hdc; - - /* TODO: Implement and use D3DKMTWaitForVerticalBlankEvent() */ - hdc = GetDC(0); - frequency = GetDeviceCaps(hdc, VREFRESH); - refresh_period = 1000 / frequency; - ReleaseDC(0, hdc); - - while (TRUE) - { - dcomp_lock(); - - count = 0; - LIST_FOR_EACH_ENTRY(target, &device->targets, struct composition_target, entry) - { - if (!target->root) - { - FIXME("Target %p has no root.\n", &target->IDCompositionTarget_iface); - continue; - } - - /* Skip targets created from a shared visual handle. Composition for such targets will - * be done for the visual tree where the visual created from the shared visual handle is at */ - if (target->shared_visual_handle) - continue; - - visual = impl_from_IDCompositionVisual(target->root); - if (SUCCEEDED(do_composite(target, visual))) - count++; - } - - if (!count) - { - TRACE("Composition thread exited.\n"); - device->thread_exited = TRUE; - dcomp_unlock(); - break; - } - - dcomp_unlock(); - - if (device->exit_thread) - break; - - Sleep(refresh_period); - } - - return 0; -} - static HRESULT STDMETHODCALLTYPE device_Commit(IDCompositionDevice *iface) { - struct composition_device *device = impl_from_IDCompositionDevice(iface); - HRESULT hr = S_OK; - - FIXME("iface %p semi-stub!\n", iface); - - dcomp_lock(); - - if (!device->thread || device->thread_exited) - { - if (device->thread) - { - device->exit_thread = TRUE; - WaitForSingleObject(device->thread, INFINITE); - CloseHandle(device->thread); - } - device->thread_exited = FALSE; - device->exit_thread = FALSE; - device->thread = CreateThread(NULL, 0, composite_thread_proc, iface, 0, NULL); - } - - dcomp_unlock(); + TRACE("iface %p.\n", iface); - return hr; + compositor_commit(); + return S_OK; } static HRESULT STDMETHODCALLTYPE device_WaitForCommitCompletion(IDCompositionDevice *iface) diff --git a/dlls/dcomp/surface.c b/dlls/dcomp/surface.c index 74f5131..93a85bb 100644 --- a/dlls/dcomp/surface.c +++ b/dlls/dcomp/surface.c @@ -245,6 +245,7 @@ static HRESULT STDMETHODCALLTYPE surface_EndDraw(IDCompositionSurfaceUnknown *if ID3D11Resource_Release(src_resource); ID3D11Device_Release(d3d11_device); + InterlockedIncrement(&surface->serial); SetRectEmpty(&surface->draw_rect); return S_OK; } diff --git a/dlls/dcomp/target.c b/dlls/dcomp/target.c index 6a2deaa..4d365ef 100644 --- a/dlls/dcomp/target.c +++ b/dlls/dcomp/target.c @@ -71,6 +71,7 @@ static ULONG STDMETHODCALLTYPE target_Release(IDCompositionTarget *iface) dcomp_lock(); list_remove(&target->entry); + compositor_remove_target(target); dcomp_unlock(); IDCompositionDevice_Release(target->device); if (target->root) @@ -172,6 +173,7 @@ HRESULT create_target(struct composition_device *device, HWND hwnd, BOOL topmost target->topmost = topmost; target->device = &device->IDCompositionDevice_iface; *new_target = &target->IDCompositionTarget_iface; + compositor_add_target(target); prop = target->topmost ? wine_window_topmost_composed : wine_window_non_topmost_composed; SetPropW(target->hwnd, prop, (HANDLE)1); @@ -207,5 +209,6 @@ HRESULT create_target_from_shared_visual_handle(struct composition_device *devic target->device = &device->IDCompositionDevice_iface; target->shared_visual_handle = handle; *new_target = &target->IDCompositionTarget_iface; + compositor_add_target(target); return S_OK; } diff --git a/dlls/dcomp/visual.c b/dlls/dcomp/visual.c index c4ff363..6048d94 100644 --- a/dlls/dcomp/visual.c +++ b/dlls/dcomp/visual.c @@ -62,30 +62,32 @@ static ULONG STDMETHODCALLTYPE visual_Release(IDCompositionVisualUnknown *iface) { struct composition_visual *visual = impl_from_IDCompositionVisualUnknown(iface); ULONG ref = InterlockedDecrement(&visual->ref); - struct composition_visual *child; + struct composition_visual *child, *next; TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) { - if (visual->interop) - ID2D1GdiInteropRenderTarget_Release(visual->interop); - if (visual->device_context) - ID2D1DeviceContext_Release(visual->device_context); - if (visual->content) - IUnknown_Release(visual->content); - if (visual->shared_visual_handle) - CloseHandle(visual->shared_visual_handle); + IUnknown *content; + + /* Unlink under the lock: the compositor walks visual trees with it held. */ dcomp_lock(); if (visual->is_child) list_remove(&visual->entry); - LIST_FOR_EACH_ENTRY(child, &visual->child_visuals, struct composition_visual, entry) + LIST_FOR_EACH_ENTRY_SAFE(child, next, &visual->child_visuals, struct composition_visual, entry) { list_remove(&child->entry); child->is_child = FALSE; child->parent = NULL; } + content = visual->content; + visual->content = NULL; dcomp_unlock(); + + if (content) + IUnknown_Release(content); + if (visual->shared_visual_handle) + CloseHandle(visual->shared_visual_handle); free(visual); } @@ -118,8 +120,12 @@ static HRESULT STDMETHODCALLTYPE visual_SetOffsetYAnimation(IDCompositionVisualU static HRESULT STDMETHODCALLTYPE visual_SetOffsetY(IDCompositionVisualUnknown *iface, float offset_y) { - FIXME("iface %p, offset_y %f stub!\n", iface, offset_y); - return E_NOTIMPL; + struct composition_visual *visual = impl_from_IDCompositionVisualUnknown(iface); + + TRACE("iface %p, offset_y %f.\n", iface, offset_y); + + visual->offset_y = offset_y; + return S_OK; } static HRESULT STDMETHODCALLTYPE visual_SetTransformObject(IDCompositionVisualUnknown *iface, @@ -192,130 +198,64 @@ static HRESULT STDMETHODCALLTYPE visual_SetClipObject(IDCompositionVisualUnknown /* C++ method: SetClip(THIS_ const D2D_RECT_F &rect). Use pointer instead of reference */ static HRESULT STDMETHODCALLTYPE visual_SetClip(IDCompositionVisualUnknown *iface, const D2D_RECT_F *rect) { - FIXME("iface %p, rect %p stub!\n", iface, rect); - return E_NOTIMPL; + struct composition_visual *visual = impl_from_IDCompositionVisualUnknown(iface); + + TRACE("iface %p, rect {%.8e, %.8e, %.8e, %.8e}.\n", iface, rect->left, rect->top, rect->right, rect->bottom); + + dcomp_lock(); + visual->clip = *rect; + visual->has_clip = TRUE; + dcomp_unlock(); + return S_OK; } static HRESULT STDMETHODCALLTYPE visual_SetContent(IDCompositionVisualUnknown *iface, IUnknown *content) { struct composition_visual *visual = impl_from_IDCompositionVisualUnknown(iface); - ID2D1GdiInteropRenderTarget *interop; - ID2D1DeviceContext *device_context; IDXGISwapChain1 *dxgi_swapchain; - IDCompositionSurface *suface; - IDXGIDevice *dxgi_device; - ID2D1Device *d2d_device; + IDCompositionSurface *surface; const GUID *iid = NULL; - HRESULT hr; - - FIXME("iface %p, content %p semi-stub!\n", iface, content); + IUnknown *old_content; - dcomp_lock(); + TRACE("iface %p, content %p.\n", iface, content); if (content) { - if (SUCCEEDED(IUnknown_QueryInterface(content, &IID_IDXGISwapChain1, - (void **)&dxgi_swapchain))) + if (SUCCEEDED(IUnknown_QueryInterface(content, &IID_IDXGISwapChain1, (void **)&dxgi_swapchain))) { iid = &IID_IDXGISwapChain1; - - hr = IDXGISwapChain1_GetDevice(dxgi_swapchain, &IID_IDXGIDevice, (void **)&dxgi_device); IDXGISwapChain1_Release(dxgi_swapchain); - if (FAILED(hr)) - { - ERR("Failed to get the swapchain device, hr %#lx.\n", hr); - dcomp_unlock(); - return hr; - } } - else if (SUCCEEDED(IUnknown_QueryInterface(content, &IID_IDCompositionSurface, (void **)&suface))) + else if (SUCCEEDED(IUnknown_QueryInterface(content, &IID_IDCompositionSurface, (void **)&surface))) { - struct composition_surface *dcomp_surface = unsafe_impl_from_IDCompositionSurface(suface); + struct composition_surface *dcomp_surface = unsafe_impl_from_IDCompositionSurface(surface); + BOOL supported = IsEqualGUID(&dcomp_surface->physical_surface_iid, &IID_IDXGISurface); - if (IsEqualGUID(&dcomp_surface->physical_surface_iid, &IID_IDXGISurface)) - { - iid = &IID_IDCompositionSurface; - - hr = IDXGISurface_GetDevice((IDXGISurface *)dcomp_surface->physical_surface, - &IID_IDXGIDevice, (void **)&dxgi_device); - IDCompositionSurface_Release(suface); - if (FAILED(hr)) - { - ERR("Failed to get the dxgi surface device, hr %#lx.\n", hr); - dcomp_unlock(); - return hr; - } - } - else + IDCompositionSurface_Release(surface); + if (!supported) { FIXME("Only IDCompositionSurface from IDXGISurface is currently supported.\n"); - IDCompositionSurface_Release(suface); - dcomp_unlock(); return E_INVALIDARG; } + iid = &IID_IDCompositionSurface; } else { FIXME("Only IDXGISwapChain1 or IDCompositionSurface are currently supported.\n"); - dcomp_unlock(); return E_INVALIDARG; } - - hr = D2D1CreateDevice(dxgi_device, NULL, &d2d_device); - IDXGIDevice_Release(dxgi_device); - if (FAILED(hr)) - { - ERR("Failed to create a D2D device, hr %#lx.\n", hr); - dcomp_unlock(); - return hr; - } - - hr = ID2D1Device_CreateDeviceContext(d2d_device, D2D1_DEVICE_CONTEXT_OPTIONS_NONE, - &device_context); - ID2D1Device_Release(d2d_device); - if (FAILED(hr)) - { - ERR("Failed to create a D2D device context, hr %#lx.\n", hr); - dcomp_unlock(); - return hr; - } - - if (FAILED(hr = ID2D1DeviceContext_QueryInterface(device_context, - &IID_ID2D1GdiInteropRenderTarget, (void **)&interop))) - { - ERR("Failed to get a ID2D1GdiInteropRenderTarget, hr %#lx.\n", hr); - ID2D1DeviceContext_Release(device_context); - dcomp_unlock(); - return hr; - } - } - - if (visual->interop) - { - ID2D1GdiInteropRenderTarget_Release(visual->interop); - visual->interop = NULL; - } - if (visual->device_context) - { - ID2D1DeviceContext_Release(visual->device_context); - visual->device_context = NULL; - } - if (visual->content) - { - IUnknown_Release(visual->content); - visual->content = NULL; + IUnknown_AddRef(content); } + dcomp_lock(); + old_content = visual->content; + visual->content = content; if (content) - { - IUnknown_AddRef(content); - visual->content = content; visual->content_iid = *iid; - visual->device_context = device_context; - visual->interop = interop; - } - dcomp_unlock(); + + if (old_content) + IUnknown_Release(old_content); return S_OK; }