HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2009
1116
もんたhmm.dllのDirectGraphicについて13解決


もんた

リンク

2009/11/16(Mon) 11:43:27|NO.28889

今回自分の組んでいるプログラムで,http://hsp.tv/make/tool3.htmlページにある
うにたまさんのhmm.dllを使わせていただいて,ゲームつくっています。
(hmm.dllのバージョンは1.42,HSPのバージョンは3.2です。)

今、DG(DirectGraphic)を使って描画しているのですが困っていることがあります。
それはメインループの中でDGDRAWを使う処理がとても重いことです。具体的には
メインループ1回で16ms休む60fpsで描画するとCPU使用率が100パーセントになって
しまうのです。(1ループ16msだと62.5fpsですが、これから便宜的に60fpsとして
話させていただきます)

ちなみにスクリプトの関係のありそうな部分だけ抜き出してみました。


    DGGSEL 0     DGRENEWALTIMING 60 *main     await 0 … gosub *Clear_Screen ;画面クリア gosub *DRAW_GRAPHIC ;裏画面描画       DGREDRAW ;表画面に描画 goto *main

画面に自分の描画したものは表示はされるのですが、CPUが100パーセントになりっぱなしです。
いろいろ自分で試行錯誤してみたりもして、改善する方法はいくつか見つけました。

・DGRENEWALTIMING をつかわないでawait 16にして、約60fpsをしようとする
 →これをやるとたしかにcpuは劇的に改善されて0〜6パーセントほどで動きます。
  しかし、awaitの精度が悪く60fpsになりません。
  なぜかawaitは自分の環境では0だと0(ms),1〜10だと10(ms),11〜20だと20(ms),…
  と段階的に変化していきます。なので16にしても20msになります。
  また,別のPCだとawaitは16ごとに変化したりしてあまり安定しないので使えません。

・SAKMISさんの高精度時間管理モジュールを使う
 →http://whss.biz/~sakmis/module.html#tmanage3にある、SAKMISさんのモジュールに
  asleep p1 (awaitをasleepにしたようなものです。p1に同じくmsの時間を入れる)があり,
  これを使ってみました。16msでCPU使用率は90〜100%,17msで50〜60%,と段階的に下がり
  18ms以上ですともうほぼ負荷なしといった状態です。
  しかし16で100%いくので60fpsになりません。

・asleep でDGDRAWをサンドイッチする
 →今度はいろいろasleepの位置を変化させて見ました。メインの中で最初のasleepは使わず,
  asleep 14:DGREDRAW :asleep 2とはさんでやるとかなり改善され、同じ1ループに16msを
  とるのでもCPU使用率が20〜30%になりました。

このようにして今、サンドイッチする方法をとっているのですが、何かほかに改善する方法が
ありそうでならないです。
なぜならほかのDirectX使用の2Dのソフトでそれほど負荷なく60fpsでるからです。
自分ではこれ以上改善できませんでしたがほかに何かよい方法がありましたら教えてください。
お願いします。



この記事に返信する


KK

リンク

2009/11/17(Tue) 16:45:31|NO.28891

「ほかのDirectX使用の2Dのソフト」がどの言語を使っているのかわかりませんが
コンパイラで機械語にするのと
中間言語などで直接、変換しながら実行するプログラム言語は
速度自体が違います。

await は
待ち時間なのでその間、空ループさせて、その間OSに処理を渡しているので
自分で作った中のプログラムのコードの量と
命令実行速度と計算速度とCPUやメモリ、GPU等により
その速度が変わります。

もし速度を上げたいのなら
計算文にしたほうが速くなります。
この場合は判断分を並べるよりも効率的になる時があります。

それとプログラム・コードは処理ごとに小さくまとめたものをまとめてわかりやすくしておき
後で変えやすくすればいいでしょう。

簡素なほうが速くなります。

わからなければ、絵でも音でも字でも何でもよいですから紙などに書いてみてください。

コードはちょっとしたドキュメント等の説明でわかりやすくしてもよいです。

他の方のソースコードを実行して速度と中を確かめてみてもいいでしょう。

ネットならいろいろ手に入りますよ。

手元におけるCD付きでURLからダウンロード可能な書籍などもあります。

