0

NSView高フレームレートで繰り返し変化するカスタムQuartz2D描画を示す大きな画像があります。ただし、フレームごとに描画の一部のみが変わる場合があります。これまでの私のアプローチは、まずオフスクリーン ビットマップ コンテキストに描画し、次にそのコンテキストから画像を作成し、最後にビューの CoreAnimation レイヤーのコンテンツをその画像で更新することです。

私の最初の質問は、そのアプローチが一般的に理にかなっているのか、それがパフォーマンスに関しては正しいのかということです。

オフスクリーン ビットマップ コンテキストへの描画は十分に高速に動作し、ダーティ エリアのみを再描画するように最適化されています。したがって、そのステップの後、画面に表示する必要があるオフスクリーンバッファー内の領域をマークする一連の長方形があります。今のところ、オフスクリーンのビットマップ コンテキストから作成された画像で CoreAnimation レイヤーのコンテンツを更新するだけです。これは基本的にはうまくいきますが、ちらつきます。すべて)まだ描かれていません。CATransaction lock/unlock/begin/end/flushNSView lockFocus/unlockFocusで遊んでみましNSDisableScreenUpdates/NSEnableScreenUpdatesたが、ちらつきを回避する方法がまだ見つからなかったので、同期を正しく行うための実際の正しいシーケンスは何だろうと思っていましたか?

以下は初期化コードのスケッチです。

NSView* theView = ...

CALayer* layer = [[CALayer new] autorelease];
layer.actions = [NSDictionary dictionaryWithObject:[NSNull null] forKey:@"contents"];
[theView setLayer: layer];
[theView setWantsLayer: YES];

// bitmapContext gets re-created when the view size increases.
CGContextRef bitmapContext = CGBitmapContextCreate(...);

そして、ここに描画コードのスケッチがあります:

CGRect[] dirtyRegions = ...

NSDisableScreenUpdates();

[CATransaction begin];
[CATransaction setDisableActions: YES];

// draw into dirty regions of bitmapContext 
// ...

// create image from bitmap context
void* buffer = CGBitmapContextGetData(bitmapContext); 
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, ...);
CGImageRef image = CGImageCreate(..., provider, ...);

// update layer contents, dirty regions are ignored
layer.contents = image;

[CATransaction commit];

NSEnableScreenUpdates();

汚い地域の知識も活かしたいです。このアプローチを使用して、画面上の汚れた領域のみを更新する方法はありますか?

ご協力いただきありがとうございます!

更新:ちらつきの原因となる問題を見つけたと思います。を使用して、ビットマップ コンテキストからピクセル バッファーを使用してイメージを作成しますCGImageCreate(...)CGBitmapContextCreateImage(...)代わりに使用すると機能します。CGBitmapContextCreateImage書き込み時にコピーするので、ビットマップコンテキストが再度更新されたときにピクセルを書き込みます。正しく理解できれば、以前は機能しなかった理由が説明されます。パフォーマンスに影響を与える可能性のあるカーネルへの呼び出しを行うため、慎重に使用する必要がある場所を読んだCGBitmapContextCreateImageので、ダーティ領域を考慮して、関連するピクセルを新しい画像バッファーにコピーするだけだと思います。これは理にかなっていますか?

4

2 に答える 2

1

「通常の」方法は、逆の方法で作業することです。内容の変更が利用可能になったことを示すために呼び出し、オンデマンドで描画CALayer -setNeedsDisplayするために応答します。-drawInContext:したがって、レイヤーのコンテンツを自分からプルすることを許可しますが、それらをプッシュすることはありません。

プッシュしようとしているときにティアリングが発生することに非常に驚いていることを認めなければなりませんが、トランザクションと画面更新ロックで追加したすべての余分な複雑さの最も単純なことから始めたと仮定するとlayer.contents = image、ティアリングを回避する試みです。 、そしてコードを過度に複雑にすることで問題が発生していないことを絶対に確信している場合、おそらく行うべきことは、更新をキューに入れ、 を作成してCVDisplayLinkから、関連するディスプレイまたはディスプレイが更新されようとしているときにのみ、保留中の更新をプッシュすることです。これは基本的に、古い CRT ベースの出力で垂直リトレース中にのみ更新するのと同じアプローチです。

于 2013-08-28T23:55:25.730 に答える
1

多くの異なるアプローチを試した後、ピクセル データのアップロードに CoreAnimation を使用するのをやめ、代わりに CoreVideo ピクセル バッファー ( CVPixelBufferRef) を OpenGL と組み合わせて画面上でピクセルを移動することにしました。

CVOpenGLTextureCacheCreateTextureFromImageCoreVideo には、ピクセル バッファから OpenGL テクスチャを作成する ( )、テクスチャ キャッシュでそれらを管理する ( CVOpenGLTextureCacheRef)、バッファに安全に描画する( )ための便利な関数がいくつか用意されていCVPixelBufferLockBaseAddress/CVPixelBufferUnlockBaseAddressます。その後、通常の OpenGL テクスチャ マッピング コマンドを使用して、ダーティな四角形をウィンドウのバック バッファにアップロードできます ( glTexCoord2fv)。

同じように機能し、同様の API を持つ別のアプローチはIOSurface、これに関する詳細情報はこちらです。

于 2013-12-08T22:28:05.570 に答える