使用 DirectX Texture2D 显示相机预览会导致 Windows Phone 8 上的振荡
本文关键字:Phone Windows 显示 Texture2D DirectX 相机 使用 | 更新日期: 2023-09-27 18:31:04
我目前正在编写一个小应用程序,它使用 SharpDX 精灵批处理显示手机摄像头的预览。对于那些拥有诺基亚开发人员帐户的人来说,代码主要来自本文。
问题
偶尔,似乎以前的帧被吸引到尖叫声("视频"来回跳跃),因为一秒钟的断裂,看起来像振荡/闪烁。
我想到了一个线程问题(因为 PreviewFrameAvailable 事件处理程序是由与负责渲染的方法不同的线程调用的),但是在这两种方法中插入 lock 语句会使代码太慢(帧速率降至 15 帧/秒以下)。
有没有人知道如何解决此问题或在这种情况下如何协调线程同步而不会损失太多性能?
法典
首先,创建所有资源,而设备是图形设备的有效实例:
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();
渲染方法由 SharpDX 游戏类调用,该类基本上使用 IDrawingSurfaceBackgroundContentProvider 接口,该接口由 Windows Phone 8 运行时的 DrawingSurfaceBackgroundGrid 组件调用。
溶液
除了Olydis解决方案(见下文)之外,由于SharpDX的错误,我还必须将Game.IsFixedTimeStep设置为false(有关详细信息,请参阅GitHub上的此问题)。
此外,由于跨线程访问,在 PreviewFrameAvailable 的处理程序中调用sender.GetPreviewBufferArgb(previewData)
是不安全的。请参阅 Windows Phone 开发人员社区中的相应线程。
我的猜测
正如您所猜测的,我也非常确定这可能是由于线程。例如,我怀疑相对较长的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[]
降低了同时SetData
和GetPreviewBufferArgb
访问同一阵列的风险 - 但是,它并不能消除风险(但不知道并发访问int[]
是否会导致奇怪的行为)。