Skip to content
Closed
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
16 changes: 16 additions & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,22 @@ foreach(agcTriangleStage IN ITEMS vert frag)
list(APPEND agcTriangleHeaders "${agcTriangleHeader}")
endforeach()

add_test_executable(agc_driver_bounded_index_fetch_tests EXCLUDE_FROM_ALL
tests/BoundedIndexFetch.cpp ${agcTriangleHeaders})
target_include_directories(agc_driver_bounded_index_fetch_tests PRIVATE
${LIBS_INCLUDE_DIR} ${agcTestShaderGeneratedRoot}
${CMAKE_SOURCE_DIR}/core/shader/recompiler ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_driver_bounded_index_fetch_tests PRIVATE libSceAgcDriver SDL2-static libc)
set_target_properties(agc_driver_bounded_index_fetch_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_bounded_index_fetch_tests)
if(BUILD_TESTING)
set_target_properties(agc_driver_bounded_index_fetch_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_driver_bounded_index_fetch COMMAND agc_driver_bounded_index_fetch_tests)
set_tests_properties(agc_driver_bounded_index_fetch PROPERTIES
SKIP_RETURN_CODE 77 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 60)
endif()

foreach(agcCoverStage IN ITEMS vert frag)
string(TOUPPER "${agcCoverStage}" agcCoverSymbol)
set(agcCoverShaderSpv ${agcTestShaderGeneratedRoot}/Cover_${agcCoverStage}.spv)
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceAgcDriver/Execution/include/Pm4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct DrawParameters {
std::uint32_t InstanceDwordOffset() const { return recordBytes == 20 ? 16u : 12u; }
};
std::optional<IndirectDraw> indirect;
std::optional<std::uint32_t> readableIndexCount;
std::uint32_t IndexReadCount() const { return readableIndexCount.value_or(indexCount); }
};