hmm.dllは環境により不安定な時があるので
他のものか最新のものなどにあるものを使ってみてはどうでしょうか?

ちなみにこのようなプログラミング言語は変数ひと文字で処理が変わることがあります。
HSPも私が使って見たかぎりはその傾向がありました。
普通に使っているかぎりは大丈夫です。
多分、サイト(hsp.tv等)にいくつかエラーが書いてありますよ。

はじめてのものは慣れなく手探りなのでどちらも否定はよくありません。

わからなければ他の方への助言などを見てはどうでしょうか?



vex

リンク

2009/11/18(Wed) 09:56:34|NO.28894

kkさんは口が達者な方ですが、ブログさながらうさんくさい方です。

>await は
>待ち時間なのでその間、空ループさせて、その間OSに処理を渡しているので
空ループなどさせていません

>もし速度を上げたいのなら
>計算文にしたほうが速くなります。
>この場合は判断分を並べるよりも効率的になる時があります。
答えが的を得ていない。

>それとプログラム・コードは処理ごとに小さくまとめたものをまとめてわかりやすくしておき
>後で変えやすくすればいいでしょう。
答えが的を得ていない。

>わからなければ、絵でも音でも字でも何でもよいですから紙などに書いてみてください。
>コードはちょっとしたドキュメント等の説明でわかりやすくしてもよいです。
>他の方のソースコードを実行して速度と中を確かめてみてもいいでしょう。
>ネットならいろいろ手に入りますよ。
>手元におけるCD付きでURLからダウンロード可能な書籍などもあります。
答えが的を得ていない。

>hmm.dllは環境により不安定な時があるので
>他のものか最新のものなどにあるものを使ってみてはどうでしょうか?
人づてに聞いた話は説得力に欠ける。

>ちなみにこのようなプログラミング言語は変数ひと文字で処理が変わることがあります。
>HSPも私が使って見たかぎりはその傾向がありました。
>普通に使っているかぎりは大丈夫です。

変数ひと文字で処理が変わる例を挙げてください。そのほうが説得力があります。

>多分、サイト(hsp.tv等)にいくつかエラーが書いてありますよ。

何でもネットを頼りにしていて、本当にプログラミング経験があるんでしょうか?
おそらくオンラインプログラマなんでしょうね。

>わからなければ他の方への助言などを見てはどうでしょうか?
答えが的を得ていない。
わからないからこうして他の方に助言を求めに来ているのでは?


kkさんは使い慣れない言語を使用しているため、言葉の端々が不自然だと思いました。



もんた

リンク

2009/11/18(Wed) 19:24:07|NO.28897

KKさん,vexさんお返事ありがとうございます。

KKさんは時間のとり方を変えるのではなく,ソース自体見直したほうがよいということなのですね。
おっしゃることを実行するのはいろいろ難しかったので、ためしに最低限の描画処理だけのサンプル
を作ってみましたがやはり重かったです。逆に、元のソースでDGRDRAWの前に;を入れてこの処理だけ
しないようにすれば極端に処理が軽くなりCPUも0といった感じでした。
なので,ほぼDGREDRAW以外の処理には処理負荷をとられていないといった感じです。
ですので、
>hmm.dllは環境により不安定な時があるので
というのが原因だったのでしょうか。どういった環境の方が不安定な状況になったのか知って
いらしたらぜひとも教えていただきたいです。

vexさん、そうですね。過去記事なども検索してみて似たものがなかったため助言を求めてきてみたので、
おっしゃるとおりです。誰か詳しい方にヒントだけでもいただけたらいいなあと思ってます。

まだ解決はできていないのでぜひとも、まだまだよろしくお願いします。



うわらば

リンク

2009/11/19(Thu) 00:27:24|NO.28900

基本的に一番負荷がかかるのは描写の部分ですから、
DGREDRAWを外せば使用率等は当然下がります。

そもそもDirectX自体にFPSを固定してくれる機能があるので、
DGRENEWALTIMING 60を呼び、ループにawait 1を入れれば大方事足りる筈です。
一応テストコード。

#include "hmm.as" hmminit 0x11000 dgscreen 640, 480 dgrenewaltiming 60 *main dgredraw await 1 hmmgetfps fps : title strf("fps: %d", fps) getkey esc, 27 : if esc : end goto *main
これでダメなら他の手を考えます・・・。

