Я пытаюсь понять следующую функцию в libvpx (vp8/common/reconinter.c):
void vp8_copy_mem16x16_c(unsigned char *src, int src_stride, unsigned char *dst,
int dst_stride) {
int r;
for (r = 0; r < 16; ++r) {
memcpy(dst, src, 16);
src += src_stride;
dst += dst_stride;
}
}
(В том же исходном файле также существуют версии 8x8 и 8x4.)
Он копирует 16 байт из src в dst 16 раз, но в то же время добавляет пользовательский stride как к src, так и к dst. Без предварительных знаний о компьютерной графике и DSP я очень запутался в этих функциях: какой смысл поддерживать пользовательские stride в src и dst? Каковы некоторые примеры или преимущества использования таких функций, а не просто копирование всех 16 x 16 байтов вместе?
Большое спасибо!
Обновление: чтобы было ясно, vp8_copy_mem16x16_c переопределяется как vp8_copy_mem16x16 на этапе сборки, когда версия, оптимизированная для вектора, недоступна на целевой платформе.
Но почему вы думаете, что все блоки непрерывны? Это было бы верно только для шага == 16.





Это попытка скопировать квадратный блок 16x16 между двумя изображениями (т.е. массив 2d).
Предполагаемое использование состоит в том, чтобы установить src и dst в начальную позицию исходного и целевого блоков и установить stride в ширину всего изображения.
Эта функция также обеспечивает два отдельных шага для src и dst, так что src и dst не должны быть одинаковой ширины.
Примечание
«Ширина» здесь действительно должна быть «шагом», потому что «ширина» — это допустимый/видимый размер каждой строки развертки, а «шаг» — это выделенный размер строки развертки. С точки зрения памяти здесь важен шаг, а не ширина.
Ваш вопрос в том, для чего нужен шаг, если я правильно его понимаю.
В контексте libvpx у него есть два больших варианта использования:
Работа с кодированием отдельных блоков в исходном потоке. Если у вас есть полное изображение, вы можете использовать исходный шаг, равный <image width + image stride - block width>, и конечный шаг, равный 0 (или что-то еще, что необходимо в вашем алгоритме), чтобы эффективно извлечь блок. Редактировать: чтобы быть ясным, большинство операций кодирования и декодирования видео работают с квадратными или прямоугольными блоками. Примером этого является JPEG, но все операции с mp4 и VP8/9 также основаны на блоках. Это очень простая, очень часто используемая операция.
В то время как большинство API допускают изображения, не являющиеся степенью двойки, эффективный доступ к памяти, особенно на графическом процессоре, в значительной степени требует этого (или, по крайней мере, требует некоторого заполнения выравнивания). И у источника, и у получателя могут быть разные такие требования, и здесь вступают в действие оба аргумента шага.
В целом, однако, есть и третий вариант использования strides: копирование спрайтов. Подобно первому пункту выше, вы можете очень эффективно преобразовать спрайты в текстуры (и/или экран, если нет двойной буферизации), используя шаги для копирования памяти.
Рассмотрим два двумерных массива с 16-байтовыми элементами, скажем, M16 A[1024][1280] и M16 B[1024][1600], и предположим, что вы хотите скопировать столбец из массива B в массив A, например:
AColumn = 37;
BColumn = 46;
for (int i = 0; i < 1024; ++i)
A[i][AColumn] = B[i][BColumn];
Элементы A, с которыми работает этот цикл, — это A[0][AColumn], A[1][AColumn], A[2][AColumn] и так далее. Поскольку ширина A составляет 1280 элементов, последовательные элементы в цикле отстоят друг от друга на 1280 элементов в памяти, что составляет 1280•16 = 20 480 байт.
Точно так же последовательные элементы B в цикле находятся на расстоянии 1600 элементов друг от друга, что составляет 1600•16 = 25 600 байтов.
Таким образом, если мы вызовем vp8_copy_mem16x16_c с src_stride 25 600 и dst_stride 20 480, он может скопировать столбец из B в столбец A. (Кроме того, для src мы передаем адрес первого элемента назначения, &A[0][AColumn], а для dst мы передаем &B[0][BColumn].
Различные варианты шагов могут копировать столбец одного массива в строку другого или наоборот. vp8_copy_mem16x16_c — это обобщенный метод «Копировать 16-байтовые фрагменты с некоторым регулярным интервалом в памяти в пункты назначения с некоторым регулярным интервалом в памяти», который может работать со строками, столбцами, чередующимися элементами (например, с каждым вторым элементом столбца) и другими способами.
В качестве другого примера рассмотрим struct { M16 m; RGB p; int i; } B[1024]; и M16 A[1024]. Мы могли бы извлечь M16 членов структур в B в однородный M16 массив A с помощью vp8_copy_mem16x16_c(A, sizeof *A, &B[0].m, sizeof *B);.
_cв вашем вопросе сбивает с толку, вас не интересует понимание самой функции, и вы спрашиваете об использовании или назначении vp8_copy_mem16x16 как операции. Это правильно?What are some examples or benefits of using such functionsРезультат просто другой, когда src_stride != dst_stride.