// One indirect draw record as the CP reads it (firstVertexOrIndex is startVertex for the
Expand Down
17 changes: 9 additions & 8 deletions core/libs/prx/libSceAgcDriver/Execution/src/Pm4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@ void Validate(std::span<const std::uint32_t> packet, std::uint32_t queue) {
graphics();
size(6);
require(packet[3] <= 0xffffu, "unsupported index address bits");
require(packet[4] <= packet[1], "index count exceeds maximum index size");
require((packet[5] & ~0x20u) == 0, "unsupported indexed draw flags");
break;
case 0x2a: graphics(); size(2); require(packet[1] <= 3, "unsupported index-type modifiers"); break;
Expand All @@ -309,7 +308,6 @@ void Validate(std::span<const std::uint32_t> packet, std::uint32_t queue) {
case 0x35:
graphics();
size(5);
require(packet[3] <= packet[1], "index count exceeds maximum index size");
require((packet[4] & ~0x20u) == 0, "unsupported indexed draw flags");
break;
case 0x24: case 0x25:
Expand Down Expand Up @@ -782,17 +780,20 @@ DrawParameters ResolveDraw(std::span<const std::uint32_t> packet, const QueueSta
const std::uint32_t indexSize = queue.indexType == 0 ? 2 : queue.indexType == 1 ? 4 : 1;
const auto base = explicitAddress ? address(packet[2], packet[3]) : queue.indexBase;
const auto count = explicitAddress ? packet[4] : packet[3];
require(base != 0 && base % indexSize == 0, "null or misaligned index base");
const auto readableCount = std::min(count, packet[1]);
require(readableCount == 0 || (base != 0 && base % indexSize == 0), "null or misaligned index base");
const auto offset = explicitAddress ? 0 : static_cast<std::uint64_t>(packet[2]) * indexSize;
require(offset <= std::numeric_limits<std::uint64_t>::max() - base, "index address overflow");
const auto address = base + offset;
const auto bytes = static_cast<std::uint64_t>(count) * indexSize;
require(readableCount == 0 || offset <= std::numeric_limits<std::uint64_t>::max() - base, "index address overflow");
const auto address = readableCount == 0 ? 0 : base + offset;
const auto bytes = static_cast<std::uint64_t>(readableCount) * indexSize;
require(bytes <= std::numeric_limits<std::size_t>::max(), "index range size overflow");
GuestMemory::CheckRange(reinterpret_cast<const void*>(address), static_cast<std::size_t>(bytes), indexSize);
if (bytes != 0) GuestMemory::CheckRange(reinterpret_cast<const void*>(address), static_cast<std::size_t>(bytes), indexSize);
APS5_LOG_OUT_DEBUG("ResolveDraw context targetMask=0x%x shaderMask=0x%x indexCount=%u indexType=%u instances=%u", queue.context.contains(0x8e) ? queue.context.at(0x8e) : 0u, queue.context.contains(0x8f) ? queue.context.at(0x8f) : 0u, count, queue.indexType, queue.instanceCount);
const auto indexOffset = queue.userConfig.find(0x24a);
require(indexOffset != queue.userConfig.end(), "missing GE_INDX_OFFSET register");
return {address, count, indexSize, queue.instanceCount, packet.back(), true, indexOffset->second, 0};
DrawParameters draw{address, count, indexSize, queue.instanceCount, packet.back(), true, indexOffset->second, 0};
draw.readableIndexCount = readableCount;
return draw;
}

std::vector<std::uint32_t> ReadIndirectRegisters(std::span<const std::uint32_t> packet) {
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/Draw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct DrawInputCopy {
std::uint64_t registryGeneration = 0;
};
DrawInputCopy CopyDrawInput(const Context& context, Recorder* recorder, std::uint64_t address, std::size_t bytes, std::size_t alignment, Recorder::SnapshotUse use);
DrawInputCopy CopyBoundedIndexInput(const Context& context, const Pm4::DrawParameters& draw);
DrawInputCopy CopyZeroPaddedDrawInput(const Context& context, std::uint64_t address, std::size_t bytes, std::size_t validBytes);
void KeepDrawInput(Recorder* recorder, std::uint64_t address, const DrawInputCopy& copy, Recorder::SnapshotUse use, std::uint32_t derived);
const HostImport* InPlaceDrawInput(const Context& context, std::uint64_t address, std::size_t bytes, std::size_t alignment);
Expand Down
42 changes: 31 additions & 11 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ void CheckBufferAliases(std::span<const CompiledShader> shaders, const ColorTarg
const auto element = offset / 4;
const bool written = element >= binding.bufferWritten.size() || binding.bufferWritten[element];
Require(!overlap(address, size, target.address, target.bytes), "shader buffer aliases the render target");
Require(!written || !overlap(address, size, indexAddress, indexBytes), "writable shader buffer aliases the index buffer");
Require(!written || indexBytes == 0 || !overlap(address, size, indexAddress, indexBytes), "writable shader buffer aliases the index buffer");
}
}
}
Expand All @@ -824,6 +824,7 @@ void CheckBufferAliases(std::span<const CompiledShader> shaders, const ColorTarg
std::array<std::uint32_t, 4> MeshIndexBufferDescriptor(const Pm4::DrawParameters& draw) {
constexpr std::uint32_t RawWord3 = 0x31016facu;
if (!draw.indexed) return {0u, 0u, 0u, RawWord3};
Require(draw.IndexReadCount() == draw.indexCount, "bounded index fetches on a mesh path are unsupported");
const auto address = draw.indexAddress;
const auto bytes = (static_cast<std::uint64_t>(draw.indexCount) * draw.indexSize + 3u) & ~std::uint64_t{3};
Require(address != 0 && bytes != 0 && bytes <= 0xffffffffu && (address >> 48u) == 0, "invalid mesh index buffer range");
Expand Down Expand Up @@ -956,6 +957,20 @@ DrawInputCopy CopyZeroPaddedDrawInput(const Context& context, std::uint64_t addr
return copy;
}

DrawInputCopy CopyBoundedIndexInput(const Context& context, const Pm4::DrawParameters& draw) {
Require(draw.indexed && (draw.indexSize == 2 || draw.indexSize == 4), "unsupported bounded index type");
Require(draw.IndexReadCount() <= draw.indexCount, "readable index count exceeds the draw count");
const auto bytes = static_cast<std::uint64_t>(draw.indexCount) * draw.indexSize;
const auto validBytes = static_cast<std::uint64_t>(draw.IndexReadCount()) * draw.indexSize;
Require(bytes <= std::numeric_limits<std::size_t>::max(), "index buffer size overflow");
DrawInputCopy copy;
copy.buffer = std::make_shared<Buffer>(context, static_cast<std::size_t>(bytes), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
const auto destination = copy.buffer->Bytes();
GuestMemory::Read(draw.indexAddress, destination.first(static_cast<std::size_t>(validBytes)), draw.indexSize);
std::fill(destination.begin() + static_cast<std::ptrdiff_t>(validBytes), destination.end(), std::byte{0});
return copy;
}

void KeepDrawInput(Recorder* recorder, std::uint64_t address, const DrawInputCopy& copy, Recorder::SnapshotUse use, std::uint32_t derived) {
if (recorder == nullptr || copy.reused || copy.generation == 0 || copy.buffer == nullptr) return;
recorder->KeepDrawSnapshot(address, copy.buffer->Bytes().size(), copy.generation, copy.registryGeneration, copy.buffer, use, derived);
Expand Down Expand Up @@ -1054,13 +1069,16 @@ DrawInputs prepareDrawInputs(const Context& context, const State& state, const P
}
if (args == nullptr) Require(draw.indexCount != 0 && draw.instanceCount != 0, "zero-count indexed draws are unsupported");
else Require((!state.stages.mesh || draw.indexed) && !state.stages.tessellation && !state.rectList, "indirect draw on a non-vertex path must be resolved by the driver");
inputs.indexBytes = static_cast<std::uint64_t>(draw.indexCount) * draw.indexSize;
Require(draw.IndexReadCount() <= draw.indexCount, "readable index count exceeds the draw count");
inputs.indexBytes = static_cast<std::uint64_t>(draw.IndexReadCount()) * draw.indexSize;
const auto indexBytes = inputs.indexBytes;
const auto drawIndexBytes = static_cast<std::uint64_t>(draw.indexCount) * draw.indexSize;
const bool paddedIndices = draw.indexed && draw.IndexReadCount() < draw.indexCount;
APS5_LOG_OUT_DEBUG("Index buffer bytes=%llu", static_cast<unsigned long long>(indexBytes));
Require(indexBytes <= std::numeric_limits<std::size_t>::max(), "index buffer size overflow");
if (draw.indexed) GuestMemory::CheckRange(reinterpret_cast<const void*>(draw.indexAddress), static_cast<std::size_t>(indexBytes), draw.indexSize);
if (draw.indexed && indexBytes != 0) GuestMemory::CheckRange(reinterpret_cast<const void*>(draw.indexAddress), static_cast<std::size_t>(indexBytes), draw.indexSize);
APS5_LOG_CHARS_OUT_DEBUG("Index buffer range OK");
Require(!draw.indexed || !state.hasColorTarget || draw.indexAddress + indexBytes <= state.color.address || state.color.address + state.color.bytes <= draw.indexAddress, "index buffer aliases the render target");
Require(!draw.indexed || indexBytes == 0 || !state.hasColorTarget || draw.indexAddress + indexBytes <= state.color.address || state.color.address + state.color.bytes <= draw.indexAddress, "index buffer aliases the render target");
APS5_LOG_CHARS_OUT_DEBUG("ValidateShaders");
if (recipe != nullptr) {
inputs.fragmentOutputs = recipe->fragmentOutputs;
Expand Down Expand Up @@ -1096,16 +1114,17 @@ DrawInputs prepareDrawInputs(const Context& context, const State& state, const P
const bool fanGeometry = state.stages.mesh && state.stages.mesh->inputPrimitive == 5;
const bool skipRestart = state.primitiveRestart && (!listTopology || context.primitiveListRestart) && !fanGeometry;
const auto use = draw.indexSize == 2 ? (skipRestart ? Recorder::SnapshotUse::Index16Restart : Recorder::SnapshotUse::Index16) : (skipRestart ? Recorder::SnapshotUse::Index32Restart : Recorder::SnapshotUse::Index32);
const auto* import = InPlaceDrawInput(context, draw.indexAddress, static_cast<std::size_t>(indexBytes), draw.indexSize);
const auto* import = paddedIndices ? nullptr : InPlaceDrawInput(context, draw.indexAddress, static_cast<std::size_t>(indexBytes), draw.indexSize);
DrawInputCopy copy;
if (import == nullptr) copy = CopyDrawInput(context, context.recorder, draw.indexAddress, static_cast<std::size_t>(indexBytes), draw.indexSize, use);
if (paddedIndices) copy = CopyBoundedIndexInput(context, draw);
else if (import == nullptr) copy = CopyDrawInput(context, context.recorder, draw.indexAddress, static_cast<std::size_t>(indexBytes), draw.indexSize, use);
else GuestMemory::CheckRange(reinterpret_cast<const void*>(draw.indexAddress), static_cast<std::size_t>(indexBytes), draw.indexSize);
std::optional<std::uint32_t> highest;
if (import != nullptr) {
highest = InPlaceIndexRange(*context.recorder, draw.indexAddress, static_cast<std::size_t>(indexBytes), draw.indexSize, skipRestart).highest;
} else if (!copy.reused) {
highest = HighestDrawIndex(copy.buffer->Bytes().first(static_cast<std::size_t>(indexBytes)), draw.indexSize, skipRestart);
KeepDrawInput(context.recorder, draw.indexAddress, copy, use, skipRestart ? (highest ? *highest + 1u : 0u) : highest.value_or(0u));
highest = HighestDrawIndex(copy.buffer->Bytes().first(static_cast<std::size_t>(drawIndexBytes)), draw.indexSize, skipRestart);
if (!paddedIndices) KeepDrawInput(context.recorder, draw.indexAddress, copy, use, skipRestart ? (highest ? *highest + 1u : 0u) : highest.value_or(0u));
} else if (!skipRestart) {
highest = copy.derived;
} else if (copy.derived != 0) {
Expand All @@ -1120,9 +1139,10 @@ DrawInputs prepareDrawInputs(const Context& context, const State& state, const P
const auto limit = *zeroFrom > draw.firstVertex ? *zeroFrom - draw.firstVertex : 0u;
if (*highest >= limit && *highest - limit >= ZeroFetchSlack) {
GuestMemory::FlushGpuWrites(draw.indexAddress, static_cast<std::size_t>(indexBytes));
auto clamped = std::make_shared<Buffer>(context, static_cast<std::size_t>(indexBytes), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
const auto bytes = clamped->Bytes().first(static_cast<std::size_t>(indexBytes));
GuestMemory::Read(draw.indexAddress, bytes, draw.indexSize);
auto clamped = std::make_shared<Buffer>(context, static_cast<std::size_t>(drawIndexBytes), VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
const auto bytes = clamped->Bytes().first(static_cast<std::size_t>(drawIndexBytes));
if (paddedIndices) std::copy_n(copy.buffer->Bytes().begin(), bytes.size(), bytes.begin());
else GuestMemory::Read(draw.indexAddress, bytes, draw.indexSize);
highest = ClampDrawIndices(bytes, draw.indexSize, skipRestart, limit);
import = nullptr;
copy = DrawInputCopy{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2868,7 +2868,7 @@ std::size_t ShaderResources::addGuestBuffer(std::span<const std::uint32_t> words
Require(target == nullptr || !overlap(address, size, target->address, target->bytes), "shader buffer aliases the render target");
// APS5_ALL_BUFFERS_WRITTEN=1: every element is noted as written, as before bufferWritten existed.
static const bool allWritten = std::getenv("APS5_ALL_BUFFERS_WRITTEN") != nullptr;
Require(!written || !overlap(address, size, indexAddress, indexBytes), "writable shader buffer aliases the index buffer");
Require(!written || indexBytes == 0 || !overlap(address, size, indexAddress, indexBytes), "writable shader buffer aliases the index buffer");
written = written || allWritten;
const bool swept = read && dispatchThreads != 0 && descriptor.Stride() != 0 && dispatchThreads * descriptor.Stride() * 2u >= byteSize;
if (written) guestMemory.AddWritable(address, size, atomic, swept);
Expand Down
Loading
Loading