/* * iter13 Janet-CRITICAL regression: XFB-capable pipeline used WITHOUT * vkCmdBeginTransformFeedback must NOT fault the GPU. * * Same pipeline shape as probe_xfb.c, but the draw is not wrapped in * Begin/End XFB and no XFB buffer is bound. The vertex shader still * emits a store_global instruction (xfb_address[0] is read from sysval). * * With the memory-sink fix (xfb_address defaults to PAN_SHADER_OOB_ADDRESS * = 0x8000_0000_0000_0000), the store is silently discarded by the MMU. * Without that fix, the store goes to address 0 → page fault → GPU job * failure. * * Pass criterion: vkQueueSubmit + vkWaitForFences returns VK_SUCCESS * (no DEVICE_LOST). No buffer to read back — we only care that the GPU * survives the draw. */ #include #include #include #include #include #include #include #define VSPV_PATH "probe_xfb.vert.spv" #define STEP(name) do { fprintf(stderr, "[step] " name "\n"); fflush(stderr); } while (0) #define VK_CHECK(call) do { \ VkResult _r = (call); \ if (_r != VK_SUCCESS) { \ fprintf(stderr, "[fail] " #call " => %d at %s:%d\n", \ (int)_r, __FILE__, __LINE__); \ exit(2); \ } \ } while (0) static uint32_t *read_spv(const char *path, size_t *out_bytes) { FILE *f = fopen(path, "rb"); if (!f) { fprintf(stderr, "[fail] open %s: %s\n", path, strerror(errno)); exit(3); } fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); uint32_t *buf = malloc((size_t)n); fread(buf, 1, (size_t)n, f); fclose(f); *out_bytes = (size_t)n; return buf; } int main(void) { STEP("vkCreateInstance"); VkApplicationInfo app = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "panvk-bifrost iter13 XFB no-draw probe", .apiVersion = VK_API_VERSION_1_0, }; const char *inst_exts[] = { "VK_KHR_get_physical_device_properties2" }; VkInstanceCreateInfo ici = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app, .enabledExtensionCount = 1, .ppEnabledExtensionNames = inst_exts, }; VkInstance inst; VK_CHECK(vkCreateInstance(&ici, NULL, &inst)); uint32_t n_phys = 0; VK_CHECK(vkEnumeratePhysicalDevices(inst, &n_phys, NULL)); VkPhysicalDevice *phys = calloc(n_phys, sizeof(*phys)); VK_CHECK(vkEnumeratePhysicalDevices(inst, &n_phys, phys)); VkPhysicalDevice gpu = phys[0]; uint32_t n_qf = 0; vkGetPhysicalDeviceQueueFamilyProperties(gpu, &n_qf, NULL); VkQueueFamilyProperties *qfp = calloc(n_qf, sizeof(*qfp)); vkGetPhysicalDeviceQueueFamilyProperties(gpu, &n_qf, qfp); uint32_t qfam = UINT32_MAX; for (uint32_t i = 0; i < n_qf; i++) { if (qfp[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { qfam = i; break; } } STEP("vkCreateDevice (+XFB feature enabled + dynamic_rendering)"); const char *dev_exts[] = { "VK_KHR_multiview", "VK_KHR_maintenance2", "VK_KHR_create_renderpass2", "VK_KHR_depth_stencil_resolve", "VK_KHR_dynamic_rendering", "VK_EXT_transform_feedback", }; VkPhysicalDeviceTransformFeedbackFeaturesEXT enable_xfb = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT, .transformFeedback = VK_TRUE, .geometryStreams = VK_FALSE, }; VkPhysicalDeviceDynamicRenderingFeaturesKHR dyn_feat = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR, .pNext = &enable_xfb, .dynamicRendering = VK_TRUE, }; float qprio = 1.0f; VkDeviceQueueCreateInfo qci = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = qfam, .queueCount = 1, .pQueuePriorities = &qprio, }; VkDeviceCreateInfo dci = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &dyn_feat, .queueCreateInfoCount = 1, .pQueueCreateInfos = &qci, .enabledExtensionCount = sizeof(dev_exts)/sizeof(dev_exts[0]), .ppEnabledExtensionNames = dev_exts, }; VkDevice dev; VK_CHECK(vkCreateDevice(gpu, &dci, NULL, &dev)); VkQueue queue; vkGetDeviceQueue(dev, qfam, 0, &queue); PFN_vkCmdBeginRenderingKHR pBeginRendering = (PFN_vkCmdBeginRenderingKHR)vkGetDeviceProcAddr(dev, "vkCmdBeginRenderingKHR"); PFN_vkCmdEndRenderingKHR pEndRendering = (PFN_vkCmdEndRenderingKHR)vkGetDeviceProcAddr(dev, "vkCmdEndRenderingKHR"); /* Same XFB-bearing vertex shader as probe_xfb — its SPIR-V has the * xfb_buffer / xfb_offset decorations on `captured`. PanVk's driver * will run pan_nir_lower_xfb on it, producing nir_store_global to * vs.xfb_address[0]. We rely on the driver setting that sysval to * PAN_SHADER_OOB_ADDRESS when xfb is inactive. */ STEP("vkCreateGraphicsPipelines (XFB-capable VS, no XFB buffer bound)"); VkPipelineLayoutCreateInfo plci = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, }; VkPipelineLayout pl; VK_CHECK(vkCreatePipelineLayout(dev, &plci, NULL, &pl)); size_t spv_bytes = 0; uint32_t *spv = read_spv(VSPV_PATH, &spv_bytes); VkShaderModuleCreateInfo smci = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = spv_bytes, .pCode = spv, }; VkShaderModule vsm; VK_CHECK(vkCreateShaderModule(dev, &smci, NULL, &vsm)); free(spv); VkPipelineShaderStageCreateInfo stages[1] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vsm, .pName = "main" }, }; VkPipelineVertexInputStateCreateInfo vi = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, }; VkPipelineInputAssemblyStateCreateInfo ia = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, }; VkViewport vp_dummy = { 0, 0, 1, 1, 0.0f, 1.0f }; VkRect2D sc_dummy = {{0,0}, {1,1}}; VkPipelineViewportStateCreateInfo vp = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .pViewports = &vp_dummy, .scissorCount = 1, .pScissors = &sc_dummy, }; VkPipelineRasterizationStateCreateInfo rs = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .rasterizerDiscardEnable = VK_TRUE, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_NONE, .lineWidth = 1.0f, }; VkPipelineMultisampleStateCreateInfo ms = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, }; VkPipelineRenderingCreateInfoKHR pri = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, .colorAttachmentCount = 0, }; VkGraphicsPipelineCreateInfo gpci = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &pri, .stageCount = 1, .pStages = stages, .pVertexInputState = &vi, .pInputAssemblyState = &ia, .pViewportState = &vp, .pRasterizationState = &rs, .pMultisampleState = &ms, .layout = pl, }; VkPipeline pipe; VK_CHECK(vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &gpci, NULL, &pipe)); VkCommandPoolCreateInfo cpoolci = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .queueFamilyIndex = qfam, }; VkCommandPool cpool; VK_CHECK(vkCreateCommandPool(dev, &cpoolci, NULL, &cpool)); VkCommandBufferAllocateInfo cbai = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = cpool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VkCommandBuffer cb; VK_CHECK(vkAllocateCommandBuffers(dev, &cbai, &cb)); STEP("record (draw WITHOUT XFB Begin/End; no buffer bound)"); VkCommandBufferBeginInfo cbbi = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; VK_CHECK(vkBeginCommandBuffer(cb, &cbbi)); VkRenderingInfoKHR ri = { .sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, .renderArea = {{0,0}, {1,1}}, .layerCount = 1, .colorAttachmentCount = 0, }; pBeginRendering(cb, &ri); vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); /* No vkCmdBindTransformFeedbackBuffersEXT. * No vkCmdBeginTransformFeedbackEXT. * Just draw — the XFB store in the shader must be silently discarded. */ vkCmdDraw(cb, 3, 1, 0, 0); pEndRendering(cb); VK_CHECK(vkEndCommandBuffer(cb)); VkFenceCreateInfo fci = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; VkFence fence; VK_CHECK(vkCreateFence(dev, &fci, NULL, &fence)); VkSubmitInfo si = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = 1, .pCommandBuffers = &cb, }; STEP("submit + wait (10s) — expect VK_SUCCESS, not DEVICE_LOST"); VK_CHECK(vkQueueSubmit(queue, 1, &si, fence)); VkResult wr = vkWaitForFences(dev, 1, &fence, VK_TRUE, 10ULL * 1000 * 1000 * 1000); if (wr == VK_ERROR_DEVICE_LOST) { fprintf(stderr, "[FAIL] DEVICE_LOST — the XFB store-global probably faulted " "(memory-sink sentinel not applied).\n"); return 1; } if (wr != VK_SUCCESS) { fprintf(stderr, "[FAIL] vkWaitForFences => %d\n", wr); return 2; } vkDestroyFence(dev, fence, NULL); vkDestroyCommandPool(dev, cpool, NULL); vkDestroyPipeline(dev, pipe, NULL); vkDestroyShaderModule(dev, vsm, NULL); vkDestroyPipelineLayout(dev, pl, NULL); vkDestroyDevice(dev, NULL); vkDestroyInstance(inst, NULL); free(phys); free(qfp); fprintf(stderr, "[PASS] XFB-capable pipeline survives non-XFB draw — memory-sink active.\n"); return 0; }