VkImageView null_image_view(vk::command_buffer &cmd) { if (g_null_image_view) return g_null_image_view->value; g_null_texture.reset(new image(g_current_renderer, get_memory_mapping(g_current_renderer.gpu()).device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_IMAGE_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, 4, 4, 1, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0)); g_null_image_view.reset(new image_view(g_current_renderer, g_null_texture->value, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1})); // Initialize memory to transparent black VkClearColorValue clear_color = {}; VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; change_image_layout(cmd, g_null_texture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range); vkCmdClearColorImage(cmd, g_null_texture->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &range); // Prep for shader access change_image_layout(cmd, g_null_texture.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range); return g_null_image_view->value; }
bool ObjectCache::Initialize() { if (!CreateDescriptorSetLayouts()) return false; if (!CreatePipelineLayouts()) return false; if (!CreateUtilityShaderVertexFormat()) return false; if (!CreateStaticSamplers()) return false; m_utility_shader_vertex_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1024 * 1024, 4 * 1024 * 1024); m_utility_shader_uniform_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 1024, 4 * 1024 * 1024); if (!m_utility_shader_vertex_buffer || !m_utility_shader_uniform_buffer) return false; m_dummy_texture = Texture2D::Create(1, 1, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); m_dummy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkClearColorValue clear_color = {}; VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), m_dummy_texture->GetImage(), m_dummy_texture->GetLayout(), &clear_color, 1, &clear_range); m_dummy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); return true; }
bool FramebufferManager::CreateEFBFramebuffer() { m_efb_width = static_cast<u32>(std::max(Renderer::GetTargetWidth(), 1)); m_efb_height = static_cast<u32>(std::max(Renderer::GetTargetHeight(), 1)); m_efb_layers = (g_ActiveConfig.iStereoMode != STEREO_OFF) ? 2 : 1; INFO_LOG(VIDEO, "EFB size: %ux%ux%u", m_efb_width, m_efb_height, m_efb_layers); // Update the static variable in the base class. Why does this even exist? FramebufferManagerBase::m_EFBLayers = m_efb_layers; // Allocate EFB render targets m_efb_color_texture = Texture2D::Create(m_efb_width, m_efb_height, 1, m_efb_layers, EFB_COLOR_TEXTURE_FORMAT, m_efb_samples, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); // We need a second texture to swap with for changing pixel formats m_efb_convert_color_texture = Texture2D::Create(m_efb_width, m_efb_height, 1, m_efb_layers, EFB_COLOR_TEXTURE_FORMAT, m_efb_samples, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); m_efb_depth_texture = Texture2D::Create( m_efb_width, m_efb_height, 1, m_efb_layers, EFB_DEPTH_TEXTURE_FORMAT, m_efb_samples, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); if (!m_efb_color_texture || !m_efb_convert_color_texture || !m_efb_depth_texture) return false; // Create resolved textures if MSAA is on if (m_efb_samples != VK_SAMPLE_COUNT_1_BIT) { m_efb_resolve_color_texture = Texture2D::Create( m_efb_width, m_efb_height, 1, m_efb_layers, EFB_COLOR_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); m_efb_resolve_depth_texture = Texture2D::Create( m_efb_width, m_efb_height, 1, m_efb_layers, EFB_DEPTH_AS_COLOR_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); if (!m_efb_resolve_color_texture || !m_efb_resolve_depth_texture) return false; VkImageView attachment = m_efb_resolve_depth_texture->GetView(); VkFramebufferCreateInfo framebuffer_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, m_depth_resolve_render_pass, 1, &attachment, m_efb_width, m_efb_height, m_efb_layers }; VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, &m_depth_resolve_framebuffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); return false; } } VkImageView framebuffer_attachments[] = { m_efb_color_texture->GetView(), m_efb_depth_texture->GetView(), }; VkFramebufferCreateInfo framebuffer_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, m_efb_load_render_pass, static_cast<u32>(ArraySize(framebuffer_attachments)), framebuffer_attachments, m_efb_width, m_efb_height, m_efb_layers }; VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, &m_efb_framebuffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); return false; } // Create second framebuffer for format conversions framebuffer_attachments[0] = m_efb_convert_color_texture->GetView(); res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, &m_efb_convert_framebuffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); return false; } // Transition to state that can be used to clear m_efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); m_efb_depth_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Clear the contents of the buffers. static const VkClearColorValue clear_color = { { 0.0f, 0.0f, 0.0f, 0.0f } }; static const VkClearDepthStencilValue clear_depth = { 0.0f, 0 }; VkImageSubresourceRange clear_color_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, m_efb_layers }; VkImageSubresourceRange clear_depth_range = { VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, m_efb_layers }; vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_efb_color_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &clear_color_range); vkCmdClearDepthStencilImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), m_efb_depth_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_depth, 1, &clear_depth_range); // Transition to color attachment state ready for rendering. m_efb_color_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); m_efb_depth_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); return true; }
bool fill_present_command_buffer(VkCommandBuffer command_buffer, VkImage image) { static VkCommandBufferBeginInfo cmd_buffer_begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType nullptr, // const void *pNext VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, // VkCommandBufferUsageFlags flags nullptr // const VkCommandBufferInheritanceInfo *pInheritanceInfo }; static VkClearColorValue clear_color = { {1.0f, 0.8f, 0.7f, 0.0f} }; static VkImageSubresourceRange image_subresource_range = { VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask 0, // uint32_t baseMipLevel 1, // uint32_t levelCount 0, // uint32_t baseArrayLayer 1 // uint32_t layerCount }; static VkImageMemoryBarrier barrier_from_present_to_clear = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void *pNext VK_ACCESS_MEMORY_READ_BIT, // VkAccessFlags srcAccessMask VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout vk_globals::present_queue.index, // uint32_t srcQueueFamilyIndex vk_globals::present_queue.index, // uint32_t dstQueueFamilyIndex image, // VkImage image image_subresource_range // VkImageSubresourceRange subresourceRange }; static VkImageMemoryBarrier barrier_from_clear_to_present = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void *pNext VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask VK_ACCESS_MEMORY_READ_BIT, // VkAccessFlags dstAccessMask VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // VkImageLayout newLayout vk_globals::present_queue.index, // uint32_t srcQueueFamilyIndex vk_globals::present_queue.index, // uint32_t dstQueueFamilyIndex image, // VkImage image image_subresource_range // VkImageSubresourceRange subresourceRange }; vkBeginCommandBuffer(command_buffer, &cmd_buffer_begin_info); vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier_from_present_to_clear); vkCmdClearColorImage(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &image_subresource_range); vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier_from_clear_to_present); VK_VERIFY (vkEndCommandBuffer(command_buffer)); return true; }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; bool U_ASSERT_ONLY pass; struct sample_info info = {}; char sample_title[] = "Input Attachment Sample"; const bool depthPresent = false; const bool vertexPresent = false; process_command_line_args(info, argc, argv); init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(info.gpus[0], VK_FORMAT_R8G8B8A8_UNORM, &props); if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { std::cout << "VK_FORMAT_R8G8B8A8_UNORM format unsupported for input " "attachment\n"; exit(-1); } init_window_size(info, 500, 500); init_connection(info); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info); /* VULKAN_KEY_START */ // Create a framebuffer with 2 attachments, one the color attachment // the shaders render into, and the other an input attachment which // will be cleared to yellow, and then used by the shaders to color // the drawn triangle. Final result should be a yellow triangle // Create the image that will be used as the input attachment // The image for the color attachment is the presentable image already // created in init_swapchain() VkImageCreateInfo image_create_info = {}; image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_create_info.pNext = NULL; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = info.format; image_create_info.extent.width = info.width; image_create_info.extent.height = info.height; image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.samples = NUM_SAMPLES; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.usage = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; image_create_info.queueFamilyIndexCount = 0; image_create_info.pQueueFamilyIndices = NULL; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_create_info.flags = 0; VkMemoryAllocateInfo mem_alloc = {}; mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.pNext = NULL; mem_alloc.allocationSize = 0; mem_alloc.memoryTypeIndex = 0; VkImage input_image; VkDeviceMemory input_memory; res = vkCreateImage(info.device, &image_create_info, NULL, &input_image); assert(res == VK_SUCCESS); VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(info.device, input_image, &mem_reqs); mem_alloc.allocationSize = mem_reqs.size; pass = memory_type_from_properties(info, mem_reqs.memoryTypeBits, 0, &mem_alloc.memoryTypeIndex); assert(pass); res = vkAllocateMemory(info.device, &mem_alloc, NULL, &input_memory); assert(res == VK_SUCCESS); res = vkBindImageMemory(info.device, input_image, input_memory, 0); assert(res == VK_SUCCESS); // Set the image layout to TRANSFER_DST_OPTIMAL to be ready for clear set_image_layout(info, input_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); VkImageSubresourceRange srRange = {}; srRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srRange.baseMipLevel = 0; srRange.levelCount = VK_REMAINING_MIP_LEVELS; srRange.baseArrayLayer = 0; srRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkClearColorValue clear_color; clear_color.float32[0] = 1.0f; clear_color.float32[1] = 1.0f; clear_color.float32[2] = 0.0f; clear_color.float32[3] = 0.0f; // Clear the input attachment image to yellow vkCmdClearColorImage(info.cmd, input_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_color, 1, &srRange); // Set the image layout to SHADER_READONLY_OPTIMAL for use by the shaders set_image_layout(info, input_image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); VkImageViewCreateInfo view_info = {}; view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.pNext = NULL; view_info.image = VK_NULL_HANDLE; view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; view_info.format = info.format; view_info.components.r = VK_COMPONENT_SWIZZLE_R; view_info.components.g = VK_COMPONENT_SWIZZLE_G; view_info.components.b = VK_COMPONENT_SWIZZLE_B; view_info.components.a = VK_COMPONENT_SWIZZLE_A; view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; view_info.subresourceRange.baseMipLevel = 0; view_info.subresourceRange.levelCount = 1; view_info.subresourceRange.baseArrayLayer = 0; view_info.subresourceRange.layerCount = 1; VkImageView input_attachment_view; view_info.image = input_image; res = vkCreateImageView(info.device, &view_info, NULL, &input_attachment_view); assert(res == VK_SUCCESS); VkDescriptorImageInfo input_image_info = {}; input_image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; input_image_info.imageView = input_attachment_view; input_image_info.sampler = VK_NULL_HANDLE; VkDescriptorSetLayoutBinding layout_bindings[1]; layout_bindings[0].binding = 0; layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; layout_bindings[0].descriptorCount = 1; layout_bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; layout_bindings[0].pImmutableSamplers = NULL; VkDescriptorSetLayoutCreateInfo descriptor_layout = {}; descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptor_layout.pNext = NULL; descriptor_layout.bindingCount = 1; descriptor_layout.pBindings = layout_bindings; info.desc_layout.resize(NUM_DESCRIPTOR_SETS); res = vkCreateDescriptorSetLayout(info.device, &descriptor_layout, NULL, info.desc_layout.data()); assert(res == VK_SUCCESS); VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pPipelineLayoutCreateInfo.pNext = NULL; pPipelineLayoutCreateInfo.pushConstantRangeCount = 0; pPipelineLayoutCreateInfo.pPushConstantRanges = NULL; pPipelineLayoutCreateInfo.setLayoutCount = NUM_DESCRIPTOR_SETS; pPipelineLayoutCreateInfo.pSetLayouts = info.desc_layout.data(); res = vkCreatePipelineLayout(info.device, &pPipelineLayoutCreateInfo, NULL, &info.pipeline_layout); assert(res == VK_SUCCESS); // First attachment is the color attachment - clear at the beginning of the // renderpass and transition layout to PRESENT_SRC_KHR at the end of // renderpass VkAttachmentDescription attachments[2]; attachments[0].format = info.format; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; attachments[0].flags = 0; // Second attachment is input attachment. Once cleared it should have // width*height yellow pixels. Doing a subpassLoad in the fragment shader // should give the shader the color at the fragments x,y location // from the input attachment attachments[1].format = info.format; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachments[1].initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; attachments[1].flags = 0; VkAttachmentReference color_reference = {}; color_reference.attachment = 0; color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference input_reference = {}; input_reference.attachment = 1; input_reference.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.flags = 0; subpass.inputAttachmentCount = 1; subpass.pInputAttachments = &input_reference; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_reference; subpass.pResolveAttachments = NULL; subpass.pDepthStencilAttachment = NULL; subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = NULL; VkRenderPassCreateInfo rp_info = {}; rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; rp_info.pNext = NULL; rp_info.attachmentCount = 2; rp_info.pAttachments = attachments; rp_info.subpassCount = 1; rp_info.pSubpasses = &subpass; rp_info.dependencyCount = 0; rp_info.pDependencies = NULL; res = vkCreateRenderPass(info.device, &rp_info, NULL, &info.render_pass); assert(!res); init_shaders(info, vertShaderText, fragShaderText); VkImageView fb_attachments[2]; fb_attachments[1] = input_attachment_view; VkFramebufferCreateInfo fbc_info = {}; fbc_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbc_info.pNext = NULL; fbc_info.renderPass = info.render_pass; fbc_info.attachmentCount = 2; fbc_info.pAttachments = fb_attachments; fbc_info.width = info.width; fbc_info.height = info.height; fbc_info.layers = 1; uint32_t i; info.framebuffers = (VkFramebuffer *)malloc(info.swapchainImageCount * sizeof(VkFramebuffer)); for (i = 0; i < info.swapchainImageCount; i++) { fb_attachments[0] = info.buffers[i].view; res = vkCreateFramebuffer(info.device, &fbc_info, NULL, &info.framebuffers[i]); assert(res == VK_SUCCESS); } VkDescriptorPoolSize type_count[1]; type_count[0].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; type_count[0].descriptorCount = 1; VkDescriptorPoolCreateInfo descriptor_pool = {}; descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptor_pool.pNext = NULL; descriptor_pool.maxSets = 1; descriptor_pool.poolSizeCount = 1; descriptor_pool.pPoolSizes = type_count; res = vkCreateDescriptorPool(info.device, &descriptor_pool, NULL, &info.desc_pool); assert(res == VK_SUCCESS); VkDescriptorSetAllocateInfo desc_alloc_info[1]; desc_alloc_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; desc_alloc_info[0].pNext = NULL; desc_alloc_info[0].descriptorPool = info.desc_pool; desc_alloc_info[0].descriptorSetCount = 1; desc_alloc_info[0].pSetLayouts = info.desc_layout.data(); info.desc_set.resize(1); res = vkAllocateDescriptorSets(info.device, desc_alloc_info, info.desc_set.data()); assert(res == VK_SUCCESS); VkWriteDescriptorSet writes[1]; // Write descriptor set with one write describing input attachment writes[0] = {}; writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[0].dstSet = info.desc_set[0]; writes[0].dstBinding = 0; writes[0].descriptorCount = 1; writes[0].descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; writes[0].pImageInfo = &input_image_info; writes[0].pBufferInfo = nullptr; writes[0].pTexelBufferView = nullptr; writes[0].dstArrayElement = 0; vkUpdateDescriptorSets(info.device, 1, writes, 0, NULL); init_pipeline_cache(info); init_pipeline(info, depthPresent, vertexPresent); // Color attachment clear to gray VkClearValue clear_values; clear_values.color.float32[0] = 0.2f; clear_values.color.float32[1] = 0.2f; clear_values.color.float32[2] = 0.2f; clear_values.color.float32[3] = 0.2f; VkSemaphoreCreateInfo imageAcquiredSemaphoreCreateInfo; imageAcquiredSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; imageAcquiredSemaphoreCreateInfo.pNext = NULL; imageAcquiredSemaphoreCreateInfo.flags = 0; res = vkCreateSemaphore(info.device, &imageAcquiredSemaphoreCreateInfo, NULL, &info.imageAcquiredSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, info.imageAcquiredSemaphore, VK_NULL_HANDLE, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); VkRenderPassBeginInfo rp_begin; rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rp_begin.pNext = NULL; rp_begin.renderPass = info.render_pass; rp_begin.framebuffer = info.framebuffers[info.current_buffer]; rp_begin.renderArea.offset.x = 0; rp_begin.renderArea.offset.y = 0; rp_begin.renderArea.extent.width = info.width; rp_begin.renderArea.extent.height = info.height; rp_begin.clearValueCount = 1; rp_begin.pClearValues = &clear_values; vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline); vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout, 0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL); init_viewports(info); init_scissors(info); vkCmdDraw(info.cmd, 3, 1, 0, 0); vkCmdEndRenderPass(info.cmd); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); /* VULKAN_KEY_END */ const VkCommandBuffer cmd_bufs[] = {info.cmd}; VkFenceCreateInfo fenceInfo; VkFence drawFence; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = NULL; fenceInfo.flags = 0; vkCreateFence(info.device, &fenceInfo, NULL, &drawFence); execute_queue_cmdbuf(info, cmd_bufs, drawFence); do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, drawFence, NULL); execute_present_image(info); wait_seconds(1); if (info.save_images) write_ppm(info, "input_attachment"); vkDestroySemaphore(info.device, info.imageAcquiredSemaphore, NULL); vkDestroyImageView(info.device, input_attachment_view, NULL); vkDestroyImage(info.device, input_image, NULL); vkFreeMemory(info.device, input_memory, NULL); destroy_pipeline(info); destroy_pipeline_cache(info); destroy_descriptor_pool(info); destroy_framebuffers(info); destroy_shaders(info); destroy_renderpass(info); destroy_descriptor_and_pipeline_layouts(info); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
int sample_main() { VkResult U_ASSERT_ONLY res; char sample_title[] = "MT Cmd Buffer Sample"; const bool depthPresent = false; init_global_layer_properties(info); init_instance_extension_names(info); init_device_extension_names(info); init_instance(info, sample_title); init_enumerate_device(info); init_window_size(info, 500, 500); init_connection(info); init_window(info); init_swapchain_extension(info); init_device(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info); VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo; presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; presentCompleteSemaphoreCreateInfo.pNext = NULL; presentCompleteSemaphoreCreateInfo.flags = 0; res = vkCreateSemaphore(info.device, &presentCompleteSemaphoreCreateInfo, NULL, &info.presentCompleteSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, info.presentCompleteSemaphore, NULL, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pPipelineLayoutCreateInfo.pNext = NULL; pPipelineLayoutCreateInfo.pushConstantRangeCount = 0; pPipelineLayoutCreateInfo.pPushConstantRanges = NULL; pPipelineLayoutCreateInfo.setLayoutCount = 0; pPipelineLayoutCreateInfo.pSetLayouts = NULL; res = vkCreatePipelineLayout(info.device, &pPipelineLayoutCreateInfo, NULL, &info.pipeline_layout); assert(res == VK_SUCCESS); init_renderpass( info, depthPresent, false); // Can't clear in renderpass load because we re-use pipeline init_shaders(info, vertShaderText, fragShaderText); init_framebuffers(info, depthPresent); /* The binding and attributes should be the same for all 3 vertex buffers, * so init here */ info.vi_binding.binding = 0; info.vi_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; info.vi_binding.stride = sizeof(triData[0]); info.vi_attribs[0].binding = 0; info.vi_attribs[0].location = 0; info.vi_attribs[0].format = VK_FORMAT_R32G32B32A32_SFLOAT; info.vi_attribs[0].offset = 0; info.vi_attribs[1].binding = 0; info.vi_attribs[1].location = 1; info.vi_attribs[1].format = VK_FORMAT_R32G32B32A32_SFLOAT; info.vi_attribs[1].offset = 16; init_pipeline_cache(info); init_pipeline(info, depthPresent); VkImageSubresourceRange srRange = {}; srRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srRange.baseMipLevel = 0; srRange.levelCount = VK_REMAINING_MIP_LEVELS; srRange.baseArrayLayer = 0; srRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkClearColorValue clear_color[1]; clear_color[0].float32[0] = 0.2f; clear_color[0].float32[1] = 0.2f; clear_color[0].float32[2] = 0.2f; clear_color[0].float32[3] = 0.2f; /* We need to do the clear here instead of as a load op since all 3 threads * share the same pipeline / renderpass */ set_image_layout(info, info.buffers[info.current_buffer].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vkCmdClearColorImage(info.cmd, info.buffers[info.current_buffer].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1, &srRange); set_image_layout(info, info.buffers[info.current_buffer].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); res = vkEndCommandBuffer(info.cmd); const VkCommandBuffer cmd_bufs[] = {info.cmd}; VkFence clearFence; init_fence(info, clearFence); VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSubmitInfo submit_info[1] = {}; submit_info[0].pNext = NULL; submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info[0].waitSemaphoreCount = 1; submit_info[0].pWaitSemaphores = &info.presentCompleteSemaphore; submit_info[0].pWaitDstStageMask = NULL; submit_info[0].commandBufferCount = 1; submit_info[0].pCommandBuffers = cmd_bufs; submit_info[0].signalSemaphoreCount = 0; submit_info[0].pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, submit_info, clearFence); assert(!res); do { res = vkWaitForFences(info.device, 1, &clearFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, clearFence, NULL); /* VULKAN_KEY_START */ /* Use the fourth slot in the command buffer array for the presentation */ /* barrier using the command buffer in info */ threadCmdBufs[3] = info.cmd; sample_platform_thread vk_threads[3]; for (size_t i = 0; i < 3; i++) { sample_platform_thread_create(&vk_threads[i], &per_thread_code, (void *)i); } VkCommandBufferBeginInfo cmd_buf_info = {}; cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmd_buf_info.pNext = NULL; cmd_buf_info.flags = 0; cmd_buf_info.pInheritanceInfo = NULL; res = vkBeginCommandBuffer(threadCmdBufs[3], &cmd_buf_info); assert(res == VK_SUCCESS); VkImageMemoryBarrier prePresentBarrier = {}; prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; prePresentBarrier.pNext = NULL; prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; prePresentBarrier.subresourceRange.baseMipLevel = 0; prePresentBarrier.subresourceRange.levelCount = 1; prePresentBarrier.subresourceRange.baseArrayLayer = 0; prePresentBarrier.subresourceRange.layerCount = 1; prePresentBarrier.image = info.buffers[info.current_buffer].image; vkCmdPipelineBarrier(threadCmdBufs[3], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &prePresentBarrier); res = vkEndCommandBuffer(threadCmdBufs[3]); assert(res == VK_SUCCESS); pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; submit_info[0].pNext = NULL; submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info[0].waitSemaphoreCount = 0; submit_info[0].pWaitSemaphores = NULL; submit_info[0].pWaitDstStageMask = &pipe_stage_flags; submit_info[0].commandBufferCount = 4; /* 3 from threads + prePresentBarrier */ submit_info[0].pCommandBuffers = threadCmdBufs; submit_info[0].signalSemaphoreCount = 0; submit_info[0].pSignalSemaphores = NULL; /* Wait for all of the threads to finish */ for (int i = 0; i < 3; i++) { sample_platform_thread_join(vk_threads[i], NULL); } VkFenceCreateInfo fenceInfo; VkFence drawFence; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = NULL; fenceInfo.flags = 0; vkCreateFence(info.device, &fenceInfo, NULL, &drawFence); /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, submit_info, drawFence); assert(!res); /* Make sure command buffer is finished before presenting */ do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); execute_present_image(info); wait_seconds(1); /* VULKAN_KEY_END */ vkDestroyBuffer(info.device, vertex_buffer[0].buf, NULL); vkDestroyBuffer(info.device, vertex_buffer[1].buf, NULL); vkDestroyBuffer(info.device, vertex_buffer[2].buf, NULL); vkFreeMemory(info.device, vertex_buffer[0].mem, NULL); vkFreeMemory(info.device, vertex_buffer[1].mem, NULL); vkFreeMemory(info.device, vertex_buffer[2].mem, NULL); for (int i = 0; i < 3; i++) { vkFreeCommandBuffers(info.device, threadCmdPools[i], 1, &threadCmdBufs[i]); vkDestroyCommandPool(info.device, threadCmdPools[i], NULL); } vkDestroySemaphore(info.device, info.presentCompleteSemaphore, NULL); vkDestroyFence(info.device, drawFence, NULL); destroy_pipeline(info); destroy_pipeline_cache(info); destroy_framebuffers(info); destroy_shaders(info); destroy_renderpass(info); vkDestroyPipelineLayout(info.device, info.pipeline_layout, NULL); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_window(info); destroy_device(info); destroy_instance(info); return 0; }
void CommandBuffers::cmdClearColorImage(const VkImage image, const VkImageLayout imageLayout, const VkClearColorValue* color, const uint32_t rangeCount, const VkImageSubresourceRange* ranges, const uint32_t bufferIndex) const { vkCmdClearColorImage(allCommandBuffers[bufferIndex], image, imageLayout, color, rangeCount, ranges); }
static void render_loop(uint32_t dev_count, struct tut1_physical_device *phy_devs, struct tut2_device *devs, struct tut6_swapchain *swapchains) { int res; struct tut7_render_essentials essentials[dev_count]; /* Allocate render essentials. See this function in tut7_render.c for explanations. */ for (uint32_t i = 0; i < dev_count; ++i) { res = tut7_render_get_essentials(&essentials[i], &phy_devs[i], &devs[i], &swapchains[i]); if (res) { printf("-- failed for device %u\n", i); return; } } unsigned int frames = 0; time_t before = time(NULL); uint8_t color = 0; /* Process events from SDL and render. If process_events returns non-zero, it signals application exit. */ while (process_events() == 0) { /* * A simple imprecise FPS calculator. Try the --no-vsync option to this program to see the difference. * * On Linux, with Nvidia GTX 970, and Vulkan 1.0.8, --no-vsync got me about 12000 FPS. */ time_t now = time(NULL); if (now != before) { printf("%lds: %u frames\n", now - before, frames); frames = 0; before = now; } ++frames; /* * We are not yet ready to actually render something. For that, we would need descriptor sets and * pipelines, but we'll get to that soon. In tut7.c, we have a repository of functions to create * resources for the eventual rendering. Here, we'll ignore all that and do what we ignored in * Tutorial 6, and that is properly transitioning the swapchain images between "present src" and * something we can render to. With a graphics pipeline, we would want to transition to * "color attachment optimal". Since we don't have one, we are going to "clear" the screen which * doesn't need a graphics pipeline. In that case, the layout of the image should be GENERAL. */ for (uint32_t i = 0; i < dev_count; ++i) { uint32_t image_index; /* * To render to an image and present it on the screen, the following sequence of operations * needs to be done: * * - acquire from swapchain * - transition to color attachment optimal * - render * - transition to present src * - present the image * * One way to implement this would be to call the corresponding functions one by one, wait and * make sure the image passes through each section, and repeat. The problem with this way is * that there is wasted time between each function call. Not that function call itself takes * measurable time, but the setup and finish times of each call, especially because we are * interacting with the GPU. * * Vulkan is made for parallelism and efficiency, so naturally it's not stupid in this regard! * There are different ways to do the above in parallel, and synchronize them. One nice thing * is that command buffers can call other secondary command buffers. So, while a small part of * the command buffer requires knowledge of which presentable image it is working with, the * majority of it doesn't, so they could be pre-recorded or recorded in parallel by other * threads. Another nice thing is that many of the functions work asynchronously, such as * submission to queue for rendering. This allows the CPU to go ahead with executing the rest * of the above algorithm, only wait for the GPU to finish rendering when it has to, and let * synchronization mechanisms take care of handling the flow of execution in the back. * * One could imagine different ways of doing things, but here is a simple example: * * - acquire from swapchain, signalling semaphore A * - wait on fence C (for previous frame to finish) * - create a command buffer with 1) first transition, 2) render, 3) second transition * - submit the command buffer with semaphore A waiting in the beginning and semaphore B * signalling the end, with fence C signalling the end as well * - present to swapchain, waiting on the second semaphore * * The significance of the fence above is the following. In Tutorial 6, we used `usleep` to * avoid busy looping. That was bad, because it put a hard limit and the frame rate. The * issue is not just busy looping though. Since the submissions to queues happen * asynchronously, we risk submitting work faster than the card can actually perform them, with * the result being that frames we send now are rendered much later, after all our previous * work is finished. This delay can easily become unacceptable; imagine a player has hit the * key to move forwards, you detect this and generate the next frame accordingly, but the * player doesn't actually see her character move forward while several older frames are still * being rendered. * * The location of the fence is chosen as such, to allow maximum overlap between GPU and CPU * work. In this case, while the GPU is still rendering, the CPU can wait for the swapchain * image to be acquired. The wait on the fence could not be delayed any further, because we * can't re-record a command buffer that is being executed. Interestingly, if we use two * command buffers and alternate between them, we could also wait for the fence later! Let's * not go that far yet. */ /* See this function in tut7_render.c for explanations */ res = tut7_render_start(&essentials[i], &devs[i], &swapchains[i], VK_IMAGE_LAYOUT_GENERAL, &image_index); if (res) { printf("-- failed for device %u\n", i); goto exit_fail; } /* * We did everything just to clear the image. Like I said, it's possible to clear an image * outside a pipeline. It is also possible to clear it inside a pipeline, so fear not! When * we have a graphics pipeline, we can transition the image directly to "color attachment * optimal" and clear it, and we don't have to first transition to "general" and then * transition again to "color attachment optimal". * * Clearing the image outside the pipeline is quite straightforward, and in fact has no notion * of the image being used for presentation later. It's just clearing a general image. * * The vkCmdClearColorImage takes the command buffer, the image, the layout the image is in * (which is "general", we just transitioned it), the color to clear the image with, and a set * of "subresources" to clear. We are going to clear everything, and we have just a single mip * level and a single array layer, so the subresource range to be cleared is similar to the * `subresourceRange` in image barrier. * * The clear color needs to be specified based on the format of the image. The * `VkClearColorValue` is a union which accepts RGBA values in float, uint32_t or int32_t, and * we should choose the appropriate field based on swapchains[i].surface_format.format. If we * weren't so lazy, we could write a simple lookup table that tells us which field to use for * each format, but luckily we are lazy, so let's assume `float` is good for now and hope it's * portable enough. * * For fun, let's change the background color on each frame! */ VkImageSubresourceRange clear_subresource_range = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; VkClearColorValue clear_color = { .float32 = {color, (color + 64) % 256 / 255.0f, (color + 128) % 256 / 255.0f, 1}, }; ++color; vkCmdClearColorImage(essentials[i].cmd_buffer, essentials[i].images[image_index], VK_IMAGE_LAYOUT_GENERAL, &clear_color, 1, &clear_subresource_range); /* See this function in tut7_render.c for explanations */ res = tut7_render_finish(&essentials[i], &devs[i], &swapchains[i], VK_IMAGE_LAYOUT_GENERAL, image_index); if (res) { printf("-- failed for device %u\n", i); goto exit_fail; } } } exit_fail: for (uint32_t i = 0; i < dev_count; ++i) tut7_render_cleanup_essentials(&essentials[i], &devs[i]); } int main(int argc, char **argv) { tut1_error res; int retval = EXIT_FAILURE; VkInstance vk; struct tut1_physical_device phy_devs[MAX_DEVICES]; struct tut2_device devs[MAX_DEVICES]; struct tut6_swapchain swapchains[MAX_DEVICES] = {0}; SDL_Window *windows[MAX_DEVICES] = {NULL}; uint32_t dev_count = MAX_DEVICES; bool no_vsync = false; for (int i = 1; i < argc; ++i) { if (strcmp(argv[1], "--help") == 0) { printf("Usage: %s [--no-vsync]\n\n", argv[0]); return 0; } if (strcmp(argv[1], "--no-vsync") == 0) no_vsync = true; } /* Fire up Vulkan */ res = tut6_init(&vk); if (!tut1_error_is_success(&res)) { tut1_error_printf(&res, "Could not initialize Vulkan\n"); goto exit_bad_init; } /* Enumerate devices */ res = tut1_enumerate_devices(vk, phy_devs, &dev_count); if (tut1_error_is_error(&res)) { tut1_error_printf(&res, "Could not enumerate devices\n"); goto exit_bad_enumerate; } /* Get logical devices and enable WSI extensions */ for (uint32_t i = 0; i < dev_count; ++i) { res = tut6_setup(&phy_devs[i], &devs[i], VK_QUEUE_GRAPHICS_BIT); if (tut1_error_is_error(&res)) { tut1_error_printf(&res, "Could not setup logical device %u, command pools and queues\n", i); goto exit_bad_setup; } } /* Set up SDL */ if (SDL_Init(SDL_INIT_VIDEO)) { printf("Could not initialize SDL: %s\n", SDL_GetError()); goto exit_bad_sdl; } for (uint32_t i = 0; i < dev_count; ++i) { char title[50]; snprintf(title, sizeof title, "Vk on device %u\n", i); windows[i] = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 768, 0); if (windows[i] == NULL) { printf("Could not create window #%u: %s\n", i + 1, SDL_GetError()); goto exit_bad_window; } } /* Get the surface and swapchain */ for (uint32_t i = 0; i < dev_count; ++i) { /* Let's still not bother with threads and use just 1 (the current thread) */ res = tut6_get_swapchain(vk, &phy_devs[i], &devs[i], &swapchains[i], windows[i], 1, no_vsync); if (tut1_error_is_error(&res)) { tut1_error_printf(&res, "Could not create surface and swapchain for device %u\n", i); goto exit_bad_swapchain; } } /* Render loop similar to Tutorial 6 */ render_loop(dev_count, phy_devs, devs, swapchains); retval = 0; /* Cleanup after yourself */ exit_bad_swapchain: for (uint32_t i = 0; i < dev_count; ++i) tut6_free_swapchain(vk, &devs[i], &swapchains[i]); exit_bad_window: for (uint32_t i = 0; i < dev_count; ++i) if (windows[i]) SDL_DestroyWindow(windows[i]); exit_bad_sdl: SDL_Quit(); exit_bad_setup: for (uint32_t i = 0; i < dev_count; ++i) tut2_cleanup(&devs[i]); exit_bad_enumerate: tut1_exit(vk); exit_bad_init: return retval; }
int sample_main(int argc, char **argv) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "Memory Barriers"; process_command_line_args(info, argc, argv); init_global_layer_properties(info); info.instance_extension_names.push_back(VK_KHR_SURFACE_EXTENSION_NAME); #ifdef _WIN32 info.instance_extension_names.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); #elif __ANDROID__ info.instance_extension_names.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); #else info.instance_extension_names.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); #endif info.device_extension_names.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); init_instance(info, sample_title); init_enumerate_device(info); init_device(info); info.width = info.height = 500; init_connection(info); init_window(info); init_swapchain_extension(info); init_command_pool(info); init_command_buffer(info); execute_begin_command_buffer(info); init_device_queue(info); init_swap_chain(info, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); // CmdClearColorImage is going to require usage of TRANSFER_DST, but // it's not clear which format feature maps to the required TRANSFER_DST usage, // BLIT_DST is a reasonable guess and it seems to work init_texture(info, nullptr, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_FORMAT_FEATURE_BLIT_DST_BIT); init_uniform_buffer(info); init_descriptor_and_pipeline_layouts(info, true); init_renderpass(info, DEPTH_PRESENT, false, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); init_shaders(info, vertShaderText, fragShaderText); init_framebuffers(info, DEPTH_PRESENT); init_vertex_buffer(info, vb_Data, sizeof(vb_Data), sizeof(vb_Data[0]), true); init_descriptor_pool(info, true); init_descriptor_set(info, true); init_pipeline_cache(info); init_pipeline(info, DEPTH_PRESENT); /* VULKAN_KEY_START */ VkImageSubresourceRange srRange = {}; srRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; srRange.baseMipLevel = 0; srRange.levelCount = VK_REMAINING_MIP_LEVELS; srRange.baseArrayLayer = 0; srRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkClearColorValue clear_color[1]; clear_color[0].float32[0] = 0.2f; clear_color[0].float32[1] = 0.2f; clear_color[0].float32[2] = 0.2f; clear_color[0].float32[3] = 0.2f; VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo; presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; presentCompleteSemaphoreCreateInfo.pNext = NULL; presentCompleteSemaphoreCreateInfo.flags = 0; res = vkCreateSemaphore(info.device, &presentCompleteSemaphoreCreateInfo, NULL, &info.imageAcquiredSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, info.imageAcquiredSemaphore, VK_NULL_HANDLE, &info.current_buffer); // TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR // return codes assert(res == VK_SUCCESS); set_image_layout(info, info.buffers[info.current_buffer].image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); // We need to do the clear here instead of using a renderpass load op since // we will use the same renderpass multiple times in the frame vkCmdClearColorImage(info.cmd, info.buffers[info.current_buffer].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1, &srRange); VkRenderPassBeginInfo rp_begin; rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rp_begin.pNext = NULL; rp_begin.renderPass = info.render_pass; rp_begin.framebuffer = info.framebuffers[info.current_buffer]; rp_begin.renderArea.offset.x = 0; rp_begin.renderArea.offset.y = 0; rp_begin.renderArea.extent.width = info.width; rp_begin.renderArea.extent.height = info.height; rp_begin.clearValueCount = 0; rp_begin.pClearValues = NULL; // Draw a textured quad on the left side of the window vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline); vkCmdBindDescriptorSets(info.cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline_layout, 0, NUM_DESCRIPTOR_SETS, info.desc_set.data(), 0, NULL); const VkDeviceSize offsets[1] = {0}; vkCmdBindVertexBuffers(info.cmd, 0, 1, &info.vertex_buffer.buf, offsets); init_viewports(info); init_scissors(info); vkCmdDraw(info.cmd, 2 * 3, 1, 0, 0); // We can't do a clear inside a renderpass, so end this one and start another one // for the next draw vkCmdEndRenderPass(info.cmd); // Send a barrier to change the texture image's layout from SHADER_READ_ONLY // to COLOR_ATTACHMENT_GENERAL because we're going to clear it VkImageMemoryBarrier textureBarrier = {}; textureBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; textureBarrier.pNext = NULL; textureBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; textureBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; textureBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; textureBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; textureBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; textureBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; textureBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; textureBarrier.subresourceRange.baseMipLevel = 0; textureBarrier.subresourceRange.levelCount = 1; textureBarrier.subresourceRange.baseArrayLayer = 0; textureBarrier.subresourceRange.layerCount = 1; textureBarrier.image = info.textures[0].image; vkCmdPipelineBarrier(info.cmd, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &textureBarrier); clear_color[0].float32[0] = 0.0f; clear_color[0].float32[1] = 1.0f; clear_color[0].float32[2] = 0.0f; clear_color[0].float32[3] = 1.0f; /* Clear texture to green */ vkCmdClearColorImage(info.cmd, info.textures[0].image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clear_color, 1, &srRange); // Send a barrier to change the texture image's layout back to SHADER_READ_ONLY // because we're going to use it as a texture again textureBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; textureBarrier.pNext = NULL; textureBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; textureBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; textureBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; textureBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; textureBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; textureBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; textureBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; textureBarrier.subresourceRange.baseMipLevel = 0; textureBarrier.subresourceRange.levelCount = 1; textureBarrier.subresourceRange.baseArrayLayer = 0; textureBarrier.subresourceRange.layerCount = 1; textureBarrier.image = info.textures[0].image; vkCmdPipelineBarrier(info.cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &textureBarrier); // Draw the second quad to the right using the (now) green texture vkCmdBeginRenderPass(info.cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); // Draw starting with vertex index 6 to draw to the right of the first quad vkCmdDraw(info.cmd, 2 * 3, 1, 6, 0); vkCmdEndRenderPass(info.cmd); // Change the present buffer from COLOR_ATTACHMENT_OPTIMAL to // PRESENT_SOURCE_KHR // so it can be presented execute_pre_present_barrier(info); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); VkSubmitInfo submit_info = {}; VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; init_submit_info(info, submit_info, pipe_stage_flags); assert(res == VK_SUCCESS); VkFence drawFence = {}; init_fence(info, drawFence); // Queue the command buffer for execution res = vkQueueSubmit(info.graphics_queue, 1, &submit_info, drawFence); assert(res == VK_SUCCESS); // Now present the image in the window VkPresentInfoKHR present{}; init_present_info(info, present); // Make sure command buffer is finished before presenting do { res = vkWaitForFences(info.device, 1, &drawFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); res = vkQueuePresentKHR(info.present_queue, &present); assert(res == VK_SUCCESS); /* VULKAN_KEY_END */ wait_seconds(1); if (info.save_images) write_ppm(info, "memory_barriers"); vkDestroySemaphore(info.device, info.imageAcquiredSemaphore, NULL); vkDestroyFence(info.device, drawFence, NULL); destroy_pipeline(info); destroy_pipeline_cache(info); destroy_textures(info); destroy_descriptor_pool(info); destroy_vertex_buffer(info); destroy_framebuffers(info); destroy_shaders(info); destroy_renderpass(info); destroy_descriptor_and_pipeline_layouts(info); destroy_uniform_buffer(info); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_window(info); destroy_device(info); destroy_instance(info); return 0; }
void VulkanBase::createInstance() { // Application info init const VkApplicationInfo applicationInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pNext = NULL, .pApplicationName = name.c_str(), .applicationVersion = 1, .pEngineName = engineName.c_str(), .engineVersion = 1, .apiVersion = VK_API_VERSION, //FIXME Nvidia driver not updated to latest Vulkan Version }; VkInstanceCreateInfo instanceCreateInfo = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE, .pApplicationInfo = &applicationInfo, .enabledLayerCount = 0, .ppEnabledLayerNames = NULL, .enabledExtensionCount = 0, .ppEnabledExtensionNames = NULL, }; std::vector<const char*> enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_XCB_SURFACE_EXTENSION_NAME}; //Check if extensions are present vkUtils::checkGlobalExtensionPresent(VK_KHR_SURFACE_EXTENSION_NAME); vkUtils::checkGlobalExtensionPresent(VK_KHR_XCB_SURFACE_EXTENSION_NAME); #ifdef _DEBUG if (enableValidation) { //Extensions management enabledExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); vkUtils::checkGlobalExtensionPresent(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); // Layer management instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; instanceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames; // Check standard debug layers are present for(uint32_t i = 0; i < instanceCreateInfo.enabledLayerCount; i++) { vkUtils::checkGlobalLayerPresent(vkDebug::validationLayerNames[i]); } } #endif // DEBUG instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data(); instanceCreateInfo.enabledExtensionCount = (uint32_t) enabledExtensions.size(); CHECK_RESULT(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); } void VulkanBase::selectVkPhysicalDevice() { uint32_t physicalDeviceCount = 0; CHECK_RESULT(vkEnumeratePhysicalDevices(instance,&physicalDeviceCount,nullptr)); if (physicalDeviceCount<=0) { ERROR("No physical device found"); } std::vector<VkPhysicalDevice> physicalDevicesVector(physicalDeviceCount); CHECK_RESULT(vkEnumeratePhysicalDevices(instance,&physicalDeviceCount,physicalDevicesVector.data())); #ifdef _DEBUG int deviceIndex = 0; for(const auto & phyDev : physicalDevicesVector) { VkPhysicalDeviceProperties phyDevProperties; vkGetPhysicalDeviceProperties(phyDev, &phyDevProperties); std::cout << "--- Physical device: " << phyDevProperties.deviceName << " (index: " << (deviceIndex++) << ")" << std::endl; std::cout << " apiVersion: " << phyDevProperties.apiVersion << std::endl; std::cout << " driverVersion: " << phyDevProperties.driverVersion << std::endl; std::cout << " vendorID: " << phyDevProperties.vendorID << std::endl; std::cout << " deviceID: " << phyDevProperties.deviceID << std::endl; std::cout << " deviceType: "; switch(phyDevProperties.deviceType) { case VK_PHYSICAL_DEVICE_TYPE_OTHER: std::cout << "OTHER"; break; case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: std::cout << "INTEGRATED_GPU"; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: std::cout << "DISCRETE_GPU"; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: std::cout << "VIRTUAL_GPU"; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: std::cout << "CPU"; break; default: std::cout << "UNKNOWN!!!"; break; } std::cout << std::endl; } #endif // _DEBUG physicalDevice = physicalDevicesVector.at(0); // Gather Physical Device Memory Properties vkGetPhysicalDeviceMemoryProperties(physicalDevice,&physicalDeviceMemoryProperties); } void VulkanBase::selectQueue() { uint32_t queueFamilyPropertyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,&queueFamilyPropertyCount,nullptr); if (queueFamilyPropertyCount<=0) ERROR("Physical device has no queue families"); std::vector<VkQueueFamilyProperties> queueFamilyPropertiesVector(queueFamilyPropertyCount); vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice,&queueFamilyPropertyCount,queueFamilyPropertiesVector.data()); uint32_t queueFamilyIndex = 0; int32_t selectedQueueFamilyIndex = -1; VkBool32 presentSupport = VK_FALSE; #ifdef _DEBUG std::cout << std::endl << "--- Number of queue families " << queueFamilyPropertyCount << std::endl; #endif // _DEBUG for(const auto & queueFamProp : queueFamilyPropertiesVector) { CHECK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, swapchain.surface, &presentSupport)); #ifdef _DEBUG std::cout << "--- Properties for queue family " << queueFamilyIndex << std::endl; std::cout << " queueFlags:"; if(queueFamProp.queueFlags & VK_QUEUE_GRAPHICS_BIT) std::cout << " G"; if(queueFamProp.queueFlags & VK_QUEUE_COMPUTE_BIT) std::cout << " C"; if(queueFamProp.queueFlags & VK_QUEUE_TRANSFER_BIT) std::cout << " T"; if(queueFamProp.queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) std::cout << " S"; std::cout << '\n'; std::cout << " queueCount: " << queueFamProp.queueCount << std::endl; std::cout << " timestampValidBits: " << queueFamProp.timestampValidBits << std::endl; std::cout << " minImageTransferGranularity: " << queueFamProp.minImageTransferGranularity.width << ", " << queueFamProp.minImageTransferGranularity.height << ", " << queueFamProp.minImageTransferGranularity.depth << std::endl; std::cout << " Supports present?: " << std::boolalpha << bool(presentSupport) << std::endl << std::endl; #endif // _DEBUG if (bool(queueFamProp.queueFlags & VK_QUEUE_GRAPHICS_BIT) && presentSupport == VK_TRUE) { if (selectedQueueFamilyIndex < 0) selectedQueueFamilyIndex = queueFamilyIndex; } queueFamilyIndex++; } if (selectedQueueFamilyIndex<0) ERROR("No queue with both graphics and present capabilities found"); // Create device after selecting the queue std::array<float,1> queuePriorities = {0.0f}; VkDeviceQueueCreateInfo queueCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .pNext = nullptr, .flags = VK_FLAGS_NONE, .queueFamilyIndex = (uint32_t) selectedQueueFamilyIndex, .queueCount = 1, //Number of queues to create .pQueuePriorities = queuePriorities.data() }; // Call to createDevice createDevice(queueCreateInfo,1); //Get a handle to the selected queue vkGetDeviceQueue(device, (uint32_t) selectedQueueFamilyIndex, 0, &queue); //TODO get handle if using multiple queues queueFamilyIndex = (uint32_t) selectedQueueFamilyIndex; } void VulkanBase::createDevice(VkDeviceQueueCreateInfo requestedQueues, uint32_t requestedQueuesCount) { //Check extensions available on the selected physical device before creating it // Check swap chain extension vkUtils::checkDeviceExtensionPresent(physicalDevice,VK_KHR_SWAPCHAIN_EXTENSION_NAME); std::vector<const char*> enabledExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; VkDeviceCreateInfo deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = nullptr, .flags = VK_FLAGS_NONE, .queueCreateInfoCount = requestedQueuesCount, .pQueueCreateInfos = &requestedQueues, .enabledLayerCount = 0, .ppEnabledLayerNames = nullptr, .enabledExtensionCount = (uint32_t) enabledExtensions.size(), .ppEnabledExtensionNames = enabledExtensions.data(), .pEnabledFeatures = NULL }; #ifdef _DEBUG if (enableValidation) { deviceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount; deviceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames; // Check standard debug layers are present on the device for(uint32_t i = 0; i < deviceCreateInfo.enabledLayerCount; i++) { vkUtils::checkGlobalLayerPresent(vkDebug::validationLayerNames[i]); } } #endif // _DEBUG CHECK_RESULT(vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device)); } void VulkanBase::createCommandPool(const uint32_t queueFamilyIndex, const VkCommandPoolCreateFlagBits createFlagBits) { const VkCommandPoolCreateInfo commandPoolCreateInfo= { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .pNext = NULL, .flags = createFlagBits, .queueFamilyIndex = queueFamilyIndex }; CHECK_RESULT(vkCreateCommandPool(device, &commandPoolCreateInfo,nullptr,&commandPool)); #ifdef _DEBUG std::cout << "\n+++ Created command pool" << std::endl; #endif // _DEBUG } void VulkanBase::createSynchroItems() { // Semaphores VkSemaphoreCreateInfo semaphoreCreateInfo = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE }; // Semaphore signaled on swapchain image ready to use and wait on the queue before rendering/present CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAcquiredSemaphore)); // Semaphore signaled on queue rendering termination and waited on present operation CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderingCompletedSemaphore)); // Fences VkFenceCreateInfo fenceCreateInfo = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE }; CHECK_RESULT(vkCreateFence(device, &fenceCreateInfo, nullptr, &presentFence)); #ifdef _DEBUG std::cout << "\n+++ Created semaphores and fences\n"; #endif // _DEBUG } void VulkanBase::createCommandBuffers(VkCommandBuffer* cmdBuffer, uint32_t commandBufferCount, VkCommandBufferLevel cmdBufferLevel) { const VkCommandBufferAllocateInfo commandBufferAllocateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .pNext = NULL, .commandPool = commandPool, .level = cmdBufferLevel, .commandBufferCount = commandBufferCount }; CHECK_RESULT(vkAllocateCommandBuffers(device, &commandBufferAllocateInfo, cmdBuffer)); #ifdef _DEBUG std::cout << "\n+++ Allocated " << commandBufferCount << " command buffers" << std::endl; #endif // _DEBUG } void VulkanBase::setupInitCommandBuffer() { VkCommandBufferBeginInfo commandBufferBeginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = NULL, .flags = VK_FLAGS_NONE, .pInheritanceInfo = NULL }; CHECK_RESULT(vkBeginCommandBuffer(initCommandBuffer, &commandBufferBeginInfo)); // Creates an image memory barrier to change the layout for every image on the swapchain VkImageMemoryBarrier imageMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = NULL, .srcAccessMask = VK_FLAGS_NONE, .dstAccessMask = VK_FLAGS_NONE, .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = 0, .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} }; // Pipeline Barrier for each swapchain image for (const auto& image: swapchain.swapchainImagesVector){ imageMemoryBarrier.image = image; vkCmdPipelineBarrier(initCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, //Put barrier on top of the pipeline VK_FLAGS_NONE, 0, nullptr, // memoryBarrier 0, nullptr, // bufferMemoryBarrier 1, &imageMemoryBarrier); // imageMemoryBarrier } CHECK_RESULT(vkEndCommandBuffer(initCommandBuffer)); #ifdef _DEBUG std::cout << "\n+++ Finished recording initCommandBuffer\n"; #endif // _DEBUG } void VulkanBase::setupPresentCommandBuffer(const VkImage currentSwapchainImage, const float* clearColors) { VkCommandBufferBeginInfo commandBufferBeginInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .pNext = NULL, .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, .pInheritanceInfo = NULL }; CHECK_RESULT(vkBeginCommandBuffer(presentCommandBuffer, &commandBufferBeginInfo)); VkImageMemoryBarrier imageMemoryBarrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = NULL, .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = currentSwapchainImage, .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} }; //Set barrier on top to change layout and access vkCmdPipelineBarrier(presentCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_FLAGS_NONE, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); VkClearColorValue clearColorValue; VkImageSubresourceRange imageSubresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; clearColorValue.float32[0] = clearColors[0]; clearColorValue.float32[1] = clearColors[1]; clearColorValue.float32[2] = clearColors[2]; clearColorValue.float32[3] = 1.0f; // Command to clear the swapchain image vkCmdClearColorImage(presentCommandBuffer,currentSwapchainImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColorValue, 1, &imageSubresourceRange); /* * Transition the swapchain image from VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL * to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR */ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; // Set barrier end of pipeline vkCmdPipelineBarrier(presentCommandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier ); CHECK_RESULT(vkEndCommandBuffer(presentCommandBuffer)); #ifdef _DEBUG //std::cout << "\n+++ Finished recording presentCommandBuffer\n"; #endif // _DEBUG } void VulkanBase::renderFrame(const float* clearColors) { // Wait on previous frame fence (render too fast) //CHECK_RESULT(vkWaitForFences(device, 1, &presentFence, VK_TRUE, UINT64_MAX)); //CHECK_RESULT(vkResetFences(device, 1, &presentFence)); // Acquire next image on the swapchain uint32_t imageIndex = UINT64_MAX; CHECK_RESULT(vkAcquireNextImageKHR(device, swapchain.swapchain, UINT64_MAX, imageAcquiredSemaphore, VK_NULL_HANDLE, &imageIndex)); // Setup the present command buffer setupPresentCommandBuffer(swapchain.swapchainImagesVector.at(imageIndex),clearColors); // Submit present command buffer to the queue // Waits on imageAcquiredSemaphore so it doesnt start rendering until the image from the swapchain is ready and // it also signals the renderingCompletedSemaphore used by the later present VkPipelineStageFlags pipelineStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = NULL, .waitSemaphoreCount = 1, .pWaitSemaphores = &imageAcquiredSemaphore, .pWaitDstStageMask = &pipelineStageFlags, .commandBufferCount = 1, .pCommandBuffers = &presentCommandBuffer, .signalSemaphoreCount = 1, .pSignalSemaphores = &renderingCompletedSemaphore }; CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); // Present the rendered image VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = NULL, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderingCompletedSemaphore, .swapchainCount = 1, .pSwapchains = &swapchain.swapchain, .pImageIndices = &imageIndex, .pResults = nullptr }; CHECK_RESULT(vkQueuePresentKHR(queue,&presentInfo)); CHECK_RESULT(vkQueueWaitIdle(queue)); //TODO Not sure this is the correct way... } void VulkanBase::prepare() { //Allocate command Buffers createCommandBuffers(&initCommandBuffer, 1, VK_COMMAND_BUFFER_LEVEL_PRIMARY); createCommandBuffers(&presentCommandBuffer, 1, VK_COMMAND_BUFFER_LEVEL_PRIMARY); commandBuffersVector.push_back(initCommandBuffer); commandBuffersVector.push_back(presentCommandBuffer); //Initialize command Buffers setupInitCommandBuffer(); // Submit initialization command buffer to the queue VkSubmitInfo submitInfo = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = 0, .pWaitSemaphores = nullptr, .pWaitDstStageMask = VK_FLAGS_NONE, .commandBufferCount = 1, .pCommandBuffers = &initCommandBuffer, .signalSemaphoreCount = 0, .pSignalSemaphores = nullptr }; CHECK_RESULT(vkQueueSubmit(queue,1,&submitInfo, VK_NULL_HANDLE)); CHECK_RESULT(vkQueueWaitIdle(queue)); vkFreeCommandBuffers(device, commandPool, 1, &initCommandBuffer); #ifdef _DEBUG std::cout << "\n+++ initCommandBuffer work complete!\n"; std::cout << "\n******* Rendering Start ******\n"; #endif // _DEBUG }
TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config) { static const VkFormat PC_TexFormat_To_VkFormat[12] { VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_NONE VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_BGRA32 VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_RGBA32 VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_I4_AS_I8 VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_IA4_AS_IA8 VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_I8 VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_IA8 VK_FORMAT_R8G8B8A8_UNORM,//PC_TEX_FMT_RGB565 VK_FORMAT_BC1_RGBA_UNORM_BLOCK,//PC_TEX_FMT_DXT1 VK_FORMAT_BC2_UNORM_BLOCK,//PC_TEX_FMT_DXT3 VK_FORMAT_BC3_UNORM_BLOCK,//PC_TEX_FMT_DXT5 VK_FORMAT_R32_SFLOAT,//PC_TEX_FMT_R32 }; // Determine image usage, we need to flag as an attachment if it can be used as a rendertarget. VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; if (config.rendertarget) usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; // Allocate texture object std::unique_ptr<Texture2D> texture = Texture2D::Create( config.width, config.height, config.levels, config.layers, PC_TexFormat_To_VkFormat[config.pcformat], VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); if (!texture) return nullptr; // If this is a render target (for efb copies), allocate a framebuffer VkFramebuffer framebuffer = VK_NULL_HANDLE; if (config.rendertarget) { VkImageView framebuffer_attachments[] = { texture->GetView() }; VkFramebufferCreateInfo framebuffer_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, m_render_pass, static_cast<u32>(ArraySize(framebuffer_attachments)), framebuffer_attachments, texture->GetWidth(), texture->GetHeight(), texture->GetLayers() }; VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, &framebuffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); return nullptr; } // Clear render targets before use to prevent reading uninitialized memory. VkClearColorValue clear_value = { { 0.0f, 0.0f, 0.0f, 1.0f } }; VkImageSubresourceRange clear_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, config.levels, 0, config.layers }; texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), texture->GetLayout(), &clear_value, 1, &clear_range); } std::unique_ptr<Texture2D> nrmtexture; if (config.materialmap) { nrmtexture = Texture2D::Create( config.width, config.height, config.levels, config.layers, PC_TexFormat_To_VkFormat[config.pcformat], VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); } TCacheEntry* entry = new TCacheEntry(config, std::move(texture), std::move(nrmtexture), framebuffer); entry->compressed = config.pcformat >= PC_TEX_FMT_DXT1 && config.pcformat < PC_TEX_FMT_R32; return entry; }
//============================================================================== // 描画 //============================================================================== void Render() { VkResult result; VkCommandBuffer command = g_commandBuffers[g_currentBufferIndex]; //================================================== // コマンド記録開始 //================================================== VkCommandBufferInheritanceInfo commandBufferInheritanceInfo = {}; commandBufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; commandBufferInheritanceInfo.pNext = nullptr; commandBufferInheritanceInfo.renderPass = nullptr; commandBufferInheritanceInfo.subpass = 0; commandBufferInheritanceInfo.framebuffer = g_frameBuffers[g_currentBufferIndex]; commandBufferInheritanceInfo.occlusionQueryEnable = VK_FALSE; commandBufferInheritanceInfo.queryFlags = 0; commandBufferInheritanceInfo.pipelineStatistics = 0; VkCommandBufferBeginInfo cmdBeginInfo = {}; cmdBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cmdBeginInfo.pNext = nullptr; cmdBeginInfo.flags = 0; cmdBeginInfo.pInheritanceInfo = &commandBufferInheritanceInfo; vkBeginCommandBuffer(command, &cmdBeginInfo); //================================================== // カラーバッファをクリア //================================================== static float count = 0; count += 0.0001f; count = fmodf(count, 1.0f); VkClearColorValue clearColor; clearColor.float32[0] = 0.0f; // R clearColor.float32[1] = count; // G clearColor.float32[2] = 1.0f; // B clearColor.float32[3] = 1.0f; VkImageSubresourceRange imageSubresourceRange; imageSubresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageSubresourceRange.baseMipLevel = 0; imageSubresourceRange.levelCount = 1; imageSubresourceRange.baseArrayLayer = 0; imageSubresourceRange.layerCount = 1; vkCmdClearColorImage( command, g_backBuffersTextures[g_currentBufferIndex].image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, &clearColor, 1, &imageSubresourceRange); //================================================== // 深度バッファをクリア //================================================== VkClearDepthStencilValue clearDepthStencil; clearDepthStencil.depth = 1.0f; clearDepthStencil.stencil = 0; imageSubresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; imageSubresourceRange.baseMipLevel = 0; imageSubresourceRange.levelCount = 1; imageSubresourceRange.baseArrayLayer = 0; imageSubresourceRange.layerCount = 1; vkCmdClearDepthStencilImage( command, g_depthBufferTexture.image, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, &clearDepthStencil, 1, &imageSubresourceRange); //================================================== // リソースバリアの設定 //================================================== VkImageMemoryBarrier imageMemoryBarrier; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.pNext = nullptr; imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; imageMemoryBarrier.subresourceRange.layerCount = 1; imageMemoryBarrier.image = g_backBuffersTextures[g_currentBufferIndex].image; vkCmdPipelineBarrier( command, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); //================================================== // コマンドの記録を終了 //================================================== vkEndCommandBuffer(command); //================================================== // コマンドを実行し,表示する //================================================== VkPipelineStageFlags pipeStageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = &pipeStageFlags; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &command; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; // コマンドを実行 result = vkQueueSubmit(g_VulkanQueue, 1, &submitInfo, g_VulkanFence); checkVulkanError(result, TEXT("グラフィックスキューへのサブミット失敗")); // 完了を待機 result = vkWaitForFences(g_VulkanDevice, 1, &g_VulkanFence, VK_TRUE, TIMEOUT_NANO_SEC); // 成功したら表示 if(result == VK_SUCCESS) { VkPresentInfoKHR present = {}; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present.pNext = nullptr; present.swapchainCount = 1; present.pSwapchains = &g_VulkanSwapChain; present.pImageIndices = &g_currentBufferIndex; present.pWaitSemaphores = nullptr; present.waitSemaphoreCount = 0; present.pResults = nullptr; result = vkQueuePresentKHR(g_VulkanQueue, &present); checkVulkanError(result, TEXT("プレゼント失敗")); } else if(result == VK_TIMEOUT) { checkVulkanError(VK_TIMEOUT, TEXT("タイムアウトしました")); } // フェンスをリセット result = vkResetFences(g_VulkanDevice, 1, &g_VulkanFence); checkVulkanError(result, TEXT("フェンスのリセット失敗")); // 次のイメージを取得 result = vkAcquireNextImageKHR( g_VulkanDevice, g_VulkanSwapChain, TIMEOUT_NANO_SEC, g_VulkanSemahoreRenderComplete, nullptr, &g_currentBufferIndex); checkVulkanError(result, TEXT("次の有効なイメージインデックスの獲得に失敗")); }