add possibility to link wgpu library from extern (#4272)

This commit is contained in:
Martin Marmsoler
2026-07-07 14:26:16 +02:00
committed by GitHub
parent 58d76e1617
commit 4cfc8a97f7
12 changed files with 169 additions and 75 deletions

View File

@@ -205,6 +205,18 @@
"MLN_WITH_X11": "ON" "MLN_WITH_X11": "ON"
} }
}, },
{
"name": "linux-webgpu-ffi",
"binaryDir": "${sourceDir}/build-linux-webgpu-wgpu-ffi",
"inherits": "linux",
"cacheVariables": {
"MLN_WITH_WEBGPU": "ON",
"MLN_WEBGPU_IMPL_WGPU": "ON",
"MLN_WEBGPU_IMPL_FFI": "ON",
"MLN_WITH_WAYLAND": "OFF",
"MLN_WITH_X11": "ON"
}
},
{ {
"name": "android-webgpu-dawn", "name": "android-webgpu-dawn",
"displayName": "Android WebGPU (Dawn)", "displayName": "Android WebGPU (Dawn)",

View File

@@ -115,7 +115,7 @@ if(MLN_WEBGPU_IMPL_DAWN)
if(TARGET mbgl-vendor-dawn) if(TARGET mbgl-vendor-dawn)
target_link_libraries(mbgl-core PRIVATE mbgl-vendor-dawn) target_link_libraries(mbgl-core PRIVATE mbgl-vendor-dawn)
endif() endif()
elseif(MLN_WEBGPU_IMPL_WGPU) elseif(MLN_WEBGPU_IMPL_WGPU OR MLN_WEBGPU_IMPL_FFI)
# Include wgpu-native integration # Include wgpu-native integration
include(${PROJECT_SOURCE_DIR}/vendor/wgpu.cmake) include(${PROJECT_SOURCE_DIR}/vendor/wgpu.cmake)
if(TARGET mbgl-vendor-wgpu) if(TARGET mbgl-vendor-wgpu)

View File

@@ -16,6 +16,7 @@ public:
bool isRenderable() override; bool isRenderable() override;
PremultipliedImage readStillImage() override; PremultipliedImage readStillImage() override;
const gfx::Texture2DPtr& getTexture() override; const gfx::Texture2DPtr& getTexture() override;
gfx::Texture2DPtr takeTexture();
}; };
} // namespace webgpu } // namespace webgpu

View File

@@ -38,11 +38,11 @@ public:
// WebGPU-specific methods // WebGPU-specific methods
void setInstance(void* instance); void setInstance(void* instance);
void setDevice(void* device); void setDevice(WGPUDevice device);
void setQueue(void* queue); void setQueue(WGPUQueue queue);
void* getInstance() const; void* getInstance() const;
void* getDevice() const; WGPUDevice getDevice() const;
void* getQueue() const; WGPUQueue getQueue() const;
void* getSurface() const; void* getSurface() const;
// Surface texture access - can be overridden by platform backends // Surface texture access - can be overridden by platform backends

View File

