bool game_buffer_editor_record_vulkan_commands(game_buffer *game_buffer, vulkan *vulkan) { game_buffer_editor *editor = game_buffer->editor; ImDrawData *draw_data = ImGui::GetDrawData(); VkResult vk_result = {}; { uint64 vertices_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); uint64 indices_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); uint64 map_size = round_to_multi(vertices_size + indices_size, vulkan->physical_device_non_coherent_atom_size); assert(map_size <= editor->imgui_vertex_index_vulkan_buffer.size); uint8 *buf_ptr = nullptr; if ((vk_result = vkMapMemory(vulkan->device, editor->imgui_vertex_index_vulkan_buffer.device_memory, 0, map_size, 0, (void **)&buf_ptr)) != VK_SUCCESS) { return false; } assert((uintptr_t)buf_ptr % 16 == 0); for (int i = 0; i < draw_data->CmdListsCount; i += 1) { ImDrawList *dlist = draw_data->CmdLists[i]; memcpy(buf_ptr, dlist->VtxBuffer.Data, dlist->VtxBuffer.Size * sizeof(ImDrawVert)); buf_ptr += dlist->VtxBuffer.Size * sizeof(ImDrawVert); } for (int i = 0; i < draw_data->CmdListsCount; i += 1) { ImDrawList *dlist = draw_data->CmdLists[i]; memcpy(buf_ptr, dlist->IdxBuffer.Data, dlist->IdxBuffer.Size * sizeof(ImDrawIdx)); buf_ptr += dlist->IdxBuffer.Size * sizeof(ImDrawIdx); } VkMappedMemoryRange memory_range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; memory_range.memory = editor->imgui_vertex_index_vulkan_buffer.device_memory; memory_range.offset = map_size; if ((vk_result = vkFlushMappedMemoryRanges(vulkan->device, 1, &memory_range)) != VK_SUCCESS) { return false; } vkUnmapMemory(vulkan->device, editor->imgui_vertex_index_vulkan_buffer.device_memory); } vkCmdBindPipeline(vulkan->swap_chain_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, editor->imgui_vulkan_pipeline); VkViewport viewport = { 0, 0, (float)game_buffer->vulkan_framebuffer_image_width, (float)game_buffer->vulkan_framebuffer_image_height, 0, 1 }; vkCmdSetViewport(vulkan->swap_chain_cmd_buffer, 0, 1, &viewport); vec2 push_consts = { (float)game_buffer->vulkan_framebuffer_image_width, (float)game_buffer->vulkan_framebuffer_image_height }; vkCmdPushConstants(vulkan->swap_chain_cmd_buffer, editor->imgui_vulkan_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_consts), &push_consts); VkDeviceSize vertex_offset = 0; VkDeviceSize index_offset = draw_data->TotalVtxCount * sizeof(ImDrawVert); for (int i = 0; i < draw_data->CmdListsCount; i += 1) { ImDrawList *dlist = draw_data->CmdLists[i]; vkCmdBindVertexBuffers(vulkan->swap_chain_cmd_buffer, 0, 1, &editor->imgui_vertex_index_vulkan_buffer.buffer, &vertex_offset); vertex_offset += dlist->VtxBuffer.Size * sizeof(ImDrawVert); for (int i = 0; i < dlist->CmdBuffer.Size; i += 1) { ImDrawCmd *dcmd = &dlist->CmdBuffer.Data[i]; VkRect2D scissor = { { (int)dcmd->ClipRect.x, (int)dcmd->ClipRect.y }, { (uint)dcmd->ClipRect.z, (uint)dcmd->ClipRect.w } }; vkCmdSetScissor(vulkan->swap_chain_cmd_buffer, 0, 1, &scissor); if (dcmd->TextureId == editor->imgui_font_atlas_vulkan_image.image) { vkCmdBindDescriptorSets(vulkan->swap_chain_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, editor->imgui_vulkan_pipeline_layout, 0, 1, &editor->imgui_vulkan_descriptor_sets[0], 0, nullptr); } else { vkCmdBindDescriptorSets(vulkan->swap_chain_cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, editor->imgui_vulkan_pipeline_layout, 0, 1, &editor->imgui_vulkan_descriptor_sets[1], 0, nullptr); } vkCmdBindIndexBuffer(vulkan->swap_chain_cmd_buffer, editor->imgui_vertex_index_vulkan_buffer.buffer, index_offset, VK_INDEX_TYPE_UINT16); vkCmdDrawIndexed(vulkan->swap_chain_cmd_buffer, dcmd->ElemCount, 1, 0, 0, 0); index_offset += dcmd->ElemCount * sizeof(ImDrawIdx); } } return true; }
void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) { // If we're still mapped, flush the mapped range if (m_map_pointer && !m_coherent) { VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, m_map_offset, m_map_size}; vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); } // Ensure writes are visible to GPU. VkDeviceSize copy_size = m_row_stride * height; Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); // Issue the buffer->image copy VkBufferImageCopy image_copy = { 0, // VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset {width, height, 1} // VkExtent3D imageExtent }; vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); }
/** * Flush a memory range of the buffer to make it visible to the device * * @note Only required for non-coherent memory * * @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range. * @param offset (Optional) Byte offset from beginning * * @return VkResult of the flush call */ VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0) { VkMappedMemoryRange mappedRange = {}; mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedRange.memory = memory; mappedRange.offset = offset; mappedRange.size = size; return vkFlushMappedMemoryRanges(device, 1, &mappedRange); }
VkResult DeviceMemory::flushMappedMemoryRanges(const VkDeviceSize offset, const VkDeviceSize size) const { VkMappedMemoryRange mappedMemoryRange{}; mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedMemoryRange.memory = deviceMemory; mappedMemoryRange.offset = offset; mappedMemoryRange.size = size; return vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange); }
void StreamBuffer::CommitMemory(size_t final_num_bytes) { _assert_((m_current_offset + final_num_bytes) <= m_current_size); _assert_(final_num_bytes <= m_last_allocation_size); // For non-coherent mappings, flush the memory range if (!m_coherent_mapping) { VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, m_current_offset, final_num_bytes}; vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); } m_current_offset += final_num_bytes; }
void updateDynamicUniformBuffer(bool force = false) { // Update at max. 60 fps animationTimer += frameTimer; if ((animationTimer <= 1.0f / 60.0f) && (!force)) { return; } // Dynamic ubo with per-object model matrices indexed by offsets in the command buffer uint32_t dim = static_cast<uint32_t>(pow(OBJECT_INSTANCES, (1.0f / 3.0f))); glm::vec3 offset(5.0f); for (uint32_t x = 0; x < dim; x++) { for (uint32_t y = 0; y < dim; y++) { for (uint32_t z = 0; z < dim; z++) { uint32_t index = x * dim * dim + y * dim + z; // Aligned offset glm::mat4* modelMat = (glm::mat4*)(((uint64_t)uboDataDynamic.model + (index * dynamicAlignment))); // Update rotations rotations[index] += animationTimer * rotationSpeeds[index]; // Update matrices glm::vec3 pos = glm::vec3(-((dim * offset.x) / 2.0f) + offset.x / 2.0f + x * offset.x, -((dim * offset.y) / 2.0f) + offset.y / 2.0f + y * offset.y, -((dim * offset.z) / 2.0f) + offset.z / 2.0f + z * offset.z); *modelMat = glm::translate(glm::mat4(), pos); *modelMat = glm::rotate(*modelMat, rotations[index].x, glm::vec3(1.0f, 1.0f, 0.0f)); *modelMat = glm::rotate(*modelMat, rotations[index].y, glm::vec3(0.0f, 1.0f, 0.0f)); *modelMat = glm::rotate(*modelMat, rotations[index].z, glm::vec3(0.0f, 0.0f, 1.0f)); } } } animationTimer = 0.0f; memcpy(uniformBuffers.dynamic.mapped, uboDataDynamic.model, uniformBuffers.dynamic.size); // Flush to make changes visible to the host VkMappedMemoryRange memoryRange = vks::initializers::mappedMemoryRange(); memoryRange.memory = uniformBuffers.dynamic.memory; memoryRange.size = uniformBuffers.dynamic.size; vkFlushMappedMemoryRanges(device, 1, &memoryRange); }
void StagingTexture2DLinear::CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) { // Flush memory range if currently mapped. if (m_map_pointer && !m_coherent) { VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, m_map_offset, m_map_size}; vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); } // Ensure any writes to the image are visible to the GPU. VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType nullptr, // const void* pNext VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask m_layout, // VkImageLayout oldLayout VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex m_image, // VkImage image {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange }; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); m_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; // Issue the image copy, host -> gpu. VkImageCopy copy_region = { {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers srcSubresource {0, 0, 0}, // VkOffset3D srcOffset {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers dstSubresource {static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D dstOffset {width, height, 1} // VkExtent3D extent }; vkCmdCopyImage(command_buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); }
void DeviceBufferAllocator::UploadData(Handle handle, VkDeviceSize offset, VkDeviceSize size, const void* data) { assert(size != 0); assert(data != nullptr); const Bank& assignedBank = mFamilyBanks[handle.eGeometryBufferType][handle.queueFamilyIndex][handle.bankIdx]; auto mappedMemoryRange = vkstruct<VkMappedMemoryRange>(); mappedMemoryRange.memory = assignedBank.memory; mappedMemoryRange.offset = handle.bankOffset + offset; mappedMemoryRange.size = size; void* mapAddr; gVkLastRes = vkMapMemory(gDevice.VkHandle(), mappedMemoryRange.memory, mappedMemoryRange.offset, mappedMemoryRange.size, 0, &mapAddr); VKFN_LAST_RES_SUCCESS_OR_QUIT(vkMapMemory); memcpy(mapAddr, data, static_cast<size_t>(size)); gVkLastRes = vkFlushMappedMemoryRanges(gDevice.VkHandle(), 1u, &mappedMemoryRange); VKFN_LAST_RES_SUCCESS_OR_QUIT(vkFlushMappedMemoryRanges); vkUnmapMemory(gDevice.VkHandle(), mappedMemoryRange.memory); }
int sample_main(int argc, char *argv[]) { VkResult U_ASSERT_ONLY res; struct sample_info info = {}; char sample_title[] = "Copy/Blit Image"; VkImageCreateInfo image_info; VkImage bltSrcImage; VkImage bltDstImage; VkMemoryRequirements memReq; VkMemoryAllocateInfo memAllocInfo; VkDeviceMemory dmem; unsigned char *pImgMem; 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); init_window_size(info, 640, 640); init_connection(info); init_window(info); init_swapchain_extension(info); VkSurfaceCapabilitiesKHR surfCapabilities; res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(info.gpus[0], info.surface, &surfCapabilities); if (!(surfCapabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { std::cout << "Surface cannot be destination of blit - abort \n"; exit(-1); } init_device(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_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); /* VULKAN_KEY_START */ VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(info.gpus[0], info.format, &formatProps); assert( (formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) && "Format cannot be used as transfer source"); VkSemaphore presentCompleteSemaphore; VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo; presentCompleteSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; presentCompleteSemaphoreCreateInfo.pNext = NULL; presentCompleteSemaphoreCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; res = vkCreateSemaphore(info.device, &presentCompleteSemaphoreCreateInfo, NULL, &presentCompleteSemaphore); assert(res == VK_SUCCESS); // Get the index of the next available swapchain image: res = vkAcquireNextImageKHR(info.device, info.swap_chain, UINT64_MAX, presentCompleteSemaphore, 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); // Create an image, map it, and write some values to the image image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.pNext = NULL; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.format = info.format; image_info.extent.width = info.width; image_info.extent.height = info.height; image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; image_info.samples = NUM_SAMPLES; image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = NULL; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; image_info.flags = 0; image_info.tiling = VK_IMAGE_TILING_LINEAR; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; res = vkCreateImage(info.device, &image_info, NULL, &bltSrcImage); memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.pNext = NULL; vkGetImageMemoryRequirements(info.device, bltSrcImage, &memReq); bool pass = memory_type_from_properties(info, memReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); memAllocInfo.allocationSize = memReq.size; res = vkAllocateMemory(info.device, &memAllocInfo, NULL, &dmem); res = vkBindImageMemory(info.device, bltSrcImage, dmem, 0); set_image_layout(info, bltSrcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); res = vkEndCommandBuffer(info.cmd); assert(res == VK_SUCCESS); VkFence cmdFence; init_fence(info, cmdFence); VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkSubmitInfo submit_info = {}; submit_info.pNext = NULL; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &presentCompleteSemaphore; submit_info.pWaitDstStageMask = &pipe_stage_flags; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &info.cmd; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, cmdFence); assert(res == VK_SUCCESS); /* Make sure command buffer is finished before mapping */ do { res = vkWaitForFences(info.device, 1, &cmdFence, VK_TRUE, FENCE_TIMEOUT); } while (res == VK_TIMEOUT); assert(res == VK_SUCCESS); vkDestroyFence(info.device, cmdFence, NULL); res = vkMapMemory(info.device, dmem, 0, memReq.size, 0, (void **)&pImgMem); // Checkerboard of 8x8 pixel squares for (int row = 0; row < info.height; row++) { for (int col = 0; col < info.width; col++) { unsigned char rgb = (((row & 0x8) == 0) ^ ((col & 0x8) == 0)) * 255; pImgMem[0] = rgb; pImgMem[1] = rgb; pImgMem[2] = rgb; pImgMem[3] = 255; pImgMem += 4; } } // Flush the mapped memory and then unmap it Assume it isn't coherent since // we didn't really confirm VkMappedMemoryRange memRange; memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; memRange.pNext = NULL; memRange.memory = dmem; memRange.offset = 0; memRange.size = memReq.size; res = vkFlushMappedMemoryRanges(info.device, 1, &memRange); vkUnmapMemory(info.device, dmem); vkResetCommandBuffer(info.cmd, 0); execute_begin_command_buffer(info); set_image_layout(info, bltSrcImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); bltDstImage = info.buffers[info.current_buffer].image; // init_swap_chain will create the images as color attachment optimal // but we want transfer dst optimal set_image_layout(info, bltDstImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // Do a 32x32 blit to all of the dst image - should get big squares VkImageBlit region; region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.srcSubresource.mipLevel = 0; region.srcSubresource.baseArrayLayer = 0; region.srcSubresource.layerCount = 1; region.srcOffsets[0].x = 0; region.srcOffsets[0].y = 0; region.srcOffsets[0].z = 0; region.srcOffsets[1].x = 32; region.srcOffsets[1].y = 32; region.srcOffsets[1].z = 1; region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.dstSubresource.mipLevel = 0; region.dstSubresource.baseArrayLayer = 0; region.dstSubresource.layerCount = 1; region.dstOffsets[0].x = 0; region.dstOffsets[0].y = 0; region.dstOffsets[0].z = 0; region.dstOffsets[1].x = info.width; region.dstOffsets[1].y = info.height; region.dstOffsets[1].z = 1; vkCmdBlitImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion, VK_FILTER_LINEAR); // Do a image copy to part of the dst image - checks should stay small VkImageCopy cregion; cregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; cregion.srcSubresource.mipLevel = 0; cregion.srcSubresource.baseArrayLayer = 0; cregion.srcSubresource.layerCount = 1; cregion.srcOffset.x = 0; cregion.srcOffset.y = 0; cregion.srcOffset.z = 0; cregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; cregion.dstSubresource.mipLevel = 0; cregion.dstSubresource.baseArrayLayer = 0; cregion.dstSubresource.layerCount = 1; cregion.dstOffset.x = 256; cregion.dstOffset.y = 256; cregion.dstOffset.z = 0; cregion.extent.width = 128; cregion.extent.height = 128; cregion.extent.depth = 1; vkCmdCopyImage(info.cmd, bltSrcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, bltDstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion); VkImageMemoryBarrier prePresentBarrier = {}; prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; prePresentBarrier.pNext = NULL; prePresentBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_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(info.cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &prePresentBarrier); res = vkEndCommandBuffer(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); submit_info.pNext = NULL; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = NULL; submit_info.pWaitDstStageMask = NULL; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &info.cmd; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = NULL; /* Queue the command buffer for execution */ res = vkQueueSubmit(info.queue, 1, &submit_info, drawFence); assert(res == VK_SUCCESS); res = vkQueueWaitIdle(info.queue); assert(res == VK_SUCCESS); /* Now present the image in the window */ VkPresentInfoKHR present; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present.pNext = NULL; present.swapchainCount = 1; present.pSwapchains = &info.swap_chain; present.pImageIndices = &info.current_buffer; present.pWaitSemaphores = NULL; present.waitSemaphoreCount = 0; present.pResults = NULL; /* 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.queue, &present); assert(res == VK_SUCCESS); wait_seconds(1); /* VULKAN_KEY_END */ if (info.save_images) write_ppm(info, "copyblitimage"); vkDestroySemaphore(info.device, presentCompleteSemaphore, NULL); vkDestroyFence(info.device, drawFence, NULL); vkDestroyImage(info.device, bltSrcImage, NULL); vkFreeMemory(info.device, dmem, NULL); destroy_swap_chain(info); destroy_command_buffer(info); destroy_command_pool(info); destroy_device(info); destroy_window(info); destroy_instance(info); return 0; }
void VulkanTexturedQuad::CreateTexture (VkCommandBuffer uploadCommandList) { int width, height; auto image = LoadImageFromMemory (RubyTexture, sizeof (RubyTexture), 1, &width, &height); VkImageCreateInfo imageCreateInfo = {}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.pNext = nullptr; imageCreateInfo.queueFamilyIndexCount = 1; uint32_t queueFamilyIndex = static_cast<uint32_t> (queueFamilyIndex_); imageCreateInfo.pQueueFamilyIndices = &queueFamilyIndex; imageCreateInfo.mipLevels = 1; imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; imageCreateInfo.arrayLayers = 1; imageCreateInfo.extent.depth = 1; imageCreateInfo.extent.height = height; imageCreateInfo.extent.width = width; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; vkCreateImage (device_, &imageCreateInfo, nullptr, &rubyImage_); VkMemoryRequirements requirements = {}; vkGetImageMemoryRequirements (device_, rubyImage_, &requirements); VkDeviceSize requiredSizeForImage = requirements.size; auto memoryHeaps = EnumerateHeaps (physicalDevice_); deviceImageMemory_ = AllocateMemory (memoryHeaps, device_, static_cast<int> (requiredSizeForImage), requirements.memoryTypeBits, MT_DeviceLocal); vkBindImageMemory (device_, rubyImage_, deviceImageMemory_, 0); VkBufferCreateInfo bufferCreateInfo = {}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.pNext = nullptr; bufferCreateInfo.queueFamilyIndexCount = 1; bufferCreateInfo.pQueueFamilyIndices = &queueFamilyIndex; bufferCreateInfo.size = requiredSizeForImage; bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; vkCreateBuffer (device_, &bufferCreateInfo, nullptr, &uploadImageBuffer_); vkGetBufferMemoryRequirements (device_, uploadImageBuffer_, &requirements); VkDeviceSize requiredSizeForBuffer = requirements.size; bool memoryIsHostCoherent = false; uploadImageMemory_ = AllocateMemory (memoryHeaps, device_, static_cast<int> (requiredSizeForBuffer), requirements.memoryTypeBits, MT_HostVisible, &memoryIsHostCoherent); vkBindBufferMemory (device_, uploadImageBuffer_, uploadImageMemory_, 0); void* data = nullptr; vkMapMemory (device_, uploadImageMemory_, 0, VK_WHOLE_SIZE, 0, &data); ::memcpy (data, image.data (), image.size ()); if (!memoryIsHostCoherent) { VkMappedMemoryRange mappedMemoryRange = {}; mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedMemoryRange.memory = uploadImageMemory_; mappedMemoryRange.offset = 0; mappedMemoryRange.size = VK_WHOLE_SIZE; vkFlushMappedMemoryRanges (device_, 1, &mappedMemoryRange); } vkUnmapMemory (device_, uploadImageMemory_); VkBufferImageCopy bufferImageCopy = {}; bufferImageCopy.imageExtent.width = width; bufferImageCopy.imageExtent.height = height; bufferImageCopy.imageExtent.depth = 1; bufferImageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferImageCopy.imageSubresource.mipLevel = 0; bufferImageCopy.imageSubresource.layerCount = 1; VkImageMemoryBarrier imageBarrier = {}; imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageBarrier.pNext = nullptr; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageBarrier.srcAccessMask = 0; imageBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageBarrier.image = rubyImage_; imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBarrier.subresourceRange.layerCount = 1; imageBarrier.subresourceRange.levelCount = 1; vkCmdPipelineBarrier (uploadCommandList, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); vkCmdCopyBufferToImage (uploadCommandList, uploadImageBuffer_, rubyImage_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopy); imageBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; imageBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; imageBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; imageBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkCmdPipelineBarrier (uploadCommandList, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); VkImageViewCreateInfo imageViewCreateInfo = {}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.format = imageCreateInfo.format; imageViewCreateInfo.image = rubyImage_; imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCreateInfo.subresourceRange.levelCount = 1; imageViewCreateInfo.subresourceRange.layerCount = 1; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; vkCreateImageView (device_, &imageViewCreateInfo, nullptr, &rubyImageView_); }
void VulkanTexturedQuad::CreateMeshBuffers(VkCommandBuffer uploadCommandBuffer) { struct Vertex { float position[3]; float uv[2]; }; static const Vertex vertices[4] = { // Upper Left { { -1.0f, 1.0f, 0 }, { 0, 1 } }, // Upper Right { { 1.0f, 1.0f, 0 }, { 1, 1 } }, // Bottom right { { 1.0f, -1.0f, 0 }, { 1, 0 } }, // Bottom left { { -1.0f, -1.0f, 0 }, { 0, 0 } } }; static const int indices[6] = { 0, 1, 2, 2, 3, 0 }; auto memoryHeaps = EnumerateHeaps(physicalDevice_); indexBuffer_ = AllocateBuffer(device_, sizeof(indices), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); vertexBuffer_ = AllocateBuffer(device_, sizeof(vertices), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); VkMemoryRequirements vertexBufferMemoryRequirements = {}; vkGetBufferMemoryRequirements(device_, vertexBuffer_, &vertexBufferMemoryRequirements); VkMemoryRequirements indexBufferMemoryRequirements = {}; vkGetBufferMemoryRequirements(device_, indexBuffer_, &indexBufferMemoryRequirements); VkDeviceSize bufferSize = vertexBufferMemoryRequirements.size; // We want to place the index buffer behind the vertex buffer. Need to take // the alignment into account to find the next suitable location VkDeviceSize indexBufferOffset = RoundToNextMultiple(bufferSize, indexBufferMemoryRequirements.alignment); bufferSize = indexBufferOffset + indexBufferMemoryRequirements.size; deviceBufferMemory_ = AllocateMemory(memoryHeaps, device_, static_cast<int>(bufferSize), vertexBufferMemoryRequirements.memoryTypeBits & indexBufferMemoryRequirements.memoryTypeBits, MT_DeviceLocal); vkBindBufferMemory(device_, vertexBuffer_, deviceBufferMemory_, 0); vkBindBufferMemory(device_, indexBuffer_, deviceBufferMemory_, indexBufferOffset); uploadBufferBuffer_ = AllocateBuffer (device_, static_cast<int> (bufferSize), VK_BUFFER_USAGE_TRANSFER_SRC_BIT); VkMemoryRequirements uploadBufferMemoryRequirements = {}; vkGetBufferMemoryRequirements (device_, uploadBufferBuffer_, &uploadBufferMemoryRequirements); bool memoryIsHostCoherent = false; uploadBufferMemory_ = AllocateMemory(memoryHeaps, device_, static_cast<int>(uploadBufferMemoryRequirements.size), vertexBufferMemoryRequirements.memoryTypeBits & indexBufferMemoryRequirements.memoryTypeBits, MT_HostVisible, &memoryIsHostCoherent); vkBindBufferMemory (device_, uploadBufferBuffer_, uploadBufferMemory_, 0); void* mapping = nullptr; vkMapMemory(device_, uploadBufferMemory_, 0, VK_WHOLE_SIZE, 0, &mapping); ::memcpy(mapping, vertices, sizeof(vertices)); ::memcpy(static_cast<uint8_t*> (mapping) + indexBufferOffset, indices, sizeof(indices)); if (!memoryIsHostCoherent) { VkMappedMemoryRange mappedMemoryRange = {}; mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedMemoryRange.memory = uploadBufferMemory_; mappedMemoryRange.offset = 0; mappedMemoryRange.size = VK_WHOLE_SIZE; vkFlushMappedMemoryRanges (device_, 1, &mappedMemoryRange); } vkUnmapMemory(device_, uploadBufferMemory_); VkBufferCopy vertexCopy = {}; vertexCopy.size = sizeof (vertices); VkBufferCopy indexCopy = {}; indexCopy.size = sizeof (indices); indexCopy.srcOffset = indexBufferOffset; vkCmdCopyBuffer (uploadCommandBuffer, uploadBufferBuffer_, vertexBuffer_, 1, &vertexCopy); vkCmdCopyBuffer (uploadCommandBuffer, uploadBufferBuffer_, indexBuffer_, 1, &indexCopy); VkBufferMemoryBarrier uploadBarriers[2] = {}; uploadBarriers[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; uploadBarriers[0].buffer = vertexBuffer_; uploadBarriers[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; uploadBarriers[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; uploadBarriers[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; uploadBarriers[0].dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; uploadBarriers[0].size = VK_WHOLE_SIZE; uploadBarriers[1].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; uploadBarriers[1].buffer = indexBuffer_; uploadBarriers[1].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; uploadBarriers[1].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; uploadBarriers[1].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; uploadBarriers[1].dstAccessMask = VK_ACCESS_INDEX_READ_BIT; uploadBarriers[1].size = VK_WHOLE_SIZE; vkCmdPipelineBarrier (uploadCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, 2, uploadBarriers, 0, nullptr); }
void VulkanQuad::CreateMeshBuffers (VkCommandBuffer /*uploadCommandBuffer*/) { struct Vertex { float position[3]; float uv[2]; }; static const Vertex vertices[4] = { // Upper Left { { -1.0f, 1.0f, 0 }, { 0, 0 } }, // Upper Right { { 1.0f, 1.0f, 0 }, { 1, 0 } }, // Bottom right { { 1.0f, -1.0f, 0 }, { 1, 1 } }, // Bottom left { { -1.0f, -1.0f, 0 }, { 0, 1 } } }; static const int indices[6] = { 0, 1, 2, 2, 3, 0 }; auto memoryHeaps = EnumerateHeaps (physicalDevice_); indexBuffer_ = AllocateBuffer(device_, sizeof(indices), VK_BUFFER_USAGE_INDEX_BUFFER_BIT); vertexBuffer_ = AllocateBuffer (device_, sizeof (vertices), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); VkMemoryRequirements vertexBufferMemoryRequirements = {}; vkGetBufferMemoryRequirements(device_, vertexBuffer_, &vertexBufferMemoryRequirements); VkMemoryRequirements indexBufferMemoryRequirements = {}; vkGetBufferMemoryRequirements(device_, indexBuffer_, &indexBufferMemoryRequirements); VkDeviceSize bufferSize = vertexBufferMemoryRequirements.size; // We want to place the index buffer behind the vertex buffer. Need to take // the alignment into account to find the next suitable location VkDeviceSize indexBufferOffset = RoundToNextMultiple(bufferSize, indexBufferMemoryRequirements.alignment); bufferSize = indexBufferOffset + indexBufferMemoryRequirements.size; bool memoryIsHostCoherent = false; deviceMemory_ = AllocateMemory(memoryHeaps, device_, static_cast<int>(bufferSize), &memoryIsHostCoherent); vkBindBufferMemory (device_, vertexBuffer_, deviceMemory_, 0); vkBindBufferMemory (device_, indexBuffer_, deviceMemory_, indexBufferOffset); void* mapping = nullptr; vkMapMemory (device_, deviceMemory_, 0, VK_WHOLE_SIZE, 0, &mapping); ::memcpy (mapping, vertices, sizeof (vertices)); ::memcpy (static_cast<uint8_t*> (mapping) + indexBufferOffset, indices, sizeof (indices)); if (!memoryIsHostCoherent) { VkMappedMemoryRange mappedMemoryRange = {}; mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; mappedMemoryRange.memory = deviceMemory_; mappedMemoryRange.offset = 0; mappedMemoryRange.size = VK_WHOLE_SIZE; vkFlushMappedMemoryRanges (device_, 1, &mappedMemoryRange); } vkUnmapMemory (device_, deviceMemory_); }
VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { SCOPED_DBG_SINK(); size_t tempmemSize = sizeof(VkSubmitInfo) * submitCount; // need to count how many semaphore and command buffer arrays to allocate for for(uint32_t i = 0; i < submitCount; i++) { tempmemSize += pSubmits[i].commandBufferCount * sizeof(VkCommandBuffer); tempmemSize += pSubmits[i].signalSemaphoreCount * sizeof(VkSemaphore); tempmemSize += pSubmits[i].waitSemaphoreCount * sizeof(VkSemaphore); } byte *memory = GetTempMemory(tempmemSize); VkSubmitInfo *unwrappedSubmits = (VkSubmitInfo *)memory; VkSemaphore *unwrappedWaitSems = (VkSemaphore *)(unwrappedSubmits + submitCount); for(uint32_t i = 0; i < submitCount; i++) { RDCASSERT(pSubmits[i].sType == VK_STRUCTURE_TYPE_SUBMIT_INFO && pSubmits[i].pNext == NULL); unwrappedSubmits[i] = pSubmits[i]; unwrappedSubmits[i].pWaitSemaphores = unwrappedSubmits[i].waitSemaphoreCount ? unwrappedWaitSems : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].waitSemaphoreCount; o++) unwrappedWaitSems[o] = Unwrap(pSubmits[i].pWaitSemaphores[o]); unwrappedWaitSems += unwrappedSubmits[i].waitSemaphoreCount; VkCommandBuffer *unwrappedCommandBuffers = (VkCommandBuffer *)unwrappedWaitSems; unwrappedSubmits[i].pCommandBuffers = unwrappedSubmits[i].commandBufferCount ? unwrappedCommandBuffers : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].commandBufferCount; o++) unwrappedCommandBuffers[o] = Unwrap(pSubmits[i].pCommandBuffers[o]); unwrappedCommandBuffers += unwrappedSubmits[i].commandBufferCount; VkSemaphore *unwrappedSignalSems = (VkSemaphore *)unwrappedCommandBuffers; unwrappedSubmits[i].pSignalSemaphores = unwrappedSubmits[i].signalSemaphoreCount ? unwrappedSignalSems : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].signalSemaphoreCount; o++) unwrappedSignalSems[o] = Unwrap(pSubmits[i].pSignalSemaphores[o]); } VkResult ret = ObjDisp(queue)->QueueSubmit(Unwrap(queue), submitCount, unwrappedSubmits, Unwrap(fence)); bool capframe = false; set<ResourceId> refdIDs; for(uint32_t s = 0; s < submitCount; s++) { for(uint32_t i = 0; i < pSubmits[s].commandBufferCount; i++) { ResourceId cmd = GetResID(pSubmits[s].pCommandBuffers[i]); VkResourceRecord *record = GetRecord(pSubmits[s].pCommandBuffers[i]); { SCOPED_LOCK(m_ImageLayoutsLock); GetResourceManager()->ApplyBarriers(record->bakedCommands->cmdInfo->imgbarriers, m_ImageLayouts); } // need to lock the whole section of code, not just the check on // m_State, as we also need to make sure we don't check the state, // start marking dirty resources then while we're doing so the // state becomes capframe. // the next sections where we mark resources referenced and add // the submit chunk to the frame record don't have to be protected. // Only the decision of whether we're inframe or not, and marking // dirty. { SCOPED_LOCK(m_CapTransitionLock); if(m_State == WRITING_CAPFRAME) { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkPendingDirty(*it); capframe = true; } else { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkDirtyResource(*it); } } if(capframe) { // for each bound descriptor set, mark it referenced as well as all resources currently // bound to it for(auto it = record->bakedCommands->cmdInfo->boundDescSets.begin(); it != record->bakedCommands->cmdInfo->boundDescSets.end(); ++it) { GetResourceManager()->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read); VkResourceRecord *setrecord = GetRecord(*it); for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); refit != setrecord->descInfo->bindFrameRefs.end(); ++refit) { refdIDs.insert(refit->first); GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second); if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT) { VkResourceRecord *sparserecord = GetResourceManager()->GetResourceRecord(refit->first); GetResourceManager()->MarkSparseMapReferenced(sparserecord->sparseInfo); } } } for(auto it = record->bakedCommands->cmdInfo->sparse.begin(); it != record->bakedCommands->cmdInfo->sparse.end(); ++it) GetResourceManager()->MarkSparseMapReferenced(*it); // pull in frame refs from this baked command buffer record->bakedCommands->AddResourceReferences(GetResourceManager()); record->bakedCommands->AddReferencedIDs(refdIDs); // ref the parent command buffer by itself, this will pull in the cmd buffer pool GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), eFrameRef_Read); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->subcmds.size(); sub++) { record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddResourceReferences( GetResourceManager()); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddReferencedIDs(refdIDs); GetResourceManager()->MarkResourceFrameReferenced( record->bakedCommands->cmdInfo->subcmds[sub]->GetResourceID(), eFrameRef_Read); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddRef(); } GetResourceManager()->MarkResourceFrameReferenced(GetResID(queue), eFrameRef_Read); if(fence != VK_NULL_HANDLE) GetResourceManager()->MarkResourceFrameReferenced(GetResID(fence), eFrameRef_Read); { SCOPED_LOCK(m_CmdBufferRecordsLock); m_CmdBufferRecords.push_back(record->bakedCommands); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->subcmds.size(); sub++) m_CmdBufferRecords.push_back(record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands); } record->bakedCommands->AddRef(); } record->cmdInfo->dirtied.clear(); } } if(capframe) { vector<VkResourceRecord *> maps; { SCOPED_LOCK(m_CoherentMapsLock); maps = m_CoherentMaps; } for(auto it = maps.begin(); it != maps.end(); ++it) { VkResourceRecord *record = *it; MemMapState &state = *record->memMapState; // potential persistent map if(state.mapCoherent && state.mappedPtr && !state.mapFlushed) { // only need to flush memory that could affect this submitted batch of work if(refdIDs.find(record->GetResourceID()) == refdIDs.end()) { RDCDEBUG("Map of memory %llu not referenced in this queue - not flushing", record->GetResourceID()); continue; } size_t diffStart = 0, diffEnd = 0; bool found = true; // enabled as this is necessary for programs with very large coherent mappings // (> 1GB) as otherwise more than a couple of vkQueueSubmit calls leads to vast // memory allocation. There might still be bugs lurking in here though #if 1 // this causes vkFlushMappedMemoryRanges call to allocate and copy to refData // from serialised buffer. We want to copy *precisely* the serialised data, // otherwise there is a gap in time between serialising out a snapshot of // the buffer and whenever we then copy into the ref data, e.g. below. // during this time, data could be written to the buffer and it won't have // been caught in the serialised snapshot, and if it doesn't change then // it *also* won't be caught in any future FindDiffRange() calls. // // Likewise once refData is allocated, the call below will also update it // with the data serialised out for the same reason. // // Note: it's still possible that data is being written to by the // application while it's being serialised out in the snapshot below. That // is OK, since the application is responsible for ensuring it's not writing // data that would be needed by the GPU in this submit. As long as the // refdata we use for future use is identical to what was serialised, we // shouldn't miss anything state.needRefData = true; // if we have a previous set of data, compare. // otherwise just serialise it all if(state.refData) found = FindDiffRange((byte *)state.mappedPtr, state.refData, (size_t)state.mapSize, diffStart, diffEnd); else #endif diffEnd = (size_t)state.mapSize; if(found) { // MULTIDEVICE should find the device for this queue. // MULTIDEVICE only want to flush maps associated with this queue VkDevice dev = GetDev(); { RDCLOG("Persistent map flush forced for %llu (%llu -> %llu)", record->GetResourceID(), (uint64_t)diffStart, (uint64_t)diffEnd); VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, (VkDeviceMemory)(uint64_t)record->Resource, state.mapOffset + diffStart, diffEnd - diffStart}; vkFlushMappedMemoryRanges(dev, 1, &range); state.mapFlushed = false; } GetResourceManager()->MarkPendingDirty(record->GetResourceID()); } else { RDCDEBUG("Persistent map flush not needed for %llu", record->GetResourceID()); } } } { CACHE_THREAD_SERIALISER(); for(uint32_t s = 0; s < submitCount; s++) { SCOPED_SERIALISE_CONTEXT(QUEUE_SUBMIT); Serialise_vkQueueSubmit(localSerialiser, queue, 1, &pSubmits[s], fence); m_FrameCaptureRecord->AddChunk(scope.Get()); for(uint32_t sem = 0; sem < pSubmits[s].waitSemaphoreCount; sem++) GetResourceManager()->MarkResourceFrameReferenced( GetResID(pSubmits[s].pWaitSemaphores[sem]), eFrameRef_Read); for(uint32_t sem = 0; sem < pSubmits[s].signalSemaphoreCount; sem++) GetResourceManager()->MarkResourceFrameReferenced( GetResID(pSubmits[s].pSignalSemaphores[sem]), eFrameRef_Read); } } } return ret; }
void vkeGameRendererDynamic::update(){ VulkanAppContext *ctxt = VulkanAppContext::GetInstance(); VulkanDC *dc = VulkanDC::Get(); VulkanDC::Device *device = dc->getDefaultDevice(); VkCommandBuffer cmd = VK_NULL_HANDLE; VulkanDC::Device::Queue::CommandBufferID cmdID = INIT_COMMAND_ID + 300; static float sTime = 0.0f; static uint32_t *uniforms = NULL; uint32_t sz = (sizeof(VkeNodeUniform) * 100) + (64 * 64); static bool ismapped(false); nv_math::mat4f tempMatrix; const float r2d = 180 / (3.14159265359); static float xTheta(0.0f); //if (!ismapped){ VKA_CHECK_ERROR(vkMapMemory(getDefaultDevice(), m_uniforms_staging, 0, sz, 0, (void **)&uniforms), "Could not map buffer memory.\n"); //ismapped = true; //} for (uint32_t i = 0; i < 64; ++i){ size_t pointerOffset = (sizeof(VkeNodeUniform) * 100) + (64 * i); nv_math::mat4f *matPtr = (nv_math::mat4f*)(((uint8_t*)uniforms) + pointerOffset); m_flight_paths[i]->update(matPtr, sTime); } m_node_data->update((VkeNodeUniform*)uniforms); m_camera->setViewport(0, 0, (float)m_width, (float)m_height); m_camera->update(); VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; memRange.memory = m_uniforms_staging; memRange.offset = 0; memRange.size = sz; vkFlushMappedMemoryRanges(device->getVKDevice(), 1, &memRange); vkUnmapMemory(device->getVKDevice(), m_uniforms_staging); if (!m_is_first_frame){ VkSubmitInfo subInfo; memset(&subInfo, 0, sizeof(subInfo)); subInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; subInfo.commandBufferCount = 1; subInfo.pCommandBuffers = &m_update_commands[m_current_buffer_index]; vkQueueSubmit(dc->getDefaultQueue()->getVKQueue(), 1, &subInfo, VK_NULL_HANDLE); vkQueueWaitIdle(dc->getDefaultQueue()->getVKQueue()); #if defined(WIN32) subInfo.waitSemaphoreCount = 1; subInfo.pWaitSemaphores = &m_present_done; subInfo.signalSemaphoreCount = 0; subInfo.pSignalSemaphores = &m_render_done; #endif subInfo.pCommandBuffers = &m_primary_commands[m_current_buffer_index]; vkQueueSubmit(dc->getDefaultQueue()->getVKQueue(), 1, &subInfo, VK_NULL_HANDLE); } else{ m_is_first_frame = false; } present(); m_current_buffer_index++; m_current_buffer_index %= 2; sTime += 0.16; generateDrawCommands(); }
bool ImGui_ImplGlfwVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) { ImGuiIO& io = ImGui::GetIO(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); size_t upload_size = width*height*4*sizeof(char); VkResult err; // Create the Image: { VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.imageType = VK_IMAGE_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.extent.width = width; info.extent.height = height; info.extent.depth = 1; info.mipLevels = 1; info.arrayLayers = 1; info.samples = VK_SAMPLE_COUNT_1_BIT; info.tiling = VK_IMAGE_TILING_OPTIMAL; info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; err = vkCreateImage(g_Device, &info, g_Allocator, &g_FontImage); ImGui_ImplGlfwVulkan_VkResult(err); VkMemoryRequirements req; vkGetImageMemoryRequirements(g_Device, g_FontImage, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_FontMemory); ImGui_ImplGlfwVulkan_VkResult(err); err = vkBindImageMemory(g_Device, g_FontImage, g_FontMemory, 0); ImGui_ImplGlfwVulkan_VkResult(err); } // Create the Image View: { VkResult err; VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.image = g_FontImage; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; err = vkCreateImageView(g_Device, &info, g_Allocator, &g_FontView); ImGui_ImplGlfwVulkan_VkResult(err); } // Update the Descriptor Set: { VkDescriptorImageInfo desc_image[1] = {}; desc_image[0].sampler = g_FontSampler; desc_image[0].imageView = g_FontView; desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet write_desc[1] = {}; write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_desc[0].dstSet = g_DescriptorSet; write_desc[0].descriptorCount = 1; write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write_desc[0].pImageInfo = desc_image; vkUpdateDescriptorSets(g_Device, 1, write_desc, 0, NULL); } // Create the Upload Buffer: { VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = upload_size; buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_UploadBuffer); ImGui_ImplGlfwVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_UploadBuffer, &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_UploadBufferMemory); ImGui_ImplGlfwVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_UploadBuffer, g_UploadBufferMemory, 0); ImGui_ImplGlfwVulkan_VkResult(err); } // Upload to Buffer: { char* map = NULL; err = vkMapMemory(g_Device, g_UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); ImGui_ImplGlfwVulkan_VkResult(err); memcpy(map, pixels, upload_size); VkMappedMemoryRange range[1] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = g_UploadBufferMemory; range[0].size = upload_size; err = vkFlushMappedMemoryRanges(g_Device, 1, range); ImGui_ImplGlfwVulkan_VkResult(err); vkUnmapMemory(g_Device, g_UploadBufferMemory); } // Copy to Image: { VkImageMemoryBarrier copy_barrier[1] = {}; copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].image = g_FontImage; copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier); VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; region.imageExtent.width = width; region.imageExtent.height = height; vkCmdCopyBufferToImage(command_buffer, g_UploadBuffer, g_FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].image = g_FontImage; use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier); } // Store our identifier io.Fonts->TexID = (void *)(intptr_t)g_FontImage; return true; }
// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) void ImGui_ImplGlfwVulkan_RenderDrawLists(ImDrawData* draw_data) { VkResult err; ImGuiIO& io = ImGui::GetIO(); // Create the Vertex Buffer: size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); if (!g_VertexBuffer[g_FrameIndex] || g_VertexBufferSize[g_FrameIndex] < vertex_size) { if (g_VertexBuffer[g_FrameIndex]) vkDestroyBuffer(g_Device, g_VertexBuffer[g_FrameIndex], g_Allocator); if (g_VertexBufferMemory[g_FrameIndex]) vkFreeMemory(g_Device, g_VertexBufferMemory[g_FrameIndex], g_Allocator); size_t vertex_buffer_size = ((vertex_size-1) / g_BufferMemoryAlignment+1) * g_BufferMemoryAlignment; VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = vertex_buffer_size; buffer_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_VertexBuffer[g_FrameIndex]); ImGui_ImplGlfwVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_VertexBuffer[g_FrameIndex], &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_VertexBufferMemory[g_FrameIndex]); ImGui_ImplGlfwVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_VertexBuffer[g_FrameIndex], g_VertexBufferMemory[g_FrameIndex], 0); ImGui_ImplGlfwVulkan_VkResult(err); g_VertexBufferSize[g_FrameIndex] = vertex_buffer_size; } // Create the Index Buffer: size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); if (!g_IndexBuffer[g_FrameIndex] || g_IndexBufferSize[g_FrameIndex] < index_size) { if (g_IndexBuffer[g_FrameIndex]) vkDestroyBuffer(g_Device, g_IndexBuffer[g_FrameIndex], g_Allocator); if (g_IndexBufferMemory[g_FrameIndex]) vkFreeMemory(g_Device, g_IndexBufferMemory[g_FrameIndex], g_Allocator); size_t index_buffer_size = ((index_size-1) / g_BufferMemoryAlignment+1) * g_BufferMemoryAlignment; VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = index_buffer_size; buffer_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &g_IndexBuffer[g_FrameIndex]); ImGui_ImplGlfwVulkan_VkResult(err); VkMemoryRequirements req; vkGetBufferMemoryRequirements(g_Device, g_IndexBuffer[g_FrameIndex], &req); g_BufferMemoryAlignment = (g_BufferMemoryAlignment > req.alignment) ? g_BufferMemoryAlignment : req.alignment; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplGlfwVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &g_IndexBufferMemory[g_FrameIndex]); ImGui_ImplGlfwVulkan_VkResult(err); err = vkBindBufferMemory(g_Device, g_IndexBuffer[g_FrameIndex], g_IndexBufferMemory[g_FrameIndex], 0); ImGui_ImplGlfwVulkan_VkResult(err); g_IndexBufferSize[g_FrameIndex] = index_buffer_size; } // Upload Vertex and index Data: { ImDrawVert* vtx_dst; ImDrawIdx* idx_dst; err = vkMapMemory(g_Device, g_VertexBufferMemory[g_FrameIndex], 0, vertex_size, 0, (void**)(&vtx_dst)); ImGui_ImplGlfwVulkan_VkResult(err); err = vkMapMemory(g_Device, g_IndexBufferMemory[g_FrameIndex], 0, index_size, 0, (void**)(&idx_dst)); ImGui_ImplGlfwVulkan_VkResult(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } VkMappedMemoryRange range[2] = {}; range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[0].memory = g_VertexBufferMemory[g_FrameIndex]; range[0].size = vertex_size; range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range[1].memory = g_IndexBufferMemory[g_FrameIndex]; range[1].size = index_size; err = vkFlushMappedMemoryRanges(g_Device, 2, range); ImGui_ImplGlfwVulkan_VkResult(err); vkUnmapMemory(g_Device, g_VertexBufferMemory[g_FrameIndex]); vkUnmapMemory(g_Device, g_IndexBufferMemory[g_FrameIndex]); } // Bind pipeline and descriptor sets: { vkCmdBindPipeline(g_CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_Pipeline); VkDescriptorSet desc_set[1] = {g_DescriptorSet}; vkCmdBindDescriptorSets(g_CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, g_PipelineLayout, 0, 1, desc_set, 0, NULL); } // Bind Vertex And Index Buffer: { VkBuffer vertex_buffers[1] = {g_VertexBuffer[g_FrameIndex]}; VkDeviceSize vertex_offset[1] = {0}; vkCmdBindVertexBuffers(g_CommandBuffer, 0, 1, vertex_buffers, vertex_offset); vkCmdBindIndexBuffer(g_CommandBuffer, g_IndexBuffer[g_FrameIndex], 0, VK_INDEX_TYPE_UINT16); } // Setup viewport: { VkViewport viewport; viewport.x = 0; viewport.y = 0; viewport.width = ImGui::GetIO().DisplaySize.x; viewport.height = ImGui::GetIO().DisplaySize.y; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(g_CommandBuffer, 0, 1, &viewport); } // Setup scale and translation: { float scale[2]; scale[0] = 2.0f/io.DisplaySize.x; scale[1] = 2.0f/io.DisplaySize.y; float translate[2]; translate[0] = -1.0f; translate[1] = -1.0f; vkCmdPushConstants(g_CommandBuffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); vkCmdPushConstants(g_CommandBuffer, g_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); } // Render the command lists: int vtx_offset = 0; int idx_offset = 0; for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { pcmd->UserCallback(cmd_list, pcmd); } else { VkRect2D scissor; scissor.offset.x = (int32_t)(pcmd->ClipRect.x); scissor.offset.y = (int32_t)(pcmd->ClipRect.y); scissor.extent.width = (uint32_t)(pcmd->ClipRect.z - pcmd->ClipRect.x); scissor.extent.height = (uint32_t)(pcmd->ClipRect.w - pcmd->ClipRect.y + 1); // TODO: + 1?????? vkCmdSetScissor(g_CommandBuffer, 0, 1, &scissor); vkCmdDrawIndexed(g_CommandBuffer, pcmd->ElemCount, 1, idx_offset, vtx_offset, 0); } idx_offset += pcmd->ElemCount; } vtx_offset += cmd_list->VtxBuffer.Size; } }