From 1d5620a65875e92151b2f032670838cb1ce0fe58 Mon Sep 17 00:00:00 2001 From: James Ross-Gowan Date: Fri, 22 Sep 2017 21:38:05 +1000 Subject: vo_gpu: override ra_swapchain_fns for the d3d11 surface ANGLE can take advantage of some of these when using the external swapchain-backed surface. --- video/out/opengl/context_angle.c | 201 ++++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 89 deletions(-) (limited to 'video') diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index a8051155e1..c96ae5cc27 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -518,6 +518,112 @@ static void angle_swap_buffers(struct ra_ctx *ctx) egl_swap_buffers(ctx); } +static struct mp_image *d3d11_screenshot(struct ra_ctx *ctx) +{ + struct priv *p = ctx->priv; + ID3D11Texture2D *frontbuffer = NULL; + ID3D11Texture2D *staging = NULL; + struct mp_image *img = NULL; + HRESULT hr; + + if (!p->dxgi_swapchain) + goto done; + + // Validate the swap chain. This screenshot method will only work on DXGI + // 1.2+ flip/sequential swap chains. It's probably not possible at all with + // discard swap chains, since by definition, the backbuffer contents is + // discarded on Present(). + DXGI_SWAP_CHAIN_DESC scd; + hr = IDXGISwapChain_GetDesc(p->dxgi_swapchain, &scd); + if (FAILED(hr)) + goto done; + if (scd.SwapEffect != DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) + goto done; + + // Get the last buffer that was presented with Present(). This should be + // the n-1th buffer for a swap chain of length n. + hr = IDXGISwapChain_GetBuffer(p->dxgi_swapchain, scd.BufferCount - 1, + &IID_ID3D11Texture2D, (void**)&frontbuffer); + if (FAILED(hr)) + goto done; + + D3D11_TEXTURE2D_DESC td; + ID3D11Texture2D_GetDesc(frontbuffer, &td); + if (td.SampleDesc.Count > 1) + goto done; + + // Validate the backbuffer format and convert to an mpv IMGFMT + enum mp_imgfmt fmt; + switch (td.Format) { + case DXGI_FORMAT_B8G8R8A8_UNORM: fmt = IMGFMT_BGR0; break; + case DXGI_FORMAT_R8G8B8A8_UNORM: fmt = IMGFMT_RGB0; break; + default: + goto done; + } + + // Create a staging texture based on the frontbuffer with CPU access + td.BindFlags = 0; + td.MiscFlags = 0; + td.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + td.Usage = D3D11_USAGE_STAGING; + hr = ID3D11Device_CreateTexture2D(p->d3d11_device, &td, 0, &staging); + if (FAILED(hr)) + goto done; + + ID3D11DeviceContext_CopyResource(p->d3d11_context, + (ID3D11Resource*)staging, (ID3D11Resource*)frontbuffer); + + // Attempt to map the staging texture to CPU-accessible memory + D3D11_MAPPED_SUBRESOURCE lock; + hr = ID3D11DeviceContext_Map(p->d3d11_context, (ID3D11Resource*)staging, + 0, D3D11_MAP_READ, 0, &lock); + if (FAILED(hr)) + goto done; + + img = mp_image_alloc(fmt, td.Width, td.Height); + if (!img) + return NULL; + for (int i = 0; i < td.Height; i++) { + memcpy(img->planes[0] + img->stride[0] * i, + (char*)lock.pData + lock.RowPitch * i, td.Width * 4); + } + + ID3D11DeviceContext_Unmap(p->d3d11_context, (ID3D11Resource*)staging, 0); + +done: + SAFE_RELEASE(frontbuffer); + SAFE_RELEASE(staging); + return img; +} + +static int angle_color_depth(struct ra_swapchain *sw) +{ + // Only 8-bit output is supported at the moment + return 8; +} + +static struct mp_image *angle_screenshot(struct ra_swapchain *sw) +{ + struct mp_image *img = d3d11_screenshot(sw->ctx); + if (img) + return img; + return ra_gl_ctx_screenshot(sw); +} + +static bool angle_submit_frame(struct ra_swapchain *sw, + const struct vo_frame *frame) +{ + struct priv *p = sw->ctx->priv; + bool ret = ra_gl_ctx_submit_frame(sw, frame); + if (p->d3d11_context) { + // DXGI Present doesn't flush the immediate context, which can make + // timers inaccurate, since the end queries might not be sent until the + // next frame. Fix this by flushing the context now. + ID3D11DeviceContext_Flush(p->d3d11_context); + } + return ret; +} + static bool angle_init(struct ra_ctx *ctx) { struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); @@ -576,8 +682,12 @@ static bool angle_init(struct ra_ctx *ctx) current_ctx = ctx; gl->SwapInterval = angle_swap_interval; - // ANGLE doesn't actually need to override any of the functions (yet) - static const struct ra_swapchain_fns empty_swapchain_fns = {0}; + // Custom swapchain impl for the D3D11 swapchain-based surface + static const struct ra_swapchain_fns empty_swapchain_fns = { + .color_depth = angle_color_depth, + .screenshot = angle_screenshot, + .submit_frame = angle_submit_frame, + }; struct ra_gl_ctx_params params = { .swap_buffers = angle_swap_buffers, .flipped = p->flipped, @@ -612,95 +722,8 @@ static bool angle_reconfig(struct ra_ctx *ctx) return true; } -static struct mp_image *d3d11_screenshot(struct ra_ctx *ctx) -{ - struct priv *p = ctx->priv; - ID3D11Texture2D *frontbuffer = NULL; - ID3D11Texture2D *staging = NULL; - struct mp_image *img = NULL; - HRESULT hr; - - if (!p->dxgi_swapchain) - goto done; - - // Validate the swap chain. This screenshot method will only work on DXGI - // 1.2+ flip/sequential swap chains. It's probably not possible at all with - // discard swap chains, since by definition, the backbuffer contents is - // discarded on Present(). - DXGI_SWAP_CHAIN_DESC scd; - hr = IDXGISwapChain_GetDesc(p->dxgi_swapchain, &scd); - if (FAILED(hr)) - goto done; - if (scd.SwapEffect != DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) - goto done; - - // Get the last buffer that was presented with Present(). This should be - // the n-1th buffer for a swap chain of length n. - hr = IDXGISwapChain_GetBuffer(p->dxgi_swapchain, scd.BufferCount - 1, - &IID_ID3D11Texture2D, (void**)&frontbuffer); - if (FAILED(hr)) - goto done; - - D3D11_TEXTURE2D_DESC td; - ID3D11Texture2D_GetDesc(frontbuffer, &td); - if (td.SampleDesc.Count > 1) - goto done; - - // Validate the backbuffer format and convert to an mpv IMGFMT - enum mp_imgfmt fmt; - switch (td.Format) { - case DXGI_FORMAT_B8G8R8A8_UNORM: fmt = IMGFMT_BGR0; break; - case DXGI_FORMAT_R8G8B8A8_UNORM: fmt = IMGFMT_RGB0; break; - default: - goto done; - } - - // Create a staging texture based on the frontbuffer with CPU access - td.BindFlags = 0; - td.MiscFlags = 0; - td.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - td.Usage = D3D11_USAGE_STAGING; - hr = ID3D11Device_CreateTexture2D(p->d3d11_device, &td, 0, &staging); - if (FAILED(hr)) - goto done; - - ID3D11DeviceContext_CopyResource(p->d3d11_context, - (ID3D11Resource*)staging, (ID3D11Resource*)frontbuffer); - - // Attempt to map the staging texture to CPU-accessible memory - D3D11_MAPPED_SUBRESOURCE lock; - hr = ID3D11DeviceContext_Map(p->d3d11_context, (ID3D11Resource*)staging, - 0, D3D11_MAP_READ, 0, &lock); - if (FAILED(hr)) - goto done; - - img = mp_image_alloc(fmt, td.Width, td.Height); - if (!img) - return NULL; - for (int i = 0; i < td.Height; i++) { - memcpy(img->planes[0] + img->stride[0] * i, - (char*)lock.pData + lock.RowPitch * i, td.Width * 4); - } - - ID3D11DeviceContext_Unmap(p->d3d11_context, (ID3D11Resource*)staging, 0); - -done: - SAFE_RELEASE(frontbuffer); - SAFE_RELEASE(staging); - return img; -} - static int angle_control(struct ra_ctx *ctx, int *events, int request, void *arg) { - // Try a D3D11-specific method of taking a window screenshot - if (request == VOCTRL_SCREENSHOT_WIN) { - struct mp_image *img = d3d11_screenshot(ctx); - if (img) { - *(struct mp_image **)arg = img; - return true; - } - } - int ret = vo_w32_control(ctx->vo, events, request, arg); if (*events & VO_EVENT_RESIZE) resize(ctx); -- cgit v1.2.3