@@ -2,6 +2,7 @@
#include <mbgl/gfx/headless_backend.hpp> #include <mbgl/gfx/headless_backend.hpp>
#include <mbgl/webgpu/renderer_backend.hpp> #include <mbgl/webgpu/renderer_backend.hpp>
#include "mbgl/gfx/offscreen_texture.hpp"
#include <memory> #include <memory>
namespace mbgl { namespace mbgl {
@@ -17,6 +18,12 @@ public:
gfx::Renderable& getDefaultRenderable() override; gfx::Renderable& getDefaultRenderable() override;
PremultipliedImage readStillImage() override; PremultipliedImage readStillImage() override;
RendererBackend* getRendererBackend() override; RendererBackend* getRendererBackend() override;
/*!
* \brief takeTexture
* Take ownership of the current texture
* \return
*/
gfx::Texture2DPtr takeTexture();
// Override WebGPU-specific methods // Override WebGPU-specific methods
void* getCurrentTextureView() override; void* getCurrentTextureView() override;

View File

@@ -30,6 +30,11 @@ const GeoJSONSource::Impl& GeoJSONSource::impl() const {
return static_cast<const Impl&>(*baseImpl); return static_cast<const Impl&>(*baseImpl);
} }
/*!
* \brief GeoJSONSource::setURL
* Set the url to a geojson file which can be fetched
* \param url_
*/
void GeoJSONSource::setURL(const std::string& url_) { void GeoJSONSource::setURL(const std::string& url_) {
url = url_; url = url_;

View File

@@ -4,6 +4,7 @@
#include <mbgl/webgpu/offscreen_texture.hpp> #include <mbgl/webgpu/offscreen_texture.hpp>
#include <mbgl/util/logging.hpp> #include <mbgl/util/logging.hpp>
#include <mbgl/webgpu/wgpu_cpp_compat.hpp> #include <mbgl/webgpu/wgpu_cpp_compat.hpp>
#include <mbgl/webgpu/texture2d.hpp>
#if MLN_WEBGPU_IMPL_DAWN #if MLN_WEBGPU_IMPL_DAWN
#include <dawn/native/DawnNative.h> #include <dawn/native/DawnNative.h>
@@ -84,6 +85,8 @@ public:
return PremultipliedImage(size); return PremultipliedImage(size);
} }
gfx::Texture2DPtr takeTexture() { return static_cast<OffscreenTexture*>(offscreenTexture.get())->takeTexture(); }
private: private:
webgpu::Context& context; webgpu::Context& context;
Size size; Size size;
@@ -153,7 +156,7 @@ HeadlessBackend::HeadlessBackend(Size size_, SwapBehaviour swapBehaviour_, gfx::
setDevice(impl->device.Get()); setDevice(impl->device.Get());
setQueue(impl->queue.Get()); setQueue(impl->queue.Get());
setInstance(impl->instance->Get()); setInstance(impl->instance->Get());
#elif MLN_WEBGPU_IMPL_WGPU #elif MLN_WEBGPU_IMPL_WGPU && !MLN_WEBGPU_IMPL_FFI
// wgpu-native backend initialization // wgpu-native backend initialization
wgpu::InstanceDescriptor instanceDesc = {}; wgpu::InstanceDescriptor instanceDesc = {};
impl->instance = wgpu::createInstance(instanceDesc); impl->instance = wgpu::createInstance(instanceDesc);
@@ -332,6 +335,13 @@ PremultipliedImage HeadlessBackend::readStillImage() {
return PremultipliedImage(getSize()); return PremultipliedImage(getSize());
} }
gfx::Texture2DPtr HeadlessBackend::takeTexture() {
if (hasResource()) {
return getResource<HeadlessRenderableResource>().takeTexture();
}
return nullptr;
}
RendererBackend* HeadlessBackend::getRendererBackend() { RendererBackend* HeadlessBackend::getRendererBackend() {
return this; return this;
} }

View File

@@ -28,16 +28,7 @@ public:
assert(!size.isEmpty()); assert(!size.isEmpty());
// Create color texture // Create color texture
colorTexture = context.createTexture2D(); colorTexture = createColorTexture();
colorTexture->setSize(size);
colorTexture->setFormat(gfx::TexturePixelType::RGBA, type);
if (auto* webgpuTexture = static_cast<Texture2D*>(colorTexture.get())) {
webgpuTexture->setUsage(WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst |
WGPUTextureUsage_CopySrc | WGPUTextureUsage_RenderAttachment);
webgpuTexture->setSizeChangedCallback([this](const Size& newSize) { handleResize(newSize); });
}
colorTexture->setSamplerConfiguration(
{gfx::TextureFilterType::Linear, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
// Create depth/stencil texture if needed // Create depth/stencil texture if needed
// WebGPU requires a combined depth-stencil texture when both are needed // WebGPU requires a combined depth-stencil texture when both are needed
@@ -304,9 +295,33 @@ public:
return colorTexture; return colorTexture;
} }
gfx::Texture2DPtr takeTexture() {
if (!colorTexture) {
return nullptr;
}
auto taken = std::move(colorTexture);
colorTexture = createColorTexture();
return taken;
}
void setSizeListener(std::function<void(const Size&)> listener) { sizeListener = std::move(listener); } void setSizeListener(std::function<void(const Size&)> listener) { sizeListener = std::move(listener); }
private: private:
gfx::Texture2DPtr createColorTexture() {
auto texture = context.createTexture2D();
texture->setSize(size);
texture->setFormat(gfx::TexturePixelType::RGBA, type);
if (auto* webgpuTexture = static_cast<Texture2D*>(texture.get())) {
webgpuTexture->setUsage(WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst |
WGPUTextureUsage_CopySrc | WGPUTextureUsage_RenderAttachment);
webgpuTexture->setSizeChangedCallback([this](const Size& newSize) { handleResize(newSize); });
}
texture->setSamplerConfiguration(
{gfx::TextureFilterType::Linear, gfx::TextureWrapType::Clamp, gfx::TextureWrapType::Clamp});
return texture;
}
void handleResize(const Size& newSize) { void handleResize(const Size& newSize) {
if (newSize == size || newSize.isEmpty()) { if (newSize == size || newSize.isEmpty()) {
return; return;
@@ -360,5 +375,9 @@ const gfx::Texture2DPtr& OffscreenTexture::getTexture() {
return getResource<OffscreenTextureResource>().getTexture(); return getResource<OffscreenTextureResource>().getTexture();
} }
gfx::Texture2DPtr OffscreenTexture::takeTexture() {
return getResource<OffscreenTextureResource>().takeTexture();
}
} // namespace webgpu } // namespace webgpu
} // namespace mbgl } // namespace mbgl

View File

@@ -25,7 +25,9 @@ public:
WGPURenderPassEncoder encoder = nullptr; WGPURenderPassEncoder encoder = nullptr;
WGPUCommandEncoder commandEncoder = nullptr; WGPUCommandEncoder commandEncoder = nullptr;
const gfx::UniformBufferArray* globalUniformBuffers = nullptr; const gfx::UniformBufferArray* globalUniformBuffers = nullptr;
// Required here because it must life as long as the render pass
wgpu::TextureView colorView; wgpu::TextureView colorView;
// Required here because it must life as long as the render pass
wgpu::TextureView depthStencilView; wgpu::TextureView depthStencilView;
wgpu::TextureFormat previousColorFormat = wgpu::TextureFormat::Undefined; wgpu::TextureFormat previousColorFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureFormat previousDepthStencilFormat = wgpu::TextureFormat::Undefined; wgpu::TextureFormat previousDepthStencilFormat = wgpu::TextureFormat::Undefined;
@@ -183,6 +185,10 @@ RenderPass::RenderPass(CommandEncoder& commandEncoder_, const char* name, const
wgpuRenderPassEncoderSetScissorRect(impl->encoder, 0, 0, size.width, size.height); wgpuRenderPassEncoderSetScissorRect(impl->encoder, 0, 0, size.width, size.height);
} else { } else {
mbgl::Log::Error(mbgl::Event::Render, "WebGPU: Failed to begin render pass"); mbgl::Log::Error(mbgl::Event::Render, "WebGPU: Failed to begin render pass");
#if MLN_WEBGPU_IMPL_WGPU
wgpuTextureViewRelease(impl->colorView);
wgpuTextureViewRelease(impl->depthStencilView);
#endif
impl->colorView = nullptr; impl->colorView = nullptr;
impl->depthStencilView = nullptr; impl->depthStencilView = nullptr;
} }
@@ -199,6 +205,15 @@ RenderPass::~RenderPass() {
wgpuRenderPassEncoderRelease(impl->encoder); wgpuRenderPassEncoderRelease(impl->encoder);
} }
#if MLN_WEBGPU_IMPL_WGPU
if (impl->colorView) {
wgpuTextureViewRelease(impl->colorView);
}
if (impl->depthStencilView) {
wgpuTextureViewRelease(impl->depthStencilView);
}
#endif
auto& backend = static_cast<RendererBackend&>(commandEncoder.getContext().getBackend()); auto& backend = static_cast<RendererBackend&>(commandEncoder.getContext().getBackend());
if (impl->colorFormatUpdated) { if (impl->colorFormatUpdated) {
backend.setColorFormat(impl->previousColorFormat); backend.setColorFormat(impl->previousColorFormat);

View File

@@ -35,8 +35,8 @@ namespace webgpu {
class RendererBackend::Impl { class RendererBackend::Impl {
public: public:
void* instance = nullptr; void* instance = nullptr;
void* device = nullptr; WGPUDevice device = nullptr;
void* queue = nullptr; WGPUQueue queue = nullptr;
void* surface = nullptr; void* surface = nullptr;
wgpu::TextureFormat depthStencilFormat = wgpu::TextureFormat::Undefined; wgpu::TextureFormat depthStencilFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureFormat colorFormat = wgpu::TextureFormat::Undefined; wgpu::TextureFormat colorFormat = wgpu::TextureFormat::Undefined;
@@ -134,11 +134,11 @@ void RendererBackend::setInstance(void* instance) {
impl->instance = instance; impl->instance = instance;
} }
void RendererBackend::setDevice(void* device) { void RendererBackend::setDevice(WGPUDevice device) {
impl->device = device; impl->device = device;
} }
void RendererBackend::setQueue(void* queue) { void RendererBackend::setQueue(WGPUQueue queue) {
impl->queue = queue; impl->queue = queue;
} }
@@ -146,11 +146,11 @@ void* RendererBackend::getInstance() const {
return impl->instance; return impl->instance;
} }
void* RendererBackend::getDevice() const { WGPUDevice RendererBackend::getDevice() const {
return impl->device; return impl->device;
} }
void* RendererBackend::getQueue() const { WGPUQueue RendererBackend::getQueue() const {
return impl->queue; return impl->queue;
} }

8
vendor/webgpu.cmake vendored
View File

@@ -3,6 +3,8 @@
set(MLN_WEBGPU_IMPL_DAWN OFF CACHE STRING "WebGPU with Dawn implementation") set(MLN_WEBGPU_IMPL_DAWN OFF CACHE STRING "WebGPU with Dawn implementation")
set(MLN_WEBGPU_IMPL_WGPU OFF CACHE STRING "WebGPU with wgpu implementation") set(MLN_WEBGPU_IMPL_WGPU OFF CACHE STRING "WebGPU with wgpu implementation")
set(MLN_WEBGPU_IMPL_FFI OFF CACHE STRING "WebGPU with ffi interface")
set(MLN_WEBGPU_IMPL_WEBGPU_HEADER_DIR "" CACHE STRING "If not empty use this path to the webgpu.h file")
if(MLN_WITH_WEBGPU) if(MLN_WITH_WEBGPU)
if(MLN_WEBGPU_IMPL_DAWN) if(MLN_WEBGPU_IMPL_DAWN)
@@ -12,9 +14,11 @@ if(MLN_WITH_WEBGPU)
elseif(MLN_WEBGPU_IMPL_WGPU) elseif(MLN_WEBGPU_IMPL_WGPU)
message(STATUS "Using wgpu as WebGPU backend") message(STATUS "Using wgpu as WebGPU backend")
add_compile_definitions(MLN_WEBGPU_IMPL_WGPU) add_compile_definitions(MLN_WEBGPU_IMPL_WGPU)
else() else()
message(FATAL_ERROR "MLN_WEBGPU_IMPL_DAWN or MLN_WEBGPU_IMPL_WGPU must be set") message(FATAL_ERROR "MLN_WEBGPU_IMPL_DAWN or MLN_WEBGPU_IMPL_WGPU must be set")
endif() endif()
if (MLN_WEBGPU_IMPL_FFI)
add_compile_definitions(MLN_WEBGPU_IMPL_FFI)
endif()
endif() endif()

123
vendor/wgpu.cmake vendored
View File

@@ -11,9 +11,14 @@ endif()
message(STATUS "Configuring wgpu-native WebGPU implementation") message(STATUS "Configuring wgpu-native WebGPU implementation")
set(_mln_wgpu_source_dir "${PROJECT_SOURCE_DIR}/vendor/wgpu-native") set(_mln_wgpu_source_dir "${PROJECT_SOURCE_DIR}/vendor/wgpu-native")
if ("${MLN_WEBGPU_IMPL_FFI_BUNDLED_WEBGPU_HEADER}" STREQUAL "")
set(WEBGPU_H_DIR ${_mln_wgpu_source_dir}/ffi/webgpu-headers)
else()
set(WEBGPU_H_DIR ${MLN_WEBGPU_IMPL_WEBGPU_HEADER_DIR})
endif()
# Check if wgpu-native exists # Check if wgpu-native exists
if(NOT EXISTS "${_mln_wgpu_source_dir}/ffi/webgpu-headers/webgpu.h") if(NOT EXISTS "${WEBGPU_H_DIR}/webgpu.h")
message(FATAL_ERROR message(FATAL_ERROR
"wgpu-native not found at ${_mln_wgpu_source_dir}. " "wgpu-native not found at ${_mln_wgpu_source_dir}. "
"Please ensure the wgpu-native submodule is initialized: " "Please ensure the wgpu-native submodule is initialized: "
@@ -78,23 +83,25 @@ macro(mln_wgpu_find_exact_library out_var out_description)
endmacro() endmacro()
# On Android/iOS, use manual path search since find_library may not work with cross-compilation toolchains # On Android/iOS, use manual path search since find_library may not work with cross-compilation toolchains
macro(mln_wgpu_find_library) if (NOT MLN_WEBGPU_IMPL_FFI)
if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS") macro(mln_wgpu_find_library)
mln_wgpu_find_exact_library(WGPU_LIBRARY "wgpu-native library") if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
else() mln_wgpu_find_exact_library(WGPU_LIBRARY "wgpu-native library")
find_library(WGPU_LIBRARY else()
NAMES wgpu_native libwgpu_native find_library(WGPU_LIBRARY
PATHS ${_wgpu_lib_search_paths} NAMES wgpu_native libwgpu_native
NO_DEFAULT_PATH PATHS ${_wgpu_lib_search_paths}
NO_CMAKE_FIND_ROOT_PATH NO_DEFAULT_PATH
) NO_CMAKE_FIND_ROOT_PATH
endif() )
endmacro() endif()
endmacro()
mln_wgpu_find_library() mln_wgpu_find_library()
mln_wgpu_find_exact_library(WGPU_STATIC_LIBRARY "wgpu-native static library") mln_wgpu_find_exact_library(WGPU_STATIC_LIBRARY "wgpu-native static library")
endif()
if(NOT WGPU_LIBRARY OR MLN_WGPU_NATIVE_VERSION) if(NOT MLN_WEBGPU_IMPL_FFI AND (NOT WGPU_LIBRARY OR MLN_WGPU_NATIVE_VERSION))
# Use a specific version of WGPU. This is required when to a rust application # Use a specific version of WGPU. This is required when to a rust application
# which uses wgpu directly instead of wgpu-native # which uses wgpu directly instead of wgpu-native
if (MLN_WGPU_NATIVE_VERSION) if (MLN_WGPU_NATIVE_VERSION)
@@ -204,7 +211,7 @@ if(NOT EXISTS "${_webgpu_cpp_header}")
execute_process( execute_process(
COMMAND ${PYTHON_EXECUTABLE} generate.py COMMAND ${PYTHON_EXECUTABLE} generate.py
--use-init-macros --use-init-macros
--header ${_mln_wgpu_source_dir}/ffi/webgpu-headers/webgpu.h --header ${WEBGPU_H_DIR}/webgpu.h
--output ${_webgpu_cpp_header} --output ${_webgpu_cpp_header}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/vendor/webgpu-cpp" WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/vendor/webgpu-cpp"
RESULT_VARIABLE _gen_result RESULT_VARIABLE _gen_result
@@ -278,42 +285,45 @@ endif()
# Create interface library # Create interface library
add_library(mbgl-vendor-wgpu INTERFACE) add_library(mbgl-vendor-wgpu INTERFACE)
# For the ffi we don't link the wgpu library, because we link in the final stage with the rust wgpu version
target_link_libraries(mbgl-vendor-wgpu INTERFACE ${WGPU_LIBRARY}) target_link_libraries(mbgl-vendor-wgpu INTERFACE ${WGPU_LIBRARY})
# Add include directories for webgpu.h, wgpu.h, and C++ wrapper # Add include directories for webgpu.h, wgpu.h, and C++ wrapper
target_include_directories(mbgl-vendor-wgpu target_include_directories(mbgl-vendor-wgpu
SYSTEM INTERFACE SYSTEM INTERFACE
${_webgpu-cpp-output_dir} # For webgpu.hpp webgpu/webgpu_cpp.h compatibility shim ${_webgpu-cpp-output_dir} # For webgpu.hpp webgpu/webgpu_cpp.h compatibility shim
${_mln_wgpu_source_dir}/ffi/webgpu-headers # webgpu.h ${WEBGPU_H_DIR}
${_mln_wgpu_source_dir}/ffi # wgpu.h ${_mln_wgpu_source_dir}/ffi # wgpu.h
) )
# Platform-specific system libraries that wgpu-native needs # Platform-specific system libraries that wgpu-native needs
if(APPLE) if (NOT MLN_WEBGPU_IMPL_FFI)
target_link_libraries(mbgl-vendor-wgpu INTERFACE if(APPLE)
"-framework Foundation" target_link_libraries(mbgl-vendor-wgpu INTERFACE
"-framework QuartzCore" "-framework Foundation"
"-framework Metal" "-framework QuartzCore"
) "-framework Metal"
elseif(WIN32) )
target_link_libraries(mbgl-vendor-wgpu INTERFACE elseif(WIN32)
d3d12 target_link_libraries(mbgl-vendor-wgpu INTERFACE
dxgi d3d12
d3dcompiler dxgi
ws2_32 d3dcompiler
userenv ws2_32
bcrypt userenv
ntdll bcrypt
) ntdll
elseif(ANDROID) )
mln_wgpu_android_link_library(mbgl-vendor-wgpu) elseif(ANDROID)
else() # Linux mln_wgpu_android_link_library(mbgl-vendor-wgpu)
# wgpu-native on Linux might need these depending on the build configuration else() # Linux
find_package(Threads REQUIRED) # wgpu-native on Linux might need these depending on the build configuration
target_link_libraries(mbgl-vendor-wgpu INTERFACE find_package(Threads REQUIRED)
Threads::Threads target_link_libraries(mbgl-vendor-wgpu INTERFACE
${CMAKE_DL_LIBS} Threads::Threads
) ${CMAKE_DL_LIBS}
)
endif()
endif() endif()
# Suppress warnings from wgpu headers # Suppress warnings from wgpu headers
@@ -324,11 +334,22 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
) )
endif() endif()
set_target_properties( if (NOT MLN_WEBGPU_IMPL_FFI)
mbgl-vendor-wgpu set_target_properties(
PROPERTIES mbgl-vendor-wgpu
INTERFACE_MAPLIBRE_NAME "wgpu-native" PROPERTIES
INTERFACE_MAPLIBRE_URL "https://github.com/gfx-rs/wgpu-native" INTERFACE_MAPLIBRE_NAME "wgpu-native"
INTERFACE_MAPLIBRE_AUTHOR "gfx-rs developers" INTERFACE_MAPLIBRE_URL "https://github.com/gfx-rs/wgpu-native"
INTERFACE_MAPLIBRE_LICENSE "${PROJECT_SOURCE_DIR}/vendor/wgpu-native/LICENSE.MIT" INTERFACE_MAPLIBRE_AUTHOR "gfx-rs developers"
) INTERFACE_MAPLIBRE_LICENSE "${PROJECT_SOURCE_DIR}/vendor/wgpu-native/LICENSE.MIT"
)
else()
set_target_properties(
mbgl-vendor-wgpu
PROPERTIES
INTERFACE_MAPLIBRE_NAME "wgpu-ffi"
INTERFACE_MAPLIBRE_URL "https://github.com/maplibre/maplibre-native"
INTERFACE_MAPLIBRE_AUTHOR "Maplibre Contributors"
INTERFACE_MAPLIBRE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.md"
)
endif()