Merge pull request #2491 from oneandonlydean/up639/depth-plane-unchanged-copy-on-425
perf(agc): skip the depth plane copy when no slice changed
This commit is contained in:
commit
2a55cfe1e8
|
|
@ -23,6 +23,8 @@
|
|||
namespace AgcDriver::Graphics {
|
||||
namespace {
|
||||
|
||||
std::uint64_t depthWrites = 0;
|
||||
|
||||
Recorder* RecorderFor(const Context& context) {
|
||||
auto* recorder = Recorder::Active();
|
||||
return recorder != nullptr && recorder->Device() == context.device ? recorder : nullptr;
|
||||
|
|
@ -98,6 +100,7 @@ public:
|
|||
const auto aspects = pendingClear & (VK_IMAGE_ASPECT_DEPTH_BIT | (target.stencilAddress != 0 ? VK_IMAGE_ASPECT_STENCIL_BIT : 0u));
|
||||
pendingClear = 0;
|
||||
if (aspects == 0) return;
|
||||
written = ++depthWrites;
|
||||
auto* recorder = RecorderFor(context);
|
||||
std::unique_ptr<CommandBatch> batch;
|
||||
if (recorder == nullptr) batch = std::make_unique<CommandBatch>(context);
|
||||
|
|
@ -144,6 +147,8 @@ public:
|
|||
return std::any_of(textures.begin(), textures.end(), [&](const auto& entry) { return entry.second.get() == texture; });
|
||||
}
|
||||
|
||||
std::uint64_t written = ++depthWrites;
|
||||
|
||||
VkImageAspectFlags pendingClear = 0;
|
||||
float clearDepth = 0.0f;
|
||||
std::uint8_t clearStencil = 0;
|
||||
|
|
@ -168,9 +173,14 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
struct DepthPlaneSlice {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
std::uint64_t written = 0;
|
||||
};
|
||||
|
||||
class DepthPlaneCopy {
|
||||
public:
|
||||
DepthPlaneCopy(const Context& context, VkExtent2D extent, VkFormat format, std::uint32_t layers) : context(context), extent(extent), format(format), layers(layers), guestLayers(layers) {
|
||||
DepthPlaneCopy(const Context& context, VkExtent2D extent, VkFormat format, std::uint32_t layers) : context(context), extent(extent), format(format), layers(layers), guestLayers(layers), surfaceLayers(layers) {
|
||||
this->context.bufferPool.reset();
|
||||
VkImageCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
|
||||
info.flags = layers % 6u == 0 && extent.width == extent.height ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0u;
|
||||
|
|
@ -203,18 +213,24 @@ public:
|
|||
DepthPlaneCopy(const DepthPlaneCopy&) = delete;
|
||||
DepthPlaneCopy& operator=(const DepthPlaneCopy&) = delete;
|
||||
|
||||
std::shared_ptr<Texture> Refresh(std::span<const VkImage> slices, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType) {
|
||||
std::shared_ptr<Texture> Refresh(std::span<const DepthPlaneSlice> slices, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType) {
|
||||
Require(slices.size() == layers, "depth plane copy slices do not match its layers");
|
||||
const auto geometry = DescribeSurface(resource);
|
||||
Require(geometry.layers == layers && geometry.sliceLinearBytes == sliceBytes() && !geometry.mips.empty(), "depth plane copy geometry does not match its layers");
|
||||
std::vector<std::shared_ptr<Buffer>> uploads;
|
||||
std::vector<std::uint32_t> uploadLayers;
|
||||
std::vector<std::uint32_t> copiedLayers;
|
||||
for (std::uint32_t layer = 0; layer < layers; ++layer) {
|
||||
auto& held = guestLayers[layer];
|
||||
if (slices[layer] != VK_NULL_HANDLE) {
|
||||
auto& surface = surfaceLayers[layer];
|
||||
if (slices[layer].image != VK_NULL_HANDLE) {
|
||||
held = {};
|
||||
if (slices[layer].written != 0 && surface.image == slices[layer].image && surface.written == slices[layer].written) continue;
|
||||
surface = slices[layer];
|
||||
copiedLayers.push_back(layer);
|
||||
continue;
|
||||
}
|
||||
surface = {};
|
||||
const GuestLayer current{resource.baseAddress + geometry.GuestLayerOffset(layer), geometry.layerBytes, resource.tileMode, geometry.thick, GuestMemory::CollectWrites(resource.baseAddress + geometry.GuestLayerOffset(layer), static_cast<std::size_t>(geometry.layerBytes))};
|
||||
if (held.generation != 0 && held.address == current.address && held.bytes == current.bytes && held.tileMode == current.tileMode && held.thick == current.thick && !Recorder::SnapshotWriteOverlaps(current.address, static_cast<std::size_t>(current.bytes)) && GuestMemory::UnchangedSince(current.address, static_cast<std::size_t>(current.bytes), held.generation)) {
|
||||
held.generation = current.generation;
|
||||
|
|
@ -226,56 +242,7 @@ public:
|
|||
uploadLayers.push_back(layer);
|
||||
held = current;
|
||||
}
|
||||
if (!uploads.empty()) {
|
||||
Require(context.detiler != nullptr, "depth plane copy requires a texture detiler");
|
||||
context.detiler->BeginBatch();
|
||||
}
|
||||
auto* recorder = RecorderFor(context);
|
||||
std::unique_ptr<CommandBatch> batch;
|
||||
if (recorder == nullptr) batch = std::make_unique<CommandBatch>(context);
|
||||
const auto commands = recorder != nullptr ? recorder->Commands() : batch->Handle();
|
||||
const auto barrier = context.Resolved(&DeviceFunctions::cmdPipelineBarrier, "vkCmdPipelineBarrier");
|
||||
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
std::vector<VkBufferImageCopy> regions(layers);
|
||||
for (std::uint32_t layer = 0; layer < layers; ++layer) {
|
||||
auto& region = regions[layer];
|
||||
region.bufferOffset = sliceBytes() * layer;
|
||||
region.imageSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1};
|
||||
region.imageExtent = {extent.width, extent.height, 1};
|
||||
if (slices[layer] != VK_NULL_HANDLE) context.Function<PFN_vkCmdCopyImageToBuffer>("vkCmdCopyImageToBuffer")(commands, slices[layer], VK_IMAGE_LAYOUT_GENERAL, staging->Handle(), 1, ®ion);
|
||||
region.imageSubresource = {aspect(), 0, layer, 1};
|
||||
}
|
||||
if (!uploads.empty()) {
|
||||
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||
for (std::size_t upload = 0; upload < uploads.size(); ++upload) {
|
||||
const auto layer = uploadLayers[upload];
|
||||
context.detiler->Dispatch(commands, resource.tileMode, format == VK_FORMAT_D32_SFLOAT ? 4u : 2u, uploads[upload]->Handle(), 0, staging->Handle(), geometry.LinearLayerOffset(layer), geometry.mips.front(), false, layer, geometry.thick);
|
||||
}
|
||||
if (recorder != nullptr) {
|
||||
for (auto& upload : uploads) recorder->Keep(upload);
|
||||
}
|
||||
}
|
||||
VkImageMemoryBarrier toTransfer{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||
toTransfer.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toTransfer.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
toTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.image = image;
|
||||
toTransfer.subresourceRange = {aspect(), 0, 1, 0, layers};
|
||||
const VkBufferMemoryBarrier staged{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, nullptr, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, staging->Handle(), 0, VK_WHOLE_SIZE};
|
||||
const auto copyCommands = commands;
|
||||
barrier(copyCommands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &staged, 1, &toTransfer);
|
||||
context.Function<PFN_vkCmdCopyBufferToImage>("vkCmdCopyBufferToImage")(copyCommands, staging->Handle(), image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers, regions.data());
|
||||
VkImageMemoryBarrier toGeneral = toTransfer;
|
||||
toGeneral.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toGeneral.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toGeneral.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toGeneral.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
barrier(copyCommands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &toGeneral);
|
||||
if (batch) batch->SubmitAndWait();
|
||||
else Recorder::CountBarriers(Recorder::CommandClass::Draw, 3);
|
||||
if (!copiedLayers.empty() || !uploads.empty()) record(slices, resource, geometry, copiedLayers, uploads, uploadLayers);
|
||||
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)};
|
||||
auto& texture = textures[key];
|
||||
if (texture == nullptr) {
|
||||
|
|
@ -304,6 +271,66 @@ private:
|
|||
std::unique_ptr<DeviceBuffer> staging;
|
||||
std::map<std::array<std::uint32_t, 5>, std::shared_ptr<Texture>> textures;
|
||||
std::vector<GuestLayer> guestLayers;
|
||||
std::vector<DepthPlaneSlice> surfaceLayers;
|
||||
bool filled = false;
|
||||
|
||||
void record(std::span<const DepthPlaneSlice> slices, const GuestTextureResource& resource, const SurfaceGeometry& geometry, std::span<const std::uint32_t> copiedLayers, std::span<const std::shared_ptr<Buffer>> uploads, std::span<const std::uint32_t> uploadLayers) {
|
||||
if (!uploads.empty()) {
|
||||
Require(context.detiler != nullptr, "depth plane copy requires a texture detiler");
|
||||
context.detiler->BeginBatch();
|
||||
}
|
||||
auto* recorder = RecorderFor(context);
|
||||
std::unique_ptr<CommandBatch> batch;
|
||||
if (recorder == nullptr) batch = std::make_unique<CommandBatch>(context);
|
||||
const auto commands = recorder != nullptr ? recorder->Commands() : batch->Handle();
|
||||
const auto barrier = context.Resolved(&DeviceFunctions::cmdPipelineBarrier, "vkCmdPipelineBarrier");
|
||||
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
std::vector<VkBufferImageCopy> regions;
|
||||
const auto region = [&](std::uint32_t layer, VkImageAspectFlags aspectMask, std::uint32_t imageLayer) {
|
||||
VkBufferImageCopy copy{};
|
||||
copy.bufferOffset = sliceBytes() * layer;
|
||||
copy.imageSubresource = {aspectMask, 0, imageLayer, 1};
|
||||
copy.imageExtent = {extent.width, extent.height, 1};
|
||||
return copy;
|
||||
};
|
||||
for (const auto layer : copiedLayers) {
|
||||
const auto copy = region(layer, VK_IMAGE_ASPECT_DEPTH_BIT, 0);
|
||||
context.Function<PFN_vkCmdCopyImageToBuffer>("vkCmdCopyImageToBuffer")(commands, slices[layer].image, VK_IMAGE_LAYOUT_GENERAL, staging->Handle(), 1, ©);
|
||||
regions.push_back(region(layer, aspect(), layer));
|
||||
}
|
||||
if (!uploads.empty()) {
|
||||
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
|
||||
for (std::size_t upload = 0; upload < uploads.size(); ++upload) {
|
||||
const auto layer = uploadLayers[upload];
|
||||
context.detiler->Dispatch(commands, resource.tileMode, format == VK_FORMAT_D32_SFLOAT ? 4u : 2u, uploads[upload]->Handle(), 0, staging->Handle(), geometry.LinearLayerOffset(layer), geometry.mips.front(), false, layer, geometry.thick);
|
||||
regions.push_back(region(layer, aspect(), layer));
|
||||
}
|
||||
if (recorder != nullptr) {
|
||||
for (const auto& upload : uploads) recorder->Keep(upload);
|
||||
}
|
||||
}
|
||||
VkImageMemoryBarrier toTransfer{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
|
||||
toTransfer.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toTransfer.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toTransfer.oldLayout = filled ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
toTransfer.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toTransfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
toTransfer.image = image;
|
||||
toTransfer.subresourceRange = {aspect(), 0, 1, 0, layers};
|
||||
const VkBufferMemoryBarrier staged{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, nullptr, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, staging->Handle(), 0, VK_WHOLE_SIZE};
|
||||
barrier(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, &staged, 1, &toTransfer);
|
||||
context.Function<PFN_vkCmdCopyBufferToImage>("vkCmdCopyBufferToImage")(commands, staging->Handle(), image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<std::uint32_t>(regions.size()), regions.data());
|
||||
VkImageMemoryBarrier toGeneral = toTransfer;
|
||||
toGeneral.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
toGeneral.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
toGeneral.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
toGeneral.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
barrier(commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &toGeneral);
|
||||
filled = true;
|
||||
if (batch) batch->SubmitAndWait();
|
||||
else Recorder::CountBarriers(Recorder::CommandClass::Draw, 3);
|
||||
}
|
||||
|
||||
VkDeviceSize sliceBytes() const {
|
||||
return static_cast<VkDeviceSize>(extent.width) * extent.height * (format == VK_FORMAT_D32_SFLOAT ? 4u : 2u);
|
||||
|
|
@ -347,6 +374,8 @@ std::set<std::uint64_t>& clearedHtiles() {
|
|||
return *cleared;
|
||||
}
|
||||
|
||||
const DepthSurface* drawnDepth = nullptr;
|
||||
|
||||
}
|
||||
|
||||
std::uint64_t DepthSliceBytes(VkExtent2D extent, std::uint32_t bytesPerTexel) {
|
||||
|
|
@ -402,11 +431,14 @@ VkImageView DepthSurfaceView(const Context& context, const DepthTarget& target)
|
|||
surface->clearStencil = target.clearStencil;
|
||||
if (cleared) surface->pendingClear |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
surface->ApplyFastClear();
|
||||
surface->written = ++depthWrites;
|
||||
drawnDepth = surface.get();
|
||||
return surface->view;
|
||||
}
|
||||
surfaces().push_back(std::make_unique<DepthSurface>(context, target));
|
||||
surfaces().back()->clearDepth = target.clearDepth;
|
||||
surfaces().back()->clearStencil = target.clearStencil;
|
||||
drawnDepth = surfaces().back().get();
|
||||
return surfaces().back()->view;
|
||||
}
|
||||
|
||||
|
|
@ -456,6 +488,7 @@ void NoteHtileDepthClear(std::uint64_t htileAddress) {
|
|||
|
||||
void ClearDepthSurfaces(VkDevice device) {
|
||||
std::lock_guard lock(surfacesMutex());
|
||||
drawnDepth = nullptr;
|
||||
std::erase_if(surfaces(), [&](const auto& surface) { return surface->context.device == device; });
|
||||
std::erase_if(planeCopies(), [&](const auto& entry) { return std::get<0>(entry.first) == device; });
|
||||
}
|
||||
|
|
@ -496,12 +529,13 @@ std::shared_ptr<Texture> DepthSurfaceTexture(const Context& context, std::span<c
|
|||
std::snprintf(text, sizeof(text), "AGC graphics: sampling the depth planes of depth surface 0x%llx (%ux%u, vk format %d) as a %ux%u texture of guest format %u (vk %d), dimension %d, %u slices, levels %u-%u, base slice %u is not implemented", static_cast<unsigned long long>(base.address), base.extent.width, base.extent.height, static_cast<int>(base.format), resource.width, resource.height, resource.format, static_cast<int>(format), static_cast<int>(resource.dimension), layers, resource.baseLevel, resource.lastLevel, resource.baseArray);
|
||||
throw std::runtime_error(text);
|
||||
}
|
||||
std::vector<VkImage> slices;
|
||||
std::vector<DepthPlaneSlice> slices;
|
||||
for (std::uint32_t layer = 0; layer < layers; ++layer) {
|
||||
const auto address = base.address + static_cast<std::uint64_t>(layer) * DepthSliceBytes(base.extent, d16 ? 2u : 4u);
|
||||
const auto slice = std::find_if(list.rbegin(), list.rend(), [&](const auto& surface) { return surface->context.device == context.device && surface->target.address == address && surface->target.extent.width == base.extent.width && surface->target.extent.height == base.extent.height && surface->target.format == base.format; });
|
||||
if (slice != list.rend()) (*slice)->ApplyFastClear();
|
||||
slices.push_back(slice == list.rend() ? VK_NULL_HANDLE : (*slice)->image);
|
||||
if (slice == list.rend()) slices.push_back({});
|
||||
else slices.push_back({(*slice)->image, slice->get() == drawnDepth ? 0u : (*slice)->written});
|
||||
}
|
||||
const auto imageFormat = integer ? VK_FORMAT_R16_UINT : d16 ? VK_FORMAT_D16_UNORM : VK_FORMAT_D32_SFLOAT;
|
||||
auto& copy = planeCopies()[{context.device, base.address, base.extent.width, base.extent.height, imageFormat, layers}];
|
||||
|
|
|
|||
|
|
@ -39,6 +39,36 @@ constexpr std::size_t LayerBytes = std::size_t{Side} * Side * 4u;
|
|||
constexpr std::size_t BlockBytes = 2u * LayerBytes;
|
||||
constexpr int Skipped = 77;
|
||||
|
||||
PFN_vkGetDeviceProcAddr realDeviceProc = nullptr;
|
||||
PFN_vkCmdCopyImageToBuffer realCopyImageToBuffer = nullptr;
|
||||
PFN_vkCmdCopyBufferToImage realCopyBufferToImage = nullptr;
|
||||
std::atomic<std::uint32_t> imageToBufferCopies{0};
|
||||
std::atomic<std::uint32_t> bufferToImageCopies{0};
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL CountImageToBuffer(VkCommandBuffer commands, VkImage image, VkImageLayout layout, VkBuffer buffer, std::uint32_t count, const VkBufferImageCopy* regions) {
|
||||
imageToBufferCopies.fetch_add(1);
|
||||
realCopyImageToBuffer(commands, image, layout, buffer, count, regions);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL CountBufferToImage(VkCommandBuffer commands, VkBuffer buffer, VkImage image, VkImageLayout layout, std::uint32_t count, const VkBufferImageCopy* regions) {
|
||||
bufferToImageCopies.fetch_add(1);
|
||||
realCopyBufferToImage(commands, buffer, image, layout, count, regions);
|
||||
}
|
||||
|
||||
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL CountingDeviceProc(VkDevice device, const char* name) {
|
||||
const auto function = realDeviceProc(device, name);
|
||||
if (function == nullptr) return nullptr;
|
||||
if (std::strcmp(name, "vkCmdCopyImageToBuffer") == 0) {
|
||||
realCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(function);
|
||||
return reinterpret_cast<PFN_vkVoidFunction>(&CountImageToBuffer);
|
||||
}
|
||||
if (std::strcmp(name, "vkCmdCopyBufferToImage") == 0) {
|
||||
realCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(function);
|
||||
return reinterpret_cast<PFN_vkVoidFunction>(&CountBufferToImage);
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device() {
|
||||
|
|
@ -93,7 +123,8 @@ public:
|
|||
device.ppEnabledExtensionNames = extensionsEnabled.data();
|
||||
device.pEnabledFeatures = &enabled;
|
||||
Check(function<PFN_vkCreateDevice>("vkCreateDevice")(context.physical, &device, nullptr, &context.device), "vkCreateDevice");
|
||||
context.deviceProc = function<PFN_vkGetDeviceProcAddr>("vkGetDeviceProcAddr");
|
||||
realDeviceProc = function<PFN_vkGetDeviceProcAddr>("vkGetDeviceProcAddr");
|
||||
context.deviceProc = &CountingDeviceProc;
|
||||
function<PFN_vkGetPhysicalDeviceMemoryProperties>("vkGetPhysicalDeviceMemoryProperties")(context.physical, &context.memory);
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
function<PFN_vkGetPhysicalDeviceProperties>("vkGetPhysicalDeviceProperties")(context.physical, &properties);
|
||||
|
|
@ -235,6 +266,28 @@ void Run(const Context& base, const WatchedBlock& block) {
|
|||
Require(sample("after the re-read") == 0, "sampling the depth planes after the re-read of the written layer read it once more");
|
||||
AgcDriver::GuestMemory::MarkWritten(address + LayerBytes, 4096);
|
||||
Require(sample("after a driver store") != 0, "sampling the depth planes after a driver store over the guest layer did not read it again");
|
||||
const DepthTarget slice{address, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 1.0f, 0, 0};
|
||||
const DepthTarget other{address + 2 * BlockBytes, 0, {Side, Side}, VK_FORMAT_D32_SFLOAT, 1.0f, 0, 0};
|
||||
const auto copies = [&](const char* when, std::uint32_t fromSlices, std::uint32_t intoCopy) {
|
||||
imageToBufferCopies = 0;
|
||||
bufferToImageCopies = 0;
|
||||
sample(when);
|
||||
if (imageToBufferCopies.load() != fromSlices || bufferToImageCopies.load() != intoCopy) {
|
||||
throw std::runtime_error(std::string("sampling the depth planes ") + when + " recorded " + std::to_string(imageToBufferCopies.load()) + " depth surface copies and " + std::to_string(bufferToImageCopies.load()) + " plane copy fills, expected " + std::to_string(fromSlices) + " and " + std::to_string(intoCopy));
|
||||
}
|
||||
};
|
||||
DepthSurfaceView(context, other);
|
||||
copies("after a draw to another depth surface", 1, 1);
|
||||
copies("with nothing written since", 0, 0);
|
||||
DepthSurfaceView(context, slice);
|
||||
copies("while a draw to its depth surface is being recorded", 1, 1);
|
||||
copies("again while that draw is the last depth draw", 1, 1);
|
||||
DepthSurfaceView(context, other);
|
||||
copies("after that draw", 1, 1);
|
||||
copies("with nothing written since that draw", 0, 0);
|
||||
FillLayer(block.Data() + LayerBytes, 0.5f);
|
||||
copies("after a CPU write to the guest layer only", 0, 1);
|
||||
copies("with nothing written since the CPU write", 0, 0);
|
||||
} catch (...) {
|
||||
AgcDriver::GuestMemory::SetFlushHook(nullptr);
|
||||
throw;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
### Functional
|
||||
|
||||
- [Reused depth storage](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp): a storage view with another extent or a format that cannot hold the cached depth plane uses guest memory. Depth rendering is not written back to those guest bytes; this admission rule does not prove allocation reuse or preserve a depth writer through an incompatible view. Matching depth/stencil storage access still throws.
|
||||
- [Depth plane copies](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver): a depth plane sampled as a 2D array or a cube, or a D16 plane read as `R16_UINT`, is copied into an image of its own (`DepthPlaneCopy`) at every use, without a cache: each draw or dispatch that binds it copies every slice again, from the live depth surface when one is bound at the slice's address and from guest memory (detiled) otherwise. A slice's address is the base plus a whole 64 KiB-block depth slice per layer (`DepthSliceBytes`), and the cube face order is the array order; both are inferred from the titles' use, not measured on hardware. Planes with mips, a base slice other than 0, a cube with other than 1 or 6 slices, and other views of the planes still throw, and so do these copies of a 16-bit plane with stencil that the device stores as D24 or D32 (16-bit depth with stencil, below).
|
||||
- [Depth plane copies](../../../core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp) (libSceAgcDriver): a depth plane sampled as a 2D array or a cube, or a D16 plane read as `R16_UINT`, is copied into an image of its own (`DepthPlaneCopy`), kept per base address, extent, format and slice count; a slice comes from the live depth surface when one is bound at the slice's address and from guest memory (detiled) otherwise. A draw or dispatch that binds the plane copies again only the slices that changed and records nothing when none did: a surface slice when its image or write serial differs from the one copied (the serial is bumped when the surface is created, when a draw binds it as its depth target and when a pending fast clear is applied), a guest-memory slice when its bytes were written since they were last read (`GuestMemory::UnchangedSince`), a snapshot write overlaps them, or its address, size or tile mode changed. The surface of the latest depth draw is copied at every use until another depth target is bound, since that draw may still be recorded after the copy. A slice's address is the base plus a whole 64 KiB-block depth slice per layer (`DepthSliceBytes`), and the cube face order is the array order; both are inferred from the titles' use, not measured on hardware. Planes with mips, a base slice other than 0, a cube with other than 1 or 6 slices, and other views of the planes still throw, and so do these copies of a 16-bit plane with stencil that the device stores as D24 or D32 (16-bit depth with stencil, below).
|
||||
- [SW_64KB_D_X color targets](../../../core/libs/prx/libSceAgcDriver/Graphics/src/ColorTargetLayout.cpp) (libSceAgcDriver) use the existing texture block/mip geometry and equations from [AMD addrlib GFX10 non-RB+](https://github.com/GPUOpen-Drivers/pal/blob/c5e800072a32f68b6ccc4422936d96167c6e0728/src/core/imported/addrlib/src/gfx10/gfx10addrlib.cpp) for 16 pipes and 256-byte interleave. The address-reference tests use legal pipe/bank XOR bits for that configuration. Raw column bits outside the source configuration's pipe/bank mask 0xcf00 throw; their console meaning is unresolved. Console address equations and render results have not been measured.
|
||||
- [Per-face depth bias](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver): Vulkan has one depth bias per draw, so a draw that rasterizes both faces with different front and back bias (PA_SU_SC_MODE_CNTL, PA_SU_POLY_OFFSET_FRONT/BACK_*) is recorded twice, front faces with the back faces culled and then back faces with the front faces culled. The vertex stages run twice, so a vertex shader that writes memory writes twice, and all front faces land before all back faces instead of in primitive order, which differs only for blending or stencil updates that depend on the order.
|
||||
- [Partial rect-list primitives](../../../core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp) (libSceAgcDriver): a rect-list draw whose vertex or index count is not a multiple of 3 draws its complete rectangles and leaves out the 1 or 2 vertices after them, because rect lists are drawn as Vulkan patch lists of 3 control points, which generate [⌊vertexCount/3⌋ patches](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#drawing-patch-lists). What the console draws for the incomplete rectangle is not measured; Palworld (PPSA23126) draws 4 vertices × 9 instances as a rect list when its character creation opens.
|
||||
|
|
|
|||
Loading…
Reference in New Issue