Add WebGPU Backend w/ Dawn Support (#3838)

This commit is contained in:
Birk Skyum
2025-10-16 20:35:33 +02:00
committed by GitHub
parent cd129e873a
commit 68459732be
119 changed files with 14067 additions and 28 deletions

View File

@@ -14,11 +14,14 @@ public:
OpenGL, ///< The OpenGL API backend
Metal, ///< The Metal API backend
Vulkan, ///< The Vulkan API backend
WebGPU, ///< The WebGPU API backend
TYPE_MAX, ///< Not a valid backend type, used to determine the number
///< of available backends (ie for array allocation).
};
#if MLN_RENDER_BACKEND_METAL
#if MLN_RENDER_BACKEND_WEBGPU
static constexpr Type DefaultType = Type::WebGPU;
#elif MLN_RENDER_BACKEND_METAL
static constexpr Type DefaultType = Type::Metal;
#elif MLN_RENDER_BACKEND_VULKAN
static constexpr Type DefaultType = Type::Vulkan;
@@ -39,7 +42,9 @@ public:
template <typename T, typename... Args>
static std::unique_ptr<T> Create(Args... args) {
#if MLN_RENDER_BACKEND_METAL
#if MLN_RENDER_BACKEND_WEBGPU
return Create<Type::WebGPU, T, Args...>(std::forward<Args>(args)...);
#elif MLN_RENDER_BACKEND_METAL
return Create<Type::Metal, T, Args...>(std::forward<Args>(args)...);
#elif MLN_RENDER_BACKEND_VULKAN
return Create<Type::Vulkan, T, Args...>(std::forward<Args>(args)...);

View File

@@ -75,7 +75,8 @@ private:
std::mutex mutex;
};
#define MLN_DEFER_UPLOAD_ON_RENDER_THREAD (MLN_RENDER_BACKEND_OPENGL || MLN_RENDER_BACKEND_VULKAN)
#define MLN_DEFER_UPLOAD_ON_RENDER_THREAD \
(MLN_RENDER_BACKEND_OPENGL || MLN_RENDER_BACKEND_VULKAN || MLN_RENDER_BACKEND_WEBGPU)
} // namespace gfx
} // namespace mbgl

View File

@@ -42,6 +42,10 @@ public:
return static_cast<T&>(*resource);
}
bool hasResource() const { return resource != nullptr; }
void setResource(std::unique_ptr<RenderableResource> resource_) { resource = std::move(resource_); }
virtual void wait() {}
bool operator!=(const Renderable& other) const { return resource.get() != other.resource.get(); }

View File

