mirror of
https://github.com/doitsujin/dxvk.git
synced 2024-12-02 10:24:12 +01:00
[dxvk] Add functions to recreate images with additional usage info
Useful when we don't know in advance which usage flags are required and don't want to set them preemptively for performance reasons.
This commit is contained in:
parent
53f14e2914
commit
bfbb7987b2
@ -1853,7 +1853,15 @@ namespace dxvk {
|
||||
void DxvkContext::invalidateImage(
|
||||
const Rc<DxvkImage>& image,
|
||||
Rc<DxvkResourceAllocation>&& slice) {
|
||||
Rc<DxvkResourceAllocation> prevAllocation = image->assignResource(std::move(slice));
|
||||
invalidateImageWithUsage(image, std::move(slice), DxvkImageUsageInfo());
|
||||
}
|
||||
|
||||
|
||||
void DxvkContext::invalidateImageWithUsage(
|
||||
const Rc<DxvkImage>& image,
|
||||
Rc<DxvkResourceAllocation>&& slice,
|
||||
const DxvkImageUsageInfo& usageInfo) {
|
||||
Rc<DxvkResourceAllocation> prevAllocation = image->assignResourceWithUsage(std::move(slice), usageInfo);
|
||||
m_cmd->trackResource<DxvkAccess::None>(std::move(prevAllocation));
|
||||
|
||||
VkImageUsageFlags usage = image->info().usage;
|
||||
@ -1875,6 +1883,140 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
bool DxvkContext::ensureImageCompatibility(
|
||||
const Rc<DxvkImage>& image,
|
||||
const DxvkImageUsageInfo& usageInfo) {
|
||||
// Check whether image usage, flags and view formats are supported.
|
||||
// If any of these are false, we need to create a new image.
|
||||
bool isUsageAndFormatCompatible = (image->info().usage & usageInfo.usage) == usageInfo.usage
|
||||
&& (image->info().flags & usageInfo.flags) == usageInfo.flags;
|
||||
|
||||
for (uint32_t i = 0; i < usageInfo.viewFormatCount && isUsageAndFormatCompatible; i++)
|
||||
isUsageAndFormatCompatible &= image->isViewCompatible(usageInfo.viewFormats[i]);
|
||||
|
||||
// Check if we need to insert a barrier and update image properties
|
||||
bool isAccessAndLayoutCompatible = (image->info().stages & usageInfo.stages) == usageInfo.stages
|
||||
&& (image->info().access & usageInfo.access) == usageInfo.access
|
||||
&& (usageInfo.layout && image->info().layout == usageInfo.layout);
|
||||
|
||||
// If everything matches already, no need to do anything.
|
||||
if (isUsageAndFormatCompatible && isAccessAndLayoutCompatible)
|
||||
return true;
|
||||
|
||||
// Ensure the image is accessible and in its default layout
|
||||
this->spillRenderPass(true);
|
||||
this->prepareImage(image, image->getAvailableSubresources());
|
||||
|
||||
if (m_execBarriers.isImageDirty(image, image->getAvailableSubresources(), DxvkAccess::Write))
|
||||
m_execBarriers.recordCommands(m_cmd);
|
||||
|
||||
if (isUsageAndFormatCompatible) {
|
||||
// Emit a barrier. If used in internal passes, this function
|
||||
// must be called *before* emitting dirty checks there.
|
||||
VkImageLayout oldLayout = image->info().layout;
|
||||
VkImageLayout newLayout = usageInfo.layout ? usageInfo.layout : oldLayout;
|
||||
|
||||
image->assignResourceWithUsage(image->getAllocation(), usageInfo);
|
||||
|
||||
m_execBarriers.accessImage(image, image->getAvailableSubresources(),
|
||||
oldLayout, image->info().stages, image->info().access,
|
||||
newLayout, image->info().stages, image->info().access);
|
||||
|
||||
m_cmd->trackResource<DxvkAccess::Write>(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some images have to stay in their place, we can't do much in that case.
|
||||
if (!image->canRelocate()) {
|
||||
Logger::err(str::format("DxvkContext: Cannot relocate image:",
|
||||
"\n Current usage: 0x", std::hex, image->info().usage, ", flags: 0x", image->info().flags, ", ", std::dec, image->info().viewFormatCount, " view formats"
|
||||
"\n Requested usage: 0x", std::hex, usageInfo.usage, ", flags: 0x", usageInfo.flags, ", ", std::dec, usageInfo.viewFormatCount, " view formats"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable mutable format bit as necessary. We do not require
|
||||
// setting this explicitly so that the caller does not have
|
||||
// to check image formats every time.
|
||||
VkImageCreateFlags createFlags = 0u;
|
||||
|
||||
for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {
|
||||
if (usageInfo.viewFormats[i] != image->info().format) {
|
||||
createFlags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the image can support the requested usage
|
||||
VkFormatFeatureFlagBits2 required = 0u;
|
||||
|
||||
switch (usageInfo.usage) {
|
||||
case VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT:
|
||||
required |= VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT:
|
||||
required |= VK_FORMAT_FEATURE_2_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_USAGE_SAMPLED_BIT:
|
||||
required |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_USAGE_STORAGE_BIT:
|
||||
required |= VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_USAGE_TRANSFER_SRC_BIT:
|
||||
required |= VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_USAGE_TRANSFER_DST_BIT:
|
||||
required |= VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure to use the correct set of feature flags for this image
|
||||
auto features = m_device->getFormatFeatures(image->info().format);
|
||||
auto& supported = image->info().tiling == VK_IMAGE_TILING_OPTIMAL
|
||||
? features.optimal : features.linear;
|
||||
|
||||
if ((supported & required) != required) {
|
||||
// Check if any of the view formats support the required features
|
||||
for (uint32_t i = 0; i < image->info().viewFormatCount; i++) {
|
||||
auto extendedFeatures = m_device->getFormatFeatures(image->info().viewFormats[i]);
|
||||
features.optimal |= extendedFeatures.optimal;
|
||||
features.linear |= extendedFeatures.linear;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {
|
||||
auto extendedFeatures = m_device->getFormatFeatures(usageInfo.viewFormats[i]);
|
||||
features.optimal |= extendedFeatures.optimal;
|
||||
features.linear |= extendedFeatures.linear;
|
||||
}
|
||||
|
||||
if ((supported & required) != required)
|
||||
return false;
|
||||
|
||||
// We're good, just need to enable extended usage
|
||||
createFlags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
|
||||
}
|
||||
|
||||
// Allocate new backing storage and relocate the image
|
||||
auto storage = image->createResourceWithUsage(usageInfo);
|
||||
|
||||
DxvkRelocateImageInfo relocateInfo;
|
||||
relocateInfo.image = image;
|
||||
relocateInfo.storage = storage;
|
||||
relocateInfo.usageInfo = usageInfo;
|
||||
|
||||
relocateResources(0, nullptr, 1, &relocateInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DxvkContext::resolveImage(
|
||||
const Rc<DxvkImage>& dstImage,
|
||||
const Rc<DxvkImage>& srcImage,
|
||||
@ -6421,10 +6563,10 @@ namespace dxvk {
|
||||
dstBarrier.subresourceRange = info.image->getAvailableSubresources();
|
||||
|
||||
VkImageMemoryBarrier2 srcBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
dstBarrier.srcStageMask = info.image->info().stages;
|
||||
dstBarrier.srcAccessMask = info.image->info().access;
|
||||
dstBarrier.dstStageMask = info.image->info().stages;
|
||||
dstBarrier.dstAccessMask = info.image->info().access;
|
||||
srcBarrier.srcStageMask = info.image->info().stages;
|
||||
srcBarrier.srcAccessMask = info.image->info().access;
|
||||
srcBarrier.dstStageMask = info.image->info().stages;
|
||||
srcBarrier.dstAccessMask = info.image->info().access;
|
||||
srcBarrier.oldLayout = info.image->info().layout;
|
||||
srcBarrier.newLayout = info.image->pickLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
srcBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
@ -6531,7 +6673,7 @@ namespace dxvk {
|
||||
m_cmd->trackResource<DxvkAccess::Write>(info.image);
|
||||
|
||||
// Invalidate image and emit post-copy barrier to use the correct layout
|
||||
invalidateImage(info.image, Rc<DxvkResourceAllocation>(info.storage));
|
||||
invalidateImageWithUsage(info.image, Rc<DxvkResourceAllocation>(info.storage), info.usageInfo);
|
||||
|
||||
VkImageMemoryBarrier2 dstBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 };
|
||||
dstBarrier.srcStageMask = info.image->info().stages;
|
||||
|
@ -977,6 +977,35 @@ namespace dxvk {
|
||||
const Rc<DxvkImage>& image,
|
||||
Rc<DxvkResourceAllocation>&& slice);
|
||||
|
||||
/**
|
||||
* \brief Invalidates image content and add usage flag
|
||||
*
|
||||
* Replaces the backing storage of an image.
|
||||
* \warning If the image is used by another context,
|
||||
* invalidating it will result in undefined behaviour.
|
||||
* \param [in] buffer The buffer to invalidate
|
||||
* \param [in] slice New buffer slice
|
||||
* \param [in] usageInfo Added usage info
|
||||
*/
|
||||
void invalidateImageWithUsage(
|
||||
const Rc<DxvkImage>& image,
|
||||
Rc<DxvkResourceAllocation>&& slice,
|
||||
const DxvkImageUsageInfo& usageInfo);
|
||||
|
||||
/**
|
||||
* \brief Ensures that an image supports the given usage
|
||||
*
|
||||
* No-op if the image already supports the requested properties.
|
||||
* Otherwise, this will allocate a new backing resource with the
|
||||
* requested properties and copy the current contents to it.
|
||||
* \param [in] image Image resource
|
||||
* \param [in] usageInfo Usage info to add
|
||||
* \returns \c true if the image can support the given usage
|
||||
*/
|
||||
bool ensureImageCompatibility(
|
||||
const Rc<DxvkImage>& image,
|
||||
const DxvkImageUsageInfo& usageInfo);
|
||||
|
||||
/**
|
||||
* \brief Updates push constants
|
||||
*
|
||||
|
@ -17,7 +17,7 @@ namespace dxvk {
|
||||
copyFormatList(createInfo.viewFormatCount, createInfo.viewFormats);
|
||||
|
||||
// Determine whether the image is shareable before creating the resource
|
||||
VkImageCreateInfo imageInfo = getImageCreateInfo();
|
||||
VkImageCreateInfo imageInfo = getImageCreateInfo(DxvkImageUsageInfo());
|
||||
m_shared = canShareImage(device, imageInfo, m_info.sharing);
|
||||
|
||||
assignResource(createResource());
|
||||
@ -38,7 +38,7 @@ namespace dxvk {
|
||||
copyFormatList(createInfo.viewFormatCount, createInfo.viewFormats);
|
||||
|
||||
// Create backing storage for existing image resource
|
||||
VkImageCreateInfo imageInfo = getImageCreateInfo();
|
||||
VkImageCreateInfo imageInfo = getImageCreateInfo(DxvkImageUsageInfo());
|
||||
assignResource(m_allocator->importImageResource(imageInfo, imageHandle));
|
||||
}
|
||||
|
||||
@ -48,6 +48,13 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
bool DxvkImage::canRelocate() const {
|
||||
return !m_imageInfo.mapPtr && !m_shared
|
||||
&& !m_storage->flags().test(DxvkAllocationFlag::Imported)
|
||||
&& !(m_info.flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT);
|
||||
}
|
||||
|
||||
|
||||
VkSubresourceLayout DxvkImage::querySubresourceLayout(
|
||||
const VkImageSubresource& subresource) const {
|
||||
VkSubresourceLayout result = { };
|
||||
@ -97,17 +104,36 @@ namespace dxvk {
|
||||
|
||||
|
||||
Rc<DxvkResourceAllocation> DxvkImage::createResource() {
|
||||
const DxvkFormatInfo* formatInfo = lookupFormatInfo(m_info.format);
|
||||
return createResourceWithUsage(DxvkImageUsageInfo());
|
||||
}
|
||||
|
||||
VkImageCreateInfo imageInfo = getImageCreateInfo();
|
||||
|
||||
Rc<DxvkResourceAllocation> DxvkImage::createResourceWithUsage(const DxvkImageUsageInfo& usageInfo) {
|
||||
const DxvkFormatInfo* formatInfo = lookupFormatInfo(m_info.format);
|
||||
small_vector<VkFormat, 4> localViewFormats;
|
||||
|
||||
VkImageCreateInfo imageInfo = getImageCreateInfo(usageInfo);
|
||||
|
||||
// Set up view format list so that drivers can better enable
|
||||
// compression. Skip for planar formats due to validation errors.
|
||||
VkImageFormatListCreateInfo formatList = { VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO };
|
||||
|
||||
if (!(formatInfo->aspectMask & VK_IMAGE_ASPECT_PLANE_0_BIT)) {
|
||||
formatList.viewFormatCount = m_info.viewFormatCount;
|
||||
formatList.pViewFormats = m_info.viewFormats;
|
||||
if (usageInfo.viewFormatCount) {
|
||||
for (uint32_t i = 0; i < m_viewFormats.size(); i++)
|
||||
localViewFormats.push_back(m_viewFormats[i]);
|
||||
|
||||
for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {
|
||||
if (!isViewCompatible(usageInfo.viewFormats[i]))
|
||||
localViewFormats.push_back(usageInfo.viewFormats[i]);
|
||||
}
|
||||
|
||||
formatList.viewFormatCount = localViewFormats.size();
|
||||
formatList.pViewFormats = localViewFormats.data();
|
||||
} else {
|
||||
formatList.viewFormatCount = m_viewFormats.size();
|
||||
formatList.pViewFormats = m_viewFormats.data();
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_info.flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) && formatList.viewFormatCount)
|
||||
@ -142,9 +168,52 @@ namespace dxvk {
|
||||
}
|
||||
|
||||
|
||||
VkImageCreateInfo DxvkImage::getImageCreateInfo() const {
|
||||
Rc<DxvkResourceAllocation> DxvkImage::assignResource(
|
||||
Rc<DxvkResourceAllocation>&& resource) {
|
||||
return assignResourceWithUsage(std::move(resource), DxvkImageUsageInfo());
|
||||
}
|
||||
|
||||
|
||||
Rc<DxvkResourceAllocation> DxvkImage::assignResourceWithUsage(
|
||||
Rc<DxvkResourceAllocation>&& resource,
|
||||
const DxvkImageUsageInfo& usageInfo) {
|
||||
Rc<DxvkResourceAllocation> old = std::move(m_storage);
|
||||
|
||||
// Self-assignment is possible here if we
|
||||
// just update the image properties
|
||||
m_storage = std::move(resource);
|
||||
|
||||
if (m_storage != old) {
|
||||
m_imageInfo = m_storage->getImageInfo();
|
||||
m_version += 1u;
|
||||
}
|
||||
|
||||
m_info.flags |= usageInfo.flags;
|
||||
m_info.usage |= usageInfo.usage;
|
||||
m_info.stages |= usageInfo.stages;
|
||||
m_info.access |= usageInfo.access;
|
||||
|
||||
if (usageInfo.layout != VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
m_info.layout = usageInfo.layout;
|
||||
|
||||
for (uint32_t i = 0; i < usageInfo.viewFormatCount; i++) {
|
||||
if (!isViewCompatible(usageInfo.viewFormats[i]))
|
||||
m_viewFormats.push_back(usageInfo.viewFormats[i]);
|
||||
}
|
||||
|
||||
if (!m_viewFormats.empty()) {
|
||||
m_info.viewFormatCount = m_viewFormats.size();
|
||||
m_info.viewFormats = m_viewFormats.data();
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
VkImageCreateInfo DxvkImage::getImageCreateInfo(
|
||||
const DxvkImageUsageInfo& usageInfo) const {
|
||||
VkImageCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
|
||||
info.flags = m_info.flags;
|
||||
info.flags = m_info.flags | usageInfo.flags;
|
||||
info.imageType = m_info.type;
|
||||
info.format = m_info.format;
|
||||
info.extent = m_info.extent;
|
||||
@ -152,7 +221,7 @@ namespace dxvk {
|
||||
info.arrayLayers = m_info.numLayers;
|
||||
info.samples = m_info.sampleCount;
|
||||
info.tiling = m_info.tiling;
|
||||
info.usage = m_info.usage;
|
||||
info.usage = m_info.usage | usageInfo.usage;
|
||||
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
info.initialLayout = m_info.initialLayout;
|
||||
|
||||
|
@ -70,6 +70,30 @@ namespace dxvk {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Extra image usage info
|
||||
*
|
||||
* Useful when recreating an image with different usage flags.
|
||||
*/
|
||||
struct DxvkImageUsageInfo {
|
||||
// New image flags to add.
|
||||
VkImageCreateFlags flags = 0u;
|
||||
// Usage flags to add to the image
|
||||
VkImageUsageFlags usage = 0u;
|
||||
// Stage flags to add to the image
|
||||
VkPipelineStageFlags stages = 0u;
|
||||
// Access flags to add to the image
|
||||
VkAccessFlags access = 0u;
|
||||
// New image layout. If undefined, the
|
||||
// default layout will not be changed.
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
// Number of new view formats to add
|
||||
uint32_t viewFormatCount = 0u;
|
||||
// View formats to add to the compatibility list
|
||||
const VkFormat* viewFormats = nullptr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Virtual image view
|
||||
*
|
||||
@ -458,6 +482,15 @@ namespace dxvk {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether the image can be relocated
|
||||
*
|
||||
* Images that are shared, imported from a different API
|
||||
* or mapped to host address space cannot be relocated.
|
||||
* \returns \c true if the image can be relocated
|
||||
*/
|
||||
bool canRelocate() const;
|
||||
|
||||
/**
|
||||
* \brief Queries memory layout of a subresource
|
||||
*
|
||||
@ -489,6 +522,17 @@ namespace dxvk {
|
||||
*/
|
||||
Rc<DxvkResourceAllocation> createResource();
|
||||
|
||||
/**
|
||||
* \brief Creates image resource with extra usage
|
||||
*
|
||||
* Creates new backing storage with additional usage flags
|
||||
* enabled. Useful to expand on usage flags after creation.
|
||||
* \param [in] usage Usage flags to add
|
||||
* \returns New underlying image resource
|
||||
*/
|
||||
Rc<DxvkResourceAllocation> createResourceWithUsage(
|
||||
const DxvkImageUsageInfo& usage);
|
||||
|
||||
/**
|
||||
* \brief Assigns backing storage to the image
|
||||
*
|
||||
@ -496,15 +540,20 @@ namespace dxvk {
|
||||
* \param [in] resource New backing storage
|
||||
* \returns Previous backing storage
|
||||
*/
|
||||
Rc<DxvkResourceAllocation> assignResource(Rc<DxvkResourceAllocation>&& resource) {
|
||||
Rc<DxvkResourceAllocation> old = std::move(m_storage);
|
||||
Rc<DxvkResourceAllocation> assignResource(
|
||||
Rc<DxvkResourceAllocation>&& resource);
|
||||
|
||||
m_storage = std::move(resource);
|
||||
m_imageInfo = m_storage->getImageInfo();
|
||||
|
||||
m_version += 1u;
|
||||
return old;
|
||||
}
|
||||
/**
|
||||
* \brief Assigns backing storage to the image with extra usage
|
||||
*
|
||||
* Implicitly invalidates all image views.
|
||||
* \param [in] resource New backing storage
|
||||
* \param [in] usageInfo Added usage info
|
||||
* \returns Previous backing storage
|
||||
*/
|
||||
Rc<DxvkResourceAllocation> assignResourceWithUsage(
|
||||
Rc<DxvkResourceAllocation>&& resource,
|
||||
const DxvkImageUsageInfo& usageInfo);
|
||||
|
||||
/**
|
||||
* \brief Retrieves current backing storage
|
||||
@ -545,7 +594,8 @@ namespace dxvk {
|
||||
std::unordered_map<DxvkImageViewKey,
|
||||
DxvkImageView, DxvkHash, DxvkEq> m_views;
|
||||
|
||||
VkImageCreateInfo getImageCreateInfo() const;
|
||||
VkImageCreateInfo getImageCreateInfo(
|
||||
const DxvkImageUsageInfo& usageInfo) const;
|
||||
|
||||
void copyFormatList(
|
||||
uint32_t formatCount,
|
||||
@ -567,6 +617,8 @@ namespace dxvk {
|
||||
Rc<DxvkImage> image;
|
||||
/// Backing storage to copy to
|
||||
Rc<DxvkResourceAllocation> storage;
|
||||
/// Additional image usage
|
||||
DxvkImageUsageInfo usageInfo;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user