Повысить производительность, чтобы нарисовать изображение, SIMD perhapse решение? - PullRequest
0 голосов
/ 06 февраля 2020

У меня нет опыта с SIMD, но есть метод, который слишком медленный. Я знаю, получить 40 кадров в секунду, и мне нужно больше. Кто-нибудь знает, как я мог сделать этот метод рисования быстрее? Возможно, инструкции SIMD являются решением?

SourceData теперь является байтом [] (videoBytes), но может также использовать указатель.

 public bool PaintFrame(IntPtr layerBuffer, ushort vStart, byte vScale)
    {
        for (ushort y = 0; y < height; y++)
        {
            ushort eff_y = (ushort)(vScale * (y - vStart) / 128);

            var newY = tileHeight > 0 ? eff_y % tileHeight : 0;
            uint y_add = (uint)(newY * tileWidth * bitsPerPixel >> 3);

            for (int x = 0; x < width; x++)
            {
                var newX = tileWidth > 0 ? x % tileWidth : 0;

                ushort x_add = (ushort)(newX * bitsPerPixel >> 3);
                uint tile_offset = y_add + x_add;
                byte color = videoBytes[tile_offset];
                var colorIndex = BitsPerPxlCalculation(color, newX);

                // Apply Palette Offset
                if (paletteOffset > 0)
                    colorIndex += paletteOffset;
                var place = x + eff_y * width;
                Marshal.WriteByte(layerBuffer + place, colorIndex);
            }
        }
        return true;
    }


    private void UpdateBitPerPixelMethod()
    {
        // Convert tile byte to indexed color
        switch (bitsPerPixel)
        {
            case 1:
                BitsPerPxlCalculation = (color, newX) => color;
                break;
            case 2:
                BitsPerPxlCalculation = (color, newX) => (byte)(color >> 6 - ((newX & 3) << 1) & 3);
                break;
            case 4:
                BitsPerPxlCalculation = (color, newX) => (byte)(color >> 4 - ((newX & 1) << 2) & 0xf);
                break;
            case 8:
                BitsPerPxlCalculation = (color, newX) => color;
                break;
        }
    }

Дополнительная информация

В зависимости от настроек, bpp может быть изменен. Индексированные цвета и цвета палитры хранятся отдельно. Здесь я должен воссоздать индексы пикселей изображения, поэтому позже я использую индексы палитры и цвета в WPF (Windows) или SDL (Linux, Ma c) для отображения изображения.

vStart - это возможность обрезать изображение сверху.

UpdateBitPerPixelMethod () не изменится во время рендеринга кадра, только до этого. Во время операции никакие данные настроек не могут быть изменены.

Поэтому я надеялся, что некоторые части могут быть записаны с использованием SIMD, потому что процедура одинакова для всех пикселей.

1 Ответ

3 голосов
/ 12 февраля 2020

Хи,

Ваш код не самый понятный для меня. Вы пытаетесь создать новую матрицу / изображение? Если да, создайте новое 2D-распределение и рассчитайте в нем все изображение. Установите его в 0 после того, как вам больше не нужны вычисления. Замените Marshal.WriteByte(layerBuffer + place, colorIndex); 2D-изображением (может быть, это изображение?).

Что касается остального, это проблема, потому что у вас есть неравномерное смещение при индексировании и скачках. Это усложнит разработку SIMD-решения (вам понадобится маскировка и прочее). Моя ставка будет состоять в том, чтобы рассчитать все для всех индексов и сохранить их в отдельных 2D матрицах, которые выделяются один раз в начале.
Например:

ushort eff_y = (ushort)(vScale * (y - vStart) / 128);

Рассчитывается для каждой строки изображения. Теперь вы можете рассчитать его один раз как массив, так как я не верю, что размер формата изображений изменяется во время выполнения.

Я не знаю, определяются ли vStart и vScale как константа при запуске программы. Вы должны делать это для каждого вычисления, которое использует константу, и просто читать матрицы позже для вычисления.

SIMD может помочь, но только если вы делаете каждую итерацию, вы вычисляете одно и то же, и если вы избегаете ветвления и переключения случаев.

Дополнение 1

У вас есть несколько проблем и конструктивных соображений с моей точки зрения. Прежде всего, вам нужно избавиться от идеи, что SIMD поможет вам в вашем случае. Вам нужно будет удалить все условные выражения. SIMD не предназначены для работы с условными утверждениями.

Ваша идея должна состоять в том, чтобы разбить логи c на управляемые части, чтобы вы могли видеть, что часть кода занимает больше времени. Одна большая проблема - это байт записи в маршале, это автоматически говорит компилятору, что вы обрабатываете только и исключительно 1 байт. Я предполагаю, что это создает на большой bottle шее.

Путем анализа кода я вижу в каждом l oop вы делаете проверки. Это должно быть реструктурировано.

Предполагается, что изображение редко обрезается, это будет отрывом от вычислений изображения.

List<ushort> eff_y = new List<ushort>();
List<uint> y_add = new List<uint>();
for (ushort y = 0; y < height; y++)
{
    eff_y.add((ushort)(vScale * (y - vStart) / 128));
    var newY = tileHeight > 0 ? eff_y % tileHeight : 0;
    y_add = (uint)(newY * tileWidth * bitsPerPixel >> 3);
}

Так что это можно предварительно рассчитать и изменить только при изменении обрезки.

Теперь все становится очень сложно.

paletteOffset - оператор if имеет смысл только в paletteOffset, может быть отрицательным, затем обнуляет его и удаляет оператор if

bitsPerPixel - this выглядит как фиксированное значение для длительности рендеринга, поэтому удалите UpdateBitPerPixelMethod и отправьте параметр.

for (ushort y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var newX = tileWidth > 0 ? x % tileWidth : 0; // conditional stetement

        ushort x_add = (ushort)(newX * bitsPerPixel >> 3);
        uint tile_offset = y_add + x_add;
        byte color = videoBytes[tile_offset];
        var colorIndex = BitsPerPxlCalculation(color, newX);

        // Apply Palette Offset
        if (paletteOffset > 0) // conditional stetement
            colorIndex += paletteOffset;
        var place = x + eff_y * width;
        Marshal.WriteByte(layerBuffer + place, colorIndex);
    }
}

Это всего лишь несколько вещей, которые необходимо сделать, прежде чем пытаться что-либо с SIMD. Но к тому времени изменения дадут компилятору подсказки о том, что вы хотите сделать. Это может улучшить выполнение машинного кода. Вам также необходимо проверить производительность вашего кода, чтобы точно определить шею bottle, которую очень трудно предположить или угадать по коду.

Удачи

...