forked from marfrit/panvk-bifrost
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>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# iter4 textured-quad probe — build glue.
|
||||
|
||||
CC ?= cc
|
||||
CFLAGS ?= -O0 -g -Wall -Wextra -std=c11
|
||||
LDLIBS ?= -lvulkan
|
||||
|
||||
PROBE = probe_texture
|
||||
SRC = probe_texture.c
|
||||
VERT = probe_texture.vert
|
||||
FRAG = probe_texture.frag
|
||||
VSPV = probe_texture.vert.spv
|
||||
FSPV = probe_texture.frag.spv
|
||||
|
||||
all: $(PROBE) $(VSPV) $(FSPV)
|
||||
|
||||
$(PROBE): $(SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDLIBS)
|
||||
|
||||
$(VSPV): $(VERT)
|
||||
glslangValidator -V $< -o $@
|
||||
|
||||
$(FSPV): $(FRAG)
|
||||
glslangValidator -V $< -o $@
|
||||
|
||||
run: all
|
||||
PAN_I_WANT_A_BROKEN_VULKAN_DRIVER=1 ./$(PROBE)
|
||||
|
||||
run-validation: all
|
||||
PAN_I_WANT_A_BROKEN_VULKAN_DRIVER=1 \
|
||||
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
|
||||
./$(PROBE)
|
||||
|
||||
clean:
|
||||
rm -f $(PROBE) $(VSPV) $(FSPV)
|
||||
|
||||
.PHONY: all run run-validation clean
|
||||
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
* iter4 textured-quad probe for panvk-bifrost campaign.
|
||||
*
|
||||
* Tests the Bifrost-specific descriptor model + texture upload + sampled-image
|
||||
* read on PanVk-Bifrost (PineTab2 / Mali-G52 r1 MC1).
|
||||
*
|
||||
* Texel encoding for 4x4 source: R = 0x10 + 0x40*x, G = 0x10 + 0x40*y,
|
||||
* B = 0x80, A = 0xff (16 unique values).
|
||||
* Output pixel (col, row) == texel(col%4, row%4), repeated in a 16x16-tile
|
||||
* grid across the 64x64 attachment.
|
||||
*/
|
||||
|
||||
#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) /* 16384 */
|
||||
|
||||
#define TEX_W 4
|
||||
#define TEX_H 4
|
||||
#define TEX_PIXELS (TEX_W * TEX_H)
|
||||
#define TEX_BYTES (TEX_PIXELS * 4) /* 64 */
|
||||
|
||||
#define VSPV_PATH "probe_texture.vert.spv"
|
||||
#define FSPV_PATH "probe_texture.frag.spv"
|
||||
|
||||
/* Source texel packed LE uint32 = (A<<24)|(B<<16)|(G<<8)|R */
|
||||
static inline uint32_t texel_le(uint32_t x, uint32_t y)
|
||||
{
|
||||
uint32_t r = 0x10 + 0x40 * x;
|
||||
uint32_t g = 0x10 + 0x40 * y;
|
||||
return (0xffu << 24) | (0x80u << 16) | (g << 8) | r;
|
||||
}
|
||||
|
||||
#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 want=0x%x bits=0x%x\n", want, type_bits); 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 iter4",
|
||||
.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));
|
||||
|
||||
STEP("vkEnumeratePhysicalDevices");
|
||||
uint32_t n_phys = 0;
|
||||
VK_CHECK(vkEnumeratePhysicalDevices(inst, &n_phys, NULL));
|
||||
if (n_phys == 0) { fprintf(stderr, "[fail] no devices\n"); return 5; }
|
||||
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; }
|
||||
}
|
||||
if (qfam == UINT32_MAX) { fprintf(stderr, "[fail] no graphics queue\n"); return 6; }
|
||||
|
||||
STEP("vkCreateDevice (+dynamic_rendering chain)");
|
||||
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");
|
||||
|
||||
/* ---- source texture (4x4) ------------------------------------------- */
|
||||
STEP("vkCreateImage source texture (4x4 RGBA8 SAMPLED|TRANSFER_DST)");
|
||||
VkImageCreateInfo tex_ici = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = VK_FORMAT_R8G8B8A8_UNORM,
|
||||
.extent = { TEX_W, TEX_H, 1 },
|
||||
.mipLevels = 1, .arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
VkImage tex;
|
||||
VK_CHECK(vkCreateImage(dev, &tex_ici, NULL, &tex));
|
||||
|
||||
VkMemoryRequirements tex_mr;
|
||||
vkGetImageMemoryRequirements(dev, tex, &tex_mr);
|
||||
fprintf(stderr, "[info] source texture memReq size=%llu align=%llu\n",
|
||||
(unsigned long long)tex_mr.size, (unsigned long long)tex_mr.alignment);
|
||||
VkMemoryAllocateInfo tex_mai = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = tex_mr.size,
|
||||
.memoryTypeIndex = pick_memtype(&mp, tex_mr.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
|
||||
};
|
||||
VkDeviceMemory tex_mem;
|
||||
VK_CHECK(vkAllocateMemory(dev, &tex_mai, NULL, &tex_mem));
|
||||
VK_CHECK(vkBindImageMemory(dev, tex, tex_mem, 0));
|
||||
|
||||
VkImageViewCreateInfo tex_ivci = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = tex,
|
||||
.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 tex_iv;
|
||||
VK_CHECK(vkCreateImageView(dev, &tex_ivci, NULL, &tex_iv));
|
||||
|
||||
/* ---- sampler -------------------------------------------------------- */
|
||||
STEP("vkCreateSampler (NEAREST, CLAMP_TO_EDGE)");
|
||||
VkSamplerCreateInfo sci = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = VK_FILTER_NEAREST,
|
||||
.minFilter = VK_FILTER_NEAREST,
|
||||
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
|
||||
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
|
||||
.minLod = 0.0f, .maxLod = 0.0f,
|
||||
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
VkSampler samp;
|
||||
VK_CHECK(vkCreateSampler(dev, &sci, NULL, &samp));
|
||||
|
||||
/* ---- staging buffer for texture upload ----------------------------- */
|
||||
uint32_t texel_data[TEX_PIXELS];
|
||||
for (uint32_t y = 0; y < TEX_H; y++)
|
||||
for (uint32_t x = 0; x < TEX_W; x++)
|
||||
texel_data[y * TEX_W + x] = texel_le(x, y);
|
||||
|
||||
VkBufferCreateInfo stage_bci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = TEX_BYTES,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
};
|
||||
VkBuffer stage_buf;
|
||||
VK_CHECK(vkCreateBuffer(dev, &stage_bci, NULL, &stage_buf));
|
||||
VkMemoryRequirements stage_mr;
|
||||
vkGetBufferMemoryRequirements(dev, stage_buf, &stage_mr);
|
||||
VkMemoryAllocateInfo stage_mai = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = stage_mr.size,
|
||||
.memoryTypeIndex = pick_host_visible(&mp, stage_mr.memoryTypeBits),
|
||||
};
|
||||
VkDeviceMemory stage_mem;
|
||||
VK_CHECK(vkAllocateMemory(dev, &stage_mai, NULL, &stage_mem));
|
||||
VK_CHECK(vkBindBufferMemory(dev, stage_buf, stage_mem, 0));
|
||||
|
||||
void *stage_mapped = NULL;
|
||||
VK_CHECK(vkMapMemory(dev, stage_mem, 0, VK_WHOLE_SIZE, 0, &stage_mapped));
|
||||
memcpy(stage_mapped, texel_data, TEX_BYTES);
|
||||
vkUnmapMemory(dev, stage_mem);
|
||||
|
||||
/* ---- color attachment image (64x64) -------------------------------- */
|
||||
STEP("vkCreateImage color attachment (64x64 RGBA8 COLOR_ATTACHMENT|TRANSFER_SRC)");
|
||||
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));
|
||||
|
||||
/* ---- readback buffer ------------------------------------------------ */
|
||||
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 ------------------------------------------------- */
|
||||
STEP("vkCreateDescriptorSetLayout (1 COMBINED_IMAGE_SAMPLER)");
|
||||
VkDescriptorSetLayoutBinding dslb = {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_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_COMBINED_IMAGE_SAMPLER, 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));
|
||||
|
||||
/* descriptor update must be done after texture is in SHADER_READ layout,
|
||||
* but it's a CPU-side update — Vulkan allows it before image is in that
|
||||
* layout, as long as the image is in the correct layout at draw-submit time. */
|
||||
VkDescriptorImageInfo dii = {
|
||||
.sampler = samp,
|
||||
.imageView = tex_iv,
|
||||
.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
};
|
||||
VkWriteDescriptorSet wds = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = dset, .dstBinding = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &dii,
|
||||
};
|
||||
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" },
|
||||
};
|
||||
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 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 (tex upload + draw + readback)");
|
||||
VkCommandBufferBeginInfo cbbi = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
VK_CHECK(vkBeginCommandBuffer(cb, &cbbi));
|
||||
|
||||
/* Source texture: UNDEFINED -> TRANSFER_DST */
|
||||
image_barrier(cb, tex,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
0, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
/* Upload staging buffer -> source texture */
|
||||
VkBufferImageCopy tex_copy = {
|
||||
.imageSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1,
|
||||
},
|
||||
.imageExtent = { TEX_W, TEX_H, 1 },
|
||||
};
|
||||
vkCmdCopyBufferToImage(cb, stage_buf, tex, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &tex_copy);
|
||||
|
||||
/* Source texture: TRANSFER_DST -> SHADER_READ_ONLY */
|
||||
image_barrier(cb, tex,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
||||
/* Color attachment: UNDEFINED -> COLOR_ATTACHMENT */
|
||||
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);
|
||||
|
||||
/* Render */
|
||||
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);
|
||||
vkCmdDraw(cb, 3, 1, 0, 0);
|
||||
|
||||
pCmdEndRendering(cb);
|
||||
|
||||
/* Color attachment: COLOR_ATTACHMENT -> TRANSFER_SRC */
|
||||
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);
|
||||
|
||||
/* Attachment -> readback buffer */
|
||||
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));
|
||||
|
||||
/* ---- submit ------------------------------------------------------- */
|
||||
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)");
|
||||
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] vkWaitForFences=>%d\n", wr); return 8; }
|
||||
|
||||
/* ---- verify ------------------------------------------------------- */
|
||||
STEP("invalidate + verify");
|
||||
VkMappedMemoryRange mmr = {
|
||||
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
|
||||
.memory = rb_mem, .offset = 0, .size = VK_WHOLE_SIZE,
|
||||
};
|
||||
vkInvalidateMappedMemoryRanges(dev, 1, &mmr);
|
||||
|
||||
uint32_t mismatches = 0, sentinel = 0, black = 0;
|
||||
uint32_t first_diff_idx = UINT32_MAX;
|
||||
for (uint32_t row = 0; row < IMG_H; row++) {
|
||||
for (uint32_t col = 0; col < IMG_W; col++) {
|
||||
uint32_t idx = row * IMG_W + col;
|
||||
uint32_t got = u32[idx];
|
||||
uint32_t want = texel_le(col % TEX_W, row % TEX_H);
|
||||
if (got != want) {
|
||||
if (first_diff_idx == UINT32_MAX) first_diff_idx = idx;
|
||||
if (got == 0xDEADBEEFu) sentinel++;
|
||||
else if (got == 0xff000000u || got == 0x00000000u) black++;
|
||||
mismatches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "[info] mismatches=%u/%u sentinel=%u black=%u\n",
|
||||
mismatches, PIXELS, sentinel, black);
|
||||
|
||||
if (mismatches) {
|
||||
uint32_t idx = first_diff_idx;
|
||||
uint32_t row = idx / IMG_W, col = idx % IMG_W;
|
||||
fprintf(stderr, "[diff] first mismatch (col=%u, row=%u): got=0x%08x want=0x%08x\n",
|
||||
col, row, u32[idx], texel_le(col % TEX_W, row % TEX_H));
|
||||
/* Dump 4x4 top-left block — should be exact 4x4 source texture. */
|
||||
fprintf(stderr, "[dump] top-left 4x4 block (expected = source texture):\n");
|
||||
for (uint32_t r = 0; r < 4; r++) {
|
||||
fprintf(stderr, "[dump] ");
|
||||
for (uint32_t c = 0; c < 4; c++) {
|
||||
fprintf(stderr, "0x%08x ", u32[r * IMG_W + c]);
|
||||
}
|
||||
fprintf(stderr, " want: ");
|
||||
for (uint32_t c = 0; c < 4; c++) {
|
||||
fprintf(stderr, "0x%08x ", texel_le(c, r));
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- teardown ----------------------------------------------------- */
|
||||
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, stage_buf, NULL);
|
||||
vkFreeMemory(dev, stage_mem, NULL);
|
||||
vkDestroySampler(dev, samp, NULL);
|
||||
vkDestroyImageView(dev, tex_iv, NULL);
|
||||
vkDestroyImage(dev, tex, NULL);
|
||||
vkFreeMemory(dev, tex_mem, NULL);
|
||||
vkDestroyDevice(dev, NULL);
|
||||
vkDestroyInstance(inst, NULL);
|
||||
free(phys); free(qfp);
|
||||
|
||||
if (mismatches == 0) {
|
||||
fprintf(stderr, "[PASS] PanVk-Bifrost textured quad: all %u pixels match.\n", PIXELS);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "[FAIL] %u / %u mismatched.\n", mismatches, PIXELS);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#version 450
|
||||
|
||||
// iter4 fragment shader: sample 4x4 source texture via texelFetch
|
||||
// (no filter, no addressing — direct integer-coord image read).
|
||||
// Output is the texel at (col%4, row%4) where col,row are gl_FragCoord.
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D tex;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
ivec2 src = ivec2(gl_FragCoord.xy) % 4;
|
||||
outColor = texelFetch(tex, src, 0);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
// Same fullscreen triangle as iter3 — positions from gl_VertexIndex.
|
||||
|
||||
void main() {
|
||||
vec2 pos = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user