C# 使用DirectX Texture2D显示摄像头预览会导致Windows Phone 8上出现振荡

C# 使用DirectX Texture2D显示摄像头预览会导致Windows Phone 8上出现振荡,c#,windows-phone-8,thread-safety,texture2d,sharpdx,C#,Windows Phone 8,Thread Safety,Texture2d,Sharpdx,我目前正在编写一个小应用程序,它使用SharpDX sprite批处理显示手机摄像头的预览。对于拥有诺基亚开发者帐户的用户,代码主要来自 问题 有时,前几帧似乎被吸引到尖叫声(“视频”来回跳跃),因为一秒钟的断裂看起来像振荡/闪烁 我想到了一个线程问题(因为PreviewFrameAvailable事件处理程序是由不同于负责呈现的方法的线程调用的),但是在这两个方法中插入lock语句会使代码速度太慢(帧速率降至15帧/秒以下) 有没有人知道如何解决这个问题,或者在这种情况下如何在不损失太多性能的

我目前正在编写一个小应用程序,它使用SharpDX sprite批处理显示手机摄像头的预览。对于拥有诺基亚开发者帐户的用户,代码主要来自

问题

有时,前几帧似乎被吸引到尖叫声(“视频”来回跳跃),因为一秒钟的断裂看起来像振荡/闪烁

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

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

代码

首先,创建所有资源,而设备是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运行时的组件

解决方案

除了Olydis解决方案(见下文),由于SharpDX错误,我还必须将Game.IsFixedTimeStep设置为false(详见此)


此外,由于跨线程访问,在PreviewFrameAvailable的处理程序中调用sender.GetPreviewBufferArgb(previewData)是不安全的。请参阅中相应的线程

我的猜测

void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
    sender.GetPreviewBufferArgb(previewData2);
    // swap buffers
    var previewDataTemp = previewData1;
    previewData1 = previewData2;
    previewData2 = previewDataTemp;
}
正如您所猜测的,我也非常确定这可能是由于线程。例如,我怀疑相对较长的
SetData
调用可能会被
Draw
调用拦截,从而导致意外的输出

解决方案

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

另外,让我们分配两个
int[]
,而不是一个,我们将交替使用

代码片段

void photoDevice_PreviewFrameAvailable(ICameraCaptureDevice sender, object args)
{
    sender.GetPreviewBufferArgb(previewData2);
    // swap buffers
    var previewDataTemp = previewData1;
    previewData1 = previewData2;
    previewData2 = previewDataTemp;
}
然后将其添加到
Draw
调用(或同等上下文)中:

结论

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