-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi, New patch which fixes some main issues with the last patch. 1) rendering is performed with no latency. drawn between the stretchblit and the present. 2) uses a L8A8 texture for the osd, so is correctly alphablended. note that even the gl driver has to map the inverted alpha value using software memory copy (line ~551 in vo_gl.c). It should be possible to come up with a better way to do this. Please be aware that although the code may use a glTexSubImage2D() for upload the chances are this is SW in the driver. It will use less memory bandwidth than the gl version as the gl version has no alpha test to reduce GPU loads from the framebuffer and its a two pass version. 3) The image is only rendered once and then presented multiple times if it does not change. 4) If no osd are visible on then there is no cost (well a few branches :-) ) I think that this addresses most of the issues. I think the main outstanding one will be the SW rendering of the subtitle to the texture. This is however a saving over a pure SW solution as the osd doesn't need to be continually blended with the changing video underneath. Like I say I may come up with a better way for texture upload in the next few days, but I think its OK for now. Jim
Hello,
I prefer to move all OSD questions/answers in this thread, because we're over with D3D's performance for time being.
First of all - Jim, thanks for updating to the latest SVN version and posting patches relative to it - it saves us all :) Let me ask some questions:
1. Drawing on the next frame is fixed easily I think. I'm sure Jim will do it right.
2. What bothers me more is this:
2) OSD is rendered in software first and then once more in hardware Again does the same as the gl renderer. You use vo_draw_alpha_rgb32 which does alpha blending and more in software. vo_gl does not.
I will look into this.
Hmmm, I have 2 questions: Why are all other drivers I looked into (like vo_directx.c) using vo_draw_alpha_xxxxx? This is supposed to be "software rendering", right Reimar? I mean - you give a texture of some size to the functions and tell in its colorspace and it draws the OSD for you? Additionally as I see the vo_draw_alpha_xxxxx has MMX/SSE/whatever optimizations and they are used when supported. What would be the "hardware" rendering? I can't really understand what is vo_gl.c doing different...
vo_gl calls vo_draw_text() with a parameter "create_osd_texture". Inside "create_osd_texture" you seem to "walk" through every OSD "part" and what... generate texture for each one?
Or better - vo_draw_text() calls "create_osd_texture" for every OSD element and "create_osd_texture" creates texture for it and places it somewhere, so finally everything gets "blended" on the backbuffer? Please excuse my prehistoric understanding about everything - my day job is writing software for network adapters :) :) :)
Are those things explained somewhere, so you can spare your words explaining what's happening? I'm ready to read literature about it...
The second question: why do you use vo_draw_alpha_rgb32? Because you create a texture with the same format as the backbuffer? This means that you DON'T "blend" the OSD with the movie's original offscreen surface and then StretchRect it on the backbuffer, but you render the OSD on a texture with the same format as the backbuffer and blend it independent from the offscreen surface of the movie?
BTW, I committed a patch for enabling BGR32/16/15 a few hours ago, but this is related to the format of the offscreen surface.
Another question: Why does vo_directx.c have support for:
vo_draw_alpha_yv12 vo_draw_alpha_yuy2 vo_draw_alpha_rgb15 .... and so on?
I think that it does blend the OSD in the equivalent of the offscreen movie source surface or just when using Overlays, the backbuffer itself is equal to the movie's source format and you have to render the OSD in YV12, YUY2 or whatever your movie uses...
Sorry, I misunderstood. It redraws for each frame whilst it is on. I'll fix this.
Does it mean that you must render the OSD in a texture on the first requested frame and just blend it until OSD is on (and haven't changed), so you don't render the OSD without need to do so on every frame (and use the already rendered instead)?
I'm sorry for my dumb questions. I'm trying to understand what's happening in the driver I originally wrote and only a week later don't understand anything :D _______________________________________________ MPlayer-dev-eng mailing list MPlayer-dev-eng@mplayerhq.hu https://lists.mplayerhq.hu/mailman/listinfo/mplayer-dev-eng
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iEYEARECAAYFAkkrOUkACgkQhrNWoHjgI1CeQgCbBnDDPkNm0GyT4R+726utCdYH mdwAn0AT3nXHitLR1DK/Z3Pk4tkXZRrK =HDb8 -----END PGP SIGNATURE----- Index: libvo/vo_direct3d.c =================================================================== --- libvo/vo_direct3d.c (revision 28034) +++ libvo/vo_direct3d.c (working copy) @@ -71,8 +71,14 @@ 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 + cant lock a normal texture. Uses RGBA */ IDirect3DSurface9 *d3d_backbuf; /**< Video card's back buffer (used to display next frame) */ + IDirect3DVertexBuffer9 *d3d_quad_vb; /**< Vertex buffer that contains the texture + coordinates for the quad. */ + int is_osd_populated; /* has the OSD something on it */ } *priv; typedef struct { @@ -93,7 +99,28 @@ }; #define DISPLAY_FORMAT_TABLE_ENTRIES \ - (sizeof(fmt_table) / sizeof(fmt_table[0])) + (sizeof(fmt_table) / sizeof(fmt_table[0])) + +#define D3DFVF_MY_VERTEX ( D3DFVF_XYZ | D3DFVF_TEX1 ) + +#define OSD_TEXTURE_SIZE 512 // as this is a texture, it can be a different size to the movie + +typedef struct vertex_s +{ + float x, y, z; // Position of vertex in 3D space + float tu, tv; // Texture coordinates +} vertex_t; + +vertex_t osd_quad_vb[] = +{ + {-1.0f,-1.0f, 0.0f, 0, 1 }, + {-1.0f, 1.0f, 0.0f, 0, 0 }, + { 1.0f, 1.0f, 0.0f, 1, 0 }, + { 1.0f,-1.0f, 0.0f, 1, 1 } +}; + +extern void vo_draw_text(int dxs, int dys, void (*draw_alpha)(int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride)); +extern int vo_osd_changed(int new_value); /**************************************************************************** * * @@ -180,6 +207,25 @@ priv->d3d_surface = NULL; } + /* kill the VB and the texture */ + 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_quad_vb != NULL) + { + IDirect3DVertexBuffer9_Release (priv->d3d_quad_vb); + priv->d3d_quad_vb = NULL; + } + if (priv->d3d_device != NULL) { IDirect3DDevice9_Release (priv->d3d_device); priv->d3d_device = NULL; @@ -201,6 +247,8 @@ { D3DPRESENT_PARAMETERS present_params; D3DDISPLAYMODE disp_mode; + void *vertices = NULL; + float aspect; mp_msg(MSGT_VO,MSGL_V,"<vo_direct3d><INFO>reconfigure_d3d called \n"); @@ -258,6 +306,55 @@ return 0; } + /* create OSD */ + if (FAILED (IDirect3DDevice9_CreateTexture( + priv->d3d_device, OSD_TEXTURE_SIZE, OSD_TEXTURE_SIZE, 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 (FAILED (IDirect3DDevice9_CreateTexture( + priv->d3d_device, OSD_TEXTURE_SIZE, OSD_TEXTURE_SIZE, 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; + } + + if (FAILED (IDirect3DDevice9_CreateVertexBuffer( priv->d3d_device, sizeof(osd_quad_vb), + 0, D3DFVF_MY_VERTEX, + D3DPOOL_DEFAULT, &priv->d3d_quad_vb, NULL ))) + { + mp_msg(MSGT_VO,MSGL_ERR, + "<vo_direct3d><INFO>IDirect3DDevice9_CreateVertexBuffer Failed (d3d_quad_vb).\n"); + return 0; + } + + if (FAILED (IDirect3DVertexBuffer9_Lock(priv->d3d_quad_vb, 0, sizeof(osd_quad_vb), + &vertices, 0 ))) + { + mp_msg(MSGT_VO,MSGL_ERR, + "<vo_direct3d><INFO>IDirect3DVertexBuffer9_Lock Failed.\n"); + return 0; + } + + memcpy( vertices, osd_quad_vb, sizeof(osd_quad_vb) ); + aspect = (float)priv->src_width / priv->src_height; + /* populate the Y with the correct coordinate */ + ((vertex_t *)vertices)[0].tv = + ((vertex_t *)vertices)[3].tv = ((1.0f / OSD_TEXTURE_SIZE) * (OSD_TEXTURE_SIZE / aspect)); + + if (FAILED (IDirect3DVertexBuffer9_Unlock(priv->d3d_quad_vb))) + { + mp_msg(MSGT_VO,MSGL_ERR, + "<vo_direct3d><INFO>IDirect3DVertexBuffer9_Unlock Failed.\n"); + return 0; + } + if (FAILED(IDirect3DDevice9_GetBackBuffer(priv->d3d_device, 0, 0, D3DBACKBUFFER_TYPE_MONO, &(priv->d3d_backbuf)))) { @@ -382,7 +479,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); } } @@ -566,13 +663,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 @@ -666,3 +756,131 @@ mp_msg(MSGT_VO,MSGL_V,"<vo_direct3d>draw_frame called\n"); return VO_FALSE; } + + +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++){ + register unsigned short *dst = (unsigned short*) dstbase; + register int x; + for(x=0;x<w;x++){ + if(srca[x]){ + dst[x]=(~srca[x] << 8) | src[x]; + } + } + src+=srcstride; + srca+=srcstride; + dstbase+=dststride; + } + return; +} + + +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, + (void *)((unsigned int)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) +{ + // calculate a width and height based on the OSD_TEXTURE_SIZE + float aspect = (float)priv->src_width / priv->src_height; + unsigned int calc_height = ((unsigned int)(OSD_TEXTURE_SIZE / aspect)); + + 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; + } + + memset(locked_rect.pBits, 0, locked_rect.Pitch*OSD_TEXTURE_SIZE); + + /* 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; + + vo_draw_text(OSD_TEXTURE_SIZE, calc_height, draw_alpha); + + 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) + { + 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_SRCBLEND, D3DBLEND_SRCALPHA); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHABLENDENABLE, TRUE); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHAREF, (DWORD)1); + IDirect3DDevice9_SetRenderState(priv->d3d_device, D3DRS_ALPHATESTENABLE, TRUE); + + /* need to use a texture here */ + 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 ); + + IDirect3DDevice9_SetTexture(priv->d3d_device, 0, (IDirect3DBaseTexture9 *)priv->d3d_texture_osd); + IDirect3DDevice9_SetStreamSource(priv->d3d_device, 0, priv->d3d_quad_vb, 0, sizeof(vertex_t) ); + IDirect3DDevice9_SetFVF(priv->d3d_device, D3DFVF_MY_VERTEX ); + IDirect3DDevice9_DrawPrimitive(priv->d3d_device, D3DPT_TRIANGLEFAN, 0, 2 ); + + /* 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; + } + } +}