2

我目前正在编写一个小应用程序,它使用 SharpDX sprite 批处理显示手机摄像头的预览。有诺基亚开发者账号的朋友,代码主要来自这篇文章

问题

有时,似乎先前的帧被吸引到屏幕上(“视频”来回跳跃),一秒钟的断裂,看起来像振荡/闪烁。

我想到了一个线程问题(因为 PreviewFrameAvailable 事件处理程序是由与负责渲染的方法不同的线程调用的),但是在这两种方法中都插入了 lock 语句会使代码太慢(帧速率下降到 15 帧以下/秒)。

有谁知道如何解决这个问题或在这种情况下如何在不损失太多性能的情况下实现线程同步?

代码

首先,创建所有资源,而 device 是 GraphicsDevice 的有效实例:

spriteBatch = new SpriteBatch(device);
photoDevice = await PhotoCaptureDevice.OpenAsync(CameraSensorLocation.Back, captureSize);
photoDevice.FocusRegion = null;

width = (int)photoDevice.PreviewResolution.Width;
height = (int)photoDevice.PreviewResolution.Height;

previewData = new int[width * height];

cameraTexture = Texture2D.New(device, width, height, PixelFormat.B8G8R8A8.UNorm);

photoDevice.PreviewFrameAvailable += photoDevice_PreviewFrameAvailable;

然后,每当预览帧发生变化时,我都会将数据设置为纹理:

void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
    sender.GetPreviewBufferArgb(previewData);
    cameraTexture.SetData(previewData);
}

最后,使用 SpriteBatch 绘制纹理,而参数 backBufferCenter、textureCenter、textureScaling 和 Math.Pi / 2 用于在横向中居中和调整纹理:

spriteBatch.Begin();

spriteBatch.Draw(cameraTexture, backBufferCenter, null, Color.White, (float)Math.PI / 2, textureCenter, textureScaling, SpriteEffects.None, 1.0f);

spriteBatch.End();

render 方法由 SharpDX 游戏类调用,该类基本使用 IDrawingSurfaceBackgroundContentProvider 接口,由Windows Phone 8 运行时的 DrawingSurfaceBackgroundGrid 组件调用。

解决方案

除了 Olydis 解决方案(见下文),由于 SharpDX 错误,我还必须将 Game.IsFixedTimeStep 设置为 false(有关详细信息,请参阅GitHub 上的此问题)。

此外,由于跨线程访问,在处理程序内部调用 PreviewFrameAvailable 是不安全的。请参阅windows phone 开发者社区sender.GetPreviewBufferArgb(previewData)中的相应线程。

4

1 回答 1

2

我猜

正如你猜到的,我也很确定这可能是由于线程。我怀疑,例如,比较长的SetData调用可能会被Draw调用截获,导致意外的输出。

解决方案

以下解决方案不使用同步,而是将“关键”部分(访问纹理)移动到相同的上下文。

另外,让我们分配两个int[]而不是一个,我们将以交替的方式使用它。

代码片段

void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
    sender.GetPreviewBufferArgb(previewData2);
    // swap buffers
    var previewDataTemp = previewData1;
    previewData1 = previewData2;
    previewData2 = previewDataTemp;
}

然后将其添加到您的Draw调用(或相同的上下文)中:

cameraTexture.SetData(previewData1);

结论

这实际上应该可以防止您的问题,因为只绘制“完全更新”的纹理并且没有对它们的并发访问。使用两个int[]降低了同时拥有SetDataGetPreviewBufferArgb访问同一个数组的风险 - 但是,它并没有消除风险(但不知道并发访问是否int[]会首先导致奇怪的行为)。

于 2013-09-12T08:59:48.440 回答