HOpenGLライブラリを使ってHaskellに書きたいかなり複雑なビデオゲームのための概念実証作業をしています。私はクライアント・サーバー・イベント・ベースの通信を実装するモジュールを作成することから始めました。私の問題は、画面にクリックを描画する簡単なプログラムに接続しようとすると表示されます。Haskellの他のスレッドやTChanに関してHOpenGLはどのように動作しますか?
イベントライブラリは、通信の優先度キューに入れられたTChansのリストを使用します。サーバーにバインドされたメッセージとクライアントにバインドされたメッセージに対応する「out」キューと「in」キューを返します。イベントの送受信は、forkIOを使用して別々のスレッドで行われます。 OpenGLパートなしでイベントライブラリをテストすると、正常に通信していることがわかります。ここで私はそれをテストするために使用されるコードだ:
-- Client connects to server at localhost with 3 priorities in the priority queue
do { (outQueue, inQueue) <- client Nothing 3
-- send 'Click' events until terminated, the server responds with the coords negated
; mapM_ (\x -> atomically $ writeThing outQueue (lookupPriority x) x)
(repeat (Click (fromIntegral 2) (fromIntegral 4)))
}
これは、予想される出力、センドのすなわち全体の多くを生成し、イベントを受け取ります。私は問題がイベント処理ライブラリにあるとは思わない。
コードのOpenGL部分は、着信キューでdisplayCallbackの新しいイベントをチェックし、イベントに関連付けられたハンドラを呼び出します。私は、displayCallbackによってキャッチされる1つのイベント(画面を単にクリアするInitイベント)を得ることができますが、その後は何もキャッチされません。ここでは、関連するコードがあります:
atomically $ PQ.writeThing inqueue (Events.lookupPriority Events.Init) Events.Init
GLUT.mainLoop
render pqueue =
do event <- atomically $
do e <- PQ.getThing pqueue
case e of
Nothing -> retry
Just event -> return event
putStrLn $ "Got event"
(Events.lookupHandler event Events.Client) event
GL.flush
GLUT.swapBuffers
だから私の理論をこれが起こっている理由としては、以下のとおりです。
- 表示コールバックが再試行に送信側と受信側のすべてのスレッドをブロックしています。
- キューが正しく返されないため、クライアントが読み取るキューとOpenGLの一部が読み取るキューが異なります。
これが起こりうる他の理由はありますか?
長すぎるわけではありませんが、完全なコードはここに投稿するには長すぎます(それぞれ100行未満の5つのファイル)が、すべてGitHubにあります。
編集1:クライアントはそうようHOpenGLコードでメイン関数内から実行される
:
main =
do args <- getArgs
let ip = args !! 0
let priorities = args !! 1
(progname, _) <- GLUT.getArgsAndInitialize
-- Run the client here and bind the queues to use for communication
(outqueue, inqueue) <- Client.client (Just ip) priorities
GLUT.createWindow "Hello World"
GLUT.initialDisplayMode $= [GLUT.DoubleBuffered, GLUT.RGBAMode]
GLUT.keyboardMouseCallback $= Just (keyboardMouse outqueue)
GLUT.displayCallback $= render inqueue
PQ.writeThing inqueue (Events.lookupPriority Events.Init) Events.Init
GLUT.mainLoop
Iコードが-package GLUT
でコンパイルするとき、私はGHCに渡すだけのフラグ。
編集2:
Githubでコードを少しきれいにしました。私はacceptInputを削除しました。実際に何もしていなかったので、クライアントコードはそれ自身のイベントをリッスンすることになっていないため、キューを返すのです。
編集3:
私は少し質問を明確にしています。私は@ Shangと@Laarから学んだことを取って、それと一緒に走った。私は、ClientHsのスレッドをforkIOの代わりにforkOSを使用するように変更しました(ghcで使用されました)。イベントは正常に通信されているように見えますが、表示コールバックでは受信されません。私はまた、表示コールバックの最後にpostRedisplay
を呼び出すことを試みましたが、私は再試行がOpenGLスレッド全体をブロックしていると考えているので、呼び出されるとは思いません。
ディスプレイコールバックの再試行でOpenGLスレッド全体がブロックされますか?表示されている場合、表示コールバックを新しいスレッドにフォークするのは安全でしょうか?同時に複数のものが画面に描画しようとしている可能性があるので、私は想像しませんが、ロックでそれを処理できるかもしれません。別の解決方法は、lookupHandler
関数をMaybe
にラップされた関数を返すように変換することです。イベントがない場合は何もしないでください。私は基本的には、私が避けようとしていたものだった忙しいループを持っていたので、それが理想的ではないように感じる。
編集4:
は、私がforkOSをしたとき、私はGHCで-threaded使用に言及し忘れました。
編集5:
私は私の理論のテストを行っていたレンダリング機能(表示コールバック)での再試行は、OpenGLのすべてをブロックしていること。レンダリング機能を書き直してもうブロックしないようにして、動作させたいと思ったように機能しました。画面内を1回クリックすると、サーバーと元のクリックから2つのポイントが得られます。ここでは、新たなレンダリング機能のコードは(:それはGitHubの中ではありません注意してください)。だ
render pqueue =
do event <- atomically $ PQ.getThing pqueue
case (Events.lookupHandler event Events.Client) of
Nothing -> return()
Just handler ->
do let e = case event of {Just e' -> e'}
handler e
return()
GL.flush
GLUT.swapBuffers
GLUT.postRedisplay Nothing
私はpostRedisplayととせずに、それを試みたが、それはそれだけで動作します。この問題は、これがビジーなループであるため、CPUが100%でペグするようになります。編集4では、ディスプレイコールバックをスレッドオフにすることを提案しました。私はまだそれを行う方法を考えています。
私はまだ言及していないので注意してください。
$ ghc -threaded -package GLUT helloworldOGL.hs -o helloworldOGL
$ ghc server.hs -o server
-- one or the other, I usually do 0.0.0.0
$ ./server "localhost" 3
$ ./server "0.0.0.0" 3
$ ./helloworldOGL "localhost" 3
編集:6:ソリューション
ソリューションのコードを実行する/構築するために探している誰もがこのようにそれを行う必要があります!スレッドに沿って、イベントをチェックし、何もない場合はブロックし、その後にハンドラを呼び出してpostRedisplayを呼び出すスレッドをOpenGLコードで作成することにしました。ここでは、次のとおりです。
checkEvents pqueue = forever $
do event <- atomically $
do e <- PQ.getThing pqueue
case e of
Nothing -> retry
Just event -> return event
putStrLn $ "Got event"
(Events.lookupHandler event Events.Client) event
GLUT.postRedisplay Nothing
表示コールバックは単純です:
render = GLUT.swapBuffers
そして、それは動作しますが、それは100%のCPUをペグしないとイベントが速やかに処理されます。私は他の答えなしでそれをしなかったので私はこれを掲示していると答えが両方非常に有用なので、私は悪いと感じるので、彼は下院を持っているので@ラアの答えを受け入れている
クライアントの作成と実行にはどのようなものがありますか?特に、ghc/runhaskell/ghciにどのようなフラグを渡しますか? – Laar