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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace AgcDriver::DriverDetail {

enum IndirectPath { IndirectGpu = 0, IndirectPendingImage = 1, IndirectCopiedWrite = 2, IndirectNotImported = 3, IndirectThreadDimensions = 4, IndirectFillKernel = 5, IndirectMisaligned = 6, IndirectDisabled = 7, IndirectPaths = 8 };
enum IndirectPath { IndirectGpu = 0, IndirectPendingImage = 1, IndirectCopiedWrite = 2, IndirectNotImported = 3, IndirectThreadDimensions = 4, IndirectFillKernel = 5, IndirectMisaligned = 6, IndirectDisabled = 7, IndirectWorkgroupMemory = 8, IndirectPaths = 9 };

}

Expand Down
10 changes: 0 additions & 10 deletions core/libs/prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,16 +225,6 @@ class VulkanDevice {
// variant (design_cpu_final M4): only when the resource cache served or took the object
// (reusable, cacheable) and recipes are on (APS5_NO_DISPATCH_RECIPE=1 builds none); else null.
void Dispatch(const ShaderRecompiler::RecompileResult& shader, std::uint32_t x, std::uint32_t y, std::uint32_t z, std::span<const Graphics::GuestMemorySnapshot> snapshots = {}, std::uint64_t programAddress = 0, std::shared_ptr<PreparedDispatch> prepared = nullptr, std::shared_ptr<const Recipe>* recipe = nullptr);
// A dispatch whose group counts are the three dwords at `arguments` in guest memory
// (DISPATCH_INDIRECT): the GPU reads them in place from the host import, ordered after everything
// recorded before, so the CPU never waits for the shader that wrote them. When the GPU could not
// see the current bytes the counts are read on the CPU instead (through the flush hook, as the
// driver resolved every indirect dispatch before) and the dispatch is recorded as a direct one:
// cpuReason 1 storage-image results were pending over them, 2 a recorded dispatch writes them
// through a copied buffer (its CPU write-back lands only when the batch is reaped) or a label
// over those dwords is pending, 3 the memory is not host-imported; 0 when recorded GPU-side. argumentReadMs is
// what that CPU read (its sync) took. The device limit on group counts is not checked GPU-side.
// Debug aid: APS5_NO_GPU_INDIRECT=1 (in the driver) keeps every indirect dispatch on the CPU path.
struct IndirectOutcome {
int cpuReason;
double argumentReadMs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void Driver::countIndirect(int path, double readMs) {
lastReport = now;
std::uint64_t cpu = 0;
for (int i = 1; i < IndirectPaths; ++i) cpu += counts[i];
std::fprintf(stderr, "[indirect] gpu-side %llu, cpu-side %llu (thread dimensions %llu, fill kernel %llu, pending image results %llu, pending label or copied write %llu, not imported %llu, misaligned %llu, disabled %llu); CPU argument reads took %.1f s\n", static_cast<unsigned long long>(counts[IndirectGpu]), static_cast<unsigned long long>(cpu), static_cast<unsigned long long>(counts[IndirectThreadDimensions]), static_cast<unsigned long long>(counts[IndirectFillKernel]), static_cast<unsigned long long>(counts[IndirectPendingImage]), static_cast<unsigned long long>(counts[IndirectCopiedWrite]), static_cast<unsigned long long>(counts[IndirectNotImported]), static_cast<unsigned long long>(counts[IndirectMisaligned]), static_cast<unsigned long long>(counts[IndirectDisabled]), readWaitedMs / 1000);
std::fprintf(stderr, "[indirect] gpu-side %llu, cpu-side %llu (thread dimensions %llu, fill kernel %llu, pending image results %llu, pending label or copied write %llu, not imported %llu, misaligned %llu, disabled %llu, device memory LDS %llu); CPU argument reads took %.1f s\n", static_cast<unsigned long long>(counts[IndirectGpu]), static_cast<unsigned long long>(cpu), static_cast<unsigned long long>(counts[IndirectThreadDimensions]), static_cast<unsigned long long>(counts[IndirectFillKernel]), static_cast<unsigned long long>(counts[IndirectPendingImage]), static_cast<unsigned long long>(counts[IndirectCopiedWrite]), static_cast<unsigned long long>(counts[IndirectNotImported]), static_cast<unsigned long long>(counts[IndirectMisaligned]), static_cast<unsigned long long>(counts[IndirectDisabled]), static_cast<unsigned long long>(counts[IndirectWorkgroupMemory]), readWaitedMs / 1000);
}

void Driver::dispatchIndirect(QueueState& queue, std::span<const std::uint32_t> packet, const Submission& submission) {
Expand Down
80 changes: 77 additions & 3 deletions core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ struct ComputePipelineObjects {
}
};

constexpr std::uint64_t WorkgroupMemoryGranule = 1u << 20u;

struct WorkgroupMemory {
VkDevice device = VK_NULL_HANDLE;
PFN_vkDestroyDescriptorPool destroyPool = nullptr;
std::unique_ptr<Graphics::DeviceBuffer> buffer;
VkDescriptorPool pool = VK_NULL_HANDLE;
VkDescriptorSet set = VK_NULL_HANDLE;
~WorkgroupMemory() {
buffer.reset();
if (pool != VK_NULL_HANDLE) destroyPool(device, pool, nullptr);
}
};

struct VulkanDevice::State {
void* library = nullptr;
PFN_vkGetInstanceProcAddr instanceProc = nullptr;
Expand Down Expand Up @@ -248,6 +262,8 @@ struct VulkanDevice::State {
// Device-local buffers of a repeated 16-byte fill pattern (FillBuffer), most recently used
// last; each is filled once by a doubling chain in device memory.
std::vector<std::pair<std::array<std::uint32_t, 4>, std::shared_ptr<Graphics::DeviceBuffer>>> patternBuffers;
VkDescriptorSetLayout workgroupMemoryLayout = VK_NULL_HANDLE;
std::shared_ptr<WorkgroupMemory> workgroupMemory;
// Compute pipeline objects by variant (and push-constant use), under their own mutex: the
// dispatch's find-or-insert and the verify switch's lookups touch the map, recipes hold weak
// references to its objects.
Expand Down Expand Up @@ -339,6 +355,54 @@ struct VulkanDevice::State {
return function;
}

void CreateWorkgroupMemoryLayout() {
const VkDescriptorSetLayoutBinding binding{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr};
VkDescriptorSetLayoutCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
info.bindingCount = 1;
info.pBindings = &binding;
check(DeviceFunction<PFN_vkCreateDescriptorSetLayout>("vkCreateDescriptorSetLayout")(device, &info, nullptr, &workgroupMemoryLayout), "vkCreateDescriptorSetLayout workgroup memory");
}

std::shared_ptr<WorkgroupMemory> WorkgroupMemoryFor(const Graphics::Context& context, std::uint32_t x, std::uint32_t y, std::uint32_t z, std::uint32_t dwords) {
const std::uint64_t range = properties.limits.maxStorageBufferRange;
const auto groupBytes = static_cast<std::uint64_t>(dwords) * 4u;
std::uint64_t groups = 1;
for (const std::uint64_t count : {x, y, z}) {
if (count != 0u && groups > range / groupBytes / count) {
throw std::runtime_error("Vulkan dispatch: the LDS of " + std::to_string(x) + "x" + std::to_string(y) + "x" + std::to_string(z) + " workgroups of " + std::to_string(groupBytes) + " bytes each is over maxStorageBufferRange " + std::to_string(range));
}
groups *= count;
}
const auto bytes = std::max<std::uint64_t>(groups, 1u) * groupBytes;
if (workgroupMemory != nullptr && workgroupMemory->buffer->Size() >= bytes) return workgroupMemory;
const auto size = std::min<std::uint64_t>((bytes + WorkgroupMemoryGranule - 1u) / WorkgroupMemoryGranule * WorkgroupMemoryGranule, range);
auto memory = std::make_shared<WorkgroupMemory>();
memory->device = device;
memory->destroyPool = DeviceFunction<PFN_vkDestroyDescriptorPool>("vkDestroyDescriptorPool");
memory->buffer = std::make_unique<Graphics::DeviceBuffer>(context, static_cast<std::size_t>(size), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
const VkDescriptorPoolSize poolSize{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1};
VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
poolInfo.maxSets = 1;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
check(DeviceFunction<PFN_vkCreateDescriptorPool>("vkCreateDescriptorPool")(device, &poolInfo, nullptr, &memory->pool), "vkCreateDescriptorPool workgroup memory");
VkDescriptorSetAllocateInfo allocation{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
allocation.descriptorPool = memory->pool;
allocation.descriptorSetCount = 1;
allocation.pSetLayouts = &workgroupMemoryLayout;
check(DeviceFunction<PFN_vkAllocateDescriptorSets>("vkAllocateDescriptorSets")(device, &allocation, &memory->set), "vkAllocateDescriptorSets workgroup memory");
const VkDescriptorBufferInfo described{memory->buffer->Handle(), 0, size};
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
write.dstSet = memory->set;
write.dstBinding = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write.pBufferInfo = &described;
DeviceFunction<PFN_vkUpdateDescriptorSets>("vkUpdateDescriptorSets")(device, 1, &write, 0, nullptr);
workgroupMemory = std::move(memory);
return workgroupMemory;
}

void Upload(std::span<const std::byte> pixels) {
if (uploadSize < pixels.size()) {
if (uploadMapping) DeviceFunction<PFN_vkUnmapMemory>("vkUnmapMemory")(device, uploadMemory);
Expand Down Expand Up @@ -537,6 +601,8 @@ struct VulkanDevice::State {
Graphics::ClearImageMirrors(device);
Graphics::ClearHostImports(device);
patternBuffers.clear();
workgroupMemory.reset();
if (workgroupMemoryLayout != VK_NULL_HANDLE) reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(deviceProc(device, "vkDestroyDescriptorSetLayout"))(device, workgroupMemoryLayout, nullptr);
descriptorCache.reset();
emptyBuffer.reset();
samplerCache.reset();
Expand Down Expand Up @@ -1215,6 +1281,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
state->colorTransfer = std::make_unique<Graphics::GpuColorTransfer>(graphicsContext());
state->descriptorCache = std::make_unique<Graphics::DescriptorCache>(graphicsContext());
state->samplerCache = std::make_unique<Graphics::SamplerCache>();
state->CreateWorkgroupMemoryLayout();
state->recorder = std::make_unique<Graphics::Recorder>(graphicsContext(), state->timelineSemaphores);
state->recorder->Activate();
state->context = buildContext();
Expand Down Expand Up @@ -3289,6 +3356,8 @@ void VulkanDevice::decideIndirect(RecordedDispatch& record, IndirectOutcome& out
outcome.cpuReason = 1;
Graphics::Recorder::CountSync(2);
recorder.Sync();
} else if (record.shader->program->workgroupMemoryDwords != 0) {
outcome.cpuReason = 8;
} else if (labelPending() || std::any_of(state->copiedWriters->begin(), state->copiedWriters->end(), [&](const auto& writer) { return writer->WritesOverlap(arguments, 12); })
|| std::any_of(Graphics::DrawCopiedWriters()->begin(), Graphics::DrawCopiedWriters()->end(), [&](const auto& writer) { return writer->WritesOverlap(arguments, 12); })) {
outcome.cpuReason = 2;
Expand Down Expand Up @@ -3412,6 +3481,11 @@ void VulkanDevice::recordDispatch(RecordedDispatch& record) {
}
context.Resolved(&Graphics::DeviceFunctions::cmdBindPipeline, "vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, record.objects->pipeline);
resources.Bind(commands, VK_PIPELINE_BIND_POINT_COMPUTE, record.objects->layout);
if (const auto dwords = record.shader->program->workgroupMemoryDwords; dwords != 0) {
auto memory = state->WorkgroupMemoryFor(context, record.x, record.y, record.z, dwords);
context.Resolved(&Graphics::DeviceFunctions::cmdBindDescriptorSets, "vkCmdBindDescriptorSets")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, record.objects->layout, ShaderRecompiler::WorkgroupMemoryDescriptorSet, 1, &memory->set, 0, nullptr);
recorder.Keep(std::move(memory));
}
if (record.pushStages != 0) {
context.Resolved(&Graphics::DeviceFunctions::cmdPushConstants, "vkCmdPushConstants")(commands, record.objects->layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::PipelinePushConstantBytes, record.pushBytes->data());
}
Expand Down Expand Up @@ -3625,11 +3699,11 @@ VulkanDevice::IndirectOutcome VulkanDevice::dispatch(const ShaderRecompiler::Rec
moduleInfo.pCode = shader.spirv.data();
check(state->DeviceFunction<PFN_vkCreateShaderModule>("vkCreateShaderModule")(state->device, &moduleInfo, nullptr, &objects->module), "vkCreateShaderModule");
timing.Mark("validate_shader_module");
const auto setLayout = resources->Layout();
const std::array<VkDescriptorSetLayout, 2> setLayouts{resources->Layout(), shader.workgroupMemoryDwords != 0 ? state->workgroupMemoryLayout : VK_NULL_HANDLE};
const VkPushConstantRange push{VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::PipelinePushConstantBytes};
VkPipelineLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
layoutInfo.setLayoutCount = 1;
layoutInfo.pSetLayouts = &setLayout;
layoutInfo.setLayoutCount = shader.workgroupMemoryDwords != 0 ? ShaderRecompiler::WorkgroupMemoryDescriptorSet + 1 : 1;
layoutInfo.pSetLayouts = setLayouts.data();
layoutInfo.pushConstantRangeCount = pushStages != 0 ? 1 : 0;
layoutInfo.pPushConstantRanges = pushStages != 0 ? &push : nullptr;
check(state->DeviceFunction<PFN_vkCreatePipelineLayout>("vkCreatePipelineLayout")(state->device, &layoutInfo, nullptr, &objects->layout), "vkCreatePipelineLayout");
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ void requireSameResult(const RecompileResult& left, const RecompileResult& right
require(sameBindings(left.bindings, right.bindings), prefix + "bindings differ");
require(left.pushConstants == right.pushConstants, prefix + "push constants differ");
require(left.specialization == right.specialization, prefix + "specialization constants differ");
require(left.workgroupMemoryDwords == right.workgroupMemoryDwords, prefix + "workgroup memory stride differs");
require(left.poisonedSrtReads == right.poisonedSrtReads, prefix + "poisoned SRT read counts differ");
require(left.vertexAttributes.size() == right.vertexAttributes.size(), prefix + "vertex attribute count differs");
for (std::size_t i = 0; i < left.vertexAttributes.size(); ++i) {
Expand Down Expand Up @@ -137,6 +138,7 @@ RecompileResult sampleResult() {
result.bdaAbiVersion = 3;
result.memoryOffsetDword = 7;
result.hostSubgroupSize = 32;
result.workgroupMemoryDwords = 32769u;
result.vertexInputs = {{1, 4, 2}, {5, 2, 0}};
result.vertexInputPatches = {{1, 20, {7, 8, 9}}, {5, 40, {10, 11, 12}}};
result.specialization = {{512, 4}, {516, 0x3f800000u}, {517, 0}};
Expand Down
Loading
Loading