Comments 17
И если пользователи Safari на macOS могут отделаться лёгким шоком и установить браузер посовременнее, то пользователи iOS такой возможности лишены политикой Apple.
Это, оказывается, WebKit… Как и firefox и всё остальное.
apps.apple.com/ru/app/chrome-браузер-от-google/id535886823#?platform=iphone
Честно, не знал до этой статьи.
all Chrome variants except iOS now use Blink
То есть современный Chrome сейчас базируется на собственном форке движка WebKit, который развивается независимо. Поэтому Chrome на macOS нормально выдаёт WebGL 2.0.
Но это НЕ касается версии Chrome для iOS.
Вопросы правда не по теме появились: например, как так получилось, что даже MS когда-то обязали делать выбор браузера ( habr.com/ru/post/85925 ), а Apple по сути практически запрещает ПО такого типа, разрешая только обёртку.
а Apple по сути практически запрещает ПО такого типа, разрешая только обёртку.
Потому что Apple двигает Metal. Сейчас повторяется ситуация DirectX vs OpenGL пятнадцатилетней давности.
Искусственные ограничения конкуренции и свобод пользователей были очевидны с самого запуска iPhone, и антимонопольные структуры должны были начать давить на Apple как только стало понятно, что их продукты стали массовыми.
Но тут есть и другие неудачные прецеденты, вроде тех же игровых приставок от Sony и Microsoft, таких же закрытых и навязывающих свои условия — практику, которую они ввели задолго до появления iOS…
Это шок, до сегодняшнего дня я был уверен что WebGl2 на iphone будет работать в хроме. Но сейчас я как будто упал с небес на землю. Как же так?? Может я все же, что то не правильно понял?
Думаю, если бы на iOS были бы альтернативы, то можно было бы и забросить эту затею с мучениями на WebGL 1.0, описанными в статье…
Хотя есть некоторые причины ожидать, что одной проблемой станет меньше с очередным крупным обновлением iOS.
а разработчики web-стандартов готовят WebGPU
Кому интересно: спецификация, issues.
При этом реализацию от Mozilla вполне можно пощупать нативно: wgpu-rs.
Поэтому было бы интересно, чтобы тот, кто варится в этих экспериментах WebGPU, рассказал какие очертания этот новый стандарт принимает на текущий момент, и на что же он на самом деле похож.
Не знаю на счёт Khronos и какое они отношение имеют к WebGPU.
началось всё с попытки Apple
Кстати по какой-то причине редактор со стороны Apple теперь бывший:
![](https://habrastorage.org/webt/p3/_n/rm/p3_nrmgabkfwgieotpvp1ee0j0c.png)
И вроде как это всё не совсем с Apple началось. По крайней мере суть в том, что все основные заинтересованные (Mozilla, Google, Apple) в какой-то момент собрались и решили делать новый GAPI ибо поняли, что WebGL это путь вникуда.
и на что же он на самом деле похож
На самом деле я достаточно плохо знаком с другими GAPI. Но точно уверен, что по чуть-чуть оно похоже на все современные GAPI. И в тоже время это полностью независимый GAPI. Однако несколько моментов всёж отмечу.
WebGPU очень простой для пользователя. Вероятно это самый простой GAPI на сегодняшний день. Определённо проще чем Vulkan/DX12/Metal. И даже проще чем OpenGL (ES). Вот пример классического треугольника:
use std::borrow::Cow;
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::Window,
};
async fn run(event_loop: EventLoop<()>, window: Window) {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::BackendBit::all());
let surface = unsafe { instance.create_surface(&window) };
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
// Request an adapter which can render to our surface
compatible_surface: Some(&surface),
})
.await
.expect("Failed to find an appropriate adapter");
// Create the logical device and command queue
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
},
None,
)
.await
.expect("Failed to create device");
// Load the shaders from disk
let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
flags: wgpu::ShaderFlags::all(),
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});
let swapchain_format = adapter.get_swap_chain_preferred_format(&surface).unwrap();
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[swapchain_format.into()],
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
});
let mut sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
format: swapchain_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
};
let mut swap_chain = device.create_swap_chain(&surface, &sc_desc);
event_loop.run(move |event, _, control_flow| {
// Have the closure take ownership of the resources.
// `event_loop.run` never returns, therefore we must do this to ensure
// the resources are properly cleaned up.
let _ = (&instance, &adapter, &shader, &pipeline_layout);
*control_flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
// Recreate the swap chain with the new size
sc_desc.width = size.width;
sc_desc.height = size.height;
swap_chain = device.create_swap_chain(&surface, &sc_desc);
}
Event::RedrawRequested(_) => {
let frame = swap_chain
.get_current_frame()
.expect("Failed to acquire next swap chain texture")
.output;
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
store: true,
},
}],
depth_stencil_attachment: None,
});
rpass.set_pipeline(&render_pipeline);
rpass.draw(0..3, 0..1);
}
queue.submit(Some(encoder.finish()));
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => {}
}
});
}
fn main() {
let event_loop = EventLoop::new();
let window = winit::window::Window::new(&event_loop).unwrap();
#[cfg(not(target_arch = "wasm32"))]
{
wgpu_subscriber::initialize_default_subscriber(None);
// Temporarily avoid srgb formats for the swapchain on the web
pollster::block_on(run(event_loop, window));
}
#[cfg(target_arch = "wasm32")]
{
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init().expect("could not initialize logger");
use winit::platform::web::WindowExtWebSys;
// On wasm, append the canvas to the document body
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| {
body.append_child(&web_sys::Element::from(window.canvas()))
.ok()
})
.expect("couldn't append canvas to document body");
wasm_bindgen_futures::spawn_local(run(event_loop, window));
}
}
[[stage(vertex)]]
fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {
var x: f32 = f32(i32(in_vertex_index) - 1);
var y: f32 = f32(i32(in_vertex_index & 1) * 2 - 1);
return vec4<f32>(x, y, 0.0, 1.0);
}
[[stage(fragment)]]
fn fs_main() -> [[location(0)]] vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
Тут примерно следующее:
- Для начала получаем два основных элемента, которые в дальнейшем будут использоваться. Это Device и Queue. Девайс нам нужен для создания всяких полезностей вроде пайплайнов, текстур, буферов и прочего. Очередь для выполнения команд на GPU.
- Создаём RenderPipeline скармливая RenderPipelineDescriptor нашему девайсу (device). Это то как мы рисуем и какие ресурсы для этого используем. Что-то вроде набора настоек рисования. Если нам нужно что-то вычислять, то берём ComputePipeline и ComputePipelineDescriptor соответсвенно.
- Создаём SwapChain. В некотором смысле эта часть опциональна. В конце концов мы можем использовать WebGPU не для графики, а для рассчётов. (Кстати логично же, что этапы 2 и 3 можно менять местами)
- Собсвенно цикл рисования. Создаём CommandEncoder и скармливаем ему всякие команды рисования/вычислений и прочего. Как только закончим, то превращяем энкодер (encoder) в CommandBuffer и отправляем в нашу очередь (queue).
Да в общем-то и всё. Остальное детали. А, ну про WGSL ещё. Не смотря на очевидную сырость и некоторые недоработки (я сам сделал пару коммитов в naga для поддержки парочки базовых вещей) этот язык ВОСХИТИТЕЛЕН. Особенно вот эта штука, где делаешь структурку типа VertexOutput
и используешь её одновременно в вертексной и фрагментной части (пример). А, ну да, они же ещё и в одном файле!
Кстати эту штуку со структурками сделали буквально пару дней назад.
Если хочется потыкать это всё прям сейчас, то подучите Rust и добро пожаловать в нашу уютную конфу. Серьёзно, wgpu-rs уже вполне можно считать достаточно юзабельным. Другое дело, что как раз таки в браузере оно не работает (не считая ночных сборок и вот этого всего). Кстати в настоящий момент есть как минимум два инересных проекта. Игровой движок bevy и реализация графического интерфейса iced. Ну ладно, они сами по себе местами сырые. Да и вообще с моей точки зрения у них есть один маленький, но фатальный недостаток. Но об этом как-нибудь в в другой раз. Возможно сделаю парочку статей на эту тему, но ничего не обещаю.
При этом WebGPU не является калькой вулкана, а чем-то новым…
Что очень хорошо. У меня нет большого опыта работы с разными графическими API, но недавно решил попробовать написать compute shader для своих задач. 30 вызовов API, 15 структур данных. Всё чертовски сложно друг с другом переплетено. Я, честно, не смог смог исправить готовое демо так, чтобы сделать от, что мне нужно. Надеюсь WebGPU будет существенно проще.
Но ведь WebGPU — это не новый графический API для всех, он разрабатывается с фокусом на браузеры (хотя по комментарию lain8dono у меня сложилось ощущение, что это не совсем так).
А мне, как разработчику кросс-платформенных приложений от появления ещё одного Direct3D, Metal, Mantle для конкретной платформы вместо кросс-платформенного стандарта или адаптации оного (как WebGL близок к OpenGL ES) никак не поможет.
Чисто технически все реализации WebGPU суть обёртки над существующими GAPI. Да ещё и реализации сделаны таким образом, что их вполне можно использовать вне браузерной тусовки. И получилось так хорошо, что это всё имеет смысл использовать в отрыве от браузеров.
При этом всём только команда от Mozilla (wgpu) ставит своей целью реализацию поверх всех GAPI, с которыми это вообще имеет смысл.
![](https://habrastorage.org/webt/ha/k9/qv/hak9qv0bki8dkzrs4g8v1yjf7s8.png)
На текущий момент wgpu-rs уже достаточно хорош для использования в небольших проектах.
разрабатывается с фокусом на браузеры
Для тех, кто использует wgpu-rs, это означает наличие браузеров в качестве ещё одной платформы. Когда-нибудь даже и не в ночных сборках.
В любом случае для вас это скорее хорошая новость. В конце концов браузерам требуется кросс-платформенность, а это ваша цель.
Надеюсь WebGPU будет существенно проще.
Пример с compute там рядом лежит. https://github.com/gfx-rs/wgpu-rs/tree/master/examples/hello-compute
Вот только что насчет качества реализации? Пробую сделать AR модель для андроид и иос, на последней все отлично, а на андроид нет. Вот так получается: модель под android, ios, desktop chrome
Тут просто беда с прозрачностью замкнутой поверхности (резервуар) — не разобрать, где скважины внутри и где снаружи, а если делаю полупрозрачной не замкнутую поверхность (рельеф), эта поверхность начинает бликовать, хотя металлический эффект полностью отключен. Или я что-то не так делаю, или реализация от гугла не рабочая для всего, кроме интернет магазинов...
На Android разработчиков GPU прорва и у каждого свои драйвера со своими багами. Первые реализации OpenGL ES 3.0 на Android были жутко глючными, но со временем подтянули уровень. А на iOS — один вендор и только одно поведение.
Разумеется, чем меньше разнообразия, тем проще вести разработку (баги / не баги — не так важно, когда всё знакомо и ожидаемо, то и баги могут стать родными). Но и общаться с одним единственным вендором сложнее — он начинает диктовать свои условия, а замечания, что у него что-то не так просто игнорировать.
Есть ли жизнь без WebGL 2.0?