>vexさん
指摘をする時は、具体的な根拠を示さないと(もしくは示せないと)
貴方の回答こそ「的を得ていない」物だと思わざるを得ません。



もんた

リンク

2009/11/19(Thu) 17:40:21|NO.28901

うわらばさん,お返事ありがとうございます。
テストコードをためしたところCPU使用率0%付近で100fpsもでました。
嬉しくなり、そのまま設定を変えずに試しに何か描画したのですが,今度は何も
映らない(描画されない)といった状況になってしまいました。

そこで,よくよくHMMのマニュアルをみたらhmminitは第5ビットを立てた場合DGが初期化になると
書いてあったので0x11000だとDGが初期化されていないみたいで、16進数ではなく2進数のまま
hmminit 10000と直したところDGが初期化されて(結局はDGINITと一緒ですが)映りました。

しかし,dgrenewaltiming 60とawait1の組み合わせの作っていただいたこのコードですと,
確かに安定した60fpsになるのですが、またCPU使用率がまた100%になってしまいました。
ですので、また改善にはいたらない状況でした。

しかし,今さらにいろいろいじっていてdgrenewaltiming のパラメータを120にしてawait 1だと
描画も問題なく、100fpsもでてCPU使用率が0%台という結果もでました。
100fpsではさすがに速すぎてゲームにならないので使えませんが、このくらいでもほぼ負荷なし
といった状況が出ることがわかったので何かほかに手を打てそうな気がしてきました。
(自分はは100fpsだすほうが60fpsより高負荷の処理だと思っているので…間違ってたらすいません)

>これでダメなら他の手を考えます・・・。
非常にありがたい言葉です。自分でもいろいろやってみますがよろしければまた力を貸して
いただけたらと思います。考えができましたらお返事よろしくお願いします。



うわらば

リンク

2009/11/19(Thu) 22:46:23|NO.28902

凄い恥ずかしい間違いを・・・。申し訳無いです。
もう一回テストコード。
#include "hmm.as"
hmminit 11000 dgscreen 640, 480 dgrenewaltiming 60 *main dgcolor : dgclear : dgredraw wait 1 hmmgetfps fps : title strf("fps: %d", fps) getkey esc, 27 : if esc : end goto *main
awaitではなくwaitにしたら良さそうです。
調べてみるとawaitはループのタイミングを大体一緒にする代わりに
パワーを食うらしい、です。
今一説得力に欠ける。



名無し

リンク

2009/11/19(Thu) 23:00:35|NO.28903

自分はゲーム制作とかには詳しくないのだけど、とりあえず気づいたことをいいます。
dgredrawは指定された描画タイミングが来るまでの待ち時間を単純にループして待ってる
だけのようなのでその間もCPUを使用し続けます。
なのでdgredrawでfpsを調整する限りcpu使用率が100%になるのは仕様と考えて良いと思います。
asleepでfps調整を行うのなら、dgrenewaltimingに1000ぐらいのとんでもない値を入れておけば、
dgredrawでの待機時間は発生しなくなるだろうからcpu使用率も下がるんじゃないかと思う。
awaitの精度は10ms単位なのでawaitであまり細かい制御は無理です。


>しかし,今さらにいろいろいじっていてdgrenewaltiming のパラメータを120にしてawait 1だと
>描画も問題なく、100fpsもでてCPU使用率が0%台という結果もでました
100fps以上に設定するとdgredrawの待機時間は10ms以下になります。
awaitの精度は10msなのでawait 1としても実際には10ms待機するので、結果としてawaitでの待機時間が発生して
CPU使用率が下がるからです。



足利超神

リンク

2009/11/19(Thu) 23:19:28|NO.28904

awaitの精度は1msではありませんか?



名無し

リンク

2009/11/20(Fri) 21:02:34|NO.28915

>awaitの精度は1msではありませんか?
1ms単位で設定できるけど実際には10ms単位でしか変化しないです。
これ実行したら分かると思うけどawaitの値を上げていっても1〜10,11〜20は同じ結果(fps)
になる。

