#include #include #include #include #define VK_PROTOTYPES #include #include #include #include #include #include #include #include "vk-spirv.h" #define for_each_bit(b, dword) \ for (uint32_t __dword = (dword); \ (b) = __builtin_ffs(__dword) - 1, __dword; \ __dword &= ~(1 << (b))) static inline uint32_t align_u32(uint32_t value, uint32_t alignment) { return (value + alignment - 1) & ~(alignment - 1); } static void fail_if(int cond, const char *format, ...) { va_list args; if (!cond) return; va_start(args, format); vfprintf(stderr, format, args); va_end(args); exit(1); } static void write_png(char *path, int32_t width, int32_t height, int32_t stride, void *pixels) { FILE *f = NULL; png_structp png_writer = NULL; png_infop png_info = NULL; uint8_t *rows[height]; for (int32_t y = 0; y < height; y++) rows[y] = pixels + y * stride; f = fopen(path, "wb"); fail_if(!f, "failed to open file for writing: %s", path); png_writer = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); fail_if (!png_writer, "failed to create png writer"); png_info = png_create_info_struct(png_writer); fail_if(!png_info, "failed to create png writer info"); png_init_io(png_writer, f); png_set_IHDR(png_writer, png_info, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_writer, png_info); png_set_rows(png_writer, png_info, rows); png_write_png(png_writer, png_info, PNG_TRANSFORM_IDENTITY, NULL); png_destroy_write_struct(&png_writer, &png_info); fclose(f); } static void * test_alloc(void* pUserData, size_t size, size_t alignment, VkSystemAllocType allocType) { return malloc(size); } static void test_free(void* pUserData, void* pMem) { free(pMem); } #define GLSL(src) "#version 330\n" #src static void create_pipeline(VkDevice device, VkPipeline *pipeline, VkPipelineLayout pipeline_layout) { VkPipelineIaStateCreateInfo ia_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_IA_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, .disableVertexReuse = false, .primitiveRestartEnable = false, .primitiveRestartIndex = 0 }; VkShader vs = GLSL_VK_SHADER(device, VERTEX, layout(location = 0) in vec4 a_position; layout(location = 1) in vec4 a_color; layout(set = 0, binding = 0) uniform block1 { vec4 color; } u1; layout(set = 0, binding = 1) uniform block2 { vec4 color; } u2; layout(set = 1, binding = 0) uniform block3 { vec4 color; } u3; out vec4 v_color; void main() { gl_Position = a_position; v_color = a_color + u1.color + u2.color + u3.color; } ); VkShader fs = GLSL_VK_SHADER(device, FRAGMENT, out vec4 f_color; in vec4 v_color; layout(set = 0, binding = 0) uniform sampler2D tex; void main() { f_color = v_color + texture(tex, vec2(0.1, 0.1)); } ); VkPipelineShaderStageCreateInfo vs_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = &ia_create_info, .shader = { .stage = VK_SHADER_STAGE_VERTEX, .shader = vs, .linkConstBufferCount = 0, .pLinkConstBufferInfo = NULL, .pSpecializationInfo = NULL } }; VkPipelineShaderStageCreateInfo fs_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = &vs_create_info, .shader = { .stage = VK_SHADER_STAGE_FRAGMENT, .shader = fs, .linkConstBufferCount = 0, .pLinkConstBufferInfo = NULL, .pSpecializationInfo = NULL } }; VkPipelineVertexInputCreateInfo vi_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO, .pNext = &fs_create_info, .bindingCount = 2, .pVertexBindingDescriptions = (VkVertexInputBindingDescription[]) { { .binding = 0, .strideInBytes = 16, .stepRate = VK_VERTEX_INPUT_STEP_RATE_VERTEX }, { .binding = 1, .strideInBytes = 0, .stepRate = VK_VERTEX_INPUT_STEP_RATE_VERTEX } }, .attributeCount = 2, .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[]) { { .location = 0, .binding = 0, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offsetInBytes = 0 }, { .location = 1, .binding = 1, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offsetInBytes = 0 } } }; VkPipelineRsStateCreateInfo rs_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RS_STATE_CREATE_INFO, .pNext = &vi_create_info, .depthClipEnable = true, .rasterizerDiscardEnable = false, .fillMode = VK_FILL_MODE_SOLID, .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_CCW }; VkPipelineCbStateCreateInfo cb_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO, .pNext = &rs_create_info, .attachmentCount = 1, .pAttachments = (VkPipelineCbAttachmentState []) { { .channelWriteMask = VK_CHANNEL_A_BIT | VK_CHANNEL_R_BIT | VK_CHANNEL_G_BIT | VK_CHANNEL_B_BIT }, } }; vkCreateGraphicsPipeline(device, &(VkGraphicsPipelineCreateInfo) { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &cb_create_info, .flags = 0, .layout = pipeline_layout }, pipeline); vkDestroyObject(device, VK_OBJECT_TYPE_SHADER, fs); vkDestroyObject(device, VK_OBJECT_TYPE_SHADER, vs); } static void test_timestamp(VkDevice device, VkQueue queue) { VkBuffer buffer; vkCreateBuffer(device, &(VkBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = 1024, .usage = VK_BUFFER_USAGE_GENERAL, .flags = 0 }, &buffer); VkMemoryRequirements buffer_requirements; size_t size = sizeof(buffer_requirements); vkGetObjectInfo(device, VK_OBJECT_TYPE_BUFFER, buffer, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &buffer_requirements); VkDeviceMemory mem; vkAllocMemory(device, &(VkMemoryAllocInfo) { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, .allocationSize = buffer_requirements.size, .memProps = VK_MEMORY_PROPERTY_HOST_DEVICE_COHERENT_BIT, .memPriority = VK_MEMORY_PRIORITY_NORMAL }, &mem); void *map; vkMapMemory(device, mem, 0, buffer_requirements.size, 0, &map); memset(map, 0x11, buffer_requirements.size); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_BUFFER, buffer, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 0); VkCmdBuffer cmdBuffer; vkCreateCommandBuffer(device, &(VkCmdBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO, .queueNodeIndex = 0, .flags = 0 }, &cmdBuffer); vkBeginCommandBuffer(cmdBuffer, &(VkCmdBufferBeginInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, .flags = 0 }); vkCmdWriteTimestamp(cmdBuffer, VK_TIMESTAMP_TYPE_TOP, buffer, 0); vkCmdWriteTimestamp(cmdBuffer, VK_TIMESTAMP_TYPE_BOTTOM, buffer, 8); vkEndCommandBuffer(cmdBuffer); vkQueueSubmit(queue, 1, &cmdBuffer, 0); vkQueueWaitIdle(queue); vkDestroyObject(device, VK_OBJECT_TYPE_BUFFER, buffer); vkDestroyObject(device, VK_OBJECT_TYPE_COMMAND_BUFFER, cmdBuffer); uint64_t *results = map; printf("top timestamp: %20ld (%016lx)\n", results[0], results[0]); printf("bottom timestamp: %20ld (%016lx)\n", results[1], results[1]); vkUnmapMemory(device, mem); vkFreeMemory(device, mem); } static void test_buffer_copy(VkDevice device, VkQueue queue) { /* We'll test copying 1000k buffers */ const int buffer_size = 1024000; VkBufferCreateInfo buffer_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = buffer_size, .usage = VK_BUFFER_USAGE_GENERAL, .flags = 0 }; VkBuffer buffer1, buffer2; vkCreateBuffer(device, &buffer_info, &buffer1); vkCreateBuffer(device, &buffer_info, &buffer2); VkMemoryRequirements buffer_requirements; size_t size = sizeof(buffer_requirements); vkGetObjectInfo(device, VK_OBJECT_TYPE_BUFFER, buffer1, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &buffer_requirements); const int memory_size = buffer_requirements.size * 2; VkDeviceMemory mem; vkAllocMemory(device, &(VkMemoryAllocInfo) { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, .allocationSize = memory_size, .memProps = VK_MEMORY_PROPERTY_HOST_DEVICE_COHERENT_BIT, .memPriority = VK_MEMORY_PRIORITY_NORMAL }, &mem); void *map; vkMapMemory(device, mem, 0, buffer_requirements.size * 2, 0, &map); /* Fill the first buffer_size of the memory with a pattern */ uint32_t *map32 = map; for (unsigned i = 0; i < buffer_size / sizeof(*map32); i++) map32[i] = i; /* Fill the rest with 0 */ memset((char *)map + buffer_size, 0, memory_size - buffer_size); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_BUFFER, buffer1, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 0); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_BUFFER, buffer2, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, buffer_requirements.size); VkCmdBuffer cmdBuffer; vkCreateCommandBuffer(device, &(VkCmdBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO, .queueNodeIndex = 0, .flags = 0 }, &cmdBuffer); vkBeginCommandBuffer(cmdBuffer, &(VkCmdBufferBeginInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, .flags = 0 }); vkCmdCopyBuffer(cmdBuffer, buffer1, buffer2, 1, &(VkBufferCopy) { .srcOffset = 0, .destOffset = 0, .copySize = buffer_size, }); vkEndCommandBuffer(cmdBuffer); vkQueueSubmit(queue, 1, &cmdBuffer, 0); vkQueueWaitIdle(queue); vkDestroyObject(device, VK_OBJECT_TYPE_BUFFER, buffer1); vkDestroyObject(device, VK_OBJECT_TYPE_BUFFER, buffer2); vkDestroyObject(device, VK_OBJECT_TYPE_COMMAND_BUFFER, cmdBuffer); uint32_t *map32_2 = map + buffer_requirements.size; for (unsigned i = 0; i < buffer_size / sizeof(*map32); i++) { if (map32[i] != map32_2[i]) { printf("buffer mismatch at dword %d: found 0x%x, expected 0x%x\n", i, map32_2[i], map32[i]); } } vkUnmapMemory(device, mem); vkFreeMemory(device, mem); } static void test_formats(VkDevice device, VkQueue queue) { VkFormatProperties properties; size_t size = sizeof(properties); uint32_t f; static const char *features[] = { "sampled_image", "storage_image", "storage_image_atomic", "uniform_texel_buffer", "storage_texel_buffer", "storage_texel_buffer_atomic", "vertex_buffer", "color_attachment", "color_attachment_blend", "depth_stencil_attachment", "conversion" }; VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; vkGetFormatInfo(device, format, VK_FORMAT_INFO_TYPE_PROPERTIES, &size, &properties); printf("format 0x%x:\n", format); printf(" linear tiling features (0x%x):", properties.linearTilingFeatures); for_each_bit(f, properties.linearTilingFeatures) printf(" %s", features[f]); printf("\n optimal tiling features (0x%x):", properties.optimalTilingFeatures); for_each_bit(f, properties.optimalTilingFeatures) printf(" %s", features[f]); printf("\n"); } #define TEST_DEPTH_FLAG 0x01 struct test_context { uint32_t width, height; VkDevice device; VkQueue queue; VkCmdBuffer cmdBuffer; VkPipeline pipeline; VkImage rt; VkImage ds; VkBuffer vertex_buffer; VkBuffer image_buffer; VkDeviceMemory mem; void *map; void *rt_map; void *vertex_map; void *image_map; VkDynamicVpState vp_state; VkDynamicRsState rs_state; VkDynamicDsState ds_state; VkDynamicCbState cb_state; VkColorAttachmentView rt_view; VkDepthStencilView ds_view; uint32_t rt_size; VkFramebuffer framebuffer; VkRenderPass pass; }; static void test_prepare(struct test_context *ctx, VkDevice device, VkQueue queue, uint32_t flags) { ctx->device = device; ctx->queue = queue; vkCreateCommandBuffer(ctx->device, &(VkCmdBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO, .queueNodeIndex = 0, .flags = 0 }, &ctx->cmdBuffer); vkCreateBuffer(ctx->device, &(VkBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = 4096, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .flags = 0 }, &ctx->vertex_buffer); VkMemoryRequirements vb_requirements; size_t size = sizeof(vb_requirements); vkGetObjectInfo(ctx->device, VK_OBJECT_TYPE_BUFFER, ctx->vertex_buffer, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &vb_requirements); vkCreateImage(ctx->device, &(VkImageCreateInfo) { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, .extent = { .width = ctx->width, .height = ctx->height, .depth = 1 }, .mipLevels = 1, .arraySize = 1, .samples = 1, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .flags = 0, }, &ctx->rt); VkMemoryRequirements rt_requirements; size = sizeof(rt_requirements); vkGetObjectInfo(ctx->device, VK_OBJECT_TYPE_IMAGE, ctx->rt, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &rt_requirements); ctx->rt_size = rt_requirements.size; VkDepthStencilBindInfo *ds_attachment; VkDepthStencilBindInfo ds_bind_info; VkMemoryRequirements ds_requirements; if (flags & TEST_DEPTH_FLAG) { vkCreateImage(ctx->device, &(VkImageCreateInfo) { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_D24_UNORM, .extent = { .width = ctx->width, .height = ctx->height, .depth = 1 }, .mipLevels = 1, .arraySize = 1, .samples = 1, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_BIT, .flags = 0, }, &ctx->ds); size = sizeof(ds_requirements); vkGetObjectInfo(ctx->device, VK_OBJECT_TYPE_IMAGE, ctx->ds, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &ds_requirements); } else { ds_requirements.size = 0; } vkCreateBuffer(ctx->device, &(VkBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = ctx->width * ctx->height * 4, .usage = VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT, .flags = 0 }, &ctx->image_buffer); VkMemoryRequirements ib_requirements; size = sizeof(ib_requirements); vkGetObjectInfo(ctx->device, VK_OBJECT_TYPE_BUFFER, ctx->image_buffer, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &ib_requirements); size_t mem_size = align_u32(vb_requirements.size, 4096) + align_u32(rt_requirements.size, 4096) + align_u32(ds_requirements.size, 4096) + align_u32(ib_requirements.size, 4096); printf("mem size %ld\n", mem_size); vkAllocMemory(ctx->device, &(VkMemoryAllocInfo) { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, .allocationSize = mem_size, .memProps = VK_MEMORY_PROPERTY_HOST_DEVICE_COHERENT_BIT, .memPriority = VK_MEMORY_PRIORITY_NORMAL }, &ctx->mem); vkMapMemory(ctx->device, ctx->mem, 0, mem_size, 0, &ctx->map); memset(ctx->map, 0, mem_size); uint32_t offset = 0; printf("vb: %ldb at %d\n", vb_requirements.size, offset); vkQueueBindObjectMemory(ctx->queue, VK_OBJECT_TYPE_BUFFER, ctx->vertex_buffer, 0, ctx->mem, offset); ctx->vertex_map = ctx->map + offset; offset = align_u32(offset + vb_requirements.size, 4096); printf("rt: %ldb at %d\n", rt_requirements.size, offset); vkQueueBindObjectMemory(ctx->queue, VK_OBJECT_TYPE_IMAGE, ctx->rt, 0, ctx->mem, offset); ctx->rt_map = ctx->map + offset; offset = align_u32(offset + rt_requirements.size, 4096); if (flags & TEST_DEPTH_FLAG) { printf("ds: %ldb at %d\n", ds_requirements.size, offset); vkQueueBindObjectMemory(ctx->queue, VK_OBJECT_TYPE_IMAGE, ctx->ds, 0, ctx->mem, offset); offset = align_u32(offset + ds_requirements.size, 4096); } printf("ib: %ldb at %d\n", ib_requirements.size, offset); vkQueueBindObjectMemory(ctx->queue, VK_OBJECT_TYPE_BUFFER, ctx->image_buffer, 0, ctx->mem, offset); ctx->image_map = ctx->map + offset; offset = align_u32(offset + ib_requirements.size, 4096); vkCreateDynamicViewportState(ctx->device, &(VkDynamicVpStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_VP_STATE_CREATE_INFO, .viewportAndScissorCount = 1, .pViewports = (VkViewport[]) { { .originX = 0, .originY = 0, .width = ctx->width, .height = ctx->height, .minDepth = -1, .maxDepth = 1 }, }, .pScissors = (VkRect[]) { { { 0, 0 }, { ctx->width, ctx->height } }, } }, &ctx->vp_state); vkCreateDynamicRasterState(ctx->device, &(VkDynamicRsStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_RS_STATE_CREATE_INFO, }, &ctx->rs_state); vkCreateDynamicDepthStencilState(ctx->device, &(VkDynamicDsStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_DS_STATE_CREATE_INFO, }, &ctx->ds_state); vkCreateDynamicColorBlendState(ctx->device, &(VkDynamicCbStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_CB_STATE_CREATE_INFO }, &ctx->cb_state); vkCreateColorAttachmentView(ctx->device, &(VkColorAttachmentViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_COLOR_ATTACHMENT_VIEW_CREATE_INFO, .image = ctx->rt, .format = VK_FORMAT_R8G8B8A8_UNORM, .mipLevel = 0, .baseArraySlice = 0, .arraySize = 1, .msaaResolveImage = 0, .msaaResolveSubResource = { 0, } }, &ctx->rt_view); if (flags & TEST_DEPTH_FLAG) { vkCreateDepthStencilView(ctx->device, &(VkDepthStencilViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_DEPTH_STENCIL_VIEW_CREATE_INFO, .image = ctx->ds, .mipLevel = 0, .baseArraySlice = 0, .arraySize = 1, .msaaResolveImage = 0, .msaaResolveSubResource = { 0, } }, &ctx->ds_view); ds_bind_info.view = ctx->ds_view; ds_bind_info.layout = 0; ds_attachment = &ds_bind_info; } else { ds_attachment = NULL; } vkCreateFramebuffer(ctx->device, &(VkFramebufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachments = (VkColorAttachmentBindInfo[]) { { .view = ctx->rt_view, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } }, .pDepthStencilAttachment = ds_attachment, .sampleCount = 1, .width = ctx->width, .height = ctx->height, .layers = 1 }, &ctx->framebuffer); vkCreateRenderPass(ctx->device, &(VkRenderPassCreateInfo) { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .renderArea = { { 0, 0 }, { ctx->width, ctx->height } }, .colorAttachmentCount = 1, .extent = { }, .sampleCount = 1, .layers = 1, .pColorFormats = (VkFormat[]) { VK_FORMAT_R8G8B8A8_UNORM }, .pColorLayouts = (VkImageLayout[]) { VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }, .pColorLoadOps = (VkAttachmentLoadOp[]) { VK_ATTACHMENT_LOAD_OP_CLEAR }, .pColorStoreOps = (VkAttachmentStoreOp[]) { VK_ATTACHMENT_STORE_OP_STORE }, .pColorLoadClearValues = (VkClearColor[]) { { .color = { .floatColor = { 0.2, 0.2, 0.2, 1.0 } }, .useRawValue = false } }, .depthStencilFormat = VK_FORMAT_D24_UNORM, .depthStencilLayout = 0, .depthLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .depthLoadClearValue = 0.5, .depthStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, }, &ctx->pass); vkBeginCommandBuffer(ctx->cmdBuffer, &(VkCmdBufferBeginInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, .flags = 0 }); vkCmdBeginRenderPass(ctx->cmdBuffer, &(VkRenderPassBegin) { .renderPass = ctx->pass, .framebuffer = ctx->framebuffer }); } static void test_finish(struct test_context *ctx) { vkCmdEndRenderPass(ctx->cmdBuffer, ctx->pass); VkBufferImageCopy copy = { .bufferOffset = 0, .imageSubresource = { .aspect = VK_IMAGE_ASPECT_COLOR, .mipLevel = 0, .arraySlice = 0, }, .imageOffset = { .x = 0, .y = 0, .z = 0 }, .imageExtent = { .width = ctx->width, .height = ctx->height, .depth = 1 }, }; vkCmdCopyImageToBuffer(ctx->cmdBuffer, ctx->rt, VK_IMAGE_LAYOUT_GENERAL, ctx->image_buffer, 1, ©); vkEndCommandBuffer(ctx->cmdBuffer); vkQueueSubmit(ctx->queue, 1, &ctx->cmdBuffer, 0); vkQueueWaitIdle(ctx->queue); write_png("vk-map.png", ctx->width, ctx->height, 1024, ctx->rt_map); write_png("vk-copy.png", ctx->width, ctx->height, 1024, ctx->image_map); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_COMMAND_BUFFER, ctx->cmdBuffer); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_PIPELINE, ctx->pipeline); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_IMAGE, ctx->rt); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_BUFFER, ctx->vertex_buffer); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_BUFFER, ctx->image_buffer); vkUnmapMemory(ctx->device, ctx->mem); vkFreeMemory(ctx->device, ctx->mem); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_DYNAMIC_VP_STATE, ctx->vp_state); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_DYNAMIC_RS_STATE, ctx->rs_state); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_DYNAMIC_CB_STATE, ctx->cb_state); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_COLOR_ATTACHMENT_VIEW, ctx->rt_view); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_FRAMEBUFFER, ctx->framebuffer); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_RENDER_PASS, ctx->pass); } static void test_create_solid_color_pipeline(struct test_context *ctx) { VkPipelineIaStateCreateInfo ia_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_IA_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, .disableVertexReuse = false, .primitiveRestartEnable = false, .primitiveRestartIndex = 0 }; VkShader vs = GLSL_VK_SHADER(ctx->device, VERTEX, layout(location = 0) in vec4 a_position; layout(location = 1) in vec4 a_color; out vec4 v_color; void main() { gl_Position = a_position; v_color = a_color; } ); VkShader fs = GLSL_VK_SHADER(ctx->device, FRAGMENT, out vec4 f_color; in vec4 v_color; void main() { f_color = v_color; } ); VkPipelineShaderStageCreateInfo vs_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = &ia_create_info, .shader = { .stage = VK_SHADER_STAGE_VERTEX, .shader = vs, .linkConstBufferCount = 0, .pLinkConstBufferInfo = NULL, .pSpecializationInfo = NULL } }; VkPipelineShaderStageCreateInfo fs_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = &vs_create_info, .shader = { .stage = VK_SHADER_STAGE_FRAGMENT, .shader = fs, .linkConstBufferCount = 0, .pLinkConstBufferInfo = NULL, .pSpecializationInfo = NULL } }; VkPipelineVertexInputCreateInfo vi_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_CREATE_INFO, .pNext = &fs_create_info, .bindingCount = 2, .pVertexBindingDescriptions = (VkVertexInputBindingDescription[]) { { .binding = 0, .strideInBytes = 16, .stepRate = VK_VERTEX_INPUT_STEP_RATE_VERTEX }, { .binding = 1, .strideInBytes = 16, .stepRate = VK_VERTEX_INPUT_STEP_RATE_INSTANCE } }, .attributeCount = 2, .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[]) { { .location = 0, .binding = 0, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offsetInBytes = 0 }, { .location = 1, .binding = 1, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offsetInBytes = 0 } } }; VkPipelineRsStateCreateInfo rs_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RS_STATE_CREATE_INFO, .pNext = &vi_create_info, .depthClipEnable = true, .rasterizerDiscardEnable = false, .fillMode = VK_FILL_MODE_SOLID, .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_CCW }; VkPipelineDsStateCreateInfo ds_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DS_STATE_CREATE_INFO, .pNext = &rs_create_info, .format = VK_FORMAT_D24_UNORM, .depthTestEnable = true, .depthWriteEnable = true, .depthCompareOp = VK_COMPARE_OP_GREATER }; VkPipelineCbStateCreateInfo cb_create_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_CB_STATE_CREATE_INFO, .pNext = &ds_create_info, .attachmentCount = 1, .pAttachments = (VkPipelineCbAttachmentState []) { { .channelWriteMask = VK_CHANNEL_A_BIT | VK_CHANNEL_R_BIT | VK_CHANNEL_G_BIT | VK_CHANNEL_B_BIT }, } }; vkCreateGraphicsPipeline(ctx->device, &(VkGraphicsPipelineCreateInfo) { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = &cb_create_info, .flags = 0, .layout = VK_NULL_HANDLE }, &ctx->pipeline); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_SHADER, fs); vkDestroyObject(ctx->device, VK_OBJECT_TYPE_SHADER, vs); } static void test_depth_stencil(VkDevice device, VkQueue queue) { struct test_context ctx; ctx.width = 256; ctx.height = 256; test_prepare(&ctx, device, queue, TEST_DEPTH_FLAG); test_create_solid_color_pipeline(&ctx); static const float vertex_data[] = { /* Triangle coordinates */ -0.5, -0.5, 0.5, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 1.0, /* Triangle coordinates */ -0.3, -0.3, 0.0, 1.0, 0.7, -0.3, 0.0, 1.0, 0.2, 0.7, 0.8, 1.0, /* Color */ 1.0, 1.0, 0.2, 1.0, 0.2, 0.2, 1.0, 1.0, }; memcpy(ctx.vertex_map, vertex_data, sizeof(vertex_data)); vkCmdBindVertexBuffers(ctx.cmdBuffer, 0, 2, (VkBuffer[]) { ctx.vertex_buffer, ctx.vertex_buffer }, (VkDeviceSize[]) { 0, 6 * 4 * sizeof(float) }); vkCmdBindPipeline(ctx.cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx.pipeline); vkCmdBindDynamicStateObject(ctx.cmdBuffer, VK_STATE_BIND_POINT_VIEWPORT, ctx.vp_state); vkCmdBindDynamicStateObject(ctx.cmdBuffer, VK_STATE_BIND_POINT_RASTER, ctx.rs_state); vkCmdBindDynamicStateObject(ctx.cmdBuffer, VK_STATE_BIND_POINT_DEPTH_STENCIL, ctx.ds_state); vkCmdBindDynamicStateObject(ctx.cmdBuffer, VK_STATE_BIND_POINT_COLOR_BLEND, ctx.cb_state); vkCmdDraw(ctx.cmdBuffer, 0, 3, 0, 1); vkCmdDraw(ctx.cmdBuffer, 3, 3, 1, 1); test_finish(&ctx); } static void test_triangle(VkDevice device, VkQueue queue) { uint32_t count; VkCmdBuffer cmdBuffer; vkCreateCommandBuffer(device, &(VkCmdBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO, .queueNodeIndex = 0, .flags = 0 }, &cmdBuffer); VkDescriptorSetLayout set_layout[2]; vkCreateDescriptorSetLayout(device, &(VkDescriptorSetLayoutCreateInfo) { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .count = 3, .pBinding = (VkDescriptorSetLayoutBinding[]) { { .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .count = 2, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .pImmutableSamplers = NULL }, { .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .count = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = NULL }, { .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, .count = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = NULL } } }, &set_layout[0]); vkCreateDescriptorSetLayout(device, &(VkDescriptorSetLayoutCreateInfo) { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .count = 1, .pBinding = (VkDescriptorSetLayoutBinding[]) { { .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .count = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .pImmutableSamplers = NULL } } }, &set_layout[1]); VkPipelineLayout pipeline_layout; vkCreatePipelineLayout(device, &(VkPipelineLayoutCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .descriptorSetCount = 2, .pSetLayouts = set_layout, }, &pipeline_layout); VkPipeline pipeline; create_pipeline(device, &pipeline, pipeline_layout); VkDescriptorSet set[2]; vkAllocDescriptorSets(device, 0 /* pool */, VK_DESCRIPTOR_SET_USAGE_STATIC, 2, set_layout, set, &count); VkBuffer buffer; vkCreateBuffer(device, &(VkBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = 1024, .usage = VK_BUFFER_USAGE_GENERAL, .flags = 0 }, &buffer); VkMemoryRequirements buffer_requirements; size_t size = sizeof(buffer_requirements); vkGetObjectInfo(device, VK_OBJECT_TYPE_BUFFER, buffer, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &buffer_requirements); int32_t width = 256, height = 256; VkImage rt; vkCreateImage(device, &(VkImageCreateInfo) { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, .extent = { .width = width, .height = height, .depth = 1 }, .mipLevels = 1, .arraySize = 1, .samples = 1, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .flags = 0, }, &rt); VkMemoryRequirements rt_requirements; size = sizeof(rt_requirements); vkGetObjectInfo(device, VK_OBJECT_TYPE_IMAGE, rt, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &rt_requirements); VkBuffer vertex_buffer; vkCreateBuffer(device, &(VkBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = 1024, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .flags = 0 }, &vertex_buffer); VkMemoryRequirements vb_requirements; size = sizeof(vb_requirements); vkGetObjectInfo(device, VK_OBJECT_TYPE_BUFFER, vertex_buffer, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &vb_requirements); VkBuffer image_buffer; vkCreateBuffer(device, &(VkBufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = width * height * 4, .usage = VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT, .flags = 0 }, &image_buffer); VkMemoryRequirements ib_requirements; size = sizeof(ib_requirements); vkGetObjectInfo(device, VK_OBJECT_TYPE_BUFFER, image_buffer, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS, &size, &ib_requirements); printf("buffer size: %lu, buffer alignment: %lu\n", buffer_requirements.size, buffer_requirements.alignment); printf("rt size: %lu, rt alignment: %lu\n", rt_requirements.size, rt_requirements.alignment); printf("vb size: %lu vb alignment: %lu\n", vb_requirements.size, vb_requirements.alignment); printf("ib size: %lu ib alignment: %lu\n", ib_requirements.size, ib_requirements.alignment); size_t mem_size = rt_requirements.size + ib_requirements.size + 2048 + 16 * 16 * 4; VkDeviceMemory mem; vkAllocMemory(device, &(VkMemoryAllocInfo) { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO, .allocationSize = mem_size, .memProps = VK_MEMORY_PROPERTY_HOST_DEVICE_COHERENT_BIT, .memPriority = VK_MEMORY_PRIORITY_NORMAL }, &mem); void *map; vkMapMemory(device, mem, 0, mem_size, 0, &map); memset(map, 192, mem_size); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_BUFFER, buffer, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 128); float color[12] = { 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.5 }; memcpy(map + 128 + 16, color, sizeof(color)); VkBufferView buffer_view[3]; vkCreateBufferView(device, &(VkBufferViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, .buffer = buffer, .viewType = VK_BUFFER_VIEW_TYPE_RAW, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = 16, .range = 64 }, &buffer_view[0]); vkCreateBufferView(device, &(VkBufferViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, .buffer = buffer, .viewType = VK_BUFFER_VIEW_TYPE_RAW, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = 32, .range = 64 }, &buffer_view[1]); vkCreateBufferView(device, &(VkBufferViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, .buffer = buffer, .viewType = VK_BUFFER_VIEW_TYPE_RAW, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = 48, .range = 64 }, &buffer_view[2]); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_BUFFER, vertex_buffer, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 1024); static const float vertex_data[] = { /* Triangle coordinates */ -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0, /* Color */ 1.0, 0.0, 0.0, 0.2, }; memcpy(map + 1024, vertex_data, sizeof(vertex_data)); VkDynamicVpState vp_state; vkCreateDynamicViewportState(device, &(VkDynamicVpStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_VP_STATE_CREATE_INFO, .viewportAndScissorCount = 2, .pViewports = (VkViewport[]) { { .originX = 0, .originY = 0, .width = width, .height = height, .minDepth = 0, .maxDepth = 1 }, { .originX = -10, .originY = -10, .width = 20, .height = 20, .minDepth = -1, .maxDepth = 1 }, }, .pScissors = (VkRect[]) { { { 0, 0 }, { width, height } }, { { 10, 10 }, { 236, 236 } } } }, &vp_state); VkDynamicRsState rs_state; vkCreateDynamicRasterState(device, &(VkDynamicRsStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_RS_STATE_CREATE_INFO, }, &rs_state); VkDynamicCbState cb_state; vkCreateDynamicColorBlendState(device, &(VkDynamicCbStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_DYNAMIC_CB_STATE_CREATE_INFO }, &cb_state); /* FIXME: Need to query memory info before binding to memory */ vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_IMAGE, rt, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 2048); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_BUFFER, image_buffer, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 2048 + rt_requirements.size); const uint32_t texture_width = 16, texture_height = 16; VkImage texture; vkCreateImage(device, &(VkImageCreateInfo) { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, .extent = { .width = texture_width, .height = texture_height, .depth = 1 }, .mipLevels = 1, .arraySize = 1, .samples = 1, .tiling = VK_IMAGE_TILING_LINEAR, .usage = VK_IMAGE_USAGE_SAMPLED_BIT, .flags = 0, }, &texture); vkQueueBindObjectMemory(queue, VK_OBJECT_TYPE_IMAGE, texture, 0, /* allocation index; for objects which need to bind to multiple mems */ mem, 2048 + 256 * 256 * 4); VkImageView image_view; vkCreateImageView(device, &(VkImageViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = texture, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = VK_FORMAT_R8G8B8A8_UNORM, .channels = { VK_CHANNEL_SWIZZLE_R, VK_CHANNEL_SWIZZLE_G, VK_CHANNEL_SWIZZLE_B, VK_CHANNEL_SWIZZLE_A }, .subresourceRange = { .aspect = VK_IMAGE_ASPECT_COLOR, .baseMipLevel = 0, .mipLevels = 1, .baseArraySlice = 0, .arraySize = 1 }, .minLod = 0 }, &image_view); VkSampler sampler; vkCreateSampler(device, &(VkSamplerCreateInfo) { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = VK_TEX_FILTER_LINEAR, .minFilter = VK_TEX_FILTER_LINEAR, .mipMode = VK_TEX_MIPMAP_MODE_NEAREST, .addressU = VK_TEX_ADDRESS_CLAMP, .addressV = VK_TEX_ADDRESS_CLAMP, .addressW = VK_TEX_ADDRESS_CLAMP, .mipLodBias = 0, .maxAnisotropy = 0, .compareOp = VK_COMPARE_OP_GREATER, .minLod = 0, .maxLod = 0, .borderColor = VK_BORDER_COLOR_TRANSPARENT_BLACK }, &sampler); vkUpdateDescriptors(device, set[0], 3, (const void * []) { &(VkUpdateBuffers) { .sType = VK_STRUCTURE_TYPE_UPDATE_BUFFERS, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .arrayIndex = 0, .binding = 0, .count = 2, .pBufferViews = (VkBufferViewAttachInfo[]) { { .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_ATTACH_INFO, .view = buffer_view[0] }, { .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_ATTACH_INFO, .view = buffer_view[1] } } }, &(VkUpdateImages) { .sType = VK_STRUCTURE_TYPE_UPDATE_IMAGES, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .binding = 2, .count = 1, .pImageViews = (VkImageViewAttachInfo[]) { { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_ATTACH_INFO, .view = image_view, .layout = VK_IMAGE_LAYOUT_GENERAL, } } }, &(const VkUpdateSamplers) { .sType = VK_STRUCTURE_TYPE_UPDATE_SAMPLERS, .binding = 3, .count = 1, .pSamplers = (const VkSampler[]) { sampler } } }); vkUpdateDescriptors(device, set[1], 1, (const void * []) { &(VkUpdateBuffers) { .sType = VK_STRUCTURE_TYPE_UPDATE_BUFFERS, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .arrayIndex = 0, .count = 1, .pBufferViews = (VkBufferViewAttachInfo[]) { { .sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_ATTACH_INFO, .view = buffer_view[2] } } } }); VkColorAttachmentView view; vkCreateColorAttachmentView(device, &(VkColorAttachmentViewCreateInfo) { .sType = VK_STRUCTURE_TYPE_COLOR_ATTACHMENT_VIEW_CREATE_INFO, .image = rt, .format = VK_FORMAT_R8G8B8A8_UNORM, .mipLevel = 0, .baseArraySlice = 0, .arraySize = 1, .msaaResolveImage = 0, .msaaResolveSubResource = { 0, } }, &view); VkFramebuffer framebuffer; vkCreateFramebuffer(device, &(VkFramebufferCreateInfo) { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .colorAttachmentCount = 1, .pColorAttachments = (VkColorAttachmentBindInfo[]) { { .view = view, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL } }, .pDepthStencilAttachment = NULL, .sampleCount = 1, .width = width, .height = height, .layers = 1 }, &framebuffer); VkRenderPass pass; vkCreateRenderPass(device, &(VkRenderPassCreateInfo) { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .renderArea = { { 0, 0 }, { width, height } }, .colorAttachmentCount = 1, .extent = { }, .sampleCount = 1, .layers = 1, .pColorFormats = (VkFormat[]) { VK_FORMAT_R8G8B8A8_UNORM }, .pColorLayouts = (VkImageLayout[]) { VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }, .pColorLoadOps = (VkAttachmentLoadOp[]) { VK_ATTACHMENT_LOAD_OP_CLEAR }, .pColorStoreOps = (VkAttachmentStoreOp[]) { VK_ATTACHMENT_STORE_OP_STORE }, .pColorLoadClearValues = (VkClearColor[]) { { .color = { .floatColor = { 1.0, 0.0, 0.0, 1.0 } }, .useRawValue = false } }, .depthStencilFormat = VK_FORMAT_UNDEFINED, }, &pass); VkQueryPool query_pool; vkCreateQueryPool(device, &(VkQueryPoolCreateInfo) { .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, .queryType = VK_QUERY_TYPE_OCCLUSION, .slots = 4, .pipelineStatistics = 0 }, &query_pool); vkBeginCommandBuffer(cmdBuffer, &(VkCmdBufferBeginInfo) { .sType = VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO, .flags = 0 }); vkCmdBeginRenderPass(cmdBuffer, &(VkRenderPassBegin) { .renderPass = pass, .framebuffer = framebuffer }); vkCmdBindVertexBuffers(cmdBuffer, 0, 2, (VkBuffer[]) { vertex_buffer, vertex_buffer }, (VkDeviceSize[]) { 0, 3 * 4 * sizeof(float) }); vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 1, &set[0], 0, NULL); vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, 1, 1, &set[1], 0, NULL); vkCmdBindDynamicStateObject(cmdBuffer, VK_STATE_BIND_POINT_VIEWPORT, vp_state); vkCmdBindDynamicStateObject(cmdBuffer, VK_STATE_BIND_POINT_RASTER, rs_state); vkCmdBindDynamicStateObject(cmdBuffer, VK_STATE_BIND_POINT_COLOR_BLEND, cb_state); vkCmdBeginQuery(cmdBuffer, query_pool, 0 /*slot*/, 0 /* flags */); vkCmdDraw(cmdBuffer, 0, 3, 0, 1); vkCmdEndQuery(cmdBuffer, query_pool, 0); vkCmdEndRenderPass(cmdBuffer, pass); vkCmdCopyQueryPoolResults(cmdBuffer, query_pool, 0, 1, buffer, 16, 8, VK_QUERY_RESULT_WAIT_BIT | VK_QUERY_RESULT_64_BIT); VkBufferImageCopy copy = { .bufferOffset = 0, .imageSubresource = { .aspect = VK_IMAGE_ASPECT_COLOR, .mipLevel = 0, .arraySlice = 0, }, .imageOffset = { .x = 0, .y = 0, .z = 0 }, .imageExtent = { .width = width, .height = height, .depth = 1 }, }; vkCmdCopyImageToBuffer(cmdBuffer, rt, VK_IMAGE_LAYOUT_GENERAL, image_buffer, 1, ©); vkEndCommandBuffer(cmdBuffer); vkQueueSubmit(queue, 1, &cmdBuffer, 0); vkQueueWaitIdle(queue); /* Result gets written to buffer at offset 0. The buffer is bound to the * memory object at offset 128 */ uint64_t *results = map + 128; uint64_t get_result; size = sizeof(get_result); vkGetQueryPoolResults(device, query_pool, 0, 1, &size, &get_result, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT); printf("oc query (copy): %20ld (%016lx)\n", results[2], results[2]); printf("oc query (get): %20ld (%016lx)\n", get_result, get_result); write_png("vk-map.png", width, height, 1024, map + 2048); write_png("vk-copy.png", width, height, 1024, map + 2048 + rt_requirements.size); vkDestroyObject(device, VK_OBJECT_TYPE_IMAGE, texture); vkDestroyObject(device, VK_OBJECT_TYPE_IMAGE, rt); vkDestroyObject(device, VK_OBJECT_TYPE_BUFFER, buffer); vkDestroyObject(device, VK_OBJECT_TYPE_COMMAND_BUFFER, cmdBuffer); vkDestroyObject(device, VK_OBJECT_TYPE_PIPELINE, pipeline); vkDestroyObject(device, VK_OBJECT_TYPE_QUERY_POOL, query_pool); } int main(int argc, char *argv[]) { VkInstance instance; vkCreateInstance(&(VkInstanceCreateInfo) { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pAllocCb = &(VkAllocCallbacks) { .pUserData = NULL, .pfnAlloc = test_alloc, .pfnFree = test_free }, .pAppInfo = &(VkApplicationInfo) { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pAppName = "vk", .apiVersion = 1 } }, &instance); uint32_t count = 1; VkPhysicalDevice physicalDevices[1]; vkEnumeratePhysicalDevices(instance, &count, physicalDevices); printf("%d physical devices\n", count); VkPhysicalDeviceProperties properties; size_t size = sizeof(properties); vkGetPhysicalDeviceInfo(physicalDevices[0], VK_PHYSICAL_DEVICE_INFO_TYPE_PROPERTIES, &size, &properties); printf("vendor id %04x, device name %s\n", properties.vendorId, properties.deviceName); VkDevice device; vkCreateDevice(physicalDevices[0], &(VkDeviceCreateInfo) { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueRecordCount = 1, .pRequestedQueues = &(VkDeviceQueueCreateInfo) { .queueNodeIndex = 0, .queueCount = 1 } }, &device); VkQueue queue; vkGetDeviceQueue(device, 0, 0, &queue); if (argc > 1 && strcmp(argv[1], "timestamp") == 0) { test_timestamp(device, queue); } else if (argc > 1 && strcmp(argv[1], "formats") == 0) { test_formats(device, queue); } else if (argc > 1 && strcmp(argv[1], "buffer-copy") == 0) { test_buffer_copy(device, queue); } else if (argc > 1 && strcmp(argv[1], "depth-stencil") == 0) { test_depth_stencil(device, queue); } else { test_triangle(device, queue); } vkDestroyDevice(device); vkDestroyInstance(instance); return 0; }