diff --git a/src/nouveau/vulkan/nvk_device_memory.c b/src/nouveau/vulkan/nvk_device_memory.c index 72d81673b8b..0468676c8b6 100644 --- a/src/nouveau/vulkan/nvk_device_memory.c +++ b/src/nouveau/vulkan/nvk_device_memory.c @@ -41,11 +41,15 @@ const VkExternalMemoryProperties nvk_dma_buf_mem_props = { static enum nvkmd_mem_flags nvk_memory_type_flags(const VkMemoryType *type, - VkExternalMemoryHandleTypeFlagBits handle_types) + VkExternalMemoryHandleTypeFlagBits handle_types, + bool pinned_to_vram) { enum nvkmd_mem_flags flags = 0; if (type->propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) - flags = NVKMD_MEM_LOCAL; + if (pinned_to_vram) + flags = NVKMD_MEM_VRAM; + else + flags = NVKMD_MEM_LOCAL; else flags = NVKMD_MEM_GART; @@ -95,7 +99,7 @@ nvk_GetMemoryFdPropertiesKHR(VkDevice device, for (unsigned t = 0; t < ARRAY_SIZE(pdev->mem_types); t++) { const VkMemoryType *type = &pdev->mem_types[t]; const enum nvkmd_mem_flags type_flags = - nvk_memory_type_flags(type, handleType); + nvk_memory_type_flags(type, handleType, false); /* Flags required to be set on mem to be imported as type * @@ -153,8 +157,12 @@ nvk_AllocateMemory(VkDevice device, if (fd_info != NULL) handle_types |= fd_info->handleType; - const enum nvkmd_mem_flags flags = nvk_memory_type_flags(type, handle_types); + bool pinned_to_vram = false; + /* Align to os page size (typically 4K) as a start as this works for + * everything, and then depending on placement and size, we either keep + * it as is or increase it to 64K or 2M. + */ uint32_t alignment = pdev->nvkmd->bind_align_B; uint8_t pte_kind = 0, tile_mode = 0; @@ -171,9 +179,25 @@ nvk_AllocateMemory(VkDevice device, alignment = MAX2(alignment, image->planes[0].nil.align_B); pte_kind = image->planes[0].nil.pte_kind; tile_mode = image->planes[0].nil.tile_mode; + } else if (image->can_compress) { + /* If it's a dedicated alloc and it's not modifiers, then it's marked + * for compression and larger pages, so we set the pinned bit and up + * the alignment. + */ + pinned_to_vram = true; + pte_kind = image->planes[0].nil.compressed_pte_kind; + tile_mode = image->planes[0].nil.tile_mode; + /* Align to 2MiB if size is >= 2MiB, otherwise align to 64KiB. */ + if (pAllocateInfo->allocationSize >= (1ULL << 21)) + alignment = (1ULL << 21); + else + alignment = (1ULL << 16); } } + const enum nvkmd_mem_flags flags = + nvk_memory_type_flags(type, handle_types, pinned_to_vram); + const uint64_t aligned_size = align64(pAllocateInfo->allocationSize, alignment); diff --git a/src/nouveau/vulkan/nvk_image.c b/src/nouveau/vulkan/nvk_image.c index b373bcf4c80..25da12e1a01 100644 --- a/src/nouveau/vulkan/nvk_image.c +++ b/src/nouveau/vulkan/nvk_image.c @@ -769,6 +769,63 @@ nvk_GetPhysicalDeviceSparseImageFormatProperties2( } } +/* To use compression and larger page sizes, we need to signal to the kernel + * that the memory requested is going to be VRAM resident. However, this + * comes with an issue where said memory can't be evicted to host RAM under + * pressure, so we work around this by going with a dedicated allocation for + * color, Z/S, and storage image targets which are the main types that would + * benefit from compression as they're heavy on writes. Additionally, they + * also aren't the majority of memory used, so they can be safely pinned in + * VRAM without worrying about eviction under high pressure. + * + * There are some additional restrictions we need to keep in mind, however: + * 1. We can only enable this for Turing onwards because prior architectures + * relied on firmware to manage the compression tags, and it's impossible to + * do this on nouveau. Additionally, since compression needs kernel changes, + * we can only enable it if the detected kernel supports it. + * + * 2. Given our approach depends on dedicated allocations, we can't enable + * compression for sparse images as dedicated allocations are not compatible + * with sparse. + * + * 3. In similar vein, we currently don't do multiplanar dedicated allocations + * so we can't do compression for multi-plane YCbCr images. + * + * 4. Host copies are a complete no-go for compression as the host doesn't know + * about the modified data layout nor the compression tags. + * + * 5. The API for VK_EXT_image_drm_format_modifier requires that we report the + * supported modifiers in GetPhysicalDeviceFormatProperties2(). However, + * since we can only know whether an image is compressed or not at bind time + * we can't actually expose any of the compressed modifiers in case the app + * chooses a compressed modifier for a non-compressed image. So for now, we + * have to disable compression for TILING_DRM_FORMAT_MODIFIER_EXT images. + * + * This helper enforces these restrictions and also makes sure to enable + * compression for storage, color, and Z/S targets only so as to avoid pinning + * too many things to VRAM. + */ +static bool +nvk_image_can_compress(const struct nvkmd_pdev *nvkmd_pdev, + const struct nvk_image *image) +{ + if (nvkmd_pdev->kmd_info.has_compression) { + if (image->plane_count > 1 || + image->vk.usage & (VK_IMAGE_USAGE_HOST_TRANSFER_BIT) || + image->vk.create_flags & (VK_IMAGE_CREATE_SPARSE_BINDING_BIT | + VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT)) + return false; + else if (image->vk.usage & (VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && + image->vk.tiling == VK_IMAGE_TILING_OPTIMAL) + return true; + else + return false; + } else + return false; +} + static VkResult nvk_image_init(struct nvk_device *dev, struct nvk_image *image, @@ -817,6 +874,13 @@ nvk_image_init(struct nvk_device *dev, VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR)) usage |= NIL_IMAGE_USAGE_VIDEO_BIT; + /* We set compression on VkImage creation in order to be able to signal to + * NIL that the image will be compressed which would let NIL choose the + * appropriate PTE kinds, and also to mark the VkImage as compressed so that + * in GetImageMemoryRequirements() we are able to detect it and specify that + * we prefer a dedicated allocation for it. + */ + image->can_compress = nvk_image_can_compress(dev->nvkmd->pdev, image); if (!image->can_compress) usage |= NIL_IMAGE_USAGE_UNCOMPRESSED_BIT; @@ -1227,10 +1291,23 @@ nvk_get_image_memory_requirements(struct nvk_device *dev, switch (ext->sType) { case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: { VkMemoryDedicatedRequirements *dedicated = (void *)ext; - dedicated->prefersDedicatedAllocation = - image->vk.tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; - dedicated->requiresDedicatedAllocation = - image->vk.tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + if (image->vk.tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) { + dedicated->prefersDedicatedAllocation = true; + dedicated->requiresDedicatedAllocation = true; + } else if (image->can_compress) { + /* We need dedicated allocations as compressed images have to be + * pinned to VRAM due to nouveau, and we can't have a separate + * memory type that's pinned and non evictable due to the Vulkan API + * disallowing equivalent image properties returning different + * memory types. We aren't allowed to require dedicated allocations + * but we can signal that we prefer them. + */ + dedicated->prefersDedicatedAllocation = true; + dedicated->requiresDedicatedAllocation = false; + } else { + dedicated->prefersDedicatedAllocation = false; + dedicated->requiresDedicatedAllocation = false; + } break; } default: @@ -1471,14 +1548,19 @@ nvk_image_plane_bind(struct nvk_device *dev, *offset_B = align64(*offset_B, plane_align_B); if (plane->nil.pte_kind != 0) { - VkResult result = nvk_image_plane_alloc_va(dev, image, plane); - if (result != VK_SUCCESS) - return result; - result = nvkmd_va_bind_mem(plane->va, &image->vk.base, 0, - mem->mem, *offset_B, - plane->va->size_B); - if (result != VK_SUCCESS) - return result; + if (mem->dedicated_image == image && image->can_compress) { + image->is_compressed = true; + plane->addr = mem->mem->va->addr + *offset_B; + } else { + VkResult result = nvk_image_plane_alloc_va(dev, image, plane); + if (result != VK_SUCCESS) + return result; + result = nvkmd_va_bind_mem(plane->va, &image->vk.base, 0, + mem->mem, *offset_B, + plane->va->size_B); + if (result != VK_SUCCESS) + return result; + } } else { plane->addr = mem->mem->va->addr + *offset_B; } diff --git a/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c index cb59cb75410..44bf06e4c94 100644 --- a/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c +++ b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c @@ -16,6 +16,8 @@ #include #include +#include "clc597.h" + static bool drm_device_is_nouveau(const char *path) { @@ -99,6 +101,8 @@ nvkmd_nouveau_try_create_pdev(struct _drmDevice *drm_device, .has_alloc_tiled = nouveau_ws_device_has_tiled_bo(ws_dev), .has_map_fixed = true, .has_overmap = true, + .has_compression = ws_dev->nouveau_version >= 0x01000401 && + ws_dev->info.cls_eng3d >= TURING_A, }; /* We get this ourselves */ diff --git a/src/nouveau/vulkan/nvkmd/nvkmd.h b/src/nouveau/vulkan/nvkmd/nvkmd.h index d13fbe42f11..5e56f4ed36c 100644 --- a/src/nouveau/vulkan/nvkmd/nvkmd.h +++ b/src/nouveau/vulkan/nvkmd/nvkmd.h @@ -120,6 +120,7 @@ struct nvkmd_info { bool has_alloc_tiled; bool has_map_fixed; bool has_overmap; + bool has_compression; }; struct nvkmd_pdev_ops {