mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
graphics: simplify image allocation
This commit is contained in:
@@ -567,6 +567,8 @@ if(BUILD_TESTING)
|
|||||||
|
|
||||||
add_test(NAME texture_cache_layered_image
|
add_test(NAME texture_cache_layered_image
|
||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --layered-image-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --layered-image-only)
|
||||||
|
add_test(NAME host_image_allocation
|
||||||
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --host-image-allocation-only)
|
||||||
add_test(NAME texture_cache_image_views
|
add_test(NAME texture_cache_image_views
|
||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --image-view-cache-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --image-view-cache-only)
|
||||||
add_test(NAME texture_cache_storage_sampled
|
add_test(NAME texture_cache_storage_sampled
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
struct VulkanImage;
|
struct VulkanImage;
|
||||||
struct VulkanMemory;
|
|
||||||
|
|
||||||
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
inline constexpr uint32_t VULKAN_TARGET_API_VERSION = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
@@ -117,12 +116,6 @@ private:
|
|||||||
m_image_format_properties;
|
m_image_format_properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanMemory {
|
|
||||||
vk::MemoryPropertyFlags property = {};
|
|
||||||
vk::MemoryPropertyFlags preferred_property = {};
|
|
||||||
VmaAllocation allocation = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VulkanImageState {
|
struct VulkanImageState {
|
||||||
vk::PipelineStageFlags2 pl_stage = vk::PipelineStageFlagBits2::eAllCommands;
|
vk::PipelineStageFlags2 pl_stage = vk::PipelineStageFlagBits2::eAllCommands;
|
||||||
vk::AccessFlags2 access_mask = vk::AccessFlagBits2::eNone;
|
vk::AccessFlags2 access_mask = vk::AccessFlagBits2::eNone;
|
||||||
@@ -144,7 +137,7 @@ struct VulkanImage {
|
|||||||
vk::Image image = nullptr;
|
vk::Image image = nullptr;
|
||||||
VulkanImageState state;
|
VulkanImageState state;
|
||||||
std::vector<VulkanImageState> subresource_states;
|
std::vector<VulkanImageState> subresource_states;
|
||||||
Graphics::VulkanMemory memory;
|
VmaAllocation allocation = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -661,26 +661,17 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
backing.format = info.pixel_format;
|
|
||||||
backing.image_type = HostImageType(info.type);
|
|
||||||
backing.extent = info.extent;
|
|
||||||
backing.layers = info.IsVolume() ? 1u : info.resources.layers;
|
|
||||||
backing.mip_levels = info.resources.levels;
|
|
||||||
backing.samples = info.samples;
|
|
||||||
backing.flags = ImageCreateFlags(graphics, info);
|
|
||||||
backing.usage = ImageUsageFlags(graphics, info);
|
|
||||||
|
|
||||||
vk::ImageCreateInfo create {};
|
vk::ImageCreateInfo create {};
|
||||||
create.flags = backing.flags;
|
create.flags = ImageCreateFlags(graphics, info);
|
||||||
create.imageType = backing.image_type;
|
create.imageType = HostImageType(info.type);
|
||||||
create.extent = backing.extent;
|
create.extent = info.extent;
|
||||||
create.mipLevels = backing.mip_levels;
|
create.mipLevels = info.resources.levels;
|
||||||
create.arrayLayers = backing.layers;
|
create.arrayLayers = info.IsVolume() ? 1u : info.resources.layers;
|
||||||
create.format = backing.format;
|
create.format = info.pixel_format;
|
||||||
create.tiling = vk::ImageTiling::eOptimal;
|
create.tiling = vk::ImageTiling::eOptimal;
|
||||||
create.initialLayout = backing.state.layout;
|
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||||
create.usage = backing.usage;
|
create.usage = ImageUsageFlags(graphics, info);
|
||||||
create.samples = vulkan_sample_count(backing.samples);
|
create.samples = vulkan_sample_count(info.samples);
|
||||||
|
|
||||||
vk::ImageFormatProperties properties {};
|
vk::ImageFormatProperties properties {};
|
||||||
if (graphics.GetImageFormatProperties(create.format, create.imageType, create.tiling,
|
if (graphics.GetImageFormatProperties(create.format, create.imageType, create.tiling,
|
||||||
@@ -691,10 +682,9 @@ Image::Image(GraphicContext& graphics, CommandScheduler& scheduler, const ImageI
|
|||||||
"flags=0x%x samples=%u\n",
|
"flags=0x%x samples=%u\n",
|
||||||
static_cast<int>(create.format), static_cast<int>(create.imageType),
|
static_cast<int>(create.format), static_cast<int>(create.imageType),
|
||||||
static_cast<vk::ImageUsageFlags::MaskType>(create.usage),
|
static_cast<vk::ImageUsageFlags::MaskType>(create.usage),
|
||||||
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), backing.samples);
|
static_cast<vk::ImageCreateFlags::MaskType>(create.flags), info.samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
backing.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
|
||||||
if (!graphics.CreateImage(create, backing)) {
|
if (!graphics.CreateImage(create, backing)) {
|
||||||
EXIT("failed to create image: extent=%ux%ux%u format=%d layers=%u levels=%u\n",
|
EXIT("failed to create image: extent=%ux%ux%u format=%d layers=%u levels=%u\n",
|
||||||
create.extent.width, create.extent.height, create.extent.depth,
|
create.extent.width, create.extent.height, create.extent.depth,
|
||||||
|
|||||||
@@ -130,35 +130,42 @@ uint64_t GraphicContext::GetTotalMemoryBudget() const {
|
|||||||
|
|
||||||
bool GraphicContext::CreateImage(const vk::ImageCreateInfo& image_info, VulkanImage& image) {
|
bool GraphicContext::CreateImage(const vk::ImageCreateInfo& image_info, VulkanImage& image) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(allocator == nullptr || image.image != nullptr || image.memory.allocation != nullptr);
|
EXIT_IF(allocator == nullptr || image.image != nullptr || image.allocation != nullptr);
|
||||||
|
|
||||||
auto& memory = image.memory;
|
|
||||||
VmaAllocationCreateInfo alloc_info {};
|
VmaAllocationCreateInfo alloc_info {};
|
||||||
alloc_info.requiredFlags = static_cast<vk::MemoryPropertyFlags::MaskType>(memory.property);
|
alloc_info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
alloc_info.preferredFlags =
|
|
||||||
static_cast<vk::MemoryPropertyFlags::MaskType>(memory.preferred_property);
|
|
||||||
|
|
||||||
vk::Image::CType native_image = VK_NULL_HANDLE;
|
vk::Image::CType native_image = VK_NULL_HANDLE;
|
||||||
const auto result = static_cast<vk::Result>(
|
const auto result = static_cast<vk::Result>(
|
||||||
vmaCreateImage(allocator, static_cast<const vk::ImageCreateInfo::NativeType*>(image_info),
|
vmaCreateImage(allocator, static_cast<const vk::ImageCreateInfo::NativeType*>(image_info),
|
||||||
&alloc_info, &native_image, &memory.allocation, nullptr));
|
&alloc_info, &native_image, &image.allocation, nullptr));
|
||||||
image.image = native_image;
|
image.image = native_image;
|
||||||
if (result != vk::Result::eSuccess) {
|
if (result != vk::Result::eSuccess) {
|
||||||
LogMemoryBudget();
|
LogMemoryBudget();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
image.format = image_info.format;
|
||||||
|
image.image_type = image_info.imageType;
|
||||||
|
image.extent = image_info.extent;
|
||||||
|
image.layers = image_info.arrayLayers;
|
||||||
|
image.mip_levels = image_info.mipLevels;
|
||||||
|
image.samples = static_cast<uint32_t>(image_info.samples);
|
||||||
|
image.usage = image_info.usage;
|
||||||
|
image.flags = image_info.flags;
|
||||||
|
image.state = {.layout = image_info.initialLayout};
|
||||||
|
image.subresource_states.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicContext::DeleteImage(VulkanImage& image) {
|
void GraphicContext::DeleteImage(VulkanImage& image) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
EXIT_IF(allocator == nullptr || image.image == nullptr || image.memory.allocation == nullptr);
|
EXIT_IF(allocator == nullptr || image.image == nullptr || image.allocation == nullptr);
|
||||||
|
|
||||||
auto& memory = image.memory;
|
vmaDestroyImage(allocator, image.image, image.allocation);
|
||||||
vmaDestroyImage(allocator, image.image, memory.allocation);
|
image.image = nullptr;
|
||||||
image.image = nullptr;
|
image.allocation = nullptr;
|
||||||
memory.allocation = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|||||||
@@ -191,24 +191,15 @@ void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
|||||||
}
|
}
|
||||||
if (dst.image != nullptr) {
|
if (dst.image != nullptr) {
|
||||||
graphics.DeleteImage(dst);
|
graphics.DeleteImage(dst);
|
||||||
dst.memory = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.extent = {extent.width, extent.height, 1};
|
|
||||||
dst.format = format;
|
|
||||||
dst.layers = 1;
|
|
||||||
dst.mip_levels = 1;
|
|
||||||
dst.state = {};
|
|
||||||
dst.subresource_states.clear();
|
|
||||||
dst.memory.property = vk::MemoryPropertyFlagBits::eDeviceLocal;
|
|
||||||
|
|
||||||
vk::ImageCreateInfo create {};
|
vk::ImageCreateInfo create {};
|
||||||
create.sType = vk::StructureType::eImageCreateInfo;
|
create.sType = vk::StructureType::eImageCreateInfo;
|
||||||
create.imageType = vk::ImageType::e2D;
|
create.imageType = vk::ImageType::e2D;
|
||||||
create.extent = {dst.extent.width, dst.extent.height, 1};
|
create.extent = {extent.width, extent.height, 1};
|
||||||
create.mipLevels = 1;
|
create.mipLevels = 1;
|
||||||
create.arrayLayers = 1;
|
create.arrayLayers = 1;
|
||||||
create.format = dst.format;
|
create.format = format;
|
||||||
create.tiling = vk::ImageTiling::eOptimal;
|
create.tiling = vk::ImageTiling::eOptimal;
|
||||||
create.initialLayout = vk::ImageLayout::eUndefined;
|
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||||
create.usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
create.usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||||
@@ -216,7 +207,7 @@ void Presenter::Frame::Configure(GraphicContext& graphics, vk::Extent2D extent,
|
|||||||
create.samples = vk::SampleCountFlagBits::e1;
|
create.samples = vk::SampleCountFlagBits::e1;
|
||||||
if (!graphics.CreateImage(create, dst)) {
|
if (!graphics.CreateImage(create, dst)) {
|
||||||
EXIT("failed to allocate prepared presentation image, extent=%ux%u format=%d\n",
|
EXIT("failed to allocate prepared presentation image, extent=%ux%u format=%d\n",
|
||||||
dst.extent.width, dst.extent.height, static_cast<int>(dst.format));
|
extent.width, extent.height, static_cast<int>(format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1762,6 +1762,73 @@ public:
|
|||||||
return Renderer();
|
return Renderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckHostImageAllocation() {
|
||||||
|
constexpr const char *name = "HostImageAllocation";
|
||||||
|
auto &graphics = RuntimeContext();
|
||||||
|
VulkanImage image;
|
||||||
|
vk::ImageCreateInfo create{};
|
||||||
|
create.flags = vk::ImageCreateFlagBits::eMutableFormat;
|
||||||
|
create.imageType = vk::ImageType::e2D;
|
||||||
|
create.format = vk::Format::eR8G8B8A8Unorm;
|
||||||
|
create.extent = {16, 8, 1};
|
||||||
|
create.mipLevels = 3;
|
||||||
|
create.arrayLayers = 4;
|
||||||
|
create.samples = vk::SampleCountFlagBits::e1;
|
||||||
|
create.tiling = vk::ImageTiling::eOptimal;
|
||||||
|
create.usage = vk::ImageUsageFlagBits::eTransferSrc |
|
||||||
|
vk::ImageUsageFlagBits::eTransferDst |
|
||||||
|
vk::ImageUsageFlagBits::eSampled;
|
||||||
|
create.initialLayout = vk::ImageLayout::eUndefined;
|
||||||
|
|
||||||
|
for (uint32_t pass = 0; pass < 2; ++pass) {
|
||||||
|
Require(name, "create", graphics.CreateImage(create, image),
|
||||||
|
"could not allocate native image");
|
||||||
|
Require(name, "allocation metadata",
|
||||||
|
image.image != nullptr && image.allocation != nullptr &&
|
||||||
|
image.format == create.format &&
|
||||||
|
image.image_type == create.imageType &&
|
||||||
|
image.extent == create.extent &&
|
||||||
|
image.layers == create.arrayLayers &&
|
||||||
|
image.mip_levels == create.mipLevels &&
|
||||||
|
image.samples == static_cast<uint32_t>(create.samples) &&
|
||||||
|
image.usage == create.usage && image.flags == create.flags,
|
||||||
|
"native image metadata did not follow its creation descriptor");
|
||||||
|
Require(name, "fresh synchronization state",
|
||||||
|
image.state.layout == create.initialLayout &&
|
||||||
|
image.state.access_mask == vk::AccessFlags2{} &&
|
||||||
|
image.state.pl_stage == vk::PipelineStageFlagBits2::eAllCommands &&
|
||||||
|
image.subresource_states.empty(),
|
||||||
|
"new allocation retained synchronization state from an old image");
|
||||||
|
VkMemoryPropertyFlags properties{};
|
||||||
|
vmaGetAllocationMemoryProperties(graphics.allocator, image.allocation,
|
||||||
|
&properties);
|
||||||
|
Require(name, "allocation policy",
|
||||||
|
(properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0,
|
||||||
|
"native image lost its device-local memory requirement");
|
||||||
|
|
||||||
|
// A reused presentation image may still describe its previous recording.
|
||||||
|
// No commands use this fixture; seed that bookkeeping before retirement.
|
||||||
|
image.state = {vk::PipelineStageFlagBits2::eTransfer,
|
||||||
|
vk::AccessFlagBits2::eTransferWrite,
|
||||||
|
vk::ImageLayout::eTransferDstOptimal};
|
||||||
|
image.subresource_states.assign(image.layers * image.mip_levels, image.state);
|
||||||
|
graphics.DeleteImage(image);
|
||||||
|
Require(name, "retirement",
|
||||||
|
image.image == nullptr && image.allocation == nullptr,
|
||||||
|
"retired native image retained an allocation handle");
|
||||||
|
|
||||||
|
create.flags = vk::ImageCreateFlagBits::e2DArrayCompatible;
|
||||||
|
create.imageType = vk::ImageType::e3D;
|
||||||
|
create.format = vk::Format::eR32Uint;
|
||||||
|
create.extent = {8, 4, 2};
|
||||||
|
create.mipLevels = 2;
|
||||||
|
create.arrayLayers = 1;
|
||||||
|
create.usage = vk::ImageUsageFlagBits::eTransferSrc |
|
||||||
|
vk::ImageUsageFlagBits::eTransferDst;
|
||||||
|
}
|
||||||
|
std::printf("[host] %-32s ok\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckDescriptorHeapLargeSet() {
|
void CheckDescriptorHeapLargeSet() {
|
||||||
EnsureRuntimeContext();
|
EnsureRuntimeContext();
|
||||||
std::array<vk::DescriptorSetLayoutBinding, 3> bindings{};
|
std::array<vk::DescriptorSetLayoutBinding, 3> bindings{};
|
||||||
@@ -28116,6 +28183,11 @@ int main(int argc, char **argv) {
|
|||||||
vulkan.CheckSchedulerTimeline();
|
vulkan.CheckSchedulerTimeline();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--host-image-allocation-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
vulkan.CheckHostImageAllocation();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--occlusion-dump-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--occlusion-dump-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
|
CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer());
|
||||||
@@ -28428,6 +28500,7 @@ int main(int argc, char **argv) {
|
|||||||
CheckIndirectImageKeySwitch();
|
CheckIndirectImageKeySwitch();
|
||||||
CheckPs5GameExampleImageClearRuntimeShape();
|
CheckPs5GameExampleImageClearRuntimeShape();
|
||||||
vulkan.CheckSchedulerTimeline();
|
vulkan.CheckSchedulerTimeline();
|
||||||
|
vulkan.CheckHostImageAllocation();
|
||||||
vulkan.CheckDescriptorHeapLargeSet();
|
vulkan.CheckDescriptorHeapLargeSet();
|
||||||
vulkan.CheckGraphicsPushConstantBank();
|
vulkan.CheckGraphicsPushConstantBank();
|
||||||
vulkan.CheckGpuMappedRangeLifecycle();
|
vulkan.CheckGpuMappedRangeLifecycle();
|
||||||
|
|||||||
Reference in New Issue
Block a user