Files
panvk-bifrost/mesa-panvk-bifrost/iter5/probe_vbo_ubo.c
T
marfrit a4e7d8ab90 initial seed: retrofit campaign lineage from local working trees
panvk-bifrost campaigns (r1..r4 Vulkan compositor + r5.video1 Vulkan
video decode) shipped before this repo existed; the deliverable
patches live in marfrit-packages, but the reasoning chain, phase docs,
and source-state evidence lived only in local working trees on the
development host.

This retrofit imports:
- mesa-panvk-bifrost/   — r1..r4 era phase docs (iter1..iter18)
                          (libmali stub blobs at iter18/blob/ excluded
                          — 109MB of RE artifacts replaced with a README
                          pointer)
- mesa-panvk-bifrost-video/ — sibling campaign phase docs + probe
- evidence/             — frozen .tgz source snapshots at each milestone
                          (basis for the 0005 patch diff generation)

Future iterations should branch off here from day one, so each iter is
a commit rather than a snapshot. See [[feedback-session-local-process-pins]]
for the process drift this retrofit closes.

Total: 1.9 MB across 124 files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 05:25:37 +02:00

653 lines
25 KiB
C

/*
* iter5 vertex+UBO probe for panvk-bifrost campaign.
*
* Tests: vertex input bindings, UBO descriptor binding (vertex stage),
* NIR vertex-side descriptor lowering, varying interpolation.
*
* Geometry: 3 vertices, interleaved pos(vec2)+color(vec3), 32-byte stride.
* UBO: mat4 transform (scale 0.8 in x/y, identity rest).
* Output: triangle apex-up in scaled-NDC, colors mix via barycentric interp.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <vulkan/vulkan.h>
#define IMG_W 64
#define IMG_H 64
#define PIXELS (IMG_W * IMG_H)
#define BUFFER_BYTES (PIXELS * 4)
#define VSPV_PATH "probe_vbo_ubo.vert.spv"
#define FSPV_PATH "probe_vbo_ubo.frag.spv"
/* Vertex struct: 32 bytes stride (pos 8 + pad 8 + color 12 + pad 4).
* Using 8-byte alignment for pos and 16-byte alignment for vec3 makes life
* easier — we just declare a 32-byte stride and tell Vulkan the offsets. */
struct vertex {
float pos[2]; /* offset 0 */
float pad0[2]; /* offset 8 */
float color[3]; /* offset 16 */
float pad1[1]; /* offset 28 */
};
/* UBO: 4x4 column-major matrix, scale 0.8 in x/y, identity rest. */
struct ubo {
float matrix[16];
};
#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);
if (n <= 0 || (n & 3)) { fprintf(stderr, "[fail] bad SPV size %ld\n", n); exit(3); }
uint32_t *buf = malloc((size_t)n);
if (fread(buf, 1, (size_t)n, f) != (size_t)n) { fprintf(stderr, "[fail] short read\n"); exit(3); }
fclose(f);
*out_bytes = (size_t)n;
return buf;
}
static uint32_t pick_memtype(const VkPhysicalDeviceMemoryProperties *mp,
uint32_t type_bits, VkMemoryPropertyFlags want)
{
for (uint32_t i = 0; i < mp->memoryTypeCount; i++) {
if ((type_bits & (1u << i)) &&
(mp->memoryTypes[i].propertyFlags & want) == want)
return i;
}
fprintf(stderr, "[fail] no memtype\n"); exit(4);
}
static uint32_t pick_host_visible(const VkPhysicalDeviceMemoryProperties *mp,
uint32_t type_bits)
{
VkMemoryPropertyFlags pref =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
for (uint32_t i = 0; i < mp->memoryTypeCount; i++) {
if ((type_bits & (1u << i)) &&
(mp->memoryTypes[i].propertyFlags & pref) == pref) return i;
}
for (uint32_t i = 0; i < mp->memoryTypeCount; i++) {
if ((type_bits & (1u << i)) &&
(mp->memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) return i;
}
fprintf(stderr, "[fail] no host_visible\n"); exit(4);
}
static void image_barrier(VkCommandBuffer cb, VkImage img,
VkImageLayout old_layout, VkImageLayout new_layout,
VkAccessFlags src_access, VkAccessFlags dst_access,
VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage)
{
VkImageMemoryBarrier ib = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = src_access, .dstAccessMask = dst_access,
.oldLayout = old_layout, .newLayout = new_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = img,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0, .levelCount = 1,
.baseArrayLayer = 0, .layerCount = 1,
},
};
vkCmdPipelineBarrier(cb, src_stage, dst_stage, 0, 0, NULL, 0, NULL, 1, &ib);
}
static VkShaderModule make_shader(VkDevice dev, const char *path)
{
size_t bytes = 0;
uint32_t *code = read_spv(path, &bytes);
VkShaderModuleCreateInfo smci = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = bytes, .pCode = code,
};
VkShaderModule sm;
VK_CHECK(vkCreateShaderModule(dev, &smci, NULL, &sm));
free(code);
return sm;
}
int main(void)
{
STEP("vkCreateInstance");
const char *inst_exts[] = { "VK_KHR_get_physical_device_properties2" };
VkApplicationInfo app = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "panvk-bifrost iter5",
.apiVersion = VK_API_VERSION_1_0,
};
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];
VkPhysicalDeviceProperties pp;
vkGetPhysicalDeviceProperties(gpu, &pp);
fprintf(stderr, "[info] gpu='%s'\n", pp.deviceName);
VkPhysicalDeviceMemoryProperties mp;
vkGetPhysicalDeviceMemoryProperties(gpu, &mp);
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");
const char *dev_exts[] = {
"VK_KHR_multiview", "VK_KHR_maintenance2",
"VK_KHR_create_renderpass2", "VK_KHR_depth_stencil_resolve",
"VK_KHR_dynamic_rendering",
};
VkPhysicalDeviceDynamicRenderingFeaturesKHR dyn_feat = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR,
.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 pCmdBeginRendering =
(PFN_vkCmdBeginRenderingKHR)vkGetDeviceProcAddr(dev, "vkCmdBeginRenderingKHR");
PFN_vkCmdEndRenderingKHR pCmdEndRendering =
(PFN_vkCmdEndRenderingKHR)vkGetDeviceProcAddr(dev, "vkCmdEndRenderingKHR");
/* ---- vertex buffer ---------------------------------------------------- */
struct vertex verts[3] = {
{ .pos = {-0.5f, -0.5f}, .color = {1.0f, 0.0f, 0.0f} }, /* red */
{ .pos = { 0.5f, -0.5f}, .color = {0.0f, 1.0f, 0.0f} }, /* green */
{ .pos = { 0.0f, 0.5f}, .color = {0.0f, 0.0f, 1.0f} }, /* blue */
};
STEP("vkCreateBuffer vertex buffer");
VkBufferCreateInfo vb_bci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(verts),
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VkBuffer vb;
VK_CHECK(vkCreateBuffer(dev, &vb_bci, NULL, &vb));
VkMemoryRequirements vb_mr;
vkGetBufferMemoryRequirements(dev, vb, &vb_mr);
VkMemoryAllocateInfo vb_mai = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = vb_mr.size,
.memoryTypeIndex = pick_host_visible(&mp, vb_mr.memoryTypeBits),
};
VkDeviceMemory vb_mem;
VK_CHECK(vkAllocateMemory(dev, &vb_mai, NULL, &vb_mem));
VK_CHECK(vkBindBufferMemory(dev, vb, vb_mem, 0));
void *vb_mapped = NULL;
VK_CHECK(vkMapMemory(dev, vb_mem, 0, VK_WHOLE_SIZE, 0, &vb_mapped));
memcpy(vb_mapped, verts, sizeof(verts));
vkUnmapMemory(dev, vb_mem);
/* ---- UBO -------------------------------------------------------------- */
STEP("vkCreateBuffer UBO");
struct ubo ubo_data = {{
0.8f, 0.0f, 0.0f, 0.0f,
0.0f, 0.8f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
}};
VkBufferCreateInfo ubo_bci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = sizeof(ubo_data),
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VkBuffer ubo_buf;
VK_CHECK(vkCreateBuffer(dev, &ubo_bci, NULL, &ubo_buf));
VkMemoryRequirements ubo_mr;
vkGetBufferMemoryRequirements(dev, ubo_buf, &ubo_mr);
VkMemoryAllocateInfo ubo_mai = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = ubo_mr.size,
.memoryTypeIndex = pick_host_visible(&mp, ubo_mr.memoryTypeBits),
};
VkDeviceMemory ubo_mem;
VK_CHECK(vkAllocateMemory(dev, &ubo_mai, NULL, &ubo_mem));
VK_CHECK(vkBindBufferMemory(dev, ubo_buf, ubo_mem, 0));
void *ubo_mapped = NULL;
VK_CHECK(vkMapMemory(dev, ubo_mem, 0, VK_WHOLE_SIZE, 0, &ubo_mapped));
memcpy(ubo_mapped, &ubo_data, sizeof(ubo_data));
vkUnmapMemory(dev, ubo_mem);
/* ---- color attachment + readback buffer ------------------------------ */
VkImageCreateInfo att_ici = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = VK_FORMAT_R8G8B8A8_UNORM,
.extent = { IMG_W, IMG_H, 1 },
.mipLevels = 1, .arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
};
VkImage att;
VK_CHECK(vkCreateImage(dev, &att_ici, NULL, &att));
VkMemoryRequirements att_mr;
vkGetImageMemoryRequirements(dev, att, &att_mr);
VkMemoryAllocateInfo att_mai = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = att_mr.size,
.memoryTypeIndex = pick_memtype(&mp, att_mr.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
};
VkDeviceMemory att_mem;
VK_CHECK(vkAllocateMemory(dev, &att_mai, NULL, &att_mem));
VK_CHECK(vkBindImageMemory(dev, att, att_mem, 0));
VkImageViewCreateInfo att_ivci = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = att,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = VK_FORMAT_R8G8B8A8_UNORM,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0, .levelCount = 1,
.baseArrayLayer = 0, .layerCount = 1,
},
};
VkImageView att_iv;
VK_CHECK(vkCreateImageView(dev, &att_ivci, NULL, &att_iv));
VkBufferCreateInfo rb_bci = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = BUFFER_BYTES,
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
};
VkBuffer rb;
VK_CHECK(vkCreateBuffer(dev, &rb_bci, NULL, &rb));
VkMemoryRequirements rb_mr;
vkGetBufferMemoryRequirements(dev, rb, &rb_mr);
VkMemoryAllocateInfo rb_mai = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = rb_mr.size,
.memoryTypeIndex = pick_host_visible(&mp, rb_mr.memoryTypeBits),
};
VkDeviceMemory rb_mem;
VK_CHECK(vkAllocateMemory(dev, &rb_mai, NULL, &rb_mem));
VK_CHECK(vkBindBufferMemory(dev, rb, rb_mem, 0));
void *rb_mapped = NULL;
VK_CHECK(vkMapMemory(dev, rb_mem, 0, VK_WHOLE_SIZE, 0, &rb_mapped));
uint32_t *u32 = (uint32_t *)rb_mapped;
for (uint32_t i = 0; i < PIXELS; i++) u32[i] = 0xDEADBEEFu;
/* ---- descriptor set (1 UBO vertex stage) ----------------------------- */
STEP("vkCreateDescriptorSetLayout (UBO at vertex stage)");
VkDescriptorSetLayoutBinding dslb = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
};
VkDescriptorSetLayoutCreateInfo dslci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1, .pBindings = &dslb,
};
VkDescriptorSetLayout dsl;
VK_CHECK(vkCreateDescriptorSetLayout(dev, &dslci, NULL, &dsl));
VkDescriptorPoolSize dps = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 };
VkDescriptorPoolCreateInfo dpci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = 1, .poolSizeCount = 1, .pPoolSizes = &dps,
};
VkDescriptorPool dpool;
VK_CHECK(vkCreateDescriptorPool(dev, &dpci, NULL, &dpool));
VkDescriptorSetAllocateInfo dsai = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = dpool,
.descriptorSetCount = 1, .pSetLayouts = &dsl,
};
VkDescriptorSet dset;
VK_CHECK(vkAllocateDescriptorSets(dev, &dsai, &dset));
VkDescriptorBufferInfo dbi = { ubo_buf, 0, VK_WHOLE_SIZE };
VkWriteDescriptorSet wds = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = dset, .dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.pBufferInfo = &dbi,
};
vkUpdateDescriptorSets(dev, 1, &wds, 0, NULL);
/* ---- pipeline -------------------------------------------------------- */
STEP("vkCreatePipelineLayout + shaders");
VkPipelineLayoutCreateInfo plci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1, .pSetLayouts = &dsl,
};
VkPipelineLayout pl;
VK_CHECK(vkCreatePipelineLayout(dev, &plci, NULL, &pl));
VkShaderModule vsm = make_shader(dev, VSPV_PATH);
VkShaderModule fsm = make_shader(dev, FSPV_PATH);
VkPipelineShaderStageCreateInfo stages[2] = {
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT, .module = vsm, .pName = "main" },
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = fsm, .pName = "main" },
};
VkVertexInputBindingDescription vibind = {
.binding = 0,
.stride = sizeof(struct vertex), /* 32 */
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
};
VkVertexInputAttributeDescription viattrs[2] = {
{ .location = 0, .binding = 0,
.format = VK_FORMAT_R32G32_SFLOAT,
.offset = offsetof(struct vertex, pos) },
{ .location = 1, .binding = 0,
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(struct vertex, color) },
};
VkPipelineVertexInputStateCreateInfo vi = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1, .pVertexBindingDescriptions = &vibind,
.vertexAttributeDescriptionCount = 2, .pVertexAttributeDescriptions = viattrs,
};
VkPipelineInputAssemblyStateCreateInfo ia = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
};
VkViewport viewport = { 0, 0, IMG_W, IMG_H, 0.0f, 1.0f };
VkRect2D scissor = {{ 0, 0 }, { IMG_W, IMG_H }};
VkPipelineViewportStateCreateInfo vp = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1, .pViewports = &viewport,
.scissorCount = 1, .pScissors = &scissor,
};
VkPipelineRasterizationStateCreateInfo rs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.0f,
};
VkPipelineMultisampleStateCreateInfo ms = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
};
VkPipelineColorBlendAttachmentState cba = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
};
VkPipelineColorBlendStateCreateInfo cb_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1, .pAttachments = &cba,
};
VkFormat color_fmt = VK_FORMAT_R8G8B8A8_UNORM;
VkPipelineRenderingCreateInfoKHR pri = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
.colorAttachmentCount = 1, .pColorAttachmentFormats = &color_fmt,
};
VkGraphicsPipelineCreateInfo gpci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &pri,
.stageCount = 2, .pStages = stages,
.pVertexInputState = &vi,
.pInputAssemblyState = &ia,
.pViewportState = &vp,
.pRasterizationState = &rs,
.pMultisampleState = &ms,
.pColorBlendState = &cb_state,
.layout = pl,
};
STEP("vkCreateGraphicsPipelines");
VkPipeline pipe;
VK_CHECK(vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &gpci, NULL, &pipe));
/* ---- cmd buffer ---------------------------------------------------- */
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 cmd buffer");
VkCommandBufferBeginInfo cbbi = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(vkBeginCommandBuffer(cb, &cbbi));
image_barrier(cb, att,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
VkClearValue clear_black = {{{0.0f, 0.0f, 0.0f, 0.0f}}};
VkRenderingAttachmentInfoKHR color_attach = {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR,
.imageView = att_iv,
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = clear_black,
};
VkRenderingInfoKHR ri = {
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
.renderArea = {{ 0, 0 }, { IMG_W, IMG_H }},
.layerCount = 1,
.colorAttachmentCount = 1, .pColorAttachments = &color_attach,
};
pCmdBeginRendering(cb, &ri);
vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pl,
0, 1, &dset, 0, NULL);
VkDeviceSize vb_offset = 0;
vkCmdBindVertexBuffers(cb, 0, 1, &vb, &vb_offset);
vkCmdDraw(cb, 3, 1, 0, 0);
pCmdEndRendering(cb);
image_barrier(cb, att,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy rb_copy = {
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1,
},
.imageExtent = { IMG_W, IMG_H, 1 },
};
vkCmdCopyImageToBuffer(cb, att, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
rb, 1, &rb_copy);
VkBufferMemoryBarrier bb = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = rb, .offset = 0, .size = VK_WHOLE_SIZE,
};
vkCmdPipelineBarrier(cb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
0, 0, NULL, 1, &bb, 0, NULL);
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");
VK_CHECK(vkQueueSubmit(queue, 1, &si, fence));
VkResult wr = vkWaitForFences(dev, 1, &fence, VK_TRUE, 10ULL * 1000 * 1000 * 1000);
if (wr == VK_TIMEOUT) { fprintf(stderr, "[fail] fence TIMEOUT\n"); return 7; }
if (wr != VK_SUCCESS) { fprintf(stderr, "[fail] wait=>%d\n", wr); return 8; }
/* ---- verify ------------------------------------------------------- */
STEP("verify");
VkMappedMemoryRange mmr = {
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = rb_mem, .offset = 0, .size = VK_WHOLE_SIZE,
};
vkInvalidateMappedMemoryRanges(dev, 1, &mmr);
/* Verification:
* - center pixel near centroid: all R,G,B > 0x10 (interpolated mix)
* - TL (0,0) outside: exactly clear (0 or 0xff000000)
* - TR (63,0) outside: exactly clear
* - non-clear pixel count: triangle area = 0.5 * 0.8 * 0.8 = 0.32 sq NDC
* viewport area = 4 sq NDC, so 8% = ~328 pixels
* allow [200, 500] for edge rule variations
*/
uint32_t center = u32[28 * IMG_W + 32];
uint32_t tl = u32[0];
uint32_t tr = u32[63];
uint32_t covered = 0;
for (uint32_t i = 0; i < PIXELS; i++)
if (u32[i] != 0u && u32[i] != 0xff000000u) covered++;
uint8_t cR = center & 0xff;
uint8_t cG = (center >> 8) & 0xff;
uint8_t cB = (center >> 16) & 0xff;
fprintf(stderr, "[info] center pixel (32,28) = 0x%08x (R=%02x G=%02x B=%02x)\n",
center, cR, cG, cB);
fprintf(stderr, "[info] TL (0,0) = 0x%08x TR (63,0) = 0x%08x\n", tl, tr);
fprintf(stderr, "[info] covered (non-clear) pixels = %u / %u\n", covered, PIXELS);
int ok = 1;
if (!(cR > 0x10 && cG > 0x10 && cB > 0x10)) {
fprintf(stderr, "[diff] center pixel does NOT have all R/G/B > 0x10\n");
ok = 0;
}
if (tl != 0u && tl != 0xff000000u) {
fprintf(stderr, "[diff] TL not clear: 0x%08x\n", tl);
ok = 0;
}
if (tr != 0u && tr != 0xff000000u) {
fprintf(stderr, "[diff] TR not clear: 0x%08x\n", tr);
ok = 0;
}
if (covered < 200 || covered > 500) {
fprintf(stderr, "[diff] coverage out of range: %u (want 200..500)\n", covered);
ok = 0;
}
/* Dump first 8 rows for inspection if failed. */
if (!ok) {
fprintf(stderr, "[dump] first 8 rows of attachment:\n");
for (uint32_t r = 0; r < 8; r++) {
fprintf(stderr, "[dump] row %2u: ", r);
for (uint32_t c = 0; c < IMG_W; c += 8) {
fprintf(stderr, "%08x ", u32[r * IMG_W + c]);
}
fprintf(stderr, "\n");
}
}
vkUnmapMemory(dev, rb_mem);
vkDestroyFence(dev, fence, NULL);
vkDestroyCommandPool(dev, cpool, NULL);
vkDestroyPipeline(dev, pipe, NULL);
vkDestroyShaderModule(dev, vsm, NULL);
vkDestroyShaderModule(dev, fsm, NULL);
vkDestroyPipelineLayout(dev, pl, NULL);
vkDestroyDescriptorPool(dev, dpool, NULL);
vkDestroyDescriptorSetLayout(dev, dsl, NULL);
vkDestroyBuffer(dev, rb, NULL);
vkFreeMemory(dev, rb_mem, NULL);
vkDestroyImageView(dev, att_iv, NULL);
vkDestroyImage(dev, att, NULL);
vkFreeMemory(dev, att_mem, NULL);
vkDestroyBuffer(dev, ubo_buf, NULL);
vkFreeMemory(dev, ubo_mem, NULL);
vkDestroyBuffer(dev, vb, NULL);
vkFreeMemory(dev, vb_mem, NULL);
vkDestroyDevice(dev, NULL);
vkDestroyInstance(inst, NULL);
free(phys); free(qfp);
if (ok) {
fprintf(stderr, "[PASS] PanVk-Bifrost vbo+ubo triangle: all checks.\n");
return 0;
} else {
fprintf(stderr, "[FAIL] one or more checks failed.\n");
return 1;
}
}