今夜も Wine で乾杯! - 21本目 [無断転載禁止]©2ch.net
■ このスレッドは過去ログ倉庫に格納されています
Linux上でWindowsのアプリを動作させるソフトウェア
Wineに関する情報交換スレ。
前スレ
今夜も Wine で乾杯! - 20本目
https://mao.2ch.net/test/read.cgi/linux/1455088008/
Wine本家
http://www.winehq.org/
http://wiki.winehq.org/
動作報告Wikiや過去ログなど
http://www.2chlinux.org/index.php?FrontPage
ここにパッチをうpするときはgzipやbzip2で圧縮した上で
base64などでエンコードしてください。おながいします。
動作報告は>>2のテンプレ使用を推奨。 >>353
/ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\
| おまわりさんコイツです! |
\_ ________/
∨ ___
,r'つ@=,r'
.| (-_- )
(・ω・´)、/~У ̄|゙i
゚こ、 つ |=◎=∪
しー-J (_(__) >>350
大半が同じ感想のはず。
ajaxの手法が公開されてから一気に沸騰した。 The Wine development release 3.1 is now available.
What's new in this release (see below for details):
- Kerberos authentication support.
- Window class redirection for Common Controls 6.
- Support for X11 ARGB visuals.
- DOSBox required for running DOS executables.
- Various bug fixes. wine-staging2.21からwine-stable3.0にしたら
負荷とメモリ消費が素晴らしく改善されたから
開発版に手を出しかねてる
3.0で改善されたのか、stagingのものが足を引っ張ってたのか判別つかなくて 3.4のときは起動すらしなかったのにな
4.0になったら快調すぎる 結局のところwinehq-stableとwine-stableの差は何なのさ winehq stableが公式サイトで公開されてるやつでwine stableは各ディストリから配布されてるやつだと思ってたけど違うのかな wine-3.1-arm.apk
ExaGearより動くかなと期待して入れたんだけど、プログラムが一つも動かない…
まだ見てくれだけの未完成品ってこと? あー Windows RT ARMのソフトしか動かないのかこれ >>371
こいつMac関連のスレ狙ってる?別にそんな事ないのかな crossoverのベースになっているwineのバージョンってどこから調べたらいいの?
もう3.0ベースになってるのかな ちがったかな
$ /opt/cxoffice/bin/wineserver --version
wine-2.8-8251-g8a457d1 ubuntu studio 16.04LTS にwineを入れて、
JaneStyle を使える様にはなった。
書き込みテスト。 >>368
まじかよ、Elonaさえ動けばいいのに。 ARM機とかスマホしか持ってないわ
これから増えるんかね PS VitaもARMだしSwitchもARMだな RTでコケて痛い目にあったからそれはないと思う。
ARMじゃない別のアーキテクチャで出す可能性はあるかも・・・
そしてまたコケるw >>385
いや出るから
HPとASUSがsnapdragon搭載Windows端末を発表済で春に発売予定 売れるとは限らないんだがなぁ
x86exeが何となくでも動くだけでいいのにな ちょっと調べてから否定しようよ
まあいいんだけどさ 古いけど、Ubuntu12.01 への Wine3.0のインストールに成功した。
git のソースから、./configure
途中、Font関連のファイルが見つからないエラーが出たので、
そのFontの「dev版」をapt-getでインストール。
その他、いくつかエラーが出るので、インストールしたりする。
その後、make, make install。
でインストールには成功するが、今まで動いていたWinアプリを起動してみると
日本語で文字化けしてる。wintricks font や regedit などをいじってみたが、
ダメだった。しかし、apt-get remove wineとしたあと、再度、
apt-get install wineとすると、なぜか、すぐに成功した。
wine --versionとすると、なぜか、3.1のままだった。
不思議な事に、文字化けはすっきり解決した状態で、Ver 3.1のWineが動いているらしい。
実際、Windowsとの互換性は明らかに向上した。 Wzエディタで、検索やGrepのダイアログのStatic Textの表示が途切れてしまう
不具合があったのが、Wine3.0では修正されている。これは使える。 透明色と MDI Window を組み合わせた自作WindowsアプリをWineで動くように修正中。
MDI Child Widnow のタイトルバーをドラッグすると遅くなるので原因を調査していた。
PreTranslateMessage() にくるメッセージを、自前でWindowに表示していたところ、
システム全体が実質的にハングアップした。マウスは動くが、タスクバーも反応しない。
ちなみに、透明色とMDI Windowの組み合わせが必ず遅くなるわけではない事は、
単純なテストプログラムを作って分かっている。ではなぜ、このプログラムに限っては
遅くなるのかが分からないので調査中だった。 The Wine development release 3.2 is now available.
What's new in this release (see below for details):
- Separate implementation of USER controls for ComCtl32 v6.
- Multisample texture support in Direct3D.
- Support for HID gamepads.
- More event support in MSHTML.
- Obsolete DOS code removed.
- Various bug fixes. Wineは、Desktopの直接の子であるような、Win32における「OVERLAPED WINDOW」
的な物しか、XWindow の Window を作らないのだろうか??
Wineのソースをダウンロードして、FIXME(WARNでもいいはずだけど)で実行を調べてみた。
でも、良く分からない。ビルドとmake installに時間がかかるため、大変。
本当はもっと実験したいんだけど、時間的に難しくなる。
ソースを見ると、HWNDの親が Desktopの場合にだけ、XCreateWindowしているように
見える。これが、透明ウィンドウが遅くなる理由かもしれない。LinuxのNativeなWindowの
場合、実験した限り透明にしても速度が余り変わらない。でも、Wineがもし、自前で透明処理
をやっているとなれば遅くなるのも頷けるが。
1つのソースしか修正してないのに、ビルドが始まるまでに数十秒かかる。make installに
また数十秒。それに、wineserver -k などにも時間がかかるし。なんか、30年前のビルド環境
みたいだ。 >>397
今回に関しては、以下を読んで、git でやってます :
https://wiki.winehq.org/Git_Wine_Tutoria
home directory (~) にて、
$ git clone git://source.winehq.org/git/wine.git [ret]
とすると、wine ディレクトリが作成されるので、
$ cd wine
とします。そこで、
$ git config remote.origin.url [ret]
と入れて
git://source.winehq.org/git/wine.gi
と出れば、大体 DL 成功です。
~/wine/ にソースが DL されており、dlls に Win32 の DLL 群の実装があります。
例えば、user32 などのフォルダがあります。~/wine/server に wine の中枢の
プログラムが入っています。CreateWindow などは、dlls の方にありますが、
それを XWindowCreate に橋渡しするのは、server の方です。
オンラインで見たいなら、
https://source.winehq.org/git/wine.git/tree/HEAD:/dlls/user32
の win.c に CreateWindow 系のソースが入っており、
https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/user32/win.c
で見られます。win.c の右の「raw」のボタンを押すと、ローカルの Wz エディタ
などでも見られます。 5ch からのアクセスは禁止されているらしいので、アドレスバーに上記の URL を
コピーしてアクセスしてください。
なお、make が遅い件は、make -j 4 などとして、マルチコア動作するとだいぶ良くなりました。
ただし、その場合、make にバグがあるので精密な神経の持ち主には、ストレスがたまります。 動きをトレースしたい場合、TRACE はデフォルトではメッセージが出ず、使い方を調べて
ないので、
FIXME( "Yamada Taro, CreateWindowEx, hWND=%08X", hWND );
みたいにやってます。 makeしたい人は、~/wine にて、
$ ./confiugre
$ make -j 4 2>build.log
$ make -j 4 install 2>inst.log
$ wineserver -k
で行けます。ここで、wz エディタを起動してみます。文字化けするようでしたら、
$ apt-get remove wine
$ apt-get install wine
で直ると思います。これは、一度やれば Ok のようです。 call set_window_pos()
--> wine/dlls/user32/winpos.c
--> X11DRV_WindowPosChanging()
--> X11DRV_create_win_data()
--> dlls/winex11.drv/window.c :
static void create_whole_window( struct x11drv_win_data *data )
--->
data->whole_window = XCreateWindow(
data->display, root_window, pos.x, pos.y,
cx, cy, 0, data->vis.depth, InputOutput,
data->vis.visual, mask, &attr );
という流れのようです。 以下を見ると、親が、DesktopWindow の時のみ、create_whole_window() が呼び出されている
らしいことが分かります。
static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *window_rect,
const RECT *client_rect ) {
Display *display;
struct x11drv_win_data *data;
HWND parent;
if (!(parent = GetAncestor( hwnd, GA_PARENT ))) return NULL; /* desktop */
/* don't create win data for HWND_MESSAGE windows */
if (parent != GetDesktopWindow() && !GetAncestor( parent, GA_PARENT )) return NULL;
if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId()) return NULL;
display = thread_init_display();
init_clip_window(); /* make sure the clip window is initialized in this thread */
if (!(data = alloc_win_data( display, hwnd ))) return NULL;
data->whole_rect = data->window_rect = *window_rect;
data->client_rect = *client_rect;
if (parent == GetDesktopWindow()){
create_whole_window( data );
TRACE( "win %p/%lx window %s whole %s client %s\n",
hwnd, data->whole_window, wine_dbgstr_rect( &data->window_rect ),
wine_dbgstr_rect( &data->whole_rect ), wine_dbgstr_rect( &data->client_rect ));
}
return data;
} >>402 の訂正:
CreateWindowExW()
--->
WIN_CreateWindowEx()
call set_window_pos()
--> wine/dlls/user32/winpos.c
--> X11DRV_WindowPosChanging()
--> X11DRV_create_win_data()
--> dlls/winex11.drv/window.c :
static void create_whole_window( struct x11drv_win_data *data )
--->
data->whole_window = XCreateWindow(
data->display, root_window, pos.x, pos.y,
cx, cy, 0, data->vis.depth, InputOutput,
data->vis.visual, mask, &attr ); ちなみに、
1. ~/.wine
2. ~/wine
は別物です。1は、binary をインストールした際に出来るデフォルトのフォルダ
ですよね。2. は、git からソースを持ってきた時にできるディレクトリです。
まずは、バイナリをインストールして動作してから、ソースを持ってきてください。 半透明化はWin32API側でどうやっているの? Linuxネイティブなアプリでは?
SetLayeredWindowAttributesであれば、user32.dllからUSER_Driverを介してwinex11.drvが呼ばれて、
window.c内のsync_window_opacityで_NET_WM_WINDOW_OPACITYにα値を設定している様子が見て取れる
ttps://github.com/wine-mirror/wine/blob/wine-3.2/dlls/winex11.drv/window.c#L435
TRACEで表示しているメッセージを確認するには環境変数WINEDEBUGを使って、
WINEDEBUG=win,x11 みたいにカンマ区切りで指定する。細かくは
ttps://wiki.winehq.org/Wine_Developer%27s_Guide/Debug_Logging に書いてある。
あとは、dlls配下の各ディレクトリでもmake && sudo make instlallできるので
特定のDLLファイルしか変更しないのであれば、この方法でビルド時間を短縮できるぞ。 デバッグ大変だな。めんどくさそう。
仕事じゃないと俺はやらないだろうな。 自分はimm32関連(日本語入力)APIを修正しようとeclipceでコンパイル環境作ったはいいけど
ネイティブのウィンドウマネージャ関連の知識不足でソースの意味がわからず寝かせてある・・・ >>406
Linux Nativeアプリの場合、32BIT COLOR にすると、A,R,G,B の 4つの値を
ドットの「色」として指定できます。Aがα値です。このようなことは、Windows
では出来ないと思います。Windowsの場合、CreateWindowExのdwExStyle に
WS_EX_LAYEREDを指定すると透明、半透明が扱えるようになります。
1.完全に「透明になる色」を24BIT値で1色指定できます。この色で描いたドットは、
デスクトップまで透けて見えるようになります。見た目だけではなく、Windowメッ
セージも下のWindowに伝達されてしまうことになりますが。
2.ドットごとではなく、Window全体のα値を1つ(1BYTE)だけ指定できます。
ドットごとでは無いので、全体的に透明度が決まってしまいます。
1と2は完全に別の概念です。
LinuxのARGB値は、ドット毎に指定できるので、Windowsの機能を包含していると
言えます。逆に Windowsでは、同じ事は出来ないはずです。 /wine/dlls/winex11.drv/x11drv.h
に次のような構造体があり、この whole_window というのが大事らしい:
/* x11drv private window data */
struct x11drv_win_data {
Display *display; /* display connection for the thread owning the window */
XVisualInfo vis; /* X visual used by this window */
Colormap colormap; /* colormap if non-default visual */
HWND hwnd; /* hwnd that this private data belongs to */
Window whole_window; /* X window for the complete window */
Window client_window; /* X window for the client area */
RECT window_rect; /* USER window rectangle relative to parent */
RECT whole_rect; /* X window rectangle for the whole window relative to parent */
RECT client_rect; /* client area relative to parent */
XIC xic; /* X input context */
BOOL managed : 1; /* is window managed? */
BOOL mapped : 1; /* is window mapped? (in either normal or iconic state) */
BOOL iconic : 1; /* is window in iconic state? */
BOOL embedded : 1; /* is window an XEMBED client? */
BOOL shaped : 1; /* is window using a custom region shape? */
BOOL layered : 1; /* is window layered and with valid attributes? */
BOOL use_alpha : 1; /* does window use an alpha channel? */
int wm_state; /* current value of the WM_STATE property */
DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */
Window embedder; /* window id of embedder */
unsigned long configure_serial; /* serial number of last configure request */
struct window_surface *surface;
Pixmap icon_pixmap;
Pixmap icon_mask;
unsigned long *icon_bits;
unsigned int icon_size;
}; >>406
後半の二つ。自分に取っては、かなり貴重な情報です。助かります。 >>406
XWindow で透明化。これはやってみて実際に出来ました:
https://stackoverflow.com/questions/39906128/
how-to-create-semi-transparent-white-window-in-xlib
子ウィンドウを入れてみても、ちゃんと出来ました。
ただし、子ウィンドウには、Win32のような、タイトルバーは付ききませんでした。
XWindow に詳しい人によれば、XWindow の Window Manager は、
Win32 の MDI のような事は、サポートしていないらしいです。絶対か
どうかは分かりませんが。
実験してみると、子ウィンドウも、親ウィンドウも、ABI で、XMoveWindow
でスムーズに動かせました。
ただし、Wineの場合、子ウィンドウに直線や文字が書かれていて、かつ、背景が透明な場合、
スムーズには動かせません。 Win32APIだけじゃなく、X Windowについても知っておかないといけないのか
面倒臭っ・・・
普段使ってるだけでずいぶん楽してたんだな。 面白そうだけど時間ねえな……
>>410を見る限りだとuse_alphaは持ってるけどAlpha値自体はもうX11の構造体にいれてるのかな?
まあ遅くなる理由とか検討つかんけど…… WINEにおいて、
1. MDIのCMDIChildWndのCViewのCLIENT領域全体(子ウィンドウの
中全体と言ってもよい) に >>409の1.の色を塗って、完全透明
にしている時は、CMDIChildWndのタイトルバー(子ウィンドウの
タイトルバー)をドラッグしても高速に動かせる。
2. 1のCViewの中に、pDC->LineTo()で直線を一本描いた状態にしてから
同じ事をしようとすると、とても遅くなる。
3. 2.は、直線の代わりに pDC->TextOut() で文字を描いても同様に遅く
なる。
4. 推定では、Wineは、Win32のCreateWindow系で、Parent Window が
Desktop 以外の場合、XWindow の Window を使わず、自前で
Parent Window の中に画像を合成して子Windowを模倣している。
5. 4.の様にしている理由は、推定だと Window Manager の種類による
挙動の違いに患わされずに安定して Emulation するためかも知れ
ない。
6. その結果、XWindow の ARGB 値による高速な透明処理を利用できな
くなり、低速になる。
7. しかし、なるべく低速にならないようにするため、少なくとも、
子ウィンドウの中全体が完全透明な場合については、対策が取ら
れており、高速にドラッグできるようになっている。
8. その結果、2, 3 のような条件の時のみ遅くなると推定される。 >>415
すまん、ウィンドウの半透明化処理(LWA_ALPHA)と勘違いしてた。
ウィンドウ領域内の特定色で書いた箇所を透過させる場合(LWA_COLORKEY)ね。
ソースを調べたら、WineではX11のShape Extensionを使ってウィンドウの形状を変更することで、
「見た目だけではなく、Windowメッセージも下のWindowに伝達すること」を実現しているようだ。
具体的にはupdate_surface_region()で1ピクセルごとにピクセル値を比較して、
XShapeCombineRectanglesに指定する矩形領域を作っている。
ttps://github.com/wine-mirror/wine/blob/2936f3f9bb9dc01c595498a821d6adb6775b62cc/dlls/winex11.drv/bitblt.c#L1616
415の2.,3.の条件だと遅くなるとすると、ピクセル値を比較する処理は1.と同じなので、
矩形が大量になったときに、X側で描画性能が低下するのではないかと思うぞ。
StackOverflowの記事はShape Extensionを使っていないので筋違いだ。 >>418
なるほど。
1つ質問です。
はっきりとは書いてなかったのですが、>>415の遅くなる条件であるところの 2,3 の場合
においても、CMainFrame、つまり、アプリケーション全体の Main の Window のタイトルバーを
ドラッグした場合は、遅くなりません。いたって高速にドラッグできます。
>>418 が正しいなら、どうして、X は、この場合だけは速く、CMDIChildWnd の場合だけは
遅く動作するのでしょうか??? ↓の構造体の dc_rect の矩形が子ウィンドウを模倣するために使われているかも知れません:
/* X physical device */
typedef struct {
struct gdi_physdev dev;
GC gc; /* X Window GC */
Drawable drawable;
RECT dc_rect; /* DC rectangle relative to drawable */
RECT *bounds; /* Graphics bounds */
HRGN region; /* Device region (visible region & clip region) */
X_PHYSPEN pen;
X_PHYSBRUSH brush;
int depth; /* bit depth of the DC */
ColorShifts *color_shifts; /* color shifts of the DC */
int exposures; /* count of graphics exposures operations */
} X11DRV_PDEVICE;
BOOL X11DRV_LineTo( PHYSDEV dev, INT x, INT y ) {
X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
POINT pt[2];
GetCurrentPositionEx( dev->hdc, &pt[0] );
pt[1].x = x;
pt[1].y = y;
LPtoDP( dev->hdc, pt, 2 );
add_pen_device_bounds( physDev, pt, 2 );
if (X11DRV_SetupGCForPen( physDev ))
XDrawLine(gdi_display, physDev->drawable, physDev->gc,
physDev->dc_rect.left + pt[0].x, physDev->dc_rect.top + pt[0].y,
physDev->dc_rect.left + pt[1].x, physDev->dc_rect.top + pt[1].y );
return TRUE;
} /wine/dlls/user32/painting.c の中の、
// Set the visible region and X11 drawable for the DC associated to a given window.
static void update_visible_region( struct dce *dce )
の中に、
USER_Driver->pGetDC( dce->hdc, dce->hwnd, top_win, &win_rect, &top_rect, flags );
とあって、
void CDECL X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect,
const RECT *top_rect, DWORD flags )
が呼び出される。引数に hwnd と top、win_rect と top_rect が対になっているらしいことに注意。
この関数の中で、x11drv_escape_set_drawable escape; の
escape.dc_rect に、win_rect の top_rect の (left, top) からの相対座標が入れられる。
そして、ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
と ExtEscape() が呼び出される。
escape = X11DRV_ESCAPE;
escape.code = X11DRV_SET_DRAWABLE;
in_data = &escape;
の状態で、
X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
const struct x11drv_escape_set_drawable *data = in_data;
physDev->dc_rect = data->dc_rect;
となる。physDev->dc_rect が、>>420 の dc_rect に他ならない。
つまり、HWND hwndのwin_rectの、HWND topの左上座標からの相対座標が、
physDev->dc_rect に入ることになると思われる。hwndの「最上位の親」が
topだとすると、topだけが XCreateWindow()された本物のWindowであって、
hwnd は 擬似Windowであるとして辻褄が合う。hwndへの描画は、
実は座標だけを修正して top の(本物の X )Window に書き込まれているだけ
かも知れない。 結論的には、Wine では、完全透明色が設定されている全ての LAYERED_WINDOW に
対して、Idle状態の時か、または、50(ms) 毎に、Windowアプリのメッセージループ
の中から自動的に、>>418 の update_surface_region() が呼び出されるようになっ
ているらしいです。この条件のWindowがあって、かつ、update_surface_region() の
処理が重い場合に、動作が遅くなる可能性が高いです。Dirty Bitのようなものは、
今のところ見つかっていませんので、何もしなくても常に重くなるのでしょうか。
【詳細】
flush_window_surfaces() なる関数が、定期的に呼び出される。
典型的なタイミングは、PeekMessage() の中からであり、GetMessage()では、
check_for_driver_events() を介して呼び出される。
flush_window_surfaces() の中に次のようなマクロ呼び出しがある :
LIST_FOR_EACH_ENTRY( surface, &window_surfaces, struct window_surface, entry )
surface->funcs->flush( surface );
ここで、
/* iterate through the list using a list entry */
#define LIST_FOR_EACH_ENTRY(elem, list, type, field) \
for ((elem) = LIST_ENTRY((list)->next, type, field); \
&(elem)->field != (list); \
(elem) = LIST_ENTRY((elem)->field.next, type, field))
であり、上記は、window_surfaces リストに登録されている全ての
window_surface *surface について、window_surface_funcs *funcs の
関数ポインタ flush の関数を呼び出すことになり、結局、
関数 x11drv_surface_flush() が呼び出されることになる。
関数 x11drv_surface_flush() の中に
if (surface->is_argb || surface->color_key != CLR_INVALID) update_surface_region( surface );
とある。update_surface_region() は、>>418 に書かれている関数。 該当の条件の時、新たには何の描画もしていない静止状態でも、Linuxのシステム・モニターで
CPUパワーが膨大に消費されていることを確認しました。このことは、>>422 を裏付ける物です。
だとすると、Dirty Bit を導入して、update_surface_region()の頻度を下げれば、この低速化
は修正できる可能性が出てきました。LineTo, TextOut, MoveWindow, SetWindowPos,
ShowWindow などを使ったときだけ、DirtyBit を 1にして、1の時だけ update_surface_region()
を呼び出し、呼び出した後には 0 にすれば良いのではないかということです。
条件は:
1. WINEを使用して Windowsアプリを走らせていること。
2. アプリ内で CreateWindowEx() のdwExStyle に WS_EX_LAYERED を指定して Windowを作成
済みであること。
3. さらに、SetLayeredWindowAttribute() に LWA_COLORKEY を指定して「完全透明色」を
指定していること。
4. その Window の背景を「完全透明色」で消去していること。
5. その Window 内部に通常色で、LineTo() や TextOut() によって図形や文字を
描いた後であること。
です。
MDIを使っている事や、CMDIChildWnd 動かすかどうかは関係ない様です。
CMainFrame をドラッグした時には遅く感じないのは、その動作についてはシステムが CPU
の優先順位を上げているからではないかと思います。 >>418 さんの指摘は凄く役立ちました。有難うございます。
ただし、こっちの調査不足のせいが大きいのでしょうが、以下の部分は、今回の
結果とは違っていたようです:
>415の2.,3.の条件だと遅くなるとすると、ピクセル値を比較する処理は1.と同じなので、
>矩形が大量になったときに、X側で描画性能が低下するのではないかと思うぞ。
「X側の描画性能の低下」が原因ではなく、「ピクセル値を比較する処理」自体が、
実は「静止状態」でも、常時、大幅に増加していた、ということです。
何の変化がないときにでも、百万ピクセルを比較し、ランレングスを導き出す処理を、
原則的には秒間20回もやっています。
また、図形が複雑だと、システムコールを呼び出す回数がランレングスの変化の回数倍
されます。例えば、直線をN本引いた場合、システムコールの回数が原則 3N (倍)になります。
縦方向が500ドットのWindowで、1本の直線を引いた場合、一回の処理で、最低でも
6,000回のシステムコールが呼び出されます。これが秒間20回も行われますので、
最低で、秒間12万回のシステムコールとなります。1回のシステムコールは、5(μs)
くらいはどうしてもかかるので、これだけで、0.6(秒)もかかってしまいます。
つまり、1秒間に0.6秒も無駄なシステムコールに時間を取られていると見積もれます。
直線を2本にすると、これだけでCPUがフルパワー状態になります。 【厳密化】
1. 多分、6,000回ではなく、4,500回程度でした。
2. 比較処理自体は、図形の複雑さとは無関係にほぼ一定の重さです。
3. 図形が複雑な場合、システムコールの回数が増えます。
4. 横方向の1つのランレングスで、3つのシステムコールが呼ばれます。
5. 図形が何も描かれていない場合、1つのy座標に対して、1つのランレングスです。
6. N 本の直線の場合、大体で言えば、3N 個のランレングスになります。
7. だから、1つのy座標に対し、9N 個のシステムコールが呼ばれることになります。
8. 縦 500 ドットの場合、500*9N = 4500 N 回のシステムコールとなります。
9. よって、中に描かれている直線の本数が増えると、大体 O(N) で処理時間が
増えます。
10. 文字を描いた場合、大体、文字の複雑さに比例して処理時間が増えます。 >>425
【訂正】
「6.」のランレングスの個数は、3N個ではなく、2N+1 個でした。
お騒がせしました。 >>425
update_surface_regionの方が効率悪そうなので、X側の描画性能が原因と推測した部分は撤回する。
Shape Extensionの仕様を見ると、
矩形領域ではなくマスク用のPixmapを指定する方法も採れるから、
BitBltで効率よくマスクを作ればそっちの方が早くなるかもと思ってみたり。
あと、MDIウィンドウがXのWindowではないことは、xwininfoコマンドの結果でも確認できる。 >>427
>矩形領域ではなくマスク用のPixmapを指定する方法も採れるから、
>BitBltで効率よくマスクを作ればそっちの方が早くなるかもと思ってみたり。
少なくともその方法だと、図形の複雑さによらずに安定して同じ処理時間で
済みますね。それと当然、BitBlt はハードウェアの補助が得られます。
あと、システムコールも全体で数回しか呼ばなて済みますし。 特定のアプリの場合にだけは、自作の user32.dll.so を読み込ませる事が出来ない。
WINEDLLPATH, WINEPATH を試してみたが、今のところ上手く行かない。 調べてみると、/wine/libs/wine/loader.c の中の build_dll_path()
関数の中で、環境変数 WINEDLLPATH が、読み取られ、: を 0x00に
変えてから、dll_paths[] 配列に : で区切られたそれぞれの文字列
の先頭アドレスを代入している。
しかし、その処理に入る前に、get_dlldir() 関数が NULL 以外を返した場合、
dll_paths[0] にその関数が返した文字列のアドレスを入れてしまう。
そして、実際の get_dlldir() は、"/usr/local/bin/../lib/wine"
という文字列を返してくるので、そっちのパスの方が、WINEDLLPATH
の優先順位よりも上になってしまう。そして、
/* retrieve the default dll dir */
const char *get_dlldir( const char **default_dlldir )
{
*default_dlldir = DLLDIR;
return dlldir;
}
となっているが、DLLDIR が全ての拡張子の全ファイルを全文検索しても
どこにも定義されておらず、参照されているのも唯一この関数だけだった。
これだと、WINEDLLPATH で「Override」できないのも当然だけど、
そもそも、DLLDIR がどこで定義されているのかが分からないので
誰かの教えをいただきたいです。 見つかりました。wine/libs/wine/Makefileの中で、
config_EXTRADEFS = \
-DBINDIR='"${bindir}"' \
-DDLLDIR='"${dlldir}"' \
-DLIB_TO_BINDIR=\"`$(MAKEDEP) -R ${libdir} ${bindir}`\" \
-DLIB_TO_DLLDIR=\"`$(MAKEDEP) -R ${libdir} ${dlldir}`\" \
-DBIN_TO_DLLDIR=\"`$(MAKEDEP) -R ${bindir} ${dlldir}`\" \
-DBIN_TO_DATADIR=\"`$(MAKEDEP) -R ${bindir} ${datadir}/wine`\"
というのが。単語検索していたのが鬼門でした。 loader.c の build_dll_path() の修正と、user32.dll.so の修正を、
自分のアプリだけに適用する事に成功しました。 surface が NULL ではない場合、xxx->pLineTo() は、X11DRV_LineTo()
ではなく、dibdrv_LineTo() が呼び出されるような・・・。 DirtyBit を使って、例の update_surface_region() は、
描画してないときには全く呼ばれないようにできたんですが、
まだかなり遅いです。何もしてないときにも CPU パワーがかなり消費されています。
その間、update_surface_region() が呼び出されていないことは、FIXME で
表示して確認が取れています。
なお、>>433は、windrv_LineTo() の方の間違いでした。windrv_LineTo() の場合、
簡単に surface にアクセスできるので、surface に bNeedToUpdate のような
フラグを追加して、それを 1 にして dirty bit にしています。
なぜ遅いままなのかは謎です。 2ch(5ch?)は人は多いけど基本的にユーザーレベルの人ばかりだから技術的な書き込みしても反応は鈍いね
Qiitaにでも書いてみたらどう? 遅い原因の1つは、Onldle の中にあることが分かりました。
1. 必要があって、外部ツールによるファイルの更新チェックをしていたところ、
それがとんでもなく重い事が判明。
2. SetTimer で CMainFrame にタイマーをしかけていると、CMainFrame::OnCmdMsg()
が、WM_TIMER メッセージが来る度に、それを何倍にも掛け算した回数だけ呼び
出される事が判明。例えば、秒間 N 回のタイマーを仕掛けると、OnCmdMsg が、
10*N 回 呼び出されるような感じです。タイマーメッセージがくる度に、
メニューなどを更新するためのメッセージが来ているような気がします。
多分、本家 Windows では、タイマーメッセージが来ても、そのような事には
ならないんじゃないかと思います。 通常、OnCmdMsg は頻度が低いことが
前提になっているので、その中で多少重い処理をしていると、とんでもなく
重たくなる。
なお、透明色を使っている場合、update_surface_region の中の処理の軽重に
よって、描画速度が大幅に変わることも確認しました。
ランレングスを数えてリージョンを add_row している箇所を、単純に何も
数えずに行全体で、1個だけ add_row するように修正してみると、描画が
だいぶ速くなりました(透明にはなりませんが。)。 >>436
そうしたいんですが、何かと大変なんです。
1. メールアドレスの登録するのが、Linuxからだと大変。
2. git の仕様が良く分からない。
3. 回線が遅いので、Project を Fork して、git が Tree 全体を
アップロードしようとしてしまったりなんかすると大変な事になる。
4. だから、一部のファイルだけを UL したいが、コミュニケーションの
問題から事情を伝えて、そこまで行くかどうか分からない。
5. だから、独自にオーバーライドしようとしたのが、先日からここでも
書いていたことです。loader を修正して、かつ、環境変数を変えて
やれば、本家と統合しなくても、部分書き変えのような事が出来る。 >>438
githubからでもプルリクエスト受け付けてるよ 先にhttps://bugs.winehq.org/に書いた方がいい
報告してあるとパッチ送る時説明が楽になるから 描画が早くなるならありがたい
素のWindowsより異様に遅いゲームとかあるし 結論だけ書いておくと、surfaceが非NULLの時の LineTo の実体は、
windrv_LineToで、surface を lock する以外は、dibdrv_LineTo と同じ。
dibdrv_LineToは、bits なるpixel配列に 32BITの色値を書き込む。
実際は汎用性のため、必ずandしてからxorする。SOLIDモードの場合、and値は0。
xor値は、00RRGGBB で、最上位バイトは0。
実際への画面の反映は、定期的に「xxx->flush」なる関数ポインタを介して、
x11drv_surface_flush() を呼び出す事で行う。その中で、議題になっている
update_surface_region() も呼ばれる。最後に bits の内容を実画面に反映させる
ため、XShmPutImage()または、XPutImage(), XFlush()を行う。
surface->image の pixel バッファは、bits[] 配列と共通らしい。
つまり、Linux の描画関数は全く使わずに自前で画像をpixel配列に合成してから、
定期的に、XPutImage() などで実が面に描き込む、というようなことをやっているらしい。 update_surface_region() が遅いんだと思って、
XShape の作成を、XShapeCombineRectangles() を使う方式から、
XCreateBitmapFromData() と XShapeCombineMask()
を使う方式へと変えた。
すると、1146 x 745 のサイズの Window で、
1. 自前のコードによる 1bit の mask bitmap 作成にかかる時間 :
796 (us)
2. XCreateBitmapFromData() と XShapeCombineMask() にかかる時間 :
38 (us)
3. XShmPutImage() と XFlush() ; x11drv_surface_flush()内 の両方にかかる時間 :
94 (us)
4. MDI Child Wnd をドラッグするときに、x11drv_surface_flush() が
呼び出される時間間隔 :
3〜100 (ms) ---> 秒間 10回〜330回
5. 肉眼で描画が更新されるまでに x11drv_surface_flush() が呼び出される回数 :
数10回 以上あることが多い。
1.〜4. だけみると、高速に処理されている様に見えるけど、5. だけが理解できない。
カウンタを設けて数えてみると、実際に目では更新が起きてないのに、
ちゃんと、1〜3 の全ての関数が数十回も呼び出されている。
ちゃんと「XFlush()」されているのに、なぜだろう???? XSync() を追加しても、XShmPutImage() を XPutImage() に修正しても、
何をやっても見た目だけが更新されない。 >>443
> update_surface_region() が遅いんだと思って、
> XShape の作成を、XShapeCombineRectangles() を使う方式から、
> XCreateBitmapFromData() と XShapeCombineMask()
> を使う方式へと変えた。
こうするとなんで速くなるの? >>445
それは、
>>427-428
の部分と深く関係しています。 X の同期の問題ではなく、MDI Child Wnd の TITLE BAR をドラッグするとき、
なぜか、数秒経ってから、x11drv_surface_flush() がまとめて何十回も呼び出されている
可能性があるかも・・・。
デバッグ表示をリアルタイムで見ていると、見た目が変化する直前までデバッグ表示が
全く出ず、見た目が変化する直前に、どどっと、数十回分の x11drv_surface_flush()
がまとめて出てくる、という現象が起きている。 以下のコードが気になる。
message_count を減少させるのは唯一、wait_message() しか見つからなかった。
後は、check_for_driver_events() の中で単調増加してしまう。
メッセージがたまって初めて、flush_window_surfaces() が呼び出されるような
コードにも
/* check for driver events if we detect that the app is not properly consuming messages */
static inline void check_for_driver_events( UINT msg )
{
if (get_user_thread_info()->message_count > 200) {
flush_window_surfaces( FALSE );
USER_Driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_ALLINPUT, 0 );
}
else if (msg == WM_TIMER || msg == WM_SYSTIMER) {
/* driver events should have priority over timers, so make sure we'll check for them soon */
get_user_thread_info()->message_count += 100;
}
else get_user_thread_info()->message_count++;
}
static DWORD wait_message( DWORD count, const HANDLE *handles, DWORD timeout, DWORD mask, DWORD flags )
{
DWORD ret = USER_Driver->pMsgWaitForMultipleObjectsEx( count, handles, timeout, mask, flags );
if (ret == WAIT_TIMEOUT && !count && !timeout) NtYieldExecution();
if ((mask & QS_INPUT) == QS_INPUT) get_user_thread_info()->message_count = 0;
return ret;
} BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT flags )
{
MSG msg;
USER_CheckNotLock();
check_for_driver_events( 0 );
if (!peek_message( &msg, hwnd, first, last, flags, 0 ))
{
DWORD ret;
flush_window_surfaces( TRUE );
ret = wow_handlers.wait_message( 0, NULL, 0, QS_ALLINPUT, 0 );
/* if we received driver events, check again for a pending message */
if (ret == WAIT_TIMEOUT || !peek_message( &msg, hwnd, first, last, flags, 0 )) return FALSE;
}
check_for_driver_events( msg.message );
/* copy back our internal safe copy of message data to msg_out.
* msg_out is a variable from the *program*, so it can't be used
* internally as it can get "corrupted" by our use of SendMessage()
* (back to the program) inside the message handling itself. */
if (!msg_out)
{
SetLastError( ERROR_NOACCESS );
return FALSE;
}
*msg_out = msg;
return TRUE;
} >>448
PeekMessage()を回しているだけの時で、メッセージがキューに残り続けている場合、
メッセージを 200回 (100回?) PeekMessage するまでは、flush_window_surfaces()
されないコードになっている気がしませんか・・・。 GetMessage() の方もほぼ同じような感じかも。。
とにかく、flush_window_surfaces() がとてつもなく長い間、呼ばれないことが
あるコードになっている様に見えて、実際、実験してもそう思える。 そうか、もともとは、dirty bit 方式じゃないから、flush_window_surfaces() を呼ぶと、
再計算する必要が無いのに必ず再計算されてしまうから、呼び出す頻度を下げるしか
高速化する方法が無いために、そんな風なコードになっているのかもしれない。 ■ このスレッドは過去ログ倉庫に格納されています