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"
}
},
{
"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",
"displayName": "Android WebGPU (Dawn)",

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,11 @@ const GeoJSONSource::Impl& GeoJSONSource::impl() const {
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_) {
url = url_;

View File

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

View File

@@ -28,16 +28,7 @@ public:
assert(!size.isEmpty());
// Create color texture
colorTexture = context.createTexture2D();
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});
colorTexture = createColorTexture();
// Create depth/stencil texture if needed
// WebGPU requires a combined depth-stencil texture when both are needed
@@ -304,9 +295,33 @@ public:
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); }
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) {
if (newSize == size || newSize.isEmpty()) {
return;
@@ -360,5 +375,9 @@ const gfx::Texture2DPtr& OffscreenTexture::getTexture() {
return getResource<OffscreenTextureResource>().getTexture();
}
gfx::Texture2DPtr OffscreenTexture::takeTexture() {
return getResource<OffscreenTextureResource>().takeTexture();
}
} // namespace webgpu
} // namespace mbgl

View File

@@ -25,7 +25,9 @@ public:
WGPURenderPassEncoder encoder = nullptr;
WGPUCommandEncoder commandEncoder = nullptr;
const gfx::UniformBufferArray* globalUniformBuffers = nullptr;
// Required here because it must life as long as the render pass
wgpu::TextureView colorView;
// Required here because it must life as long as the render pass
wgpu::TextureView depthStencilView;
wgpu::TextureFormat previousColorFormat = 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);
} else {
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->depthStencilView = nullptr;
}
@@ -199,6 +205,15 @@ RenderPass::~RenderPass() {
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());
if (impl->colorFormatUpdated) {
backend.setColorFormat(impl->previousColorFormat);

View File

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

6
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_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_WEBGPU_IMPL_DAWN)
@@ -12,9 +14,11 @@ if(MLN_WITH_WEBGPU)
elseif(MLN_WEBGPU_IMPL_WGPU)
message(STATUS "Using wgpu as WebGPU backend")
add_compile_definitions(MLN_WEBGPU_IMPL_WGPU)
else()
message(FATAL_ERROR "MLN_WEBGPU_IMPL_DAWN or MLN_WEBGPU_IMPL_WGPU must be set")
endif()
if (MLN_WEBGPU_IMPL_FFI)
add_compile_definitions(MLN_WEBGPU_IMPL_FFI)
endif()
endif()

29
vendor/wgpu.cmake vendored
View File

@@ -11,9 +11,14 @@ endif()
message(STATUS "Configuring wgpu-native WebGPU implementation")
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
if(NOT EXISTS "${_mln_wgpu_source_dir}/ffi/webgpu-headers/webgpu.h")
if(NOT EXISTS "${WEBGPU_H_DIR}/webgpu.h")
message(FATAL_ERROR
"wgpu-native not found at ${_mln_wgpu_source_dir}. "
"Please ensure the wgpu-native submodule is initialized: "
@@ -78,6 +83,7 @@ macro(mln_wgpu_find_exact_library out_var out_description)
endmacro()
# On Android/iOS, use manual path search since find_library may not work with cross-compilation toolchains
if (NOT MLN_WEBGPU_IMPL_FFI)
macro(mln_wgpu_find_library)
if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
mln_wgpu_find_exact_library(WGPU_LIBRARY "wgpu-native library")
@@ -93,8 +99,9 @@ endmacro()
mln_wgpu_find_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
# which uses wgpu directly instead of wgpu-native
if (MLN_WGPU_NATIVE_VERSION)
@@ -204,7 +211,7 @@ if(NOT EXISTS "${_webgpu_cpp_header}")
execute_process(
COMMAND ${PYTHON_EXECUTABLE} generate.py
--use-init-macros
--header ${_mln_wgpu_source_dir}/ffi/webgpu-headers/webgpu.h
--header ${WEBGPU_H_DIR}/webgpu.h
--output ${_webgpu_cpp_header}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/vendor/webgpu-cpp"
RESULT_VARIABLE _gen_result
@@ -278,17 +285,19 @@ endif()
# Create interface library
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})
# Add include directories for webgpu.h, wgpu.h, and C++ wrapper
target_include_directories(mbgl-vendor-wgpu
SYSTEM INTERFACE
${_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
)
# Platform-specific system libraries that wgpu-native needs
if (NOT MLN_WEBGPU_IMPL_FFI)
if(APPLE)
target_link_libraries(mbgl-vendor-wgpu INTERFACE
"-framework Foundation"
@@ -315,6 +324,7 @@ else() # Linux
${CMAKE_DL_LIBS}
)
endif()
endif()
# Suppress warnings from wgpu headers
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
@@ -324,6 +334,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
)
endif()
if (NOT MLN_WEBGPU_IMPL_FFI)
set_target_properties(
mbgl-vendor-wgpu
PROPERTIES
@@ -332,3 +343,13 @@ set_target_properties(
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()