rhi_texture Rhi_CreateTexture(const char* Name, uint32_t Width, uint32_t Height, rhi_memory_usage MemoryUsage, rhi_texture_usage TextureUsage, rhi_texture_format TextureFormat) { rhi_texture_data* Texture = nullptr; for (uint32_t i = 0; i < rhi->limits.TexturesCount; ++i) { if (!(rhi->Textures[i].Flags & VulkanFlag_Used)) { Texture = &rhi->Textures[i]; break; } } ASSERT(Texture); MEM_ZERO(Texture, 0); Texture->Flags = VulkanFlag_Used; Texture->extent.width = Width; Texture->extent.height = Height; Texture->extent.depth = 1; Texture->format = ConvertTextureFormatIntoVkFormat(TextureFormat, &Texture->aspectMask); VkMemoryPropertyFlags memoryPropertyFlags = ConvertMemoryUsageIntoVkMemoryPropertyFlags(MemoryUsage); VkImageCreateInfo imageCreateInfo{}; imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = Texture->format; imageCreateInfo.extent = { Width, Height, 1 }; imageCreateInfo.mipLevels = 1; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = ConvertTextureUsageToVkImageUsageFlags(TextureUsage); imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; VK_CHECK(vkCreateImage(rhi->dev, &imageCreateInfo, rhi->vkAlloc, &Texture->image)); VkMemoryRequirements memoryRequirements; vkGetImageMemoryRequirements(rhi->dev, Texture->image, &memoryRequirements); VkMemoryAllocateInfo memoryAllocInfo{}; memoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memoryAllocInfo.allocationSize = memoryRequirements.size; memoryAllocInfo.memoryTypeIndex = VulkanFindMemoryTypeIndex(memoryRequirements, memoryPropertyFlags); vkAllocateMemory(rhi->dev, &memoryAllocInfo, rhi->vkAlloc, &Texture->memory); vkBindImageMemory(rhi->dev, Texture->image, Texture->memory, 0); VulkanNameHandle(VK_OBJECT_TYPE_IMAGE, (uint64_t)Texture->image, Name); return Texture; } static rhi_texture_view_data* VulkanCreateTextureView(rhi_texture_view_data* TextureViews, uint32_t TextureViewsCount, const char* name, rhi_texture texture, rhi_texture_format format, uint32_t aspectMask) { rhi_texture_data* Texture = (rhi_texture_data*)texture; rhi_texture_view_data* View = nullptr; for (uint32_t i = 0; i < TextureViewsCount; ++i) { if (!(TextureViews[i].Flags & VulkanFlag_Used)) { View = &TextureViews[i]; break; } } ASSERT(View); MEM_ZERO(View, 0); View->texture = texture; View->Flags = VulkanFlag_Used; // TODO: expose these as well! View->imageViewType = VK_IMAGE_VIEW_TYPE_2D; View->subresourceRange = {}; View->subresourceRange.aspectMask = aspectMask; View->subresourceRange.baseMipLevel = 0; View->subresourceRange.levelCount = 1; View->subresourceRange.baseArrayLayer = 0; View->subresourceRange.layerCount = 1; View->format = format == TextureFormatNone ? Texture->format : ConvertTextureFormatIntoVkFormat(format, nullptr); VkImageViewCreateInfo imageViewCreateInfo{}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.image = Texture->image; imageViewCreateInfo.viewType = View->imageViewType; imageViewCreateInfo.format = View->format; imageViewCreateInfo.subresourceRange = View->subresourceRange; VK_CHECK(vkCreateImageView(rhi->dev, &imageViewCreateInfo, rhi->vkAlloc, &View->imageView)); VulkanNameHandle(VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)View->imageView, name); return View; } rhi_sampled_texture_view Rhi_CreateSampledTextureView(const char* name, rhi_texture texture, rhi_texture_format format) { return VulkanCreateTextureView(rhi->SampledTextureViews, rhi->limits.SampledTextureViewsCount, name, texture, format, VK_IMAGE_ASPECT_COLOR_BIT); } rhi_render_target_view Rhi_CreateRenderTargetView(const char* name, rhi_texture texture, rhi_texture_format format) { return VulkanCreateTextureView(rhi->RenderTargetViews, rhi->limits.RenderTargetViewsCount, name, texture, format, VK_IMAGE_ASPECT_COLOR_BIT); } rhi_depth_stencil_view Rhi_CreateDepthStencilView(const char* name, rhi_texture texture, rhi_texture_format format) { return VulkanCreateTextureView(rhi->DepthStencilViews, rhi->limits.DepthStencilViewsCount, name, texture, format, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); } void Rhi_CmdTransitionTexture(rhi_cmdlist cmd, rhi_texture texture, rhi_texture_state newState) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; rhi_texture_data* Texture = (rhi_texture_data*)texture; vulkan_texture_state_sync_info newSyncInfo = VulkanTextureStateGetSyncInfo(newState); if (newSyncInfo.layout == Texture->layout) return; vulkan_texture_state_sync_info oldSyncInfo = VulkanTextureStateGetSyncInfo(Texture->state); VkImageMemoryBarrier2 imageMemoryBarrier{}; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; imageMemoryBarrier.srcStageMask = oldSyncInfo.stage; imageMemoryBarrier.srcAccessMask = oldSyncInfo.access; imageMemoryBarrier.dstStageMask = newSyncInfo.stage; imageMemoryBarrier.dstAccessMask = newSyncInfo.access; imageMemoryBarrier.oldLayout = Texture->layout; imageMemoryBarrier.newLayout = newSyncInfo.layout; imageMemoryBarrier.image = Texture->image; imageMemoryBarrier.subresourceRange = {}; imageMemoryBarrier.subresourceRange.aspectMask = Texture->aspectMask; imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; imageMemoryBarrier.subresourceRange.layerCount = 1; VkDependencyInfo dependencyInfo{}; dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; dependencyInfo.imageMemoryBarrierCount = 1; dependencyInfo.pImageMemoryBarriers = &imageMemoryBarrier; vkCmdPipelineBarrier2(cmdbuf, &dependencyInfo); Texture->state = newState; Texture->layout = newSyncInfo.layout; } void Rhi_CmdCopyTexture(rhi_cmdlist cmd, rhi_texture dst, rhi_buffer src, uint32_t srcOffset, uint32_t size) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; VulkanBufferEnsureHostWritesVisible(cmdbuf, (rhi_buffer_data*)src); VkBufferImageCopy region{}; region.bufferOffset = srcOffset; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource = {}; region.imageSubresource.aspectMask = ((rhi_texture_data*)dst)->aspectMask; region.imageSubresource.layerCount = 1; region.imageOffset = { 0, 0, 0 }; region.imageExtent = ((rhi_texture_data*)dst)->extent; vkCmdCopyBufferToImage(cmdbuf, ((rhi_buffer_data*)src)->buffer, ((rhi_texture_data*)dst)->image, ((rhi_texture_data*)dst)->layout, 1, ®ion); } void Rhi_CmdCopyTexture(rhi_cmdlist cmd, rhi_texture dst, rhi_texture src) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; VkImageCopy region{}; region.srcSubresource = {}; region.srcSubresource.aspectMask = ((rhi_texture_data*)src)->aspectMask; region.srcSubresource.layerCount = 1; region.dstSubresource = {}; region.dstSubresource.aspectMask = ((rhi_texture_data*)dst)->aspectMask; region.dstSubresource.layerCount = 1; region.extent = ((rhi_texture_data*)src)->extent; vkCmdCopyImage(cmdbuf, ((rhi_texture_data*)src)->image, ((rhi_texture_data*)src)->layout, ((rhi_texture_data*)dst)->image, ((rhi_texture_data*)dst)->layout, 1, ®ion); } void Rhi_CmdCopyTextureToBuffer(rhi_cmdlist cmd, rhi_texture src, rhi_buffer dst, uint32_t dstOffset) { VkCommandBuffer cmdbuf = ((rhi_cmdlist_data*)cmd)->cmdbuf; rhi_texture_data* Texture = (rhi_texture_data*)src; rhi_buffer_data* Buffer = (rhi_buffer_data*)dst; VkBufferImageCopy region{}; region.bufferOffset = dstOffset; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource = {}; region.imageSubresource.aspectMask = Texture->aspectMask; region.imageSubresource.layerCount = 1; region.imageOffset = { 0, 0, 0 }; region.imageExtent = Texture->extent; vkCmdCopyImageToBuffer(cmdbuf, Texture->image, Texture->layout, Buffer->buffer, 1, ®ion); VkBufferMemoryBarrier2 barrier{}; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; barrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT; barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; barrier.dstStageMask = VK_PIPELINE_STAGE_2_HOST_BIT; barrier.dstAccessMask = VK_ACCESS_2_HOST_READ_BIT; 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); } rhi_texture Rhi_GetTextureFromSampled(rhi_sampled_texture_view View) { return ((rhi_texture_view_data*)View)->texture; } rhi_texture Rhi_GetTextureFromRender(rhi_render_target_view View) { return ((rhi_texture_view_data*)View)->texture; } rhi_texture Rhi_GetTextureFromDepth(rhi_depth_stencil_view View) { return ((rhi_texture_view_data*)View)->texture; } rhi_texture_info Rhi_GetTextureInfo(rhi_texture texture) { rhi_texture_data* Texture = (rhi_texture_data*)texture; rhi_texture_info ret{}; ret.width = Texture->extent.width; ret.height = Texture->extent.height; ret.format = ConvertVkFormatIntoTextureFormat(Texture->format); return ret; } void Rhi_DestroyTexture(rhi_texture Texture) { vkDestroyImage(rhi->dev, Texture->image, rhi->vkAlloc); vkFreeMemory(rhi->dev, Texture->memory, rhi->vkAlloc); MEM_ZERO(Texture, 0); } void Rhi_DestroySampledTextureView(rhi_sampled_texture_view View) { vkDestroyImageView(rhi->dev, ((rhi_texture_view_data*)View)->imageView, rhi->vkAlloc); MEM_ZERO(View, 0); } void Rhi_DestroyRenderTargetView(rhi_render_target_view View) { vkDestroyImageView(rhi->dev, ((rhi_texture_view_data*)View)->imageView, rhi->vkAlloc); MEM_ZERO(View, 0); } void Rhi_DestroyDepthStencilView(rhi_depth_stencil_view View) { vkDestroyImageView(rhi->dev, ((rhi_texture_view_data*)View)->imageView, rhi->vkAlloc); MEM_ZERO(View, 0); } static void CreateSwapchain(alloc* Scratch) { uint8_t* ScratchMark = Scratch->at; VkSurfaceCapabilitiesKHR surfaceCapabilities; VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhi->physDev, rhi->surface, &surfaceCapabilities)); VkExtent2D surfaceExtent; if (surfaceCapabilities.currentExtent.width == UINT32_MAX) { uint32_t width; uint32_t height; WinGetSize(&width, &height); surfaceExtent.width = CLAMP(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); surfaceExtent.height = CLAMP(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); } else { surfaceExtent = surfaceCapabilities.currentExtent; } if (!surfaceExtent.height || !surfaceExtent.width) return; VkSwapchainKHR oldSwapchain = rhi->swapchain; VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; { uint32_t presentModeCount; VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(rhi->physDev, rhi->surface, &presentModeCount, nullptr)); VkPresentModeKHR* presentModes = PUSH_ALLOC(Scratch, VkPresentModeKHR, presentModeCount); VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(rhi->physDev, rhi->surface, &presentModeCount, presentModes)); static bool logPresentModes = true; for (uint32_t i = 0; i < presentModeCount; ++i) { if (logPresentModes) LOG("Present Mode available: %s", string_VkPresentModeKHR(presentMode)); bool better = false; switch (presentModes[i]) { case VK_PRESENT_MODE_FIFO_LATEST_READY_KHR: { better = true; } break; case VK_PRESENT_MODE_IMMEDIATE_KHR: { better = !(rhi->flags & Flag_VSync) && presentMode != VK_PRESENT_MODE_FIFO_LATEST_READY_KHR; } break; } if (better) presentMode = presentModes[i]; } if (logPresentModes) { LOG("Chose %s", string_VkPresentModeKHR(presentMode)); logPresentModes = false; } } VkSurfaceFormatKHR surfaceFormat; { uint32_t surfaceFormatCount; VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(rhi->physDev, rhi->surface, &surfaceFormatCount, nullptr)); VkSurfaceFormatKHR* surfaceFormats = PUSH_ALLOC(Scratch, VkSurfaceFormatKHR, surfaceFormatCount); VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(rhi->physDev, rhi->surface, &surfaceFormatCount, surfaceFormats)); bool found = false; for (uint32_t i = 0; i < surfaceFormatCount; ++i) { surfaceFormat = surfaceFormats[i]; if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_SRGB && surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { found = true; break; } } ASSERT(found); } VkSwapchainCreateInfoKHR createInfo {}; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.surface = rhi->surface; createInfo.minImageCount = surfaceCapabilities.minImageCount; createInfo.imageFormat = surfaceFormat.format; createInfo.imageColorSpace = surfaceFormat.colorSpace; createInfo.imageExtent = surfaceExtent; createInfo.imageArrayLayers = 1; createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.preTransform = surfaceCapabilities.currentTransform; createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createInfo.presentMode = presentMode; createInfo.clipped = VK_TRUE; createInfo.oldSwapchain = rhi->swapchain; VK_CHECK(vkCreateSwapchainKHR(rhi->dev, &createInfo, nullptr, &rhi->swapchain)); uint32_t swapchainImageCount; vkGetSwapchainImagesKHR(rhi->dev, rhi->swapchain, &swapchainImageCount, nullptr); ASSERT(COUNT(rhi->SwapChain) >= swapchainImageCount); VkImage* swapchainImages = PUSH_ALLOC(Scratch, VkImage, swapchainImageCount); vkGetSwapchainImagesKHR(rhi->dev, rhi->swapchain, &swapchainImageCount, swapchainImages); for (size_t i = 0; i < swapchainImageCount; ++i) { rhi_texture texture; rhi_render_target_view rtv; rhi_texture_view_data* View = (rhi_texture_view_data*)rhi->SwapChain[i].View; if (rhi->SwapChainCount > i) { View->format = surfaceFormat.format; rhi_texture_data* Texture = (rhi_texture_data*)View->texture; Texture->image = swapchainImages[i]; Texture->state = TextureState_Present; Texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; Texture->format = surfaceFormat.format; Texture->extent = VkExtent3D{ surfaceExtent.width, surfaceExtent.height, 1 }; Texture->aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; vkDestroyImageView(rhi->dev, View->imageView, rhi->vkAlloc); VkImageViewCreateInfo imageViewCreateInfo {}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.image = Texture->image; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; imageViewCreateInfo.format = View->format; imageViewCreateInfo.subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }; vkCreateImageView(rhi->dev, &imageViewCreateInfo, rhi->vkAlloc, &View->imageView); } else { rhi_texture_data *Texture; for (uint32_t itexture = 0; itexture < rhi->limits.TexturesCount; ++itexture) { if (!(rhi->Textures[itexture].Flags & VulkanFlag_Used)) { Texture = &rhi->Textures[itexture]; break; } } ASSERT(Texture); Texture->Flags = VulkanFlag_Used; Texture->image = swapchainImages[i]; Texture->memory = nullptr; Texture->state = TextureState_Present; Texture->format = surfaceFormat.format; Texture->aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; Texture->extent = { surfaceExtent.width, surfaceExtent.height, 1 }; rhi->SwapChain[rhi->SwapChainCount++].View = Rhi_CreateRenderTargetView("swapchain view", Texture, (rhi_texture_format)0); } } if (oldSwapchain) vkDestroySwapchainKHR(rhi->dev, oldSwapchain, rhi->vkAlloc); Scratch->at = ScratchMark; }