#c# #winforms
#c# #winforms
Вопрос:
У меня есть некоторый буфер визуальных данных, который довольно быстро меняется. И мне нужно нарисовать его в форме, со скоростью не менее 60 кадров в секунду.
Во всех других подобных вопросах, которые я нашел, было рекомендовано использовать Bitmap
в качестве временного буфера, например:
public unsafe TimeSpan CopyToFormBuffer(float* buff)
{
var b = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
var bd = b.LockBits(this.ClientRectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
//this is more of a GPU work, but for simplicity i put it on CPU:
var hy = bd.Scan0;
for (int y = 0; y < this.ClientSize.Height; y, hy = bd.Stride)
{
var hx = hy;
for (int x = 0; x < this.ClientSize.Width; x, hx = 4, buff = 4)
{
var p = (byte*)hx;
p[3] = Convert.ToByte(buff[0] * 255);
p[2] = Convert.ToByte(buff[1] * 255);
p[1] = Convert.ToByte(buff[2] * 255);
p[0] = Convert.ToByte(buff[3] * 255);
}
}
b.UnlockBits(bd);
var sw = Stopwatch.StartNew();
this.CreateGraphics().DrawImage(b, 0, 0);
sw.Stop();
return sw.Elapsed;
}
private unsafe void Form1_Load(object sender, EventArgs e)
{
var buff = (float*)Marshal.AllocHGlobal(this.ClientSize.Width * this.ClientSize.Height * 4 * sizeof(float));
for (var i = 100; i > 0; i--)
MessageBox.Show(CopyToFormBuffer(buff).ToString());
}
Но таким образом, строка:
this.CreateGraphics().DrawImage(b, 0, 0);
выполняет дополнительную операцию копирования, что недопустимо медленно (80-90 мс на моем ПК в полноэкранном режиме).
Итак, есть ли способ рисовать в буфере формы напрямую, как я делаю с Bitmap
‘s Scan0
?
Обратите внимание, что, хотя в примере буфер визуальных данных представляет собой просто массив растровых цветов 4xFloat, мне нужно то же самое для более сложных форматов, поэтому в любом случае необходимо выполнить какое-то преобразование цвета.
Комментарии:
1.
CreateGraphics
плохо. Этоthis
окнаForm
, на которых вы рисуете? Если это так, вам действительно нужно использоватьPaint
событие формы и его графический элемент.2. Чтобы дать нам некоторую перспективу, не могли бы вы предоставить размер растрового изображения, для обработки которого потребовалось 80-90 мс.
3. @TnTinMn 1920×1080
Ответ №1:
Вот реализация того, чего вы пытаетесь достичь, используя событие Forms Paint() . Это будет намного быстрее, чем выделять экранный буфер для каждого кадра. Вы можете изменить буфер пикселей «buff» в любом месте вашей формы, и он будет перерисован в форме. Вам просто нужно вызвать это.Invalidate() для принудительного вызова Paint . Каждый раз, когда вы меняете buff , вызывайте Invalidate в самой форме.
public partial class Form1 : Form
{
private unsafe float* buff;
public unsafe Form1()
{
InitializeComponent();
buff = (float*)Marshal.AllocHGlobal(this.ClientSize.Width * this.ClientSize.Height * 4 * sizeof(float));
}
private unsafe void Form1_Paint(object sender, PaintEventArgs e)
{
var sw = Stopwatch.StartNew();
var b = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
var bd = b.LockBits(this.ClientRectangle, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
//this is more of a GPU work, but for simplicity i put it on CPU:
var hy = bd.Scan0;
for (int y = 0; y < this.ClientSize.Height; y, hy = bd.Stride)
{
var hx = hy;
for (int x = 0; x < this.ClientSize.Width; x, hx = 4, buff = 4)
{
var p = (byte*)hx;
p[3] = Convert.ToByte(buff[0] * 255);
p[2] = Convert.ToByte(buff[1] * 255);
p[1] = Convert.ToByte(buff[2] * 255);
p[0] = Convert.ToByte(buff[3] * 255);
}
}
b.UnlockBits(bd);
e.Graphics.DrawImage(b, 0, 0);
sw.Stop();
Debug.WriteLine($"Frame took {sw.Elapsed}ms to draw");
}
}
Комментарии:
1. Я вижу, что использование
CreateGraphics
делает это медленнее. Но для выполнения вашей строки путиDrawImage
требуется 50-55 мс. Который по-прежнему составляет <20 кадров в секунду и действительно далек от 60 кадров в секунду.2. Вы выполняете манипуляцию на уровне пикселей, которая равна O (N ^ 2). Это не будет хорошо для процессора, такие вещи нужно делать на графическом процессоре. Вы захотите взглянуть на фреймворки DirectX или OpenGL.
3. для преобразования пикселей я собираюсь использовать OpenCL, но копирование буфера не должно занимать 50 мс для обработки.
Buffer.BlockCopy
, например, копирует тот же объем байта за 8-9 мс, что приемлемо.4. О, извините, на самом деле это 2-3 мс, я тестировал на массиве байтов в 4 раза больше.
5. Значит, вы настроены?