話変わるけどNo.28903で言っていたのは、このサンプルでawaitの代わりにもっと
細かい単位で制御できるasleepとか使えば低負荷で細かいFPS制御ができるってことです。


#include "hmm.as" #include "winmm.as" #include "comctl32.as" t=1 input t,50,25 ic=8,16 InitCommonControlsEx varptr(ic) CreateUpDownControl 0x50800086,0,0,0,0,hwnd,0x10000,hinstance,objinfo_hwnd(0),1000,0,1 hmminit %10000 dgscreen 640, 480 dgrenewaltiming 1000 *main dgcolor : dgclear : dgredraw await t hmmgetfps fps : title strf("fps: %d", fps) getkey esc, 27 : if esc : end goto *main



KK

リンク

2009/11/21(Sat) 03:08:30|NO.28919

長文はいけないでしょうが入れておきます。
少し長いですがこれは私のノートパソコンで動いたものです。
この文章の最後まで見て下さい。


/* DirectGraphicsのサンプルです。 マウスカーソルをの位置で画像を表示します。 ESCで終了します。 SHIFTで画面モードを切り替えます。 SPACEで残像っぽいエフェクトサンプル */ ;---------------------- ; ;---------------------- #include "hmm.as" #include "winmm.as" #include "comctl32.as" #include "hspext.as" ;---------------------- ; ;---------------------- #define FULL_SCREEN_MODE 0 #define WINDOW_MODE 1 #define STAR_MAX 256 #define PARAM_MAX 10 #define pos_x 0 #define pos_y 1 #define speed_x 2 #define speed_y 3 #define scale 4 #define angle 5 #define color_r 6 #define color_g 7 #define color_b 8 #define color_a 9 ;---------------------- ; ;---------------------- *init title "hmm.dll sample [esc=end][shift=chngscrmode][space=effect]" randomize diInit ScreenMode = 1: gosub *initdg dim SinTbl, 256: dim CosTbl, 256 repeat 256 emsin SinTbl.cnt, cnt: emcos CosTbl.cnt, cnt loop dim Star, STAR_MAX, PARAM_MAX repeat STAR_MAX StarNumber = cnt: gosub *setparam loop ;------- t=1 input t,50,25 ic=8,16 InitCommonControlsEx varptr(ic) CreateUpDownControl 0x50800086,0,0,0,0,hwnd,0x10000,hinstance,objinfo_hwnd(0),1000,0,1 hmminit %10000 *main dgcolor : dgclear; : dgredraw await t hmmgetfps fps : title strf("fps: %d", fps) gosub *keycheck gosub *draw gosub *moveparam dgReDraw ;画面更新 if stat == 0 { // ALT+TAB、スクリーンセーバー、省電力モードなどで // 画面更新が失敗したら初期化して画像などを読み込みし直す // この時にはウィンドウモードにすること ScreenMode = WINDOW_MODE gosub *initdg } getkey esc, 27 : if esc : end goto *main ;---------------------- ; ;---------------------- *setparam Star.StarNumber.pos_x = mousex<<8: Star.StarNumber.pos_y = mousey<<8 r = rnd( 256 ) Star.StarNumber.speed_x = CosTbl.r Star.StarNumber.speed_y = SinTbl.r Star.StarNumber.scale = rnd( 392 ) Star.StarNumber.angle = rnd( 257 ) Star.StarNumber.color_r = rnd( 128 ): Star.StarNumber.color_r += 128 Star.StarNumber.color_g = rnd( 128 ): Star.StarNumber.color_g += 128 Star.StarNumber.color_b = rnd( 128 ): Star.StarNumber.color_b += 128 Star.StarNumber.color_a = rnd( 128 ): Star.StarNumber.color_a += 128 return ;---------------------- ; ;---------------------- *moveparam repeat STAR_MAX StarNumber = cnt ;パラメーターの変更 Star.StarNumber.pos_x += Star.StarNumber.speed_x: Star.StarNumber.pos_y += Star.StarNumber.speed_y Star.StarNumber.color_a --: Star.StarNumber.angle += 4 ;パラメーターの初期化判定 x = Star.StarNumber.pos_x>>8: y = Star.StarNumber.pos_y>>8: a = Star.StarNumber.color_a if ( x < 0 ) || ( x > 640 ) || ( y < 0 ) || ( y > 480 ) || ( a <= 0 ): gosub *setparam loop return ;---------------------- ; ;---------------------- *draw dgGsel 0 dgColor 0, 0, 0, 255: dgClear ;画面のクリア dgGsel 2: dgColor 0, 0, 0, 255: dgClear /* ;星の描画( ver DGGCOPY ) dgBlendMode 2: dgRect 0, 0, 96, 32 repeat STAR_MAX StarNumber = cnt dgScaleAndAngle Star.StarNumber.scale, Star.StarNumber.scale, Star.StarNumber.angle dgColor Star.StarNumber.color_r, Star.StarNumber.color_g, Star.StarNumber.color_b, Star.StarNumber.color_a dgPos Star.StarNumber.pos_x>>8, Star.StarNumber.pos_y>>8: dgGcopy 1,5 loop */ ;星の描画( ver DGDRAWPRIMITIVE ) dgTexture 1: dgBlendMode 2: dgRect 0, 0, 96, 32 repeat STAR_MAX StarNumber = cnt dgScaleAndAngle Star.StarNumber.scale, Star.StarNumber.scale, Star.StarNumber.angle dgColor Star.StarNumber.color_r, Star.StarNumber.color_g, Star.StarNumber.color_b, Star.StarNumber.color_a dgPos Star.StarNumber.pos_x>>8, Star.StarNumber.pos_y>>8: dgAddPrimitive 5 loop: dgDrawPrimitive dgBlendMode 2 repeat 128 ca = rnd( 256 ): cb = rnd( 256 ): cg = rnd( 256 ) ex = rnd( 320 ): ey = rnd( 240 ) dgColor ca, cb, cg, 255: dgLine 0, 0, ex, ey loop if AfterEffect { dgGsel 3 dgBlendMode 1: dgColor 255, 255, 255, 16 dgRect 0, 0, 640, 480: dgScaleAndAngle 256, 256, 0 dgPos 0, 0: dgGcopy 2 cpybufid = 3 } else { cpybufid = 2 } dgGsel 0 dgBlendMode 0: dgColor 255, 255, 255, 255 dgRect 0, 0, 640, 480: dgScaleAndAngle 256, 256, 0 dgPos 0, 0: dgGcopy cpybufid dgPos 640-96, 480-16: dgColor 255, 255, 255, 255: dgDrawText "fps="+fps, 96, 16 return ;---------------------- ; ;---------------------- *keycheck diGetKeyState key, 0 diGetMomentKeyState key, 0 //押した瞬間のキーを取得 //サンプルの終了と画面のキャプチャ hmmBitCheck key, KEY_ESC_BIT if stat == 1 { dgBmpSave "bmpsave.bmp" end //ESCが押されたら終了 } //画面モードチェンジ hmmBitCheck key, KEY_SHIFT_BIT if stat == 1 { if ScreenMode { ScreenMode = FULL_SCREEN_MODE } else { ScreenMode = WINDOW_MODE } gosub *initdg } hmmBitCheck key, Key_SPACE_BIT if stat == 1 : AfterEffect = AfterEffect^1 return ;---------------------- ; ;---------------------- *initdg dgInit: if stat == DG_ERR: dialog "init err": end dgScreen 640, 480, ScreenMode, 32, 0: if stat == DG_ERR: dialog "screen err": end dgRenewalTiming 60 //PCの性能にまかせて描画 //dgScreen 640, 480, ScreenMode, 32, 1: if stat == DG_ERR: dialog "screen err": end //dgRenewalTiming 60 dgBuffer 1, 64, 64: if stat == DG_ERR: dialog "buffer err": end dgFont "MS ゴシック", 32: if stat == DG_ERR: end dgGsel 1: dgScaleAndAngle 256, 256, 0: dgClear dgColor 255, 255, 255, 255: dgPos 0, 0 dgDrawText "HSP", 96, 32: if stat == DG_ERR: end dgCreatePrimitive STAR_MAX dgFont "MS ゴシック", 16: if stat == DG_ERR: end dgBuffer 2, 640, 480: dgBuffer 3, 640, 480 return

