Для рендеринга в <canvas> кажется, что navigator.getPreferredCanvasFormat() — единственный способ получить действительный формат.
Согласно документации, он всегда будет возвращать либо rgba8unorm, либо bgra8unorm, оба типа f32. Если я попытаюсь установить какой-либо формат, кроме этого, например. на rgba8uint, я получаю GPUCanvasContext.getCurrentTexture: Canvas not configured на рендере.
Тем не менее, документация подразумевает, что возможны и другие форматы, утверждая, что
если вы не используете предпочтительный формат при настройке холста контексте, вы можете понести дополнительные накладные расходы, например, дополнительную текстуру. копий, в зависимости от платформы.
Это кажется неэффективным, даже если последний этап рендеринга все равно происходит на графическом процессоре. Вместо 1 байта на канал (4Б на пиксель) придется использовать 4Б на канал = 4x4 байта с плавающей запятой = 16Б на пиксель. Расточительно?
Буду рад принять удар по преобразованию текстур, если смогу снизить затраты на полосу пропускания.
Я что-то упускаю? Это возможно?





Вы можете выполнять рендеринг текстур, не являющихся холстом, которые вы создаете сами.
const nonScreenTexture = device.createTexture({});
Это текстуры, которые не предназначены для отрисовки непосредственно на экране.
Если вы используете типы TypeScript (@webgpu/types) и посмотрите список форматов GPUTextureFormat, вы увидите, что вариантов очень много. Если вы используете расширения, их даже больше для некоторых форматов игровой индустрии со сжатием с потерями.
В настоящее время canvas поддерживает два формата: rgba8unorm и bgra8unorm. Это форматы SDR, которые будут отправлены на серверную часть Vulkan/DirectX/Metal, где они будут объединены со всем остальным, что ОС должна отрисовать, и вывести на экран. Один формат (rgba) является общим для портативных устройств. Другой формат (bgra) распространен для настольных компьютеров/ноутбуков. Если дать нужному устройству правильный формат, лишних копий не будет. Если вы присвоите ему неправильный формат, он заменит его на правильный формат. ...даже если он поддерживает больше форматов, к тому времени, когда вы его отправите, а WebGPU отправит его в Vulkan/DX/Metal, он скопирует его и преобразует в один из этих форматов.
Здесь нет никакой экономии места, потому что, если вы говорите о формате текстуры холста, это не то, что вы можете трогать или контролировать. Это делают внутренние компоненты браузера и графические драйверы компьютера. Чтобы обеспечить экономию места, вы будете говорить о своей собственной сборке Chrome со специальными вилками Dawn/ANGLE, чтобы сохранить DX/Vulkan биты при вызове отрисовки SDR...
но они работают над добавлением поддержки широкой гаммы и HDR в WebGPU (они уже доступны в 2D-холсте и CSS). Этот монитор передо мной имеет разрешение 12 бит на пиксель, HDR, примерно на 98% DCI-P3 и огромную часть BT2020 с низкой ошибкой. Чтобы WebGPU работал так же, как настольные приложения с поддержкой графического процессора (а не только SDR с узкой гаммой), ему необходимо обрабатывать больше битов на пиксель, а не меньше. В итоге вы получаете такие вещи, как DXGI_FORMAT_R11G11B10, которые все еще могут быть 32-битными, но, очевидно, совсем не разбиты таким же образом.
Да. Вопрос в том, «чего вы на самом деле пытаетесь достичь»? Если вы просто пытаетесь отобразить изображения на холсте, на экране, не пытайтесь исправить то, что делает WebGPU. Он делает то, что делает. Если вы хотите избежать дополнительных копий буфера, используйте getPreferredCanvasFormat. Если вы пытаетесь получить данные для сохранения в файл или что-то еще, используйте экземпляр текстуры, а не контекст холста. Если это промежуточный этап рендеринга, вы можете использовать другой формат, а затем в конце скомпоновать его в bgra или rgba. Чего вы на самом деле пытаетесь достичь?
«Тем не менее, у меня все еще остается вопрос (кода) о том, как может произойти это неявное преобразование текстур, поскольку я получаю ошибку при отправке неправильного формата текстуры». Возможно, это помогает: в WebGPU почти нет неявных шагов. Это делается как услуга, если вы указываете неправильный тип кадрового буфера для ОС/устройства. Это не шаг «преобразовать буквально что угодно в действительный фреймбуфер». Если вы хотите выполнить компоновку, это отдельный проход, выборка текстуры в контексте, или копирование с этапа вычислений, или что-то еще. Но это утверждение также относится и к отправке изображения на экран.
Кажется, что для рендеринга в , navigator.getPreferredCanvasFormat() — единственный способ получить действительный формат.
Нет, это способ получить оптимальный по скорости формат. Вы можете использовать либо 'rgba8unorm', либо 'bgra8norm', и это будет работать, но если выбранный вами формат не соответствует формату, возвращаемому из navigator.getPreferredCanvasFormat(), то может возникнуть снижение производительности при преобразовании в то, что нужно браузеру/ОС для объединения холста с остальной частью страница/экран
Согласно документации, он всегда будет возвращать либо rgba8unorm, либо bgra8unorm, оба типа f32.
Знак f32 указывает на то, что эти форматы возвращают значения f32 при их выборке, а не на размер их хранилища. Другими словами, вы можете привязать эти форматы к
var t: texture_2d<f32>;
Но вы не можете привязать их к
var t: texture_2d<u32>;
ни
var t: texture_2d<i32>;
Это кажется неэффективным, даже если последний этап рендеринга все равно происходит на графическом процессоре. Вместо 1 байта на канал (4Б на пиксель) придется использовать 4Б на канал = 4x4 байта с плавающей запятой = 16Б на пиксель. Расточительно?
rgba8unorm и bgra8unorm — это форматы по 1 байту на канал. 4 байта на пиксель
Подробную информацию о размере смотрите в таблице возможностей формата текстуры внизу спецификации
Хотя, даже не глядя на таблицу, большинство форматов говорят сами за себя. Примеры
'rgba8unorm' 8 = 8 бит на канал (то есть 1 байт на канал) с 4 каналами r, g, b и a, каждый канал считывается и нормализуется до беззнакового значения от 0 до 1 (это то, что означает unorm), поэтому 0 в текстура = 0,0 при чтении. 255 в текстуре = 1.0 при чтении и т.д...'rgba8snorm' 8 = 8 бит на канал (то есть 1 байт на канал) с 4 каналами r, g, b и a, каждый канал считывается и нормализуется до знакового значения от -1 до 1 (это то, что означает snorm), поэтому - 128 в текстуре = -1.0 и +127 в текстуре = 1.0'rgba16float' 16 = 16 бит на канал (2 байта), с 4 каналами r, g, b и a. эти значения представлены в 16-битном формате с плавающей запятой'rgba32float' 32 = 32 бита на канал (4 байта), с 4 каналами r, g, b и a. эти значения имеют стандартный формат float 32. Тот же формат, что и Float32Array'rg8uint' 8 = 8 бит на канал (1 байт) с 2 каналами (r, g). Байты представляют собой 8-битные целые числа без знака. Этот формат можно использовать с texture_2d<u32>, но не с texture_2d<f32>.
Похороненный среди пустяков, ваш комментарий о том, «что я не могу контролировать», полезен. Тем не менее, у меня все еще остается вопрос (кода) о том, как может происходить это неявное преобразование текстур, поскольку я получаю ошибку при отправке неправильного формата текстуры. Я ожидаю, что WebGPU примет эту отправку и понесет накладные расходы, как указано в документации. Возможно, я делаю что-то еще не так.