@@ -65,6 +65,8 @@ enum {
idGlobalUBOIndex,
#elif MLN_RENDER_BACKEND_VULKAN
idGlobalPlatformParamsUBO,
#elif MLN_RENDER_BACKEND_WEBGPU
idGlobalUBOIndex,
#endif
globalUBOCount
};
@@ -75,7 +77,7 @@ enum {
drawableReservedUBOCount
};
#define MLN_UBO_CONSOLIDATION (MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_VULKAN)
#define MLN_UBO_CONSOLIDATION (MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_VULKAN || MLN_RENDER_BACKEND_WEBGPU)
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,230 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/background_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::BackgroundShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "BackgroundShader";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
};
struct BackgroundDrawableUBO {
matrix: mat4x4<f32>,
};
struct BackgroundDrawableUnionUBO {
matrix_col0: vec4<f32>,
matrix_col1: vec4<f32>,
matrix_col2: vec4<f32>,
matrix_col3: vec4<f32>,
extra0: vec4<f32>,
extra1: vec4<f32>,
};
struct BackgroundPropsUBO {
color: vec4<f32>,
opacity: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<BackgroundDrawableUnionUBO>;
@group(0) @binding(4) var<uniform> props: BackgroundPropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let matrix = mat4x4<f32>(drawable.matrix_col0,
drawable.matrix_col1,
drawable.matrix_col2,
drawable.matrix_col3);
let clip = matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
out.position = clip;
return out;
}
)";
static constexpr auto fragment = R"(
struct BackgroundPropsUBO {
color: vec4<f32>,
opacity: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
@group(0) @binding(4) var<uniform> props: BackgroundPropsUBO;
@fragment
fn main() -> @location(0) vec4<f32> {
return props.color * props.opacity;
}
)";
};
template <>
struct ShaderSource<BuiltIn::BackgroundPatternShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "BackgroundPatternShader";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) pos_a: vec2<f32>,
@location(1) pos_b: vec2<f32>,
};
struct BackgroundPatternDrawableUnionUBO {
matrix_col0: vec4<f32>,
matrix_col1: vec4<f32>,
matrix_col2: vec4<f32>,
matrix_col3: vec4<f32>,
pixel_coord_upper: vec2<f32>,
pixel_coord_lower: vec2<f32>,
tile_units_to_pixels: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
struct BackgroundPatternPropsUBO {
pattern_tl_a: vec2<f32>,
pattern_br_a: vec2<f32>,
pattern_tl_b: vec2<f32>,
pattern_br_b: vec2<f32>,
pattern_size_a: vec2<f32>,
pattern_size_b: vec2<f32>,
scale_a: f32,
scale_b: f32,
mix: f32,
opacity: f32,
pad1: f32,
pad2: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<BackgroundPatternDrawableUnionUBO>;
@group(0) @binding(4) var<uniform> props: BackgroundPatternPropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let matrix = mat4x4<f32>(drawable.matrix_col0,
drawable.matrix_col1,
drawable.matrix_col2,
drawable.matrix_col3);
let pos = vec2<f32>(f32(in.position.x), f32(in.position.y));
// Use get_pattern_pos helper function for pattern positioning
out.pos_a = get_pattern_pos(
drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
props.scale_a * props.pattern_size_a,
drawable.tile_units_to_pixels,
pos
);
out.pos_b = get_pattern_pos(
drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
props.scale_b * props.pattern_size_b,
drawable.tile_units_to_pixels,
pos
);
let clip = matrix * vec4<f32>(pos, 0.0, 1.0);
out.position = clip;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) pos_a: vec2<f32>,
@location(1) pos_b: vec2<f32>,
};
struct BackgroundPatternPropsUBO {
pattern_tl_a: vec2<f32>,
pattern_br_a: vec2<f32>,
pattern_tl_b: vec2<f32>,
pattern_br_b: vec2<f32>,
pattern_size_a: vec2<f32>,
pattern_size_b: vec2<f32>,
scale_a: f32,
scale_b: f32,
mix: f32,
opacity: f32,
pad1: f32,
pad2: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
// other fields...
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(4) var<uniform> props: BackgroundPatternPropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var pattern_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let texsize = paintParams.pattern_atlas_texsize;
// Sample pattern A
let imagecoord_a = gl_mod(in.pos_a, vec2<f32>(1.0));
let pos_a = mix(props.pattern_tl_a / texsize, props.pattern_br_a / texsize, imagecoord_a);
let color_a = textureSample(pattern_texture, texture_sampler, pos_a);
// Sample pattern B
let imagecoord_b = gl_mod(in.pos_b, vec2<f32>(1.0));
let pos_b = mix(props.pattern_tl_b / texsize, props.pattern_br_b / texsize, imagecoord_b);
let color_b = textureSample(pattern_texture, texture_sampler, pos_b);
// Mix patterns and apply opacity
return mix(color_a, color_b, props.mix) * props.opacity;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,211 @@
#pragma once
#include <mbgl/shaders/circle_layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::CircleShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "CircleShader";
static const std::array<AttributeInfo, 8> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto prelude = R"(
struct CircleDrawableUBO {
matrix: mat4x4<f32>,
extrude_scale: vec2<f32>,
color_t: f32,
radius_t: f32,
blur_t: f32,
opacity_t: f32,
stroke_color_t: f32,
stroke_width_t: f32,
stroke_opacity_t: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
struct CircleEvaluatedPropsUBO {
color: vec4<f32>,
stroke_color: vec4<f32>,
radius: f32,
blur: f32,
opacity: f32,
stroke_width: f32,
stroke_opacity: f32,
scale_with_map: i32,
pitch_with_map: i32,
pad1: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<CircleDrawableUBO>;
@group(0) @binding(4) var<uniform> props: CircleEvaluatedPropsUBO;
)";
static constexpr auto vertex = R"(
struct VertexInput {
@location(0) position: vec2<i32>,
@location(1) color: vec4<f32>,
@location(2) radius: vec2<f32>,
@location(3) blur: vec2<f32>,
@location(4) opacity: vec2<f32>,
@location(5) stroke_color: vec4<f32>,
@location(6) stroke_width: vec2<f32>,
@location(7) stroke_opacity: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) extrude: vec2<f32>,
@location(1) circle_data: vec4<f32>,
@location(2) color: vec4<f32>,
@location(3) stroke_color: vec4<f32>,
@location(4) stroke_data: vec2<f32>,
};
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let scale_with_map = props.scale_with_map != 0;
let pitch_with_map = props.pitch_with_map != 0;
let pos_f = vec2<f32>(f32(in.position.x), f32(in.position.y));
let extrude = glMod2v(pos_f, vec2<f32>(2.0, 2.0)) * 2.0 - vec2<f32>(1.0, 1.0);
let scaled_extrude = extrude * drawable.extrude_scale;
let circle_center = floor(pos_f * 0.5);
var color = props.color;
#ifndef HAS_UNIFORM_u_color
color = unpack_mix_color(in.color, drawable.color_t);
#endif
var radius = props.radius;
#ifndef HAS_UNIFORM_u_radius
radius = unpack_mix_float(in.radius, drawable.radius_t);
#endif
var blur = props.blur;
#ifndef HAS_UNIFORM_u_blur
blur = unpack_mix_float(in.blur, drawable.blur_t);
#endif
var opacity = props.opacity;
#ifndef HAS_UNIFORM_u_opacity
opacity = unpack_mix_float(in.opacity, drawable.opacity_t);
#endif
var stroke_color = props.stroke_color;
#ifndef HAS_UNIFORM_u_stroke_color
stroke_color = unpack_mix_color(in.stroke_color, drawable.stroke_color_t);
#endif
var stroke_width = props.stroke_width;
#ifndef HAS_UNIFORM_u_stroke_width
stroke_width = unpack_mix_float(in.stroke_width, drawable.stroke_width_t);
#endif
var stroke_opacity = props.stroke_opacity;
#ifndef HAS_UNIFORM_u_stroke_opacity
stroke_opacity = unpack_mix_float(in.stroke_opacity, drawable.stroke_opacity_t);
#endif
let radius_with_stroke = radius + stroke_width;
var position: vec4<f32>;
if (pitch_with_map) {
var corner_position = circle_center;
if (scale_with_map) {
corner_position += scaled_extrude * radius_with_stroke;
} else {
let projected_center = drawable.matrix * vec4<f32>(circle_center, 0.0, 1.0);
corner_position += scaled_extrude * radius_with_stroke *
(projected_center.w / paintParams.camera_to_center_distance);
}
position = drawable.matrix * vec4<f32>(corner_position, 0.0, 1.0);
} else {
position = drawable.matrix * vec4<f32>(circle_center, 0.0, 1.0);
var factor = position.w;
if (scale_with_map) {
factor = paintParams.camera_to_center_distance;
}
let delta = scaled_extrude * radius_with_stroke * factor;
position = vec4<f32>(position.x + delta.x, position.y + delta.y, position.z, position.w);
}
let antialiasblur = 1.0 / max(paintParams.pixel_ratio * radius_with_stroke, 1e-6);
out.position = position;
out.extrude = extrude;
out.circle_data = vec4<f32>(antialiasblur, radius, blur, opacity);
out.color = color;
out.stroke_color = stroke_color;
out.stroke_data = vec2<f32>(stroke_width, stroke_opacity);
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) extrude: vec2<f32>,
@location(1) circle_data: vec4<f32>,
@location(2) color: vec4<f32>,
@location(3) stroke_color: vec4<f32>,
@location(4) stroke_data: vec2<f32>,
};
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let extrude_length = length(in.extrude);
let antialiasblur = in.circle_data.x;
let radius = in.circle_data.y;
let blur = in.circle_data.z;
let opacity = in.circle_data.w;
let stroke_width = in.stroke_data.x;
let stroke_opacity = in.stroke_data.y;
let antialiased_blur = -max(blur, antialiasblur);
let opacity_t = smoothstep(0.0, antialiased_blur, extrude_length - 1.0);
var color_t: f32;
if (stroke_width < 0.01) {
color_t = 0.0;
} else {
color_t = smoothstep(antialiased_blur, 0.0, extrude_length - radius / (radius + stroke_width));
}
let final_color = mix(in.color * opacity, in.stroke_color * stroke_opacity, color_t);
return opacity_t * final_color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,65 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <array>
#include <cstdint>
namespace mbgl {
namespace shaders {
struct alignas(16) ClipUBO {
std::array<float, 4 * 4> matrix;
std::uint32_t stencil_ref;
float pad1;
float pad2;
float pad3;
};
static_assert(sizeof(ClipUBO) == 5 * 16, "wrong size");
template <>
struct ShaderSource<BuiltIn::ClippingMaskProgram, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "ClippingMaskProgram";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(2) position: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
};
struct ClipUBO {
matrix: mat4x4<f32>,
stencil_ref: u32,
pad1: f32,
pad2: f32,
pad3: f32,
};
@group(0) @binding(0) var<uniform> clip: ClipUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let clip_pos = clip.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
out.position = vec4<f32>(clip_pos.x, clip_pos.y, clip_pos.z, clip_pos.w);
return out;
}
)";
static constexpr auto fragment = R"(
@fragment
fn main() -> @location(0) vec4<f32> {
return vec4<f32>(0.0, 0.0, 0.0, 0.0);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,240 @@
#pragma once
#include <mbgl/shaders/collision_layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::CollisionBoxShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "CollisionBoxShader";
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) anchor_position: vec2<i32>,
@location(5) extrude: vec2<i32>,
@location(6) placed: vec2<u32>,
@location(7) shift: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) placed: f32,
@location(1) not_used: f32,
};
struct CollisionDrawableUBO {
matrix: mat4x4<f32>,
};
struct CollisionTilePropsUBO {
extrude_scale: vec2<f32>,
overscale_factor: f32,
pad1: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(2) var<uniform> drawable: CollisionDrawableUBO;
@group(0) @binding(4) var<uniform> tile_props: CollisionTilePropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let projected_point = drawable.matrix * vec4<f32>(f32(in.anchor_position.x), f32(in.anchor_position.y), 0.0, 1.0);
let camera_to_anchor_distance = projected_point.w;
let collision_perspective_ratio = clamp(
0.5 + 0.5 * (paintParams.camera_to_center_distance / camera_to_anchor_distance),
0.0,
4.0);
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
let extrude_shift = vec2<f32>(f32(in.extrude.x), f32(in.extrude.y)) + in.shift;
out.position.x += extrude_shift.x * tile_props.extrude_scale.x * out.position.w * collision_perspective_ratio;
out.position.y += extrude_shift.y * tile_props.extrude_scale.y * out.position.w * collision_perspective_ratio;
out.placed = f32(in.placed.x);
out.not_used = f32(in.placed.y);
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) placed: f32,
@location(1) not_used: f32,
};
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let alpha = 0.5;
// Red = collision, hide label
var color = vec4<f32>(1.0, 0.0, 0.0, 1.0) * alpha;
// Blue = no collision, label is showing
if (in.placed > 0.5) {
color = vec4<f32>(0.0, 0.0, 1.0, 0.5) * alpha;
}
if (in.not_used > 0.5) {
// This box not used, fade it out
color = color * 0.1;
}
return color;
}
)";
};
template <>
struct ShaderSource<BuiltIn::CollisionCircleShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "CollisionCircleShader";
static const std::array<AttributeInfo, 4> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) anchor_position: vec2<i32>,
@location(5) extrude: vec2<i32>,
@location(6) placed: vec2<u32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) placed: f32,
@location(1) not_used: f32,
@location(2) radius: f32,
@location(3) extrude: vec2<f32>,
@location(4) extrude_scale: vec2<f32>,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct CollisionDrawableUBO {
matrix: mat4x4<f32>,
};
struct CollisionTilePropsUBO {
extrude_scale: vec2<f32>,
overscale_factor: f32,
pad1: f32,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(2) var<uniform> drawable: CollisionDrawableUBO;
@group(0) @binding(4) var<uniform> tile_props: CollisionTilePropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let projected_point = drawable.matrix * vec4<f32>(f32(in.anchor_position.x), f32(in.anchor_position.y), 0.0, 1.0);
let camera_to_anchor_distance = projected_point.w;
let perspective = max(camera_to_anchor_distance, 1e-6);
let collision_perspective_ratio = clamp(
0.5 + 0.5 * (paintParams.camera_to_center_distance / perspective),
0.0,
4.0);
var position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
let padding_factor = 1.2;
let extrude_vec = vec2<f32>(f32(in.extrude.x), f32(in.extrude.y));
position.x += extrude_vec.x * tile_props.extrude_scale.x * padding_factor * position.w * collision_perspective_ratio;
position.y += extrude_vec.y * tile_props.extrude_scale.y * padding_factor * position.w * collision_perspective_ratio;
out.position = position;
out.placed = f32(in.placed.x);
out.not_used = f32(in.placed.y);
out.radius = abs(f32(in.extrude.y));
out.extrude = extrude_vec * padding_factor;
out.extrude_scale = tile_props.extrude_scale * paintParams.camera_to_center_distance * collision_perspective_ratio;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) placed: f32,
@location(1) not_used: f32,
@location(2) radius: f32,
@location(3) extrude: vec2<f32>,
@location(4) extrude_scale: vec2<f32>,
};
struct CollisionTilePropsUBO {
extrude_scale: vec2<f32>,
overscale_factor: f32,
pad1: f32,
};
@group(0) @binding(4) var<uniform> tile_props: CollisionTilePropsUBO;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let alpha = 0.5;
// Red = collision, hide label
var color = vec4<f32>(1.0, 0.0, 0.0, 1.0) * alpha;
// Blue = no collision, label is showing
if (in.placed > 0.5) {
color = vec4<f32>(0.0, 0.0, 1.0, 0.5) * alpha;
}
if (in.not_used > 0.5) {
color = color * 0.2;
}
// Add circle outline effect
let extrude_scale_length = max(length(in.extrude_scale), 1e-6);
let extrude_length = length(in.extrude) * extrude_scale_length;
let overscale = max(tile_props.overscale_factor, 1.0);
let stroke_width = 15.0 * extrude_scale_length / overscale;
let radius = in.radius * extrude_scale_length;
let distance_to_edge = abs(extrude_length - radius);
let opacity_t = smoothstep(-stroke_width, 0.0, -distance_to_edge);
return opacity_t * color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,90 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
namespace mbgl {
namespace shaders {
// The enum values are already defined in layer_ubo.hpp
// WebGPU uses the same indices as Metal
template <>
struct ShaderSource<BuiltIn::Prelude, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "Prelude";
// Common WGSL code that can be included in other shaders
static constexpr auto prelude = R"(
// Constants
const PI: f32 = 3.14159265358979323846;
const LINE_NORMAL_SCALE: f32 = 1.0 / 63.0; // 1.0 / (127 / 2)
const MAX_LINE_DISTANCE: f32 = 32767.0;
const SDF_PX: f32 = 8.0;
// Helper functions for unpacking data
fn unpack_float(packedValue: f32) -> vec2<f32> {
let packedIntValue = i32(packedValue);
let v0 = packedIntValue / 256;
return vec2<f32>(f32(v0), f32(packedIntValue - v0 * 256));
}
fn unpack_opacity(packedOpacity: f32) -> vec2<f32> {
let intOpacity = i32(packedOpacity) / 2;
return vec2<f32>(f32(intOpacity) / 127.0, packedOpacity % 2.0);
}
// Decode a color that has been packed into two floats
fn decode_color(encoded: vec2<f32>) -> vec4<f32> {
let e0 = unpack_float(encoded[0]) / 255.0;
let e1 = unpack_float(encoded[1]) / 255.0;
return vec4<f32>(e0.x, e0.y, e1.x, e1.y);
}
// Unpack and interpolate between two float values
fn unpack_mix_float(packedValue: vec2<f32>, t: f32) -> f32 {
return mix(packedValue[0], packedValue[1], t);
}
// Unpack and interpolate between two colors
fn unpack_mix_color(packedColors: vec4<f32>, t: f32) -> vec4<f32> {
let minColor = decode_color(vec2<f32>(packedColors[0], packedColors[1]));
let maxColor = decode_color(vec2<f32>(packedColors[2], packedColors[3]));
return mix(minColor, maxColor, t);
}
// Get pattern position for texture atlas lookups
fn get_pattern_pos(pixel_coord_upper: vec2<f32>, pixel_coord_lower: vec2<f32>,
pattern_size: vec2<f32>, tile_units_to_pixels: f32, pos: vec2<f32>) -> vec2<f32> {
let offset_a = (pixel_coord_upper % pattern_size) * 256.0;
let offset_b = (offset_a % pattern_size) * 256.0 + pixel_coord_lower;
let offset = offset_b % pattern_size;
return (tile_units_to_pixels * pos + offset) / pattern_size;
}
// Radians conversion
fn radians(degrees: f32) -> f32 {
return PI * degrees / 180.0;
}
// WebGPU equivalent of GLSL mod function
fn glMod(x: f32, y: f32) -> f32 {
return x - y * floor(x / y);
}
fn glMod2(x: vec2<f32>, y: f32) -> vec2<f32> {
return x - y * floor(x / y);
}
fn glMod2v(x: vec2<f32>, y: vec2<f32>) -> vec2<f32> {
return x - y * floor(x / y);
}
// GLSL compatibility helpers
fn gl_mod(x: vec2<f32>, y: vec2<f32>) -> vec2<f32> {
return glMod2v(x, y);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,66 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/custom_geometry_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::CustomGeometryShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "CustomGeometryShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec3<f32>,
@location(4) texcoord: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
};
struct CustomDrawableUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> drawable: CustomDrawableUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.uv = in.texcoord;
out.position = drawable.matrix * vec4<f32>(in.position, 1.0);
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) uv: vec2<f32>,
};
struct CustomDrawableUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> drawable: CustomDrawableUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var texture_image: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
return textureSample(texture_image, texture_sampler, in.uv) * drawable.color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,68 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/custom_drawable_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "CustomSymbolIconShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(4) position: vec3<f32>,
@location(5) texcoord: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
};
struct SymbolUBO {
matrix: mat4x4<f32>,
opacity: f32,
};
@group(0) @binding(0) var<uniform> symbol_ubo: SymbolUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = symbol_ubo.matrix * vec4<f32>(in.position, 1.0);
out.uv = in.texcoord;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) uv: vec2<f32>,
};
struct SymbolUBO {
matrix: mat4x4<f32>,
opacity: f32,
};
@group(0) @binding(0) var<uniform> symbol_ubo: SymbolUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var texture_image: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
var color = textureSample(texture_image, texture_sampler, in.uv);
color.a = color.a * symbol_ubo.opacity;
return color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,80 @@
#pragma once
#include <mbgl/shaders/debug_layer_ubo.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::DebugShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "DebugShader";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
};
struct DebugUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
overlay_scale: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
@group(0) @binding(0) var<uniform> debug: DebugUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let scaled_pos = vec2<f32>(f32(in.position.x), f32(in.position.y)) * debug.overlay_scale;
out.position = debug.matrix * vec4<f32>(scaled_pos, 0.0, 1.0);
// This vertex shader expects a EXTENT x EXTENT quad,
// The UV co-ordinates for the overlay texture can be calculated using that knowledge
out.uv = vec2<f32>(f32(in.position.x), f32(in.position.y)) / 8192.0;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) uv: vec2<f32>,
};
struct DebugUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
overlay_scale: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
@group(0) @binding(0) var<uniform> debug: DebugUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var overlay_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let overlay_color = textureSample(overlay_texture, texture_sampler, in.uv);
return mix(debug.color, overlay_color, overlay_color.a);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,910 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/fill_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillShader";
static const std::array<AttributeInfo, 3> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr const char* vertex = R"(
struct VertexInput {
@location(0) position: vec2<i32>,
#ifndef HAS_UNIFORM_u_color
@location(1) color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(2) opacity: vec2<f32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) opacity: f32,
};
struct FillDrawableUBO {
matrix: mat4x4<f32>,
color_t: f32,
opacity_t: f32,
pad1: f32,
pad2: f32,
};
struct FillDrawableUnionUBO {
fill: FillDrawableUBO,
padding: vec4<f32>,
};
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<FillDrawableUnionUBO>;
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
// Transform position using the matrix
let drawable = drawableVector[globalIndex.value].fill;
let clip = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
out.position = clip;
var color: vec4<f32>;
#ifdef HAS_UNIFORM_u_color
color = props.color;
#else
color = unpack_mix_color(in.color, drawable.color_t);
#endif
var opacity: f32;
#ifdef HAS_UNIFORM_u_opacity
opacity = props.opacity;
#else
opacity = unpack_mix_float(in.opacity, drawable.opacity_t);
#endif
out.color = color;
out.opacity = opacity;
return out;
}
)";
static constexpr const char* fragment = R"(
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
struct FragmentInput {
@location(0) color: vec4<f32>,
@location(1) opacity: f32,
};
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
var color: vec4<f32>;
#ifdef HAS_UNIFORM_u_color
color = props.color;
#else
color = in.color;
#endif
var opacity: f32;
#ifdef HAS_UNIFORM_u_opacity
opacity = props.opacity;
#else
opacity = in.opacity;
#endif
return color * opacity;
}
)";
};
template <>
struct ShaderSource<BuiltIn::FillOutlineShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillOutlineShader";
static const std::array<AttributeInfo, 3> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr const char* vertex = R"(
struct VertexInput {
@location(0) position: vec2<i32>,
#ifndef HAS_UNIFORM_u_outline_color
@location(1) outline_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(2) opacity: vec2<f32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) opacity: f32,
@location(2) pos: vec2<f32>,
};
struct FillOutlineDrawableUBO {
matrix: mat4x4<f32>,
outline_color_t: f32,
opacity_t: f32,
pad1: f32,
pad2: f32,
};
struct FillOutlineDrawableUnionUBO {
fill: FillOutlineDrawableUBO,
padding: vec4<f32>,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct FillOutlineEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<FillOutlineDrawableUnionUBO>;
@group(0) @binding(5) var<uniform> props: FillOutlineEvaluatedPropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
// Transform position using the matrix
let drawable = drawableVector[globalIndex.value].fill;
let clip = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
let invW = 1.0 / clip.w;
let ndcXY = clip.xy * invW;
out.position = clip;
out.pos = (ndcXY + vec2<f32>(1.0)) * 0.5 * paintParams.world_size;
var color: vec4<f32>;
#ifdef HAS_UNIFORM_u_outline_color
color = props.outline_color;
#else
color = unpack_mix_color(in.outline_color, drawable.outline_color_t);
#endif
var opacity: f32;
#ifdef HAS_UNIFORM_u_opacity
opacity = props.opacity;
#else
opacity = unpack_mix_float(in.opacity, drawable.opacity_t);
#endif
out.color = color;
out.opacity = opacity;
return out;
}
)";
static constexpr const char* fragment = R"(
struct FillOutlineEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
@group(0) @binding(5) var<uniform> props: FillOutlineEvaluatedPropsUBO;
struct FragmentInput {
@location(0) color: vec4<f32>,
@location(1) opacity: f32,
@location(2) pos: vec2<f32>,
};
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
var color: vec4<f32>;
#ifdef HAS_UNIFORM_u_outline_color
color = props.outline_color;
#else
color = in.color;
#endif
var opacity: f32;
#ifdef HAS_UNIFORM_u_opacity
opacity = props.opacity;
#else
opacity = in.opacity;
#endif
return color * opacity;
}
)";
};
template <>
struct ShaderSource<BuiltIn::FillPatternShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillPatternShader";
static const std::array<AttributeInfo, 4> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr const char* vertex = R"(
struct VertexInput {
@location(4) position: vec2<i32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(5) pattern_from: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(6) pattern_to: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(7) opacity: vec2<f32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) v_pos_a: vec2<f32>,
@location(1) v_pos_b: vec2<f32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(2) pattern_from: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(3) pattern_to: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(4) opacity: f32,
#endif
};
struct FillPatternDrawableUBO {
matrix: mat4x4<f32>,
pixel_coord_upper: vec2<f32>,
pixel_coord_lower: vec2<f32>,
tile_ratio: f32,
pattern_from_t: f32,
pattern_to_t: f32,
opacity_t: f32,
};
struct FillPatternTilePropsUBO {
pattern_from: vec4<f32>,
pattern_to: vec4<f32>,
texsize: vec2<f32>,
pad1: f32,
pad2: f32,
};
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<FillPatternDrawableUBO>;
@group(0) @binding(4) var<storage, read> tilePropsVector: array<FillPatternTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let index = globalIndex.value;
let drawable = drawableVector[index];
let tileProps = tilePropsVector[index];
var pattern_from: vec4<f32>;
#ifdef HAS_UNIFORM_u_pattern_from
pattern_from = tileProps.pattern_from;
#else
pattern_from = vec4<f32>(in.pattern_from);
#endif
var pattern_to: vec4<f32>;
#ifdef HAS_UNIFORM_u_pattern_to
pattern_to = tileProps.pattern_to;
#else
pattern_to = vec4<f32>(in.pattern_to);
#endif
let pattern_tl_a = pattern_from.xy;
let pattern_br_a = pattern_from.zw;
let pattern_tl_b = pattern_to.xy;
let pattern_br_b = pattern_to.zw;
let pixelRatio = paintParams.pixel_ratio;
let tileZoomRatio = drawable.tile_ratio;
let fromScale = props.from_scale;
let toScale = props.to_scale;
let display_size_a = vec2<f32>(
(pattern_br_a.x - pattern_tl_a.x) / pixelRatio,
(pattern_br_a.y - pattern_tl_a.y) / pixelRatio
);
let display_size_b = vec2<f32>(
(pattern_br_b.x - pattern_tl_b.x) / pixelRatio,
(pattern_br_b.y - pattern_tl_b.y) / pixelRatio
);
let pos = vec2<f32>(f32(in.position.x), f32(in.position.y));
let clip = drawable.matrix * vec4<f32>(pos, 0.0, 1.0);
out.position = clip;
out.v_pos_a = get_pattern_pos(
drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
fromScale * display_size_a,
tileZoomRatio,
pos
);
out.v_pos_b = get_pattern_pos(
drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
toScale * display_size_b,
tileZoomRatio,
pos
);
#ifndef HAS_UNIFORM_u_pattern_from
out.pattern_from = pattern_from;
#endif
#ifndef HAS_UNIFORM_u_pattern_to
out.pattern_to = pattern_to;
#endif
#ifndef HAS_UNIFORM_u_opacity
out.opacity = unpack_mix_float(in.opacity, drawable.opacity_t);
#endif
return out;
}
)";
static constexpr const char* fragment = R"(
struct FragmentInput {
@location(0) v_pos_a: vec2<f32>,
@location(1) v_pos_b: vec2<f32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(2) pattern_from: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(3) pattern_to: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(4) opacity: f32,
#endif
};
struct FillPatternTilePropsUBO {
pattern_from: vec4<f32>,
pattern_to: vec4<f32>,
texsize: vec2<f32>,
pad1: f32,
pad2: f32,
};
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(4) var<storage, read> tilePropsVector: array<FillPatternTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var pattern_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
let pattern_from =
#ifdef HAS_UNIFORM_u_pattern_from
tileProps.pattern_from;
#else
in.pattern_from;
#endif
let pattern_to =
#ifdef HAS_UNIFORM_u_pattern_to
tileProps.pattern_to;
#else
in.pattern_to;
#endif
let pattern_tl_a = pattern_from.xy;
let pattern_br_a = pattern_from.zw;
let pattern_tl_b = pattern_to.xy;
let pattern_br_b = pattern_to.zw;
// Sample pattern A
let imagecoord_a = gl_mod(in.v_pos_a, vec2<f32>(1.0));
let pos_a = mix(pattern_tl_a / tileProps.texsize, pattern_br_a / tileProps.texsize, imagecoord_a);
let color_a = textureSample(pattern_texture, texture_sampler, pos_a);
// Sample pattern B
let imagecoord_b = gl_mod(in.v_pos_b, vec2<f32>(1.0));
let pos_b = mix(pattern_tl_b / tileProps.texsize, pattern_br_b / tileProps.texsize, imagecoord_b);
let color_b = textureSample(pattern_texture, texture_sampler, pos_b);
// Mix patterns and apply opacity
let opacity =
#ifdef HAS_UNIFORM_u_opacity
props.opacity;
#else
in.opacity;
#endif
return mix(color_a, color_b, props.fade) * opacity;
}
)";
};
template <>
struct ShaderSource<BuiltIn::FillOutlinePatternShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillOutlinePatternShader";
static const std::array<AttributeInfo, 4> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr const char* vertex = R"(
struct VertexInput {
@location(4) position: vec2<i32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(5) pattern_from: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(6) pattern_to: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(7) opacity: vec2<f32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) v_pos_a: vec2<f32>,
@location(1) v_pos_b: vec2<f32>,
@location(2) v_pos: vec2<f32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(3) pattern_from: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(4) pattern_to: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(5) opacity: f32,
#endif
};
struct FillOutlinePatternDrawableUBO {
matrix: mat4x4<f32>,
pixel_coord_upper: vec2<f32>,
pixel_coord_lower: vec2<f32>,
tile_ratio: f32,
pattern_from_t: f32,
pattern_to_t: f32,
opacity_t: f32,
};
struct FillOutlinePatternTilePropsUBO {
pattern_from: vec4<f32>,
pattern_to: vec4<f32>,
texsize: vec2<f32>,
pad1: f32,
pad2: f32,
};
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<FillOutlinePatternDrawableUBO>;
@group(0) @binding(4) var<storage, read> tilePropsVector: array<FillOutlinePatternTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let index = globalIndex.value;
let drawable = drawableVector[index];
let tileProps = tilePropsVector[index];
var pattern_from: vec4<f32>;
#ifdef HAS_UNIFORM_u_pattern_from
pattern_from = tileProps.pattern_from;
#else
pattern_from = vec4<f32>(in.pattern_from);
#endif
var pattern_to: vec4<f32>;
#ifdef HAS_UNIFORM_u_pattern_to
pattern_to = tileProps.pattern_to;
#else
pattern_to = vec4<f32>(in.pattern_to);
#endif
let pattern_tl_a = pattern_from.xy;
let pattern_br_a = pattern_from.zw;
let pattern_tl_b = pattern_to.xy;
let pattern_br_b = pattern_to.zw;
let pixelRatio = paintParams.pixel_ratio;
let tileZoomRatio = drawable.tile_ratio;
let fromScale = props.from_scale;
let toScale = props.to_scale;
let display_size_a = vec2<f32>(
(pattern_br_a.x - pattern_tl_a.x) / pixelRatio,
(pattern_br_a.y - pattern_tl_a.y) / pixelRatio
);
let display_size_b = vec2<f32>(
(pattern_br_b.x - pattern_tl_b.x) / pixelRatio,
(pattern_br_b.y - pattern_tl_b.y) / pixelRatio
);
let pos = vec2<f32>(f32(in.position.x), f32(in.position.y));
let clip = drawable.matrix * vec4<f32>(pos, 0.0, 1.0);
let invW = 1.0 / clip.w;
let ndcXY = clip.xy * invW;
out.position = clip;
out.v_pos_a = get_pattern_pos(
drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
fromScale * display_size_a,
tileZoomRatio,
pos
);
out.v_pos_b = get_pattern_pos(
drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
toScale * display_size_b,
tileZoomRatio,
pos
);
out.v_pos = (ndcXY + vec2<f32>(1.0, 1.0)) * 0.5 * paintParams.world_size;
#ifndef HAS_UNIFORM_u_pattern_from
out.pattern_from = pattern_from;
#endif
#ifndef HAS_UNIFORM_u_pattern_to
out.pattern_to = pattern_to;
#endif
#ifndef HAS_UNIFORM_u_opacity
out.opacity = unpack_mix_float(in.opacity, drawable.opacity_t);
#endif
return out;
}
)";
static constexpr const char* fragment = R"(
struct FragmentInput {
@location(0) v_pos_a: vec2<f32>,
@location(1) v_pos_b: vec2<f32>,
@location(2) v_pos: vec2<f32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(3) pattern_from: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(4) pattern_to: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(5) opacity: f32,
#endif
};
struct FillOutlinePatternTilePropsUBO {
pattern_from: vec4<f32>,
pattern_to: vec4<f32>,
texsize: vec2<f32>,
pad1: f32,
pad2: f32,
};
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(4) var<storage, read> tilePropsVector: array<FillOutlinePatternTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var pattern_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
let pattern_from =
#ifdef HAS_UNIFORM_u_pattern_from
tileProps.pattern_from;
#else
in.pattern_from;
#endif
let pattern_to =
#ifdef HAS_UNIFORM_u_pattern_to
tileProps.pattern_to;
#else
in.pattern_to;
#endif
let pattern_tl_a = pattern_from.xy;
let pattern_br_a = pattern_from.zw;
let pattern_tl_b = pattern_to.xy;
let pattern_br_b = pattern_to.zw;
// Sample pattern A
let imagecoord_a = gl_mod(in.v_pos_a, vec2<f32>(1.0));
let pos_a = mix(pattern_tl_a / tileProps.texsize, pattern_br_a / tileProps.texsize, imagecoord_a);
let color_a = textureSample(pattern_texture, texture_sampler, pos_a);
// Sample pattern B
let imagecoord_b = gl_mod(in.v_pos_b, vec2<f32>(1.0));
let pos_b = mix(pattern_tl_b / tileProps.texsize, pattern_br_b / tileProps.texsize, imagecoord_b);
let color_b = textureSample(pattern_texture, texture_sampler, pos_b);
// Mix patterns and apply opacity
let opacity =
#ifdef HAS_UNIFORM_u_opacity
props.opacity;
#else
in.opacity;
#endif
return mix(color_a, color_b, props.fade) * opacity;
}
)";
};
template <>
struct ShaderSource<BuiltIn::FillOutlineTriangulatedShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillOutlineTriangulatedShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr const char* vertex = R"(
const SCALE: f32 = 0.015873016;
struct VertexInput {
@location(0) pos_normal: vec2<i32>,
@location(1) data: vec4<u32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) normal: vec2<f32>,
@location(1) width: f32,
@location(2) gamma_scale: f32,
};
struct FillOutlineTriangulatedDrawableUnionUBO {
matrix_col0: vec4<f32>,
matrix_col1: vec4<f32>,
matrix_col2: vec4<f32>,
matrix_col3: vec4<f32>,
params: vec4<f32>,
padding: vec4<f32>,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<FillOutlineTriangulatedDrawableUnionUBO>;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let matrix = mat4x4<f32>(drawable.matrix_col0,
drawable.matrix_col1,
drawable.matrix_col2,
drawable.matrix_col3);
let ratio = max(drawable.params.x, 1e-6);
let pos_normal = vec2<f32>(f32(in.pos_normal.x), f32(in.pos_normal.y));
let pos = floor(pos_normal * 0.5);
var normal = pos_normal - pos * 2.0;
normal.y = normal.y * 2.0 - 1.0;
let data = vec4<f32>(in.data);
let extrude = data.xy - vec2<f32>(128.0, 128.0);
let half_width = 0.5;
let antialiasing = 0.5 / paintParams.pixel_ratio;
let outset = half_width + antialiasing;
let dist = extrude * outset * SCALE;
let extrude_vec = dist / ratio;
let projected_extrude = matrix * vec4<f32>(extrude_vec, 0.0, 0.0);
let base = matrix * vec4<f32>(pos, 0.0, 1.0);
let clip = base + projected_extrude;
out.position = clip;
let extrude_length_without_perspective = length(dist);
let extrude_length_with_perspective = length((projected_extrude.xy / clip.w) * paintParams.units_to_pixels);
let gamma_denom = max(extrude_length_with_perspective, 1e-6);
out.normal = normal;
out.width = outset;
out.gamma_scale = extrude_length_without_perspective / gamma_denom;
return out;
}
)";
static constexpr const char* fragment = R"(
struct FragmentInput {
@location(0) normal: vec2<f32>,
@location(1) width: f32,
@location(2) gamma_scale: f32,
};
struct FillEvaluatedPropsUBO {
color: vec4<f32>,
outline_color: vec4<f32>,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(5) var<uniform> props: FillEvaluatedPropsUBO;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let dist = length(in.normal) * in.width;
let blur2 = (1.0 / paintParams.pixel_ratio) * in.gamma_scale;
let denom = max(blur2, 1e-6);
let alpha = clamp(min(dist + blur2, in.width - dist) / denom, 0.0, 1.0);
return props.outline_color * (alpha * props.opacity);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,411 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/fill_extrusion_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillExtrusionShader";
static const std::array<AttributeInfo, 5> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) normal_ed: vec4<i32>,
#ifndef HAS_UNIFORM_u_color
@location(5) color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_base
@location(6) base: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_height
@location(7) height: vec2<f32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
};
struct FillExtrusionDrawableUBO {
matrix: mat4x4<f32>,
pixel_coord_upper: vec2<f32>,
pixel_coord_lower: vec2<f32>,
height_factor: f32,
tile_ratio: f32,
base_t: f32,
height_t: f32,
color_t: f32,
pattern_from_t: f32,
pattern_to_t: f32,
pad1: f32,
};
struct FillExtrusionPropsUBO {
color: vec4<f32>,
light_color_pad: vec4<f32>,
light_position_base: vec4<f32>,
height: f32,
light_intensity: f32,
vertical_gradient: f32,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
pad2: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(2) var<storage, read> drawableVector: array<FillExtrusionDrawableUBO>;
@group(0) @binding(5) var<uniform> props: FillExtrusionPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
#ifndef HAS_UNIFORM_u_base
let baseValue = max(unpack_mix_float(in.base, drawable.base_t), 0.0);
#else
let baseValue = max(props.light_position_base.w, 0.0);
#endif
#ifndef HAS_UNIFORM_u_height
let heightValue = max(unpack_mix_float(in.height, drawable.height_t), 0.0);
#else
let heightValue = max(props.height, 0.0);
#endif
let normal_i = vec3<f32>(f32(in.normal_ed.x), f32(in.normal_ed.y), f32(in.normal_ed.z));
let t = glMod(normal_i.x, 2.0);
let z = select(baseValue, heightValue, t != 0.0);
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), z, 1.0);
#ifdef OVERDRAW_INSPECTOR
out.color = vec4<f32>(1.0, 1.0, 1.0, 1.0);
return out;
#endif
#ifndef HAS_UNIFORM_u_color
var color = unpack_mix_color(in.color, drawable.color_t);
#else
var color = props.color;
#endif
color = color + min(vec4<f32>(0.03, 0.03, 0.03, 1.0), vec4<f32>(1.0));
let luminance = dot(color.rgb, vec3<f32>(0.2126, 0.7152, 0.0722));
let unitNormal = normal_i / 16384.0;
let directionalFraction = clamp(dot(unitNormal, props.light_position_base.xyz), 0.0, 1.0);
let minDirectional = 1.0 - props.light_intensity;
let maxDirectional = max(1.0 - luminance + props.light_intensity, 1.0);
var directional = mix(minDirectional, maxDirectional, directionalFraction);
if (normal_i.y != 0.0) {
let gradientMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
let factor = clamp((t + baseValue) * pow(heightValue / 150.0, 0.5), gradientMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + props.vertical_gradient * factor;
}
let lightColor = props.light_color_pad.xyz;
let minLight = mix(vec3<f32>(0.0), vec3<f32>(0.3), 1.0 - lightColor);
let lit = clamp(color.rgb * directional * lightColor, minLight, vec3<f32>(1.0));
var vcolor = vec4<f32>(0.0, 0.0, 0.0, 1.0);
vcolor += vec4<f32>(lit, 0.0);
out.color = vcolor * props.opacity;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) color: vec4<f32>,
};
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
return in.color;
}
)";
};
template <>
struct ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "FillExtrusionPatternShader";
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(3) position: vec2<i32>,
@location(4) normal_ed: vec4<i32>,
#ifndef HAS_UNIFORM_u_base
@location(5) base: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_height
@location(6) height: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_from
@location(7) pattern_from: vec4<u32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(8) pattern_to: vec4<u32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) lighting: vec4<f32>,
@location(1) pos_a: vec2<f32>,
@location(2) pos_b: vec2<f32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(3) pattern_from: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(4) pattern_to: vec4<f32>,
#endif
};
struct FillExtrusionPatternDrawableUBO {
matrix: mat4x4<f32>,
pixel_coord_upper: vec2<f32>,
pixel_coord_lower: vec2<f32>,
height_factor: f32,
tile_ratio: f32,
base_t: f32,
height_t: f32,
pattern_from_t: f32,
pattern_to_t: f32,
pad1: f32,
pad2: f32,
};
struct FillExtrusionPatternTilePropsUBO {
pattern_from: vec4<f32>,
pattern_to: vec4<f32>,
texsize: vec2<f32>,
pad1: f32,
pad2: f32,
};
struct FillExtrusionPropsUBO {
color: vec4<f32>,
light_color_pad: vec4<f32>,
light_position_base: vec4<f32>,
height: f32,
light_intensity: f32,
vertical_gradient: f32,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
pad2: f32,
};
struct GlobalPaintParamsUBO {
pixel_ratio: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<FillExtrusionPatternDrawableUBO>;
@group(0) @binding(4) var<storage, read> tilePropsVector: array<FillExtrusionPatternTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: FillExtrusionPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let index = globalIndex.value;
let drawable = drawableVector[index];
#ifndef HAS_UNIFORM_u_base
let baseValue = max(unpack_mix_float(in.base, drawable.base_t), 0.0);
#else
let baseValue = max(props.light_position_base.w, 0.0);
#endif
#ifndef HAS_UNIFORM_u_height
let heightValue = max(unpack_mix_float(in.height, drawable.height_t), 0.0);
#else
let heightValue = max(props.height, 0.0);
#endif
let normal_i = vec3<f32>(f32(in.normal_ed.x), f32(in.normal_ed.y), f32(in.normal_ed.z));
let edgedistance = f32(in.normal_ed.w);
let t = glMod(normal_i.x, 2.0);
let z = select(baseValue, heightValue, t != 0.0);
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), z, 1.0);
var patternPos: vec2<f32>;
if (normal_i.x == 1.0 && normal_i.y == 0.0 && normal_i.z == 16384.0) {
patternPos = vec2<f32>(f32(in.position.x), f32(in.position.y));
} else {
patternPos = vec2<f32>(edgedistance, z * drawable.height_factor);
}
let pixelRatio = paintParams.pixel_ratio;
let tileZoomRatio = drawable.tile_ratio;
let fromScale = props.from_scale;
let toScale = props.to_scale;
#ifdef HAS_UNIFORM_u_pattern_from
let patternFrom = tilePropsVector[index].pattern_from;
#else
let patternFrom = vec4<f32>(in.pattern_from);
out.pattern_from = patternFrom;
#endif
#ifdef HAS_UNIFORM_u_pattern_to
let patternTo = tilePropsVector[index].pattern_to;
#else
let patternTo = vec4<f32>(in.pattern_to);
out.pattern_to = patternTo;
#endif
let pattern_tl_a = patternFrom.xy;
let pattern_br_a = patternFrom.zw;
let pattern_tl_b = patternTo.xy;
let pattern_br_b = patternTo.zw;
let display_size_a = vec2<f32>((pattern_br_a.x - pattern_tl_a.x) / pixelRatio,
(pattern_br_a.y - pattern_tl_a.y) / pixelRatio);
let display_size_b = vec2<f32>((pattern_br_b.x - pattern_tl_b.x) / pixelRatio,
(pattern_br_b.y - pattern_tl_b.y) / pixelRatio);
var lighting = vec4<f32>(0.0, 0.0, 0.0, 1.0);
var directional = clamp(dot(normal_i / 16383.0, props.light_position_base.xyz), 0.0, 1.0);
directional = mix(1.0 - props.light_intensity, max(0.5 + props.light_intensity, 1.0), directional);
if (normal_i.y != 0.0) {
let gradientMin = mix(0.7, 0.98, 1.0 - props.light_intensity);
let factor = clamp((t + baseValue) * pow(heightValue / 150.0, 0.5), gradientMin, 1.0);
directional *= (1.0 - props.vertical_gradient) + props.vertical_gradient * factor;
}
let lightColor = props.light_color_pad.xyz;
let lit = clamp(directional * lightColor,
mix(vec3<f32>(0.0), vec3<f32>(0.3), 1.0 - lightColor),
vec3<f32>(1.0));
lighting = vec4<f32>(lighting.rgb + lit, lighting.a);
lighting *= props.opacity;
out.lighting = lighting;
out.pos_a = get_pattern_pos(drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
fromScale * display_size_a,
tileZoomRatio,
patternPos);
out.pos_b = get_pattern_pos(drawable.pixel_coord_upper,
drawable.pixel_coord_lower,
toScale * display_size_b,
tileZoomRatio,
patternPos);
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) lighting: vec4<f32>,
@location(1) pos_a: vec2<f32>,
@location(2) pos_b: vec2<f32>,
#ifndef HAS_UNIFORM_u_pattern_from
@location(3) pattern_from: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_pattern_to
@location(4) pattern_to: vec4<f32>,
#endif
};
struct FillExtrusionPatternTilePropsUBO {
pattern_from: vec4<f32>,
pattern_to: vec4<f32>,
texsize: vec2<f32>,
pad1: f32,
pad2: f32,
};
struct FillExtrusionPropsUBO {
color: vec4<f32>,
light_color_pad: vec4<f32>,
light_position_base: vec4<f32>,
height: f32,
light_intensity: f32,
vertical_gradient: f32,
opacity: f32,
fade: f32,
from_scale: f32,
to_scale: f32,
pad2: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(4) var<storage, read> tilePropsVector: array<FillExtrusionPatternTilePropsUBO>;
@group(0) @binding(5) var<uniform> props: FillExtrusionPropsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var pattern_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
#ifdef HAS_UNIFORM_u_pattern_from
let pattern_from = tileProps.pattern_from;
#else
let pattern_from = in.pattern_from;
#endif
#ifdef HAS_UNIFORM_u_pattern_to
let pattern_to = tileProps.pattern_to;
#else
let pattern_to = in.pattern_to;
#endif
let pattern_tl_a = pattern_from.xy;
let pattern_br_a = pattern_from.zw;
let pattern_tl_b = pattern_to.xy;
let pattern_br_b = pattern_to.zw;
let imagecoord_a = gl_mod(in.pos_a, vec2<f32>(1.0));
let pos_a = mix(pattern_tl_a / tileProps.texsize, pattern_br_a / tileProps.texsize, imagecoord_a);
let color_a = textureSample(pattern_texture, texture_sampler, pos_a);
let imagecoord_b = gl_mod(in.pos_b, vec2<f32>(1.0));
let pos_b = mix(pattern_tl_b / tileProps.texsize, pattern_br_b / tileProps.texsize, imagecoord_b);
let color_b = textureSample(pattern_texture, texture_sampler, pos_b);
let pattern_color = mix(color_a, color_b, props.fade);
return pattern_color * in.lighting;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,127 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/heatmap_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::HeatmapShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "HeatmapShader";
static const std::array<AttributeInfo, 3> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(0) position: vec2<i32>,
#ifndef HAS_UNIFORM_u_weight
@location(1) weight: vec2<f32>,
#endif
#ifndef HAS_UNIFORM_u_radius
@location(2) radius: vec2<f32>,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) weight: f32,
@location(1) extrude: vec2<f32>,
};
struct HeatmapDrawableUBO {
matrix: mat4x4<f32>,
extrude_scale: f32,
weight_t: f32,
radius_t: f32,
pad1: f32,
};
struct HeatmapEvaluatedPropsUBO {
weight: f32,
radius: f32,
intensity: f32,
padding: f32,
};
struct GlobalIndexUBO {
value: u32,
pad: vec3<u32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<HeatmapDrawableUBO>;
@group(0) @binding(4) var<uniform> props: HeatmapEvaluatedPropsUBO;
const ZERO: f32 = 1.0 / 255.0 / 16.0;
const GAUSS_COEF: f32 = 0.3989422804014327;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
#ifdef HAS_UNIFORM_u_weight
let weight = props.weight;
#else
let weight = unpack_mix_float(in.weight, drawable.weight_t);
#endif
#ifdef HAS_UNIFORM_u_radius
let radius = props.radius;
#else
let radius = unpack_mix_float(in.radius, drawable.radius_t);
#endif
let pos = vec2<f32>(f32(in.position.x), f32(in.position.y));
let unscaled_extrude = fma(vec2<f32>(f32(in.position.x & 1), f32(in.position.y & 1)), vec2<f32>(2.0, 2.0),
vec2<f32>(-1.0, -1.0));
let S = sqrt(-2.0 * log(ZERO / (max(weight, ZERO) * max(props.intensity, ZERO) * GAUSS_COEF))) / 3.0;
let extrude = S * unscaled_extrude;
let scaled_extrude = extrude * radius * drawable.extrude_scale;
let base = floor(pos * 0.5);
out.position = drawable.matrix * vec4<f32>(base + scaled_extrude, 0.0, 1.0);
out.weight = weight;
out.extrude = extrude;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) weight: f32,
@location(1) extrude: vec2<f32>,
};
struct HeatmapEvaluatedPropsUBO {
weight: f32,
radius: f32,
intensity: f32,
padding: f32,
};
const GAUSS_COEF: f32 = 0.3989422804014327;
@group(0) @binding(4) var<uniform> props: HeatmapEvaluatedPropsUBO;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef OVERDRAW_INSPECTOR
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
#else
let d = -0.5 * 3.0 * 3.0 * dot(in.extrude, in.extrude);
let val = in.weight * props.intensity * GAUSS_COEF * exp(d);
return vec4<f32>(val, 1.0, 1.0, 1.0);
#endif
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,87 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/heatmap_texture_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::HeatmapTextureShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "HeatmapTextureShader";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 2> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex_coord: vec2<f32>,
};
struct HeatmapTextureUBO {
matrix: mat4x4<f32>,
opacity: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
// remaining fields unused
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(4) var<uniform> ubo: HeatmapTextureUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let quad_pos = vec2<f32>(f32(in.position.x), f32(in.position.y));
let scaled = quad_pos * paintParams.world_size;
out.position = ubo.matrix * vec4<f32>(scaled, 0.0, 1.0);
out.tex_coord = quad_pos;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) tex_coord: vec2<f32>,
};
struct HeatmapTextureUBO {
matrix: mat4x4<f32>,
opacity: f32,
pad1: f32,
pad2: f32,
pad3: f32,
};
@group(0) @binding(4) var<uniform> ubo: HeatmapTextureUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var heatmap_texture: texture_2d<f32>;
@group(1) @binding(2) var color_ramp_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let t = textureSample(heatmap_texture, texture_sampler, in.tex_coord).r;
let color = textureSample(color_ramp_texture, texture_sampler, vec2<f32>(t, 0.5));
return color * ubo.opacity;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,122 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/hillshade_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::HillshadeShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "HillshadeShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<i32>,
@location(6) texcoord: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex_coord: vec2<f32>,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
struct HillshadeDrawableUBO {
matrix: mat4x4<f32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<HillshadeDrawableUBO>;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
var tex = vec2<f32>(f32(in.texcoord.x), f32(in.texcoord.y)) / 8192.0;
tex.y = 1.0 - tex.y;
out.tex_coord = tex;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) tex_coord: vec2<f32>,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
struct HillshadeTilePropsUBO {
latrange: vec2<f32>,
light: vec2<f32>,
};
struct HillshadeEvaluatedPropsUBO {
highlight: vec4<f32>,
shadow: vec4<f32>,
accent: vec4<f32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(3) var<storage, read> tilePropsVector: array<HillshadeTilePropsUBO>;
@group(0) @binding(4) var<uniform> props: HillshadeEvaluatedPropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var hillshade_texture: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef OVERDRAW_INSPECTOR
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
#endif
let tileProps = tilePropsVector[globalIndex.value];
let pixel = textureSample(hillshade_texture, texture_sampler, in.tex_coord);
let deriv = pixel.rg * 2.0 - vec2<f32>(1.0, 1.0);
let latRange = tileProps.latrange;
let latitude = (latRange.x - latRange.y) * in.tex_coord.y + latRange.y;
let scaleFactor = cos(radians(latitude));
let slope = atan(1.25 * length(deriv) / scaleFactor);
let aspectDefault = 0.5 * PI * select(-1.0, 1.0, deriv.y > 0.0);
let aspect = select(aspectDefault, atan2(deriv.y, -deriv.x), deriv.x != 0.0);
let intensity = tileProps.light.x;
let azimuth = tileProps.light.y + PI;
let base = 1.875 - intensity * 1.75;
let maxValue = 0.5 * PI;
let denom = pow(base, maxValue) - 1.0;
let useLinear = abs(intensity - 0.5) < 1e-6;
let scaledSlope = select(((pow(base, slope) - 1.0) / denom) * maxValue,
slope,
useLinear);
let accent = cos(scaledSlope);
let accentColor = (1.0 - accent) * props.accent * clamp(intensity * 2.0, 0.0, 1.0);
let shade = abs(glMod((aspect + azimuth) / PI + 0.5, 2.0) - 1.0);
let shadeColor = mix(props.shadow, props.highlight, shade) * sin(scaledSlope) * clamp(intensity * 2.0, 0.0, 1.0);
let color = accentColor * (1.0 - shadeColor.a) + shadeColor;
return color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,117 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/hillshade_prepare_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "HillshadePrepareShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<i32>,
@location(6) texcoord: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex_coord: vec2<f32>,
};
struct HillshadePrepareDrawableUBO {
matrix: mat4x4<f32>,
};
struct HillshadePrepareTilePropsUBO {
unpack: vec4<f32>,
dimension: vec2<f32>,
zoom: f32,
maxzoom: f32,
};
@group(0) @binding(2) var<uniform> drawable: HillshadePrepareDrawableUBO;
@group(0) @binding(4) var<uniform> tileProps: HillshadePrepareTilePropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
let epsilon = vec2<f32>(1.0, 1.0) / tileProps.dimension;
let scale = (tileProps.dimension.x - 2.0) / tileProps.dimension.x;
out.tex_coord = vec2<f32>(f32(in.texcoord.x), f32(in.texcoord.y)) / 8192.0 * scale + epsilon;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) tex_coord: vec2<f32>,
};
struct HillshadePrepareTilePropsUBO {
unpack: vec4<f32>,
dimension: vec2<f32>,
zoom: f32,
maxzoom: f32,
};
@group(0) @binding(4) var<uniform> tileProps: HillshadePrepareTilePropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var dem_texture: texture_2d<f32>;
fn getElevation(coord: vec2<f32>) -> f32 {
var data = textureSample(dem_texture, texture_sampler, coord) * 255.0;
data.a = -1.0;
return dot(data, tileProps.unpack) / 4.0;
}
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
#ifdef OVERDRAW_INSPECTOR
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
#endif
let epsilon = vec2<f32>(1.0, 1.0) / tileProps.dimension;
let a = getElevation(in.tex_coord + vec2<f32>(-epsilon.x, -epsilon.y));
let b = getElevation(in.tex_coord + vec2<f32>(0.0, -epsilon.y));
let c = getElevation(in.tex_coord + vec2<f32>(epsilon.x, -epsilon.y));
let d = getElevation(in.tex_coord + vec2<f32>(-epsilon.x, 0.0));
let f = getElevation(in.tex_coord + vec2<f32>(epsilon.x, 0.0));
let g = getElevation(in.tex_coord + vec2<f32>(-epsilon.x, epsilon.y));
let h = getElevation(in.tex_coord + vec2<f32>(0.0, epsilon.y));
let i = getElevation(in.tex_coord + vec2<f32>(epsilon.x, epsilon.y));
var exaggeration = 0.3;
if (tileProps.zoom < 4.5) {
exaggeration = 0.35;
}
if (tileProps.zoom < 2.0) {
exaggeration = 0.4;
}
let denom = pow(2.0, (tileProps.zoom - tileProps.maxzoom) * exaggeration + 19.2562 - tileProps.zoom);
let deriv = vec2<f32>((c + f + f + i) - (a + d + d + g),
(g + h + h + i) - (a + b + b + c)) / denom;
let color = clamp(vec4<f32>(deriv.x / 2.0 + 0.5,
deriv.y / 2.0 + 0.5,
1.0,
1.0),
vec4<f32>(0.0, 0.0, 0.0, 0.0),
vec4<f32>(1.0, 1.0, 1.0, 1.0));
return color;
}
)";
};
} // namespace shaders
} // namespace mbgl

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/location_indicator_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::LocationIndicatorShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "LocationIndicatorShader";
static const std::array<AttributeInfo, 1> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 0> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
};
struct LocationIndicatorUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> ubo: LocationIndicatorUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = ubo.matrix * vec4<f32>(in.position, 0.0, 1.0);
return out;
}
)";
static constexpr auto fragment = R"(
struct LocationIndicatorUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> ubo: LocationIndicatorUBO;
@fragment
fn main(@builtin(position) position: vec4<f32>) -> @location(0) vec4<f32> {
return ubo.color;
}
)";
};
template <>
struct ShaderSource<BuiltIn::LocationIndicatorTexturedShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "LocationIndicatorTexturedShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec2<f32>,
@location(6) uv: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
};
struct LocationIndicatorUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> ubo: LocationIndicatorUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = ubo.matrix * vec4<f32>(in.position, 0.0, 1.0);
out.uv = in.uv;
return out;
}
)";
static constexpr auto fragment = R"(
struct LocationIndicatorUBO {
matrix: mat4x4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> ubo: LocationIndicatorUBO;
@group(0) @binding(1) var colorTexture: texture_2d<f32>;
@group(0) @binding(2) var colorSampler: sampler;
@fragment
fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let textureColor = textureSample(colorTexture, colorSampler, uv);
return textureColor * ubo.color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,134 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/raster_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::RasterShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "RasterShader";
static const std::array<AttributeInfo, 2> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 2> textures;
static constexpr auto vertex = R"(
struct VertexInput {
@location(0) position: vec2<i32>,
@location(1) texcoord: vec2<i32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) pos0: vec2<f32>,
@location(1) pos1: vec2<f32>,
};
struct RasterDrawableUBO {
matrix: mat4x4<f32>,
};
struct RasterEvaluatedPropsUBO {
spin_weights: vec4<f32>,
tl_parent: vec2<f32>,
scale_parent: f32,
buffer_scale: f32,
fade_t: f32,
opacity: f32,
brightness_low: f32,
brightness_high: f32,
saturation_factor: f32,
contrast_factor: f32,
pad1: f32,
pad2: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<RasterDrawableUBO>;
@group(0) @binding(4) var<uniform> props: RasterEvaluatedPropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let clip = drawable.matrix * vec4<f32>(f32(in.position.x), f32(in.position.y), 0.0, 1.0);
out.position = clip;
let tex = vec2<f32>(f32(in.texcoord.x), f32(in.texcoord.y));
let pos0 = (((tex / 8192.0) - vec2<f32>(0.5, 0.5)) / props.buffer_scale) + vec2<f32>(0.5, 0.5);
out.pos0 = pos0;
out.pos1 = pos0 * props.scale_parent + props.tl_parent;
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) pos0: vec2<f32>,
@location(1) pos1: vec2<f32>,
};
struct RasterEvaluatedPropsUBO {
spin_weights: vec4<f32>,
tl_parent: vec2<f32>,
scale_parent: f32,
buffer_scale: f32,
fade_t: f32,
opacity: f32,
brightness_low: f32,
brightness_high: f32,
saturation_factor: f32,
contrast_factor: f32,
pad1: f32,
pad2: f32,
};
@group(0) @binding(4) var<uniform> props: RasterEvaluatedPropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var image0: texture_2d<f32>;
@group(1) @binding(2) var image1: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
var color0 = textureSample(image0, texture_sampler, in.pos0);
var color1 = textureSample(image1, texture_sampler, in.pos1);
if (color0.a > 0.0) {
color0 = vec4<f32>(color0.rgb / color0.a, color0.a);
}
if (color1.a > 0.0) {
color1 = vec4<f32>(color1.rgb / color1.a, color1.a);
}
var color = mix(color0, color1, props.fade_t);
color.a = color.a * props.opacity;
var rgb = color.rgb;
let spin = props.spin_weights;
rgb = vec3<f32>(dot(rgb, spin.xyz),
dot(rgb, spin.zxy),
dot(rgb, spin.yzx));
let average = (color.r + color.g + color.b) / 3.0;
rgb = rgb + (average - rgb) * props.saturation_factor;
rgb = (rgb - vec3<f32>(0.5, 0.5, 0.5)) * props.contrast_factor + vec3<f32>(0.5, 0.5, 0.5);
let high_vec = vec3<f32>(props.brightness_low, props.brightness_low, props.brightness_low);
let low_vec = vec3<f32>(props.brightness_high, props.brightness_high, props.brightness_high);
let final_rgb = mix(high_vec, low_vec, rgb) * color.a;
return vec4<f32>(final_rgb, color.a);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,109 @@
#pragma once
#include <mbgl/gfx/shader_group.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/webgpu/common.hpp>
#include <mbgl/shaders/webgpu/wgsl_preprocessor.hpp>
#include <mbgl/shaders/program_parameters.hpp>
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/util/hash.hpp>
#include <mbgl/util/containers.hpp>
#include <mbgl/webgpu/context.hpp>
#include <string>
#include <type_traits>
#include <unordered_map>
namespace mbgl {
namespace webgpu {
class ShaderGroupBase : public gfx::ShaderGroup {
protected:
ShaderGroupBase(const ProgramParameters& parameters_)
: programParameters(parameters_) {}
using DefinesMap = mbgl::unordered_map<std::string, std::string>;
void addAdditionalDefines(const StringIDSetsPair& propertiesAsUniforms, DefinesMap& additionalDefines) {
additionalDefines.reserve(propertiesAsUniforms.first.size());
for (const auto name : propertiesAsUniforms.first) {
// We expect the names to be prefixed by "a_", but we need just the base here.
const auto* base = (name[0] == 'a' && name[1] == '_') ? &name[2] : name.data();
additionalDefines.insert(std::make_pair(std::string(uniformPrefix) + base, std::string()));
}
}
ProgramParameters programParameters;
private:
static constexpr auto uniformPrefix = "HAS_UNIFORM_u_";
};
template <shaders::BuiltIn ShaderID>
class ShaderGroup final : public ShaderGroupBase {
public:
ShaderGroup(const ProgramParameters& programParameters_)
: ShaderGroupBase(programParameters_) {}
~ShaderGroup() noexcept override = default;
gfx::ShaderPtr getOrCreateShader(gfx::Context& gfxContext,
const StringIDSetsPair& propertiesAsUniforms,
std::string_view /*firstAttribName*/) override {
using ShaderSource = shaders::ShaderSource<ShaderID, gfx::Backend::Type::WebGPU>;
constexpr auto& name = ShaderSource::name;
constexpr auto& vert = ShaderSource::vertex;
constexpr auto& frag = ShaderSource::fragment;
std::size_t seed = 0;
mbgl::util::hash_combine(seed, propertyHash(propertiesAsUniforms));
mbgl::util::hash_combine(seed, programParameters.getDefinesHash());
const std::string shaderName = getShaderName(name, seed);
auto shader = get<webgpu::ShaderProgram>(shaderName);
if (!shader) {
DefinesMap additionalDefines;
addAdditionalDefines(propertiesAsUniforms, additionalDefines);
std::string vertexSource;
std::string fragmentSource;
if constexpr (requires { ShaderSource::prelude; }) {
vertexSource = std::string(ShaderSource::prelude) + vert;
fragmentSource = std::string(ShaderSource::prelude) + frag;
} else {
vertexSource = vert;
fragmentSource = frag;
}
std::unordered_map<std::string, bool> defineSet;
const auto& programDefines = programParameters.getDefines();
for (const auto& entry : programDefines) {
defineSet.emplace(entry.first, true);
}
for (const auto& entry : additionalDefines) {
defineSet.emplace(entry.first, true);
}
vertexSource = detail::preprocessWGSL(vertexSource, defineSet);
fragmentSource = detail::preprocessWGSL(fragmentSource, defineSet);
auto& context = static_cast<Context&>(gfxContext);
shader = std::make_shared<ShaderProgram>(context,
vertexSource,
fragmentSource,
ShaderSource::attributes,
ShaderSource::textures,
std::string(name));
assert(shader);
if (!shader || !registerShader(shader, shaderName)) {
assert(false);
throw std::runtime_error("Failed to register " + shaderName + " with shader group!");
}
}
return shader;
}
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,169 @@
#pragma once
#include <mbgl/shaders/shader_program_base.hpp>
#include <webgpu/webgpu.h>
#include <mbgl/gfx/gfx_types.hpp>
#include <mbgl/gfx/attribute.hpp>
#include <mbgl/gfx/color_mode.hpp>
#include <mbgl/gfx/depth_mode.hpp>
#include <mbgl/gfx/stencil_mode.hpp>
#include <string>
#include <unordered_map>
#include <array>
#include <optional>
namespace mbgl {
namespace shaders {
struct UniformBlockInfo {
constexpr UniformBlockInfo(bool vertex_, bool fragment_, std::size_t size_, std::size_t id_)
: index(id_),
vertex(vertex_),
fragment(fragment_),
size(size_),
id(id_) {}
std::size_t index;
bool vertex;
bool fragment;
std::size_t size;
std::size_t id;
};
struct AttributeInfo {
constexpr AttributeInfo(std::size_t index_, gfx::AttributeDataType dataType_, std::size_t id_)
: index(index_),
dataType(dataType_),
id(id_) {}
std::size_t index;
gfx::AttributeDataType dataType;
std::size_t id;
};
struct TextureInfo {
constexpr TextureInfo(std::size_t index_, std::size_t id_)
: index(index_),
id(id_) {}
std::size_t index;
std::size_t id;
};
} // namespace shaders
namespace webgpu {
class Context;
class RendererBackend;
class ShaderProgram final : public gfx::ShaderProgramBase {
public:
// Static name member required by is_shader_v trait
static constexpr std::string_view Name = "WebGPUShader";
enum class BindingType {
UniformBuffer,
ReadOnlyStorageBuffer,
StorageBuffer,
Sampler,
Texture
};
struct BindingInfo {
uint32_t group = 0;
uint32_t binding = 0;
BindingType type = BindingType::UniformBuffer;
WGPUShaderStage visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
};
ShaderProgram(std::string name,
RendererBackend& backend,
WGPUShaderModule vertexModule,
WGPUShaderModule fragmentModule);
ShaderProgram(Context& context, const std::string& vertexSource, const std::string& fragmentSource);
template <size_t N, size_t M>
ShaderProgram(Context& context,
const std::string& vertexSource,
const std::string& fragmentSource,
const std::array<shaders::AttributeInfo, N>& attrs,
const std::array<shaders::TextureInfo, M>& texts,
const std::string& name = "ShaderProgram")
: ShaderProgram(context, vertexSource, fragmentSource) {
if (name != "ShaderProgram") {
shaderName = name;
}
for (const auto& attr : attrs) {
initAttribute(attr);
}
for (const auto& tex : texts) {
initTexture(tex);
}
}
~ShaderProgram() override;
WGPURenderPipeline getRenderPipeline(const gfx::Renderable& renderable,
const WGPUVertexBufferLayout* vertexLayouts,
uint32_t vertexLayoutCount,
const gfx::ColorMode& colorMode,
const gfx::DepthMode& depthMode,
const gfx::StencilMode& stencilMode,
gfx::DrawModeType drawModeType,
const std::optional<std::size_t> reuseHash = std::nullopt);
const std::vector<BindingInfo>& getBindingInfos() const { return bindingInfos; }
const std::vector<BindingInfo>& getBindingInfosForGroup(uint32_t group) const;
WGPUBindGroupLayout getBindGroupLayout(uint32_t group) const;
const std::vector<WGPUBindGroupLayout>& getBindGroupLayouts() const { return bindGroupLayouts; }
const std::vector<uint32_t>& getBindGroupOrder() const { return bindGroupOrder; }
const std::string_view typeName() const noexcept override { return Name; }
std::optional<size_t> getSamplerLocation(const size_t id) const override;
const gfx::VertexAttributeArray& getVertexAttributes() const override { return vertexAttributes; }
const gfx::VertexAttributeArray& getInstanceAttributes() const override { return instanceAttributes; }
void initAttribute(const shaders::AttributeInfo& info);
void initInstanceAttribute(const shaders::AttributeInfo& info);
void initTexture(const shaders::TextureInfo& info);
protected:
void createPipelineLayout(const std::string& vertexSource, const std::string& fragmentSource);
WGPURenderPipeline createPipeline(const WGPUVertexBufferLayout* vertexLayouts,
uint32_t vertexLayoutCount,
const gfx::ColorMode& colorMode,
const gfx::DepthMode& depthMode,
const gfx::StencilMode& stencilMode,
gfx::DrawModeType drawModeType);
void analyzeShaderBindings(const std::string& source, WGPUShaderStage stage);
void rebuildBindGroupLayouts();
std::string shaderName;
RendererBackend& backend;
WGPUShaderModule vertexShaderModule = nullptr;
WGPUShaderModule fragmentShaderModule = nullptr;
std::vector<WGPUBindGroupLayout> bindGroupLayouts;
std::vector<uint32_t> bindGroupOrder;
WGPUPipelineLayout pipelineLayout = nullptr;
mutable std::unordered_map<std::size_t, WGPURenderPipeline> renderPipelineCache;
gfx::VertexAttributeArray vertexAttributes;
gfx::VertexAttributeArray instanceAttributes;
std::array<std::optional<size_t>, shaders::maxTextureCountPerShader> textureBindings;
std::vector<BindingInfo> bindingInfos;
std::vector<std::vector<BindingInfo>> bindingsPerGroup;
bool hasVertexEntryPoint = true;
bool hasFragmentEntryPoint = true;
bool loggedMissingEntryPoint = false;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,752 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/symbol_layer_ubo.hpp>
namespace mbgl {
namespace shaders {
constexpr auto symbolShaderPrelude = R"(
// Symbol shader UBO indices
const idGlobalPaintParamsUBO = 0u;
const idGlobalUBOIndex = 1u;
const idSymbolDrawableUBO = 2u;
const idSymbolTilePropsUBO = 3u;
const idSymbolEvaluatedPropsUBO = 4u;
const symbolUBOCount = 5u;
struct GlobalPaintParamsUBO {
pattern_atlas_texsize: vec2<f32>,
units_to_pixels: vec2<f32>,
world_size: vec2<f32>,
camera_to_center_distance: f32,
symbol_fade_change: f32,
aspect_ratio: f32,
pixel_ratio: f32,
map_zoom: f32,
pad1: f32,
};
struct SymbolDrawableUBO {
matrix: mat4x4<f32>,
label_plane_matrix: mat4x4<f32>,
coord_matrix: mat4x4<f32>,
texsize: vec2<f32>,
texsize_icon: vec2<f32>,
is_text_prop: u32,
rotate_symbol: u32,
pitch_with_map: u32,
is_size_zoom_constant: u32,
is_size_feature_constant: u32,
size_t: f32,
size: f32,
// Interpolations
fill_color_t: f32,
halo_color_t: f32,
opacity_t: f32,
halo_width_t: f32,
halo_blur_t: f32,
};
struct SymbolTilePropsUBO {
is_text: u32,
is_halo: u32,
gamma_scale: f32,
pad1: f32,
};
struct SymbolEvaluatedPropsUBO {
text_fill_color: vec4<f32>,
text_halo_color: vec4<f32>,
text_opacity: f32,
text_halo_width: f32,
text_halo_blur: f32,
pad1: f32,
icon_fill_color: vec4<f32>,
icon_halo_color: vec4<f32>,
icon_opacity: f32,
icon_halo_width: f32,
icon_halo_blur: f32,
pad2: f32,
};
struct GlobalIndexUBO {
value: u32,
pad0: vec3<u32>,
};
const c_offscreen_degenerate_triangle_location: f32 = -2.0;
)";
template <>
struct ShaderSource<BuiltIn::SymbolIconShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "SymbolIconShader";
static const std::array<AttributeInfo, 6> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto prelude = symbolShaderPrelude;
static constexpr auto vertex = R"()"
R"(
struct VertexInput {
@location(3) pos_offset: vec4<i32>,
@location(4) data: vec4<u32>,
@location(5) pixeloffset: vec4<i32>,
@location(6) projected_pos: vec3<f32>,
@location(7) fade_opacity: f32,
#ifndef HAS_UNIFORM_u_opacity
@location(8) opacity: f32,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex: vec2<f32>,
@location(1) fade_opacity: f32,
#ifndef HAS_UNIFORM_u_opacity
@location(2) opacity: f32,
#endif
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<SymbolDrawableUBO>;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let raw_fade_opacity = unpack_opacity(in.fade_opacity);
let fade_change = select(-paintParams.symbol_fade_change, paintParams.symbol_fade_change, raw_fade_opacity.y > 0.5);
let fade_opacity = max(0.0, min(1.0, raw_fade_opacity.x + fade_change));
#ifdef HAS_UNIFORM_u_opacity
let final_opacity = fade_opacity;
#else
let paint_opacity = unpack_mix_float(vec2<f32>(in.opacity, in.opacity), drawable.opacity_t);
let final_opacity = paint_opacity * fade_opacity;
#endif
// Cull vertices with zero opacity
if (final_opacity == 0.0) {
out.position = vec4<f32>(c_offscreen_degenerate_triangle_location,
c_offscreen_degenerate_triangle_location,
c_offscreen_degenerate_triangle_location, 1.0);
return out;
}
let a_pos = vec2<f32>(f32(in.pos_offset.x), f32(in.pos_offset.y));
let a_offset = vec2<f32>(f32(in.pos_offset.z), f32(in.pos_offset.w));
let a_tex = vec2<f32>(f32(in.data.x), f32(in.data.y));
let a_size = vec2<f32>(f32(in.data.z), f32(in.data.w));
let a_size_min = floor(a_size.x * 0.5);
let a_pxoffset = vec2<f32>(f32(in.pixeloffset.x), f32(in.pixeloffset.y));
let a_minFontScale = vec2<f32>(f32(in.pixeloffset.z), f32(in.pixeloffset.w)) / 256.0;
let segment_angle = -in.projected_pos.z;
let size_zoom_constant = drawable.is_size_zoom_constant != 0u;
let size_feature_constant = drawable.is_size_feature_constant != 0u;
var size: f32;
if (!size_zoom_constant && !size_feature_constant) {
size = mix(a_size_min, a_size.y, drawable.size_t) / 128.0;
} else if (size_zoom_constant && !size_feature_constant) {
size = a_size_min / 128.0;
} else {
size = drawable.size;
}
let projectedPoint = drawable.matrix * vec4<f32>(a_pos, 0.0, 1.0);
let camera_to_anchor_distance = projectedPoint.w;
let pitch_with_map = drawable.pitch_with_map != 0u;
let distance_ratio = select(
paintParams.camera_to_center_distance / camera_to_anchor_distance,
camera_to_anchor_distance / paintParams.camera_to_center_distance,
pitch_with_map
);
let perspective_ratio = clamp(0.5 + 0.5 * distance_ratio, 0.0, 4.0);
size *= perspective_ratio;
let is_text = drawable.is_text_prop != 0u;
let fontScale = select(size, size / 24.0, is_text);
var symbol_rotation = 0.0;
if (drawable.rotate_symbol != 0u) {
let offsetProjectedPoint = drawable.matrix * vec4<f32>(a_pos + vec2<f32>(1.0, 0.0), 0.0, 1.0);
let a = projectedPoint.xy / projectedPoint.w;
let b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
symbol_rotation = atan2((b.y - a.y) / paintParams.aspect_ratio, b.x - a.x);
}
let angle_sin = sin(segment_angle + symbol_rotation);
let angle_cos = cos(segment_angle + symbol_rotation);
let rotation_matrix = mat2x2<f32>(angle_cos, -angle_sin, angle_sin, angle_cos);
let projected_pos = drawable.label_plane_matrix * vec4<f32>(in.projected_pos.xy, 0.0, 1.0);
let pos0 = projected_pos.xy / projected_pos.w;
let posOffset = a_offset * max(a_minFontScale, vec2<f32>(fontScale)) / 32.0 + a_pxoffset / 16.0;
let position = drawable.coord_matrix * vec4<f32>(pos0 + rotation_matrix * posOffset, 0.0, 1.0);
out.position = position;
out.tex = a_tex / drawable.texsize;
out.fade_opacity = fade_opacity;
#ifndef HAS_UNIFORM_u_opacity
out.opacity = final_opacity;
#endif
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) tex: vec2<f32>,
@location(1) fade_opacity: f32,
#ifndef HAS_UNIFORM_u_opacity
@location(2) opacity: f32,
#endif
};
@group(0) @binding(3) var<storage, read> tilePropsVector: array<SymbolTilePropsUBO>;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(4) var<uniform> props: SymbolEvaluatedPropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var image: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
#ifdef HAS_UNIFORM_u_opacity
let base_opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u);
let opacity = base_opacity * in.fade_opacity;
#else
let opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u) * in.opacity;
#endif
return textureSample(image, texture_sampler, in.tex) * opacity;
}
)";
};
template <>
struct ShaderSource<BuiltIn::SymbolSDFShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "SymbolSDFShader";
static const std::array<AttributeInfo, 10> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 1> textures;
static constexpr auto prelude = symbolShaderPrelude;
static constexpr auto vertex = R"()"
R"(
struct VertexInput {
@location(3) pos_offset: vec4<i32>,
@location(4) data: vec4<u32>,
@location(5) pixeloffset: vec4<i32>,
@location(6) projected_pos: vec3<f32>,
@location(7) fade_opacity: f32,
#ifndef HAS_UNIFORM_u_fill_color
@location(8) fill_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_halo_color
@location(9) halo_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(10) opacity: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_width
@location(11) halo_width: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_blur
@location(12) halo_blur: f32,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex: vec2<f32>,
@location(1) fade_opacity: f32,
@location(2) gamma_scale: f32,
@location(3) fontScale: f32,
#ifndef HAS_UNIFORM_u_fill_color
@location(4) fill_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_halo_color
@location(5) halo_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(6) opacity: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_width
@location(7) halo_width: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_blur
@location(8) halo_blur: f32,
#endif
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<SymbolDrawableUBO>;
@group(0) @binding(4) var<uniform> props: SymbolEvaluatedPropsUBO;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let fade_opacity = unpack_opacity(in.fade_opacity);
let fade_change = select(-paintParams.symbol_fade_change, paintParams.symbol_fade_change, fade_opacity.y > 0.5);
let fo = max(0.0, min(1.0, fade_opacity.x + fade_change));
// Cull vertices with zero opacity
if (fo == 0.0) {
out.position = vec4<f32>(c_offscreen_degenerate_triangle_location,
c_offscreen_degenerate_triangle_location,
c_offscreen_degenerate_triangle_location, 1.0);
return out;
}
let a_pos = vec2<f32>(f32(in.pos_offset.x), f32(in.pos_offset.y));
let a_offset = vec2<f32>(f32(in.pos_offset.z), f32(in.pos_offset.w));
let a_tex = vec2<f32>(f32(in.data.x), f32(in.data.y));
let a_size = vec2<f32>(f32(in.data.z), f32(in.data.w));
let a_size_min = floor(a_size.x * 0.5);
let a_pxoffset = vec2<f32>(f32(in.pixeloffset.x), f32(in.pixeloffset.y));
let segment_angle = -in.projected_pos.z;
var size: f32;
let size_zoom_constant = drawable.is_size_zoom_constant != 0u;
let size_feature_constant = drawable.is_size_feature_constant != 0u;
if (!size_zoom_constant && !size_feature_constant) {
size = mix(a_size_min, a_size.y, drawable.size_t) / 128.0;
} else if (size_zoom_constant && !size_feature_constant) {
size = a_size_min / 128.0;
} else {
size = drawable.size;
}
let projectedPoint = drawable.matrix * vec4<f32>(a_pos, 0.0, 1.0);
let camera_to_anchor_distance = projectedPoint.w;
let pitch_with_map = drawable.pitch_with_map != 0u;
let distance_ratio = select(
paintParams.camera_to_center_distance / camera_to_anchor_distance,
camera_to_anchor_distance / paintParams.camera_to_center_distance,
pitch_with_map
);
let perspective_ratio = clamp(0.5 + 0.5 * distance_ratio, 0.0, 4.0);
size *= perspective_ratio;
let is_text = drawable.is_text_prop != 0u;
let fontScale = select(size, size / 24.0, is_text);
var symbol_rotation = 0.0;
if (drawable.rotate_symbol != 0u) {
let offsetProjectedPoint = drawable.matrix * vec4<f32>(a_pos + vec2<f32>(1.0, 0.0), 0.0, 1.0);
let a = projectedPoint.xy / projectedPoint.w;
let b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
symbol_rotation = atan2((b.y - a.y) / paintParams.aspect_ratio, b.x - a.x);
}
let angle_sin = sin(segment_angle + symbol_rotation);
let angle_cos = cos(segment_angle + symbol_rotation);
let rotation_matrix = mat2x2<f32>(angle_cos, -angle_sin, angle_sin, angle_cos);
let projected_pos = drawable.label_plane_matrix * vec4<f32>(in.projected_pos.xy, 0.0, 1.0);
let pos_rot = a_offset / 32.0 * fontScale + a_pxoffset;
let pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot;
let position = drawable.coord_matrix * vec4<f32>(pos0, 0.0, 1.0);
out.position = position;
out.tex = a_tex / drawable.texsize;
out.gamma_scale = position.w;
out.fontScale = fontScale;
out.fade_opacity = fo;
#ifndef HAS_UNIFORM_u_fill_color
out.fill_color = unpack_mix_color(in.fill_color, drawable.fill_color_t);
#endif
#ifndef HAS_UNIFORM_u_halo_color
out.halo_color = unpack_mix_color(in.halo_color, drawable.halo_color_t);
#endif
#ifndef HAS_UNIFORM_u_halo_width
out.halo_width = unpack_mix_float(vec2<f32>(in.halo_width, in.halo_width), drawable.halo_width_t);
#endif
#ifndef HAS_UNIFORM_u_halo_blur
out.halo_blur = unpack_mix_float(vec2<f32>(in.halo_blur, in.halo_blur), drawable.halo_blur_t);
#endif
#ifndef HAS_UNIFORM_u_opacity
out.opacity = unpack_mix_float(vec2<f32>(in.opacity, in.opacity), drawable.opacity_t);
#endif
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) tex: vec2<f32>,
@location(1) fade_opacity: f32,
@location(2) gamma_scale: f32,
@location(3) fontScale: f32,
#ifndef HAS_UNIFORM_u_fill_color
@location(4) fill_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_halo_color
@location(5) halo_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(6) opacity: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_width
@location(7) halo_width: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_blur
@location(8) halo_blur: f32,
#endif
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(3) var<storage, read> tilePropsVector: array<SymbolTilePropsUBO>;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(4) var<uniform> props: SymbolEvaluatedPropsUBO;
@group(1) @binding(0) var texture_sampler: sampler;
@group(1) @binding(1) var glyph_atlas: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
#ifndef HAS_UNIFORM_u_fill_color
let fill_color = in.fill_color;
#else
let fill_color = select(props.icon_fill_color, props.text_fill_color, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_halo_color
let halo_color = in.halo_color;
#else
let halo_color = select(props.icon_halo_color, props.text_halo_color, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_opacity
let opacity = in.opacity;
#else
let opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_halo_width
let halo_width = in.halo_width;
#else
let halo_width = select(props.icon_halo_width, props.text_halo_width, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_halo_blur
let halo_blur = in.halo_blur;
#else
let halo_blur = select(props.icon_halo_blur, props.text_halo_blur, tileProps.is_text != 0u);
#endif
let EDGE_GAMMA = 0.105 / paintParams.pixel_ratio;
let fontGamma = in.fontScale * tileProps.gamma_scale;
let color = select(fill_color, halo_color, tileProps.is_halo != 0u);
let gamma = (select(0.0, halo_blur * 1.19 / SDF_PX, tileProps.is_halo != 0u) + EDGE_GAMMA) / fontGamma;
let gamma_scaled = gamma * in.gamma_scale;
// For halos, offset the inner edge outward to create the ring
var inner_edge = (256.0 - 64.0) / 256.0;
if (tileProps.is_halo != 0u) {
inner_edge = inner_edge + gamma_scaled;
}
let sample = textureSample(glyph_atlas, texture_sampler, in.tex);
let dist = select(sample.a, sample.r, tileProps.is_text != 0u);
var alpha = smoothstep(inner_edge - gamma_scaled, inner_edge + gamma_scaled, dist);
// When drawing halos, make the inside transparent so the fill can show through
if (tileProps.is_halo != 0u) {
let halo_edge = (6.0 - halo_width / in.fontScale) / SDF_PX;
alpha = min(smoothstep(halo_edge - gamma_scaled, halo_edge + gamma_scaled, dist), 1.0 - alpha);
}
let coverage = alpha * opacity * in.fade_opacity;
let outAlpha = color.a * coverage;
return vec4<f32>(color.rgb * coverage, outAlpha);
}
)";
};
template <>
struct ShaderSource<BuiltIn::SymbolTextAndIconShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "SymbolTextAndIconShader";
static const std::array<AttributeInfo, 9> attributes;
static constexpr std::array<AttributeInfo, 0> instanceAttributes{};
static const std::array<TextureInfo, 2> textures;
static constexpr auto prelude = symbolShaderPrelude;
static constexpr auto vertex = R"()"
R"(
struct VertexInput {
@location(3) pos_offset: vec4<i32>,
@location(4) data: vec4<u32>,
@location(5) projected_pos: vec3<f32>,
@location(6) fade_opacity: f32,
#ifndef HAS_UNIFORM_u_fill_color
@location(7) fill_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_halo_color
@location(8) halo_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(9) opacity: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_width
@location(10) halo_width: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_blur
@location(11) halo_blur: f32,
#endif
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) tex: vec2<f32>,
@location(1) fade_opacity: f32,
@location(2) gamma_scale: f32,
@location(3) fontScale: f32,
@location(4) is_icon: f32,
#ifndef HAS_UNIFORM_u_fill_color
@location(5) fill_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_halo_color
@location(6) halo_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(7) opacity: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_width
@location(8) halo_width: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_blur
@location(9) halo_blur: f32,
#endif
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(2) var<storage, read> drawableVector: array<SymbolDrawableUBO>;
const SDF = 1.0;
const ICON = 0.0;
@vertex
fn main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
let drawable = drawableVector[globalIndex.value];
let fade_opacity = unpack_opacity(in.fade_opacity);
let fade_change = select(-paintParams.symbol_fade_change, paintParams.symbol_fade_change, fade_opacity.y > 0.5);
let fo = max(0.0, min(1.0, fade_opacity.x + fade_change));
// Cull vertices with zero opacity
if (fo == 0.0) {
out.position = vec4<f32>(c_offscreen_degenerate_triangle_location,
c_offscreen_degenerate_triangle_location,
c_offscreen_degenerate_triangle_location, 1.0);
return out;
}
let a_pos = vec2<f32>(f32(in.pos_offset.x), f32(in.pos_offset.y));
let a_offset = vec2<f32>(f32(in.pos_offset.z), f32(in.pos_offset.w));
let a_tex = vec2<f32>(f32(in.data.x), f32(in.data.y));
let a_size = vec2<f32>(f32(in.data.z), f32(in.data.w));
let a_size_min = floor(a_size.x * 0.5);
let is_sdf = a_size.x - 2.0 * a_size_min;
let segment_angle = -in.projected_pos.z;
let size_zoom_constant = drawable.is_size_zoom_constant != 0u;
let size_feature_constant = drawable.is_size_feature_constant != 0u;
var size: f32;
if (!size_zoom_constant && !size_feature_constant) {
size = mix(a_size_min, a_size.y, drawable.size_t) / 128.0;
} else if (size_zoom_constant && !size_feature_constant) {
size = a_size_min / 128.0;
} else {
size = drawable.size;
}
let projectedPoint = drawable.matrix * vec4<f32>(a_pos, 0.0, 1.0);
let camera_to_anchor_distance = projectedPoint.w;
let pitch_with_map = drawable.pitch_with_map != 0u;
let distance_ratio = select(
paintParams.camera_to_center_distance / camera_to_anchor_distance,
camera_to_anchor_distance / paintParams.camera_to_center_distance,
pitch_with_map
);
let perspective_ratio = clamp(0.5 + 0.5 * distance_ratio, 0.0, 4.0);
size *= perspective_ratio;
let fontScale = size / 24.0;
var symbol_rotation = 0.0;
if (drawable.rotate_symbol != 0u) {
let offsetProjectedPoint = drawable.matrix * vec4<f32>(a_pos + vec2<f32>(1.0, 0.0), 0.0, 1.0);
let a = projectedPoint.xy / projectedPoint.w;
let b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
symbol_rotation = atan2((b.y - a.y) / paintParams.aspect_ratio, b.x - a.x);
}
let angle_sin = sin(segment_angle + symbol_rotation);
let angle_cos = cos(segment_angle + symbol_rotation);
let rotation_matrix = mat2x2<f32>(angle_cos, -angle_sin, angle_sin, angle_cos);
let projected_pos = drawable.label_plane_matrix * vec4<f32>(in.projected_pos.xy, 0.0, 1.0);
let pos_rot = a_offset / 32.0 * fontScale;
let pos0 = projected_pos.xy / projected_pos.w + rotation_matrix * pos_rot;
let position = drawable.coord_matrix * vec4<f32>(pos0, 0.0, 1.0);
let gamma_scale = position.w;
let is_icon = select(1.0, 0.0, is_sdf == SDF);
out.position = position;
let inv_tex = 1.0 / select(drawable.texsize, drawable.texsize_icon, is_icon != 0.0);
out.tex = a_tex * inv_tex;
out.gamma_scale = gamma_scale;
out.fontScale = fontScale;
out.fade_opacity = fo;
out.is_icon = is_icon;
#ifndef HAS_UNIFORM_u_fill_color
out.fill_color = unpack_mix_color(in.fill_color, drawable.fill_color_t);
#endif
#ifndef HAS_UNIFORM_u_halo_color
out.halo_color = unpack_mix_color(in.halo_color, drawable.halo_color_t);
#endif
#ifndef HAS_UNIFORM_u_opacity
out.opacity = unpack_mix_float(vec2<f32>(in.opacity, in.opacity), drawable.opacity_t);
#endif
#ifndef HAS_UNIFORM_u_halo_width
out.halo_width = unpack_mix_float(vec2<f32>(in.halo_width, in.halo_width), drawable.halo_width_t);
#endif
#ifndef HAS_UNIFORM_u_halo_blur
out.halo_blur = unpack_mix_float(vec2<f32>(in.halo_blur, in.halo_blur), drawable.halo_blur_t);
#endif
return out;
}
)";
static constexpr auto fragment = R"(
struct FragmentInput {
@location(0) tex: vec2<f32>,
@location(1) fade_opacity: f32,
@location(2) gamma_scale: f32,
@location(3) fontScale: f32,
@location(4) is_icon: f32,
#ifndef HAS_UNIFORM_u_fill_color
@location(5) fill_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_halo_color
@location(6) halo_color: vec4<f32>,
#endif
#ifndef HAS_UNIFORM_u_opacity
@location(7) opacity: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_width
@location(8) halo_width: f32,
#endif
#ifndef HAS_UNIFORM_u_halo_blur
@location(9) halo_blur: f32,
#endif
};
@group(0) @binding(0) var<uniform> paintParams: GlobalPaintParamsUBO;
@group(0) @binding(3) var<storage, read> tilePropsVector: array<SymbolTilePropsUBO>;
@group(0) @binding(1) var<uniform> globalIndex: GlobalIndexUBO;
@group(0) @binding(4) var<uniform> props: SymbolEvaluatedPropsUBO;
@group(1) @binding(0) var glyph_sampler: sampler;
@group(1) @binding(1) var glyph_image: texture_2d<f32>;
@group(1) @binding(2) var icon_sampler: sampler;
@group(1) @binding(3) var icon_image: texture_2d<f32>;
@fragment
fn main(in: FragmentInput) -> @location(0) vec4<f32> {
let tileProps = tilePropsVector[globalIndex.value];
#ifndef HAS_UNIFORM_u_fill_color
let fill_color = in.fill_color;
#else
let fill_color = select(props.icon_fill_color, props.text_fill_color, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_halo_color
let halo_color = in.halo_color;
#else
let halo_color = select(props.icon_halo_color, props.text_halo_color, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_opacity
let opacity = in.opacity;
#else
let opacity = select(props.icon_opacity, props.text_opacity, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_halo_width
let halo_width = in.halo_width;
#else
let halo_width = select(props.icon_halo_width, props.text_halo_width, tileProps.is_text != 0u);
#endif
#ifndef HAS_UNIFORM_u_halo_blur
let halo_blur = in.halo_blur;
#else
let halo_blur = select(props.icon_halo_blur, props.text_halo_blur, tileProps.is_text != 0u);
#endif
if (in.is_icon != 0.0) {
let alpha = opacity * in.fade_opacity;
let iconSample = textureSampleLevel(icon_image, icon_sampler, in.tex, 0.0);
return iconSample * alpha;
}
let EDGE_GAMMA = 0.105 / paintParams.pixel_ratio;
let color = select(fill_color, halo_color, tileProps.is_halo != 0u);
let fontGamma = in.fontScale * tileProps.gamma_scale;
let gamma = (select(0.0, halo_blur * 1.19 / SDF_PX, tileProps.is_halo != 0u) + EDGE_GAMMA) / fontGamma;
let gamma_scaled = gamma * in.gamma_scale;
// For halos, offset the inner edge outward to create the ring
var inner_edge = (256.0 - 64.0) / 256.0;
if (tileProps.is_halo != 0u) {
inner_edge = inner_edge + gamma_scaled;
}
let sample = textureSampleLevel(glyph_image, glyph_sampler, in.tex, 0.0);
let dist = select(sample.a, sample.r, tileProps.is_text != 0u);
var alpha = smoothstep(inner_edge - gamma_scaled, inner_edge + gamma_scaled, dist);
// When drawing halos, make the inside transparent so the fill can show through
if (tileProps.is_halo != 0u) {
let halo_edge = (6.0 - halo_width / in.fontScale) / SDF_PX;
alpha = min(smoothstep(halo_edge - gamma_scaled, halo_edge + gamma_scaled, dist), 1.0 - alpha);
}
let coverage = alpha * opacity * in.fade_opacity;
let outAlpha = color.a * coverage;
return vec4<f32>(color.rgb * coverage, outAlpha);
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <unordered_map>
namespace mbgl {
namespace webgpu {
namespace detail {
bool isDirective(const std::string& line, const std::string& directive);
std::string getDirectiveArgument(const std::string& line);
std::string preprocessWGSL(const std::string& source, const std::unordered_map<std::string, bool>& defines);
} // namespace detail
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,82 @@
#pragma once
#include <mbgl/shaders/shader_source.hpp>
#include <mbgl/shaders/webgpu/shader_program.hpp>
#include <mbgl/shaders/widevector_ubo.hpp>
namespace mbgl {
namespace shaders {
template <>
struct ShaderSource<BuiltIn::WideVectorShader, gfx::Backend::Type::WebGPU> {
static constexpr const char* name = "WideVectorShader";
static const std::array<AttributeInfo, 3> attributes;
static const std::array<AttributeInfo, 4> instanceAttributes;
static const std::array<TextureInfo, 0> textures;
// Simplified wide vector shader for WebGPU
static constexpr auto vertex = R"(
struct VertexInput {
@location(5) position: vec3<f32>,
@location(6) normal: vec2<f32>,
@location(7) texCoord: vec2<f32>,
};
struct InstanceInput {
@location(8) instanceCenter: vec3<f32>,
@location(9) instanceColor: vec4<f32>,
@location(10) instancePrev: i32,
@location(11) instanceNext: i32,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) color: vec4<f32>,
@location(1) texCoord: vec2<f32>,
};
struct WideVectorUBO {
mvpMatrix: mat4x4<f32>,
mvpMatrixDiff: mat4x4<f32>,
mvMatrix: mat4x4<f32>,
mvMatrixDiff: mat4x4<f32>,
pMatrix: mat4x4<f32>,
pMatrixDiff: mat4x4<f32>,
frameSize: vec2<f32>,
color: vec4<f32>,
width: f32,
offset: f32,
edge: f32,
texRepeat: f32,
texOffset: vec2<f32>,
};
@group(0) @binding(0) var<uniform> ubo: WideVectorUBO;
@vertex
fn main(in: VertexInput, instance: InstanceInput) -> VertexOutput {
var out: VertexOutput;
// Calculate position with offset based on normal
let worldPos = instance.instanceCenter + vec3<f32>(in.normal * ubo.width, 0.0);
out.position = ubo.mvpMatrix * vec4<f32>(worldPos, 1.0);
// Pass through color and texture coordinates
out.color = instance.instanceColor * ubo.color;
out.texCoord = in.texCoord * ubo.texRepeat + ubo.texOffset;
return out;
}
)";
static constexpr auto fragment = R"(
@fragment
fn main(@location(0) color: vec4<f32>, @location(1) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
// Simple color output with alpha blending support
return color;
}
)";
};
} // namespace shaders
} // namespace mbgl

View File

@@ -0,0 +1,65 @@
#pragma once
#include <webgpu/webgpu.h>
#include <cstdint>
#include <cstddef>
#include <vector>
namespace mbgl {
namespace webgpu {
class Context;
class BufferResource {
public:
BufferResource() noexcept = delete;
BufferResource(Context& context,
const void* data,
std::size_t size,
uint32_t usage,
bool isIndexBuffer = false,
bool persistent = false);
BufferResource(BufferResource&&) noexcept;
virtual ~BufferResource() noexcept;
BufferResource& operator=(BufferResource&&) noexcept;
BufferResource clone() const;
void update(const void* data, std::size_t size, std::size_t offset) noexcept;
std::size_t getSizeInBytes() const noexcept { return size; }
const void* contents() const noexcept { return raw.empty() ? nullptr : raw.data(); }
Context& getContext() const noexcept { return context; }
WGPUBuffer getBuffer() const noexcept { return buffer; }
bool isValid() const noexcept { return buffer || !raw.empty(); }
operator bool() const noexcept { return isValid(); }
bool operator!() const noexcept { return !isValid(); }
using VersionType = std::uint16_t;
/// Used to detect whether buffer contents have changed
VersionType getVersion() const noexcept { return version; }
/// Indicates whether this buffer needs to be re-bound from a previous binding at the given version
bool needReBind(VersionType version) const noexcept;
uint32_t getUsage() const { return usage; }
bool isPersistent() const { return persistent; }
protected:
Context& context;
WGPUBuffer buffer = nullptr;
std::vector<std::uint8_t> raw;
std::size_t size = 0;
uint32_t usage = 0;
std::uint16_t version = 0;
bool isIndexBuffer = false;
bool persistent = false;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,84 @@
#pragma once
#include <mbgl/gfx/command_encoder.hpp>
#include <mbgl/util/containers.hpp>
#include <webgpu/webgpu.h>
#include <functional>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
namespace mbgl {
namespace gfx {
class Renderable;
} // namespace gfx
namespace webgpu {
class Context;
class RenderPass;
class UploadPass;
class CommandEncoder final : public gfx::CommandEncoder {
public:
explicit CommandEncoder(Context& context_);
~CommandEncoder() override;
webgpu::Context& getContext() { return context; }
const webgpu::Context& getContext() const { return context; }
WGPUCommandEncoder getEncoder() const { return encoder; }
std::unique_ptr<gfx::UploadPass> createUploadPass(const char* name, gfx::Renderable&) override;
std::unique_ptr<gfx::RenderPass> createRenderPass(const char* name, const gfx::RenderPassDescriptor&) override;
void present(gfx::Renderable&) override;
// WebGPU-specific: submit command buffer directly
void submitCommandBuffer();
template <typename TFunc>
void visitDebugGroups(TFunc f) {
for (const auto& item : debugGroupNames) {
f(item.name);
}
}
private:
friend class RenderPass;
friend class UploadPass;
// Track a RenderPass, propagating debug groups to it
void trackRenderPass(RenderPass*);
// Stop tracking a RenderPass
void forgetRenderPass(RenderPass*);
// Track an UploadPass, propagating debug groups to it
void trackUploadPass(UploadPass*);
// Stop tracking a RenderPass
void forgetUploadPass(UploadPass*);
void pushDebugGroup(const char* name) override;
void popDebugGroup() override;
protected:
struct GroupEntry {
std::string name;
std::size_t renderGroups;
std::size_t uploadGroups;
};
protected:
webgpu::Context& context;
WGPUCommandEncoder encoder = nullptr;
std::vector<GroupEntry> debugGroupNames;
std::vector<gfx::DebugGroup<gfx::RenderPass>> renderDebugGroups;
std::vector<gfx::DebugGroup<gfx::UploadPass>> uploadDebugGroups;
mbgl::unordered_set<RenderPass*> renderPasses;
mbgl::unordered_set<UploadPass*> uploadPasses;
UploadPass* currentUploadPass = nullptr;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,132 @@
#pragma once
#include <mbgl/gfx/context.hpp>
#include <mbgl/gfx/vertex_buffer.hpp>
#include <mbgl/webgpu/renderer_backend.hpp>
#include <mbgl/webgpu/buffer_resource.hpp>
#include <memory>
#include <optional>
#include <vector>
namespace mbgl {
class RenderStaticData;
namespace gfx {
// Forward declaration
class AttributeBinding;
using AttributeBindingArray = std::vector<std::optional<AttributeBinding>>;
class RenderPass;
class Renderable;
class ShaderProgramBase;
} // namespace gfx
namespace shaders {
struct ClipUBO;
} // namespace shaders
namespace webgpu {
// Forward declaration
class VertexBufferResource;
class Context : public gfx::Context {
public:
explicit Context(RendererBackend& backend);
~Context() override;
RendererBackend& getBackend() { return backend; }
const RendererBackend& getBackend() const { return backend; }
// Frame management
void beginFrame() override;
void endFrame() override;
void performCleanup() override;
void reduceMemoryUsage() override;
// Resource creation
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType, bool, bool);
std::unique_ptr<gfx::OffscreenTexture> createOffscreenTexture(Size, gfx::TextureChannelDataType) override;
std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
gfx::VertexAttributeArrayPtr createVertexAttributeArray() const override;
gfx::UniqueDrawableBuilder createDrawableBuilder(std::string name) override;
gfx::UniformBufferPtr createUniformBuffer(const void* data,
std::size_t size,
bool persistent = false,
bool ssbo = false) override;
gfx::UniqueUniformBufferArray createLayerUniformBufferArray() override;
gfx::ShaderProgramBasePtr getGenericShader(gfx::ShaderRegistry&, const std::string& name) override;
TileLayerGroupPtr createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) override;
LayerGroupPtr createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) override;
gfx::Texture2DPtr createTexture2D() override;
RenderTargetPtr createRenderTarget(const Size size, const gfx::TextureChannelDataType type) override;
// State management
void resetState(gfx::DepthMode depthMode, gfx::ColorMode colorMode) override;
void setDirtyState() override;
void clearStencilBuffer(int32_t) override;
// Uniform buffer management
bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&,
const void* data,
std::size_t size,
bool persistent = false) override;
const gfx::UniformBufferArray& getGlobalUniformBuffers() const override;
gfx::UniformBufferArray& mutableGlobalUniformBuffers() override;
void bindGlobalUniformBuffers(gfx::RenderPass&) const noexcept override;
void unbindGlobalUniformBuffers(gfx::RenderPass&) const noexcept override;
#if !defined(NDEBUG)
void visualizeStencilBuffer() override;
void visualizeDepthBuffer(float depthRangeSize) override;
#endif
// Buffer creation (aligned with Metal)
BufferResource createBuffer(
const void* data, std::size_t size, uint32_t usage, bool isIndexBuffer, bool persistent) const;
// Get reusable buffers (aligned with Metal)
const BufferResource& getTileVertexBuffer();
const BufferResource& getTileIndexBuffer();
// Tile clipping mask rendering
bool renderTileClippingMasks(gfx::RenderPass& renderPass,
RenderStaticData& staticData,
const std::vector<shaders::ClipUBO>& tileUBOs);
// Command encoder tracking
class CommandEncoder* getCurrentCommandEncoder() const { return currentCommandEncoder; }
void setCurrentCommandEncoder(class CommandEncoder* encoder) { currentCommandEncoder = encoder; }
// Vertex binding creation - needed for drawable geometry
gfx::AttributeBindingArray getOrCreateVertexBindings(gfx::Context&,
const gfx::AttributeDataType vertexType,
const size_t vertexAttributeIndex,
const std::vector<uint8_t>& vertexData,
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
gfx::BufferUsageType,
const std::optional<std::chrono::duration<double>>&,
std::shared_ptr<webgpu::VertexBufferResource>&) noexcept;
protected:
std::unique_ptr<gfx::RenderbufferResource> createRenderbufferResource(gfx::RenderbufferPixelType, Size) override;
std::unique_ptr<gfx::DrawScopeResource> createDrawScopeResource() override;
private:
RendererBackend& backend;
std::unique_ptr<gfx::UniformBufferArray> globalUniformBuffers;
class CommandEncoder* currentCommandEncoder = nullptr;
// Cached buffers (aligned with Metal)
std::optional<BufferResource> tileVertexBuffer;
std::optional<BufferResource> tileIndexBuffer;
// Cached clipping resources
gfx::ShaderProgramBasePtr clipMaskShader;
const gfx::Renderable* clipMaskRenderable = nullptr;
std::optional<std::size_t> clipMaskPipelineHash;
std::vector<BufferResource> clipMaskUniformBuffers;
std::vector<WGPUBindGroup> clipMaskActiveBindGroups;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,20 @@
#pragma once
#include <mbgl/gfx/draw_scope.hpp>
namespace mbgl {
namespace webgpu {
class Context;
class DrawScopeResource : public gfx::DrawScopeResource {
public:
explicit DrawScopeResource(Context& context);
~DrawScopeResource() override = default;
private:
Context& context;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,64 @@
#pragma once
#include <mbgl/gfx/drawable.hpp>
#include <mbgl/gfx/draw_mode.hpp>
#include <webgpu/webgpu.h>
#include <memory>
#include <vector>
// Forward declare WebGPU types
typedef struct WGPUBindGroupLayoutImpl* WGPUBindGroupLayout;
namespace mbgl {
namespace gfx {
class UploadPass;
class DepthMode;
class StencilMode;
} // namespace gfx
namespace webgpu {
class CommandEncoder;
class UploadPass;
class RenderPipeline;
class Drawable : public gfx::Drawable {
public:
Drawable(std::string name);
~Drawable() override;
void upload(gfx::UploadPass&);
void draw(PaintParameters&) const override;
void setIndexData(gfx::IndexVectorBasePtr, std::vector<UniqueDrawSegment> segments) override;
void setVertices(std::vector<uint8_t>&&, std::size_t, gfx::AttributeDataType) override;
const gfx::UniformBufferArray& getUniformBuffers() const override;
gfx::UniformBufferArray& mutableUniformBuffers() override;
void setColorMode(const gfx::ColorMode&) override;
void setShader(gfx::ShaderProgramBasePtr) override;
void setEnableStencil(bool) override;
void setEnableDepth(bool) override;
void setSubLayerIndex(int32_t) override;
void setDepthType(gfx::DepthMaskType) override;
void setDepthModeFor3D(const gfx::DepthMode&);
void setStencilModeFor3D(const gfx::StencilMode&);
void setVertexAttrId(const size_t);
void updateVertexAttributes(gfx::VertexAttributeArrayPtr,
std::size_t vertexCount,
gfx::DrawMode,
gfx::IndexVectorBasePtr,
const SegmentBase* segments,
std::size_t segmentCount) override;
class Impl;
const std::unique_ptr<Impl> impl;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#pragma once
#include <mbgl/gfx/drawable_builder.hpp>
#include <mbgl/webgpu/drawable.hpp>
#include <memory>
#include <string>
namespace mbgl {
namespace webgpu {
class Context;
class DrawableBuilder : public gfx::DrawableBuilder {
public:
explicit DrawableBuilder(std::string name);
~DrawableBuilder() override;
protected:
std::unique_ptr<gfx::Drawable> createDrawable() const override;
std::unique_ptr<gfx::Drawable::DrawSegment> createSegment(gfx::DrawMode, SegmentBase&&) override;
void init() override;
private:
// WebGPU-specific drawable configuration is handled in the drawable implementation
// Pipeline, buffers, and textures are managed by the Context and Drawable classes
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <mbgl/gfx/index_buffer.hpp>
#include <mbgl/webgpu/buffer_resource.hpp>
namespace mbgl {
namespace webgpu {
class IndexBufferResource : public gfx::IndexBufferResource {
public:
IndexBufferResource(BufferResource&&) noexcept;
IndexBufferResource(IndexBufferResource&& other) noexcept
: buffer(std::move(other.buffer)) {}
~IndexBufferResource() noexcept override;
std::size_t getSizeInBytes() const noexcept { return buffer.getSizeInBytes(); }
const void* contents() const noexcept { return buffer.contents(); }
BufferResource& get() noexcept { return buffer; }
const BufferResource& get() const noexcept { return buffer; }
// Compatibility with existing code
const BufferResource& getBuffer() const { return buffer; }
BufferResource& getBuffer() { return buffer; }
void update(const void* data, std::size_t size) { buffer.update(data, size, 0); }
protected:
BufferResource buffer;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,31 @@
#pragma once
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/webgpu/uniform_buffer.hpp>
namespace mbgl {
class PaintParameters;
namespace webgpu {
/**
* A layer group for non-tile-based drawables
*/
class LayerGroup : public mbgl::LayerGroup {
public:
LayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name);
~LayerGroup() override {}
void upload(gfx::UploadPass&) override;
void render(RenderOrchestrator&, PaintParameters&) override;
const gfx::UniformBufferArray& getUniformBuffers() const override { return uniformBuffers; }
gfx::UniformBufferArray& mutableUniformBuffers() override { return uniformBuffers; }
protected:
webgpu::UniformBufferArray uniformBuffers;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,22 @@
#pragma once
#include <mbgl/gfx/offscreen_texture.hpp>
#include <mbgl/gfx/types.hpp>
namespace mbgl {
namespace webgpu {
class Context;
class Texture2D;
class OffscreenTexture final : public gfx::OffscreenTexture {
public:
OffscreenTexture(Context&, Size size, gfx::TextureChannelDataType type, bool depth, bool stencil);
bool isRenderable() override;
PremultipliedImage readStillImage() override;
const gfx::Texture2DPtr& getTexture() override;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,52 @@
#pragma once
#include <mbgl/gfx/render_pass.hpp>
#include <memory>
// Forward declare WebGPU types
typedef struct WGPURenderPassEncoderImpl* WGPURenderPassEncoder;
namespace mbgl {
namespace gfx {
class CommandEncoder;
class UniformBufferArray;
} // namespace gfx
namespace webgpu {
class CommandEncoder;
class RenderPass final : public gfx::RenderPass {
public:
RenderPass(CommandEncoder& commandEncoder, const char* name, const gfx::RenderPassDescriptor& descriptor);
~RenderPass() override;
// Match Metal's interface
CommandEncoder& getCommandEncoder() { return commandEncoder; }
const CommandEncoder& getCommandEncoder() const { return commandEncoder; }
// Get the WebGPU render pass encoder (similar to Metal's getMetalEncoder)
WGPURenderPassEncoder getEncoder() const;
const gfx::RenderPassDescriptor& getDescriptor() const { return descriptor; }
// Set/get global uniform buffers for drawables to access
void setGlobalUniformBuffers(const gfx::UniformBufferArray* buffers);
const gfx::UniformBufferArray* getGlobalUniformBuffers() const;
private:
void pushDebugGroup(const char* name) override;
void popDebugGroup() override;
void addDebugSignpost(const char* name) override;
private:
gfx::RenderPassDescriptor descriptor;
CommandEncoder& commandEncoder;
const gfx::DebugGroup<gfx::CommandEncoder> debugGroup;
// Implementation details
class Impl;
std::unique_ptr<Impl> impl;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,54 @@
#pragma once
#include <mbgl/gfx/renderable.hpp>
#include <mbgl/webgpu/wgpu_cpp_compat.hpp>
#include <cstdlib>
#include <optional>
namespace mbgl {
namespace webgpu {
class RendererBackend;
class RenderableResource : public gfx::RenderableResource {
protected:
explicit RenderableResource() = default;
public:
void bind() override {
// WebGPU binding happens at the command encoder level
// This is a no-op for now
}
virtual void swap() {
// Renderable resources that require a swap function to be called
// explicitly can override this method.
}
virtual const mbgl::webgpu::RendererBackend& getBackend() const = 0;
virtual const WGPUCommandEncoder& getCommandEncoder() const = 0;
virtual WGPURenderPassEncoder getRenderPassEncoder() const = 0;
/**
* Obtain a texture view that should be bound as the color attachment for
* the render pass targeting this resource. Implementations should return a
* valid `WGPUTextureView` or `nullptr` if the resource relies on the
* backend to supply the swapchain view (e.g. default framebuffer).
*/
virtual WGPUTextureView getColorTextureView() { return nullptr; }
virtual std::optional<wgpu::TextureFormat> getColorTextureFormat() const { return std::nullopt; }
/**
* Obtain a depth/stencil texture view for the render pass if the resource
* manages one. Returning `nullptr` indicates that the backend-provided
* depth/stencil view (if any) should be used instead.
*/
virtual WGPUTextureView getDepthStencilTextureView() { return nullptr; }
virtual std::optional<wgpu::TextureFormat> getDepthStencilTextureFormat() const { return std::nullopt; }
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,62 @@
#pragma once
#include <mbgl/gfx/renderer_backend.hpp>
#include <mbgl/util/size.hpp>
#include <mbgl/webgpu/wgpu_cpp_compat.hpp>
#include <memory>
namespace mbgl {
class ProgramParameters;
namespace gfx {
class ShaderRegistry;
}
namespace webgpu {
class Context;
class RendererBackend : public gfx::RendererBackend {
public:
explicit RendererBackend(gfx::ContextMode);
~RendererBackend() override;
// getDefaultRenderable() is pure virtual - must be implemented by platform backends
// Platform backends typically inherit from gfx::Renderable and return *this
void initShaders(gfx::ShaderRegistry&, const ProgramParameters&) override;
// Platform-specific surface creation
void setSurface(void* nativeWindow);
// WebGPU-specific methods
void setInstance(void* instance);
void setDevice(void* device);
void setQueue(void* queue);
void* getInstance() const;
void* getDevice() const;
void* getQueue() const;
void* getSurface() const;
// Surface texture access - can be overridden by platform backends
virtual void* getCurrentTextureView();
virtual void* getDepthStencilView();
virtual mbgl::Size getFramebufferSize() const;
void setDepthStencilFormat(wgpu::TextureFormat);
wgpu::TextureFormat getDepthStencilFormat() const;
void setColorFormat(wgpu::TextureFormat);
wgpu::TextureFormat getColorFormat() const;
protected:
std::unique_ptr<gfx::Context> createContext() override;
void activate() override;
void deactivate() override;
private:
class Impl;
std::unique_ptr<Impl> impl;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,64 @@
#pragma once
#include <mbgl/gfx/texture2d.hpp>
#include <webgpu/webgpu.h>
#include <mbgl/util/image.hpp>
#include <memory>
namespace mbgl {
namespace webgpu {
class Context;
class RenderPass;
class Texture2D : public gfx::Texture2D {
public:
Texture2D(Context& context_);
~Texture2D() override;
gfx::Texture2D& setSamplerConfiguration(const SamplerState&) noexcept override;
gfx::Texture2D& setFormat(gfx::TexturePixelType, gfx::TextureChannelDataType) noexcept override;
gfx::Texture2D& setSize(Size size_) noexcept override;
gfx::Texture2D& setImage(std::shared_ptr<PremultipliedImage>) noexcept override;
Texture2D& setUsage(uint32_t usageFlags_) noexcept;
void setSizeChangedCallback(std::function<void(const Size&)>) noexcept;
WGPUTextureFormat getNativeFormat() const noexcept { return nativeFormat; }
gfx::TexturePixelType getFormat() const noexcept override { return pixelFormat; }
Size getSize() const noexcept override { return size; }
size_t getDataSize() const noexcept override;
size_t getPixelStride() const noexcept override;
size_t numChannels() const noexcept override;
void create() noexcept override;
void upload(const void* pixelData, const Size& size) noexcept override;
void upload() noexcept override;
void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) noexcept override;
bool needsUpload() const noexcept override { return dirty; }
WGPUTexture getTexture() const { return texture; }
WGPUTextureView getTextureView() const { return textureView; }
WGPUSampler getSampler() const { return sampler; }
const SamplerState& getSamplerState() const noexcept { return samplerState; }
protected:
Context& context;
WGPUTexture texture = nullptr;
WGPUTextureView textureView = nullptr;
WGPUSampler sampler = nullptr;
uint32_t usageFlags = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst | WGPUTextureUsage_CopySrc;
WGPUTextureFormat nativeFormat = WGPUTextureFormat_Undefined;
Size size{0, 0};
gfx::TexturePixelType pixelFormat = gfx::TexturePixelType::RGBA;
gfx::TextureChannelDataType channelType = gfx::TextureChannelDataType::UnsignedByte;
SamplerState samplerState;
std::shared_ptr<PremultipliedImage> image;
bool dirty = false;
std::function<void(const Size&)> sizeChangedCallback;
};
} // namespace webgpu
} // namespace mbgl
#include <functional>

View File

@@ -0,0 +1,31 @@
#pragma once
#include <mbgl/renderer/layer_group.hpp>
#include <mbgl/webgpu/uniform_buffer.hpp>
namespace mbgl {
class PaintParameters;
namespace webgpu {
/**
* A layer group for tile-based drawables
*/
class TileLayerGroup : public mbgl::TileLayerGroup {
public:
TileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name);
~TileLayerGroup() override {}
void upload(gfx::UploadPass&) override;
void render(RenderOrchestrator&, PaintParameters&) override;
const gfx::UniformBufferArray& getUniformBuffers() const override { return uniformBuffers; }
gfx::UniformBufferArray& mutableUniformBuffers() override { return uniformBuffers; }
protected:
webgpu::UniformBufferArray uniformBuffers;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,60 @@
#pragma once
#include <mbgl/gfx/uniform_buffer.hpp>
#include <webgpu/webgpu.h>
namespace mbgl {
namespace webgpu {
class Context;
class RenderPass;
class UniformBuffer final : public gfx::UniformBuffer {
public:
UniformBuffer(Context& context, const void* data, std::size_t size);
~UniformBuffer() override;
UniformBuffer(const UniformBuffer&) = delete;
UniformBuffer(UniformBuffer&& other) noexcept;
UniformBuffer& operator=(const UniformBuffer&) = delete;
UniformBuffer& operator=(UniformBuffer&&) = delete;
UniformBuffer clone() const;
void update(const void* data, std::size_t dataSize) override;
WGPUBuffer getBuffer() const { return buffer; }
private:
Context& context;
WGPUBuffer buffer = nullptr;
};
/// Stores a collection of uniform buffers by name
class UniformBufferArray final : public gfx::UniformBufferArray {
public:
UniformBufferArray() = default;
UniformBufferArray(UniformBufferArray&& other)
: gfx::UniformBufferArray(std::move(other)) {}
UniformBufferArray(const UniformBufferArray&) = delete;
UniformBufferArray& operator=(UniformBufferArray&& other) {
gfx::UniformBufferArray::operator=(std::move(other));
return *this;
}
UniformBufferArray& operator=(const UniformBufferArray& other) {
gfx::UniformBufferArray::operator=(other);
return *this;
}
void bindWebgpu(RenderPass&) const noexcept;
void bind(gfx::RenderPass& renderPass) override;
private:
gfx::UniqueUniformBuffer copy(const gfx::UniformBuffer& buffer) override {
return std::make_unique<UniformBuffer>(static_cast<const UniformBuffer&>(buffer).clone());
}
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,70 @@
#pragma once
#include <mbgl/gfx/index_buffer.hpp>
#include <mbgl/gfx/vertex_buffer.hpp>
#include <mbgl/gfx/upload_pass.hpp>
#include <mbgl/gfx/renderbuffer.hpp>
namespace mbgl {
namespace gfx {
class CommandEncoder;
class Renderable;
} // namespace gfx
namespace webgpu {
class CommandEncoder;
class RenderbufferResource : public gfx::RenderbufferResource {
public:
RenderbufferResource() = default;
};
class UploadPass final : public gfx::UploadPass {
public:
UploadPass(gfx::Renderable& renderable, CommandEncoder& commandEncoder, const char* name);
~UploadPass() override;
private:
void pushDebugGroup(const char* name) override;
void popDebugGroup() override;
public:
gfx::Context& getContext() override;
const gfx::Context& getContext() const override;
std::unique_ptr<gfx::VertexBufferResource> createVertexBufferResource(const void* data,
std::size_t size,
gfx::BufferUsageType,
bool persistent) override;
void updateVertexBufferResource(gfx::VertexBufferResource&, const void* data, std::size_t size) override;
std::unique_ptr<gfx::IndexBufferResource> createIndexBufferResource(const void* data,
std::size_t size,
gfx::BufferUsageType,
bool persistent) override;
void updateIndexBufferResource(gfx::IndexBufferResource&, const void* data, std::size_t size) override;
gfx::AttributeBindingArray buildAttributeBindings(
const std::size_t vertexCount,
const gfx::AttributeDataType vertexType,
const std::size_t vertexAttributeIndex,
const std::vector<std::uint8_t>& vertexData,
const gfx::VertexAttributeArray& defaults,
const gfx::VertexAttributeArray& overrides,
gfx::BufferUsageType,
const std::optional<std::chrono::duration<double>> lastUpdate,
/*out*/ std::vector<std::unique_ptr<gfx::VertexBufferResource>>& outBuffers) override;
const gfx::UniqueVertexBufferResource& getBuffer(const gfx::VertexVectorBasePtr&,
gfx::BufferUsageType,
bool forceUpdate);
private:
CommandEncoder& commandEncoder;
std::vector<gfx::DebugGroup<gfx::UploadPass>> debugGroups;
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,52 @@
#pragma once
#include <mbgl/gfx/types.hpp>
#include <mbgl/gfx/vertex_attribute.hpp>
namespace mbgl {
namespace gfx {
class VertexBufferResource;
} // namespace gfx
namespace webgpu {
class UploadPass;
class VertexAttribute final : public gfx::VertexAttribute {
public:
VertexAttribute(int index_, gfx::AttributeDataType dataType_, std::size_t count_)
: gfx::VertexAttribute(index_, dataType_, count_) {}
VertexAttribute(const VertexAttribute& other) = delete;
VertexAttribute(VertexAttribute&& other)
: gfx::VertexAttribute(std::move(other)) {}
~VertexAttribute() override = default;
static const gfx::UniqueVertexBufferResource& getBuffer(gfx::VertexAttribute&,
UploadPass&,
const gfx::BufferUsageType,
bool forceUpdate);
};
/// Stores a collection of vertex attributes by name
class VertexAttributeArray final : public gfx::VertexAttributeArray {
public:
VertexAttributeArray() = default;
VertexAttributeArray(VertexAttributeArray&& other)
: gfx::VertexAttributeArray(std::move(other)) {}
VertexAttributeArray& operator=(VertexAttributeArray&& other) {
gfx::VertexAttributeArray::operator=(std::move(other));
return *this;
}
VertexAttributeArray& operator=(const VertexAttributeArray& other) = delete;
/// Indicates whether any values have changed
bool isModifiedAfter(std::chrono::duration<double> time) const;
private:
gfx::UniqueVertexAttribute create(int index, gfx::AttributeDataType dataType, std::size_t count) const override {
return std::make_unique<VertexAttribute>(index, dataType, count);
}
};
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,44 @@
#pragma once
#include <mbgl/gfx/vertex_buffer.hpp>
#include <mbgl/webgpu/buffer_resource.hpp>
#include <mbgl/util/monotonic_timer.hpp>
#include <memory>
namespace mbgl {
namespace webgpu {
class Context;
class VertexBufferResource : public gfx::VertexBufferResource {
public:
VertexBufferResource(BufferResource&&) noexcept;
VertexBufferResource(VertexBufferResource&& other) noexcept
: buffer(std::move(other.buffer)) {}
~VertexBufferResource() noexcept override;
std::size_t getSizeInBytes() const noexcept { return buffer.getSizeInBytes(); }
const void* contents() const noexcept { return buffer.contents(); }
BufferResource& get() noexcept { return buffer; }
const BufferResource& get() const noexcept { return buffer; }
// Compatibility with existing code
const BufferResource& getBuffer() const { return buffer; }
BufferResource& getBuffer() { return buffer; }
std::chrono::duration<double> getLastUpdated() const { return lastUpdated; }
void setLastUpdated(std::chrono::duration<double> time) { lastUpdated = time; }
void update(const void* data, std::size_t size) { buffer.update(data, size, 0); }
protected:
BufferResource buffer;
std::chrono::duration<double> lastUpdated;
};
using UniqueVertexBufferResource = std::unique_ptr<VertexBufferResource>;
} // namespace webgpu
} // namespace mbgl

View File

@@ -0,0 +1,19 @@
#pragma once
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic ignored "-Wstrict-aliasing"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif
#include <webgpu/webgpu_cpp.h>
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif