diff --git a/CMakePresets.json b/CMakePresets.json index d55efb6e2..e47066494 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -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)", diff --git a/cmake/webgpu.cmake b/cmake/webgpu.cmake index 0b6bd0bde..04fcbac0f 100644 --- a/cmake/webgpu.cmake +++ b/cmake/webgpu.cmake @@ -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) diff --git a/include/mbgl/webgpu/offscreen_texture.hpp b/include/mbgl/webgpu/offscreen_texture.hpp index 04bfc95f2..5a824b974 100644 --- a/include/mbgl/webgpu/offscreen_texture.hpp +++ b/include/mbgl/webgpu/offscreen_texture.hpp @@ -16,6 +16,7 @@ public: bool isRenderable() override; PremultipliedImage readStillImage() override; const gfx::Texture2DPtr& getTexture() override; + gfx::Texture2DPtr takeTexture(); }; } // namespace webgpu diff --git a/include/mbgl/webgpu/renderer_backend.hpp b/include/mbgl/webgpu/renderer_backend.hpp index 062e09433..8dc5989c1 100644 --- a/include/mbgl/webgpu/renderer_backend.hpp +++ b/include/mbgl/webgpu/renderer_backend.hpp @@ -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 diff --git a/platform/default/include/mbgl/webgpu/headless_backend.hpp b/platform/default/include/mbgl/webgpu/headless_backend.hpp index 1a3774817..45ed5ae11 100644 --- a/platform/default/include/mbgl/webgpu/headless_backend.hpp +++ b/platform/default/include/mbgl/webgpu/headless_backend.hpp @@ -2,6 +2,7 @@ #include #include +#include "mbgl/gfx/offscreen_texture.hpp" #include 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; diff --git a/src/mbgl/style/sources/geojson_source.cpp b/src/mbgl/style/sources/geojson_source.cpp index 66dbf6b2f..68fb83c9d 100644 --- a/src/mbgl/style/sources/geojson_source.cpp +++ b/src/mbgl/style/sources/geojson_source.cpp @@ -30,6 +30,11 @@ const GeoJSONSource::Impl& GeoJSONSource::impl() const { return static_cast(*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_; diff --git a/src/mbgl/webgpu/headless_backend.cpp b/src/mbgl/webgpu/headless_backend.cpp index e81891f93..26ac0aef7 100644 --- a/src/mbgl/webgpu/headless_backend.cpp +++ b/src/mbgl/webgpu/headless_backend.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #if MLN_WEBGPU_IMPL_DAWN #include @@ -84,6 +85,8 @@ public: return PremultipliedImage(size); } + gfx::Texture2DPtr takeTexture() { return static_cast(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().takeTexture(); + } + return nullptr; +} + RendererBackend* HeadlessBackend::getRendererBackend() { return this; } diff --git a/src/mbgl/webgpu/offscreen_texture.cpp b/src/mbgl/webgpu/offscreen_texture.cpp index 1a4642cec..294fa11cf 100644 --- a/src/mbgl/webgpu/offscreen_texture.cpp +++ b/src/mbgl/webgpu/offscreen_texture.cpp @@ -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(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 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(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().getTexture(); } +gfx::Texture2DPtr OffscreenTexture::takeTexture() { + return getResource().takeTexture(); +} + } // namespace webgpu } // namespace mbgl diff --git a/src/mbgl/webgpu/render_pass.cpp b/src/mbgl/webgpu/render_pass.cpp index a261d437b..94a4e6aa3 100644 --- a/src/mbgl/webgpu/render_pass.cpp +++ b/src/mbgl/webgpu/render_pass.cpp @@ -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(commandEncoder.getContext().getBackend()); if (impl->colorFormatUpdated) { backend.setColorFormat(impl->previousColorFormat); diff --git a/src/mbgl/webgpu/renderer_backend.cpp b/src/mbgl/webgpu/renderer_backend.cpp index 6b526916f..99e993a29 100644 --- a/src/mbgl/webgpu/renderer_backend.cpp +++ b/src/mbgl/webgpu/renderer_backend.cpp @@ -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; } diff --git a/vendor/webgpu.cmake b/vendor/webgpu.cmake index dad2a525b..cf7d26e82 100644 --- a/vendor/webgpu.cmake +++ b/vendor/webgpu.cmake @@ -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() diff --git a/vendor/wgpu.cmake b/vendor/wgpu.cmake index 385b32b75..f63051f72 100644 --- a/vendor/wgpu.cmake +++ b/vendor/wgpu.cmake @@ -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,23 +83,25 @@ 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 -macro(mln_wgpu_find_library) - if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS") - mln_wgpu_find_exact_library(WGPU_LIBRARY "wgpu-native library") - else() - find_library(WGPU_LIBRARY - NAMES wgpu_native libwgpu_native - PATHS ${_wgpu_lib_search_paths} - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH - ) - endif() -endmacro() +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") + else() + find_library(WGPU_LIBRARY + NAMES wgpu_native libwgpu_native + PATHS ${_wgpu_lib_search_paths} + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + endif() + endmacro() -mln_wgpu_find_library() -mln_wgpu_find_exact_library(WGPU_STATIC_LIBRARY "wgpu-native static library") + 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,42 +285,45 @@ 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(APPLE) - target_link_libraries(mbgl-vendor-wgpu INTERFACE - "-framework Foundation" - "-framework QuartzCore" - "-framework Metal" - ) -elseif(WIN32) - target_link_libraries(mbgl-vendor-wgpu INTERFACE - d3d12 - dxgi - d3dcompiler - ws2_32 - userenv - bcrypt - ntdll - ) -elseif(ANDROID) - mln_wgpu_android_link_library(mbgl-vendor-wgpu) -else() # Linux - # wgpu-native on Linux might need these depending on the build configuration - find_package(Threads REQUIRED) - target_link_libraries(mbgl-vendor-wgpu INTERFACE - Threads::Threads - ${CMAKE_DL_LIBS} - ) +if (NOT MLN_WEBGPU_IMPL_FFI) + if(APPLE) + target_link_libraries(mbgl-vendor-wgpu INTERFACE + "-framework Foundation" + "-framework QuartzCore" + "-framework Metal" + ) + elseif(WIN32) + target_link_libraries(mbgl-vendor-wgpu INTERFACE + d3d12 + dxgi + d3dcompiler + ws2_32 + userenv + bcrypt + ntdll + ) + elseif(ANDROID) + mln_wgpu_android_link_library(mbgl-vendor-wgpu) + else() # Linux + # wgpu-native on Linux might need these depending on the build configuration + find_package(Threads REQUIRED) + target_link_libraries(mbgl-vendor-wgpu INTERFACE + Threads::Threads + ${CMAKE_DL_LIBS} + ) + endif() endif() # Suppress warnings from wgpu headers @@ -324,11 +334,22 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") ) endif() -set_target_properties( - mbgl-vendor-wgpu - PROPERTIES - INTERFACE_MAPLIBRE_NAME "wgpu-native" - INTERFACE_MAPLIBRE_URL "https://github.com/gfx-rs/wgpu-native" - INTERFACE_MAPLIBRE_AUTHOR "gfx-rs developers" - INTERFACE_MAPLIBRE_LICENSE "${PROJECT_SOURCE_DIR}/vendor/wgpu-native/LICENSE.MIT" -) +if (NOT MLN_WEBGPU_IMPL_FFI) + set_target_properties( + mbgl-vendor-wgpu + PROPERTIES + INTERFACE_MAPLIBRE_NAME "wgpu-native" + INTERFACE_MAPLIBRE_URL "https://github.com/gfx-rs/wgpu-native" + 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()