fix(agc): bind array image views for Image2DMultisampleArray descriptors

This commit is contained in:
hamb3r committed 2026-10-16 03:53:34 +03:00
1 parent b155e1d0eb
commit 729558cc66
6 files changed
+37 -29

No files matched your search

@@ -42,7 +42,7 @@ void NoteDepthMetadataFill(std::uint64_t address, std::size_t bytes, std::uint32
VkImageAspectFlags HtileFillClears(std::uint32_t pattern, bool stencilInHtile);
std::uint64_t HtileMetadataBytes(VkExtent2D extent);
bool HtileFillCovers(std::uint64_t htile, VkExtent2D extent, std::uint64_t address, std::size_t bytes);
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components);
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D);
VkFormat HostDepthFormat(const Context& context, VkFormat format);
float HostDepthBiasConstant(const Context& context, VkFormat format, float constant);
std::shared_ptr<StorageTexture> DepthSurfaceStorage(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, std::uint32_t mip);
@@ -52,7 +52,7 @@ std::uint64_t MultisampledColorBytes(const ColorTarget& target);
bool MultisampledFillClearColor(const ColorTarget& target, std::uint32_t fill, VkClearColorValue& clear);
bool TakeMultisampledClear(MultisampledCmask& cmask, MultisampledColorFill& colorFill, const ColorTarget& use, VkClearColorValue& clear);
VkImageView MultisampleTargetView(const Context& context, const ColorTarget& target);
std::shared_ptr<Texture> MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components);
std::shared_ptr<Texture> MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D);
void MaterializeMultisampleTargetClear(const Context& context, const ColorTarget& target);
void ResolveMultisampleTarget(const Context& context, const ColorTarget& source, StorageTexture& destination, VkImageView destinationView);
bool MultisampledColorFillAbsorbs(std::uint64_t address, std::size_t bytes, std::uint32_t pattern);
@@ -39,7 +39,7 @@ struct TextureCacheUse {
};
TextureCacheUse TextureCacheUsage();
std::uint64_t SampledTextureCacheBudget(const Context& context);
std::shared_ptr<Texture> CachedSampledTexture(const Context& context, std::span<const std::uint32_t> words);
std::shared_ptr<Texture> CachedSampledTexture(const Context& context, std::span<const std::uint32_t> words, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D);
bool SampledTexturesShareEntry(std::span<const std::uint32_t> first, std::span<const std::uint32_t> second);
bool CompletePendingCpuWrites(const Context& context, std::uint64_t address, std::size_t bytes);
@@ -410,6 +410,7 @@ private:
struct PreviousSampled {
std::array<std::uint32_t, 8> words{};
bool depthCompare = false;
VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D;
std::shared_ptr<Texture> texture;
};
PreviousSampled previousSampled;
@@ -280,15 +280,16 @@ public:
return (ResolveTextureFormat(resource.format) == expected || depthBits || UnormStencilView(resource)) && oneSlice && resource.width == target.extent.width && resource.height == target.extent.height && resource.baseLevel == 0 && resource.lastLevel == 0 && resource.baseArray == 0;
}
std::shared_ptr<Texture> Sampled(std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, bool drawn = false) {
std::shared_ptr<Texture> Sampled(std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, bool drawn = false, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D) {
CheckCurrent();
CheckResource(resource);
std::array<std::uint32_t, 12> key{};
std::array<std::uint32_t, 13> key{};
std::copy_n(words.begin(), std::min<std::size_t>(words.size(), 8), key.begin());
key[8] = components.r;
key[9] = components.g;
key[10] = components.b;
key[11] = components.a;
key[12] = static_cast<std::uint32_t>(multisampleViewType);
if (const auto found = textures.find(key); found != textures.end()) return found->second;
const bool multisampled = target.samples != 1;
Require(!multisampled || sampleable, "the device cannot sample the native depth target's format and sample count");
@@ -312,7 +313,7 @@ public:
static_cast<unsigned>(resource.tileMode), static_cast<int>(resource.dimension), resource.baseLevel, resource.lastLevel, resource.baseArray, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
throw std::runtime_error(text);
}
const auto viewType = resource.dimension == TextureDimension::k2DArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
const auto viewType = multisampled ? multisampleViewType : resource.dimension == TextureDimension::k2DArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
auto texture = std::make_shared<Texture>(context, image, hostFormat, stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, components, viewType, 0, owned);
if (multisampled) {
Require(samplingActive, "native multisample depth sampling requires its creation-time owner");
@@ -493,18 +494,18 @@ private:
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
std::unique_ptr<DeviceBuffer> staging;
std::map<std::array<std::uint32_t, 12>, std::shared_ptr<Texture>> textures;
std::map<std::array<std::uint32_t, 13>, std::shared_ptr<Texture>> textures;
std::uint64_t written = 0;
bool filled = false;
};
std::map<std::array<std::uint32_t, 12>, std::shared_ptr<Texture>> textures;
std::map<std::array<std::uint32_t, 13>, std::shared_ptr<Texture>> textures;
PlaneStorage depthPlane;
PlaneStorage stencilPlane;
std::uint64_t depthMemory = 0;
std::uint64_t stencilMemory = 0;
StencilCopy stencilCopy;
std::shared_ptr<Texture> unormStencil(const std::array<std::uint32_t, 12>& key, VkComponentMapping components, VkImageViewType viewType, bool drawn) {
std::shared_ptr<Texture> unormStencil(const std::array<std::uint32_t, 13>& key, VkComponentMapping components, VkImageViewType viewType, bool drawn) {
if (stencilCopy.image == VK_NULL_HANDLE) {
VkImageCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
info.imageType = VK_IMAGE_TYPE_2D;
@@ -1317,7 +1318,7 @@ void ClearDepthSurfaces(VkDevice device) {
UpdateResidentImageUnmapObserver_nid_no_patch();
}
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components) {
std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType multisampleViewType) {
const auto held = [&](const auto& surface) {
return surface->active->load(std::memory_order_acquire) && surface->context.device == context.device && !surface->retired && (surface->target.address == resource.baseAddress || (surface->target.stencilAddress != 0 && surface->target.stencilAddress == resource.baseAddress));
};
@@ -1341,7 +1342,7 @@ std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<c
Require(found->pendingClear == 0u, "native multisample depth fast-clear transition is not implemented");
RecordMemoryBarrier(context, recorder->Commands(), VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
Recorder::CountBarriers(Recorder::CommandClass::Draw, 1);
return found->Sampled(words, resource, components);
return found->Sampled(words, resource, components, false, multisampleViewType);
}
if (resource.width != base.extent.width || resource.height != base.extent.height) return nullptr;
const bool d16 = base.format == VK_FORMAT_D16_UNORM || base.format == VK_FORMAT_D16_UNORM_S8_UINT;
@@ -126,7 +126,7 @@ public:
});
}
std::shared_ptr<Texture> Sampled(VkComponentMapping components) {
std::shared_ptr<Texture> Sampled(VkComponentMapping components, VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D) {
Require(sampleable, "the device cannot sample the multisampled color target's format at its sample count");
Require(cmask.Refusal().empty(), cmask.Refusal());
Require(cmask.Keys() != DccKeys::Clear0000, "sampling a multisampled color target whose CMASK fast clear no draw has applied is not implemented");
@@ -136,9 +136,9 @@ public:
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
Recorder::CountBarriers(Recorder::CommandClass::Draw, 1);
});
const std::array<std::uint32_t, 4> key{static_cast<std::uint32_t>(components.r), static_cast<std::uint32_t>(components.g), static_cast<std::uint32_t>(components.b), static_cast<std::uint32_t>(components.a)};
const std::array<std::uint32_t, 5> key{static_cast<std::uint32_t>(components.r), static_cast<std::uint32_t>(components.g), static_cast<std::uint32_t>(components.b), static_cast<std::uint32_t>(components.a), static_cast<std::uint32_t>(viewType)};
if (const auto found = textures.find(key); found != textures.end()) return found->second;
auto texture = std::make_shared<Texture>(context, image, target.format, VK_IMAGE_ASPECT_COLOR_BIT, components);
auto texture = std::make_shared<Texture>(context, image, target.format, VK_IMAGE_ASPECT_COLOR_BIT, components, viewType);
textures.emplace(key, texture);
return texture;
}
@@ -188,7 +188,7 @@ public:
private:
bool sampleable = false;
std::map<std::array<std::uint32_t, 4>, std::shared_ptr<Texture>> textures;
std::map<std::array<std::uint32_t, 5>, std::shared_ptr<Texture>> textures;
void release() noexcept {
textures.clear();
@@ -465,7 +465,7 @@ VkImageView MultisampleTargetView(const Context& context, const ColorTarget& tar
return acquire(context, target).view;
}
std::shared_ptr<Texture> MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components) {
std::shared_ptr<Texture> MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType) {
std::lock_guard lock(targetsMutex());
const auto format = ResolveTextureFormat(resource.format);
const MultisampleTarget* other = nullptr;
@@ -473,12 +473,12 @@ std::shared_ptr<Texture> MultisampleTargetTexture(const Context& context, const
if (!target->active->load(std::memory_order_acquire)) continue;
if (target->context.device != context.device || target->target.surfaceAddress != resource.baseAddress) continue;
const auto& rendered = target->target;
if (rendered.format == format && rendered.extent.width == resource.width && rendered.extent.height == resource.height && rendered.samples == resource.samples && rendered.mip == 0) return target->Sampled(components);
if (rendered.format == format && rendered.extent.width == resource.width && rendered.extent.height == resource.height && rendered.samples == resource.samples && rendered.mip == 0) return target->Sampled(components, viewType);
other = target.get();
}
if (other == nullptr) {
Require(Recorder::Active() != nullptr, "a multisampled texture is only sampled by recorded draws and dispatches");
return createFilled(context, resource, format).Sampled(components);
return createFilled(context, resource, format).Sampled(components, viewType);
}
char text[320];
std::snprintf(text, sizeof(text), "AGC graphics: sampling multisampled color target 0x%llx (%ux%u, %u samples, vk format %d) as a %ux%u texture of %u samples and guest format %u (vk %d) is not implemented", static_cast<unsigned long long>(resource.baseAddress), other->target.extent.width, other->target.extent.height, other->target.samples, static_cast<int>(other->target.format), resource.width, resource.height, resource.samples, resource.format, static_cast<int>(format));
@@ -560,14 +560,14 @@ std::array<std::uint64_t, 4> untrackedKey(std::uint64_t address, std::size_t byt
}
// `guestBytes` is the surface size when the caller described the surface already (0: described here).
std::shared_ptr<Texture> cachedTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, std::uint64_t guestBytes = 0, bool depthCompare = false) {
std::shared_ptr<Texture> cachedTexture(const Context& context, std::span<const std::uint32_t> words, const GuestTextureResource& resource, VkComponentMapping components, std::uint64_t guestBytes = 0, bool depthCompare = false, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D) {
if (resource.dimension == TextureDimension::k2DMsaa) {
Require(!depthCompare, "native multisampled depth comparison sampling is not implemented");
if (auto depth = DepthSurfaceTexture(context, words, resource, components)) {
if (auto depth = DepthSurfaceTexture(context, words, resource, components, multisampleViewType)) {
logLookup({depth.get(), resource, 0, DccKeys::Uncompressed, 0, nullptr, true});
return depth;
}
auto target = MultisampleTargetTexture(context, resource, components);
auto target = MultisampleTargetTexture(context, resource, components, multisampleViewType);
logLookup({target.get(), resource, 0, DccKeys::Uncompressed, 0, nullptr, true});
return target;
}
@@ -1044,9 +1044,9 @@ std::uint64_t SampledTextureCacheBudget(const Context& context) {
return sampledBudget(context, cache);
}
std::shared_ptr<Texture> CachedSampledTexture(const Context& context, std::span<const std::uint32_t> words) {
std::shared_ptr<Texture> CachedSampledTexture(const Context& context, std::span<const std::uint32_t> words, VkImageViewType multisampleViewType) {
const auto resource = DecodeTextureResource(words);
return cachedTexture(context, words, resource, ViewComponents(resource));
return cachedTexture(context, words, resource, ViewComponents(resource), 0, false, multisampleViewType);
}
bool SampledTexturesShareEntry(std::span<const std::uint32_t> first, std::span<const std::uint32_t> second) {
@@ -2849,7 +2849,8 @@ bool ShaderResources::Revalidate(std::span<const CompiledShader> shaders, ProofR
if (IsNullTextureResource(words)) continue;
const auto resource = DecodeTextureResource(words);
const VkComponentMapping components = ViewComponents(resource);
if (textureIndex >= textures.size() || cachedTexture(context, words, resource, components, 0, !binding.imageDepthCompare.empty() && binding.imageDepthCompare.at(element)) != textures[textureIndex]) return false;
const VkImageViewType multisampleViewType = binding.imageShape == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
if (textureIndex >= textures.size() || cachedTexture(context, words, resource, components, 0, !binding.imageDepthCompare.empty() && binding.imageDepthCompare.at(element), multisampleViewType) != textures[textureIndex]) return false;
++textureIndex;
}
} else if (binding.kind == ShaderRecompiler::DescriptorKind::StorageImage) {
@@ -2894,7 +2895,8 @@ bool ShaderResources::Revalidate(std::span<const CompiledShader> shaders, ProofR
if (table.elementTextures[element] == std::numeric_limits<std::size_t>::max()) continue;
const auto words = std::span<const std::uint32_t>(images->guestDescriptor).subspan(static_cast<std::size_t>(element) * 8u, 8u);
const auto resource = DecodeTextureResource(words);
if (textureIndex >= textures.size() || table.elementTextures[element] != textureIndex || cachedTexture(context, words, resource, ViewComponents(resource), 0, images->imageDepthCompare.at(element)) != textures[textureIndex]) return false;
const VkImageViewType multisampleViewType = images->imageShapes.at(element) == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
if (textureIndex >= textures.size() || table.elementTextures[element] != textureIndex || cachedTexture(context, words, resource, ViewComponents(resource), 0, images->imageDepthCompare.at(element), multisampleViewType) != textures[textureIndex]) return false;
++textureIndex;
}
}
@@ -3989,12 +3991,13 @@ void ShaderResources::resolveImageBinding(const ShaderRecompiler::DescriptorBind
if (resource.dccAddress != 0) CompletePendingCpuWrites(context, resource.dccAddress, DccKeyBytes(guestBytes));
std::shared_ptr<Texture> texture;
const bool depthCompare = !binding.imageDepthCompare.empty() && binding.imageDepthCompare.at(element);
if (previousSampled.texture != nullptr && previousSampled.depthCompare == depthCompare && words.size() == 8 && std::equal(words.begin(), words.end(), previousSampled.words.begin())) texture = previousSampled.texture;
const VkImageViewType multisampleViewType = binding.imageShape == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
if (previousSampled.texture != nullptr && previousSampled.depthCompare == depthCompare && previousSampled.multisampleViewType == multisampleViewType && words.size() == 8 && std::equal(words.begin(), words.end(), previousSampled.words.begin())) texture = previousSampled.texture;
if (texture == nullptr && record != nullptr && record->texture != nullptr) {
texture = fastTexture(*record);
(texture != nullptr ? counters.fastHits : counters.fastMisses).fetch_add(1, std::memory_order_relaxed);
}
if (texture == nullptr) texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare);
if (texture == nullptr) texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare, multisampleViewType);
if (resource.dimension == TextureDimension::k2DMsaa) multisampleSources.push_back(resource.baseAddress);
bool baseLevel = false;
if (element < binding.imageUnnormalized.size() && binding.imageUnnormalized[element]) {
@@ -4020,6 +4023,7 @@ void ShaderResources::resolveImageBinding(const ShaderRecompiler::DescriptorBind
if (words.size() == 8) {
std::copy(words.begin(), words.end(), previousSampled.words.begin());
previousSampled.depthCompare = depthCompare;
previousSampled.multisampleViewType = multisampleViewType;
previousSampled.texture = texture;
}
textures.push_back(std::move(texture));
@@ -4238,9 +4242,10 @@ void ShaderResources::resolveImageTables() {
GuestTextureResource resource{};
std::uint64_t guestBytes = 0;
bool firstLayer = false;
auto shape = ShaderRecompiler::DescriptorImageShape::Image2D;
try {
resource = DecodeTextureResource(words);
const auto shape = binding.imageShapes.at(element);
shape = binding.imageShapes.at(element);
firstLayer = shape == ShaderRecompiler::DescriptorImageShape::Image2D && resource.dimension == TextureDimension::k2DArray;
if (!firstLayer && !MatchesGuestDimension(shape, resource.dimension)) throw std::runtime_error("AGC graphics: guest texture dimension disagrees with the table entry's image shape (shape " + std::to_string(static_cast<int>(shape)) + ", dimension " + std::to_string(static_cast<int>(resource.dimension)) + ")");
guestBytes = DescribeSurface(resource).guestBytes;
@@ -4256,8 +4261,9 @@ void ShaderResources::resolveImageTables() {
std::shared_ptr<Texture> texture;
const auto components = ViewComponents(resource);
const bool depthCompare = binding.imageDepthCompare.at(element);
const VkImageViewType multisampleViewType = shape == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
try {
texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare);
texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare, multisampleViewType);
} catch (const std::runtime_error& error) {
table.map[faults + element] = rejectTableElement(table, words, error.what());
if (reusable) fresh.elements.push_back({{}, {}, {}, 0, false, true, error.what()});