Merge pull request #5113 from XairRrR/fix/agc-depth-store-back-any-storage

fix(agc): store a written storage image over a depth plane into the surface
This commit is contained in:
Adrià authored and GitHub committed 2026-10-14 20:17:41 +02:00
commit 32e810db49
2 files changed
+79 -6

No files matched your search

@@ -213,7 +213,7 @@ public:
if (plane.storage != nullptr) plane.replaced.push_back(plane.storage);
plane.storage = StorageTexture::Create(context, *context.detiler, resource, 0);
}
copyPlane(plane, stencil, true);
copyPlane(plane, plane.storage, stencil, true);
return plane.storage;
}
@@ -221,7 +221,7 @@ public:
for (const bool stencil : {false, true}) {
auto& plane = stencil ? stencilPlane : depthPlane;
if (plane.storage.get() == &candidate) {
copyPlane(plane, stencil, false);
copyPlane(plane, plane.storage, stencil, false);
written = ++depthWrites;
return true;
}
@@ -233,6 +233,18 @@ public:
return false;
}
bool StoreBackCovering(const StorageTexture& candidate) {
for (const bool stencil : {false, true}) {
if (!CoversPlane(candidate, stencil)) continue;
auto& plane = stencil ? stencilPlane : depthPlane;
const auto source = std::const_pointer_cast<StorageTexture>(candidate.shared_from_this());
copyPlane(plane, source, stencil, false);
written = ++depthWrites;
return true;
}
return false;
}
const Context context;
const DepthTarget target;
const VkFormat hostFormat;
@@ -250,20 +262,30 @@ private:
PlaneStorage depthPlane;
PlaneStorage stencilPlane;
void copyPlane(PlaneStorage& plane, bool stencil, bool toStorage) {
bool CoversPlane(const StorageTexture& storage, bool stencil) const {
const auto& resource = storage.Descriptor();
const auto address = stencil ? target.stencilAddress : target.address;
if (address == 0 || resource.baseAddress != address || resource.width != target.extent.width || resource.height != target.extent.height || resource.baseLevel != 0 || resource.mipCount != 1) return false;
if (resource.dimension != TextureDimension::k2D && (resource.dimension != TextureDimension::k2DArray || resource.baseArray != 0 || resource.depthOrLastArray != 0)) return false;
if (target.samples != 1 || (!stencil && hostFormat != target.format)) return false;
const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT;
return ResolveTextureFormat(resource.format) == (stencil ? VK_FORMAT_R8_UINT : d16 ? VK_FORMAT_R16_UNORM : VK_FORMAT_R32_SFLOAT);
}
void copyPlane(PlaneStorage& plane, const std::shared_ptr<StorageTexture>& storage, bool stencil, bool toStorage) {
auto* recorder = Recorder::Active();
Require(recorder != nullptr, "depth surface storage copies need an active recorder");
const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT;
const auto bytes = static_cast<std::size_t>(target.extent.width) * target.extent.height * (stencil ? 1u : d16 ? 2u : 4u);
if (plane.staging == nullptr) plane.staging = std::make_unique<DeviceBuffer>(context, bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const auto commands = recorder->Commands();
recorder->Keep(plane.storage);
recorder->Keep(storage);
const auto timing = recorder->BeginGpuTiming(Recorder::CommandClass::Copy);
VkBufferImageCopy planeRegion{0, 0, 0, {stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1}, {0, 0, 0}, {target.extent.width, target.extent.height, 1}};
VkBufferImageCopy colorRegion = planeRegion;
colorRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
const auto sourceImage = toStorage ? image : plane.storage->Image();
const auto destinationImage = toStorage ? plane.storage->Image() : image;
const auto sourceImage = toStorage ? image : storage->Image();
const auto destinationImage = toStorage ? storage->Image() : image;
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
context.Function<PFN_vkCmdCopyImageToBuffer>("vkCmdCopyImageToBuffer")(commands, sourceImage, VK_IMAGE_LAYOUT_GENERAL, plane.staging->Handle(), 1, toStorage ? &planeRegion : &colorRegion);
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
@@ -941,6 +963,9 @@ bool DepthSurfaceStoreBack(const Context& context, const StorageTexture& storage
for (const auto& surface : surfaces()) {
if (surface->context.device == context.device && surface->StoreBack(storage)) return true;
}
for (auto surface = surfaces().rbegin(); surface != surfaces().rend(); ++surface) {
if ((*surface)->context.device == context.device && (*surface)->StoreBackCovering(storage)) return true;
}
return false;
}
@@ -246,6 +246,52 @@ void Run(Context context, std::uint8_t* block) {
ExpectFailure([&] { static_cast<void>(DepthSurfaceStorage(context, {}, layeredDepth, 0)); }, "with a layer pitch of", "a two-layer 2D array view of the depth plane with a layer pitch other than the depth slice size was accepted");
layeredDepth.baseArray = 1;
ExpectFailure([&] { static_cast<void>(DepthSurfaceStorage(context, {}, layeredDepth, 0)); }, "with a layer pitch of", "a 2D array view of layer 1 of the depth plane with a layer pitch other than the depth slice size was accepted");
const auto own = StorageTexture::Create(context, *context.detiler, PlaneResource(address, Format32Float), 0);
const std::array<std::uint32_t, 4> eighth{std::bit_cast<std::uint32_t>(0.125f), std::bit_cast<std::uint32_t>(0.125f), std::bit_cast<std::uint32_t>(0.125f), std::bit_cast<std::uint32_t>(0.125f)};
Require(own->FillClear(eighth, StorageTexture::WholeImage, refusal), refusal != nullptr ? refusal : "the storage image could not be filled");
Require(DepthSurfaceStoreBack(context, *own), "a storage image over the depth plane that the depth surface did not hand out was not stored back");
submit(recorder);
const auto reread = DepthSurfaceStorage(context, {}, PlaneResource(address, Format32Float), 0);
Require(reread != nullptr, "the depth plane has no storage image");
submit(recorder);
readBack(*reread);
Require(CountFloats(block, 0.125f) == Side * Side, "a storage image written over the depth plane did not reach the depth surface");
Require(!DepthSurfaceStoreBack(context, *StorageTexture::Create(context, *context.detiler, PlaneResource(address, Format32Float, Side / 2), 0)), "a storage image of another extent over the depth memory was stored into the depth surface");
}
void RunPairings(Context context, std::uint8_t* block) {
const auto address = reinterpret_cast<std::uint64_t>(block);
const auto submit = [&](Recorder& recorder) {
recorder.Submit();
Check(context.Function<PFN_vkQueueWaitIdle>("vkQueueWaitIdle")(context.queue), "vkQueueWaitIdle");
};
Recorder recorder(context);
recorder.Activate();
const auto readBack = [&](StorageTexture& texture) {
texture.MarkDirty();
texture.WriteBack();
submit(recorder);
};
const DepthTarget first{address, address + BlockBytes / 2, {Side, Side}, VK_FORMAT_D32_SFLOAT_S8_UINT, 0.25f, 7};
const DepthTarget second{address, address + BlockBytes / 2 + 8192, {Side, Side}, VK_FORMAT_D32_SFLOAT_S8_UINT, 0.5f, 3};
Require(DepthSurfaceView(context, first) != VK_NULL_HANDLE && DepthSurfaceView(context, second) != VK_NULL_HANDLE, "the two pairings of the depth plane were not created");
const auto depth = DepthSurfaceStorage(context, {}, PlaneResource(address, Format32Float), 0);
Require(depth != nullptr, "the depth plane has no storage image");
submit(recorder);
const std::array<std::uint32_t, 4> pattern{std::bit_cast<std::uint32_t>(0.75f), std::bit_cast<std::uint32_t>(0.75f), std::bit_cast<std::uint32_t>(0.75f), std::bit_cast<std::uint32_t>(0.75f)};
const char* refusal = nullptr;
Require(depth->FillClear(pattern, StorageTexture::WholeImage, refusal), refusal != nullptr ? refusal : "the storage image could not be filled");
Require(DepthSurfaceStoreBack(context, *depth), "the newest pairing's storage image was not stored back");
submit(recorder);
const auto reread = DepthSurfaceStorage(context, {}, PlaneResource(address, Format32Float), 0);
Require(reread != nullptr, "the depth plane has no storage image");
submit(recorder);
readBack(*reread);
Require(CountFloats(block, 0.75f) == Side * Side, "a storage image stored back went into an older pairing instead of the pairing that handed it out");
auto layered = PlaneResource(address, Format32Float);
layered.dimension = TextureDimension::k2DArray;
Require(DepthSurfaceStorage(context, {}, layered, 0) != nullptr, "a one-layer 2D array view of the depth plane was not taken");
ExpectFailure([&] { static_cast<void>(DepthSurfaceStoreBack(context, *depth)); }, "through a view that another view of the plane", "a store back through a replaced view was taken by an older pairing that covers the plane");
}
}
@@ -269,6 +315,8 @@ int main() {
std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex());
Run(context, block);
ClearDepthSurfaces(context.device);
RunPairings(context, block);
ClearDepthSurfaces(context.device);
}
GuestAllocations::Mutation().Remove(block);
std::cout << "depth surface storage access tests passed\n";