この部分が大切です。


#include "hmm.as" #include "winmm.as" #include "comctl32.as" t=1 input t,50,25 ic=8,16 InitCommonControlsEx varptr(ic) CreateUpDownControl 0x50800086,0,0,0,0,hwnd,0x10000,hinstance,objinfo_hwnd(0),1000,0,1 hmminit %10000 *main dgcolor : dgclear await t hmmgetfps fps : title strf("fps: %d", fps) gosub *keycheck gosub *draw gosub *moveparam dgReDraw ;画面更新 if stat == 0 { // ALT+TAB、スクリーンセーバー、省電力モードなどで // 画面更新が失敗したら初期化して画像などを読み込みし直す // この時にはウィンドウモードにすること ScreenMode = WINDOW_MODE gosub *initdg } getkey esc, 27 : if esc : end goto *main

ボックスの数値を変えて見て下さい。
fpsが変わると思います。(私のでは0と1で違いました)
「dgReDraw」
これの更新タイミングの位置のずれだけでずれて表示不可能になります。
これはサンプルを見て実行して比べると分かります。
この他にも理由があります。
サンプルでも示されています。
このようにひとつひとつ研鑽もあります。

スクリプトは貼り付けて即に実行・変換できて便利です。

本当の長文になってしまい、すみませんでした。



