1010 * appropriate vulkan structures.
1111 */
1212
13+ #include < executorch/backends/arm/runtime/VGFCommandStream.h>
1314#include < executorch/backends/arm/runtime/VGFExecutionStats.h>
1415#include < executorch/backends/arm/runtime/VGFMutableAlias.h>
1516#include < executorch/backends/arm/runtime/VGFSetup.h>
@@ -5241,6 +5242,30 @@ bool VgfRepr::record_execute_command_buffer(VkCommandBuffer command_buffer) {
52415242 return true ;
52425243}
52435244
5245+ // VGF_FUSED_MAIN_BEGIN (opt-in real producer/consumer commands)
5246+ namespace {
5247+ thread_local VgfFusedCommandStreamRequest* active_vgf_fused_stream = nullptr ;
5248+ } // namespace
5249+
5250+ // cppcheck-suppress unusedFunction
5251+ bool vgf_try_register_fused_command_stream (
5252+ VgfFusedCommandStreamRequest* request) noexcept {
5253+ if (request == nullptr || active_vgf_fused_stream != nullptr ||
5254+ request->attempted || request->submitted || request->completed ) {
5255+ return false ;
5256+ }
5257+ active_vgf_fused_stream = request;
5258+ return true ;
5259+ }
5260+
5261+ // cppcheck-suppress unusedFunction
5262+ void vgf_unregister_fused_command_stream (
5263+ const VgfFusedCommandStreamRequest* request) noexcept {
5264+ if (active_vgf_fused_stream == request) {
5265+ active_vgf_fused_stream = nullptr ;
5266+ }
5267+ }
5268+
52445269bool VgfRepr::execute_vgf (executorch::runtime::EventTracer* event_tracer) {
52455270 if (!execution_valid_) {
52465271 ET_LOG (
@@ -5256,14 +5281,84 @@ bool VgfRepr::execute_vgf(executorch::runtime::EventTracer* event_tracer) {
52565281 return false ;
52575282 }
52585283
5284+ // Optional application command stream. The callback sees this VGF
5285+ // representation's actual VkDevice and family so it can record its own
5286+ // commands even when the generic shared-Vulkan-context bridge is absent.
5287+ // It is called under VGFBackend's mutex, and MUST NOT re-enter ExecuTorch.
5288+ VgfFusedCommandStreamRequest* fused = active_vgf_fused_stream;
5289+ std::array<VkCommandBuffer, 3 > fused_commands{};
5290+ if (fused != nullptr ) {
5291+ if (fused->attempted || fused->submitted || fused->completed ||
5292+ (fused->expected_device != VK_NULL_HANDLE &&
5293+ fused->expected_device != vk_device) ||
5294+ (fused->expected_queue_family_index != UINT32_MAX &&
5295+ fused->expected_queue_family_index != vk_queue_family_index) ||
5296+ (fused->prepare == nullptr &&
5297+ (fused->expected_device == VK_NULL_HANDLE ||
5298+ fused->expected_queue_family_index == UINT32_MAX ))) {
5299+ ET_LOG (Error, " Invalid or reused VGF fused command-stream request" );
5300+ return false ;
5301+ }
5302+ // Fail closed even if the main-branch queue picker has moved into another
5303+ // translation unit and could not be changed by the installer.
5304+ // This check is active only when the strict graphics queue mode is set.
5305+ const char * graphics_required =
5306+ std::getenv (" EXECUTORCH_VGF_FUSED_REQUIRE_GRAPHICS_QUEUE" );
5307+ if (graphics_required != nullptr && graphics_required[0 ] == ' 1' &&
5308+ graphics_required[1 ] == ' \0 ' ) {
5309+ uint32_t family_count = 0 ;
5310+ vkGetPhysicalDeviceQueueFamilyProperties (
5311+ vk_physical, &family_count, nullptr );
5312+ if (vk_queue_family_index >= family_count) {
5313+ ET_LOG (Error, " Invalid VGF fused graphics queue family index" );
5314+ return false ;
5315+ }
5316+ std::vector<VkQueueFamilyProperties> families (family_count);
5317+ vkGetPhysicalDeviceQueueFamilyProperties (
5318+ vk_physical, &family_count, families.data ());
5319+ constexpr VkQueueFlags required = VK_QUEUE_GRAPHICS_BIT |
5320+ VK_QUEUE_COMPUTE_BIT | VK_QUEUE_DATA_GRAPH_BIT_ARM ;
5321+ if ((families[vk_queue_family_index].queueFlags & required) != required) {
5322+ ET_LOG (
5323+ Error,
5324+ " VGF fused stream needs graphics+compute+data graph queue; "
5325+ " selected family %u cannot run the requested commands" ,
5326+ vk_queue_family_index);
5327+ return false ;
5328+ }
5329+ }
5330+ fused->attempted = true ;
5331+ if (fused->prepare != nullptr ) {
5332+ const VgfFusedDeviceContext context{
5333+ vk_physical, vk_device, vk_queue_family_index};
5334+ if (!fused->prepare (
5335+ fused->user_data , context, &fused->producer , &fused->consumer )) {
5336+ ET_LOG (Error, " VGF fused command recording callback failed" );
5337+ return false ;
5338+ }
5339+ }
5340+ if (fused->producer == VK_NULL_HANDLE ||
5341+ fused->consumer == VK_NULL_HANDLE ||
5342+ fused->producer == fused->consumer ||
5343+ fused->producer == vk_execute_cmd ||
5344+ fused->consumer == vk_execute_cmd) {
5345+ ET_LOG (Error, " VGF fused stream requires two distinct primary commands" );
5346+ return false ;
5347+ }
5348+ // The caller guarantees both commands were recorded on vk_device for
5349+ // vk_queue_family_index, and supplies Vulkan memory/layout barriers.
5350+ fused_commands = {fused->producer , vk_execute_cmd, fused->consumer };
5351+ }
5352+
52595353 VkSubmitInfo submit{
52605354 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO ,
52615355 .pNext = nullptr ,
52625356 .waitSemaphoreCount = 0 ,
52635357 .pWaitSemaphores = nullptr ,
52645358 .pWaitDstStageMask = nullptr ,
5265- .commandBufferCount = 1 ,
5266- .pCommandBuffers = &vk_execute_cmd,
5359+ .commandBufferCount = fused != nullptr ? 3u : 1u ,
5360+ .pCommandBuffers =
5361+ fused != nullptr ? fused_commands.data () : &vk_execute_cmd,
52675362 .signalSemaphoreCount = 0 ,
52685363 .pSignalSemaphores = nullptr ,
52695364 };
@@ -5287,6 +5382,10 @@ bool VgfRepr::execute_vgf(executorch::runtime::EventTracer* event_tracer) {
52875382 // Submit + fence wait only; reset is above.
52885383 VGF_STATS_TIME (submit_wait_ns);
52895384 result = vkQueueSubmit (vk_queue, 1 , &submit, vk_execute_fence);
5385+ if (fused != nullptr ) {
5386+ fused->submit_result = result;
5387+ fused->submitted = result == VK_SUCCESS ;
5388+ }
52905389 if (result != VK_SUCCESS ) {
52915390 ET_LOG (Error, " VGF/VkFence wait failed, error %d" , result);
52925391 return false ;
@@ -5299,6 +5398,10 @@ bool VgfRepr::execute_vgf(executorch::runtime::EventTracer* event_tracer) {
52995398 &vk_execute_fence,
53005399 VK_TRUE ,
53015400 std::numeric_limits<uint64_t >::max ());
5401+ if (fused != nullptr ) {
5402+ fused->wait_result = result;
5403+ fused->completed = result == VK_SUCCESS ;
5404+ }
53025405 if (result == VK_SUCCESS ) {
53035406 execute_submission_pending_ = false ;
53045407 }
0 commit comments