mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
feat(agc): render layered draws into 2D array color views
A draw whose vertex shader exports the render target array index into a color view of several slices of a 2D array target was refused. The view now renders layers lastSlice - slice + 1 through a 2D array attachment view of the array's resident image starting at its first slice, and the draw marks every rendered slice dirty. A view of several slices of a surface that is not a 2D array is still refused, as is a layered draw without the array's resident image. AI-assisted: yes
This commit is contained in:
1 parent
95d901b937
commit
8c84dfcd35
5 files changed
+22
-16
No files matched your search
@@ -197,7 +197,7 @@ public:
|
||||
void WriteBack();
|
||||
// Deferred write-back (APS5_EAGER_WRITEBACK=1 stores at once instead).
|
||||
void MarkDirty();
|
||||
void MarkSliceDirty(std::uint32_t slice);
|
||||
void MarkSliceDirty(std::uint32_t slice, std::uint32_t count = 1);
|
||||
void Flush();
|
||||
bool Dirty() const;
|
||||
// Stores every pending image overlapping the range, except `except`; returns whether any was.
|
||||
|
||||
@@ -1648,7 +1648,7 @@ struct Kept {
|
||||
// The completion side of a recorded draw: the kept objects, the record check, the lease outcome,
|
||||
// the GPU write notes, the copied-buffer write-back (listed in DrawCopiedWriters or run as a
|
||||
// completion action) and the targets marked dirty.
|
||||
void keepRecordedDraw(Recorder& recorder, const std::shared_ptr<ShaderResources>& resources, std::shared_ptr<Pipeline> pipeline, std::shared_ptr<Framebuffer> framebuffer, DrawInputs& inputs, std::vector<std::shared_ptr<StorageTexture>> targets, std::vector<std::uint32_t> slices, std::unique_ptr<DeviceBuffer> scratch, std::function<void()> checkRecords, bool listed, bool completion, const DrawOutcome& outcome) {
|
||||
void keepRecordedDraw(Recorder& recorder, const std::shared_ptr<ShaderResources>& resources, std::shared_ptr<Pipeline> pipeline, std::shared_ptr<Framebuffer> framebuffer, DrawInputs& inputs, std::vector<std::shared_ptr<StorageTexture>> targets, std::vector<std::pair<std::uint32_t, std::uint32_t>> slices, std::unique_ptr<DeviceBuffer> scratch, std::function<void()> checkRecords, bool listed, bool completion, const DrawOutcome& outcome) {
|
||||
auto kept = std::make_shared<Kept>();
|
||||
kept->resources = resources;
|
||||
kept->pipeline = std::move(pipeline);
|
||||
@@ -1689,7 +1689,7 @@ void keepRecordedDraw(Recorder& recorder, const std::shared_ptr<ShaderResources>
|
||||
}
|
||||
for (std::size_t index = 0; index < residents.size(); ++index) {
|
||||
if (residents[index] == nullptr) continue;
|
||||
if (index < slices.size()) residents[index]->MarkSliceDirty(slices[index]);
|
||||
if (index < slices.size()) residents[index]->MarkSliceDirty(slices[index].first, slices[index].second);
|
||||
else residents[index]->MarkDirty();
|
||||
}
|
||||
for (const auto& resident : residents) {
|
||||
@@ -1709,7 +1709,7 @@ struct RecordedDraw {
|
||||
std::shared_ptr<Framebuffer> framebuffer;
|
||||
std::span<const VkImageView> targetViews;
|
||||
std::vector<std::shared_ptr<StorageTexture>> targets;
|
||||
std::vector<std::uint32_t> targetSlices;
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> targetSlices;
|
||||
std::vector<std::shared_ptr<void>> multisampleTargets;
|
||||
std::vector<std::shared_ptr<StorageTexture>> proxies;
|
||||
std::vector<VkImage> multisampleImages;
|
||||
@@ -2217,6 +2217,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
|
||||
materializeCmaskClear(context, color, nullptr);
|
||||
Require(!color.mipTail || binding.gpuTiling, "rendering into a packed mip tail requires the GPU tiled fallback");
|
||||
Require(color.depth == 1, "rendering into a 3D color target needs the resident image of its surface");
|
||||
Require(color.layers == 1, "layered rendering into a 2D array color target needs the resident image of its array");
|
||||
Require(color.arraySlice == 0 || !ColorTileModeIsXor(color.tileMode), "rendering into a slice of a 2D array color target with an XOR tile mode needs the resident image of its array");
|
||||
if (binding.gpuTiling) {
|
||||
binding.mip = color.mipTail ? ComputeColorTargetMipLayout(color.tileMode, color.elementBytes, color.surfaceExtent.width, color.surfaceExtent.height, color.mipCount, color.mip) : ColorTargetMip(color, colorLayout);
|
||||
@@ -2451,7 +2452,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
|
||||
record.framebuffer = framebuffer;
|
||||
record.targetViews = targetViews;
|
||||
record.targets = owners;
|
||||
for (const auto& binding : targets) record.targetSlices.push_back(binding.color.arraySlice);
|
||||
for (const auto& binding : targets) record.targetSlices.emplace_back(binding.color.arraySlice, binding.color.arrayLastSlice != 0 ? binding.color.layers : 1u);
|
||||
for (const auto& binding : targets) {
|
||||
if (binding.proxied) record.proxies.push_back(binding.resident);
|
||||
if (binding.multisampled) {
|
||||
@@ -2665,7 +2666,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
|
||||
APS5_LOG_CHARS_OUT_DEBUG("SubmitAndWait");
|
||||
timer.phase(PhaseRecord);
|
||||
if (recorded) {
|
||||
keepRecordedDraw(*recorder, resources, pipeline, framebuffer, inputs, owners, [&] { std::vector<std::uint32_t> slices; for (const auto& binding : targets) slices.push_back(binding.color.arraySlice); return slices; }(), std::move(scratch), std::move(checkRecords), listed, completion, outcome);
|
||||
keepRecordedDraw(*recorder, resources, pipeline, framebuffer, inputs, owners, [&] { std::vector<std::pair<std::uint32_t, std::uint32_t>> slices; for (const auto& binding : targets) slices.emplace_back(binding.color.arraySlice, binding.color.arrayLastSlice != 0 ? binding.color.layers : 1u); return slices; }(), std::move(scratch), std::move(checkRecords), listed, completion, outcome);
|
||||
timer.phase(PhaseKeep);
|
||||
if (outcome.waited) {
|
||||
// The wait the dispatch path makes for such work (source 3, "address-based"): the
|
||||
@@ -2711,7 +2712,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
|
||||
if (skipTargetWrite) break;
|
||||
if (binding.resident != nullptr) {
|
||||
// The results stay on the GPU until something reads the target's memory.
|
||||
binding.resident->MarkSliceDirty(binding.color.arraySlice);
|
||||
binding.resident->MarkSliceDirty(binding.color.arraySlice, binding.color.arrayLastSlice != 0 ? binding.color.layers : 1u);
|
||||
continue;
|
||||
}
|
||||
if (binding.gpuTiling) GuestMemory::WriteChanged(binding.color.address, binding.tiled->Bytes(), binding.original);
|
||||
@@ -2854,7 +2855,7 @@ DrawRecipeOutcome DrawWithRecipe(const Context& context, const State& state, con
|
||||
record.framebuffer = std::move(framebuffer);
|
||||
record.targetViews = recipe.targetViews;
|
||||
record.targets = std::move(targets);
|
||||
for (const auto& color : state.colors) record.targetSlices.push_back(color.arraySlice);
|
||||
for (const auto& color : state.colors) record.targetSlices.emplace_back(color.arraySlice, color.arrayLastSlice != 0 ? color.layers : 1u);
|
||||
record.pushBytes = &recipe.pushBytes;
|
||||
record.pushStages = recipe.pushStages;
|
||||
recordDraw(context, state, draw, shaders, inputs, record, outcome, timer, ownWaitedMs);
|
||||
|
||||
@@ -1659,7 +1659,10 @@ ColorTarget DecodeColorBuffer(const Registers& cx, std::uint32_t slot, bool allo
|
||||
}
|
||||
if (!volume && lastSlice != slice) {
|
||||
const auto exports = find(cx, 0x207);
|
||||
Require(exports == cx.end() || (exports->second & (1u << 18u)) == 0, "layered draws into a color view of several array slices are unsupported");
|
||||
if (exports != cx.end() && (exports->second & (1u << 18u)) != 0) {
|
||||
Require(color.arrayLastSlice != 0 && lastSlice > slice, "layered draws into a color view of several slices of a surface that is not a 2D array are unsupported");
|
||||
color.layers = lastSlice + 1u - slice;
|
||||
}
|
||||
}
|
||||
color.extent = {((attrib2 >> 14u) & 0x3fffu) + 1u, (attrib2 & 0x3fffu) + 1u};
|
||||
color.elementBytes = decoded.elementBytes;
|
||||
|
||||
@@ -1820,8 +1820,9 @@ VkImageView StorageTexture::AttachmentView(VkFormat format, std::uint32_t mip, s
|
||||
Require(attachable, "storage image cannot be a color attachment");
|
||||
Require(mip < descriptor.mipCount, "attachment mip exceeds the storage image");
|
||||
const bool volume = descriptor.dimension == TextureDimension::k3D;
|
||||
Require((depthSlice == 0 && layers == 1) || (volume && mip == 0 && layers != 0 && depthSlice + layers - 1u <= descriptor.depthOrLastArray), "attachment slice is outside the storage image");
|
||||
Require(layer == 0 || (!volume && descriptor.dimension == TextureDimension::k2DArray && descriptor.baseArray + layer <= descriptor.depthOrLastArray), "attachment layer is outside the storage image");
|
||||
const bool array = !volume && descriptor.dimension == TextureDimension::k2DArray;
|
||||
Require((depthSlice == 0 && layers == 1) || (volume && mip == 0 && layers != 0 && depthSlice + layers - 1u <= descriptor.depthOrLastArray) || (array && depthSlice == 0 && layers != 0), "attachment slice is outside the storage image");
|
||||
Require((layer == 0 && layers == 1) || volume || (array && descriptor.baseArray + layer + layers - 1u <= descriptor.depthOrLastArray), "attachment layer is outside the storage image");
|
||||
const auto found = attachmentViews.find({format, mip, depthSlice, layers, layer});
|
||||
if (found != attachmentViews.end()) return found->second;
|
||||
VkImageViewUsageCreateInfo usage{VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO};
|
||||
@@ -3513,14 +3514,14 @@ void StorageTexture::MarkDirty() {
|
||||
markLayersPending(0, trackedLayers);
|
||||
}
|
||||
|
||||
void StorageTexture::MarkSliceDirty(std::uint32_t slice) {
|
||||
if (geometry.thick || geometry.imageLayers <= 1 || slice >= geometry.imageLayers || geometry.layerBytes == 0 || trackedLayerBytes == 0 || descriptor.dccAddress != 0) {
|
||||
void StorageTexture::MarkSliceDirty(std::uint32_t slice, std::uint32_t count) {
|
||||
if (geometry.thick || geometry.imageLayers <= 1 || count == 0 || slice >= geometry.imageLayers || count > geometry.imageLayers - slice || geometry.layerBytes == 0 || trackedLayerBytes == 0 || descriptor.dccAddress != 0) {
|
||||
MarkDirty();
|
||||
return;
|
||||
}
|
||||
const auto begin = geometry.GuestLayerOffset(slice);
|
||||
const auto first = static_cast<std::uint32_t>(begin / trackedLayerBytes);
|
||||
const auto last = static_cast<std::uint32_t>(std::min<std::uint64_t>((begin + geometry.layerBytes + trackedLayerBytes - 1) / trackedLayerBytes, trackedLayers));
|
||||
const auto last = static_cast<std::uint32_t>(std::min<std::uint64_t>((begin + geometry.layerBytes * count + trackedLayerBytes - 1) / trackedLayerBytes, trackedLayers));
|
||||
if (first >= last) {
|
||||
MarkDirty();
|
||||
return;
|
||||
|
||||
@@ -2341,7 +2341,7 @@ void DepthStencilTests() {
|
||||
const auto firstSlice = AgcDriver::Graphics::DecodeState(queue);
|
||||
Require(firstSlice.color.address == reinterpret_cast<std::uintptr_t>(colorMemory.data()), "a color view of several slices did not retain its first slice");
|
||||
queue.context[0x207] = 1u << 18u;
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "layered draws into a color view of several array slices");
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "layered draws into a color view of several slices of a surface that is not a 2D array");
|
||||
queue.context[0x207] = 0;
|
||||
const auto sliced = reinterpret_cast<std::uintptr_t>(sliceMemory.data());
|
||||
queue.context[0x318] = static_cast<std::uint32_t>(sliced >> 8u);
|
||||
@@ -2356,7 +2356,8 @@ void DepthStencilTests() {
|
||||
const auto range = AgcDriver::Graphics::DecodeState(queue);
|
||||
Require(range.color.address == sliced && range.color.bytes == 1024u, "a color view of slices 0 to 1 of a 2D array target did not render into its first slice");
|
||||
queue.context[0x207] = 1u << 18u;
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "layered draws into a color view of several array slices");
|
||||
const auto layeredArray = AgcDriver::Graphics::DecodeState(queue);
|
||||
Require(layeredArray.color.address == sliced && layeredArray.color.arraySlice == 0u && layeredArray.color.layers == 2u && layeredArray.renderLayers == 2u && layeredArray.layerExports != 0u, "a layeredArray draw into slices 0 to 1 of a 2D array target did not render two layers");
|
||||
queue.context[0x207] = 0;
|
||||
queue.context[0x31b] = 2u | (2u << 13u);
|
||||
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "beyond the array surface");
|
||||
|
||||
Reference in new issue
Block a user