processors/vulkan_gpu_probe.cpp¶
Namespaces¶
| Name |
|---|
| sgns |
| sgns::sgprocessing Artifact and manifest binary serialization. |
Functions¶
| Name | |
|---|---|
| bool | HasUsableVulkanDevice() |
| bool | HasUsableVulkanDeviceCached() |
Functions Documentation¶
function HasUsableVulkanDevice¶
Runtime probe answering "does this host have at least one usable Vulkan device?", mirroring RenderProcessor::IsAcceptable's DISCRETE_GPU/ INTEGRATED_GPU device-type filter (D-32). Builds and immediately tears down its own throwaway VkInstance – it never creates a VkDevice and never touches RenderProcessor's own Vulkan state.
Callers MUST treat a false return as "skip GPU-dependent work" (e.g. via GTEST_SKIP()), never as a hard error – a GPU-less host is an expected, valid environment (D-34), not a failure condition.
Never throws.
function HasUsableVulkanDeviceCached¶
Process-lifetime cached variant of HasUsableVulkanDevice().
The uncached probe builds and destroys a whole VkInstance plus a full physical-device enumeration per call – far too heavy for per-session or per-chunk MNN backend selection (MNN_Volume creates a session per chunk). The device set does not change over a process's lifetime in any environment we care about, so the first caller's result is cached in a function-local static (thread-safe initialization guaranteed by C++11) and every later caller gets a plain bool read.
Intended use: MNN processors select MNN_FORWARD_VULKAN only when this returns true; on software-Vulkan-only hosts (llvmpipe/lavapipe in GPU-less CI containers) they select MNN_FORWARD_CPU instead – the native CPU backend is dramatically faster than Vulkan-on-lavapipe and restores the pre-WHOLEARCHIVE behavior these hosts always had (MNN's empty creator map silently CPU-fell-back then). Render passes keep their existing GTEST_SKIP policy instead.
Never throws.
Source code¶
#include "processors/vulkan_gpu_probe.hpp"
#include "processors/processing_processor_render.hpp"
#include "processingbase/vulkan_init_guard.hpp"
#include <VkBootstrap.h>
#include <algorithm>
#include <mutex>
#include <spdlog/spdlog.h>
namespace sgns::sgprocessing
{
bool HasUsableVulkanDevice()
{
try
{
std::lock_guard<std::mutex> lock( sgns::sgprocessing::VulkanInitMutex() );
// On macOS MoltenVK is statically linked (libMoltenVK.a), so there is
// no libvulkan.dylib for vk-bootstrap's default dlopen path to find.
// Pass the statically-available vkGetInstanceProcAddr directly to
// bypass dynamic loading entirely.
#if defined(__APPLE__)
vkb::InstanceBuilder instance_builder( vkGetInstanceProcAddr );
#else
vkb::InstanceBuilder instance_builder;
#endif
auto inst_ret = instance_builder.set_app_name( "SGProcessingManager GPU Probe" )
.set_app_version( 1, 0, 0 )
.request_validation_layers( false )
.build();
if ( !inst_ret )
{
// No instance was created -- nothing to destroy.
return false;
}
auto vkb_instance = inst_ret.value();
vkb::PhysicalDeviceSelector selector( vkb_instance );
// Headless/offscreen probe -- no VkSurfaceKHR ever exists, same rationale
// as RenderProcessor::InitializeContext()'s own require_present(false).
selector.require_present( false );
auto devices_ret = selector.select_devices();
if ( !devices_ret )
{
vkb::destroy_instance( vkb_instance );
return false;
}
auto devices = devices_ret.value();
devices.erase( std::remove_if( devices.begin(),
devices.end(),
[]( const vkb::PhysicalDevice &d )
{ return !RenderProcessor::IsAcceptable( d.properties.deviceType ); } ),
devices.end() );
bool usable = !devices.empty();
vkb::destroy_instance( vkb_instance );
return usable;
}
catch ( ... )
{
// Never throw -- a probe failure of any kind means "no usable device".
return false;
}
}
bool HasUsableVulkanDeviceCached()
{
// Function-local static: C++11 guarantees thread-safe one-shot
// initialization, so concurrent MNN session creations race through
// the probe exactly once.
static const bool usable = []()
{
const bool hasDevice = HasUsableVulkanDevice();
if ( !hasDevice )
{
spdlog::info( "[SGProcessingManager] No usable Vulkan GPU (DISCRETE_GPU/INTEGRATED_GPU) "
"present; MNN inference sessions fall back to the CPU backend "
"(software Vulkan / llvmpipe is deliberately not used)" );
}
return hasDevice;
}();
return usable;
}
}
Updated on 2026-09-17 at 06:29:15 +0000