Add WebGPU Backend w/ Dawn Support (#3838)
This commit is contained in:
@@ -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)...);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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
|
||||
|
||||
230
include/mbgl/shaders/webgpu/background.hpp
Normal file
230
include/mbgl/shaders/webgpu/background.hpp
Normal 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
|
||||
211
include/mbgl/shaders/webgpu/circle.hpp
Normal file
211
include/mbgl/shaders/webgpu/circle.hpp
Normal 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
|
||||
65
include/mbgl/shaders/webgpu/clipping_mask.hpp
Normal file
65
include/mbgl/shaders/webgpu/clipping_mask.hpp
Normal 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
|
||||
240
include/mbgl/shaders/webgpu/collision.hpp
Normal file
240
include/mbgl/shaders/webgpu/collision.hpp
Normal 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
|
||||
90
include/mbgl/shaders/webgpu/common.hpp
Normal file
90
include/mbgl/shaders/webgpu/common.hpp
Normal 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
|
||||
66
include/mbgl/shaders/webgpu/custom_geometry.hpp
Normal file
66
include/mbgl/shaders/webgpu/custom_geometry.hpp
Normal 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
|
||||
68
include/mbgl/shaders/webgpu/custom_symbol_icon.hpp
Normal file
68
include/mbgl/shaders/webgpu/custom_symbol_icon.hpp
Normal 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
|
||||
80
include/mbgl/shaders/webgpu/debug.hpp
Normal file
80
include/mbgl/shaders/webgpu/debug.hpp
Normal 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
|
||||
910
include/mbgl/shaders/webgpu/fill.hpp
Normal file
910
include/mbgl/shaders/webgpu/fill.hpp
Normal 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
|
||||
411
include/mbgl/shaders/webgpu/fill_extrusion.hpp
Normal file
411
include/mbgl/shaders/webgpu/fill_extrusion.hpp
Normal 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
|
||||
127
include/mbgl/shaders/webgpu/heatmap.hpp
Normal file
127
include/mbgl/shaders/webgpu/heatmap.hpp
Normal 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
|
||||
87
include/mbgl/shaders/webgpu/heatmap_texture.hpp
Normal file
87
include/mbgl/shaders/webgpu/heatmap_texture.hpp
Normal 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
|
||||
122
include/mbgl/shaders/webgpu/hillshade.hpp
Normal file
122
include/mbgl/shaders/webgpu/hillshade.hpp
Normal 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
|
||||
117
include/mbgl/shaders/webgpu/hillshade_prepare.hpp
Normal file
117
include/mbgl/shaders/webgpu/hillshade_prepare.hpp
Normal 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
|
||||
1027
include/mbgl/shaders/webgpu/line.hpp
Normal file
1027
include/mbgl/shaders/webgpu/line.hpp
Normal file
File diff suppressed because it is too large
Load Diff
109
include/mbgl/shaders/webgpu/location_indicator.hpp
Normal file
109
include/mbgl/shaders/webgpu/location_indicator.hpp
Normal 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
|
||||
134
include/mbgl/shaders/webgpu/raster.hpp
Normal file
134
include/mbgl/shaders/webgpu/raster.hpp
Normal 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
|
||||
109
include/mbgl/shaders/webgpu/shader_group.hpp
Normal file
109
include/mbgl/shaders/webgpu/shader_group.hpp
Normal 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
|
||||
169
include/mbgl/shaders/webgpu/shader_program.hpp
Normal file
169
include/mbgl/shaders/webgpu/shader_program.hpp
Normal 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
|
||||
752
include/mbgl/shaders/webgpu/symbol.hpp
Normal file
752
include/mbgl/shaders/webgpu/symbol.hpp
Normal 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
|
||||
16
include/mbgl/shaders/webgpu/wgsl_preprocessor.hpp
Normal file
16
include/mbgl/shaders/webgpu/wgsl_preprocessor.hpp
Normal 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
|
||||
82
include/mbgl/shaders/webgpu/widevector.hpp
Normal file
82
include/mbgl/shaders/webgpu/widevector.hpp
Normal 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
|
||||
65
include/mbgl/webgpu/buffer_resource.hpp
Normal file
65
include/mbgl/webgpu/buffer_resource.hpp
Normal 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
|
||||
84
include/mbgl/webgpu/command_encoder.hpp
Normal file
84
include/mbgl/webgpu/command_encoder.hpp
Normal 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
|
||||
132
include/mbgl/webgpu/context.hpp
Normal file
132
include/mbgl/webgpu/context.hpp
Normal 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
|
||||
20
include/mbgl/webgpu/draw_scope_resource.hpp
Normal file
20
include/mbgl/webgpu/draw_scope_resource.hpp
Normal 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
|
||||
64
include/mbgl/webgpu/drawable.hpp
Normal file
64
include/mbgl/webgpu/drawable.hpp
Normal 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
|
||||
29
include/mbgl/webgpu/drawable_builder.hpp
Normal file
29
include/mbgl/webgpu/drawable_builder.hpp
Normal 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
|
||||
33
include/mbgl/webgpu/index_buffer_resource.hpp
Normal file
33
include/mbgl/webgpu/index_buffer_resource.hpp
Normal 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
|
||||
31
include/mbgl/webgpu/layer_group.hpp
Normal file
31
include/mbgl/webgpu/layer_group.hpp
Normal 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
|
||||
22
include/mbgl/webgpu/offscreen_texture.hpp
Normal file
22
include/mbgl/webgpu/offscreen_texture.hpp
Normal 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
|
||||
52
include/mbgl/webgpu/render_pass.hpp
Normal file
52
include/mbgl/webgpu/render_pass.hpp
Normal 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
|
||||
54
include/mbgl/webgpu/renderable_resource.hpp
Normal file
54
include/mbgl/webgpu/renderable_resource.hpp
Normal 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
|
||||
62
include/mbgl/webgpu/renderer_backend.hpp
Normal file
62
include/mbgl/webgpu/renderer_backend.hpp
Normal 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
|
||||
64
include/mbgl/webgpu/texture2d.hpp
Normal file
64
include/mbgl/webgpu/texture2d.hpp
Normal 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>
|
||||
31
include/mbgl/webgpu/tile_layer_group.hpp
Normal file
31
include/mbgl/webgpu/tile_layer_group.hpp
Normal 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
|
||||
60
include/mbgl/webgpu/uniform_buffer.hpp
Normal file
60
include/mbgl/webgpu/uniform_buffer.hpp
Normal 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
|
||||
70
include/mbgl/webgpu/upload_pass.hpp
Normal file
70
include/mbgl/webgpu/upload_pass.hpp
Normal 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
|
||||
52
include/mbgl/webgpu/vertex_attribute.hpp
Normal file
52
include/mbgl/webgpu/vertex_attribute.hpp
Normal 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
|
||||
44
include/mbgl/webgpu/vertex_buffer_resource.hpp
Normal file
44
include/mbgl/webgpu/vertex_buffer_resource.hpp
Normal 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
|
||||
19
include/mbgl/webgpu/wgpu_cpp_compat.hpp
Normal file
19
include/mbgl/webgpu/wgpu_cpp_compat.hpp
Normal 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
|
||||
Reference in New Issue
Block a user