-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Reimar Döffinger wrote:
On Mon, Dec 08, 2008 at 12:00:52PM +0000, Jim Hauxwell wrote:
It should never go outside the texture area when generating the OSD
While that is true you fall back to using the whole texture, which i think is as in your very first patch, so the aspect issue should arise again. Setiing max_texture_width to 256 should help you see the issue if I am right.
This is exactly what I did to test it. Yes I agree the results for some really low end cards (which don't support decent texture sizes) wont be ideal, but it will probably not affect 99% of people. Also it shouldn't now crash. Ideally for a card which only supported say 1024 width, I wanted to use the whole width to get the best OSD possible
@@ -397,7 +500,7 @@ /* If we're here, then we should lock the rect and copy a packed frame */ if (!priv->locked_rect.pBits) { if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface, - &priv->locked_rect, NULL, 0))) { + &priv->locked_rect, NULL, D3DLOCK_DISCARD))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failure\n"); return VO_ERROR; }
Unrelated. If that is intentional and you explain the why i can make this change, you don't need to send a separate patch.
Sorry, I was playing elsewhere with some performance enhancing stuff. It didn't actually make any noticeable difference on my card, so could probably come out for now. I'll go back to it later.
@@ -530,9 +636,34 @@ /* Store in priv->desktop_fmt the user desktop's colorspace. Usually XRGB. */ priv->desktop_fmt = disp_mode.Format;
- mp_msg(MSGT_VO, MSGL_V, "disp_mode.Width %d, disp_mode.Height %d\n", + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>disp_mode.Width %d, disp_mode.Height %d\n", disp_mode.Width, disp_mode.Height);
Unrelated, remind me to fix it (at the latest) after the OSD was applied if I forget.
Will do Patch attached with no intrinsics as per your last mail. I am running this on Vista just FYI Jim -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iEYEARECAAYFAkk9VrAACgkQhrNWoHjgI1DLcQCgj29GX0Q6aZcDvj5jYWv73urj 5RMAn1UA3D40e6rwq2QNM/hAGekpBnZV =JZPU -----END PGP SIGNATURE----- Index: libvo/vo_direct3d.c =================================================================== --- libvo/vo_direct3d.c (revision 28089) +++ libvo/vo_direct3d.c (working copy) @@ -30,6 +30,7 @@ #include "aspect.h" #include "w32_common.h" #include "libavutil/common.h" +#include "sub.h" static const vo_info_t info = { @@ -71,8 +72,23 @@ IDirect3DSurface9 *d3d_surface; /**< Offscreen Direct3D Surface. MPlayer renders inside it. Uses colorspace priv->movie_src_fmt */ + IDirect3DTexture9 *d3d_texture_osd; /**< Direct3D Texture. Uses RGBA */ + IDirect3DTexture9 *d3d_texture_system; /**< Direct3D Texture. System memory + cannot lock a normal texture. Uses RGBA */ IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to display next frame) */ + int is_osd_populated; /**< 1 = OSD texture has something to display, + 0 = OSD texture is clear */ + int device_caps_power2; /**< 1 = texture sizes have to be power 2 + 0 = texture sizes can be anything */ + int device_caps_square_only; /**< 1 = textures have to be square + 0 = textures do not have to be square */ + int device_texture_sys; /**< 1 = device can texture from system memory + 0 = device requires shadow */ + int max_texture_width; /**< from the device capabilities */ + int max_texture_height; /**< from the device capabilities */ + int osd_texture_width; /**< current width of the OSD */ + int osd_texture_height; /**< current height of the OSD */ } *priv; typedef struct { @@ -100,6 +116,13 @@ #define DISPLAY_FORMAT_TABLE_ENTRIES (sizeof(fmt_table) / sizeof(fmt_table[0])) +#define D3DFVF_MY_VERTEX (D3DFVF_XYZ | D3DFVF_TEX1) + +typedef struct { + float x, y, z; /* Position of vertex in 3D space */ + float tu, tv; /* Texture coordinates */ +} struct_vertex; + /**************************************************************************** * * * * @@ -185,17 +208,42 @@ priv->d3d_surface = NULL; } + /* kill the OSD texture and its shadow copy */ + if (priv->d3d_texture_osd != NULL) { + IDirect3DTexture9_Release(priv->d3d_texture_osd); + priv->d3d_texture_osd = NULL; + } + + if (priv->d3d_texture_system != NULL) { + IDirect3DTexture9_Release(priv->d3d_texture_system); + priv->d3d_texture_system = NULL; + } + if (priv->d3d_backbuf) { IDirect3DSurface9_Release(priv->d3d_backbuf); priv->d3d_backbuf = NULL; } } +/** @brief Round up to the next power2 of the input + */ +static unsigned int next_power2(unsigned int p) +{ + p--; + p |= p >> 1; + p |= p >> 2; + p |= p >> 4; + p |= p >> 8; + p |= p >> 16; + return p + 1; +} + /** @brief Create D3D Offscreen and Backbuffer surfaces. * @return 1 on success, 0 on failure */ static int create_d3d_surfaces(void) { + int osd_width, osd_height; mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d><INFO>create_d3d_surfaces called.\n"); if (FAILED(IDirect3DDevice9_CreateOffscreenPlainSurface( @@ -213,6 +261,76 @@ return 0; } + /* round up to next power of 2 */ + osd_width = next_power2(vo_dwidth); + osd_height = next_power2(vo_dheight); + + /* calculate the best size for the OSD depending on the factors from the device */ + if (priv->device_caps_square_only) { + /* device only supports square textures */ + /* this is likely that they are only power2 (old HW) */ + priv->osd_texture_width = + priv->osd_texture_height = max(osd_width, osd_height); + } else { + if (priv->device_caps_power2) { + /* device supports non square, but power 2 */ + priv->osd_texture_width = osd_width; + priv->osd_texture_height = osd_height; + } else { + /* device supports any size, but power round to 16pixels */ + priv->osd_texture_width = (vo_dwidth + 15) & ~0xF; + priv->osd_texture_height = (vo_dheight + 15) & ~0xF; + } + } + + /* clamp to recorded max texture width and height */ + priv->osd_texture_width = min(priv->max_texture_width, priv->osd_texture_width); + priv->osd_texture_height = min(priv->max_texture_height, priv->osd_texture_height); + + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d><INFO>surface (%d, %d) requested = (%d, %d)\n", + vo_dwidth, vo_dheight, priv->osd_texture_width, priv->osd_texture_height); + + /* create OSD */ + if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, + priv->osd_texture_width, + priv->osd_texture_height, + 1, + D3DUSAGE_DYNAMIC, + D3DFMT_A8L8, + D3DPOOL_SYSTEMMEM, + &priv->d3d_texture_system, + NULL))) { + mp_msg(MSGT_VO,MSGL_ERR, + "<vo_direct3d><INFO>IDirect3DDevice9_CreateTexture Failed (d3d_texture_system).\n"); + return 0; + } + + if (!priv->device_texture_sys) { + /* only create if we need a shadow version on the external device */ + if (FAILED(IDirect3DDevice9_CreateTexture(priv->d3d_device, + priv->osd_texture_width, + priv->osd_texture_height, + 1, + D3DUSAGE_DYNAMIC, + D3DFMT_A8L8, + D3DPOOL_DEFAULT, + &priv->d3d_texture_osd, + NULL))) { + mp_msg(MSGT_VO,MSGL_ERR, + "<vo_direct3d><INFO>IDirect3DDevice9_CreateTexture Failed (d3d_texture_osd).\n"); + return 0; + } + } + + /* setup default renderstate */ + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_SRCBLEND, D3DBLEND_ONE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAFUNC, D3DCMP_GREATER); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAREF, (DWORD)0x0); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_LIGHTING, FALSE); + IDirect3DDevice9_SetSamplerState(priv->d3d_device, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + IDirect3DDevice9_SetSamplerState(priv->d3d_device, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + return 1; } @@ -470,7 +588,7 @@ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Accepted image format: %s\n", vo_format_name(fmt_table[i].mplayer_fmt)); return (VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW - /*| VFCAP_OSD*/ | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN); + | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN); } } @@ -501,6 +619,9 @@ static int preinit(const char *arg) { D3DDISPLAYMODE disp_mode; + D3DCAPS9 disp_caps; + DWORD texture_caps; + DWORD dev_caps; /* Set to zero all global variables. */ priv = calloc(1, sizeof(struct global_priv)); @@ -533,6 +654,31 @@ mp_msg(MSGT_VO, MSGL_V, "disp_mode.Width %d, disp_mode.Height %d\n", disp_mode.Width, disp_mode.Height); + if (FAILED(IDirect3D9_GetDeviceCaps(priv->d3d_handle, + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + &disp_caps))) { + mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Could not read display capabilities\n"); + return -1; + } + + /* Store relevant information reguarding caps of device */ + texture_caps = disp_caps.TextureCaps; + dev_caps = disp_caps.DevCaps; + priv->device_caps_power2 = ((texture_caps & D3DPTEXTURECAPS_POW2) != 0) == + ((texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) != 0); + priv->device_caps_square_only = ((texture_caps & D3DPTEXTURECAPS_SQUAREONLY) != 0); + priv->device_texture_sys = ((dev_caps & D3DDEVCAPS_TEXTURESYSTEMMEMORY) != 0); + priv->max_texture_width = disp_caps.MaxTextureWidth; + priv->max_texture_height = disp_caps.MaxTextureHeight; + + mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>device_caps_power2 %d, device_caps_square_only %d\n" + "<vo_direct3d>device_texture_sys %d\n" + "<vo_direct3d>max_texture_width %d, max_texture_height %d\n", + priv->device_caps_power2, priv->device_caps_square_only, + priv->device_texture_sys, priv->max_texture_width, + priv->max_texture_height); + /* w32_common framework call. Configures window on the screen, gets * fullscreen dimensions and does other useful stuff. */ @@ -667,13 +813,6 @@ } } -/** @brief libvo Callback: Draw OSD/Subtitles, - * @return N/A - */ -static void draw_osd(void) -{ -} - /** @brief libvo Callback: Uninitializes all pointers and closes * all D3D related stuff, * @return N/A @@ -770,3 +909,141 @@ mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>draw_frame called\n"); return VO_FALSE; } + +/** @brief Maps MPlayer alpha to D3D + * 0x0 -> transparent and discarded by alpha test + * 0x1 -> 0xFF to become opaque + * other alpha values are inverted +1 (2 = -2) + * These values are then inverted again with + the texture filter D3DBLEND_INVSRCALPHA + */ +void vo_draw_alpha_l8a8(int w,int h, unsigned char* src, unsigned char *srca, + int srcstride, unsigned char* dstbase,int dststride) +{ + int y; + for (y = 0; y < h; y++) { + unsigned short *dst = (unsigned short*)dstbase; + int x; + for (x = 0; x < w; x++) { + dst[x] = (-srca[x] << 8) | src[x]; + } + src += srcstride; + srca += srcstride; + dstbase += dststride; + } +} + +/** @brief Callback function to render the OSD to the texture + */ +static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, + unsigned char *srca, int stride) +{ + D3DLOCKED_RECT locked_rect; /**< Offscreen surface we lock in order + to copy MPlayer's frame inside it.*/ + + if (FAILED(IDirect3DTexture9_LockRect(priv->d3d_texture_system, 0, + &locked_rect, NULL, 0))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>IDirect3DTexture9_LockRect failure.\n"); + return; + } + + vo_draw_alpha_l8a8(w,h,src,srca,stride, + (unsigned char *)locked_rect.pBits+locked_rect.Pitch*y0+2*x0, locked_rect.Pitch); + + /* this unlock is used for both slice_draw path and D3DRenderFrame path */ + if (FAILED(IDirect3DTexture9_UnlockRect(priv->d3d_texture_system, 0))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>IDirect3DTexture9_UnlockRect failure.\n"); + return; + } + + priv->is_osd_populated = 1; +} + +/** @brief libvo Callback: Draw OSD/Subtitles, + * @return N/A + */ +static void draw_osd(void) +{ + if (vo_osd_changed(0)) { + D3DLOCKED_RECT locked_rect; /**< Offscreen surface we lock in order + to copy MPlayer's frame inside it.*/ + + /* clear the OSD */ + if (FAILED(IDirect3DTexture9_LockRect(priv->d3d_texture_system, 0, + &locked_rect, NULL, 0))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>IDirect3DTexture9_LockRect failure.\n"); + return; + } + + /* only need to clear upto the size of the display window */ + memset(locked_rect.pBits, 0, locked_rect.Pitch * min(vo_dheight, priv->osd_texture_height)); + + /* this unlock is used for both slice_draw path and D3DRenderFrame path */ + if (FAILED(IDirect3DTexture9_UnlockRect(priv->d3d_texture_system, 0))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>IDirect3DTexture9_UnlockRect failure.\n"); + return; + } + + priv->is_osd_populated = 0; + /* required for if subs are in the boarder region */ + priv->is_clear_needed = 1; + + vo_draw_text(min(vo_dwidth, priv->osd_texture_width), + min(vo_dheight, priv->osd_texture_height), draw_alpha); + + if (!priv->device_texture_sys) + { + /* only DMA to the shadow if its required */ + if (FAILED(IDirect3DDevice9_UpdateTexture(priv->d3d_device, + (IDirect3DBaseTexture9 *)priv->d3d_texture_system, + (IDirect3DBaseTexture9 *)priv->d3d_texture_osd))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>IDirect3DDevice9_UpdateTexture failure.\n"); + return; + } + } + } + + /* update OSD */ + + if (priv->is_osd_populated) { + + struct_vertex osd_quad_vb[] = { + {-1.0f, 1.0f, 0.0f, 0, 0 }, + { 1.0f, 1.0f, 0.0f, 1, 0 }, + {-1.0f,-1.0f, 0.0f, 0, 1 }, + { 1.0f,-1.0f, 0.0f, 1, 1 } + }; + + /* calculate the texture coordinates */ + osd_quad_vb[1].tu = + osd_quad_vb[3].tu = min((1.0 / priv->osd_texture_width) * vo_dwidth, 1.0); + osd_quad_vb[2].tv = + osd_quad_vb[3].tv = min((1.0 / priv->osd_texture_height) * vo_dheight, 1.0); + + if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>BeginScene failed\n"); + return; + } + + /* turn on alpha test */ + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, TRUE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, TRUE); + + /* need to use a texture here (done here as we may be able to texture from system memory) */ + IDirect3DDevice9_SetTexture(priv->d3d_device, 0, + (IDirect3DBaseTexture9 *)((priv->device_texture_sys) + ? priv->d3d_texture_system : priv->d3d_texture_osd)); + + IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_MY_VERTEX); + IDirect3DDevice9_DrawPrimitiveUP(priv->d3d_device, D3DPT_TRIANGLESTRIP, 2, osd_quad_vb, sizeof(struct_vertex)); + + /* turn off alpha test */ + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, FALSE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, FALSE); + + if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) { + mp_msg(MSGT_VO,MSGL_ERR,"<vo_direct3d>EndScene failed\n"); + return; + } + } +}