/* * Copyright © 2026 mfritsche / claude-noether * SPDX-License-Identifier: MIT * * iter13: VK_EXT_transform_feedback command handlers for the JM * architecture path (Bifrost v6/v7 + Valhall-JM v9). * * The runtime contract: * - vkCmdBindTransformFeedbackBuffersEXT: stash (gpu_addr, offset, size) * for each slot into cmdbuf->state.gfx.xfb.buffers[]. * - vkCmdBeginTransformFeedbackEXT: set cmdbuf->state.gfx.xfb.active = true. * Mark sysvals dirty so the next draw re-emits vs.xfb_address[]. * - vkCmdEndTransformFeedbackEXT: set active = false. * * Counter buffers (firstCounterBuffer/counterBufferCount/pCounterBuffers/ * pCounterBufferOffsets) are accepted by API but ignored — v1 doesn't * support pause/resume. transformFeedbackDraw is advertised as false. * * Per-draw integration: jm/panvk_vX_cmd_draw.c reads cmdbuf->state.gfx.xfb * and populates vs.xfb_address[i] for shader use. The pan_nir_lower_xfb * pass in panvk_vX_shader.c emits nir_load_xfb_address(i) which lowers * (via panvk_vX_shader.c sysval handler) to a load from the per-draw * sysval push area. */ #include "vk_log.h" #include "util/log.h" #include "panvk_cmd_buffer.h" #include "panvk_cmd_draw.h" #include "panvk_buffer.h" #include "panvk_entrypoints.h" VKAPI_ATTR void VKAPI_CALL panvk_per_arch(CmdBindTransformFeedbackBuffersEXT)( VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer *pBuffers, const VkDeviceSize *pOffsets, const VkDeviceSize *pSizes) { VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer); struct panvk_cmd_graphics_state *gfx = &cmdbuf->state.gfx; for (uint32_t i = 0; i < bindingCount; i++) { uint32_t slot = firstBinding + i; if (slot >= 4) continue; VK_FROM_HANDLE(panvk_buffer, buf, pBuffers[i]); gfx->xfb.buffers[slot].addr = panvk_buffer_gpu_ptr(buf, 0); gfx->xfb.buffers[slot].offset = pOffsets[i]; gfx->xfb.buffers[slot].size = (pSizes != NULL && pSizes[i] != VK_WHOLE_SIZE) ? pSizes[i] : (buf->vk.size - pOffsets[i]); } if (firstBinding + bindingCount > gfx->xfb.buffer_count) gfx->xfb.buffer_count = firstBinding + bindingCount; } VKAPI_ATTR void VKAPI_CALL panvk_per_arch(CmdBeginTransformFeedbackEXT)( VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, uint32_t counterBufferCount, const VkBuffer *pCounterBuffers, const VkDeviceSize *pCounterBufferOffsets) { VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer); struct panvk_cmd_graphics_state *gfx = &cmdbuf->state.gfx; /* Counter buffers ignored in v1 — see VkPhysicalDeviceTransformFeedback * PropertiesEXT.transformFeedbackDraw = false in panvk_vX_physical_device.c. * App is spec-compliant if it does not pass counter buffers (which our * features advertisement allows), but warn loudly if it does so we do not * silently produce wrong capture state. */ (void)firstCounterBuffer; (void)pCounterBufferOffsets; if (counterBufferCount > 0 && pCounterBuffers != NULL) { mesa_logw("panvk: CmdBeginTransformFeedbackEXT: counter buffers not " "implemented (transformFeedbackDraw=false); XFB resume will " "restart at buffer offset 0"); } gfx->xfb.active = true; /* Per-draw set_gfx_sysval picks up the change automatically — no * explicit dirty marking required (set_gfx_sysval uses memcmp + * BITSET to detect state diffs and re-emit sysvals). */ } VKAPI_ATTR void VKAPI_CALL panvk_per_arch(CmdEndTransformFeedbackEXT)( VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, uint32_t counterBufferCount, const VkBuffer *pCounterBuffers, const VkDeviceSize *pCounterBufferOffsets) { VK_FROM_HANDLE(panvk_cmd_buffer, cmdbuf, commandBuffer); struct panvk_cmd_graphics_state *gfx = &cmdbuf->state.gfx; (void)firstCounterBuffer; (void)counterBufferCount; (void)pCounterBuffers; (void)pCounterBufferOffsets; gfx->xfb.active = false; }