もんた

リンク

2009/11/21(Sat) 20:58:51|NO.28932

こんばんは。
たくさんお返事いただいていてうれしいです。ありがとうございます。

うわらばさん、そのコードを試したところ60fpsの安定でかなり軽減されました!
自分の最初の段階より処理をいろいろふやしてもだいたい20〜30パーセントの負荷で動かすことができました。
助かりました、本当にありがとうございます。今のところこの方針で生きたいと思います。

名無しさん、理論的なことを教えていただいてありがとうございます。
仕組みからいって,ループの中でDGDRAWに待機させる処理時間を減らすようなコードが
よいのだということがわかりました。これだとwaitさせるのがよいことがわかります。また、
逆にwaitを増やしすぎると今度は60fpsさせるような待機時間が取れなくなるのだということも
わかりました。

足利超神さん、そうですね名無しさんが答えていらっしゃるようになぜか精度がよくないみたいです。
実験してみても10ms単位での精度しか出ないみたいでした。
細かいものはSAKMISさんの高精度モジュールを使ったほうがいいみたいです。

そして、KKさんコードありがとうございます。知らないことが多くてまだ完全に読めていません。
申し訳ないです。ですので今読んでる最中です。まずは2日もあけてしまったので先に
お返事を投稿させていただきました。
ためしにコードのみ実行したところ値を増やせば処理が軽くなる代わりにfpsが落ちていきますね。
何か秘めていそうなので詳しく読ませていただいてお返事させていただきます。
本当にありがとうございます。



名無し

リンク

2009/11/22(Sun) 00:28:31|NO.28936

まあ本人が納得したのなら良いんだが、分かってるのかもしてないけど補足しておきます。
>これだとwaitさせるのがよいことがわかります
waitはawaitと違って単純に指定された時間を待つだけの命令です。
60fpsを目標にしているのならループ一回に掛けられる時間は16msです。
waitで10ms使ってしまったら実際の処理に使える時間は6msしかなくなってしまう。
今の環境で問題なく動いていてもCPU性能の劣るPCで実行したとき、もし処理に8msかかるなら
本来なら余裕で60fpsをだせるのに、実際には処理落ちするという現象が出ます。



もんた

リンク

2009/11/29(Sun) 20:40:18|NO.29069

どうもお久しぶりです。
KKさん、よくよく読ませていただきました。ありがとうございます。
KKさんのコードは処理が早くなりそうな記述があり、節々でいろいろと得るものがありました。
読むのに時間がかかりましたが、これからも役に立つ部分があり吸収させていただきました。
本当にありがとうございました。

名無しさん、わざわざ補足までしていただきありがとうございます。
そうですね、その部分も今回の問題で実感できてた部分でした。
名無しさんの指摘どおりそのようなこともありえるので十分注意していきたいです。

今回はこれで解決できたのでひとまずチェックさせていただきます。

みなさん本当にありがとうございました。



ONION software Copyright 1997-2023(c) All rights reserved.