static void VulkanBufferEnsureHostWritesVisible(VkCommandBuffer cmd, rhi_buffer_data* Buffer) { if (Buffer->memoryUsage == MemoryUsage_CpuToGpu && Buffer->Flags & VulkanFlag_Dirty) { VkBufferMemoryBarrier2 barrier{}; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; barrier.srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT; barrier.srcAccessMask = VK_ACCESS_2_HOST_WRITE_BIT; barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.buffer = Buffer->buffer; barrier.offset = Buffer->dirtyBegin; barrier.size = Buffer->dirtyEnd - Buffer->dirtyBegin; VkDependencyInfo dependencyInfo{}; dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dependencyInfo.bufferMemoryBarrierCount = 1; dependencyInfo.pBufferMemoryBarriers = &barrier; vkCmdPipelineBarrier2(cmd, &dependencyInfo); Buffer->Flags &= ~VulkanFlag_Dirty; } } struct vulkan_buffer_sync_state_info { VkPipelineStageFlags2 stage; VkAccessFlags2 access; }; vulkan_buffer_sync_state_info VulkanBufferStateGetSyncInfo(rhi_buffer_state state) { vulkan_buffer_sync_state_info ret{}; switch (state) { case BufferState_Undefined: { ret.stage = VK_PIPELINE_STAGE_2_NONE; ret.access = VK_ACCESS_2_NONE; } break; case BufferState_CopySrc: { ret.stage = VK_PIPELINE_STAGE_2_COPY_BIT; ret.access = VK_ACCESS_2_TRANSFER_READ_BIT; } break; case BufferState_CopyDst: { ret.stage = VK_PIPELINE_STAGE_2_COPY_BIT; ret.access = VK_ACCESS_2_TRANSFER_WRITE_BIT;; } break; case BufferState_ShaderRead: { ret.stage = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; ret.access = VK_ACCESS_2_UNIFORM_READ_BIT | VK_ACCESS_2_SHADER_STORAGE_READ_BIT; } break; case BufferState_ShaderWrite: { ret.stage = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; ret.access = VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT; } break; case BufferState_Vertex: { ret.stage = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT; ret.access = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT; } break; case BufferState_Index: { ret.stage = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT; ret.access = VK_ACCESS_2_INDEX_READ_BIT; } break; case BufferState_Indirect: { ret.stage = VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT; ret.access = VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT; } break; default: { ASSERT(false); } break; } return ret; } static VkBufferUsageFlags ConvertBufferUsageIntoVkBufferUsageFlags(rhi_buffer_usage usage) { VkBufferUsageFlags ret = 0; if (usage & BufferUsage_Vertex) { ret |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } if (usage & BufferUsage_Index) { ret |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; } if (usage & BufferUsage_Uniform) { ret |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; } if (usage & BufferUsage_CopySrc) { ret |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; } if (usage & BufferUsage_CopyDst) { ret |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; } return ret; } rhi_buffer Rhi_CreateBuffer(const char* BufferName, uint64_t BufferSize, rhi_buffer_usage BufferUsage, rhi_memory_usage MemoryUsage) { rhi_buffer_data* Buffer = nullptr; for (uint32_t i = 0; i < rhi->limits.BuffersCount; ++i) { if (!(rhi->Buffers[i].Flags & VulkanFlag_Used)) { Buffer = &rhi->Buffers[i]; break; } } ASSERT(Buffer); MEM_ZERO(Buffer, 0); Buffer->Flags = VulkanFlag_Used; Buffer->size = BufferSize; Buffer->memoryUsage = MemoryUsage; VkBufferCreateInfo bufferCreateInfo{}; bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.size = BufferSize; bufferCreateInfo.usage = ConvertBufferUsageIntoVkBufferUsageFlags(BufferUsage); bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VK_CHECK(vkCreateBuffer(rhi->dev, &bufferCreateInfo, nullptr, &Buffer->buffer)); VkMemoryRequirements memRequirements; vkGetBufferMemoryRequirements(rhi->dev, Buffer->buffer, &memRequirements); uint32_t memoryTypeIndex = VulkanFindMemoryTypeIndex(memRequirements, ConvertMemoryUsageIntoVkMemoryPropertyFlags(Buffer->memoryUsage)); VkMemoryAllocateInfo memoryAllocInfo{}; memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memoryAllocInfo.allocationSize = memRequirements.size; memoryAllocInfo.memoryTypeIndex = memoryTypeIndex; VK_CHECK(vkAllocateMemory(rhi->dev, &memoryAllocInfo, nullptr, &Buffer->memory)); VK_CHECK(vkBindBufferMemory(rhi->dev, Buffer->buffer, Buffer->memory, 0)); VulkanNameHandle(VK_OBJECT_TYPE_BUFFER, (uint64_t)Buffer->buffer, BufferName); return Buffer; } void Rhi_DestroyBuffer(rhi_buffer buffer) { rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; if (Buffer->mapped) { vkUnmapMemory(rhi->dev, Buffer->memory); } vkDestroyBuffer(rhi->dev, Buffer->buffer, nullptr); vkFreeMemory(rhi->dev, Buffer->memory, nullptr); MEM_ZERO(Buffer, 0); } uint64_t Rhi_GetBufferSize(rhi_buffer buffer) { rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; return Buffer->size; } uint8_t* Rhi_MapBuffer(rhi_buffer buffer) { rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; if (!Buffer->mapped) { vkMapMemory(rhi->dev, Buffer->memory, 0, VK_WHOLE_SIZE, 0, (void**)&Buffer->mapped); } return Buffer->mapped; } void Rhi_UnmapBuffer(rhi_buffer buffer) { rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; if (Buffer->mapped) { vkUnmapMemory(rhi->dev, Buffer->memory); Buffer->mapped = nullptr; } } void Rhi_CmdCopyBuffer(rhi_cmdlist cmd, rhi_buffer dst, uint32_t dstOffset, rhi_buffer src, uint32_t srcOffset, uint32_t size) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; rhi_buffer_data* DstBuffer = (rhi_buffer_data*)dst; rhi_buffer_data* SrcBuffer = (rhi_buffer_data*)src; VulkanBufferEnsureHostWritesVisible(cmdbuf, SrcBuffer); VkBufferCopy region{}; region.srcOffset = srcOffset; region.dstOffset = dstOffset; region.size = size; vkCmdCopyBuffer(cmdbuf, SrcBuffer->buffer, DstBuffer->buffer, 1, ®ion); } void Rhi_CmdTransitionBuffer(rhi_cmdlist cmd, rhi_buffer buffer, rhi_buffer_state newState) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; vulkan_buffer_sync_state_info oldSync = VulkanBufferStateGetSyncInfo(Buffer->state); vulkan_buffer_sync_state_info newSync = VulkanBufferStateGetSyncInfo(newState); VkBufferMemoryBarrier2 barrier{}; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; barrier.srcStageMask = oldSync.stage; barrier.srcAccessMask = oldSync.access; barrier.dstStageMask = newSync.stage; barrier.dstAccessMask = newSync.access; barrier.buffer = Buffer->buffer; barrier.size = Rhi_GetBufferSize(buffer); VkDependencyInfo dependencyInfo{}; dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dependencyInfo.bufferMemoryBarrierCount = 1; dependencyInfo.pBufferMemoryBarriers = &barrier; vkCmdPipelineBarrier2(cmdbuf, &dependencyInfo); Buffer->state = newState; } void Rhi_CmdUavBarrierBuffer(rhi_cmdlist cmd, rhi_buffer buffer) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; VkBufferMemoryBarrier2 barrier{}; barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2; barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; barrier.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT; barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.buffer = Buffer->buffer; barrier.offset = 0; barrier.size = VK_WHOLE_SIZE; VkDependencyInfo dependencyInfo{}; dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dependencyInfo.bufferMemoryBarrierCount = 1; dependencyInfo.pBufferMemoryBarriers = &barrier; vkCmdPipelineBarrier2(cmdbuf, &dependencyInfo); } void Rhi_BufferMarkWritten(rhi_buffer buffer, uint32_t offset, uint32_t size) { rhi_buffer_data* Buffer = (rhi_buffer_data*)buffer; if (Buffer->Flags & VulkanFlag_Dirty) { Buffer->dirtyBegin = MIN(Buffer->dirtyBegin, offset); Buffer->dirtyEnd = MAX(Buffer->dirtyBegin, offset + size); } else { Buffer->Flags |= VulkanFlag_Dirty; Buffer->dirtyBegin = offset; Buffer->dirtyEnd = offset + size; } }