Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/TextureDetiler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Texture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/PassHazards.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/BarrierBatch.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/UnitShadow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Sampler.cpp
${agcTextureDetileShaderHeader}
Expand Down Expand Up @@ -909,6 +910,12 @@ target_link_libraries(agc_pass_hazards_tests PRIVATE libc)
set_target_properties(agc_pass_hazards_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_pass_hazards_tests)

add_test_executable(agc_barrier_batch_tests EXCLUDE_FROM_ALL tests/BarrierBatch.cpp Graphics/src/BarrierBatch.cpp)
target_include_directories(agc_barrier_batch_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_barrier_batch_tests PRIVATE libc)
set_target_properties(agc_barrier_batch_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_barrier_batch_tests)

add_test_executable(agc_dcc_key_sharing_tests EXCLUDE_FROM_ALL tests/DccKeySharing.cpp)
target_include_directories(agc_dcc_key_sharing_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/core/shader/recompiler ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_dcc_key_sharing_tests PRIVATE libc)
Expand Down Expand Up @@ -1893,7 +1900,7 @@ if(BUILD_TESTING)
if(WIN32)
set_tests_properties(agc_shader_bindless agc_shader_selected_records_image_limit agc_shader_selected_records_pair_limit agc_shader_selected_records_direct_image_limit agc_shader_selected_records_direct_pair_limit agc_shader_srt_read_patching agc_shader_type_zero_image_materialization agc_shader_frontend_pair agc_shader_frontend_pair_off agc_shader_layout_pair agc_shader_layout_pair_off agc_shader_entry_lane_proof_budget PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
foreach(agcTest IN ITEMS agc_vertex_input_tests agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_ir_reclaim_tests agc_vector_window_tests agc_constant_bit_test_tests agc_integer_narrowing_tests agc_scalar_select_mask_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_pass_hazards_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_placeholder_images_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests agc_formatted_buffer_once_tests agc_formatted_buffer_byte_stores_tests ${agcExecutionTests} agc_fma_mix_once_tests agc_f16_single_convert_tests)
foreach(agcTest IN ITEMS agc_vertex_input_tests agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_ir_reclaim_tests agc_vector_window_tests agc_constant_bit_test_tests agc_integer_narrowing_tests agc_scalar_select_mask_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_pass_hazards_tests agc_barrier_batch_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_placeholder_images_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests agc_formatted_buffer_once_tests agc_formatted_buffer_byte_stores_tests ${agcExecutionTests} agc_fma_mix_once_tests agc_f16_single_convert_tests)
if(agcTest STREQUAL "agc_driver_constant_store_tests" OR agcTest STREQUAL "agc_driver_flat_apertures_tests" OR agcTest STREQUAL "agc_driver_pipeline_optimized_readback_tests")
continue()
endif()
Expand Down
25 changes: 25 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/BarrierBatch.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_BARRIERBATCH_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_BARRIERBATCH_HPP

#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>

namespace AgcDriver::Graphics {

struct BarrierMask {
VkPipelineStageFlags srcStage = 0;
VkPipelineStageFlags dstStage = 0;
VkAccessFlags srcAccess = 0;
VkAccessFlags dstAccess = 0;
bool changesLayout = false;
bool transfersQueue = false;
};

bool Covers(const BarrierMask& outer, const BarrierMask& inner);
bool OrderedByTrailing(VkAccessFlags covered, const BarrierMask& wanted);

}

#endif
3 changes: 3 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_RECORDER_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_GRAPHICS_INCLUDE_RECORDER_HPP

#include "prx/libSceAgcDriver/Graphics/include/BarrierBatch.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Context.hpp"
#include "prx/libSceAgcDriver/Graphics/include/PassHazards.hpp"
#include <unordered_map>
Expand Down Expand Up @@ -481,6 +482,8 @@ class Recorder {
// (or APS5_NO_BARRIER_ELISION=1) records every leading barrier, as before.
static bool MergeBarriers();
static void CountMerged(CommandClass which);
static bool BarrierBatch();
static bool ElideCovered(CommandClass which, const BarrierMask& wanted, VkAccessFlags covered);
// Hazard tracker, counting mode (APS5_BARRIER_VALIDATE=1): every command tells the tracker
// what it reads and writes (guest ranges through host imports, images) with its stage before
// it is recorded; the tracker simulates barriers emitted only on a hazard against the accesses
Expand Down
27 changes: 27 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/BarrierBatch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "prx/libSceAgcDriver/Graphics/include/BarrierBatch.hpp"

namespace AgcDriver::Graphics {

namespace {

bool StageCovers(VkPipelineStageFlags outer, VkPipelineStageFlags inner) {
auto rest = inner & ~outer;
if ((outer & VK_PIPELINE_STAGE_ALL_COMMANDS_BIT) != 0) rest &= VK_PIPELINE_STAGE_HOST_BIT;
return rest == 0;
}

}

bool Covers(const BarrierMask& outer, const BarrierMask& inner) {
if (inner.changesLayout || inner.transfersQueue) return false;
return StageCovers(outer.srcStage, inner.srcStage) && StageCovers(outer.dstStage, inner.dstStage) && (inner.srcAccess & ~outer.srcAccess) == 0 && (inner.dstAccess & ~outer.dstAccess) == 0;
}

bool OrderedByTrailing(VkAccessFlags covered, const BarrierMask& wanted) {
if (wanted.changesLayout || wanted.transfersQueue || (wanted.dstStage & VK_PIPELINE_STAGE_HOST_BIT) != 0) return false;
constexpr VkAccessFlags shaderAndTransfer = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
const VkAccessFlags needed = shaderAndTransfer | wanted.dstAccess;
return (covered & needed) == needed;
}

}
20 changes: 14 additions & 6 deletions core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4357,15 +4357,19 @@ void GuestBufferMemory::recordGpuCopies(std::span<Region* const> copies, bool ad
recorder->FlushKeyStoresOverlapping(region->begin, static_cast<std::size_t>(region->end - region->begin));
recorder->FlushStoresOverlapping(region->begin, static_cast<std::size_t>(region->end - region->begin));
}
const auto commands = recorder->Commands();
VkAccessFlags covered = 0;
const auto commands = recorder->Commands(&covered);
const auto timing = recorder->BeginGpuTiming(StagingCopyInKey);
if (Recorder::BarrierValidate()) {
std::vector<std::pair<std::uint64_t, std::uint64_t>> reads;
for (const auto* region : copies) reads.emplace_back(region->begin, region->end);
recorder->NoteAccess(Recorder::CommandClass::StagingIn, Recorder::Access{reads, {}, {}, VK_PIPELINE_STAGE_TRANSFER_BIT});
}
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
Recorder::CountBarriers(Recorder::CommandClass::StagingIn);
constexpr BarrierMask leading{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT};
if (!Recorder::ElideCovered(Recorder::CommandClass::StagingIn, leading, covered)) {
RecordMemoryBarrier(context, commands, leading.srcStage, leading.dstStage, leading.srcAccess, leading.dstAccess);
Recorder::CountBarriers(Recorder::CommandClass::StagingIn);
}
bool stagedAny = false;
std::uint64_t copiedBytes = 0;
for (auto* region : copies) {
Expand Down Expand Up @@ -4476,11 +4480,15 @@ void GuestBufferMemory::RecordCopyBacks(Recorder& recorder) {
const auto to = std::min(end, region.end);
if (from >= to) continue;
if (!recording) {
commands = recorder.Commands();
VkAccessFlags covered = 0;
commands = recorder.Commands(&covered);
timing = recorder.BeginGpuTiming(StagingCopyBackKey);
// The shader's stores into the buffer (whichever stage made them) precede the copy.
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
Recorder::CountBarriers(Recorder::CommandClass::StagingOut);
constexpr BarrierMask leading{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT};
if (!Recorder::ElideCovered(Recorder::CommandClass::StagingOut, leading, covered)) {
RecordMemoryBarrier(context, commands, leading.srcStage, leading.dstStage, leading.srcAccess, leading.dstAccess);
Recorder::CountBarriers(Recorder::CommandClass::StagingOut);
}
recording = true;
}
// Exactly the sub-range the shader may write, in place in the import: what the shader
Expand Down
26 changes: 23 additions & 3 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,8 @@ void ReleaseDeferredKeeps() {
// Lock-free state of the active recorder's open batch, read by the queue workers between packets
// and inside WAIT_REG_MEM polls (see the static readers in Recorder.hpp). Written under the mutex.
constexpr std::int64_t NoPendingLabel = std::numeric_limits<std::int64_t>::min();
constexpr BarrierMask PassEndBarrier{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT};
constexpr BarrierMask LabelLeadingBarrier{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT};
std::atomic<std::int64_t> pendingLabelSince{NoPendingLabel};
std::atomic<std::uint64_t> writeGeneration{0};
std::atomic<std::uint64_t> publishGeneration{0};
Expand Down Expand Up @@ -1416,7 +1418,7 @@ void Recorder::endOpenRenderPass(bool barrier) {
// The pass's attachment and shader writes are visible to everything recorded after it (the
// host sees them at the batch's fence).
if (barrier) {
recordBarrier(open->commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
recordBarrier(open->commands, PassEndBarrier.srcStage, PassEndBarrier.dstStage, PassEndBarrier.srcAccess, PassEndBarrier.dstAccess);
CountBarriers(CommandClass::Draw);
}
if (pass.afterPass) pass.afterPass(open->commands);
Expand Down Expand Up @@ -1636,6 +1638,7 @@ bool Recorder::closeStoreRun(bool atSubmit) {
// A label says the work before it is done: the queued key stores (results of that work)
// land ahead of every store of the run, and the pass a draw left open ends.
if (!open->keyStores.empty()) recordKeyStores(true);
const bool passEndLast = open->renderPass.open && !open->renderPass.afterPass;
if (open->renderPass.open) endOpenRenderPass();
const auto commands = open->commands;
run.timing = beginTiming(ClassKey(CommandClass::LabelRun));
Expand All @@ -1646,9 +1649,15 @@ bool Recorder::closeStoreRun(bool atSubmit) {
}
// At Submit every result of the batch reaches the host before the stores (the title's
// pollers read results after the label); in place, later work sees the stores as well.
if (atSubmit) recordBarrier(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_READ_BIT);
else recordBarrier(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
std::uint32_t barriers = 2;
if (atSubmit) {
recordBarrier(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_READ_BIT);
} else if (passEndLast && BarrierBatch() && Covers(PassEndBarrier, LabelLeadingBarrier)) {
CountMerged(CommandClass::LabelRun);
barriers = 1;
} else {
recordBarrier(commands, LabelLeadingBarrier.srcStage, LabelLeadingBarrier.dstStage, LabelLeadingBarrier.srcAccess, LabelLeadingBarrier.dstAccess);
}
std::uint64_t storedBytes = 0;
const auto update = function(cmdUpdateBuffer, "vkCmdUpdateBuffer");
std::vector<QueuedWrite> writes;
Expand Down Expand Up @@ -1832,6 +1841,17 @@ bool Recorder::MergeBarriers() {
return merge;
}

bool Recorder::BarrierBatch() {
static const bool batch = MergeBarriers() && std::getenv("APS5_NO_BARRIER_BATCH") == nullptr;
return batch;
}

bool Recorder::ElideCovered(CommandClass which, const BarrierMask& wanted, VkAccessFlags covered) {
if (!BarrierBatch() || !OrderedByTrailing(covered, wanted)) return false;
CountMerged(which);
return true;
}

void Recorder::CountMerged(CommandClass which) {
if (!DrawOrGpuProfiled()) return;
classMerged[static_cast<std::size_t>(which)].fetch_add(1, std::memory_order_relaxed);
Expand Down
4 changes: 3 additions & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3387,7 +3387,9 @@ std::uint64_t StorageTexture::writeBackWindows(std::span<const ImportSegment> se
toRead.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
toRead.newLayout = VK_IMAGE_LAYOUT_GENERAL;
context.Function<PFN_vkCmdPipelineBarrier>("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &importReady, 0, nullptr, 1, &toRead);
const BarrierMask ready{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, importReady.srcAccessMask | toRead.srcAccessMask, importReady.dstAccessMask | toRead.dstAccessMask, toRead.oldLayout != toRead.newLayout, toRead.srcQueueFamilyIndex != toRead.dstQueueFamilyIndex};
const bool ordered = recorder != nullptr && seeds.empty() && padding.copies.empty() && Recorder::ElideCovered(Recorder::CommandClass::StorageWriteBack, ready, covered);
if (!ordered) context.Function<PFN_vkCmdPipelineBarrier>("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &importReady, 0, nullptr, 1, &toRead);
for (std::size_t i = 0; i < windows.size(); ++i) {
const auto& window = windows[i];
auto retile = window.window;
Expand Down
Loading
Loading