Elevated global-priority encode queue — PUNKTFUNK LOCAL PATCH. Not upstream. PyroWave's wavelet encode runs on the GPU's compute/shader cores, so a GPU-bound game starves it: `pyrowave_encoder_encode_gpu_synchronous` spikes from ~2 ms to ~15 ms under a 95%+ game load and the stream framerate collapses (NVENC is immune — it uses the separate encoder ASIC). A WDDM *process* scheduling-priority raise does not help (it orders packet submission, not hardware preemption). This requests a global-priority Vulkan *queue* — the actual NVIDIA compute- preemption lever — on the pyrowave encode device. `Context::create_device` (the pyrowave path uses plain vkCreateDevice, not the vpCreateDevice profile path): enable VK_KHR_global_priority (or the EXT alias), chain a VkDeviceQueueGlobalPriorityCreateInfoKHR into every queue-create-info, and a create loop that tries `PYROWAVE_QUEUE_PRIORITY` (off|high|realtime, default realtime) → HIGH → no-priority, downgrading on VK_ERROR_NOT_PERMITTED_KHR so a refused class NEVER regresses the encoder. Gated on !inherit_info (only the fresh encoder device). This Granite copy is vendored solely for the PyroWave codec. NOTE: on an RTX 4090 / Windows / WDDM this did not reduce the spikes (the graphics-vs-compute preemption granularity is the wall) — kept because it is correct, harmless (graceful fallback), and may help other GPUs/drivers. Reduce the encode's GPU cost (4:2:0/8-bit) or use H.265 for a GPU-saturated game. diff --git a/crates/pyrowave-sys/vendor/pyrowave/Granite/vulkan/context.cpp b/crates/pyrowave-sys/vendor/pyrowave/Granite/vulkan/context.cpp index 5257fc33..479eeded 100644 --- a/crates/pyrowave-sys/vendor/pyrowave/Granite/vulkan/context.cpp +++ b/crates/pyrowave-sys/vendor/pyrowave/Granite/vulkan/context.cpp @@ -2115,6 +2115,51 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface, vpGetProfileProperties(profile.profile, &props); #endif + // PUNKTFUNK PATCH (patches/0005-global-priority-queue.patch, NOT upstream): request a high + // global-priority queue so PyroWave's compute-shader encode can PREEMPT a GPU-bound game on + // the shared shader cores. A WDDM process-priority raise does not help (it only orders packet + // submission, not hardware preemption); a global-priority queue is the actual NVIDIA compute- + // preemption lever. `PYROWAVE_QUEUE_PRIORITY` = off | high | realtime (default realtime). The + // device-create loop below downgrades on NOT_PERMITTED, so a refused class NEVER fails the + // encoder — it just runs at default priority. Only the fresh encoder device (no inherit_info) + // is affected; this Granite copy is vendored solely for the PyroWave codec. + Util::SmallVector pf_priority_candidates; + Util::SmallVector pf_global_priority_infos; + if (!inherit_info) + { + const char *pf_gp_ext = nullptr; + if (has_extension(VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME)) + pf_gp_ext = VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME; + else if (has_extension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME)) + pf_gp_ext = VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME; + std::string pf_prio; + if (!Util::get_environment("PYROWAVE_QUEUE_PRIORITY", pf_prio)) + pf_prio = "realtime"; + for (auto &pf_ch : pf_prio) + if (pf_ch >= 'A' && pf_ch <= 'Z') + pf_ch = char(pf_ch + 32); + if (pf_gp_ext && pf_prio != "off") + { + if (pf_prio == "high") + { + pf_priority_candidates.push_back(VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR); + } + else + { + pf_priority_candidates.push_back(VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR); + pf_priority_candidates.push_back(VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR); + } + enabled_extensions.push_back(pf_gp_ext); + pf_global_priority_infos.resize(queue_infos.size()); + for (size_t pf_i = 0; pf_i < queue_infos.size(); pf_i++) + { + pf_global_priority_infos[pf_i] = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR }; + pf_global_priority_infos[pf_i].globalPriority = pf_priority_candidates.front(); + queue_infos[pf_i].pNext = &pf_global_priority_infos[pf_i]; + } + } + } + if (inherit_info) { device_info.enabledExtensionCount = inherit_info->enabledExtensionCount; @@ -2144,8 +2189,41 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface, if (device == VK_NULL_HANDLE) return false; } - else if (vkCreateDevice(gpu, &device_info, nullptr, &device) != VK_SUCCESS) - return false; + else + { + // PUNKTFUNK: try the requested global-priority class, downgrade through the + // candidate list on NOT_PERMITTED, then finally create with no global priority. + VkResult pf_res; + if (pf_priority_candidates.empty()) + { + pf_res = vkCreateDevice(gpu, &device_info, nullptr, &device); + } + else + { + pf_res = VK_ERROR_NOT_PERMITTED_KHR; + for (size_t pf_a = 0; pf_a < pf_priority_candidates.size(); pf_a++) + { + for (auto &pf_gp : pf_global_priority_infos) + pf_gp.globalPriority = pf_priority_candidates[pf_a]; + pf_res = vkCreateDevice(gpu, &device_info, nullptr, &device); + if (pf_res != VK_ERROR_NOT_PERMITTED_KHR) + break; + LOGW("PyroWave: global queue priority %u not permitted; downgrading.\n", + unsigned(pf_priority_candidates[pf_a])); + } + if (pf_res == VK_ERROR_NOT_PERMITTED_KHR) + { + for (auto &pf_qi : queue_infos) + pf_qi.pNext = nullptr; + pf_res = vkCreateDevice(gpu, &device_info, nullptr, &device); + LOGW("PyroWave: all global queue priorities refused; default priority.\n"); + } + else + LOGI("PyroWave: encode device created with an elevated global queue priority.\n"); + } + if (pf_res != VK_SUCCESS) + return false; + } } if (inherit_info)