/***************************************************************************** * d3d9_fmt.c : D3D9 helper calls ***************************************************************************** * Copyright © 2017 VLC authors, VideoLAN and VideoLabs * * Authors: Steve Lhomme * * This program 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 program 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 program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "d3d9_fmt.h" #include "../codec/avcodec/va_surface.h" #include "copy.h" #include picture_sys_t *ActivePictureSys(picture_t *p_pic) { struct va_pic_context *pic_ctx = (struct va_pic_context*)p_pic->context; return pic_ctx ? &pic_ctx->picsys : p_pic->p_sys; } #undef D3D9_CreateDevice HRESULT D3D9_CreateDevice(vlc_object_t *o, d3d9_handle_t *hd3d, HWND hwnd, const video_format_t *source, d3d9_device_t *out) { HRESULT hr; UINT AdapterToUse = D3DADAPTER_DEFAULT; D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL; #ifndef NDEBUG // Look for 'NVIDIA PerfHUD' adapter // If it is present, override default settings for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(hd3d->obj); ++Adapter) { D3DADAPTER_IDENTIFIER9 Identifier; hr = IDirect3D9_GetAdapterIdentifier(hd3d->obj,Adapter,0,&Identifier); if (SUCCEEDED(hr) && strstr(Identifier.Description,"PerfHUD") != 0) { AdapterToUse = Adapter; DeviceType = D3DDEVTYPE_REF; break; } } #endif /* ** Get device capabilities */ ZeroMemory(&out->caps, sizeof(out->caps)); hr = IDirect3D9_GetDeviceCaps(hd3d->obj, AdapterToUse, DeviceType, &out->caps); if (FAILED(hr)) { msg_Err(o, "Could not read adapter capabilities. (hr=0x%0lx)", hr); return hr; } msg_Dbg(o, "D3D9 device caps 0x%0lX / 0x%0lX", out->caps.DevCaps, out->caps.DevCaps2); /* TODO: need to test device capabilities and select the right render function */ if (!(out->caps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES)) { msg_Err(o, "Device does not support stretching from textures."); return E_INVALIDARG; } if ( source->i_width > out->caps.MaxTextureWidth || source->i_height > out->caps.MaxTextureHeight ) { msg_Err(o, "Textures too large %ux%u max possible: %ux%u", source->i_width, source->i_height, (unsigned) out->caps.MaxTextureWidth, (unsigned) out->caps.MaxTextureHeight); return E_INVALIDARG; } out->adapterId = AdapterToUse; out->hwnd = hwnd; if (D3D9_FillPresentationParameters(hd3d, source, out)) { msg_Err(o, "Could not presentation parameters"); return E_INVALIDARG; } /* */ if (FAILED(IDirect3D9_GetAdapterIdentifier(hd3d->obj, AdapterToUse,0, &out->identifier))) { msg_Warn(o, "IDirect3D9_GetAdapterIdentifier failed"); } else { msg_Dbg(o, "Direct3d9 Device: %s %lx %lx %lx", out->identifier.Description, out->identifier.VendorId, out->identifier.DeviceId, out->identifier.Revision ); } DWORD thread_modes[] = { D3DCREATE_MULTITHREADED, 0 }; DWORD vertex_modes[] = { D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_MIXED_VERTEXPROCESSING, D3DCREATE_SOFTWARE_VERTEXPROCESSING }; for (size_t t = 0; t < ARRAY_SIZE(thread_modes); t++) { for (size_t v = 0; v < ARRAY_SIZE(vertex_modes); v++) { DWORD creationFlags = thread_modes[t] | vertex_modes[v]; if (hd3d->use_ex) hr = IDirect3D9Ex_CreateDeviceEx(hd3d->objex, AdapterToUse, DeviceType, hwnd, creationFlags, &out->pp, NULL, &out->devex); else hr = IDirect3D9_CreateDevice(hd3d->obj, AdapterToUse, DeviceType, hwnd, creationFlags, &out->pp, &out->dev); if (SUCCEEDED(hr)) { out->owner = true; return hr; } } } msg_Err(o, "failed to create the D3D9%s device %d/%d. (hr=0x%lX)", hd3d->use_ex?"Ex":"", AdapterToUse, DeviceType, hr); return hr; } void D3D9_ReleaseDevice(d3d9_device_t *d3d_dev) { if (d3d_dev->dev) { IDirect3DDevice9_Release(d3d_dev->dev); d3d_dev->dev = NULL; } } /** * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display * from the default adapter. */ int D3D9_FillPresentationParameters(d3d9_handle_t *hd3d, const video_format_t *source, d3d9_device_t *out) { /* ** Get the current desktop display mode, so we can set up a back ** buffer of the same format */ D3DDISPLAYMODE d3ddm; if (source->i_width) { HRESULT hr = IDirect3D9_GetAdapterDisplayMode(hd3d->obj, out->adapterId, &d3ddm); if (FAILED(hr)) return VLC_EGENERIC; } /* Set up the structure used to create the D3DDevice. */ D3DPRESENT_PARAMETERS *d3dpp = &out->pp; ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS)); d3dpp->Flags = D3DPRESENTFLAG_VIDEO; d3dpp->Windowed = TRUE; d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; d3dpp->EnableAutoDepthStencil = FALSE; if (source->i_width) { d3dpp->hDeviceWindow = out->hwnd; d3dpp->SwapEffect = D3DSWAPEFFECT_COPY; d3dpp->BackBufferFormat = d3ddm.Format; d3dpp->BackBufferCount = 1; d3dpp->BackBufferWidth = __MAX((unsigned int)GetSystemMetrics(SM_CXVIRTUALSCREEN), source->i_width); d3dpp->BackBufferHeight = __MAX((unsigned int)GetSystemMetrics(SM_CYVIRTUALSCREEN), source->i_height); } else { d3dpp->hDeviceWindow = NULL; d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp->BackBufferCount = 0; d3dpp->BackBufferFormat = D3DFMT_X8R8G8B8; /* FIXME what to put here */ } return VLC_SUCCESS; } void D3D9_Destroy(d3d9_handle_t *hd3d) { if (hd3d->obj) { IDirect3D9_Release(hd3d->obj); hd3d->obj = NULL; } if (hd3d->hdll) { FreeLibrary(hd3d->hdll); hd3d->hdll = NULL; } } /** * It initializes an instance of Direct3D9 */ #undef D3D9_Create int D3D9_Create(vlc_object_t *o, d3d9_handle_t *hd3d) { hd3d->hdll = LoadLibrary(TEXT("D3D9.DLL")); if (!hd3d->hdll) { msg_Warn(o, "cannot load d3d9.dll, aborting"); return VLC_EGENERIC; } LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion); OurDirect3DCreate9 = (void *)GetProcAddress(hd3d->hdll, "Direct3DCreate9"); if (!OurDirect3DCreate9) { msg_Err(o, "Cannot locate reference to Direct3DCreate9 ABI in DLL"); goto error; } HRESULT (WINAPI *OurDirect3DCreate9Ex)(UINT SDKVersion, IDirect3D9Ex **ppD3D); OurDirect3DCreate9Ex = (void *)GetProcAddress(hd3d->hdll, "Direct3DCreate9Ex"); /* Create the D3D object. */ hd3d->use_ex = false; if (OurDirect3DCreate9Ex) { HRESULT hr = OurDirect3DCreate9Ex(D3D_SDK_VERSION, &hd3d->objex); if(!FAILED(hr)) { msg_Dbg(o, "Using Direct3D9 Extended API!"); hd3d->use_ex = true; } } if (!hd3d->obj) { hd3d->obj = OurDirect3DCreate9(D3D_SDK_VERSION); if (!hd3d->obj) { msg_Err(o, "Could not create Direct3D9 instance."); goto error; } } return VLC_SUCCESS; error: D3D9_Destroy( hd3d ); return VLC_EGENERIC; } static void DestroyPicture(picture_t *picture) { ReleasePictureSys(picture->p_sys); free(picture->p_sys); free(picture); } int Direct3D9LockSurface(picture_t *picture) { /* Lock the surface to get a valid pointer to the picture buffer */ D3DLOCKED_RECT d3drect; HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0); if (FAILED(hr)) { return VLC_EGENERIC; } return picture_UpdatePlanes(picture, d3drect.pBits, d3drect.Pitch); } void Direct3D9UnlockSurface(picture_t *picture) { /* Unlock the Surface */ HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface); if (FAILED(hr)) { //msg_Dbg(vd, "Failed IDirect3DSurface9_UnlockRect: 0x%0lx", hr); } } /* */ picture_pool_t *Direct3D9CreatePicturePool(vlc_object_t *o, d3d9_device_t *p_d3d9_dev, const d3d9_format_t *default_d3dfmt, const video_format_t *fmt, unsigned count) { picture_pool_t* pool = NULL; picture_t** pictures = NULL; unsigned picture_count = 0; pictures = calloc(count, sizeof(*pictures)); if (!pictures) goto error; D3DFORMAT format; switch (fmt->i_chroma) { case VLC_CODEC_D3D9_OPAQUE_10B: format = MAKEFOURCC('P','0','1','0'); break; case VLC_CODEC_D3D9_OPAQUE: format = MAKEFOURCC('N','V','1','2'); break; default: if (!default_d3dfmt) goto error; format = default_d3dfmt->format; break; } for (picture_count = 0; picture_count < count; ++picture_count) { picture_sys_t *picsys = malloc(sizeof(*picsys)); if (unlikely(picsys == NULL)) goto error; memset(picsys, 0, sizeof(*picsys)); HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3d9_dev->dev, fmt->i_width, fmt->i_height, format, D3DPOOL_DEFAULT, &picsys->surface, NULL); if (FAILED(hr)) { msg_Err(o, "Failed to allocate surface %d (hr=0x%0lx)", picture_count, hr); free(picsys); goto error; } picture_resource_t resource = { .p_sys = picsys, .pf_destroy = DestroyPicture, }; picture_t *picture = picture_NewFromResource(fmt, &resource); if (unlikely(picture == NULL)) { free(picsys); goto error; } pictures[picture_count] = picture; } picture_pool_configuration_t pool_cfg; memset(&pool_cfg, 0, sizeof(pool_cfg)); pool_cfg.picture_count = count; pool_cfg.picture = pictures; if( !is_d3d9_opaque( fmt->i_chroma ) ) { pool_cfg.lock = Direct3D9LockSurface; pool_cfg.unlock = Direct3D9UnlockSurface; } pool = picture_pool_NewExtended( &pool_cfg ); error: if (pool == NULL && pictures) { for (unsigned i=0;i