Add WebGPU Backend w/ Dawn Support (#3838)
This commit is contained in:
7
.github/labeler.yml
vendored
7
.github/labeler.yml
vendored
@@ -56,3 +56,10 @@ metal:
|
||||
- any-glob-to-any-file:
|
||||
- '**/*metal*'
|
||||
- '**/*metal*/**'
|
||||
WebGPU:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- '**/*webgpu*'
|
||||
- '**/*webgpu*/**'
|
||||
- '**/*wgpu*'
|
||||
- '**/*dawn*'
|
||||
|
||||
6
.github/scripts/install-linux-deps
vendored
6
.github/scripts/install-linux-deps
vendored
@@ -12,4 +12,8 @@ DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \
|
||||
mesa-vulkan-drivers \
|
||||
llvm \
|
||||
glslang-dev \
|
||||
libbz2-dev
|
||||
libbz2-dev \
|
||||
libxinerama-dev \
|
||||
libxcursor-dev \
|
||||
libxi-dev \
|
||||
libx11-xcb-dev
|
||||
|
||||
75
.github/workflows/macos-ci.yml
vendored
75
.github/workflows/macos-ci.yml
vendored
@@ -8,12 +8,15 @@ on:
|
||||
paths:
|
||||
- 'platform/macos/**'
|
||||
- 'platform/darwin/**'
|
||||
- 'platform/glfw/**'
|
||||
- '.github/workflows/macos-ci.yml'
|
||||
- 'bin/**'
|
||||
- 'include/**'
|
||||
- 'platform/default/**'
|
||||
- 'render-test/**'
|
||||
- 'src/**'
|
||||
- 'vendor/**'
|
||||
- 'metrics/**'
|
||||
- '.gitmodules'
|
||||
- '!**/*.md'
|
||||
- 'WORKSPACE'
|
||||
@@ -21,6 +24,9 @@ on:
|
||||
- '.bazelrc'
|
||||
- '.bazelversion'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'CMakeLists.txt'
|
||||
- 'CMakePresets.json'
|
||||
- 'cmake/**'
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -28,12 +34,15 @@ on:
|
||||
paths:
|
||||
- 'platform/macos/**'
|
||||
- 'platform/darwin/**'
|
||||
- 'platform/glfw/**'
|
||||
- '.github/workflows/macos-ci.yml'
|
||||
- 'bin/**'
|
||||
- 'include/**'
|
||||
- 'platform/default/**'
|
||||
- 'render-test/**'
|
||||
- 'src/**'
|
||||
- 'vendor/**'
|
||||
- 'metrics/**'
|
||||
- '.gitmodules'
|
||||
- '!**/*.md'
|
||||
- 'WORKSPACE'
|
||||
@@ -41,6 +50,9 @@ on:
|
||||
- '.bazelrc'
|
||||
- '.bazelversion'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'CMakeLists.txt'
|
||||
- 'CMakePresets.json'
|
||||
- 'cmake/**'
|
||||
|
||||
concurrency:
|
||||
# cancel jobs on PRs only
|
||||
@@ -49,8 +61,8 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: MacOS CI Tests
|
||||
runs-on: [macos-14]
|
||||
name: MacOS Metal CI Tests
|
||||
runs-on: [macos-15]
|
||||
defaults:
|
||||
run:
|
||||
working-directory: platform/macos
|
||||
@@ -60,7 +72,7 @@ jobs:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Bazel
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
@@ -80,3 +92,60 @@ jobs:
|
||||
|
||||
- name: Generate Xcode project for macOS
|
||||
run: bazel run //platform/macos:xcodeproj --@rules_xcodeproj//xcodeproj:extra_common_flags="--//:renderer=metal"
|
||||
|
||||
webgpu-cmake:
|
||||
name: macOS WebGPU (${{ matrix.variant.name }})
|
||||
runs-on: macos-15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant:
|
||||
- impl: dawn
|
||||
name: Dawn
|
||||
# - impl: wgpu
|
||||
# name: wgpu-native
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4
|
||||
with:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||
run: |
|
||||
brew install cmake ninja pkg-config python@3.11 go glfw libuv icu4c jpeg-turbo webp libpng ccache
|
||||
brew link --overwrite python@3.11
|
||||
|
||||
- name: Configure environment
|
||||
run: |
|
||||
{
|
||||
echo "PKG_CONFIG_PATH=$(brew --prefix icu4c)/lib/pkgconfig:$(brew --prefix libuv)/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
||||
echo "PATH=$(brew --prefix python@3.11)/bin:$(brew --prefix go)/bin:$PATH"
|
||||
echo "CC=clang"
|
||||
echo "CXX=clang++"
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Configure build
|
||||
run: cmake --preset macos-webgpu-${{ matrix.variant.impl }}
|
||||
|
||||
- name: Build targets
|
||||
run: |
|
||||
cmake --build build-macos-webgpu-${{ matrix.variant.impl }} --target \
|
||||
mbgl-render-test-runner
|
||||
|
||||
- name: Prepare render test output directory
|
||||
run: mkdir -p metrics/macos-webgpu-${{ matrix.variant.impl }}
|
||||
|
||||
- name: Run WebGPU render tests
|
||||
id: render_test
|
||||
run: build-macos-webgpu-${{ matrix.variant.impl }}/mbgl-render-test-runner --manifestPath metrics/macos-webgpu-${{ matrix.variant.impl }}.json
|
||||
|
||||
- name: Upload render report
|
||||
if: always() && steps.render_test.outcome == 'failure'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: macos-webgpu-${{ matrix.variant.impl }}-render-report
|
||||
path: metrics/macos-webgpu-${{ matrix.variant.impl }}.html
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -36,6 +36,7 @@ compile_commands.json
|
||||
node_modules
|
||||
.cxx
|
||||
.env
|
||||
vendor/dawn/
|
||||
|
||||
# Node binaries.
|
||||
/lib
|
||||
|
||||
15
BUILD.bazel
15
BUILD.bazel
@@ -12,6 +12,8 @@ load(
|
||||
"MLN_DRAWABLES_MTL_HEADERS",
|
||||
"MLN_DRAWABLES_MTL_SOURCE",
|
||||
"MLN_DRAWABLES_SOURCE",
|
||||
"MLN_DRAWABLES_WEBGPU_HEADERS",
|
||||
"MLN_DRAWABLES_WEBGPU_SOURCE",
|
||||
"MLN_GENERATED_OPENGL_SHADER_HEADERS",
|
||||
"MLN_GENERATED_SHADER_HEADERS",
|
||||
"MLN_GENERATED_SHADER_SOURCE",
|
||||
@@ -85,6 +87,7 @@ cc_library(
|
||||
MLN_GENERATED_STYLE_SOURCE + select({
|
||||
":drawable_renderer": MLN_OPENGL_SOURCE + MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_GL_SOURCE,
|
||||
":metal_renderer": MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_MTL_SOURCE,
|
||||
":webgpu_renderer": MLN_DRAWABLES_SOURCE + MLN_DRAWABLES_WEBGPU_SOURCE,
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//:harfbuzz_text_shaping": MLN_SHAPING_HARFBUZZ_SRCS,
|
||||
@@ -93,6 +96,7 @@ cc_library(
|
||||
hdrs = MLN_LAYER_PLUGIN_HEADERS + MLN_CORE_HEADERS + select({
|
||||
":drawable_renderer": MLN_OPENGL_HEADERS + MLN_DRAWABLES_HEADERS + MLN_DRAWABLES_GL_HEADERS,
|
||||
":metal_renderer": MLN_DRAWABLES_HEADERS + MLN_DRAWABLES_MTL_HEADERS,
|
||||
":webgpu_renderer": MLN_DRAWABLES_HEADERS + MLN_DRAWABLES_WEBGPU_HEADERS,
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
copts = CPP_FLAGS + MAPLIBRE_FLAGS,
|
||||
@@ -103,6 +107,9 @@ cc_library(
|
||||
":metal_renderer": [
|
||||
"MLN_RENDER_BACKEND_METAL=1",
|
||||
],
|
||||
":webgpu_renderer": [
|
||||
"MLN_RENDER_BACKEND_WEBGPU=1",
|
||||
],
|
||||
}) + select({
|
||||
"@platforms//os:ios": ["GLES_SILENCE_DEPRECATION=1"],
|
||||
"//conditions:default": [],
|
||||
@@ -188,6 +195,7 @@ string_flag(
|
||||
values = [
|
||||
"drawable",
|
||||
"metal",
|
||||
"webgpu",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -205,6 +213,13 @@ config_setting(
|
||||
},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "webgpu_renderer",
|
||||
flag_values = {
|
||||
":renderer": "webgpu",
|
||||
},
|
||||
)
|
||||
|
||||
# Selects the shaping implementation to utilize in the core
|
||||
|
||||
string_flag(
|
||||
|
||||
118
CMakeLists.txt
118
CMakeLists.txt
@@ -12,6 +12,7 @@ option(MLN_WITH_OPENGL "Build with OpenGL renderer" OFF)
|
||||
option(MLN_WITH_EGL "Build with EGL renderer" OFF)
|
||||
option(MLN_WITH_VULKAN "Build with Vulkan renderer" OFF)
|
||||
option(MLN_WITH_METAL "Build with Metal renderer" OFF)
|
||||
option(MLN_WITH_WEBGPU "Build with WebGPU renderer" OFF)
|
||||
option(MLN_WITH_PMTILES "Build with PMTiles support" ON)
|
||||
option(MLN_WITH_WERROR "Make all compilation warnings errors" ON)
|
||||
option(MLN_USE_UNORDERED_DENSE "Use ankerl dense containers for performance" ON)
|
||||
@@ -1309,6 +1310,119 @@ if(MLN_WITH_VULKAN)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MLN_WITH_WEBGPU)
|
||||
message(STATUS "Configuring WebGPU renderer backend")
|
||||
|
||||
target_compile_definitions(
|
||||
mbgl-core
|
||||
PUBLIC
|
||||
MLN_RENDER_BACKEND_WEBGPU=1
|
||||
)
|
||||
|
||||
list(APPEND
|
||||
SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/buffer_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/context.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/command_encoder.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/headless_backend.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/renderer_backend.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/drawable.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/drawable_builder.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/draw_scope_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/uniform_buffer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/shader_program.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/wgsl_preprocessor.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/background.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/circle.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/clipping_mask.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/collision.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/custom_geometry.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/custom_symbol_icon.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/debug.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/fill.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/fill_extrusion.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/heatmap.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/heatmap_texture.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/hillshade.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/hillshade_prepare.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/line.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/location_indicator.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/raster.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/symbol.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/widevector.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/vertex_buffer_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/vertex_attribute.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/texture2d.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/offscreen_texture.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/tile_layer_group.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/layer_group.cpp
|
||||
)
|
||||
|
||||
list(APPEND
|
||||
INCLUDE_FILES
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/buffer_resource.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/command_encoder.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/context.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/drawable.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/drawable_builder.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/index_buffer_resource.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/render_pass.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/renderer_backend.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/shader_program.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/background.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/circle.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/clipping_mask.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/collision.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/common.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/custom_geometry.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/custom_symbol_icon.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/debug.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/fill.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/fill_extrusion.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/heatmap.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/heatmap_texture.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/hillshade.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/hillshade_prepare.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/line.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/location_indicator.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/raster.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/symbol.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/webgpu/widevector.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/texture2d.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/uniform_buffer.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/upload_pass.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/webgpu/vertex_buffer_resource.hpp
|
||||
)
|
||||
|
||||
list(APPEND
|
||||
SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/buffer_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/command_encoder.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/context.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/drawable.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/drawable_builder.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/index_buffer_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/render_pass.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/renderer_backend.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/webgpu/shader_program.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/texture2d.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/uniform_buffer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/upload_pass.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/webgpu/vertex_buffer_resource.cpp
|
||||
)
|
||||
|
||||
# Include WebGPU vendor configuration (Dawn or wgpu)
|
||||
include(${PROJECT_SOURCE_DIR}/vendor/webgpu.cmake)
|
||||
|
||||
# Include Dawn integration when requested
|
||||
if(MLN_WEBGPU_IMPL STREQUAL "dawn")
|
||||
include(${PROJECT_SOURCE_DIR}/vendor/dawn.cmake)
|
||||
if(TARGET mbgl-vendor-dawn)
|
||||
target_link_libraries(mbgl-core PUBLIC mbgl-vendor-dawn)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND
|
||||
SRC_FILES
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/custom_drawable_layer_factory.cpp
|
||||
@@ -1515,6 +1629,10 @@ if(MLN_TEXT_SHAPING_HARFBUZZ)
|
||||
list(APPEND EXPORT_TARGETS mbgl-freetype mbgl-harfbuzz)
|
||||
endif()
|
||||
|
||||
if(TARGET mbgl-vendor-dawn)
|
||||
list(APPEND EXPORT_TARGETS mbgl-vendor-dawn)
|
||||
endif()
|
||||
|
||||
export(TARGETS ${EXPORT_TARGETS} FILE MapboxCoreTargets.cmake)
|
||||
|
||||
if(MLN_WITH_VULKAN)
|
||||
|
||||
@@ -75,6 +75,34 @@
|
||||
"MLN_DARWIN_USE_LIBUV": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos-webgpu-dawn",
|
||||
"displayName": "macOS WebGPU (Dawn)",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build-macos-webgpu-dawn",
|
||||
"inherits": "macos",
|
||||
"cacheVariables": {
|
||||
"MLN_WITH_METAL": "OFF",
|
||||
"MLN_WITH_OPENGL": "OFF",
|
||||
"MLN_WITH_WEBGPU": "ON",
|
||||
"MLN_WEBGPU_IMPL": "dawn",
|
||||
"MLN_DARWIN_USE_LIBUV": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "macos-webgpu-wgpu",
|
||||
"displayName": "macOS WebGPU (wgpu-native)",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build-macos-webgpu-wgpu",
|
||||
"inherits": "macos",
|
||||
"cacheVariables": {
|
||||
"MLN_WITH_METAL": "OFF",
|
||||
"MLN_WITH_OPENGL": "OFF",
|
||||
"MLN_WITH_WEBGPU": "ON",
|
||||
"MLN_WEBGPU_IMPL": "wgpu",
|
||||
"MLN_DARWIN_USE_LIBUV": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux",
|
||||
"hidden": true,
|
||||
@@ -113,6 +141,28 @@
|
||||
"MLN_WITH_X11": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-webgpu-dawn",
|
||||
"binaryDir": "${sourceDir}/build-linux-webgpu-dawn",
|
||||
"inherits": "linux",
|
||||
"cacheVariables": {
|
||||
"MLN_WITH_WEBGPU": "ON",
|
||||
"MLN_WEBGPU_IMPL": "dawn",
|
||||
"MLN_WITH_WAYLAND": "OFF",
|
||||
"MLN_WITH_X11": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-webgpu-wgpu",
|
||||
"binaryDir": "${sourceDir}/build-linux-webgpu-wgpu",
|
||||
"inherits": "linux",
|
||||
"cacheVariables": {
|
||||
"MLN_WITH_WEBGPU": "ON",
|
||||
"MLN_WEBGPU_IMPL": "wgpu",
|
||||
"MLN_WITH_WAYLAND": "OFF",
|
||||
"MLN_WITH_X11": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-opengl-node",
|
||||
"inherits": "linux",
|
||||
|
||||
@@ -1101,6 +1101,91 @@ MLN_DRAWABLES_MTL_HEADERS = [
|
||||
"include/mbgl/shaders/mtl/widevector.hpp",
|
||||
]
|
||||
|
||||
MLN_DRAWABLES_WEBGPU_SOURCE = [
|
||||
"src/mbgl/webgpu/buffer_resource.cpp",
|
||||
"src/mbgl/webgpu/command_encoder.cpp",
|
||||
"src/mbgl/webgpu/context.cpp",
|
||||
"src/mbgl/webgpu/draw_scope_resource.cpp",
|
||||
"src/mbgl/webgpu/drawable.cpp",
|
||||
"src/mbgl/webgpu/drawable_builder.cpp",
|
||||
"src/mbgl/webgpu/headless_backend.cpp",
|
||||
"src/mbgl/webgpu/index_buffer_resource.cpp",
|
||||
"src/mbgl/webgpu/layer_group.cpp",
|
||||
"src/mbgl/webgpu/offscreen_texture.cpp",
|
||||
"src/mbgl/webgpu/render_pass.cpp",
|
||||
"src/mbgl/webgpu/renderer_backend.cpp",
|
||||
"src/mbgl/webgpu/texture2d.cpp",
|
||||
"src/mbgl/webgpu/tile_layer_group.cpp",
|
||||
"src/mbgl/webgpu/uniform_buffer.cpp",
|
||||
"src/mbgl/webgpu/upload_pass.cpp",
|
||||
"src/mbgl/webgpu/vertex_attribute.cpp",
|
||||
"src/mbgl/webgpu/vertex_buffer_resource.cpp",
|
||||
"src/mbgl/shaders/webgpu/shader_program.cpp",
|
||||
"src/mbgl/shaders/webgpu/wgsl_preprocessor.cpp",
|
||||
"src/mbgl/shaders/webgpu/background.cpp",
|
||||
"src/mbgl/shaders/webgpu/circle.cpp",
|
||||
"src/mbgl/shaders/webgpu/clipping_mask.cpp",
|
||||
"src/mbgl/shaders/webgpu/collision.cpp",
|
||||
"src/mbgl/shaders/webgpu/custom_geometry.cpp",
|
||||
"src/mbgl/shaders/webgpu/custom_symbol_icon.cpp",
|
||||
"src/mbgl/shaders/webgpu/debug.cpp",
|
||||
"src/mbgl/shaders/webgpu/fill.cpp",
|
||||
"src/mbgl/shaders/webgpu/fill_extrusion.cpp",
|
||||
"src/mbgl/shaders/webgpu/heatmap.cpp",
|
||||
"src/mbgl/shaders/webgpu/heatmap_texture.cpp",
|
||||
"src/mbgl/shaders/webgpu/hillshade.cpp",
|
||||
"src/mbgl/shaders/webgpu/hillshade_prepare.cpp",
|
||||
"src/mbgl/shaders/webgpu/line.cpp",
|
||||
"src/mbgl/shaders/webgpu/location_indicator.cpp",
|
||||
"src/mbgl/shaders/webgpu/raster.cpp",
|
||||
"src/mbgl/shaders/webgpu/symbol.cpp",
|
||||
"src/mbgl/shaders/webgpu/widevector.cpp",
|
||||
]
|
||||
|
||||
MLN_DRAWABLES_WEBGPU_HEADERS = [
|
||||
"include/mbgl/webgpu/buffer_resource.hpp",
|
||||
"include/mbgl/webgpu/command_encoder.hpp",
|
||||
"include/mbgl/webgpu/context.hpp",
|
||||
"include/mbgl/webgpu/draw_scope_resource.hpp",
|
||||
"include/mbgl/webgpu/drawable.hpp",
|
||||
"include/mbgl/webgpu/drawable_builder.hpp",
|
||||
"include/mbgl/webgpu/index_buffer_resource.hpp",
|
||||
"include/mbgl/webgpu/layer_group.hpp",
|
||||
"include/mbgl/webgpu/offscreen_texture.hpp",
|
||||
"include/mbgl/webgpu/render_pass.hpp",
|
||||
"include/mbgl/webgpu/renderable_resource.hpp",
|
||||
"include/mbgl/webgpu/renderer_backend.hpp",
|
||||
"include/mbgl/webgpu/texture2d.hpp",
|
||||
"include/mbgl/webgpu/tile_layer_group.hpp",
|
||||
"include/mbgl/webgpu/uniform_buffer.hpp",
|
||||
"include/mbgl/webgpu/upload_pass.hpp",
|
||||
"include/mbgl/webgpu/vertex_attribute.hpp",
|
||||
"include/mbgl/webgpu/vertex_buffer_resource.hpp",
|
||||
"include/mbgl/webgpu/wgpu_cpp_compat.hpp",
|
||||
"include/mbgl/shaders/webgpu/background.hpp",
|
||||
"include/mbgl/shaders/webgpu/circle.hpp",
|
||||
"include/mbgl/shaders/webgpu/clipping_mask.hpp",
|
||||
"include/mbgl/shaders/webgpu/collision.hpp",
|
||||
"include/mbgl/shaders/webgpu/common.hpp",
|
||||
"include/mbgl/shaders/webgpu/custom_geometry.hpp",
|
||||
"include/mbgl/shaders/webgpu/custom_symbol_icon.hpp",
|
||||
"include/mbgl/shaders/webgpu/debug.hpp",
|
||||
"include/mbgl/shaders/webgpu/fill.hpp",
|
||||
"include/mbgl/shaders/webgpu/fill_extrusion.hpp",
|
||||
"include/mbgl/shaders/webgpu/heatmap.hpp",
|
||||
"include/mbgl/shaders/webgpu/heatmap_texture.hpp",
|
||||
"include/mbgl/shaders/webgpu/hillshade.hpp",
|
||||
"include/mbgl/shaders/webgpu/hillshade_prepare.hpp",
|
||||
"include/mbgl/shaders/webgpu/line.hpp",
|
||||
"include/mbgl/shaders/webgpu/location_indicator.hpp",
|
||||
"include/mbgl/shaders/webgpu/raster.hpp",
|
||||
"include/mbgl/shaders/webgpu/shader_group.hpp",
|
||||
"include/mbgl/shaders/webgpu/shader_program.hpp",
|
||||
"include/mbgl/shaders/webgpu/symbol.hpp",
|
||||
"include/mbgl/shaders/webgpu/wgsl_preprocessor.hpp",
|
||||
"include/mbgl/shaders/webgpu/widevector.hpp",
|
||||
]
|
||||
|
||||
MLN_SHAPING_HARFBUZZ_SRCS = [
|
||||
"src/mbgl/text/freetype.hpp",
|
||||
"src/mbgl/text/freetype.cpp",
|
||||
|
||||
@@ -16,13 +16,16 @@ endif()
|
||||
if (MLN_WITH_VULKAN)
|
||||
math(EXPR backend_count "${backend_count} + 1")
|
||||
endif()
|
||||
if (MLN_WITH_WEBGPU)
|
||||
math(EXPR backend_count "${backend_count} + 1")
|
||||
endif()
|
||||
|
||||
if (backend_count EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"You need to set a rendering backend. "
|
||||
"Set exactly one of: MLN_WITH_OPENGL, MLN_WITH_METAL, or MLN_WITH_VULKAN.")
|
||||
"Set exactly one of: MLN_WITH_OPENGL, MLN_WITH_METAL, MLN_WITH_VULKAN, or MLN_WITH_WEBGPU.")
|
||||
elseif (backend_count GREATER 1)
|
||||
message(FATAL_ERROR
|
||||
"Multiple rendering backends selected. "
|
||||
"Please enable only one of: MLN_WITH_OPENGL, MLN_WITH_METAL, or MLN_WITH_VULKAN.")
|
||||
"Please enable only one of: MLN_WITH_OPENGL, MLN_WITH_METAL, MLN_WITH_VULKAN, or MLN_WITH_WEBGPU.")
|
||||
endif()
|
||||
|
||||
@@ -28,15 +28,16 @@ Originally the project only supported OpenGL 2.0. In 2023, the [renderer was mod
|
||||
|
||||
What platforms support which rendering backend can be found below.
|
||||
|
||||
| Platform | OpenGL ES 3.0 | Vulkan 1.0 | Metal |
|
||||
|---|---|---|---|
|
||||
| Android | ✅ | ✅ | ❌ |
|
||||
| iOS | ❌ | ❌ | ✅ |
|
||||
| Linux | ✅ | ✅ | ❌ |
|
||||
| Windows | ✅ | ❌ | ❌ |
|
||||
| macOS | ❌ | ✅ | ✅[^1] |
|
||||
| Node.js | ✅ | ❌ | ✅ [^2] |
|
||||
| Qt | ✅ | ❌ | ❌ |
|
||||
|
||||
| Platform | OpenGL ES 3.0 | Vulkan 1.0 | Metal | WebGPU |
|
||||
| -------- | -------------- | ---------- | ------ | ------- |
|
||||
| Android | ✅ | ✅ | ❌ | ❌ |
|
||||
| iOS | ❌ | ❌ | ✅ | ❌ |
|
||||
| Linux | ✅ | ✅ | ❌ | ✅ |
|
||||
| Windows | ✅ | ❌ | ❌ | ❓ |
|
||||
| macOS | ❌ | ✅ | ✅[^1] | ✅ |
|
||||
| Node.js | ✅ | ❌ | ✅[^2] | ❌ |
|
||||
| Qt | ✅ | ❌ | ❌ | ❌ |
|
||||
|
||||
[^1]: Requires MoltenVK. Only available when built via CMake.
|
||||
[^2]: Issue reported, see [#2928](https://github.com/maplibre/maplibre-native/issues/2928).
|
||||
|
||||
@@ -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
|
||||
8
metrics/ignores/platform-macos-webgpu.json
Normal file
8
metrics/ignores/platform-macos-webgpu.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"render-tests/icon-text-fit/enlargen-both": "Needs to be investigated and fixed.",
|
||||
"render-tests/regressions/mapbox-gl-js#5642": "Failing with mbgl-render-test",
|
||||
"render-tests/regressions/mapbox-gl-js#7066": "Failing with mbgl-render-test",
|
||||
"render-tests/fill-outline-color/zoom-and-property-function": "It will be fixed after triangulated lines will support data driven properties",
|
||||
"render-tests/fill-outline-color/property-function": "It will be fixed after triangulated lines will support data driven properties",
|
||||
"render-tests/fill-extrusion-color/function": "Layer Z Fighting: https://github.com/maplibre/maplibre-native/issues/1847"
|
||||
}
|
||||
15
metrics/linux-webgpu-dawn.json
Normal file
15
metrics/linux-webgpu-dawn.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"base_test_path": "integration",
|
||||
"cache_path": "cache-style.db",
|
||||
"expectation_paths": [
|
||||
"expectations/platform-linux"
|
||||
],
|
||||
"ignore_paths": [
|
||||
"ignores/platform-all.json",
|
||||
"ignores/platform-linux-webgpu.json"
|
||||
],
|
||||
"metric_path": "linux-webgpu-dawn",
|
||||
"probes": [
|
||||
"probeNetwork"
|
||||
]
|
||||
}
|
||||
15
metrics/linux-webgpu-wgpu.json
Normal file
15
metrics/linux-webgpu-wgpu.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"base_test_path": "integration",
|
||||
"cache_path": "cache-style.db",
|
||||
"expectation_paths": [
|
||||
"expectations/platform-linux"
|
||||
],
|
||||
"ignore_paths": [
|
||||
"ignores/platform-all.json",
|
||||
"ignores/platform-linux-webgpu.json"
|
||||
],
|
||||
"metric_path": "linux-webgpu-wgpu",
|
||||
"probes": [
|
||||
"probeNetwork"
|
||||
]
|
||||
}
|
||||
15
metrics/macos-webgpu-dawn.json
Normal file
15
metrics/macos-webgpu-dawn.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"base_test_path": "integration",
|
||||
"cache_path": "cache-style.db",
|
||||
"expectation_paths": [
|
||||
"expectations/platform-macos"
|
||||
],
|
||||
"ignore_paths": [
|
||||
"ignores/platform-all.json",
|
||||
"ignores/platform-macos-webgpu.json"
|
||||
],
|
||||
"metric_path": "macos-webgpu-dawn",
|
||||
"probes": [
|
||||
"probeNetwork"
|
||||
]
|
||||
}
|
||||
15
metrics/macos-webgpu-wgpu.json
Normal file
15
metrics/macos-webgpu-wgpu.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"base_test_path": "integration",
|
||||
"cache_path": "cache-style.db",
|
||||
"expectation_paths": [
|
||||
"expectations/platform-macos"
|
||||
],
|
||||
"ignore_paths": [
|
||||
"ignores/platform-all.json",
|
||||
"ignores/platform-macos-webgpu.json"
|
||||
],
|
||||
"metric_path": "macos-webgpu-wgpu",
|
||||
"probes": [
|
||||
"probeNetwork"
|
||||
]
|
||||
}
|
||||
@@ -12,6 +12,18 @@ target_link_libraries(
|
||||
z
|
||||
)
|
||||
|
||||
if(TARGET mbgl-vendor-dawn)
|
||||
target_link_libraries(
|
||||
mbgl-vendor-dawn
|
||||
INTERFACE
|
||||
"-framework Metal"
|
||||
"-framework QuartzCore"
|
||||
"-framework IOKit"
|
||||
"-framework IOSurface"
|
||||
"-framework CoreGraphics"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MLN_DARWIN_USE_LIBUV)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBUV REQUIRED IMPORTED_TARGET libuv)
|
||||
|
||||
38
platform/default/include/mbgl/webgpu/headless_backend.hpp
Normal file
38
platform/default/include/mbgl/webgpu/headless_backend.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/gfx/headless_backend.hpp>
|
||||
#include <mbgl/webgpu/renderer_backend.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
class HeadlessBackend final : public webgpu::RendererBackend, public gfx::HeadlessBackend {
|
||||
public:
|
||||
HeadlessBackend(Size = {256, 256},
|
||||
SwapBehaviour = SwapBehaviour::NoFlush,
|
||||
gfx::ContextMode = gfx::ContextMode::Unique);
|
||||
~HeadlessBackend() override;
|
||||
|
||||
gfx::Renderable& getDefaultRenderable() override;
|
||||
PremultipliedImage readStillImage() override;
|
||||
RendererBackend* getRendererBackend() override;
|
||||
|
||||
// Override WebGPU-specific methods
|
||||
void* getCurrentTextureView() override;
|
||||
void* getDepthStencilView() override;
|
||||
mbgl::Size getFramebufferSize() const override;
|
||||
|
||||
private:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void createOffscreenTextures();
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
@@ -69,6 +69,37 @@ if(MLN_WITH_METAL)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MLN_WITH_WEBGPU)
|
||||
if(APPLE)
|
||||
target_sources(
|
||||
mbgl-glfw
|
||||
PRIVATE ${PROJECT_SOURCE_DIR}/platform/glfw/glfw_webgpu_backend.mm
|
||||
)
|
||||
else()
|
||||
target_sources(
|
||||
mbgl-glfw
|
||||
PRIVATE ${PROJECT_SOURCE_DIR}/platform/glfw/glfw_webgpu_backend.cpp
|
||||
)
|
||||
endif()
|
||||
if(TARGET mbgl-vendor-dawn)
|
||||
target_link_libraries(mbgl-glfw PRIVATE mbgl-vendor-dawn)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED MLN_WITH_X11 AND MLN_WITH_X11)
|
||||
target_compile_definitions(
|
||||
mbgl-glfw
|
||||
PRIVATE MLN_WITH_X11=1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(DEFINED MLN_WITH_WAYLAND AND MLN_WITH_WAYLAND)
|
||||
target_compile_definitions(
|
||||
mbgl-glfw
|
||||
PRIVATE MLN_WITH_WAYLAND=1
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(
|
||||
mbgl-glfw
|
||||
PRIVATE
|
||||
|
||||
@@ -222,6 +222,13 @@ GLFWView::GLFWView(bool fullscreen_,
|
||||
glfwSetErrorCallback(glfwError);
|
||||
|
||||
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
||||
#if defined(MLN_RENDER_BACKEND_WEBGPU)
|
||||
#ifdef __linux__
|
||||
// Force X11 platform for WebGPU compatibility (Dawn doesn't support Wayland yet)
|
||||
// For now, always use X11 when WebGPU might be used
|
||||
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!glfwInit()) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "failed to initialize glfw");
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
#if (defined(MLN_RENDER_BACKEND_OPENGL) || defined(MLN_RENDER_BACKEND_VULKAN)) && \
|
||||
#if (defined(MLN_RENDER_BACKEND_OPENGL) || defined(MLN_RENDER_BACKEND_VULKAN) || \
|
||||
defined(MLN_RENDER_BACKEND_WEBGPU)) && \
|
||||
!defined(MBGL_LAYER_CUSTOM_DISABLE_ALL)
|
||||
#define ENABLE_LOCATION_INDICATOR
|
||||
#endif
|
||||
|
||||
4
platform/glfw/glfw_webgpu_backend.cpp
Normal file
4
platform/glfw/glfw_webgpu_backend.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifndef __APPLE__
|
||||
#define GLFW_WEBGPU_BACKEND_IMPLEMENTATION
|
||||
#include "glfw_webgpu_backend_impl.ipp"
|
||||
#endif
|
||||
119
platform/glfw/glfw_webgpu_backend.hpp
Normal file
119
platform/glfw/glfw_webgpu_backend.hpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include "glfw_backend.hpp"
|
||||
#include <mbgl/webgpu/renderer_backend.hpp>
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
#include <mbgl/gfx/renderable.hpp>
|
||||
#include <mbgl/gfx/renderer_backend.hpp>
|
||||
#include <memory>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
struct GLFWwindow;
|
||||
struct WGPUDeviceImpl;
|
||||
struct WGPUSurfaceImpl;
|
||||
|
||||
namespace dawn::native {
|
||||
class Instance;
|
||||
class Adapter;
|
||||
} // namespace dawn::native
|
||||
|
||||
// Multiple inheritance: GLFWBackend for window management,
|
||||
// webgpu::RendererBackend for rendering, gfx::Renderable for framebuffer
|
||||
class GLFWWebGPUBackend final : public GLFWBackend, public mbgl::webgpu::RendererBackend, public mbgl::gfx::Renderable {
|
||||
public:
|
||||
GLFWWebGPUBackend(GLFWwindow* window, bool capFrameRate);
|
||||
~GLFWWebGPUBackend() override;
|
||||
|
||||
void swap();
|
||||
|
||||
// GLFWBackend implementation
|
||||
public:
|
||||
mbgl::gfx::RendererBackend& getRendererBackend() override;
|
||||
mbgl::Size getSize() const override;
|
||||
void setSize(mbgl::Size) override;
|
||||
|
||||
// mbgl::gfx::RendererBackend implementation
|
||||
public:
|
||||
mbgl::gfx::Renderable& getDefaultRenderable() override;
|
||||
|
||||
protected:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
// WebGPU-specific methods
|
||||
public:
|
||||
// Override virtual methods from RendererBackend
|
||||
void* getCurrentTextureView() override;
|
||||
void* getDepthStencilView() override;
|
||||
mbgl::Size getFramebufferSize() const override;
|
||||
|
||||
private:
|
||||
class SpinLock {
|
||||
public:
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
mutable SpinLock textureStateLock;
|
||||
|
||||
GLFWwindow* window;
|
||||
std::unique_ptr<dawn::native::Instance> instance;
|
||||
wgpu::Device wgpuDevice; // This owns the device
|
||||
wgpu::Queue queue;
|
||||
wgpu::Surface wgpuSurface;
|
||||
wgpu::TextureFormat swapChainFormat = wgpu::TextureFormat::BGRA8Unorm;
|
||||
wgpu::TextureFormat depthStencilFormat = wgpu::TextureFormat::Undefined;
|
||||
std::array<wgpu::TextureFormat, 1> configuredViewFormats{wgpu::TextureFormat::BGRA8Unorm};
|
||||
|
||||
// Protect texture view access from multiple threads
|
||||
wgpu::TextureView currentTextureView; // Keep the current texture view alive
|
||||
wgpu::Texture currentTexture; // Keep the texture alive as well
|
||||
|
||||
// Depth/stencil texture and view
|
||||
wgpu::Texture depthStencilTexture;
|
||||
wgpu::TextureView depthStencilView;
|
||||
|
||||
// Frame synchronization
|
||||
std::atomic<bool> frameInProgress{false};
|
||||
// Track if we have presented the current frame
|
||||
std::atomic<bool> framePresented{true};
|
||||
|
||||
// Track if we're shutting down to prevent mutex usage during destruction
|
||||
std::atomic<bool> isShuttingDown{false};
|
||||
|
||||
// Previous frame resources - keep alive until next swap
|
||||
wgpu::TextureView previousTextureView;
|
||||
wgpu::Texture previousTexture;
|
||||
|
||||
// Surface state tracking
|
||||
std::atomic<bool> surfaceConfigured{false};
|
||||
std::atomic<bool> surfaceNeedsReconfigure{false};
|
||||
mbgl::Size lastConfiguredSize{0, 0};
|
||||
|
||||
// Error recovery
|
||||
std::atomic<int> consecutiveErrors{0};
|
||||
static constexpr int maxConsecutiveErrors = 3;
|
||||
|
||||
// Helper methods
|
||||
void reconfigureSurface();
|
||||
void createDepthStencilTexture(uint32_t width, uint32_t height);
|
||||
bool waitForFrame(std::chrono::milliseconds timeout = std::chrono::milliseconds(100));
|
||||
void signalFrameComplete();
|
||||
void periodicMaintenance();
|
||||
void processEvents();
|
||||
|
||||
// Debug triangle utilities
|
||||
void ensureDebugTriangleResources();
|
||||
void drawDebugTriangle(const wgpu::TextureView& targetView);
|
||||
|
||||
bool debugTriangleInitialized = false;
|
||||
wgpu::RenderPipeline debugTrianglePipeline;
|
||||
wgpu::Buffer debugTriangleVertexBuffer;
|
||||
uint64_t debugTriangleVertexBufferSize = 0;
|
||||
};
|
||||
2
platform/glfw/glfw_webgpu_backend.mm
Normal file
2
platform/glfw/glfw_webgpu_backend.mm
Normal file
@@ -0,0 +1,2 @@
|
||||
#define GLFW_WEBGPU_BACKEND_IMPLEMENTATION
|
||||
#include "glfw_webgpu_backend_impl.ipp"
|
||||
1198
platform/glfw/glfw_webgpu_backend_impl.ipp
Normal file
1198
platform/glfw/glfw_webgpu_backend_impl.ipp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
||||
MLN_TRIANGULATE_FILL_OUTLINES = 0 : Simple line primitives will be generated. Draw using gfx::Lines
|
||||
MLN_TRIANGULATE_FILL_OUTLINES = 1 : Generate triangulated lines. Draw using gfx::Triangles and a Line shader.
|
||||
*/
|
||||
#define MLN_TRIANGULATE_FILL_OUTLINES (MLN_RENDER_BACKEND_METAL)
|
||||
#define MLN_TRIANGULATE_FILL_OUTLINES (MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_WEBGPU)
|
||||
|
||||
#if MLN_TRIANGULATE_FILL_OUTLINES
|
||||
#include <mbgl/renderer/buckets/line_bucket.hpp>
|
||||
|
||||
@@ -138,6 +138,40 @@ void LineLayerTweaker::execute(LayerGroupBase& layerGroup, const PaintParameters
|
||||
: evaluate<LineFloorWidth>(parameters),
|
||||
.expressionMask = expressionMask,
|
||||
.pad1 = 0};
|
||||
#elif MLN_RENDER_BACKEND_WEBGPU
|
||||
expressionMask = LineExpressionMask::None;
|
||||
if (evaluated.get<LineColor>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::Color;
|
||||
}
|
||||
if (evaluated.get<LineBlur>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::Blur;
|
||||
}
|
||||
if (evaluated.get<LineOpacity>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::Opacity;
|
||||
}
|
||||
if (evaluated.get<LineGapWidth>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::GapWidth;
|
||||
}
|
||||
if (evaluated.get<LineOffset>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::Offset;
|
||||
}
|
||||
if (evaluated.get<LineWidth>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::Width;
|
||||
}
|
||||
if (evaluated.get<LineFloorWidth>().isConstant()) {
|
||||
expressionMask |= LineExpressionMask::FloorWidth;
|
||||
}
|
||||
|
||||
const LineEvaluatedPropsUBO propsUBO{
|
||||
.color = evaluated.get<LineColor>().constantOr(LineColor::defaultValue()),
|
||||
.blur = evaluated.get<LineBlur>().constantOr(LineBlur::defaultValue()),
|
||||
.opacity = evaluated.get<LineOpacity>().constantOr(LineOpacity::defaultValue()),
|
||||
.gapwidth = evaluated.get<LineGapWidth>().constantOr(LineGapWidth::defaultValue()),
|
||||
.offset = evaluated.get<LineOffset>().constantOr(LineOffset::defaultValue()),
|
||||
.width = evaluated.get<LineWidth>().constantOr(LineWidth::defaultValue()),
|
||||
.floorwidth = evaluated.get<LineFloorWidth>().constantOr(LineFloorWidth::defaultValue()),
|
||||
.expressionMask = expressionMask,
|
||||
.pad1 = 0};
|
||||
#else
|
||||
const LineEvaluatedPropsUBO propsUBO{/*color =*/evaluate<LineColor>(parameters),
|
||||
/*blur =*/evaluate<LineBlur>(parameters),
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
#include <mbgl/renderer/layer_tweaker.hpp>
|
||||
#include <mbgl/style/layers/line_layer_properties.hpp>
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
#if MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_WEBGPU
|
||||
#include <mbgl/shaders/line_layer_ubo.hpp>
|
||||
#endif // MLN_RENDER_BACKEND_METAL
|
||||
#endif // MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_WEBGPU
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -68,9 +68,12 @@ protected:
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
gfx::UniformBufferPtr expressionUniformBuffer;
|
||||
Unevaluated::GPUExpressions gpuExpressions;
|
||||
shaders::LineExpressionMask expressionMask = shaders::LineExpressionMask::None;
|
||||
bool gpuExpressionsUpdated = true;
|
||||
#endif // MLN_RENDER_BACKEND_METAL
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_WEBGPU
|
||||
shaders::LineExpressionMask expressionMask = shaders::LineExpressionMask::None;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -253,9 +253,29 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders,
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#if MLN_RENDER_BACKEND_WEBGPU
|
||||
bool anyUpdated = false;
|
||||
bool skippedDrawables = false;
|
||||
tileLayerGroup->visitDrawables(renderPass, tileID, [&](gfx::Drawable& drawable) {
|
||||
if (updateExisting(drawable)) {
|
||||
anyUpdated = true;
|
||||
} else {
|
||||
skippedDrawables = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!anyUpdated && skippedDrawables) {
|
||||
removeTile(renderPass, tileID);
|
||||
}
|
||||
|
||||
if (anyUpdated) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (updateTile(renderPass, tileID, std::move(updateExisting))) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!propertiesAsUniforms) {
|
||||
propertiesAsUniforms.emplace();
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
#include <mbgl/shaders/gl/legacy/clipping_mask_program.hpp>
|
||||
#endif
|
||||
|
||||
#if MLN_RENDER_BACKEND_WEBGPU
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
#include <mbgl/shaders/webgpu/clipping_mask.hpp>
|
||||
#endif
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
#include <mbgl/mtl/context.hpp>
|
||||
#include <mbgl/shaders/mtl/clipping_mask.hpp>
|
||||
@@ -155,6 +160,9 @@ void PaintParameters::clearStencil() {
|
||||
const auto& vulkanRenderPass = static_cast<vulkan::RenderPass&>(*renderPass);
|
||||
vulkanRenderPass.clearStencil();
|
||||
|
||||
context.renderingStats().stencilClears++;
|
||||
#elif MLN_RENDER_BACKEND_WEBGPU
|
||||
// WebGPU clears stencil through render pass descriptor
|
||||
context.renderingStats().stencilClears++;
|
||||
#elif MLN_RENDER_BACKEND_OPENGL
|
||||
context.clearStencilBuffer(0b00000000);
|
||||
@@ -176,7 +184,40 @@ void PaintParameters::renderTileClippingMasks(const RenderTiles& renderTiles) {
|
||||
clearStencil();
|
||||
}
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL
|
||||
#if MLN_RENDER_BACKEND_WEBGPU
|
||||
std::vector<shaders::ClipUBO> tileUBOs;
|
||||
for (const auto& tileRef : *renderTiles) {
|
||||
const auto& tileID = tileRef.get().id;
|
||||
|
||||
const int32_t stencilID = nextStencilID;
|
||||
const auto result = tileClippingMaskIDs.insert(std::make_pair(tileID, stencilID));
|
||||
if (result.second) {
|
||||
nextStencilID++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tileUBOs.empty()) {
|
||||
tileUBOs.reserve(count);
|
||||
}
|
||||
|
||||
tileUBOs.emplace_back(shaders::ClipUBO{/* .matrix = */ util::cast<float>(matrixForTile(tileID)),
|
||||
/* .stencil_ref = */ static_cast<uint32_t>(stencilID),
|
||||
/* .pad1 = */ 0,
|
||||
/* .pad2 = */ 0,
|
||||
/* .pad3 = */ 0});
|
||||
}
|
||||
|
||||
if (!tileUBOs.empty()) {
|
||||
#if !defined(NDEBUG)
|
||||
const auto debugGroup = renderPass->createDebugGroup("tile-clip-masks");
|
||||
#endif
|
||||
|
||||
auto& webgpuContext = static_cast<webgpu::Context&>(context);
|
||||
webgpuContext.renderTileClippingMasks(*renderPass, staticData, tileUBOs);
|
||||
webgpuContext.renderingStats().stencilUpdates++;
|
||||
}
|
||||
#elif MLN_RENDER_BACKEND_METAL
|
||||
// Assign a stencil ID and build a UBO for each tile in the set
|
||||
std::vector<shaders::ClipUBO> tileUBOs;
|
||||
for (const auto& tileRef : *renderTiles) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <mbgl/gfx/drawable_tweaker.hpp>
|
||||
#include <mbgl/renderer/layer_tweaker.hpp>
|
||||
|
||||
#if MLN_RENDER_BACKEND_METAL || (MLN_RENDER_BACKEND_VULKAN && defined(__ANDROID__))
|
||||
#if MLN_RENDER_BACKEND_METAL || MLN_RENDER_BACKEND_WEBGPU || (MLN_RENDER_BACKEND_VULKAN && defined(__ANDROID__))
|
||||
#define MLN_ENABLE_POLYLINE_DRAWABLES 1
|
||||
#else
|
||||
#define MLN_ENABLE_POLYLINE_DRAWABLES 0
|
||||
|
||||
29
src/mbgl/shaders/webgpu/background.cpp
Normal file
29
src/mbgl/shaders/webgpu/background.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <mbgl/shaders/webgpu/background.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/background_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
//
|
||||
// Background
|
||||
|
||||
using BackgroundShaderSource = ShaderSource<BuiltIn::BackgroundShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 1> BackgroundShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short2, idBackgroundPosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> BackgroundShaderSource::textures = {};
|
||||
|
||||
//
|
||||
// Background pattern
|
||||
|
||||
using BackgroundPatternShaderSource = ShaderSource<BuiltIn::BackgroundPatternShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 1> BackgroundPatternShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short2, idBackgroundPosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> BackgroundPatternShaderSource::textures = {TextureInfo{0, idBackgroundImageTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
23
src/mbgl/shaders/webgpu/circle.cpp
Normal file
23
src/mbgl/shaders/webgpu/circle.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <mbgl/shaders/webgpu/circle.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/circle_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using CircleShaderSource = ShaderSource<BuiltIn::CircleShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 8> CircleShaderSource::attributes = {
|
||||
AttributeInfo{0, gfx::AttributeDataType::Short2, idCirclePosVertexAttribute},
|
||||
AttributeInfo{1, gfx::AttributeDataType::Float4, idCircleColorVertexAttribute},
|
||||
AttributeInfo{2, gfx::AttributeDataType::Float2, idCircleRadiusVertexAttribute},
|
||||
AttributeInfo{3, gfx::AttributeDataType::Float2, idCircleBlurVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::Float2, idCircleOpacityVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float4, idCircleStrokeColorVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float2, idCircleStrokeWidthVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idCircleStrokeOpacityVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> CircleShaderSource::textures = {};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
15
src/mbgl/shaders/webgpu/clipping_mask.cpp
Normal file
15
src/mbgl/shaders/webgpu/clipping_mask.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <mbgl/shaders/webgpu/clipping_mask.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using ClippingMaskShaderSource = ShaderSource<BuiltIn::ClippingMaskProgram, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 1> ClippingMaskShaderSource::attributes = {
|
||||
AttributeInfo{2, gfx::AttributeDataType::Short2, idClippingMaskPosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> ClippingMaskShaderSource::textures = {};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
36
src/mbgl/shaders/webgpu/collision.cpp
Normal file
36
src/mbgl/shaders/webgpu/collision.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <mbgl/shaders/webgpu/collision.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/collision_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
//
|
||||
// Collision box
|
||||
|
||||
using CollisionBoxShaderSource = ShaderSource<BuiltIn::CollisionBoxShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 5> CollisionBoxShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short2, idCollisionPosVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idCollisionAnchorPosVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short2, idCollisionExtrudeVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::UShort2, idCollisionPlacedVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idCollisionShiftVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> CollisionBoxShaderSource::textures = {};
|
||||
|
||||
//
|
||||
// Collision circle
|
||||
|
||||
using CollisionCircleShaderSource = ShaderSource<BuiltIn::CollisionCircleShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 4> CollisionCircleShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short2, idCollisionPosVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idCollisionAnchorPosVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short2, idCollisionExtrudeVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::UShort2, idCollisionPlacedVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> CollisionCircleShaderSource::textures = {};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
19
src/mbgl/shaders/webgpu/custom_geometry.cpp
Normal file
19
src/mbgl/shaders/webgpu/custom_geometry.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <mbgl/shaders/webgpu/custom_geometry.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/custom_geometry_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using CustomGeometryShaderSource = ShaderSource<BuiltIn::CustomGeometryShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> CustomGeometryShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Float3, idCustomGeometryPosVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::Float2, idCustomGeometryTexVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> CustomGeometryShaderSource::textures = {
|
||||
TextureInfo{0, idCustomGeometryTexture},
|
||||
};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
17
src/mbgl/shaders/webgpu/custom_symbol_icon.cpp
Normal file
17
src/mbgl/shaders/webgpu/custom_symbol_icon.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <mbgl/shaders/webgpu/custom_symbol_icon.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/custom_drawable_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using CustomSymbolIconShaderSource = ShaderSource<BuiltIn::CustomSymbolIconShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> CustomSymbolIconShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Float3, idCustomSymbolPosVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float2, idCustomSymbolTexVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> CustomSymbolIconShaderSource::textures = {TextureInfo{0, idCustomSymbolImageTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
16
src/mbgl/shaders/webgpu/debug.cpp
Normal file
16
src/mbgl/shaders/webgpu/debug.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <mbgl/shaders/webgpu/debug.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/debug_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using DebugShaderSource = ShaderSource<BuiltIn::DebugShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 1> DebugShaderSource::attributes = {
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short2, idDebugPosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> DebugShaderSource::textures = {TextureInfo{0, idDebugOverlayTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
71
src/mbgl/shaders/webgpu/fill.cpp
Normal file
71
src/mbgl/shaders/webgpu/fill.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <mbgl/shaders/webgpu/fill.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/fill_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
//
|
||||
// Fill
|
||||
|
||||
using FillShaderSource = ShaderSource<BuiltIn::FillShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 3> FillShaderSource::attributes = {
|
||||
AttributeInfo{0, gfx::AttributeDataType::Short2, idFillPosVertexAttribute},
|
||||
AttributeInfo{1, gfx::AttributeDataType::Float4, idFillColorVertexAttribute},
|
||||
AttributeInfo{2, gfx::AttributeDataType::Float2, idFillOpacityVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> FillShaderSource::textures = {};
|
||||
|
||||
//
|
||||
// Fill outline
|
||||
|
||||
using FillOutlineShaderSource = ShaderSource<BuiltIn::FillOutlineShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 3> FillOutlineShaderSource::attributes = {
|
||||
AttributeInfo{0, gfx::AttributeDataType::Short2, idFillPosVertexAttribute},
|
||||
AttributeInfo{1, gfx::AttributeDataType::Float4, idFillOutlineColorVertexAttribute},
|
||||
AttributeInfo{2, gfx::AttributeDataType::Float2, idFillOpacityVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> FillOutlineShaderSource::textures = {};
|
||||
|
||||
//
|
||||
// Fill pattern
|
||||
|
||||
using FillPatternShaderSource = ShaderSource<BuiltIn::FillPatternShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 4> FillPatternShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idFillPosVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::UShort4, idFillPatternFromVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::UShort4, idFillPatternToVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idFillOpacityVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> FillPatternShaderSource::textures = {TextureInfo{0, idFillImageTexture}};
|
||||
|
||||
//
|
||||
// Fill outline pattern
|
||||
|
||||
using FillOutlinePatternShaderSource = ShaderSource<BuiltIn::FillOutlinePatternShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 4> FillOutlinePatternShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idFillPosVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::UShort4, idFillPatternFromVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::UShort4, idFillPatternToVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idFillOpacityVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> FillOutlinePatternShaderSource::textures = {TextureInfo{0, idFillImageTexture}};
|
||||
|
||||
//
|
||||
// Fill outline triangulated
|
||||
|
||||
using FillOutlineTriangulatedShaderSource =
|
||||
ShaderSource<BuiltIn::FillOutlineTriangulatedShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> FillOutlineTriangulatedShaderSource::attributes = {
|
||||
AttributeInfo{0, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
|
||||
AttributeInfo{1, gfx::AttributeDataType::UByte4, idLineDataVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> FillOutlineTriangulatedShaderSource::textures = {};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
40
src/mbgl/shaders/webgpu/fill_extrusion.cpp
Normal file
40
src/mbgl/shaders/webgpu/fill_extrusion.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <mbgl/shaders/webgpu/fill_extrusion.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/fill_extrusion_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
//
|
||||
// Fill extrusion
|
||||
|
||||
using FillExtrusionShaderSource = ShaderSource<BuiltIn::FillExtrusionShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 5> FillExtrusionShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short2, idFillExtrusionPosVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short4, idFillExtrusionNormalEdVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float4, idFillExtrusionColorVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float2, idFillExtrusionBaseVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idFillExtrusionHeightVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> FillExtrusionShaderSource::textures = {};
|
||||
|
||||
//
|
||||
// Fill extrusion pattern
|
||||
|
||||
using FillExtrusionPatternShaderSource = ShaderSource<BuiltIn::FillExtrusionPatternShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 6> FillExtrusionPatternShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short2, idFillExtrusionPosVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short4, idFillExtrusionNormalEdVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float2, idFillExtrusionBaseVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float2, idFillExtrusionHeightVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::UShort4, idFillExtrusionPatternFromVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::UShort4, idFillExtrusionPatternToVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> FillExtrusionPatternShaderSource::textures = {
|
||||
TextureInfo{0, idFillExtrusionImageTexture},
|
||||
};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
18
src/mbgl/shaders/webgpu/heatmap.cpp
Normal file
18
src/mbgl/shaders/webgpu/heatmap.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <mbgl/shaders/webgpu/heatmap.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/heatmap_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using HeatmapShaderSource = ShaderSource<BuiltIn::HeatmapShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 3> HeatmapShaderSource::attributes = {
|
||||
AttributeInfo{0, gfx::AttributeDataType::Short2, idHeatmapPosVertexAttribute},
|
||||
AttributeInfo{1, gfx::AttributeDataType::Float2, idHeatmapWeightVertexAttribute},
|
||||
AttributeInfo{2, gfx::AttributeDataType::Float2, idHeatmapRadiusVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> HeatmapShaderSource::textures = {};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
17
src/mbgl/shaders/webgpu/heatmap_texture.cpp
Normal file
17
src/mbgl/shaders/webgpu/heatmap_texture.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <mbgl/shaders/webgpu/heatmap_texture.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/heatmap_texture_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using HeatmapTextureShaderSource = ShaderSource<BuiltIn::HeatmapTextureShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 1> HeatmapTextureShaderSource::attributes = {
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short2, idHeatmapPosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 2> HeatmapTextureShaderSource::textures = {TextureInfo{0, idHeatmapImageTexture},
|
||||
TextureInfo{1, idHeatmapColorRampTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
17
src/mbgl/shaders/webgpu/hillshade.cpp
Normal file
17
src/mbgl/shaders/webgpu/hillshade.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <mbgl/shaders/webgpu/hillshade.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/hillshade_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using HillshadeShaderSource = ShaderSource<BuiltIn::HillshadeShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> HillshadeShaderSource::attributes = {
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short2, idHillshadePosVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Short2, idHillshadeTexturePosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> HillshadeShaderSource::textures = {TextureInfo{0, idHillshadeImageTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
17
src/mbgl/shaders/webgpu/hillshade_prepare.cpp
Normal file
17
src/mbgl/shaders/webgpu/hillshade_prepare.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <mbgl/shaders/webgpu/hillshade_prepare.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/hillshade_prepare_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using HillshadePrepareShaderSource = ShaderSource<BuiltIn::HillshadePrepareShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> HillshadePrepareShaderSource::attributes = {
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short2, idHillshadePosVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Short2, idHillshadeTexturePosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> HillshadePrepareShaderSource::textures = {TextureInfo{0, idHillshadeImageTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
69
src/mbgl/shaders/webgpu/line.cpp
Normal file
69
src/mbgl/shaders/webgpu/line.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include <mbgl/shaders/webgpu/line.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/line_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using LineShaderSource = ShaderSource<BuiltIn::LineShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 8> LineShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::UByte4, idLineDataVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float4, idLineColorVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idLineBlurVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float2, idLineOpacityVertexAttribute},
|
||||
AttributeInfo{9, gfx::AttributeDataType::Float2, idLineGapWidthVertexAttribute},
|
||||
AttributeInfo{10, gfx::AttributeDataType::Float2, idLineOffsetVertexAttribute},
|
||||
AttributeInfo{11, gfx::AttributeDataType::Float2, idLineWidthVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> LineShaderSource::textures = {};
|
||||
|
||||
// Line Gradient
|
||||
using LineGradientShaderSource = ShaderSource<BuiltIn::LineGradientShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 7> LineGradientShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::UByte4, idLineDataVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float2, idLineBlurVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idLineOpacityVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float2, idLineGapWidthVertexAttribute},
|
||||
AttributeInfo{9, gfx::AttributeDataType::Float2, idLineOffsetVertexAttribute},
|
||||
AttributeInfo{10, gfx::AttributeDataType::Float2, idLineWidthVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> LineGradientShaderSource::textures = {TextureInfo{0, idLineImageTexture}};
|
||||
|
||||
// Line Pattern
|
||||
using LinePatternShaderSource = ShaderSource<BuiltIn::LinePatternShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 9> LinePatternShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::UByte4, idLineDataVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float2, idLineBlurVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idLineOpacityVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float2, idLineGapWidthVertexAttribute},
|
||||
AttributeInfo{9, gfx::AttributeDataType::Float2, idLineOffsetVertexAttribute},
|
||||
AttributeInfo{10, gfx::AttributeDataType::Float2, idLineWidthVertexAttribute},
|
||||
AttributeInfo{11, gfx::AttributeDataType::UShort4, idLinePatternFromVertexAttribute},
|
||||
AttributeInfo{12, gfx::AttributeDataType::UShort4, idLinePatternToVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> LinePatternShaderSource::textures = {TextureInfo{0, idLineImageTexture}};
|
||||
|
||||
// Line SDF
|
||||
using LineSDFShaderSource = ShaderSource<BuiltIn::LineSDFShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 9> LineSDFShaderSource::attributes = {
|
||||
AttributeInfo{4, gfx::AttributeDataType::Short2, idLinePosNormalVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::UByte4, idLineDataVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float4, idLineColorVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float2, idLineBlurVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float2, idLineOpacityVertexAttribute},
|
||||
AttributeInfo{9, gfx::AttributeDataType::Float2, idLineGapWidthVertexAttribute},
|
||||
AttributeInfo{10, gfx::AttributeDataType::Float2, idLineOffsetVertexAttribute},
|
||||
AttributeInfo{11, gfx::AttributeDataType::Float2, idLineWidthVertexAttribute},
|
||||
AttributeInfo{12, gfx::AttributeDataType::Float2, idLineFloorWidthVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> LineSDFShaderSource::textures = {TextureInfo{0, idLineImageTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
32
src/mbgl/shaders/webgpu/location_indicator.cpp
Normal file
32
src/mbgl/shaders/webgpu/location_indicator.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <mbgl/shaders/webgpu/location_indicator.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/location_indicator_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
//
|
||||
// Location indicator
|
||||
|
||||
using LocationIndicatorShaderSource = ShaderSource<BuiltIn::LocationIndicatorShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 1> LocationIndicatorShaderSource::attributes = {
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float2, idLocationIndicatorPosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 0> LocationIndicatorShaderSource::textures = {};
|
||||
|
||||
//
|
||||
// Location indicator textured
|
||||
|
||||
using LocationIndicatorTexturedShaderSource =
|
||||
ShaderSource<BuiltIn::LocationIndicatorTexturedShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> LocationIndicatorTexturedShaderSource::attributes = {
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float2, idLocationIndicatorPosVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float2, idLocationIndicatorTexVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 1> LocationIndicatorTexturedShaderSource::textures = {
|
||||
TextureInfo{0, idLocationIndicatorTexture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
18
src/mbgl/shaders/webgpu/raster.cpp
Normal file
18
src/mbgl/shaders/webgpu/raster.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <mbgl/shaders/webgpu/raster.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/raster_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using RasterShaderSource = ShaderSource<BuiltIn::RasterShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 2> RasterShaderSource::attributes = {
|
||||
AttributeInfo{0, gfx::AttributeDataType::Short2, idRasterPosVertexAttribute},
|
||||
AttributeInfo{1, gfx::AttributeDataType::Short2, idRasterTexturePosVertexAttribute},
|
||||
};
|
||||
const std::array<TextureInfo, 2> RasterShaderSource::textures = {TextureInfo{0, idRasterImage0Texture},
|
||||
TextureInfo{1, idRasterImage1Texture}};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
797
src/mbgl/shaders/webgpu/shader_program.cpp
Normal file
797
src/mbgl/shaders/webgpu/shader_program.cpp
Normal file
@@ -0,0 +1,797 @@
|
||||
#include <mbgl/shaders/webgpu/shader_program.hpp>
|
||||
#include <mbgl/shaders/webgpu/common.hpp>
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
#include <mbgl/webgpu/renderer_backend.hpp>
|
||||
#include <mbgl/webgpu/renderable_resource.hpp>
|
||||
#include <mbgl/webgpu/uniform_buffer.hpp>
|
||||
#include <mbgl/webgpu/vertex_attribute.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/gfx/color_mode.hpp>
|
||||
#include <mbgl/gfx/depth_mode.hpp>
|
||||
#include <mbgl/gfx/stencil_mode.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <mbgl/util/variant.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
namespace {
|
||||
std::string trimString(std::string_view value) {
|
||||
auto begin = value.begin();
|
||||
auto end = value.end();
|
||||
while (begin != end && std::isspace(static_cast<unsigned char>(*begin))) {
|
||||
++begin;
|
||||
}
|
||||
while (begin != end) {
|
||||
auto prev = end - 1;
|
||||
if (!std::isspace(static_cast<unsigned char>(*prev))) {
|
||||
break;
|
||||
}
|
||||
end = prev;
|
||||
}
|
||||
return std::string(begin, end);
|
||||
}
|
||||
|
||||
WGPUBlendOperation webgpuBlendOperation(const gfx::ColorBlendEquationType& equation) {
|
||||
switch (equation) {
|
||||
case gfx::ColorBlendEquationType::Add:
|
||||
return WGPUBlendOperation_Add;
|
||||
case gfx::ColorBlendEquationType::Subtract:
|
||||
return WGPUBlendOperation_Subtract;
|
||||
case gfx::ColorBlendEquationType::ReverseSubtract:
|
||||
return WGPUBlendOperation_ReverseSubtract;
|
||||
default:
|
||||
return WGPUBlendOperation_Add;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUBlendFactor webgpuBlendFactor(const gfx::ColorBlendFactorType& factor) {
|
||||
switch (factor) {
|
||||
case gfx::ColorBlendFactorType::Zero:
|
||||
return WGPUBlendFactor_Zero;
|
||||
case gfx::ColorBlendFactorType::One:
|
||||
return WGPUBlendFactor_One;
|
||||
case gfx::ColorBlendFactorType::SrcColor:
|
||||
return WGPUBlendFactor_Src;
|
||||
case gfx::ColorBlendFactorType::OneMinusSrcColor:
|
||||
return WGPUBlendFactor_OneMinusSrc;
|
||||
case gfx::ColorBlendFactorType::SrcAlpha:
|
||||
return WGPUBlendFactor_SrcAlpha;
|
||||
case gfx::ColorBlendFactorType::OneMinusSrcAlpha:
|
||||
return WGPUBlendFactor_OneMinusSrcAlpha;
|
||||
case gfx::ColorBlendFactorType::DstAlpha:
|
||||
return WGPUBlendFactor_DstAlpha;
|
||||
case gfx::ColorBlendFactorType::OneMinusDstAlpha:
|
||||
return WGPUBlendFactor_OneMinusDstAlpha;
|
||||
case gfx::ColorBlendFactorType::DstColor:
|
||||
return WGPUBlendFactor_Dst;
|
||||
case gfx::ColorBlendFactorType::OneMinusDstColor:
|
||||
return WGPUBlendFactor_OneMinusDst;
|
||||
case gfx::ColorBlendFactorType::SrcAlphaSaturate:
|
||||
return WGPUBlendFactor_SrcAlphaSaturated;
|
||||
case gfx::ColorBlendFactorType::ConstantColor:
|
||||
return WGPUBlendFactor_Constant;
|
||||
case gfx::ColorBlendFactorType::OneMinusConstantColor:
|
||||
return WGPUBlendFactor_OneMinusConstant;
|
||||
case gfx::ColorBlendFactorType::ConstantAlpha:
|
||||
return WGPUBlendFactor_Constant;
|
||||
case gfx::ColorBlendFactorType::OneMinusConstantAlpha:
|
||||
return WGPUBlendFactor_OneMinusConstant;
|
||||
default:
|
||||
return WGPUBlendFactor_One;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUPrimitiveTopology toPrimitiveTopology(gfx::DrawModeType drawModeType) {
|
||||
switch (drawModeType) {
|
||||
case gfx::DrawModeType::Points:
|
||||
return WGPUPrimitiveTopology_PointList;
|
||||
case gfx::DrawModeType::Lines:
|
||||
case gfx::DrawModeType::LineLoop:
|
||||
return WGPUPrimitiveTopology_LineList;
|
||||
case gfx::DrawModeType::LineStrip:
|
||||
return WGPUPrimitiveTopology_LineStrip;
|
||||
case gfx::DrawModeType::TriangleStrip:
|
||||
return WGPUPrimitiveTopology_TriangleStrip;
|
||||
case gfx::DrawModeType::TriangleFan:
|
||||
// WebGPU does not support triangle fan directly; approximate with triangle list
|
||||
return WGPUPrimitiveTopology_TriangleList;
|
||||
case gfx::DrawModeType::Triangles:
|
||||
default:
|
||||
return WGPUPrimitiveTopology_TriangleList;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUCompareFunction toCompareFunction(const gfx::DepthFunctionType func) {
|
||||
switch (func) {
|
||||
case gfx::DepthFunctionType::Never:
|
||||
return WGPUCompareFunction_Never;
|
||||
case gfx::DepthFunctionType::Less:
|
||||
return WGPUCompareFunction_Less;
|
||||
case gfx::DepthFunctionType::Equal:
|
||||
return WGPUCompareFunction_Equal;
|
||||
case gfx::DepthFunctionType::LessEqual:
|
||||
return WGPUCompareFunction_LessEqual;
|
||||
case gfx::DepthFunctionType::Greater:
|
||||
return WGPUCompareFunction_Greater;
|
||||
case gfx::DepthFunctionType::NotEqual:
|
||||
return WGPUCompareFunction_NotEqual;
|
||||
case gfx::DepthFunctionType::GreaterEqual:
|
||||
return WGPUCompareFunction_GreaterEqual;
|
||||
case gfx::DepthFunctionType::Always:
|
||||
default:
|
||||
return WGPUCompareFunction_Always;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUCompareFunction toCompareFunction(const gfx::StencilFunctionType func) {
|
||||
switch (func) {
|
||||
case gfx::StencilFunctionType::Never:
|
||||
return WGPUCompareFunction_Never;
|
||||
case gfx::StencilFunctionType::Less:
|
||||
return WGPUCompareFunction_Less;
|
||||
case gfx::StencilFunctionType::Equal:
|
||||
return WGPUCompareFunction_Equal;
|
||||
case gfx::StencilFunctionType::LessEqual:
|
||||
return WGPUCompareFunction_LessEqual;
|
||||
case gfx::StencilFunctionType::Greater:
|
||||
return WGPUCompareFunction_Greater;
|
||||
case gfx::StencilFunctionType::NotEqual:
|
||||
return WGPUCompareFunction_NotEqual;
|
||||
case gfx::StencilFunctionType::GreaterEqual:
|
||||
return WGPUCompareFunction_GreaterEqual;
|
||||
case gfx::StencilFunctionType::Always:
|
||||
default:
|
||||
return WGPUCompareFunction_Always;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUStencilOperation toStencilOp(const gfx::StencilOpType op) {
|
||||
switch (op) {
|
||||
case gfx::StencilOpType::Zero:
|
||||
return WGPUStencilOperation_Zero;
|
||||
case gfx::StencilOpType::Keep:
|
||||
return WGPUStencilOperation_Keep;
|
||||
case gfx::StencilOpType::Replace:
|
||||
return WGPUStencilOperation_Replace;
|
||||
case gfx::StencilOpType::Increment:
|
||||
return WGPUStencilOperation_IncrementClamp;
|
||||
case gfx::StencilOpType::Decrement:
|
||||
return WGPUStencilOperation_DecrementClamp;
|
||||
case gfx::StencilOpType::Invert:
|
||||
return WGPUStencilOperation_Invert;
|
||||
case gfx::StencilOpType::IncrementWrap:
|
||||
return WGPUStencilOperation_IncrementWrap;
|
||||
case gfx::StencilOpType::DecrementWrap:
|
||||
return WGPUStencilOperation_DecrementWrap;
|
||||
default:
|
||||
return WGPUStencilOperation_Keep;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ShaderProgram::ShaderProgram(std::string name,
|
||||
RendererBackend& backend_,
|
||||
WGPUShaderModule vertexModule,
|
||||
WGPUShaderModule fragmentModule)
|
||||
: ShaderProgramBase(),
|
||||
shaderName(std::move(name)),
|
||||
backend(backend_),
|
||||
vertexShaderModule(vertexModule),
|
||||
fragmentShaderModule(fragmentModule) {
|
||||
createPipelineLayout({}, {});
|
||||
}
|
||||
|
||||
// Minimal constructor for Context::createShader compatibility
|
||||
ShaderProgram::ShaderProgram(Context& context, const std::string& vertexSource, const std::string& fragmentSource)
|
||||
: ShaderProgramBase(),
|
||||
shaderName("ContextShader"),
|
||||
backend(static_cast<RendererBackend&>(context.getBackend())) {
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
if (!device) return;
|
||||
|
||||
std::string trimmedVertex = trimString(vertexSource);
|
||||
std::string trimmedFragment = trimString(fragmentSource);
|
||||
hasVertexEntryPoint = !trimmedVertex.empty();
|
||||
hasFragmentEntryPoint = !trimmedFragment.empty();
|
||||
|
||||
// Get the prelude from common.hpp
|
||||
using PreludeShader = shaders::ShaderSource<shaders::BuiltIn::Prelude, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
std::string vertexWithPrelude;
|
||||
if (hasVertexEntryPoint) {
|
||||
vertexWithPrelude = std::string(PreludeShader::prelude) + "\n" + vertexSource;
|
||||
WGPUShaderSourceWGSL wgslDesc = {};
|
||||
wgslDesc.chain.sType = WGPUSType_ShaderSourceWGSL;
|
||||
wgslDesc.chain.next = nullptr;
|
||||
WGPUStringView vertexCode = {vertexWithPrelude.c_str(), vertexWithPrelude.length()};
|
||||
wgslDesc.code = vertexCode;
|
||||
|
||||
WGPUShaderModuleDescriptor vertexShaderDesc = {};
|
||||
WGPUStringView vertexLabel = {"Vertex Shader Module", strlen("Vertex Shader Module")};
|
||||
vertexShaderDesc.label = vertexLabel;
|
||||
vertexShaderDesc.nextInChain = (WGPUChainedStruct*)&wgslDesc;
|
||||
|
||||
vertexShaderModule = wgpuDeviceCreateShaderModule(device, &vertexShaderDesc);
|
||||
if (!vertexShaderModule) {
|
||||
Log::Error(Event::Render, "Failed to create vertex shader module for " + shaderName);
|
||||
Log::Error(Event::Render, "Vertex shader source length: " + std::to_string(vertexWithPrelude.length()));
|
||||
if (shaderName.find("CircleShader") != std::string::npos) {
|
||||
std::ofstream dump("/tmp/circle_vertex.wgsl", std::ios::trunc);
|
||||
dump << vertexSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string fragmentWithPrelude;
|
||||
if (hasFragmentEntryPoint) {
|
||||
fragmentWithPrelude = std::string(PreludeShader::prelude) + "\n" + fragmentSource;
|
||||
|
||||
WGPUShaderSourceWGSL wgslDesc = {};
|
||||
wgslDesc.chain.sType = WGPUSType_ShaderSourceWGSL;
|
||||
wgslDesc.chain.next = nullptr;
|
||||
WGPUStringView fragmentCode = {fragmentWithPrelude.c_str(), fragmentWithPrelude.length()};
|
||||
wgslDesc.code = fragmentCode;
|
||||
|
||||
WGPUShaderModuleDescriptor fragmentShaderDesc = {};
|
||||
WGPUStringView fragmentLabel = {"Fragment Shader Module", strlen("Fragment Shader Module")};
|
||||
fragmentShaderDesc.label = fragmentLabel;
|
||||
fragmentShaderDesc.nextInChain = (WGPUChainedStruct*)&wgslDesc;
|
||||
|
||||
fragmentShaderModule = wgpuDeviceCreateShaderModule(device, &fragmentShaderDesc);
|
||||
if (!fragmentShaderModule) {
|
||||
Log::Error(Event::Render, "Failed to create fragment shader module for " + shaderName);
|
||||
Log::Error(Event::Render, "Fragment shader source length: " + std::to_string(fragmentWithPrelude.length()));
|
||||
if (shaderName.find("CircleShader") != std::string::npos) {
|
||||
std::ofstream dump("/tmp/circle_fragment.wgsl", std::ios::trunc);
|
||||
dump << fragmentSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createPipelineLayout(vertexWithPrelude, fragmentWithPrelude);
|
||||
}
|
||||
|
||||
ShaderProgram::~ShaderProgram() {
|
||||
// Release cached pipelines
|
||||
for (auto& pair : renderPipelineCache) {
|
||||
if (pair.second) {
|
||||
wgpuRenderPipelineRelease(pair.second);
|
||||
}
|
||||
}
|
||||
renderPipelineCache.clear();
|
||||
|
||||
// Release resources
|
||||
if (pipelineLayout) {
|
||||
wgpuPipelineLayoutRelease(pipelineLayout);
|
||||
pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
for (auto layout : bindGroupLayouts) {
|
||||
if (layout) {
|
||||
wgpuBindGroupLayoutRelease(layout);
|
||||
}
|
||||
}
|
||||
bindGroupLayouts.clear();
|
||||
bindGroupOrder.clear();
|
||||
bindingsPerGroup.clear();
|
||||
|
||||
// Release shader modules if we created them
|
||||
if (vertexShaderModule) {
|
||||
wgpuShaderModuleRelease(vertexShaderModule);
|
||||
vertexShaderModule = nullptr;
|
||||
}
|
||||
|
||||
if (fragmentShaderModule) {
|
||||
wgpuShaderModuleRelease(fragmentShaderModule);
|
||||
fragmentShaderModule = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
WGPURenderPipeline ShaderProgram::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) {
|
||||
(void)renderable;
|
||||
// Check cache first
|
||||
if (reuseHash.has_value()) {
|
||||
auto it = renderPipelineCache.find(reuseHash.value());
|
||||
if (it != renderPipelineCache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new pipeline
|
||||
WGPURenderPipeline pipeline = createPipeline(
|
||||
vertexLayouts, vertexLayoutCount, colorMode, depthMode, stencilMode, drawModeType);
|
||||
|
||||
// Cache the pipeline if we have a reuse hash
|
||||
if (reuseHash.has_value() && pipeline) {
|
||||
renderPipelineCache[reuseHash.value()] = pipeline;
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
std::optional<size_t> ShaderProgram::getSamplerLocation(const size_t id) const {
|
||||
return (id < textureBindings.size()) ? textureBindings[id] : std::nullopt;
|
||||
}
|
||||
|
||||
void ShaderProgram::initAttribute(const shaders::AttributeInfo& info) {
|
||||
const auto index = static_cast<int>(info.index);
|
||||
#if !defined(NDEBUG)
|
||||
// Indexes must be unique, if there's a conflict check the `attributes` array in the shader
|
||||
vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
|
||||
instanceAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
|
||||
#endif
|
||||
vertexAttributes.set(info.id, index, info.dataType, 1);
|
||||
}
|
||||
|
||||
void ShaderProgram::initInstanceAttribute(const shaders::AttributeInfo& info) {
|
||||
// Index is the block index of the instance attribute
|
||||
const auto index = static_cast<int>(info.index);
|
||||
#if !defined(NDEBUG)
|
||||
// Indexes must not be reused by regular attributes or uniform blocks
|
||||
// More than one instance attribute can have the same index, if they share the block
|
||||
vertexAttributes.visitAttributes([&](const gfx::VertexAttribute& attrib) { assert(attrib.getIndex() != index); });
|
||||
#endif
|
||||
instanceAttributes.set(info.id, index, info.dataType, 1);
|
||||
}
|
||||
|
||||
void ShaderProgram::initTexture(const shaders::TextureInfo& info) {
|
||||
assert(info.id < textureBindings.size());
|
||||
if (info.id >= textureBindings.size()) {
|
||||
return;
|
||||
}
|
||||
textureBindings[info.id] = info.index;
|
||||
}
|
||||
|
||||
const std::vector<ShaderProgram::BindingInfo>& ShaderProgram::getBindingInfosForGroup(uint32_t group) const {
|
||||
for (size_t i = 0; i < bindGroupOrder.size(); ++i) {
|
||||
if (bindGroupOrder[i] == group) {
|
||||
return bindingsPerGroup[i];
|
||||
}
|
||||
}
|
||||
static const std::vector<BindingInfo> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
WGPUBindGroupLayout ShaderProgram::getBindGroupLayout(uint32_t group) const {
|
||||
for (size_t i = 0; i < bindGroupOrder.size(); ++i) {
|
||||
if (bindGroupOrder[i] == group) {
|
||||
return bindGroupLayouts[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShaderProgram::createPipelineLayout(const std::string& vertexSource, const std::string& fragmentSource) {
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vertexSource.empty() || !fragmentSource.empty()) {
|
||||
bindingInfos.clear();
|
||||
if (!vertexSource.empty()) {
|
||||
analyzeShaderBindings(vertexSource, WGPUShaderStage_Vertex);
|
||||
}
|
||||
if (!fragmentSource.empty()) {
|
||||
analyzeShaderBindings(fragmentSource, WGPUShaderStage_Fragment);
|
||||
}
|
||||
}
|
||||
|
||||
rebuildBindGroupLayouts();
|
||||
|
||||
if (pipelineLayout) {
|
||||
wgpuPipelineLayoutRelease(pipelineLayout);
|
||||
pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
std::vector<WGPUBindGroupLayout> layoutHandles;
|
||||
layoutHandles.reserve(bindGroupLayouts.size());
|
||||
for (const auto& layout : bindGroupLayouts) {
|
||||
layoutHandles.push_back(layout);
|
||||
}
|
||||
|
||||
WGPUPipelineLayoutDescriptor pipelineLayoutDesc = {};
|
||||
WGPUStringView pipelineLayoutLabel = {"Pipeline Layout", strlen("Pipeline Layout")};
|
||||
pipelineLayoutDesc.label = pipelineLayoutLabel;
|
||||
pipelineLayoutDesc.bindGroupLayoutCount = layoutHandles.size();
|
||||
pipelineLayoutDesc.bindGroupLayouts = layoutHandles.empty() ? nullptr : layoutHandles.data();
|
||||
|
||||
pipelineLayout = wgpuDeviceCreatePipelineLayout(device, &pipelineLayoutDesc);
|
||||
}
|
||||
|
||||
void ShaderProgram::analyzeShaderBindings(const std::string& source, WGPUShaderStage stage) {
|
||||
size_t searchPos = 0;
|
||||
while (true) {
|
||||
size_t groupToken = source.find("@group", searchPos);
|
||||
if (groupToken == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t groupParen = source.find('(', groupToken);
|
||||
if (groupParen == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t groupStart = groupParen + 1;
|
||||
while (groupStart < source.size() && std::isspace(static_cast<unsigned char>(source[groupStart]))) {
|
||||
++groupStart;
|
||||
}
|
||||
size_t groupEnd = groupStart;
|
||||
while (groupEnd < source.size() && std::isdigit(static_cast<unsigned char>(source[groupEnd]))) {
|
||||
++groupEnd;
|
||||
}
|
||||
if (groupEnd == groupStart) {
|
||||
searchPos = groupEnd;
|
||||
continue;
|
||||
}
|
||||
uint32_t group = static_cast<uint32_t>(
|
||||
std::strtoul(source.substr(groupStart, groupEnd - groupStart).c_str(), nullptr, 10));
|
||||
|
||||
size_t bindingToken = source.find("@binding", groupEnd);
|
||||
if (bindingToken == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t bindingParen = source.find('(', bindingToken);
|
||||
if (bindingParen == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t bindingStart = bindingParen + 1;
|
||||
while (bindingStart < source.size() && std::isspace(static_cast<unsigned char>(source[bindingStart]))) {
|
||||
++bindingStart;
|
||||
}
|
||||
size_t bindingEnd = bindingStart;
|
||||
while (bindingEnd < source.size() && std::isdigit(static_cast<unsigned char>(source[bindingEnd]))) {
|
||||
++bindingEnd;
|
||||
}
|
||||
if (bindingEnd == bindingStart) {
|
||||
searchPos = bindingEnd;
|
||||
continue;
|
||||
}
|
||||
uint32_t binding = static_cast<uint32_t>(
|
||||
std::strtoul(source.substr(bindingStart, bindingEnd - bindingStart).c_str(), nullptr, 10));
|
||||
|
||||
size_t varPos = source.find("var", bindingEnd);
|
||||
if (varPos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
if (varPos > bindingEnd && std::isalpha(static_cast<unsigned char>(source[varPos - 1]))) {
|
||||
searchPos = varPos + 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t afterVar = varPos + 3;
|
||||
while (afterVar < source.size() && std::isspace(static_cast<unsigned char>(source[afterVar]))) {
|
||||
++afterVar;
|
||||
}
|
||||
|
||||
BindingType type = BindingType::UniformBuffer;
|
||||
if (afterVar < source.size() && source[afterVar] == '<') {
|
||||
size_t qualifierEnd = source.find('>', afterVar);
|
||||
if (qualifierEnd == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
const auto qualifier = trimString(
|
||||
std::string_view(source.data() + afterVar + 1, qualifierEnd - afterVar - 1));
|
||||
if (qualifier.find("storage") != std::string::npos) {
|
||||
if (qualifier.find("read_write") != std::string::npos) {
|
||||
type = BindingType::StorageBuffer;
|
||||
} else {
|
||||
type = BindingType::ReadOnlyStorageBuffer;
|
||||
}
|
||||
} else {
|
||||
type = BindingType::UniformBuffer;
|
||||
}
|
||||
afterVar = qualifierEnd + 1;
|
||||
}
|
||||
|
||||
size_t colonPos = source.find(':', afterVar);
|
||||
if (colonPos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
size_t typeStart = colonPos + 1;
|
||||
size_t typeEnd = typeStart;
|
||||
while (typeEnd < source.size()) {
|
||||
char c = source[typeEnd];
|
||||
if (c == ';' || c == '\n' || c == '\r') {
|
||||
break;
|
||||
}
|
||||
++typeEnd;
|
||||
}
|
||||
const auto typeName = trimString(std::string_view(source.data() + typeStart, typeEnd - typeStart));
|
||||
|
||||
if (type == BindingType::UniformBuffer) {
|
||||
if (typeName.find("sampler") != std::string::npos) {
|
||||
type = BindingType::Sampler;
|
||||
} else if (typeName.find("texture") != std::string::npos) {
|
||||
type = BindingType::Texture;
|
||||
}
|
||||
}
|
||||
|
||||
auto existing = std::find_if(bindingInfos.begin(), bindingInfos.end(), [&](const BindingInfo& info) {
|
||||
return info.group == group && info.binding == binding;
|
||||
});
|
||||
|
||||
if (existing == bindingInfos.end()) {
|
||||
bindingInfos.push_back({group, binding, type, stage});
|
||||
} else {
|
||||
existing->visibility |= stage;
|
||||
if (existing->type == BindingType::UniformBuffer && type != BindingType::UniformBuffer) {
|
||||
existing->type = type;
|
||||
}
|
||||
}
|
||||
|
||||
searchPos = typeEnd;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderProgram::rebuildBindGroupLayouts() {
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto layout : bindGroupLayouts) {
|
||||
if (layout) {
|
||||
wgpuBindGroupLayoutRelease(layout);
|
||||
}
|
||||
}
|
||||
bindGroupLayouts.clear();
|
||||
bindGroupOrder.clear();
|
||||
bindingsPerGroup.clear();
|
||||
|
||||
if (bindingInfos.empty()) {
|
||||
bindingInfos.push_back({0u, 0u, BindingType::UniformBuffer, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment});
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::vector<BindingInfo>> grouped;
|
||||
for (const auto& info : bindingInfos) {
|
||||
grouped[info.group].push_back(info);
|
||||
}
|
||||
|
||||
for (auto& entry : grouped) {
|
||||
auto& infos = entry.second;
|
||||
std::sort(infos.begin(), infos.end(), [](const BindingInfo& lhs, const BindingInfo& rhs) {
|
||||
return lhs.binding < rhs.binding;
|
||||
});
|
||||
|
||||
std::vector<WGPUBindGroupLayoutEntry> layoutEntries;
|
||||
layoutEntries.reserve(infos.size());
|
||||
|
||||
for (const auto& info : infos) {
|
||||
WGPUBindGroupLayoutEntry layoutEntry = {};
|
||||
layoutEntry.binding = info.binding;
|
||||
layoutEntry.visibility = info.visibility;
|
||||
|
||||
switch (info.type) {
|
||||
case BindingType::UniformBuffer:
|
||||
layoutEntry.buffer.type = WGPUBufferBindingType_Uniform;
|
||||
layoutEntry.buffer.hasDynamicOffset = 0;
|
||||
layoutEntry.buffer.minBindingSize = 0;
|
||||
break;
|
||||
case BindingType::ReadOnlyStorageBuffer:
|
||||
layoutEntry.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
|
||||
layoutEntry.buffer.hasDynamicOffset = 0;
|
||||
layoutEntry.buffer.minBindingSize = 0;
|
||||
break;
|
||||
case BindingType::StorageBuffer:
|
||||
layoutEntry.buffer.type = WGPUBufferBindingType_Storage;
|
||||
layoutEntry.buffer.hasDynamicOffset = 0;
|
||||
layoutEntry.buffer.minBindingSize = 0;
|
||||
break;
|
||||
case BindingType::Sampler:
|
||||
layoutEntry.sampler.type = WGPUSamplerBindingType_Filtering;
|
||||
break;
|
||||
case BindingType::Texture:
|
||||
layoutEntry.texture.sampleType = WGPUTextureSampleType_Float;
|
||||
layoutEntry.texture.viewDimension = WGPUTextureViewDimension_2D;
|
||||
layoutEntry.texture.multisampled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
layoutEntries.push_back(layoutEntry);
|
||||
}
|
||||
|
||||
WGPUBindGroupLayoutDescriptor desc = {};
|
||||
const std::string labelStr = "Shader Bind Group " + std::to_string(entry.first);
|
||||
WGPUStringView labelView = {labelStr.c_str(), labelStr.length()};
|
||||
desc.label = labelView;
|
||||
desc.entryCount = layoutEntries.size();
|
||||
desc.entries = layoutEntries.data();
|
||||
|
||||
WGPUBindGroupLayout layout = wgpuDeviceCreateBindGroupLayout(device, &desc);
|
||||
if (layout) {
|
||||
bindGroupOrder.push_back(entry.first);
|
||||
bindingsPerGroup.push_back(infos);
|
||||
bindGroupLayouts.push_back(layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WGPURenderPipeline ShaderProgram::createPipeline(const WGPUVertexBufferLayout* vertexLayouts,
|
||||
uint32_t vertexLayoutCount,
|
||||
const gfx::ColorMode& colorMode,
|
||||
const gfx::DepthMode& depthMode,
|
||||
const gfx::StencilMode& stencilMode,
|
||||
gfx::DrawModeType drawModeType) {
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
|
||||
if (!hasVertexEntryPoint || !hasFragmentEntryPoint) {
|
||||
if (!loggedMissingEntryPoint) {
|
||||
Log::Warning(
|
||||
Event::Render,
|
||||
"WebGPU: shader '" + shaderName + "' is missing WGSL entry points; skipping pipeline creation");
|
||||
loggedMissingEntryPoint = true;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!device || !vertexShaderModule || !fragmentShaderModule || !pipelineLayout) {
|
||||
Log::Error(Event::Render,
|
||||
"createPipeline missing requirement: device=" + std::to_string(device != nullptr) +
|
||||
", vertexModule=" + std::to_string(vertexShaderModule != nullptr) +
|
||||
", fragmentModule=" + std::to_string(fragmentShaderModule != nullptr) +
|
||||
", pipelineLayout=" + std::to_string(pipelineLayout != nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set up vertex state
|
||||
WGPUVertexState vertexState = {};
|
||||
vertexState.module = vertexShaderModule;
|
||||
WGPUStringView vertexEntryPoint = {"main", strlen("main")};
|
||||
vertexState.entryPoint = vertexEntryPoint;
|
||||
vertexState.bufferCount = vertexLayoutCount;
|
||||
vertexState.buffers = vertexLayouts;
|
||||
|
||||
// Set up fragment state with color mode blending
|
||||
WGPUBlendComponent alphaBlend = {};
|
||||
alphaBlend.operation = webgpuBlendOperation(gfx::ColorBlendEquationType::Add);
|
||||
alphaBlend.srcFactor = webgpuBlendFactor(gfx::ColorBlendFactorType::SrcAlpha);
|
||||
alphaBlend.dstFactor = webgpuBlendFactor(gfx::ColorBlendFactorType::OneMinusSrcAlpha);
|
||||
|
||||
WGPUBlendComponent colorBlend = {};
|
||||
colorBlend.operation = webgpuBlendOperation(gfx::ColorBlendEquationType::Add);
|
||||
colorBlend.srcFactor = webgpuBlendFactor(gfx::ColorBlendFactorType::SrcAlpha);
|
||||
colorBlend.dstFactor = webgpuBlendFactor(gfx::ColorBlendFactorType::OneMinusSrcAlpha);
|
||||
|
||||
// Apply color mode blending if not replace
|
||||
if (!colorMode.blendFunction.is<gfx::ColorMode::Replace>()) {
|
||||
colorMode.blendFunction.match([&](const auto& blendFunction) {
|
||||
colorBlend.operation = webgpuBlendOperation(gfx::ColorBlendEquationType(blendFunction.equation));
|
||||
colorBlend.srcFactor = webgpuBlendFactor(gfx::ColorBlendFactorType(blendFunction.srcFactor));
|
||||
colorBlend.dstFactor = webgpuBlendFactor(gfx::ColorBlendFactorType(blendFunction.dstFactor));
|
||||
alphaBlend = colorBlend;
|
||||
});
|
||||
}
|
||||
|
||||
WGPUBlendState blendState = {};
|
||||
blendState.color = colorBlend;
|
||||
blendState.alpha = alphaBlend;
|
||||
|
||||
WGPUColorTargetState colorTarget = {};
|
||||
auto colorFormat = backend.getColorFormat();
|
||||
if (colorFormat == wgpu::TextureFormat::Undefined) {
|
||||
colorFormat = wgpu::TextureFormat::BGRA8Unorm;
|
||||
}
|
||||
colorTarget.format = static_cast<WGPUTextureFormat>(colorFormat);
|
||||
colorTarget.blend = &blendState;
|
||||
colorTarget.writeMask = (colorMode.mask.r ? WGPUColorWriteMask_Red : WGPUColorWriteMask_None) |
|
||||
(colorMode.mask.g ? WGPUColorWriteMask_Green : WGPUColorWriteMask_None) |
|
||||
(colorMode.mask.b ? WGPUColorWriteMask_Blue : WGPUColorWriteMask_None) |
|
||||
(colorMode.mask.a ? WGPUColorWriteMask_Alpha : WGPUColorWriteMask_None);
|
||||
|
||||
WGPUFragmentState fragmentState = {};
|
||||
fragmentState.module = fragmentShaderModule;
|
||||
WGPUStringView fragmentEntryPoint = {"main", strlen("main")};
|
||||
fragmentState.entryPoint = fragmentEntryPoint;
|
||||
fragmentState.targetCount = 1;
|
||||
fragmentState.targets = &colorTarget;
|
||||
|
||||
// Set up primitive state
|
||||
WGPUPrimitiveState primitiveState = {};
|
||||
primitiveState.topology = toPrimitiveTopology(drawModeType);
|
||||
primitiveState.stripIndexFormat = (drawModeType == gfx::DrawModeType::LineStrip ||
|
||||
drawModeType == gfx::DrawModeType::TriangleStrip)
|
||||
? WGPUIndexFormat_Uint16
|
||||
: WGPUIndexFormat_Undefined;
|
||||
primitiveState.frontFace = WGPUFrontFace_CCW;
|
||||
primitiveState.cullMode = WGPUCullMode_None;
|
||||
|
||||
// Set up depth stencil state
|
||||
WGPUDepthStencilState depthStencilState = {};
|
||||
const auto depthFormat = backend.getDepthStencilFormat();
|
||||
if (depthFormat != wgpu::TextureFormat::Undefined) {
|
||||
depthStencilState.format = static_cast<WGPUTextureFormat>(depthFormat);
|
||||
depthStencilState.depthWriteEnabled = depthMode.mask == gfx::DepthMaskType::ReadWrite ? WGPUOptionalBool_True
|
||||
: WGPUOptionalBool_False;
|
||||
depthStencilState.depthCompare = toCompareFunction(depthMode.func);
|
||||
|
||||
depthStencilState.stencilFront.compare = WGPUCompareFunction_Always;
|
||||
depthStencilState.stencilFront.failOp = WGPUStencilOperation_Keep;
|
||||
depthStencilState.stencilFront.depthFailOp = WGPUStencilOperation_Keep;
|
||||
depthStencilState.stencilFront.passOp = WGPUStencilOperation_Keep;
|
||||
depthStencilState.stencilBack = depthStencilState.stencilFront;
|
||||
depthStencilState.stencilReadMask = 0;
|
||||
depthStencilState.stencilWriteMask = 0;
|
||||
|
||||
bool stencilEnabled = true;
|
||||
if (stencilMode.test.is<gfx::StencilMode::Always>() && stencilMode.mask == 0 &&
|
||||
stencilMode.fail == gfx::StencilOpType::Keep && stencilMode.depthFail == gfx::StencilOpType::Keep &&
|
||||
stencilMode.pass == gfx::StencilOpType::Keep) {
|
||||
stencilEnabled = false;
|
||||
}
|
||||
|
||||
if (stencilEnabled) {
|
||||
gfx::StencilFunctionType function = gfx::StencilFunctionType::Always;
|
||||
uint32_t readMask = 0xFF;
|
||||
mapbox::util::apply_visitor(
|
||||
[&](const auto& test) {
|
||||
using TestType = std::decay_t<decltype(test)>;
|
||||
function = TestType::func;
|
||||
if constexpr (requires { test.mask; }) {
|
||||
readMask = test.mask;
|
||||
} else {
|
||||
readMask = 0xFF;
|
||||
}
|
||||
},
|
||||
stencilMode.test);
|
||||
|
||||
depthStencilState.stencilFront.compare = toCompareFunction(function);
|
||||
depthStencilState.stencilBack = depthStencilState.stencilFront;
|
||||
depthStencilState.stencilFront.failOp = toStencilOp(stencilMode.fail);
|
||||
depthStencilState.stencilFront.depthFailOp = toStencilOp(stencilMode.depthFail);
|
||||
depthStencilState.stencilFront.passOp = toStencilOp(stencilMode.pass);
|
||||
depthStencilState.stencilBack = depthStencilState.stencilFront;
|
||||
depthStencilState.stencilReadMask = readMask;
|
||||
depthStencilState.stencilWriteMask = stencilMode.mask;
|
||||
}
|
||||
|
||||
if (shaderName == "ClippingMaskProgram") {
|
||||
depthStencilState.stencilFront.passOp = WGPUStencilOperation_Replace;
|
||||
depthStencilState.stencilBack = depthStencilState.stencilFront;
|
||||
if (depthStencilState.stencilWriteMask == 0) {
|
||||
depthStencilState.stencilWriteMask = 0xFF;
|
||||
}
|
||||
if (depthStencilState.stencilReadMask == 0) {
|
||||
depthStencilState.stencilReadMask = 0xFF;
|
||||
}
|
||||
depthStencilState.stencilFront.compare = WGPUCompareFunction_Always;
|
||||
depthStencilState.stencilBack = depthStencilState.stencilFront;
|
||||
}
|
||||
}
|
||||
|
||||
// Create render pipeline
|
||||
WGPURenderPipelineDescriptor pipelineDesc = {};
|
||||
WGPUStringView pipelineLabel = {shaderName.c_str(), shaderName.length()};
|
||||
pipelineDesc.label = pipelineLabel;
|
||||
pipelineDesc.layout = pipelineLayout;
|
||||
pipelineDesc.vertex = vertexState;
|
||||
pipelineDesc.fragment = &fragmentState;
|
||||
pipelineDesc.primitive = primitiveState;
|
||||
pipelineDesc.depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState : nullptr;
|
||||
pipelineDesc.multisample.count = 1;
|
||||
pipelineDesc.multisample.mask = 0xFFFFFFFF;
|
||||
pipelineDesc.multisample.alphaToCoverageEnabled = 0;
|
||||
|
||||
WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device, &pipelineDesc);
|
||||
if (!pipeline) {
|
||||
Log::Error(Event::Render, shaderName + " pipeline creation failed");
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
65
src/mbgl/shaders/webgpu/symbol.cpp
Normal file
65
src/mbgl/shaders/webgpu/symbol.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <mbgl/shaders/webgpu/symbol.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/symbol_layer_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
// Symbol Icon
|
||||
using SymbolIconShaderSource = ShaderSource<BuiltIn::SymbolIconShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 6> SymbolIconShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short4, idSymbolPosOffsetVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::UShort4, idSymbolDataVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short4, idSymbolPixelOffsetVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float3, idSymbolProjectedPosVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float, idSymbolFadeOpacityVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float, idSymbolOpacityVertexAttribute},
|
||||
};
|
||||
|
||||
const std::array<TextureInfo, 1> SymbolIconShaderSource::textures = {
|
||||
TextureInfo{0, idSymbolImageTexture},
|
||||
};
|
||||
|
||||
// Symbol SDF
|
||||
using SymbolSDFShaderSource = ShaderSource<BuiltIn::SymbolSDFShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 10> SymbolSDFShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short4, idSymbolPosOffsetVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::UShort4, idSymbolDataVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Short4, idSymbolPixelOffsetVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float3, idSymbolProjectedPosVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float, idSymbolFadeOpacityVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float4, idSymbolColorVertexAttribute},
|
||||
AttributeInfo{9, gfx::AttributeDataType::Float4, idSymbolHaloColorVertexAttribute},
|
||||
AttributeInfo{10, gfx::AttributeDataType::Float, idSymbolOpacityVertexAttribute},
|
||||
AttributeInfo{11, gfx::AttributeDataType::Float, idSymbolHaloWidthVertexAttribute},
|
||||
AttributeInfo{12, gfx::AttributeDataType::Float, idSymbolHaloBlurVertexAttribute},
|
||||
};
|
||||
|
||||
const std::array<TextureInfo, 1> SymbolSDFShaderSource::textures = {
|
||||
TextureInfo{0, idSymbolImageTexture},
|
||||
};
|
||||
|
||||
// Symbol Text and Icon
|
||||
using SymbolTextAndIconShaderSource = ShaderSource<BuiltIn::SymbolTextAndIconShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 9> SymbolTextAndIconShaderSource::attributes = {
|
||||
AttributeInfo{3, gfx::AttributeDataType::Short4, idSymbolPosOffsetVertexAttribute},
|
||||
AttributeInfo{4, gfx::AttributeDataType::UShort4, idSymbolDataVertexAttribute},
|
||||
AttributeInfo{5, gfx::AttributeDataType::Float3, idSymbolProjectedPosVertexAttribute},
|
||||
AttributeInfo{6, gfx::AttributeDataType::Float, idSymbolFadeOpacityVertexAttribute},
|
||||
AttributeInfo{7, gfx::AttributeDataType::Float4, idSymbolColorVertexAttribute},
|
||||
AttributeInfo{8, gfx::AttributeDataType::Float4, idSymbolHaloColorVertexAttribute},
|
||||
AttributeInfo{9, gfx::AttributeDataType::Float, idSymbolOpacityVertexAttribute},
|
||||
AttributeInfo{10, gfx::AttributeDataType::Float, idSymbolHaloWidthVertexAttribute},
|
||||
AttributeInfo{11, gfx::AttributeDataType::Float, idSymbolHaloBlurVertexAttribute},
|
||||
};
|
||||
|
||||
const std::array<TextureInfo, 2> SymbolTextAndIconShaderSource::textures = {
|
||||
TextureInfo{0, idSymbolImageTexture},
|
||||
TextureInfo{2, idSymbolImageIconTexture},
|
||||
};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
115
src/mbgl/shaders/webgpu/wgsl_preprocessor.cpp
Normal file
115
src/mbgl/shaders/webgpu/wgsl_preprocessor.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <mbgl/shaders/webgpu/wgsl_preprocessor.hpp>
|
||||
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
namespace detail {
|
||||
|
||||
bool isDirective(const std::string& line, const std::string& directive) {
|
||||
std::size_t pos = 0;
|
||||
while (pos < line.size() && std::isspace(static_cast<unsigned char>(line[pos]))) {
|
||||
++pos;
|
||||
}
|
||||
if (pos >= line.size() || line[pos] != '#') {
|
||||
return false;
|
||||
}
|
||||
++pos;
|
||||
while (pos < line.size() && std::isspace(static_cast<unsigned char>(line[pos]))) {
|
||||
++pos;
|
||||
}
|
||||
if (line.compare(pos, directive.size(), directive) != 0) {
|
||||
return false;
|
||||
}
|
||||
pos += directive.size();
|
||||
return pos == line.size() || std::isspace(static_cast<unsigned char>(line[pos]));
|
||||
}
|
||||
|
||||
std::string getDirectiveArgument(const std::string& line) {
|
||||
auto hash = line.find('#');
|
||||
if (hash == std::string::npos) {
|
||||
return {};
|
||||
}
|
||||
auto pos = line.find_first_not_of(" \t", hash + 1);
|
||||
pos = line.find_first_of(" \t", pos);
|
||||
if (pos == std::string::npos) {
|
||||
return {};
|
||||
}
|
||||
pos = line.find_first_not_of(" \t", pos);
|
||||
if (pos == std::string::npos) {
|
||||
return {};
|
||||
}
|
||||
const auto end = line.find_first_of(" \t\r\n", pos);
|
||||
return line.substr(pos, end == std::string::npos ? std::string::npos : end - pos);
|
||||
}
|
||||
|
||||
std::string preprocessWGSL(const std::string& source, const std::unordered_map<std::string, bool>& defines) {
|
||||
std::stringstream input(source);
|
||||
std::string line;
|
||||
std::string output;
|
||||
output.reserve(source.size());
|
||||
|
||||
struct ConditionalFrame {
|
||||
bool parentActive;
|
||||
bool condition;
|
||||
bool elseSeen;
|
||||
};
|
||||
|
||||
std::stack<ConditionalFrame> stack;
|
||||
bool currentActive = true;
|
||||
|
||||
while (std::getline(input, line)) {
|
||||
if (isDirective(line, "ifdef")) {
|
||||
const auto symbol = getDirectiveArgument(line);
|
||||
const bool condition = defines.contains(symbol);
|
||||
stack.push(ConditionalFrame{currentActive, condition, false});
|
||||
currentActive = currentActive && condition;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDirective(line, "ifndef")) {
|
||||
const auto symbol = getDirectiveArgument(line);
|
||||
const bool condition = !defines.contains(symbol);
|
||||
stack.push(ConditionalFrame{currentActive, condition, false});
|
||||
currentActive = currentActive && condition;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDirective(line, "else")) {
|
||||
if (stack.empty()) {
|
||||
continue;
|
||||
}
|
||||
auto& frame = stack.top();
|
||||
if (!frame.elseSeen) {
|
||||
frame.elseSeen = true;
|
||||
currentActive = frame.parentActive && !frame.condition;
|
||||
} else {
|
||||
currentActive = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDirective(line, "endif")) {
|
||||
if (stack.empty()) {
|
||||
continue;
|
||||
}
|
||||
currentActive = stack.top().parentActive;
|
||||
stack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentActive) {
|
||||
output.append(line);
|
||||
output.push_back('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
26
src/mbgl/shaders/webgpu/widevector.cpp
Normal file
26
src/mbgl/shaders/webgpu/widevector.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <mbgl/shaders/webgpu/widevector.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/widevector_ubo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace shaders {
|
||||
|
||||
using WideVectorShaderSource = ShaderSource<BuiltIn::WideVectorShader, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
const std::array<AttributeInfo, 3> WideVectorShaderSource::attributes = {
|
||||
AttributeInfo{wideVectorUBOCount + 0, gfx::AttributeDataType::Float3, idWideVectorScreenPos},
|
||||
AttributeInfo{wideVectorUBOCount + 1, gfx::AttributeDataType::Float4, idWideVectorColor},
|
||||
AttributeInfo{wideVectorUBOCount + 2, gfx::AttributeDataType::Int, idWideVectorIndex},
|
||||
};
|
||||
|
||||
const std::array<AttributeInfo, 4> WideVectorShaderSource::instanceAttributes = {
|
||||
AttributeInfo{wideVectorUBOCount + 3, gfx::AttributeDataType::Float3, idWideVectorInstanceCenter},
|
||||
AttributeInfo{wideVectorUBOCount + 4, gfx::AttributeDataType::Float4, idWideVectorInstanceColor},
|
||||
AttributeInfo{wideVectorUBOCount + 5, gfx::AttributeDataType::Int, idWideVectorInstancePrevious},
|
||||
AttributeInfo{wideVectorUBOCount + 6, gfx::AttributeDataType::Int, idWideVectorInstanceNext},
|
||||
};
|
||||
|
||||
const std::array<TextureInfo, 0> WideVectorShaderSource::textures = {};
|
||||
|
||||
} // namespace shaders
|
||||
} // namespace mbgl
|
||||
149
src/mbgl/webgpu/buffer_resource.cpp
Normal file
149
src/mbgl/webgpu/buffer_resource.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include <mbgl/webgpu/buffer_resource.hpp>
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
#include <mbgl/webgpu/renderer_backend.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
BufferResource::BufferResource(
|
||||
Context& context_, const void* data, std::size_t size_, uint32_t usage_, bool isIndexBuffer_, bool persistent_)
|
||||
: context(context_),
|
||||
size(size_),
|
||||
usage(usage_),
|
||||
isIndexBuffer(isIndexBuffer_),
|
||||
persistent(persistent_) {
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store raw data if provided
|
||||
if (data) {
|
||||
raw.resize(size);
|
||||
std::memcpy(raw.data(), data, size);
|
||||
}
|
||||
|
||||
auto& backend = static_cast<RendererBackend&>(context.getBackend());
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create buffer descriptor
|
||||
WGPUBufferDescriptor bufferDesc{};
|
||||
WGPUStringView label = {"Buffer Resource", strlen("Buffer Resource")};
|
||||
bufferDesc.label = label;
|
||||
const std::size_t paddedSize = (size + 3u) & ~std::size_t(3);
|
||||
|
||||
bufferDesc.usage = usage | WGPUBufferUsage_CopyDst;
|
||||
bufferDesc.size = paddedSize;
|
||||
bufferDesc.mappedAtCreation = 0;
|
||||
|
||||
// Create buffer
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
|
||||
if (buffer && data) {
|
||||
// Upload initial data via queue write
|
||||
WGPUQueue queue = static_cast<WGPUQueue>(backend.getQueue());
|
||||
if (queue) {
|
||||
if (paddedSize == size) {
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
} else {
|
||||
std::vector<std::uint8_t> padded(paddedSize, 0);
|
||||
std::memcpy(padded.data(), data, size);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, padded.data(), paddedSize);
|
||||
}
|
||||
} else {
|
||||
// Fallback to mapping when queue is unavailable (should be rare)
|
||||
void* mapped = wgpuBufferGetMappedRange(buffer, 0, paddedSize);
|
||||
if (mapped) {
|
||||
std::memcpy(mapped, data, size);
|
||||
if (paddedSize > size) {
|
||||
std::memset(static_cast<std::uint8_t*>(mapped) + size, 0, paddedSize - size);
|
||||
}
|
||||
wgpuBufferUnmap(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BufferResource::BufferResource(BufferResource&& other) noexcept
|
||||
: context(other.context),
|
||||
buffer(other.buffer),
|
||||
raw(std::move(other.raw)),
|
||||
size(other.size),
|
||||
usage(other.usage),
|
||||
version(other.version),
|
||||
isIndexBuffer(other.isIndexBuffer),
|
||||
persistent(other.persistent) {
|
||||
other.buffer = nullptr;
|
||||
other.size = 0;
|
||||
}
|
||||
|
||||
BufferResource::~BufferResource() noexcept {
|
||||
if (buffer) {
|
||||
wgpuBufferRelease(buffer);
|
||||
buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BufferResource& BufferResource::operator=(BufferResource&& other) noexcept {
|
||||
if (this != &other) {
|
||||
// Can't reassign context reference, so check they match
|
||||
assert(&context == &other.context);
|
||||
|
||||
if (buffer) {
|
||||
wgpuBufferRelease(buffer);
|
||||
}
|
||||
buffer = other.buffer;
|
||||
raw = std::move(other.raw);
|
||||
size = other.size;
|
||||
usage = other.usage;
|
||||
version = other.version;
|
||||
isIndexBuffer = other.isIndexBuffer;
|
||||
persistent = other.persistent;
|
||||
|
||||
other.buffer = nullptr;
|
||||
other.size = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BufferResource BufferResource::clone() const {
|
||||
return BufferResource(context, raw.empty() ? nullptr : raw.data(), size, usage, isIndexBuffer, persistent);
|
||||
}
|
||||
|
||||
void BufferResource::update(const void* data, std::size_t updateSize, std::size_t offset) noexcept {
|
||||
if (!data || updateSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update raw data if we have it
|
||||
if (!raw.empty() && offset + updateSize <= raw.size()) {
|
||||
std::memcpy(raw.data() + offset, data, updateSize);
|
||||
}
|
||||
|
||||
// Update GPU buffer if we have one
|
||||
if (buffer) {
|
||||
// Get the queue from backend
|
||||
auto& backend = static_cast<RendererBackend&>(context.getBackend());
|
||||
WGPUQueue queue = static_cast<WGPUQueue>(backend.getQueue());
|
||||
if (queue) {
|
||||
// Write data to buffer using queue
|
||||
wgpuQueueWriteBuffer(queue, buffer, offset, data, updateSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Increment version to indicate change
|
||||
++version;
|
||||
}
|
||||
|
||||
bool BufferResource::needReBind(VersionType otherVersion) const noexcept {
|
||||
return version != otherVersion;
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
118
src/mbgl/webgpu/command_encoder.cpp
Normal file
118
src/mbgl/webgpu/command_encoder.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include <mbgl/webgpu/command_encoder.hpp>
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
#include <mbgl/webgpu/render_pass.hpp>
|
||||
#include <mbgl/webgpu/upload_pass.hpp>
|
||||
#include <mbgl/webgpu/renderer_backend.hpp>
|
||||
#include <mbgl/webgpu/renderable_resource.hpp>
|
||||
#include <webgpu/webgpu.h>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <cstring> // for strlen
|
||||
|
||||
// Platform-specific includes for swap
|
||||
#ifdef __APPLE__
|
||||
#include "../../platform/glfw/glfw_webgpu_backend.hpp"
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
CommandEncoder::CommandEncoder(Context& ctx)
|
||||
: context(ctx) {
|
||||
// Initialize WebGPU command encoder
|
||||
auto& backend = static_cast<RendererBackend&>(context.getBackend());
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
|
||||
if (device) {
|
||||
WGPUCommandEncoderDescriptor desc = {};
|
||||
WGPUStringView label = {"MapLibre Command Encoder", strlen("MapLibre Command Encoder")};
|
||||
desc.label = label;
|
||||
encoder = wgpuDeviceCreateCommandEncoder(device, &desc);
|
||||
}
|
||||
}
|
||||
|
||||
CommandEncoder::~CommandEncoder() {
|
||||
if (encoder) {
|
||||
wgpuCommandEncoderRelease(encoder);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::RenderPass> CommandEncoder::createRenderPass(const char* name,
|
||||
const gfx::RenderPassDescriptor& descriptor) {
|
||||
return std::make_unique<RenderPass>(*this, name, descriptor);
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::UploadPass> CommandEncoder::createUploadPass(const char* name, gfx::Renderable& renderable) {
|
||||
return std::make_unique<UploadPass>(renderable, *this, name);
|
||||
}
|
||||
|
||||
void CommandEncoder::present(gfx::Renderable& renderable) {
|
||||
// Submit the command buffer first
|
||||
submitCommandBuffer();
|
||||
|
||||
// Then call swap on the RenderableResource to present the surface
|
||||
// This matches Metal's pattern where swap handles surface presentation
|
||||
try {
|
||||
renderable.getResource<webgpu::RenderableResource>().swap();
|
||||
} catch (...) {
|
||||
// No RenderableResource available - surface presentation handled elsewhere
|
||||
}
|
||||
}
|
||||
|
||||
void CommandEncoder::submitCommandBuffer() {
|
||||
// Submit command buffer directly (for cases without RenderableResource)
|
||||
auto& backend = static_cast<RendererBackend&>(context.getBackend());
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
WGPUQueue queue = static_cast<WGPUQueue>(backend.getQueue());
|
||||
|
||||
if (!device || !queue || !encoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Finish and submit the command buffer
|
||||
WGPUCommandBufferDescriptor cmdBufferDesc = {};
|
||||
WGPUStringView label = {"Command Buffer", strlen("Command Buffer")};
|
||||
cmdBufferDesc.label = label;
|
||||
WGPUCommandBuffer cmdBuffer = wgpuCommandEncoderFinish(encoder, &cmdBufferDesc);
|
||||
|
||||
if (cmdBuffer) {
|
||||
wgpuQueueSubmit(queue, 1, &cmdBuffer);
|
||||
wgpuCommandBufferRelease(cmdBuffer);
|
||||
} else {
|
||||
Log::Error(Event::Render, "WebGPU: Failed to finish command encoder");
|
||||
}
|
||||
|
||||
// Create a new command encoder so subsequent passes in the same frame can continue recording
|
||||
WGPUCommandEncoderDescriptor desc = {};
|
||||
WGPUStringView encoderLabel = {"MapLibre Command Encoder", strlen("MapLibre Command Encoder")};
|
||||
desc.label = encoderLabel;
|
||||
encoder = wgpuDeviceCreateCommandEncoder(device, &desc);
|
||||
|
||||
if (encoder) {
|
||||
context.setCurrentCommandEncoder(this);
|
||||
} else {
|
||||
context.setCurrentCommandEncoder(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void CommandEncoder::pushDebugGroup(const char*) {
|
||||
// Debug markers are currently handled at the render pass level.
|
||||
}
|
||||
|
||||
void CommandEncoder::popDebugGroup() {
|
||||
// Debug markers are currently handled at the render pass level.
|
||||
}
|
||||
|
||||
void CommandEncoder::trackUploadPass(UploadPass* uploadPass) {
|
||||
// Track the upload pass for debug group management
|
||||
currentUploadPass = uploadPass;
|
||||
}
|
||||
|
||||
void CommandEncoder::forgetUploadPass(UploadPass* uploadPass) {
|
||||
// Forget the upload pass when it's destroyed
|
||||
if (currentUploadPass == uploadPass) {
|
||||
currentUploadPass = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
467
src/mbgl/webgpu/context.cpp
Normal file
467
src/mbgl/webgpu/context.cpp
Normal file
@@ -0,0 +1,467 @@
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
#include <mbgl/webgpu/vertex_buffer_resource.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
#include <mbgl/gfx/color_mode.hpp>
|
||||
#include <array>
|
||||
#include <mbgl/gfx/depth_mode.hpp>
|
||||
#include <mbgl/webgpu/command_encoder.hpp>
|
||||
#include <mbgl/webgpu/drawable_builder.hpp>
|
||||
#include <mbgl/webgpu/draw_scope_resource.hpp>
|
||||
#include <mbgl/webgpu/offscreen_texture.hpp>
|
||||
#include <mbgl/gfx/upload_pass.hpp>
|
||||
#include <mbgl/webgpu/uniform_buffer.hpp>
|
||||
#include <mbgl/webgpu/upload_pass.hpp>
|
||||
#include <mbgl/webgpu/vertex_attribute.hpp>
|
||||
#include <mbgl/webgpu/texture2d.hpp>
|
||||
#include <mbgl/renderer/render_static_data.hpp>
|
||||
#include <mbgl/renderer/render_target.hpp>
|
||||
#include <mbgl/webgpu/tile_layer_group.hpp>
|
||||
#include <mbgl/webgpu/layer_group.hpp>
|
||||
#include <mbgl/webgpu/render_pass.hpp>
|
||||
#include <mbgl/gfx/shader_registry.hpp>
|
||||
#include <mbgl/util/hash.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
#include <mbgl/shaders/attributes.hpp>
|
||||
#include <mbgl/shaders/webgpu/clipping_mask.hpp>
|
||||
#include <mbgl/shaders/webgpu/shader_program.hpp>
|
||||
#include <mbgl/shaders/program_parameters.hpp>
|
||||
#include <mbgl/gfx/gfx_types.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
namespace {
|
||||
const auto clipMaskStencilMode = gfx::StencilMode{
|
||||
/*.test=*/gfx::StencilMode::Always(),
|
||||
/*.ref=*/0,
|
||||
/*.mask=*/0xFF,
|
||||
/*.fail=*/gfx::StencilOpType::Keep,
|
||||
/*.depthFail=*/gfx::StencilOpType::Keep,
|
||||
/*.pass=*/gfx::StencilOpType::Replace,
|
||||
};
|
||||
const auto clipMaskDepthMode = gfx::DepthMode{.func = gfx::DepthFunctionType::Always,
|
||||
.mask = gfx::DepthMaskType::ReadOnly};
|
||||
} // namespace
|
||||
|
||||
Context::Context(RendererBackend& backend_)
|
||||
: gfx::Context(gfx::Context::minimumRequiredVertexBindingCount),
|
||||
backend(backend_),
|
||||
globalUniformBuffers(std::make_unique<UniformBufferArray>()) {}
|
||||
|
||||
Context::~Context() = default;
|
||||
|
||||
void Context::beginFrame() {
|
||||
// Begin a new frame - WebGPU command recording starts here
|
||||
}
|
||||
|
||||
void Context::endFrame() {
|
||||
// End the frame - submit WebGPU commands
|
||||
}
|
||||
|
||||
void Context::performCleanup() {
|
||||
// Clean up unused resources
|
||||
}
|
||||
|
||||
void Context::reduceMemoryUsage() {
|
||||
// Free cached resources to reduce memory
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::OffscreenTexture> Context::createOffscreenTexture(Size size,
|
||||
gfx::TextureChannelDataType type,
|
||||
bool depth,
|
||||
bool stencil) {
|
||||
return std::make_unique<OffscreenTexture>(*this, size, type, depth, stencil);
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::OffscreenTexture> Context::createOffscreenTexture(Size size, gfx::TextureChannelDataType type) {
|
||||
return std::make_unique<OffscreenTexture>(*this, size, type, true, false);
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::CommandEncoder> Context::createCommandEncoder() {
|
||||
auto encoder = std::make_unique<CommandEncoder>(*this);
|
||||
currentCommandEncoder = encoder.get();
|
||||
return encoder;
|
||||
}
|
||||
|
||||
gfx::VertexAttributeArrayPtr Context::createVertexAttributeArray() const {
|
||||
return std::make_shared<VertexAttributeArray>();
|
||||
}
|
||||
|
||||
gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) {
|
||||
return std::make_unique<DrawableBuilder>(std::move(name));
|
||||
}
|
||||
|
||||
gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size, bool persistent, bool) {
|
||||
(void)persistent;
|
||||
return std::make_shared<UniformBuffer>(*this, data, size);
|
||||
}
|
||||
|
||||
gfx::UniqueUniformBufferArray Context::createLayerUniformBufferArray() {
|
||||
return std::make_unique<UniformBufferArray>();
|
||||
}
|
||||
|
||||
gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& registry, const std::string& name) {
|
||||
// Align with Metal - just get shader from registry without caching
|
||||
const auto shaderGroup = registry.getShaderGroup(name);
|
||||
auto shader = shaderGroup ? shaderGroup->getOrCreateShader(*this, {}) : gfx::ShaderProgramBasePtr{};
|
||||
return std::static_pointer_cast<gfx::ShaderProgramBase>(std::move(shader));
|
||||
}
|
||||
|
||||
TileLayerGroupPtr Context::createTileLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) {
|
||||
auto tileLayerGroup = std::make_shared<webgpu::TileLayerGroup>(layerIndex, initialCapacity, std::move(name));
|
||||
return tileLayerGroup;
|
||||
}
|
||||
|
||||
LayerGroupPtr Context::createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) {
|
||||
auto layerGroup = std::make_shared<webgpu::LayerGroup>(layerIndex, initialCapacity, std::move(name));
|
||||
return layerGroup;
|
||||
}
|
||||
|
||||
gfx::Texture2DPtr Context::createTexture2D() {
|
||||
return std::make_shared<Texture2D>(*this);
|
||||
}
|
||||
|
||||
RenderTargetPtr Context::createRenderTarget(const Size size, const gfx::TextureChannelDataType type) {
|
||||
return std::make_shared<RenderTarget>(*this, size, type);
|
||||
}
|
||||
|
||||
void Context::resetState(gfx::DepthMode, gfx::ColorMode) {
|
||||
// Reset WebGPU render state
|
||||
}
|
||||
|
||||
void Context::setDirtyState() {
|
||||
// Mark state as needing update
|
||||
}
|
||||
|
||||
void Context::clearStencilBuffer(int32_t value) {
|
||||
// Clear stencil buffer to specified value
|
||||
(void)value;
|
||||
}
|
||||
|
||||
bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& ptr,
|
||||
const void* data,
|
||||
std::size_t size,
|
||||
bool persistent) {
|
||||
if (!ptr) {
|
||||
ptr = createUniformBuffer(data, size, persistent);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update existing buffer
|
||||
auto* buffer = static_cast<UniformBuffer*>(ptr.get());
|
||||
buffer->update(data, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
const gfx::UniformBufferArray& Context::getGlobalUniformBuffers() const {
|
||||
return *globalUniformBuffers;
|
||||
}
|
||||
|
||||
gfx::UniformBufferArray& Context::mutableGlobalUniformBuffers() {
|
||||
return *globalUniformBuffers;
|
||||
}
|
||||
|
||||
void Context::bindGlobalUniformBuffers(gfx::RenderPass& renderPass) const noexcept {
|
||||
// In WebGPU, we can't bind buffers globally to the render pass like Metal does.
|
||||
// Instead, we need to ensure these buffers are available for drawables to include
|
||||
// in their bind groups. The actual binding happens per-drawable.
|
||||
|
||||
// Store a reference to global buffers in the render pass for drawables to access
|
||||
auto& webgpuRenderPass = static_cast<webgpu::RenderPass&>(renderPass);
|
||||
webgpuRenderPass.setGlobalUniformBuffers(globalUniformBuffers.get());
|
||||
}
|
||||
|
||||
void Context::unbindGlobalUniformBuffers(gfx::RenderPass& renderPass) const noexcept {
|
||||
// Clear the global buffer reference from the render pass
|
||||
auto& webgpuRenderPass = static_cast<webgpu::RenderPass&>(renderPass);
|
||||
webgpuRenderPass.setGlobalUniformBuffers(nullptr);
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
void Context::visualizeStencilBuffer() {
|
||||
// Debug visualization of stencil buffer
|
||||
}
|
||||
|
||||
void Context::visualizeDepthBuffer([[maybe_unused]] float depthRangeSize) {
|
||||
// Debug visualization of depth buffer
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<gfx::RenderbufferResource> Context::createRenderbufferResource(gfx::RenderbufferPixelType type,
|
||||
Size size) {
|
||||
(void)type;
|
||||
(void)size;
|
||||
return std::make_unique<RenderbufferResource>();
|
||||
}
|
||||
|
||||
std::unique_ptr<gfx::DrawScopeResource> Context::createDrawScopeResource() {
|
||||
return std::make_unique<DrawScopeResource>(*this);
|
||||
}
|
||||
|
||||
// Buffer creation (aligned with Metal)
|
||||
BufferResource Context::createBuffer(
|
||||
const void* data, std::size_t size, uint32_t usage, bool isIndexBuffer, bool persistent) const {
|
||||
return BufferResource(const_cast<Context&>(*this), data, size, usage, isIndexBuffer, persistent);
|
||||
}
|
||||
|
||||
// Get reusable tile vertex buffer (aligned with Metal)
|
||||
const BufferResource& Context::getTileVertexBuffer() {
|
||||
if (!tileVertexBuffer) {
|
||||
const auto vertices = RenderStaticData::tileVertices();
|
||||
const uint32_t usage = WGPUBufferUsage_Vertex;
|
||||
tileVertexBuffer = createBuffer(vertices.data(), vertices.bytes(), usage, false, true);
|
||||
}
|
||||
return *tileVertexBuffer;
|
||||
}
|
||||
|
||||
// Get reusable tile index buffer (aligned with Metal)
|
||||
const BufferResource& Context::getTileIndexBuffer() {
|
||||
if (!tileIndexBuffer) {
|
||||
const auto indices = RenderStaticData::quadTriangleIndices();
|
||||
const uint32_t usage = WGPUBufferUsage_Index;
|
||||
tileIndexBuffer = createBuffer(indices.data(), indices.bytes(), usage, true, true);
|
||||
}
|
||||
return *tileIndexBuffer;
|
||||
}
|
||||
|
||||
bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass,
|
||||
RenderStaticData& staticData,
|
||||
const std::vector<shaders::ClipUBO>& tileUBOs) {
|
||||
using ShaderClass = shaders::ShaderSource<shaders::BuiltIn::ClippingMaskProgram, gfx::Backend::Type::WebGPU>;
|
||||
|
||||
if (tileUBOs.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clipMaskShader) {
|
||||
if (auto group = staticData.shaders->getShaderGroup(ShaderClass::name)) {
|
||||
clipMaskShader = std::static_pointer_cast<gfx::ShaderProgramBase>(group->getOrCreateShader(*this, {}));
|
||||
}
|
||||
}
|
||||
if (!clipMaskShader) {
|
||||
Log::Error(Event::Render, "WebGPU: Failed to acquire clipping mask shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& shader = static_cast<webgpu::ShaderProgram&>(*clipMaskShader);
|
||||
auto& webgpuRenderPass = static_cast<webgpu::RenderPass&>(renderPass);
|
||||
const auto encoder = webgpuRenderPass.getEncoder();
|
||||
if (!encoder) {
|
||||
Log::Error(Event::Render, "WebGPU: render pass encoder unavailable for clipping masks");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& renderable = webgpuRenderPass.getDescriptor().renderable;
|
||||
if (clipMaskRenderable != &renderable) {
|
||||
clipMaskPipelineHash.reset();
|
||||
clipMaskRenderable = &renderable;
|
||||
}
|
||||
|
||||
const auto& vertexResource = getTileVertexBuffer();
|
||||
const auto& indexResource = getTileIndexBuffer();
|
||||
if (!vertexResource.getBuffer() || !indexResource.getBuffer()) {
|
||||
Log::Error(Event::Render, "WebGPU: Missing tile geometry buffers for clipping masks");
|
||||
return false;
|
||||
}
|
||||
|
||||
const gfx::ColorMode colorMode = gfx::ColorMode::disabled();
|
||||
|
||||
WGPUVertexAttribute vertexAttribute{};
|
||||
vertexAttribute.format = WGPUVertexFormat_Sint16x2;
|
||||
vertexAttribute.offset = 0;
|
||||
vertexAttribute.shaderLocation = static_cast<uint32_t>(ShaderClass::attributes[0].index);
|
||||
|
||||
WGPUVertexBufferLayout vertexLayout{};
|
||||
vertexLayout.arrayStride = sizeof(gfx::Vertex<PositionOnlyLayoutAttributes>);
|
||||
vertexLayout.attributeCount = 1;
|
||||
vertexLayout.attributes = &vertexAttribute;
|
||||
vertexLayout.stepMode = WGPUVertexStepMode_Vertex;
|
||||
|
||||
if (!clipMaskPipelineHash) {
|
||||
clipMaskPipelineHash = util::hash(colorMode.hash(),
|
||||
vertexLayout.arrayStride,
|
||||
static_cast<uint32_t>(vertexAttribute.format),
|
||||
vertexAttribute.shaderLocation);
|
||||
}
|
||||
|
||||
const auto pipeline = shader.getRenderPipeline(renderable,
|
||||
&vertexLayout,
|
||||
1,
|
||||
colorMode,
|
||||
clipMaskDepthMode,
|
||||
clipMaskStencilMode,
|
||||
gfx::DrawModeType::Triangles,
|
||||
clipMaskPipelineHash);
|
||||
if (!pipeline) {
|
||||
Log::Error(Event::Render, "WebGPU: Failed to create clipping mask pipeline");
|
||||
return false;
|
||||
}
|
||||
|
||||
wgpuRenderPassEncoderSetPipeline(encoder, pipeline);
|
||||
|
||||
wgpuRenderPassEncoderSetVertexBuffer(encoder,
|
||||
/*slot=*/0,
|
||||
vertexResource.getBuffer(),
|
||||
/*offset=*/0,
|
||||
vertexResource.getSizeInBytes());
|
||||
wgpuRenderPassEncoderSetIndexBuffer(encoder,
|
||||
indexResource.getBuffer(),
|
||||
WGPUIndexFormat_Uint16,
|
||||
/*offset=*/0,
|
||||
indexResource.getSizeInBytes());
|
||||
|
||||
const uint64_t uboSize = sizeof(shaders::ClipUBO);
|
||||
|
||||
clipMaskUniformBuffers.clear();
|
||||
clipMaskUniformBuffers.reserve(tileUBOs.size());
|
||||
for (const auto& ubo : tileUBOs) {
|
||||
auto buffer = createBuffer(&ubo, uboSize, WGPUBufferUsage_Uniform, false, false);
|
||||
if (!buffer.getBuffer()) {
|
||||
Log::Error(Event::Render, "WebGPU: Failed to allocate uniform buffer for clipping mask tile");
|
||||
return false;
|
||||
}
|
||||
clipMaskUniformBuffers.emplace_back(std::move(buffer));
|
||||
}
|
||||
|
||||
auto& rendererBackend = static_cast<RendererBackend&>(getBackend());
|
||||
WGPUDevice device = static_cast<WGPUDevice>(rendererBackend.getDevice());
|
||||
if (!device) {
|
||||
Log::Error(Event::Render, "WebGPU: Device unavailable for clipping masks");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& bindGroupOrder = shader.getBindGroupOrder();
|
||||
if (bindGroupOrder.empty()) {
|
||||
Log::Error(Event::Render, "WebGPU: No bind group layouts available for clipping mask shader");
|
||||
return false;
|
||||
}
|
||||
const uint32_t bindGroupIndex = bindGroupOrder.front();
|
||||
const auto layout = shader.getBindGroupLayout(bindGroupIndex);
|
||||
if (!layout) {
|
||||
Log::Error(Event::Render, "WebGPU: Bind group layout missing for clipping mask shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& bindingInfos = shader.getBindingInfosForGroup(bindGroupIndex);
|
||||
uint32_t uniformBinding = 0;
|
||||
bool bindingFound = false;
|
||||
for (const auto& info : bindingInfos) {
|
||||
if (info.type == webgpu::ShaderProgram::BindingType::UniformBuffer) {
|
||||
uniformBinding = info.binding;
|
||||
bindingFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bindingFound) {
|
||||
Log::Error(Event::Render, "WebGPU: Uniform binding not found for clipping mask shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
clipMaskActiveBindGroups.clear();
|
||||
clipMaskActiveBindGroups.reserve(tileUBOs.size());
|
||||
|
||||
for (std::size_t i = 0; i < tileUBOs.size(); ++i) {
|
||||
WGPUBindGroupEntry entry{};
|
||||
entry.binding = uniformBinding;
|
||||
const auto& uboBuffer = clipMaskUniformBuffers[i];
|
||||
entry.buffer = uboBuffer.getBuffer();
|
||||
entry.offset = 0;
|
||||
entry.size = uboSize;
|
||||
|
||||
WGPUBindGroupDescriptor descriptor{};
|
||||
descriptor.layout = layout;
|
||||
descriptor.entryCount = 1;
|
||||
descriptor.entries = &entry;
|
||||
|
||||
const auto bindGroup = wgpuDeviceCreateBindGroup(device, &descriptor);
|
||||
if (!bindGroup) {
|
||||
Log::Error(Event::Render, "WebGPU: Failed to create bind group for clipping mask draw");
|
||||
continue;
|
||||
}
|
||||
|
||||
clipMaskActiveBindGroups.push_back(bindGroup);
|
||||
|
||||
wgpuRenderPassEncoderSetBindGroup(encoder, bindGroupIndex, bindGroup, 0, nullptr);
|
||||
wgpuRenderPassEncoderSetStencilReference(encoder, tileUBOs[i].stencil_ref);
|
||||
|
||||
wgpuRenderPassEncoderDrawIndexed(encoder,
|
||||
/*indexCount=*/6,
|
||||
/*instanceCount=*/1,
|
||||
/*firstIndex=*/0,
|
||||
/*baseVertex=*/0,
|
||||
/*firstInstance=*/0);
|
||||
}
|
||||
|
||||
for (auto bindGroup : clipMaskActiveBindGroups) {
|
||||
if (bindGroup) {
|
||||
wgpuBindGroupRelease(bindGroup);
|
||||
}
|
||||
}
|
||||
clipMaskActiveBindGroups.clear();
|
||||
|
||||
auto& renderStats = renderingStats();
|
||||
renderStats.numDrawCalls += tileUBOs.size();
|
||||
renderStats.totalDrawCalls += tileUBOs.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gfx::AttributeBindingArray Context::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<VertexBufferResource>&) noexcept {
|
||||
static_cast<void>(vertexType);
|
||||
static_cast<void>(vertexAttributeIndex);
|
||||
static_cast<void>(vertexData);
|
||||
|
||||
gfx::AttributeBindingArray result;
|
||||
result.resize(overrides.allocatedSize());
|
||||
|
||||
// Process each vertex attribute
|
||||
overrides.visitAttributes([&](const gfx::VertexAttribute& attr) {
|
||||
const auto index = static_cast<std::size_t>(attr.getIndex());
|
||||
if (index >= result.size()) return;
|
||||
|
||||
// Get the shared raw data if available
|
||||
gfx::VertexVectorBasePtr sharedRaw = attr.getSharedRawData();
|
||||
if (!sharedRaw) {
|
||||
// Try default attributes by ID
|
||||
defaults.visitAttributes([&](const gfx::VertexAttribute& defaultAttr) {
|
||||
if (!sharedRaw && defaultAttr.getIndex() == attr.getIndex()) {
|
||||
sharedRaw = defaultAttr.getSharedRawData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (sharedRaw) {
|
||||
// Create vertex buffer from the shared data
|
||||
const auto dataSize = sharedRaw->getRawSize() * sharedRaw->getRawCount();
|
||||
if (dataSize > 0) {
|
||||
// Create GPU buffer for this vertex attribute
|
||||
uint32_t usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst;
|
||||
auto bufferResource = std::make_shared<VertexBufferResource>(
|
||||
createBuffer(sharedRaw->getRawData(), dataSize, usage, false, false));
|
||||
|
||||
// Create attribute binding
|
||||
result[index] = {/*.attribute = */ {attr.getDataType(), /*offset=*/0},
|
||||
/*.vertexStride = */ static_cast<uint32_t>(attr.getStride()),
|
||||
/*.vertexBufferResource = */ bufferResource.get(),
|
||||
/*.vertexOffset = */ 0};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
11
src/mbgl/webgpu/draw_scope_resource.cpp
Normal file
11
src/mbgl/webgpu/draw_scope_resource.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include <mbgl/webgpu/draw_scope_resource.hpp>
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
DrawScopeResource::DrawScopeResource(Context& context_)
|
||||
: context(context_) {}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
976
src/mbgl/webgpu/drawable.cpp
Normal file
976
src/mbgl/webgpu/drawable.cpp
Normal file
@@ -0,0 +1,976 @@
|
||||
#include <mbgl/webgpu/drawable.hpp>
|
||||
|
||||
#include <mbgl/gfx/color_mode.hpp>
|
||||
#include <mbgl/gfx/cull_face_mode.hpp>
|
||||
#include <mbgl/gfx/depth_mode.hpp>
|
||||
#include <mbgl/gfx/stencil_mode.hpp>
|
||||
#include <mbgl/gfx/vertex_attribute.hpp>
|
||||
#include <mbgl/gfx/vertex_vector.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/shaders/segment.hpp>
|
||||
#include <mbgl/shaders/shader_defines.hpp>
|
||||
#include <mbgl/shaders/webgpu/shader_program.hpp>
|
||||
#include <mbgl/webgpu/command_encoder.hpp>
|
||||
#include <mbgl/webgpu/context.hpp>
|
||||
#include <mbgl/webgpu/drawable_impl.hpp>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <mbgl/webgpu/index_buffer_resource.hpp>
|
||||
#include <mbgl/webgpu/render_pass.hpp>
|
||||
#include <mbgl/webgpu/renderer_backend.hpp>
|
||||
#include <mbgl/webgpu/texture2d.hpp>
|
||||
#include <mbgl/webgpu/uniform_buffer.hpp>
|
||||
#include <mbgl/webgpu/upload_pass.hpp>
|
||||
#include <mbgl/webgpu/vertex_buffer_resource.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/variant.hpp>
|
||||
#include <mbgl/util/hash.hpp>
|
||||
#include <mbgl/util/convert.hpp>
|
||||
#include <mbgl/util/monotonic_timer.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#if !defined(NDEBUG)
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
namespace mbgl {
|
||||
namespace webgpu {
|
||||
|
||||
namespace {
|
||||
#if !defined(NDEBUG)
|
||||
std::string debugLabel(const Drawable& drawable) {
|
||||
std::ostringstream ss;
|
||||
ss << drawable.getName() << "#" << drawable.getID();
|
||||
return ss.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct alignas(16) GlobalUBOIndexData {
|
||||
uint32_t value = 0;
|
||||
uint32_t pad[3] = {0, 0, 0};
|
||||
};
|
||||
|
||||
std::size_t hashStencilMode(const gfx::StencilMode& mode) {
|
||||
std::size_t hash = util::hash(static_cast<int>(mode.ref),
|
||||
static_cast<uint32_t>(mode.mask),
|
||||
static_cast<int>(mode.fail),
|
||||
static_cast<int>(mode.depthFail),
|
||||
static_cast<int>(mode.pass));
|
||||
mapbox::util::apply_visitor(
|
||||
[&](const auto& test) {
|
||||
using TestType = std::decay_t<decltype(test)>;
|
||||
hash = util::hash(hash, static_cast<int>(TestType::func));
|
||||
if constexpr (requires { test.mask; }) {
|
||||
hash = util::hash(hash, static_cast<uint32_t>(test.mask));
|
||||
}
|
||||
},
|
||||
mode.test);
|
||||
return hash;
|
||||
}
|
||||
|
||||
WGPUVertexFormat wgpuVertexFormatOf(gfx::AttributeDataType type) {
|
||||
switch (type) {
|
||||
case gfx::AttributeDataType::Byte:
|
||||
return WGPUVertexFormat_Sint8;
|
||||
case gfx::AttributeDataType::Byte2:
|
||||
return WGPUVertexFormat_Sint8x2;
|
||||
case gfx::AttributeDataType::Byte4:
|
||||
return WGPUVertexFormat_Sint8x4;
|
||||
case gfx::AttributeDataType::UByte:
|
||||
return WGPUVertexFormat_Uint8;
|
||||
case gfx::AttributeDataType::UByte2:
|
||||
return WGPUVertexFormat_Uint8x2;
|
||||
case gfx::AttributeDataType::UByte4:
|
||||
return WGPUVertexFormat_Uint8x4;
|
||||
case gfx::AttributeDataType::Short:
|
||||
return WGPUVertexFormat_Sint16;
|
||||
case gfx::AttributeDataType::Short2:
|
||||
return WGPUVertexFormat_Sint16x2;
|
||||
case gfx::AttributeDataType::Short4:
|
||||
return WGPUVertexFormat_Sint16x4;
|
||||
case gfx::AttributeDataType::UShort:
|
||||
return WGPUVertexFormat_Uint16;
|
||||
case gfx::AttributeDataType::UShort2:
|
||||
return WGPUVertexFormat_Uint16x2;
|
||||
case gfx::AttributeDataType::UShort4:
|
||||
return WGPUVertexFormat_Uint16x4;
|
||||
case gfx::AttributeDataType::Int:
|
||||
return WGPUVertexFormat_Sint32;
|
||||
case gfx::AttributeDataType::Int2:
|
||||
return WGPUVertexFormat_Sint32x2;
|
||||
case gfx::AttributeDataType::Int3:
|
||||
return WGPUVertexFormat_Sint32x3;
|
||||
case gfx::AttributeDataType::Int4:
|
||||
return WGPUVertexFormat_Sint32x4;
|
||||
case gfx::AttributeDataType::UInt:
|
||||
return WGPUVertexFormat_Uint32;
|
||||
case gfx::AttributeDataType::UInt2:
|
||||
return WGPUVertexFormat_Uint32x2;
|
||||
case gfx::AttributeDataType::UInt3:
|
||||
return WGPUVertexFormat_Uint32x3;
|
||||
case gfx::AttributeDataType::UInt4:
|
||||
return WGPUVertexFormat_Uint32x4;
|
||||
case gfx::AttributeDataType::Float:
|
||||
return WGPUVertexFormat_Float32;
|
||||
case gfx::AttributeDataType::Float2:
|
||||
return WGPUVertexFormat_Float32x2;
|
||||
case gfx::AttributeDataType::Float3:
|
||||
return WGPUVertexFormat_Float32x3;
|
||||
case gfx::AttributeDataType::Float4:
|
||||
return WGPUVertexFormat_Float32x4;
|
||||
default:
|
||||
assert(!"Unsupported vertex attribute format");
|
||||
return WGPUVertexFormat_Float32x2; // Default fallback
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Simple wrapper to adapt gfx::IndexBuffer to IndexBufferBase
|
||||
struct IndexBuffer : public gfx::IndexBufferBase {
|
||||
IndexBuffer(std::unique_ptr<gfx::IndexBuffer>&& buffer_)
|
||||
: buffer(std::move(buffer_)) {}
|
||||
~IndexBuffer() override = default;
|
||||
|
||||
std::unique_ptr<mbgl::gfx::IndexBuffer> buffer;
|
||||
};
|
||||
|
||||
Drawable::Drawable(std::string name_)
|
||||
: gfx::Drawable(std::move(name_)),
|
||||
impl(std::make_unique<Impl>()) {}
|
||||
|
||||
Drawable::~Drawable() {
|
||||
// Clean up WebGPU resources safely
|
||||
// Note: We don't own the pipeline - it's owned by the shader program
|
||||
// So we should NOT release it here
|
||||
impl->pipelineState = nullptr;
|
||||
|
||||
for (auto& record : impl->bindGroups) {
|
||||
if (record.handle) {
|
||||
wgpuBindGroupRelease(record.handle);
|
||||
}
|
||||
}
|
||||
impl->bindGroups.clear();
|
||||
|
||||
// Uniform buffers are managed by UniformBufferArray
|
||||
// Vertex and index buffers are now managed through attributeBindings and indexes
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
[[maybe_unused]] WGPUPrimitiveTopology getPrimitiveTopology(const gfx::DrawModeType type) noexcept {
|
||||
switch (type) {
|
||||
default:
|
||||
assert(false);
|
||||
[[fallthrough]];
|
||||
case gfx::DrawModeType::Points:
|
||||
return WGPUPrimitiveTopology_PointList;
|
||||
case gfx::DrawModeType::Lines:
|
||||
return WGPUPrimitiveTopology_LineList;
|
||||
case gfx::DrawModeType::LineStrip:
|
||||
return WGPUPrimitiveTopology_LineStrip;
|
||||
case gfx::DrawModeType::Triangles:
|
||||
return WGPUPrimitiveTopology_TriangleList;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
[[maybe_unused]] std::string debugLabel(const gfx::Drawable& drawable) {
|
||||
std::ostringstream oss;
|
||||
oss << drawable.getID().id() << "/" << drawable.getName() << "/tile=";
|
||||
if (const auto& tileID = drawable.getTileID()) {
|
||||
oss << util::toString(*tileID);
|
||||
} else {
|
||||
oss << "(null)";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
} // namespace
|
||||
|
||||
void Drawable::setColorMode(const gfx::ColorMode& value) {
|
||||
gfx::Drawable::setColorMode(value);
|
||||
impl->pipelineState = nullptr; // Rebuild pipeline to honour new color mode
|
||||
}
|
||||
|
||||
void Drawable::setEnableStencil(bool value) {
|
||||
if (getEnableStencil() == value) {
|
||||
return;
|
||||
}
|
||||
gfx::Drawable::setEnableStencil(value);
|
||||
impl->pipelineState = nullptr; // Reset pipeline to force recreation with new stencil state
|
||||
}
|
||||
|
||||
void Drawable::setEnableDepth(bool value) {
|
||||
if (getEnableDepth() == value) {
|
||||
return;
|
||||
}
|
||||
gfx::Drawable::setEnableDepth(value);
|
||||
impl->pipelineState = nullptr; // Reset pipeline to force recreation with new depth state
|
||||
}
|
||||
|
||||
void Drawable::setSubLayerIndex(int32_t value) {
|
||||
if (getSubLayerIndex() == value) {
|
||||
return;
|
||||
}
|
||||
gfx::Drawable::setSubLayerIndex(value);
|
||||
}
|
||||
|
||||
void Drawable::setDepthType(gfx::DepthMaskType value) {
|
||||
if (getDepthType() == value) {
|
||||
return;
|
||||
}
|
||||
gfx::Drawable::setDepthType(value);
|
||||
impl->pipelineState = nullptr; // Reset pipeline to force recreation with new depth mask
|
||||
}
|
||||
|
||||
void Drawable::setDepthModeFor3D(const gfx::DepthMode& value) {
|
||||
if (impl->depthMode.func != value.func || impl->depthMode.mask != value.mask) {
|
||||
impl->pipelineState = nullptr;
|
||||
}
|
||||
impl->depthMode = value;
|
||||
}
|
||||
|
||||
void Drawable::setStencilModeFor3D(const gfx::StencilMode& value) {
|
||||
const auto newHash = hashStencilMode(value);
|
||||
if (!impl->stencilModeHashValid || impl->stencilModeHash != newHash || impl->stencilMode.ref != value.ref ||
|
||||
impl->stencilMode.mask != value.mask || impl->stencilMode.fail != value.fail ||
|
||||
impl->stencilMode.depthFail != value.depthFail || impl->stencilMode.pass != value.pass) {
|
||||
impl->pipelineState = nullptr;
|
||||
impl->stencilModeHashValid = false;
|
||||
}
|
||||
impl->stencilMode = value;
|
||||
impl->stencilModeHash = newHash;
|
||||
}
|
||||
|
||||
void Drawable::setShader(gfx::ShaderProgramBasePtr value) {
|
||||
if (shader == value) {
|
||||
return;
|
||||
}
|
||||
shader = std::move(value);
|
||||
impl->pipelineState = nullptr; // Reset pipeline when shader changes
|
||||
}
|
||||
|
||||
void Drawable::upload(gfx::UploadPass& uploadPass) {
|
||||
if (isCustom) {
|
||||
return;
|
||||
}
|
||||
if (!shader) {
|
||||
Log::Warning(Event::General, "Missing shader for drawable " + util::toString(getID()) + "/" + getName());
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& webgpuUploadPass = static_cast<webgpu::UploadPass&>(uploadPass);
|
||||
auto& gfxContext = webgpuUploadPass.getContext();
|
||||
[[maybe_unused]] auto& context = static_cast<webgpu::Context&>(gfxContext);
|
||||
constexpr auto usage = gfx::BufferUsageType::StaticDraw;
|
||||
|
||||
// We need either raw index data or a buffer already created from them.
|
||||
// We can have a buffer and no indexes, but only if it's not marked dirty.
|
||||
if (!impl->indexes || (impl->indexes->empty() && (!impl->indexes->getBuffer() || impl->indexes->getDirty()))) {
|
||||
assert(!"Missing index data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!impl->indexes->getBuffer() || impl->indexes->getDirty()) {
|
||||
// Create or update a buffer for the index data. We don't update any
|
||||
// existing buffer because it may still be in use by the previous frame.
|
||||
auto indexBufferResource = webgpuUploadPass.createIndexBufferResource(impl->indexes->data(),
|
||||
impl->indexes->bytes(),
|
||||
usage,
|
||||
/*persistent=*/false);
|
||||
auto gfxIndexBuffer = std::make_unique<gfx::IndexBuffer>(impl->indexes->elements(),
|
||||
std::move(indexBufferResource));
|
||||
auto buffer = std::make_unique<IndexBuffer>(std::move(gfxIndexBuffer));
|
||||
|
||||
impl->indexes->setBuffer(std::move(buffer));
|
||||
impl->indexes->setDirty(false);
|
||||
}
|
||||
|
||||
// Upload vertex attributes (like Metal does)
|
||||
const bool buildAttribs = !vertexAttributes || !attributeUpdateTime ||
|
||||
vertexAttributes->isModifiedAfter(*attributeUpdateTime);
|
||||
|
||||
if (buildAttribs) {
|
||||
#if !defined(NDEBUG)
|
||||
const auto debugGroup = webgpuUploadPass.createDebugGroup(debugLabel(*this));
|
||||
#endif
|
||||
|
||||
if (!vertexAttributes) {
|
||||
vertexAttributes = std::make_shared<gfx::VertexAttributeArray>();
|
||||
}
|
||||
|
||||
// Apply drawable values to shader defaults (matching Metal's approach)
|
||||
std::vector<std::unique_ptr<gfx::VertexBufferResource>> vertexBuffers;
|
||||
auto attributeBindings_ = webgpuUploadPass.buildAttributeBindings(impl->vertexCount,
|
||||
impl->vertexType,
|
||||
/*vertexAttributeIndex=*/-1,
|
||||
/*vertexData=*/{},
|
||||
shader->getVertexAttributes(),
|
||||
*vertexAttributes,
|
||||
usage,
|
||||
attributeUpdateTime,
|
||||
vertexBuffers);
|
||||
|
||||
if (impl->attributeBindings != attributeBindings_) {
|
||||
impl->attributeBindings = std::move(attributeBindings_);
|
||||
impl->vertexDescHash = 0; // Reset hash when bindings change
|
||||
impl->pipelineState = nullptr; // Reset pipeline state when attribute bindings change
|
||||
// Store the dummy vertex buffers to keep them alive
|
||||
impl->dummyVertexBuffers = std::move(vertexBuffers);
|
||||
}
|
||||
}
|
||||
|
||||
// Build instance buffer (like Metal does)
|
||||
const bool buildInstanceBuffer =
|
||||
(instanceAttributes && (!attributeUpdateTime || instanceAttributes->isModifiedAfter(*attributeUpdateTime)));
|
||||
|
||||
if (buildInstanceBuffer) {
|
||||
// Apply instance values to shader defaults (matching Metal's approach)
|
||||
std::vector<std::unique_ptr<gfx::VertexBufferResource>> instanceBuffers;
|
||||
auto instanceBindings_ = webgpuUploadPass.buildAttributeBindings(impl->vertexCount,
|
||||
impl->vertexType,
|
||||
/*vertexAttributeIndex=*/-1,
|
||||
/*vertexData=*/{},
|
||||
shader->getInstanceAttributes(),
|
||||
*instanceAttributes,
|
||||
usage,
|
||||
attributeUpdateTime,
|
||||
instanceBuffers);
|
||||
|
||||
if (impl->instanceBindings != instanceBindings_) {
|
||||
impl->instanceBindings = std::move(instanceBindings_);
|
||||
impl->pipelineState = nullptr; // Reset pipeline state when instance bindings change
|
||||
// Store the dummy instance buffers to keep them alive
|
||||
if (!instanceBuffers.empty()) {
|
||||
impl->dummyVertexBuffers.insert(impl->dummyVertexBuffers.end(),
|
||||
std::make_move_iterator(instanceBuffers.begin()),
|
||||
std::make_move_iterator(instanceBuffers.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upload uniform buffers to ensure they're ready for the GPU
|
||||
// This is critical for making drawables visible
|
||||
for (size_t i = 0; i < impl->uniformBuffers.allocatedSize(); ++i) {
|
||||
auto& uniformBuffer = impl->uniformBuffers.get(i);
|
||||
if (uniformBuffer) {
|
||||
// Make sure the uniform buffer has its data uploaded
|
||||
// The uniform buffer should handle its own upload internally
|
||||
// but we need to ensure it's marked as not dirty
|
||||
auto* webgpuUniformBuffer = static_cast<webgpu::UniformBuffer*>(uniformBuffer.get());
|
||||
if (webgpuUniformBuffer) {
|
||||
// The uniform buffer's data should already be uploaded when it was created/updated
|
||||
// Just ensure we have a valid buffer handle
|
||||
if (!webgpuUniformBuffer->getBuffer()) {
|
||||
Log::Error(Event::Render, "Uniform buffer " + std::to_string(i) + " has no GPU buffer!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upload textures if needed
|
||||
const bool texturesNeedUpload = std::any_of(
|
||||
textures.begin(), textures.end(), [](const auto& texture) { return texture && texture->needsUpload(); });
|
||||
|
||||
if (texturesNeedUpload) {
|
||||
for (auto& texture : textures) {
|
||||
if (texture && texture->needsUpload()) {
|
||||
texture->upload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attributeUpdateTime = util::MonotonicTimer::now();
|
||||
}
|
||||
|
||||
void Drawable::draw(PaintParameters& parameters) const {
|
||||
static int drawCallCount = 0;
|
||||
drawCallCount++;
|
||||
|
||||
if (isCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get WebGPU context and render pass (following Metal's pattern)
|
||||
auto& context = static_cast<webgpu::Context&>(parameters.context);
|
||||
auto& webgpuRenderPass = static_cast<webgpu::RenderPass&>(*parameters.renderPass);
|
||||
WGPURenderPassEncoder renderPassEncoder = webgpuRenderPass.getEncoder();
|
||||
if (!renderPassEncoder) {
|
||||
Log::Error(Event::Render, "No render pass encoder available");
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shader) {
|
||||
Log::Warning(Event::General, "Missing shader for drawable " + util::toString(getID()) + "/" + getName());
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get WebGPU backend and device
|
||||
auto& backend = static_cast<webgpu::RendererBackend&>(context.getBackend());
|
||||
WGPUDevice device = static_cast<WGPUDevice>(backend.getDevice());
|
||||
WGPUQueue queue = static_cast<WGPUQueue>(backend.getQueue());
|
||||
|
||||
if (!device || !queue) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
// Debug group creation would go here (Metal has this but we need to implement it for WebGPU)
|
||||
// const auto debugGroup = parameters.encoder->createDebugGroup(getName());
|
||||
#endif
|
||||
|
||||
// Handle uboIndex like Metal does
|
||||
// In WebGPU, we'll need to handle this differently since we use bind groups
|
||||
// Metal uses setVertexBytes/setFragmentBytes for the uboIndex
|
||||
// For now, we'll store it for use in bind group creation
|
||||
|
||||
// Bind uniform buffers (like Metal does)
|
||||
impl->uniformBuffers.bind(webgpuRenderPass);
|
||||
|
||||
// Check index buffer is valid (like Metal does)
|
||||
if (impl->indexes && (!impl->indexes->getBuffer() || impl->indexes->getDirty())) {
|
||||
assert(!"Index buffer not uploaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build bind groups based on shader metadata
|
||||
if (shader) {
|
||||
auto webgpuShader = std::static_pointer_cast<mbgl::webgpu::ShaderProgram>(shader);
|
||||
if (webgpuShader) {
|
||||
WGPUDevice deviceHandle = static_cast<WGPUDevice>(backend.getDevice());
|
||||
|
||||
for (auto& record : impl->bindGroups) {
|
||||
if (record.handle) {
|
||||
wgpuBindGroupRelease(record.handle);
|
||||
}
|
||||
}
|
||||
impl->bindGroups.clear();
|
||||
|
||||
if (deviceHandle) {
|
||||
const auto& groupOrder = webgpuShader->getBindGroupOrder();
|
||||
static bool loggedBindGroupOrder = false;
|
||||
if (!loggedBindGroupOrder && getName().find("fill-outline") != std::string::npos) {
|
||||
std::string orderStr;
|
||||
for (size_t idx = 0; idx < groupOrder.size(); ++idx) {
|
||||
orderStr += std::to_string(groupOrder[idx]);
|
||||
if (idx + 1 < groupOrder.size()) orderStr += ",";
|
||||
}
|
||||
loggedBindGroupOrder = true;
|
||||
}
|
||||
const auto* globalBuffers = webgpuRenderPass.getGlobalUniformBuffers();
|
||||
|
||||
const auto findTextureForBinding = [&](uint32_t binding, bool samplerBinding) -> Texture2D* {
|
||||
for (size_t texIndex = 0; texIndex < textures.size(); ++texIndex) {
|
||||
if (!textures[texIndex]) {
|
||||
continue;
|
||||
}
|
||||
if (const auto location = webgpuShader->getSamplerLocation(texIndex)) {
|
||||
if ((samplerBinding && *location == binding) ||
|
||||
(!samplerBinding && (*location == binding || *location + 1 == binding))) {
|
||||
return static_cast<Texture2D*>(textures[texIndex].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
for (size_t slot = 0; slot < groupOrder.size(); ++slot) {
|
||||
const uint32_t group = groupOrder[slot];
|
||||
auto layout = webgpuShader->getBindGroupLayout(group);
|
||||
if (!layout) {
|
||||
const std::string shaderName = webgpuShader ? std::string(webgpuShader->typeName()) : "unknown";
|
||||
Log::Warning(Event::Render,
|
||||
"WebGPU: missing bind group layout for group " + std::to_string(group) +
|
||||
" in shader '" + shaderName + "'");
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& bindingInfos = webgpuShader->getBindingInfosForGroup(group);
|
||||
if (getName().find("fill-outline") != std::string::npos) {
|
||||
std::string infoStr;
|
||||
for (const auto& info : bindingInfos) {
|
||||
infoStr += "(binding=" + std::to_string(info.binding) +
|
||||
", type=" + std::to_string(static_cast<int>(info.type)) + ")";
|
||||
}
|
||||
}
|
||||
std::vector<WGPUBindGroupEntry> entries;
|
||||
entries.reserve(bindingInfos.size());
|
||||
bool validBindings = true;
|
||||
|
||||
for (const auto& bindingInfo : bindingInfos) {
|
||||
WGPUBindGroupEntry entry = {};
|
||||
entry.binding = bindingInfo.binding;
|
||||
|
||||
switch (bindingInfo.type) {
|
||||
case ShaderProgram::BindingType::UniformBuffer:
|
||||
case ShaderProgram::BindingType::ReadOnlyStorageBuffer:
|
||||
case ShaderProgram::BindingType::StorageBuffer: {
|
||||
std::shared_ptr<gfx::UniformBuffer> buffer = impl->uniformBuffers.get(
|
||||
bindingInfo.binding);
|
||||
if (bindingInfo.binding == shaders::idGlobalUBOIndex) {
|
||||
GlobalUBOIndexData indexData;
|
||||
indexData.value = getUBOIndex();
|
||||
context.emplaceOrUpdateUniformBuffer(
|
||||
impl->uboIndexUniform, &indexData, sizeof(indexData), false);
|
||||
if (impl->uboIndexUniform) {
|
||||
impl->uniformBuffers.set(bindingInfo.binding, impl->uboIndexUniform);
|
||||
buffer = impl->uboIndexUniform;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer && globalBuffers) {
|
||||
buffer = globalBuffers->get(bindingInfo.binding);
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
if (drawCallCount <= 200) {
|
||||
Log::Warning(Event::Render,
|
||||
"No buffer found for binding " +
|
||||
std::to_string(bindingInfo.binding) +
|
||||
" type=" + std::to_string(static_cast<int>(bindingInfo.type)) +
|
||||
" group=" + std::to_string(group));
|
||||
}
|
||||
validBindings = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto* webgpuUniform = static_cast<const webgpu::UniformBuffer*>(buffer.get());
|
||||
if (!webgpuUniform || !webgpuUniform->getBuffer()) {
|
||||
if (drawCallCount <= 200) {
|
||||
Log::Warning(Event::Render,
|
||||
"Uniform buffer missing GPU handle for binding " +
|
||||
std::to_string(bindingInfo.binding) +
|
||||
" group=" + std::to_string(group));
|
||||
}
|
||||
validBindings = false;
|
||||
break;
|
||||
}
|
||||
|
||||
entry.buffer = webgpuUniform->getBuffer();
|
||||
entry.offset = 0;
|
||||
entry.size = buffer->getSize();
|
||||
break;
|
||||
}
|
||||
case ShaderProgram::BindingType::Sampler: {
|
||||
Texture2D* texture = findTextureForBinding(bindingInfo.binding, true);
|
||||
if (!texture) {
|
||||
validBindings = false;
|
||||
break;
|
||||
}
|
||||
if (!texture->getSampler()) {
|
||||
texture->setSamplerConfiguration(texture->getSamplerState());
|
||||
}
|
||||
entry.sampler = texture->getSampler();
|
||||
break;
|
||||
}
|
||||
case ShaderProgram::BindingType::Texture: {
|
||||
Texture2D* texture = findTextureForBinding(bindingInfo.binding, false);
|
||||
if (!texture) {
|
||||
validBindings = false;
|
||||
break;
|
||||
}
|
||||
if (!texture->getTexture() || !texture->getTextureView()) {
|
||||
texture->create();
|
||||
}
|
||||
if (!texture->getTextureView()) {
|
||||
validBindings = false;
|
||||
break;
|
||||
}
|
||||
entry.textureView = texture->getTextureView();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validBindings) {
|
||||
break;
|
||||
}
|
||||
|
||||
entries.push_back(entry);
|
||||
}
|
||||
|
||||
if (!validBindings || entries.empty()) {
|
||||
Log::Warning(Event::Render,
|
||||
"WebGPU: invalid bindings for drawable '" + getName() +
|
||||
"' (group=" + std::to_string(group) + ", slot=" + std::to_string(slot) + ")");
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string label = getName() + " bind-group " + std::to_string(group);
|
||||
WGPUStringView labelView = {label.c_str(), label.length()};
|
||||
|
||||
WGPUBindGroupDescriptor descriptor = {};
|
||||
descriptor.label = labelView;
|
||||
descriptor.layout = layout;
|
||||
descriptor.entryCount = entries.size();
|
||||
descriptor.entries = entries.data();
|
||||
|
||||
WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(deviceHandle, &descriptor);
|
||||
impl->bindGroups.push_back({static_cast<uint32_t>(slot), group, bindGroup});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto drawableTileID = getTileID();
|
||||
|
||||
gfx::DepthMode depthMode = gfx::DepthMode::disabled();
|
||||
gfx::StencilMode stencilMode = gfx::StencilMode::disabled();
|
||||
|
||||
if (is3D) {
|
||||
depthMode = impl->depthMode;
|
||||
stencilMode = impl->stencilMode;
|
||||
} else {
|
||||
if (getEnableDepth()) {
|
||||
depthMode = parameters.depthModeForSublayer(getSubLayerIndex(), getDepthType());
|
||||
}
|
||||
if (getEnableStencil() && drawableTileID) {
|
||||
stencilMode = parameters.stencilModeForClipping(drawableTileID->toUnwrapped());
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t newStencilHash = hashStencilMode(stencilMode);
|
||||
if (!impl->stencilModeHashValid || impl->stencilModeHash != newStencilHash ||
|
||||
impl->depthMode.func != depthMode.func || impl->depthMode.mask != depthMode.mask) {
|
||||
impl->pipelineState = nullptr;
|
||||
impl->stencilModeHashValid = true;
|
||||
}
|
||||
|
||||
impl->depthMode = depthMode;
|
||||
impl->stencilMode = stencilMode;
|
||||
impl->stencilModeHash = newStencilHash;
|
||||
impl->previousStencilMode = stencilMode;
|
||||
|
||||
// Get pipeline from shader if we don't have it yet (like Metal)
|
||||
struct UniqueBinding {
|
||||
const VertexBufferResource* vertexResource = nullptr;
|
||||
const BufferResource* buffer = nullptr;
|
||||
uint32_t stride = 0;
|
||||
uint64_t baseOffset = 0;
|
||||
WGPUVertexStepMode stepMode = WGPUVertexStepMode_Vertex;
|
||||
std::vector<WGPUVertexAttribute> attributes;
|
||||
std::vector<uint64_t> attributeByteOffsets;
|
||||
};
|
||||
|
||||
auto accumulateBindings = [](const gfx::AttributeBindingArray& source,
|
||||
const WGPUVertexStepMode stepMode,
|
||||
std::vector<UniqueBinding>& out) {
|
||||
for (size_t attributeIndex = 0; attributeIndex < source.size(); ++attributeIndex) {
|
||||
const auto& binding = source[attributeIndex];
|
||||
if (!binding.has_value() || !binding->vertexBufferResource) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* vertexBufferRes = static_cast<const VertexBufferResource*>(binding->vertexBufferResource);
|
||||
if (!vertexBufferRes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& bufferResource = vertexBufferRes->getBuffer();
|
||||
if (!bufferResource.getBuffer()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t bindingByteOffset = static_cast<uint64_t>(binding->vertexOffset) * binding->vertexStride;
|
||||
|
||||
auto it = std::find_if(out.begin(), out.end(), [&](const UniqueBinding& entry) {
|
||||
return entry.vertexResource == vertexBufferRes && entry.stride == binding->vertexStride &&
|
||||
entry.stepMode == stepMode;
|
||||
});
|
||||
|
||||
if (it == out.end()) {
|
||||
UniqueBinding entry;
|
||||
entry.vertexResource = vertexBufferRes;
|
||||
entry.buffer = &bufferResource;
|
||||
entry.stride = binding->vertexStride;
|
||||
entry.stepMode = stepMode;
|
||||
entry.baseOffset = bindingByteOffset;
|
||||
entry.attributes.reserve(4);
|
||||
entry.attributeByteOffsets.reserve(4);
|
||||
out.push_back(std::move(entry));
|
||||
it = out.end() - 1;
|
||||
} else {
|
||||
it->baseOffset = std::min(it->baseOffset, bindingByteOffset);
|
||||
}
|
||||
|
||||
WGPUVertexAttribute attr = {};
|
||||
attr.format = wgpuVertexFormatOf(binding->attribute.dataType);
|
||||
attr.shaderLocation = static_cast<uint32_t>(attributeIndex);
|
||||
attr.offset = binding->attribute.offset;
|
||||
|
||||
it->attributes.push_back(attr);
|
||||
it->attributeByteOffsets.push_back(bindingByteOffset);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<UniqueBinding> uniqueBindings;
|
||||
uniqueBindings.reserve(impl->attributeBindings.size() + impl->instanceBindings.size());
|
||||
accumulateBindings(impl->attributeBindings, WGPUVertexStepMode_Vertex, uniqueBindings);
|
||||
accumulateBindings(impl->instanceBindings, WGPUVertexStepMode_Instance, uniqueBindings);
|
||||
|
||||
constexpr uint32_t maxVertexBufferSlots = 8;
|
||||
if (uniqueBindings.size() > maxVertexBufferSlots) {
|
||||
Log::Warning(Event::Render,
|
||||
"WebGPU: reducing vertex buffer bindings from " + std::to_string(uniqueBindings.size()) + " to " +
|
||||
std::to_string(maxVertexBufferSlots));
|
||||
uniqueBindings.resize(maxVertexBufferSlots);
|
||||
}
|
||||
|
||||
for (auto& entry : uniqueBindings) {
|
||||
for (size_t j = 0; j < entry.attributes.size(); ++j) {
|
||||
if (entry.attributeByteOffsets[j] < entry.baseOffset) {
|
||||
continue;
|
||||
}
|
||||
const auto delta = entry.attributeByteOffsets[j] - entry.baseOffset;
|
||||
entry.attributes[j].offset += static_cast<uint32_t>(delta);
|
||||
}
|
||||
}
|
||||
|
||||
auto& shaderWebGPU = static_cast<mbgl::webgpu::ShaderProgram&>(*shader);
|
||||
|
||||
if (!impl->pipelineState) {
|
||||
// Create vertex layout from attribute bindings
|
||||
std::vector<WGPUVertexBufferLayout> vertexLayouts;
|
||||
vertexLayouts.reserve(uniqueBindings.size());
|
||||
|
||||
for (auto& binding : uniqueBindings) {
|
||||
WGPUVertexBufferLayout layout = {};
|
||||
layout.arrayStride = binding.stride;
|
||||
layout.stepMode = binding.stepMode;
|
||||
layout.attributeCount = binding.attributes.size();
|
||||
layout.attributes = binding.attributes.data();
|
||||
vertexLayouts.push_back(layout);
|
||||
}
|
||||
|
||||
// Get render pipeline similar to Metal's getRenderPipelineState
|
||||
// Use the actual color mode from the drawable (like Metal does)
|
||||
const auto& colorMode = getColorMode();
|
||||
|
||||
// Get the renderable from render pass descriptor like Metal does
|
||||
const auto& renderable = webgpuRenderPass.getDescriptor().renderable;
|
||||
|
||||
gfx::DrawModeType drawModeType = gfx::DrawModeType::Triangles;
|
||||
if (!impl->segments.empty()) {
|
||||
drawModeType = impl->segments.front()->getMode().type;
|
||||
}
|
||||
|
||||
impl->pipelineState = shaderWebGPU.getRenderPipeline(renderable,
|
||||
vertexLayouts.empty() ? nullptr : vertexLayouts.data(),
|
||||
vertexLayouts.size(),
|
||||
colorMode,
|
||||
depthMode,
|
||||
stencilMode,
|
||||
drawModeType,
|
||||
std::nullopt);
|
||||
if (!impl->pipelineState) {
|
||||
Log::Warning(Event::Render,
|
||||
"WebGPU: skipping pipeline creation for drawable '" + getName() + "' (shader='" +
|
||||
shaderWebGPU.typeName().data() + "')");
|
||||
}
|
||||
}
|
||||
|
||||
if (impl->pipelineState) {
|
||||
if (!renderPassEncoder) {
|
||||
Log::Error(Event::Render, "renderPassEncoder is null!");
|
||||
return;
|
||||
}
|
||||
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, impl->pipelineState);
|
||||
if (getEnableStencil()) {
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, static_cast<uint32_t>(stencilMode.ref));
|
||||
}
|
||||
} else {
|
||||
Log::Warning(Event::Render, "WebGPU: no pipeline available for drawable '" + getName() + "'; draw skipped");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t bufferSlot = 0;
|
||||
for (const auto& binding : uniqueBindings) {
|
||||
if (!binding.buffer || !binding.buffer->getBuffer()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t bufferSize = binding.buffer->getSizeInBytes();
|
||||
if (binding.baseOffset >= bufferSize) {
|
||||
Log::Warning(Event::Render,
|
||||
"Skipping vertex buffer slot " + std::to_string(bufferSlot) + " due to base offset " +
|
||||
std::to_string(binding.baseOffset) + " >= size " + std::to_string(bufferSize));
|
||||
continue;
|
||||
}
|
||||
|
||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder,
|
||||
bufferSlot,
|
||||
binding.buffer->getBuffer(),
|
||||
binding.baseOffset,
|
||||
bufferSize - binding.baseOffset);
|
||||
bufferSlot++;
|
||||
}
|
||||
|
||||
// Bind index buffer if present
|
||||
if (impl->indexes && impl->indexes->elements() > 0 && !impl->indexes->getDirty()) {
|
||||
// Get the buffer from indexes
|
||||
if (const auto* indexBufferBase = impl->indexes->getBuffer()) {
|
||||
if (const auto* webgpuIndexBuffer = static_cast<const IndexBuffer*>(indexBufferBase)) {
|
||||
if (webgpuIndexBuffer->buffer) {
|
||||
const auto& indexBufferRes = webgpuIndexBuffer->buffer->getResource<IndexBufferResource>();
|
||||
const auto& bufferResource = indexBufferRes.getBuffer();
|
||||
if (bufferResource.getBuffer()) {
|
||||
// Always use Uint16 format for now
|
||||
WGPUIndexFormat indexFormat = WGPUIndexFormat_Uint16;
|
||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder,
|
||||
bufferResource.getBuffer(),
|
||||
indexFormat,
|
||||
0,
|
||||
bufferResource.getSizeInBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set bind group
|
||||
for (const auto& record : impl->bindGroups) {
|
||||
if (record.handle) {
|
||||
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, record.slot, record.handle, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle depth and stencil states (like Metal does)
|
||||
// For 3D mode, stenciling is handled by the layer group
|
||||
if (!is3D) {
|
||||
if (enableStencil) {
|
||||
if (tileID) {
|
||||
const auto expectedStencil = parameters.stencilModeForClipping(tileID->toUnwrapped());
|
||||
#if !defined(NDEBUG)
|
||||
assert(hashStencilMode(expectedStencil) == hashStencilMode(impl->stencilMode));
|
||||
#else
|
||||
if (hashStencilMode(expectedStencil) != hashStencilMode(impl->stencilMode)) {
|
||||
Log::Warning(
|
||||
Event::Render,
|
||||
"WebGPU drawable stencil mismatch for '" + getName() + "' tile=" + util::toString(*tileID));
|
||||
}
|
||||
#endif
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, static_cast<uint32_t>(expectedStencil.ref));
|
||||
}
|
||||
} else {
|
||||
// Reset the reference so subsequent drawables that re-enable stencilling start from a known value.
|
||||
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0u);
|
||||
}
|
||||
|
||||
if (getEnableDepth()) {
|
||||
#if !defined(NDEBUG)
|
||||
const auto expectedDepth = parameters.depthModeForSublayer(getSubLayerIndex(), getDepthType());
|
||||
assert(expectedDepth.func == impl->depthMode.func);
|
||||
assert(expectedDepth.mask == impl->depthMode.mask);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Draw indexed geometry - loop through segments (exactly like Metal does)
|
||||
// int segmentCount = 0;
|
||||
for (const auto& seg_ : impl->segments) {
|
||||
const auto& segment = static_cast<DrawSegment&>(*seg_);
|
||||
const auto& mlSegment = segment.getSegment();
|
||||
if (mlSegment.indexLength > 0) {
|
||||
const uint32_t instanceCount = instanceAttributes ? instanceAttributes->getMaxCount() : 1;
|
||||
const uint32_t indexOffset = mlSegment.indexOffset;
|
||||
const int32_t baseVertex = static_cast<int32_t>(mlSegment.vertexOffset);
|
||||
const uint32_t baseInstance = 0;
|
||||
|
||||
// Check if encoder is valid before drawing
|
||||
if (!renderPassEncoder) {
|
||||
Log::Error(Event::Render, "Render pass encoder became null before draw!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have valid indices
|
||||
if (mlSegment.indexLength == 0) {
|
||||
Log::Warning(Event::Render, "Skipping draw with 0 indices");
|
||||
continue;
|
||||
}
|
||||
|
||||
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder,
|
||||
mlSegment.indexLength, // indexCount
|
||||
instanceCount, // instanceCount
|
||||
indexOffset, // firstIndex
|
||||
baseVertex, // baseVertex
|
||||
baseInstance); // firstInstance
|
||||
|
||||
context.renderingStats().numDrawCalls++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::setIndexData(gfx::IndexVectorBasePtr indices, std::vector<UniqueDrawSegment> segments) {
|
||||
impl->indexes = std::move(indices);
|
||||
impl->segments = std::move(segments);
|
||||
}
|
||||
|
||||
void Drawable::setVertices(std::vector<uint8_t>&& data, std::size_t count, gfx::AttributeDataType dataType) {
|
||||
impl->vertexCount = count;
|
||||
impl->vertexType = dataType;
|
||||
|
||||
if (count && dataType != gfx::AttributeDataType::Invalid && !data.empty()) {
|
||||
if (!vertexAttributes) {
|
||||
vertexAttributes = std::make_shared<gfx::VertexAttributeArray>();
|
||||
}
|
||||
if (auto& attrib = vertexAttributes->set(impl->vertexAttrId, /*index=*/-1, dataType)) {
|
||||
attrib->setRawData(std::move(data));
|
||||
attrib->setStride(gfx::VertexAttribute::getStrideOf(dataType));
|
||||
} else {
|
||||
using namespace std::string_literals;
|
||||
Log::Warning(Event::General,
|
||||
"Vertex attribute type mismatch: "s + name + " / " + util::toString(impl->vertexAttrId));
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const gfx::UniformBufferArray& Drawable::getUniformBuffers() const {
|
||||
return impl->uniformBuffers;
|
||||
}
|
||||
|
||||
gfx::UniformBufferArray& Drawable::mutableUniformBuffers() {
|
||||
return impl->uniformBuffers;
|
||||
}
|
||||
|
||||
void Drawable::setVertexAttrId(const size_t id) {
|
||||
impl->vertexAttrId = id;
|
||||
}
|
||||
|
||||
void Drawable::updateVertexAttributes(gfx::VertexAttributeArrayPtr vertices,
|
||||
std::size_t vertexCount,
|
||||
gfx::DrawMode mode,
|
||||
gfx::IndexVectorBasePtr indexes,
|
||||
const SegmentBase* segments,
|
||||
std::size_t segmentCount) {
|
||||
// Store the vertex attributes (base class method)
|
||||
gfx::Drawable::setVertexAttributes(std::move(vertices));
|
||||
|
||||
// Store vertex count
|
||||
impl->vertexCount = vertexCount;
|
||||
|
||||
// Handle segments - check for nullptr
|
||||
std::vector<UniqueDrawSegment> drawSegs;
|
||||
if (segments && segmentCount > 0) {
|
||||
drawSegs.reserve(segmentCount);
|
||||
for (std::size_t i = 0; i < segmentCount; ++i) {
|
||||
const auto& seg = segments[i];
|
||||
drawSegs.push_back(std::make_unique<DrawSegment>(
|
||||
mode, SegmentBase{seg.vertexOffset, seg.indexOffset, seg.vertexLength, seg.indexLength, seg.sortKey}));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the index data with segments
|
||||
setIndexData(std::move(indexes), std::move(drawSegs));
|
||||
}
|
||||
|
||||
} // namespace webgpu
|
||||
} // namespace mbgl
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user