CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/Vulkan/VulkanImage.cpp
Views: 1401
#include <algorithm>12#include "Common/Log.h"3#include "Common/GPU/Vulkan/VulkanContext.h"4#include "Common/GPU/Vulkan/VulkanAlloc.h"5#include "Common/GPU/Vulkan/VulkanImage.h"6#include "Common/GPU/Vulkan/VulkanMemory.h"7#include "Common/GPU/Vulkan/VulkanBarrier.h"8#include "Common/StringUtils.h"910using namespace PPSSPP_VK;1112VulkanTexture::VulkanTexture(VulkanContext *vulkan, const char *tag)13: vulkan_(vulkan) {14truncate_cpy(tag_, tag);15}1617void VulkanTexture::Wipe() {18if (view_ != VK_NULL_HANDLE) {19vulkan_->Delete().QueueDeleteImageView(view_);20}21if (image_ != VK_NULL_HANDLE) {22_dbg_assert_(allocation_ != VK_NULL_HANDLE);23vulkan_->Delete().QueueDeleteImageAllocation(image_, allocation_);24}25}2627static bool IsDepthStencilFormat(VkFormat format) {28switch (format) {29case VK_FORMAT_D16_UNORM:30case VK_FORMAT_D16_UNORM_S8_UINT:31case VK_FORMAT_D24_UNORM_S8_UINT:32case VK_FORMAT_D32_SFLOAT:33case VK_FORMAT_D32_SFLOAT_S8_UINT:34return true;35default:36return false;37}38}3940bool VulkanTexture::CreateDirect(int w, int h, int depth, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage, VulkanBarrierBatch *barrierBatch, const VkComponentMapping *mapping) {41if (w == 0 || h == 0 || numMips == 0) {42ERROR_LOG(Log::G3D, "Can't create a zero-size VulkanTexture");43return false;44}45int maxDim = vulkan_->GetPhysicalDeviceProperties(0).properties.limits.maxImageDimension2D;46if (w > maxDim || h > maxDim) {47ERROR_LOG(Log::G3D, "Can't create a texture this large");48return false;49}5051Wipe();5253width_ = w;54height_ = h;55depth_ = depth;56numMips_ = numMips;57format_ = format;5859VkImageAspectFlags aspect = IsDepthStencilFormat(format) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;6061VkImageCreateInfo image_create_info{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };62image_create_info.imageType = depth > 1 ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;63image_create_info.format = format_;64image_create_info.extent.width = width_;65image_create_info.extent.height = height_;66image_create_info.extent.depth = depth;67image_create_info.mipLevels = numMips;68image_create_info.arrayLayers = 1;69image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;70image_create_info.flags = 0;71image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;72image_create_info.usage = usage;73if (initialLayout == VK_IMAGE_LAYOUT_PREINITIALIZED) {74image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;75} else {76image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;77}7879// The graphics debugger always "needs" TRANSFER_SRC but in practice doesn't matter -80// unless validation is on. So let's only force it on when being validated, for now.81if (vulkan_->GetFlags() & VULKAN_FLAG_VALIDATE) {82image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;83}84VmaAllocationCreateInfo allocCreateInfo{};85allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;86VmaAllocationInfo allocInfo{};87VkResult res = vmaCreateImage(vulkan_->Allocator(), &image_create_info, &allocCreateInfo, &image_, &allocation_, &allocInfo);8889// Apply the tag90vulkan_->SetDebugName(image_, VK_OBJECT_TYPE_IMAGE, tag_);9192// Write a command to transition the image to the requested layout, if it's not already that layout.93// TODO: We may generate mipmaps right after, so can't add to the end of frame batch. Well actually depending94// on the amount of mips we probably sometimes can..9596if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) {97VkPipelineStageFlags dstStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;98VkAccessFlagBits dstAccessFlags;99switch (initialLayout) {100case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:101dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;102dstAccessFlags = VK_ACCESS_TRANSFER_WRITE_BIT;103break;104case VK_IMAGE_LAYOUT_GENERAL:105// We use this initial layout when we're about to write to the image using a compute shader, only.106dstStage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;107dstAccessFlags = VK_ACCESS_SHADER_READ_BIT;108break;109default:110// If you planned to use UploadMip, you want VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL. After the111// upload, you can transition using EndCreate.112_assert_(false);113break;114}115barrierBatch->TransitionImage(image_, 0, numMips, 1, VK_IMAGE_ASPECT_COLOR_BIT,116VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,1170, dstAccessFlags,118VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dstStage);119}120121// Create the view while we're at it.122VkImageViewCreateInfo view_info{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };123view_info.image = image_;124view_info.viewType = depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D;125view_info.format = format_;126if (mapping) {127view_info.components = *mapping;128} else {129view_info.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };130}131view_info.subresourceRange.aspectMask = aspect;132view_info.subresourceRange.baseMipLevel = 0;133view_info.subresourceRange.levelCount = numMips;134view_info.subresourceRange.baseArrayLayer = 0;135view_info.subresourceRange.layerCount = 1;136137res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view_);138if (res != VK_SUCCESS) {139ERROR_LOG(Log::G3D, "vkCreateImageView failed: %s. Destroying image.", VulkanResultToString(res));140_assert_msg_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS, "%d", (int)res);141vmaDestroyImage(vulkan_->Allocator(), image_, allocation_);142view_ = VK_NULL_HANDLE;143image_ = VK_NULL_HANDLE;144allocation_ = VK_NULL_HANDLE;145return false;146}147vulkan_->SetDebugName(view_, VK_OBJECT_TYPE_IMAGE_VIEW, tag_);148149// Additionally, create an array view, but only if it's a 2D texture.150if (view_info.viewType == VK_IMAGE_VIEW_TYPE_2D) {151view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;152res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &arrayView_);153// Assume that if the above view creation succeeded, so will this.154_assert_msg_(res == VK_SUCCESS, "View creation failed: %d", (int)res);155vulkan_->SetDebugName(arrayView_, VK_OBJECT_TYPE_IMAGE_VIEW, tag_);156}157158return true;159}160161void VulkanTexture::CopyBufferToMipLevel(VkCommandBuffer cmd, TextureCopyBatch *copyBatch, int mip, int mipWidth, int mipHeight, int depthLayer, VkBuffer buffer, uint32_t offset, size_t rowLength) {162VkBufferImageCopy copy_region{};163copy_region.bufferOffset = offset;164copy_region.bufferRowLength = (uint32_t)rowLength;165copy_region.bufferImageHeight = 0; // 2D166copy_region.imageOffset.z = depthLayer;167copy_region.imageExtent.width = mipWidth;168copy_region.imageExtent.height = mipHeight;169copy_region.imageExtent.depth = 1;170copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;171copy_region.imageSubresource.mipLevel = mip;172copy_region.imageSubresource.baseArrayLayer = 0;173copy_region.imageSubresource.layerCount = 1;174175_dbg_assert_(mip < numMips_);176177if (!copyBatch->buffer) {178copyBatch->buffer = buffer;179} else if (copyBatch->buffer != buffer) {180// Need to flush the batch if this image isn't from the same buffer as the previous ones.181FinishCopyBatch(cmd, copyBatch);182copyBatch->buffer = buffer;183}184copyBatch->copies.push_back(copy_region);185}186187void VulkanTexture::FinishCopyBatch(VkCommandBuffer cmd, TextureCopyBatch *copyBatch) {188if (!copyBatch->copies.empty()) {189vkCmdCopyBufferToImage(cmd, copyBatch->buffer, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)copyBatch->copies.size(), copyBatch->copies.data());190copyBatch->copies.clear();191}192}193194void VulkanTexture::ClearMip(VkCommandBuffer cmd, int mip, uint32_t value) {195// Must be in TRANSFER_DST mode.196VkClearColorValue clearVal;197for (int i = 0; i < 4; i++) {198clearVal.float32[i] = ((value >> (i * 8)) & 0xFF) / 255.0f;199}200VkImageSubresourceRange range{};201range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;202range.layerCount = 1;203range.baseMipLevel = mip;204range.levelCount = 1;205vkCmdClearColorImage(cmd, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearVal, 1, &range);206}207208// Low-quality mipmap generation by bilinear blit, but works okay.209void VulkanTexture::GenerateMips(VkCommandBuffer cmd, int firstMipToGenerate, bool fromCompute) {210_assert_msg_(firstMipToGenerate > 0, "Cannot generate the first level");211_assert_msg_(firstMipToGenerate < numMips_, "Can't generate levels beyond storage");212213VulkanBarrierBatch batch;214// Transition the pre-set levels to GENERAL.215216VkImageMemoryBarrier *barrier = batch.Add(image_,217fromCompute ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT,218VK_PIPELINE_STAGE_TRANSFER_BIT, 0);219barrier->oldLayout = fromCompute ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;220barrier->newLayout = VK_IMAGE_LAYOUT_GENERAL;221barrier->srcAccessMask = fromCompute ? VK_ACCESS_SHADER_WRITE_BIT : VK_ACCESS_TRANSFER_WRITE_BIT;222barrier->dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;223barrier->subresourceRange.levelCount = firstMipToGenerate;224225barrier = batch.Add(image_,226VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,227VK_PIPELINE_STAGE_TRANSFER_BIT, 0);228barrier->subresourceRange.baseMipLevel = firstMipToGenerate;229barrier->subresourceRange.levelCount = numMips_ - firstMipToGenerate;230barrier->oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;231barrier->newLayout = VK_IMAGE_LAYOUT_GENERAL;232barrier->srcAccessMask = 0;233barrier->dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;234235batch.Flush(cmd);236237// Now we can blit and barrier the whole pipeline.238for (int mip = firstMipToGenerate; mip < numMips_; mip++) {239VkImageBlit blit{};240blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;241blit.srcSubresource.layerCount = 1;242blit.srcSubresource.mipLevel = mip - 1;243blit.srcOffsets[1].x = std::max(width_ >> (mip - 1), 1);244blit.srcOffsets[1].y = std::max(height_ >> (mip - 1), 1);245blit.srcOffsets[1].z = 1;246247blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;248blit.dstSubresource.layerCount = 1;249blit.dstSubresource.mipLevel = mip;250blit.dstOffsets[1].x = std::max(width_ >> mip, 1);251blit.dstOffsets[1].y = std::max(height_ >> mip, 1);252blit.dstOffsets[1].z = 1;253254// TODO: We could do better with the image transitions - would be enough with one per level255// for the memory barrier, then one final one for the whole stack when done. This function256// currently doesn't have a global enough view, though.257// We should also coalesce barriers across multiple texture uploads in a frame and all kinds of other stuff, but...258259vkCmdBlitImage(cmd, image_, VK_IMAGE_LAYOUT_GENERAL, image_, VK_IMAGE_LAYOUT_GENERAL, 1, &blit, VK_FILTER_LINEAR);260261barrier = batch.Add(image_, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0);262barrier->subresourceRange.baseMipLevel = mip;263barrier->oldLayout = VK_IMAGE_LAYOUT_GENERAL;264barrier->newLayout = VK_IMAGE_LAYOUT_GENERAL;265barrier->srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;266barrier->dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;267batch.Flush(cmd);268}269}270271void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkPipelineStageFlags prevStage, VkImageLayout layout) {272VulkanBarrierBatch batch;273VkImageMemoryBarrier *barrier = batch.Add(image_, prevStage, vertexTexture ? VK_PIPELINE_STAGE_VERTEX_SHADER_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0);274barrier->subresourceRange.levelCount = numMips_;275barrier->oldLayout = layout;276barrier->newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;277barrier->srcAccessMask = prevStage == VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ? VK_ACCESS_SHADER_WRITE_BIT : VK_ACCESS_TRANSFER_WRITE_BIT;278barrier->dstAccessMask = VK_ACCESS_SHADER_READ_BIT;279batch.Flush(cmd);280}281282void VulkanTexture::PrepareForTransferDst(VkCommandBuffer cmd, int levels) {283VulkanBarrierBatch batch;284VkImageMemoryBarrier *barrier = batch.Add(image_, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0);285barrier->subresourceRange.levelCount = levels;286barrier->srcAccessMask = VK_ACCESS_SHADER_READ_BIT;287barrier->dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;288barrier->oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;289barrier->newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;290batch.Flush(cmd);291}292293void VulkanTexture::RestoreAfterTransferDst(int levels, VulkanBarrierBatch *barriers) {294VkImageMemoryBarrier *barrier = barriers->Add(image_, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0);295barrier->subresourceRange.levelCount = levels;296barrier->srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;297barrier->dstAccessMask = VK_ACCESS_SHADER_READ_BIT;298barrier->oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;299barrier->newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;300}301302VkImageView VulkanTexture::CreateViewForMip(int mip) {303VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };304view_info.image = image_;305view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;306view_info.format = format_;307view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;308view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;309view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;310view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;311view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;312view_info.subresourceRange.baseMipLevel = mip;313view_info.subresourceRange.levelCount = 1;314view_info.subresourceRange.baseArrayLayer = 0;315view_info.subresourceRange.layerCount = 1;316VkImageView view;317VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);318vulkan_->SetDebugName(view, VK_OBJECT_TYPE_IMAGE_VIEW, "mipview");319_assert_(res == VK_SUCCESS);320return view;321}322323void VulkanTexture::Destroy() {324if (view_ != VK_NULL_HANDLE) {325vulkan_->Delete().QueueDeleteImageView(view_);326}327if (arrayView_ != VK_NULL_HANDLE) {328vulkan_->Delete().QueueDeleteImageView(arrayView_);329}330if (image_ != VK_NULL_HANDLE) {331_dbg_assert_(allocation_ != VK_NULL_HANDLE);332vulkan_->Delete().QueueDeleteImageAllocation(image_, allocation_);333}334}335336337