我试图从SharpDX.DataStream中获取像素。我这样做每秒25次,这导致了大量的内存使用。我想我错过了一些清理工作。
一旦我开始调用GetColor方法,内存使用量就开始上升。我试过放弃课堂,但无济于事。拥有更多SharpDX经验的人可能能够指出我所缺少的东西。它很可能像释放资源一样简单,但我很困难。
// Original code by Florian Schnell
// http://www.floschnell.de/computer-science/super-fast-screen-capture-with-windows-8.html
using System;
using System.IO;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Color = System.Drawing.Color;
using Device = SharpDX.Direct3D11.Device;
using MapFlags = SharpDX.DXGI.MapFlags;
using Point = System.Drawing.Point;
using Resource = SharpDX.DXGI.Resource;
using ResultCode = SharpDX.DXGI.ResultCode;
namespace Artemis.Modules.Effects.AmbientLightning
{
internal class ScreenCapture : IDisposable
{
private readonly Device _device;
private readonly Factory1 _factory;
private readonly Texture2D _screenTexture;
private DataStream _dataStream;
private readonly OutputDuplication _duplicatedOutput;
private Resource _screenResource;
private Surface _screenSurface;
public ScreenCapture()
{
// Create device and factory
_device = new Device(DriverType.Hardware);
_factory = new Factory1();
// Creating CPU-accessible texture resource
var texdes = new Texture2DDescription
{
CpuAccessFlags = CpuAccessFlags.Read,
BindFlags = BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Height = _factory.Adapters1[0].Outputs[0].Description.DesktopBounds.Bottom,
Width = _factory.Adapters1[0].Outputs[0].Description.DesktopBounds.Right,
OptionFlags = ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription =
{
Count = 1,
Quality = 0
},
Usage = ResourceUsage.Staging
};
_screenTexture = new Texture2D(_device, texdes);
// duplicate output stuff
var output = new Output1(_factory.Adapters1[0].Outputs[0].NativePointer);
_duplicatedOutput = output.DuplicateOutput(_device);
_screenResource = null;
_dataStream = null;
}
public void Dispose()
{
_duplicatedOutput.Dispose();
_screenResource.Dispose();
_dataStream.Dispose();
_factory.Dispose();
}
public DataStream Capture()
{
try
{
OutputDuplicateFrameInformation duplicateFrameInformation;
_duplicatedOutput.AcquireNextFrame(1000, out duplicateFrameInformation, out _screenResource);
}
catch (SharpDXException e)
{
if (e.ResultCode.Code == ResultCode.WaitTimeout.Result.Code ||
e.ResultCode.Code == ResultCode.AccessDenied.Result.Code ||
e.ResultCode.Code == ResultCode.AccessLost.Result.Code)
return null;
throw;
}
// copy resource into memory that can be accessed by the CPU
_device.ImmediateContext.CopyResource(_screenResource.QueryInterface<SharpDX.Direct3D11.Resource>(),
_screenTexture);
// cast from texture to surface, so we can access its bytes
_screenSurface = _screenTexture.QueryInterface<Surface>();
// map the resource to access it
_screenSurface.Map(MapFlags.Read, out _dataStream);
// seek within the stream and read one byte
_dataStream.Position = 4;
_dataStream.ReadByte();
// free resources
_dataStream.Close();
_screenSurface.Unmap();
_screenSurface.Dispose();
_screenResource.Dispose();
_duplicatedOutput.ReleaseFrame();
return _dataStream;
}
/// <summary>
/// Gets a specific pixel out of the data stream.
/// </summary>
/// <param name="surfaceDataStream"></param>
/// <param name="position">Given point on the screen.</param>
/// <returns></returns>
public Color GetColor(DataStream surfaceDataStream, Point position)
{
var data = new byte[4];
surfaceDataStream.Seek(
position.Y*_factory.Adapters1[0].Outputs[0].Description.DesktopBounds.Right*4 + position.X*4,
SeekOrigin.Begin);
surfaceDataStream.Read(data, 0, 4);
return Color.FromArgb(255, data[2], data[1], data[0]);
}
}
}
答案 0 :(得分:3)
不确定它是否会解决您的内存问题,但有一些改进:
_screenResource.QueryInterface<SharpDX.Direct3D11.Resource>()
_screenSurface = _screenTexture.QueryInterface<Surface>();
Direct3D11.DeviceContext.Map()
的DXGI
DeviceContext.Map()
版本(仅包含非托管内存指针)而不是DataStream
Utilities.Read<ColorBGRA>(intptr)
和正确的字节偏移量来提取数据(请注意,DataStream
也允许直接读取颜色,它不能与ReadByte
一起使用)position.Y*_factory.Adapters1[0].Outputs[0].Description.DesktopBounds.Right*4 + position.X*4
。使用Map方法(DataBox.RowPitch
)返回的步幅偏移到正确的行而不是纹理的宽度(每行的字节数可以改变,可能不与纹理宽度对齐)答案 1 :(得分:2)
如果问题出在GetColor方法中,则可能是您创建的字节数组的绝对数量。您必须等待GC接收它们,如果必须检查每个参考文献,它将需要一段时间。
如果您想直接阅读,可以尝试以ints形式存储:
int b = DataStream.ReadByte(), g = DataStream.ReadByte(), r = DataStream.ReadByte();
DataStream.ReadByte(); //discard the alpha byte
return Color.FromRgb((byte)r, (byte)g, (byte)b);
对我来说似乎有点奇怪,你的流是bgr而不是rbg。