WebGPU: возможно ли изменить формат вывода на неплавающие типы?

Для рендеринга в <canvas> кажется, что navigator.getPreferredCanvasFormat() — единственный способ получить действительный формат.

Согласно документации, он всегда будет возвращать либо rgba8unorm, либо bgra8unorm, оба типа f32. Если я попытаюсь установить какой-либо формат, кроме этого, например. на rgba8uint, я получаю GPUCanvasContext.getCurrentTexture: Canvas not configured на рендере.

Тем не менее, документация подразумевает, что возможны и другие форматы, утверждая, что

если вы не используете предпочтительный формат при настройке холста контексте, вы можете понести дополнительные накладные расходы, например, дополнительную текстуру. копий, в зависимости от платформы.

Это кажется неэффективным, даже если последний этап рендеринга все равно происходит на графическом процессоре. Вместо 1 байта на канал (4Б на пиксель) придется использовать 4Б на канал = 4x4 байта с плавающей запятой = 16Б на пиксель. Расточительно?

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

Я что-то упускаю? Это возможно?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
96
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете выполнять рендеринг текстур, не являющихся холстом, которые вы создаете сами.

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 примет эту отправку и понесет накладные расходы, как указано в документации. Возможно, я делаю что-то еще не так.

Engineer 10.05.2024 12:17

Да. Вопрос в том, «чего вы на самом деле пытаетесь достичь»? Если вы просто пытаетесь отобразить изображения на холсте, на экране, не пытайтесь исправить то, что делает WebGPU. Он делает то, что делает. Если вы хотите избежать дополнительных копий буфера, используйте getPreferredCanvasFormat. Если вы пытаетесь получить данные для сохранения в файл или что-то еще, используйте экземпляр текстуры, а не контекст холста. Если это промежуточный этап рендеринга, вы можете использовать другой формат, а затем в конце скомпоновать его в bgra или rgba. Чего вы на самом деле пытаетесь достичь?

LetterEh 10.05.2024 16:36

«Тем не менее, у меня все еще остается вопрос (кода) о том, как может произойти это неявное преобразование текстур, поскольку я получаю ошибку при отправке неправильного формата текстуры». Возможно, это помогает: в WebGPU почти нет неявных шагов. Это делается как услуга, если вы указываете неправильный тип кадрового буфера для ОС/устройства. Это не шаг «преобразовать буквально что угодно в действительный фреймбуфер». Если вы хотите выполнить компоновку, это отдельный проход, выборка текстуры в контексте, или копирование с этапа вычислений, или что-то еще. Но это утверждение также относится и к отправке изображения на экран.

LetterEh 10.05.2024 17:26
Ответ принят как подходящий

Кажется, что для рендеринга в , 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>.
  • ...и т. д...

Другие вопросы по теме