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
10 changes: 8 additions & 2 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -686,8 +686,14 @@ configure_windows_unwind(agc_buffer_pool_tests)
if(BUILD_TESTING)
set_target_properties(agc_buffer_pool_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_buffer_pool COMMAND agc_buffer_pool_tests)
if(WIN32)
set_tests_properties(agc_buffer_pool PROPERTIES ENVIRONMENT_MODIFICATION
add_test(NAME agc_buffer_pool_small_device_shared COMMAND agc_buffer_pool_tests --small-device-retention)
set_tests_properties(agc_buffer_pool_small_device_shared PROPERTIES ENVIRONMENT
"APS5_STAGING_POOL_MIB=256;APS5_BUFFER_POOL_SHARED=1")
add_test(NAME agc_buffer_pool_small_device_no_staging COMMAND agc_buffer_pool_tests --small-device-retention)
set_tests_properties(agc_buffer_pool_small_device_no_staging PROPERTIES ENVIRONMENT
"APS5_STAGING_POOL_MIB=0")
if(WIN32)
set_tests_properties(agc_buffer_pool agc_buffer_pool_small_device_shared agc_buffer_pool_small_device_no_staging PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
Expand Down
21 changes: 14 additions & 7 deletions core/libs/prx/libSceAgcDriver/Graphics/Metal/MetalDraw.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,7 @@ MetalShaderResources resources(backend, ranges,
DynamicRenderState lastDynamicState{};
std::vector<MetalBufferBinding> lastVertexBuffers;
std::vector<id<MTLResource>> residentResources;
std::vector<std::byte> lastPushConstants;

for (std::size_t d = start; d < end; ++d) {
const auto& item = draws[d];
Expand Down Expand Up @@ -2090,14 +2091,20 @@ MetalShaderResources resources(backend, ranges,
}

lastRecordId = record->recordId;
lastPushConstants.assign(item.pushConstants.begin(), item.pushConstants.end());
} else {
if (record->vertexPushBuffer && record->vertexPushBytes > 0) {
auto span = std::span(item.pushConstants).first(std::min(record->vertexPushBytes, item.pushConstants.size()));
[encoder setVertexBytes:span.data() length:span.size() atIndex:*record->vertexPushBuffer];
}
if (record->fragmentPushBuffer && record->fragmentPushBytes > 0) {
auto span = std::span(item.pushConstants).first(std::min(record->fragmentPushBytes, item.pushConstants.size()));
[encoder setFragmentBytes:span.data() length:span.size() atIndex:*record->fragmentPushBuffer];
const bool pushChanged = (lastPushConstants.size() != item.pushConstants.size() ||
std::memcmp(lastPushConstants.data(), item.pushConstants.data(), item.pushConstants.size()) != 0);
if (pushChanged) {
if (record->vertexPushBuffer && record->vertexPushBytes > 0) {
auto span = std::span(item.pushConstants).first(std::min(record->vertexPushBytes, item.pushConstants.size()));
[encoder setVertexBytes:span.data() length:span.size() atIndex:*record->vertexPushBuffer];
}
if (record->fragmentPushBuffer && record->fragmentPushBytes > 0) {
auto span = std::span(item.pushConstants).first(std::min(record->fragmentPushBytes, item.pushConstants.size()));
[encoder setFragmentBytes:span.data() length:span.size() atIndex:*record->fragmentPushBuffer];
}
lastPushConstants.assign(item.pushConstants.begin(), item.pushConstants.end());
}
if (!(lastDynamicState == item.dynamicState)) {
BindRenderState(encoder, item.state, record->depthStencilState);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class MetalPresentation {
std::unique_ptr<MetalFXUpscaler> fxUpscaler;
std::mutex mutex;
std::map<void*, CAMetalLayer*> layers;
id<MTLTexture> scanoutTexture{nil};
std::uint32_t scanoutWidth{0};
std::uint32_t scanoutHeight{0};
MTLPixelFormat scanoutFormat{MTLPixelFormatInvalid};
};

}
28 changes: 22 additions & 6 deletions core/libs/prx/libSceAgcDriver/Graphics/Metal/MetalPresentation.mm
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
: backend(backend), presenter(backend.Device(), library),
fxUpscaler(std::make_unique<MetalFXUpscaler>(backend.Device())) {}

MetalPresentation::~MetalPresentation() = default;
MetalPresentation::~MetalPresentation() {
scanoutTexture = nil;
}

std::vector<std::byte> MetalPresentation::ReadPixels(const DisplayBuffer& buffer) { return displayPixels(buffer); }

Expand All @@ -71,6 +73,10 @@
if (const auto previous = layers.find(window.context); previous != layers.end() && previous->second != layer) {
backend.Wait(backend.CommandBuffer());
if (fxUpscaler) fxUpscaler->Reset();
scanoutTexture = nil;
scanoutWidth = 0;
scanoutHeight = 0;
scanoutFormat = MTLPixelFormatInvalid;
}
layers[window.context] = layer;
std::uint32_t width = 0;
Expand Down Expand Up @@ -108,11 +114,17 @@
if (width != 0 && height != 0 && !unread) {
if (buffer != nullptr) {
const auto pixels = decoded != nullptr ? *decoded : displayPixels(*buffer);
auto descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:layer.pixelFormat width:buffer->width height:buffer->height mipmapped:NO];
descriptor.storageMode = MTLStorageModeShared;
descriptor.usage = MTLTextureUsageShaderRead;
auto source = [backend.Device() newTextureWithDescriptor:descriptor];
if (source == nil) throw std::runtime_error("Metal cannot allocate the decoded scanout texture");
if (scanoutTexture == nil || scanoutWidth != buffer->width || scanoutHeight != buffer->height || scanoutFormat != layer.pixelFormat) {
auto descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:layer.pixelFormat width:buffer->width height:buffer->height mipmapped:NO];
descriptor.storageMode = MTLStorageModeShared;
descriptor.usage = MTLTextureUsageShaderRead;
scanoutTexture = [backend.Device() newTextureWithDescriptor:descriptor];
if (scanoutTexture == nil) throw std::runtime_error("Metal cannot allocate the decoded scanout texture");
scanoutWidth = buffer->width;
scanoutHeight = buffer->height;
scanoutFormat = layer.pixelFormat;
}
auto source = scanoutTexture;
[source replaceRegion:MTLRegionMake2D(0, 0, buffer->width, buffer->height) mipmapLevel:0 withBytes:pixels.data() bytesPerRow:buffer->width * 4u];
if (layer.device != backend.Device()) throw std::invalid_argument("Metal presentation requires a layer on its device");
id<CAMetalDrawable> drawable = [layer nextDrawable];
Expand Down Expand Up @@ -155,6 +167,10 @@
backend.Wait(backend.CommandBuffer());
layers.erase(found);
if (fxUpscaler) fxUpscaler->Reset();
scanoutTexture = nil;
scanoutWidth = 0;
scanoutHeight = 0;
scanoutFormat = MTLPixelFormatInvalid;
}
}

Expand Down
48 changes: 36 additions & 12 deletions core/libs/prx/libSceAgcDriver/Graphics/Metal/MetalShaderPipeline.mm
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,32 @@ PreparedBindings prepare(id<MTLDevice> device, const ShaderResult& shader,
std::span<const id<MTLResource>> indirectResources) {
ValidatePixelSamplerBindings(shader.guest.bindings);
PreparedBindings result;
std::map<std::pair<std::uint32_t, std::uint32_t>, const MetalShaderResourceBinding*> bindings;
for (const auto& binding : supplied) {
if (!bindings.emplace(std::pair(binding.descriptorSet, binding.binding), &binding).second) {
throw std::invalid_argument("Metal shader descriptor binding was supplied more than once");
result.buffers.reserve(shader.resources.size() + (shader.samplerArgumentBuffer ? 1 : 0));
result.textures.reserve(shader.resources.size());
result.samplers.reserve(shader.resources.size());
struct BindingEntry {
std::uint32_t descriptorSet;
std::uint32_t binding;
const MetalShaderResourceBinding* resource;
bool consumed{false};
};
std::array<BindingEntry, 64> inlineEntries;
std::vector<BindingEntry> heapEntries;
std::span<BindingEntry> entries;
if (supplied.size() <= inlineEntries.size()) {
entries = std::span(inlineEntries.data(), supplied.size());
} else {
heapEntries.resize(supplied.size());
entries = std::span(heapEntries.data(), heapEntries.size());
}
for (std::size_t i = 0; i < supplied.size(); ++i) {
const auto& item = supplied[i];
for (std::size_t j = 0; j < i; ++j) {
if (entries[j].descriptorSet == item.descriptorSet && entries[j].binding == item.binding) {
throw std::invalid_argument("Metal shader descriptor binding was supplied more than once");
}
}
entries[i] = {item.descriptorSet, item.binding, &item, false};
}
SlotMask<32> bufferSlots;
SlotMask<128> textureSlots;
Expand Down Expand Up @@ -326,10 +347,11 @@ PreparedBindings prepare(id<MTLDevice> device, const ShaderResult& shader,
reserveSlots(bufferSlots, mapping.index, 1, 31);
}
for (const auto& mapping : shader.resources) {
const auto key = std::pair(mapping.descriptorSet, mapping.binding);
const auto found = bindings.find(key);
auto found = std::find_if(entries.begin(), entries.end(), [&](const auto& entry) {
return !entry.consumed && entry.descriptorSet == mapping.descriptorSet && entry.binding == mapping.binding;
});
if (!mapping.active) {
if (found != bindings.end()) bindings.erase(found);
if (found != entries.end()) found->consumed = true;
continue;
}
validateKind(mapping);
Expand All @@ -340,10 +362,10 @@ PreparedBindings prepare(id<MTLDevice> device, const ShaderResult& shader,
if (descriptor == shader.guest.bindings.end() || descriptor->kind != mapping.kind || descriptor->count != mapping.count) {
throw std::invalid_argument("Metal shader resource reflection disagrees with guest descriptor metadata");
}
if (found == bindings.end()) {
if (found == entries.end()) {
throw std::invalid_argument("Metal shader has an unresolved active descriptor binding");
}
const auto& binding = *found->second;
const auto& binding = *found->resource;
const std::size_t buffers = mapping.buffer ? mapping.count : 0;
const std::size_t textures = mapping.texture ? mapping.count : 0;
const std::size_t samplers = mapping.sampler ? mapping.count : 0;
Expand Down Expand Up @@ -422,10 +444,12 @@ PreparedBindings prepare(id<MTLDevice> device, const ShaderResult& shader,
result.samplers.push_back({*mapping.sampler + i, sampler});
}
}
bindings.erase(found);
found->consumed = true;
}
if (!bindings.empty()) {
throw std::invalid_argument("Metal shader received a descriptor binding absent from reflection");
for (const auto& entry : entries) {
if (!entry.consumed) {
throw std::invalid_argument("Metal shader received a descriptor binding absent from reflection");
}
}
result.pushConstants = pushConstants.empty() ? std::span<const std::byte>(shader.pushConstantData) : pushConstants;
if (result.pushConstants.size() > 4096 ||
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ VkBufferUsageFlags BufferPool::Usage(VkBufferUsageFlags usage, VkMemoryPropertyF
}

BufferPool::Tier& BufferPool::tierFor(std::size_t capacity, VkMemoryPropertyFlags properties) {
if (DeviceTiered(properties)) return deviceTier;
if (DeviceTiered(properties) && (sharedTiers() || capacity >= classLimit)) return deviceTier;
return !sharedTiers() && capacity < classLimit ? smallTier : largeTier;
}

Expand Down
33 changes: 32 additions & 1 deletion core/libs/prx/libSceAgcDriver/tests/BufferPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "prx/libSceAgcDriver/Graphics/include/Resources.hpp"
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <map>
Expand Down Expand Up @@ -260,6 +261,31 @@ void SmallDeviceClasses() {
Expect(nextArgs.Handle() == tiny && mock.allocations == made, "a 20-byte device request did not reuse the retained 256-byte buffer of its class");
}

void SmallDeviceRetention() {
mock = MockDevice{};
{
auto context = mockContext();
constexpr auto usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
const auto deviceBudget = BufferPool::DeviceBudget(context.memory);
const auto bytes = static_cast<std::size_t>((deviceBudget != 0 ? deviceBudget : 512 * MiB) / 2) + MiB;
VkDeviceMemory small = VK_NULL_HANDLE;
{
DeviceBuffer buffer(context, 256, usage);
small = MemoryOf(buffer.Handle());
}
std::vector<std::unique_ptr<DeviceBuffer>> large;
for (int i = 0; i < 2; ++i) large.push_back(std::make_unique<DeviceBuffer>(context, bytes, usage));
large.clear();
Expect(mock.frees != 0, "large device buffers did not exercise pool budget eviction");
const auto made = mock.allocations;
const bool isolated = std::getenv("APS5_BUFFER_POOL_SHARED") == nullptr;
DeviceBuffer again(context, 256, usage);
Expect((MemoryOf(again.Handle()) == small) == isolated, "small device buffer retention did not follow the separate or shared pool budget");
Expect(mock.allocations == made + (isolated ? 0 : 1), "large device buffer churn changed small buffer allocation reuse");
}
Expect(mock.liveBytes == 0 && mock.liveBuffers.empty(), "device pool tier retention leaked Vulkan objects");
}

void Slabs() {
Expect(BufferPool::SlabEligible(512, 256, 512, 64), "a size class with a smaller alignment and atom was refused a slab");
Expect(!BufferPool::SlabEligible(512, 1024, 512, 64), "a size class below its alignment was given a slab");
Expand Down Expand Up @@ -321,14 +347,19 @@ void Slabs() {

}

int main() {
int main(int argc, char** argv) {
try {
if (argc == 2 && std::string_view(argv[1]) == "--small-device-retention") {
SmallDeviceRetention();
return failures == 0 ? 0 : 1;
}
SizeClassesAndDirections();
LargerClass();
KeptUntilFence();
Budget();
AddressAndHostUnchanged();
SmallDeviceClasses();
SmallDeviceRetention();
Slabs();
} catch (const std::exception& error) {
std::fprintf(stderr, "FAIL: %s\n", error.what());
Expand Down
Loading