feat(agc): add timing logs for path after recompling

This commit is contained in:
boykopovar 2026-09-23 22:11:41 +03:00
parent 10e4d5e469
commit 630d918b4a
10 changed files with 153 additions and 11 deletions

2
.gitignore vendored
View File

@ -29,6 +29,8 @@
*.app
_SaveData/
_sd/
build/
b/
Build/

View File

@ -0,0 +1,54 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PERFORMANCETIMER_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PERFORMANCETIMER_HPP
#include "prx/libc/include/general/LogMacros.hpp"
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdio>
#include <exception>
#include <stdexcept>
namespace AgcDriver {
class PerformanceTimer {
public:
explicit PerformanceTimer(const char* scope) : scope(scope) {}
PerformanceTimer(const PerformanceTimer&) = delete;
PerformanceTimer& operator=(const PerformanceTimer&) = delete;
~PerformanceTimer() {
const auto end = Clock::now();
APS5_LOG_OUT("[Timing] id=%llu scope=%s total_ms=%.3f tail_ms=%.3f unwinding=%u%s", id, scope, milliseconds(end - start), milliseconds(end - previous), std::uncaught_exceptions() > exceptions ? 1u : 0u, stages);
}
void Mark(const char* stage) {
const auto now = Clock::now();
const auto count = std::snprintf(stages + used, sizeof(stages) - used, " %s_ms=%.3f", stage, milliseconds(now - previous));
if (count < 0 || static_cast<std::size_t>(count) >= sizeof(stages) - used) {
throw std::runtime_error("PerformanceTimer: timing log capacity exceeded");
}
used += static_cast<std::size_t>(count);
previous = now;
}
private:
using Clock = std::chrono::steady_clock;
static double milliseconds(Clock::duration duration) {
return std::chrono::duration<double, std::milli>(duration).count();
}
inline static std::atomic<unsigned long long> nextId{0};
const char* scope;
unsigned long long id = nextId.fetch_add(1, std::memory_order_relaxed);
Clock::time_point start = Clock::now();
Clock::time_point previous = start;
int exceptions = std::uncaught_exceptions();
char stages[2048]{};
std::size_t used = 0;
};
}
#endif

View File

@ -1,5 +1,6 @@
#include "prx/libSceAgcDriver/Execution/include/DisplayBuffer.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include <cstring>
#include <limits>
#include <stdexcept>
@ -27,10 +28,12 @@ std::size_t DisplayBufferSize(const DisplayBuffer& buffer) {
}
std::vector<std::byte> DecodeDisplayBuffer(const DisplayBuffer& buffer, std::span<const std::byte> source) {
PerformanceTimer timing("DisplayBuffer.Decode");
require(source.size() == DisplayBufferSize(buffer), "VideoOut: invalid tiled display buffer size");
std::vector<std::byte> pixels(static_cast<std::size_t>(buffer.width) * buffer.height * 4);
const auto blocksPerRow = (buffer.width + 127u) / 128u;
const bool rgba = buffer.pixelFormat == 0x8000000022000000ull;
timing.Mark("validate_allocate");
for (std::uint32_t y = 0; y < buffer.height; ++y) {
for (std::uint32_t x = 0; x < buffer.width; ++x) {
const auto tiled = (static_cast<std::size_t>(y / 128u) * blocksPerRow + x / 128u) * 65536u + tileOffset(x, y);
@ -41,6 +44,7 @@ std::vector<std::byte> DecodeDisplayBuffer(const DisplayBuffer& buffer, std::spa
pixels[linear + 3] = source[tiled + 3];
}
}
timing.Mark("detile_convert");
return pixels;
}

View File

@ -1,4 +1,5 @@
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/ShaderMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
@ -184,35 +185,45 @@ public:
}
void Present(const PresentationWindow& window, const DisplayBuffer* buffer, bool opaque, void (*gpuReady)(void*), void* context) {
PerformanceTimer timing("Driver.Present");
CheckFailure();
require(gpuReady != nullptr && context != nullptr, "missing GPU completion callback");
require(window.getDrawableSize != nullptr, "missing window drawable size query");
std::shared_ptr<VulkanDevice> presenting;
timing.Mark("validate");
try {
{
std::lock_guard lock(gpuMutex);
timing.Mark("gpu_mutex_wait");
if (device == nullptr || device->Window() == nullptr) {
if (device) device->WaitIdle();
device = std::make_shared<VulkanDevice>(&window);
}
require(device->Window() == window.context, "presentation window does not match device surface");
presenting = device;
timing.Mark("device_setup");
std::uint32_t drawableWidth = 0;
std::uint32_t drawableHeight = 0;
window.getDrawableSize(window.context, &drawableWidth, &drawableHeight);
presenting->Resize(drawableWidth, drawableHeight);
timing.Mark("resize");
if (presenting->Presentable()) {
if (buffer != nullptr) {
require(buffer->width == window.width && buffer->height == window.height, "display buffer extent differs from output");
presenting->WaitIdle();
timing.Mark("device_idle_wait");
const auto pixels = ReadDisplayBuffer(*buffer);
timing.Mark("display_read_detile");
presenting->PresentPixels(window.width, window.height, pixels);
timing.Mark("present_pixels");
} else {
presenting->PresentClear(window.width, window.height, opaque);
timing.Mark("present_clear");
}
}
}
gpuReady(context);
timing.Mark("release_and_callback");
CheckFailure();
} catch (...) {
ReportFailure(std::current_exception());
@ -329,6 +340,7 @@ private:
}
void draw(QueueState& queue, std::span<const std::uint32_t> packet, const Submission& submission) {
PerformanceTimer timing("Driver.Draw");
auto drawParameters = Pm4::ResolveDraw(packet, queue);
if (!drawParameters.indexed && (drawParameters.indexCount == 0 || drawParameters.instanceCount == 0)) return;
const auto graphics = Graphics::DecodeState(queue);
@ -416,8 +428,11 @@ private:
memory.insert(memory.end(), program.memory.begin(), program.memory.end());
linked.push_back({roles[i], program.binary, program.userDataBase, program.firstUserSgpr, program.userData});
}
timing.Mark("prepare");
std::lock_guard gpuLock(gpuMutex);
timing.Mark("gpu_mutex_wait");
if (device == nullptr) device = std::make_shared<VulkanDevice>();
timing.Mark("device_setup");
ShaderMemory shaderMemory(memory);
std::vector<ShaderRecompiler::RecompileResult> results;
std::vector<Graphics::CompiledShader> stages;
@ -454,6 +469,7 @@ private:
stages.push_back({program.binary.stage, &result, result.pushConstants.empty() ? 0u : pushCursorBytes});
pushCursorBytes += static_cast<std::uint32_t>(result.pushConstants.size());
}
timing.Mark("shader_compile_and_link");
if (graphics.rectList) {
require(stages.size() == 2, "rect-list requires vertex and fragment programs");
auto rectangle = ShaderRecompiler::BuildRectListShaders(results[0], results[1], device->Target());
@ -463,7 +479,9 @@ private:
}
std::vector<Graphics::GuestMemorySnapshot> snapshots;
for (const auto& region : memory) snapshots.push_back({region.guestAddress, region.bytes});
timing.Mark("post_compile_prepare");
device->Draw(graphics, drawParameters, stages, snapshots);
timing.Mark("draw_and_resource_release");
}
void execute(const Submission& submission) {

View File

@ -1,5 +1,6 @@
#include "BdaAbi.hpp"
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp"
#include "prx/libSceAgcDriver/Execution/include/PresentationScaler.hpp"
#include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp"
@ -534,10 +535,12 @@ void VulkanDevice::PresentPixels(std::uint32_t width, std::uint32_t height, std:
}
void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaque, std::span<const std::byte> pixels) {
PerformanceTimer timing("Vulkan.Present");
APS5_LOG_OUT_DEBUG("present begin width=%u height=%u opaque=%u pixels=%zu", width, height, static_cast<unsigned>(opaque), pixels.size());
require(state->swapchain != VK_NULL_HANDLE, "device has no swapchain");
require(state->extent.width != 0 && state->extent.height != 0, "output window is minimized");
if (!pixels.empty()) state->Upload(pixels);
timing.Mark("pixel_upload");
APS5_LOG_OUT_DEBUG("present source=%s bytes=%zu", pixels.empty() ? "clear" : "pixels", pixels.size());
auto wait = state->DeviceFunction<PFN_vkWaitForFences>("vkWaitForFences");
auto reset = state->DeviceFunction<PFN_vkResetFences>("vkResetFences");
@ -545,9 +548,12 @@ void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaqu
check(reset(state->device, static_cast<std::uint32_t>(fences.size()), fences.data()), "vkResetFences");
APS5_LOG_CHARS_OUT_DEBUG("present fences reset");
std::uint32_t index = 0;
timing.Mark("fence_reset");
check(state->DeviceFunction<PFN_vkAcquireNextImageKHR>("vkAcquireNextImageKHR")(state->device, state->swapchain, 5'000'000'000ULL, VK_NULL_HANDLE, state->acquireFence, &index), "vkAcquireNextImageKHR");
timing.Mark("acquire_image");
APS5_LOG_OUT_DEBUG("vkAcquireNextImageKHR index=%u imageCount=%zu", index, state->images.size());
check(wait(state->device, 1, &state->acquireFence, VK_TRUE, std::numeric_limits<std::uint64_t>::max()), "vkWaitForFences acquire");
timing.Mark("acquire_fence_wait");
APS5_LOG_OUT_DEBUG("Acquire fence complete index=%u", index);
require(index < state->images.size() && index < state->rendered.size(), "acquired image index is out of range");
auto& rendered = state->rendered[index];
@ -558,6 +564,7 @@ void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaqu
check(state->DeviceFunction<PFN_vkCreateSemaphore>("vkCreateSemaphore")(state->device, &semaphore, nullptr, &rendered), "vkCreateSemaphore presentation");
}
auto commands = state->clearCommands;
timing.Mark("retired_swapchains");
check(state->DeviceFunction<PFN_vkResetCommandBuffer>("vkResetCommandBuffer")(commands, 0), "vkResetCommandBuffer");
VkCommandBufferBeginInfo begin{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
@ -610,9 +617,12 @@ void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaqu
submit.pCommandBuffers = &commands;
submit.signalSemaphoreCount = 1;
submit.pSignalSemaphores = &rendered;
timing.Mark("command_record_scale");
check(state->DeviceFunction<PFN_vkQueueSubmit>("vkQueueSubmit")(state->queue, 1, &submit, state->renderFence), "vkQueueSubmit clear");
timing.Mark("queue_submit");
APS5_LOG_CHARS_OUT_DEBUG("Presentation vkQueueSubmit OK");
check(wait(state->device, 1, &state->renderFence, VK_TRUE, std::numeric_limits<std::uint64_t>::max()), "vkWaitForFences clear");
timing.Mark("render_fence_wait");
APS5_LOG_CHARS_OUT_DEBUG("Presentation render fence complete");
VkPresentInfoKHR present{VK_STRUCTURE_TYPE_PRESENT_INFO_KHR};
present.waitSemaphoreCount = 1;
@ -621,6 +631,7 @@ void VulkanDevice::present(std::uint32_t width, std::uint32_t height, bool opaqu
present.pSwapchains = &state->swapchain;
present.pImageIndices = &index;
check(state->DeviceFunction<PFN_vkQueuePresentKHR>("vkQueuePresentKHR")(state->queue, &present), "vkQueuePresentKHR");
timing.Mark("queue_present");
APS5_LOG_OUT_DEBUG("vkQueuePresentKHR queued imageIndex=%u", index);
}
@ -668,6 +679,7 @@ void VulkanDevice::Draw(const Graphics::State& graphics, const Pm4::DrawParamete
}
void VulkanDevice::Dispatch(const ShaderRecompiler::RecompileResult& shader, std::uint32_t x, std::uint32_t y, std::uint32_t z, std::span<const Graphics::GuestMemorySnapshot> snapshots) {
PerformanceTimer timing("Vulkan.Dispatch");
APS5_LOG_OUT_DEBUG("Dispatch groups=%ux%ux%u spirvWords=%zu bindings=%zu pushConstants=%zu", x, y, z, shader.spirv.size(), shader.bindings.size(), shader.pushConstants.size());
if (shader.spirv.size() < 5 || shader.spirv[0] != 0x07230203u) {
throw std::runtime_error("Vulkan dispatch: invalid SPIR-V");
@ -707,7 +719,9 @@ void VulkanDevice::Dispatch(const ShaderRecompiler::RecompileResult& shader, std
moduleInfo.codeSize = shader.spirv.size() * sizeof(std::uint32_t);
moduleInfo.pCode = shader.spirv.data();
check(state->DeviceFunction<PFN_vkCreateShaderModule>("vkCreateShaderModule")(state->device, &moduleInfo, nullptr, &module), "vkCreateShaderModule");
timing.Mark("validate_shader_module");
Graphics::ShaderResources resources(context, shaders[0], snapshots);
timing.Mark("shader_resources");
const auto setLayout = resources.Layout();
const VkPushConstantRange push{VK_SHADER_STAGE_COMPUTE_BIT, 0, Graphics::PipelinePushConstantBytes};
VkPipelineLayoutCreateInfo layoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
@ -723,6 +737,7 @@ void VulkanDevice::Dispatch(const ShaderRecompiler::RecompileResult& shader, std
pipelineInfo.stage.pName = "main";
pipelineInfo.layout = layout;
check(state->DeviceFunction<PFN_vkCreateComputePipelines>("vkCreateComputePipelines")(state->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline), "vkCreateComputePipelines");
timing.Mark("pipeline_create");
VkCommandBufferAllocateInfo allocation{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
allocation.commandPool = state->pool;
allocation.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
@ -751,15 +766,19 @@ void VulkanDevice::Dispatch(const ShaderRecompiler::RecompileResult& shader, std
VkSubmitInfo submission{VK_STRUCTURE_TYPE_SUBMIT_INFO};
submission.commandBufferCount = 1;
submission.pCommandBuffers = &commands;
timing.Mark("command_record");
check(submit(state->queue, 1, &submission, fence), "vkQueueSubmit");
timing.Mark("queue_submit");
APS5_LOG_OUT_DEBUG("Dispatch submitted groups=%ux%ux%u", x, y, z);
const auto result = wait(state->device, 1, &fence, VK_TRUE, std::numeric_limits<std::uint64_t>::max());
timing.Mark("fence_wait");
if (result != VK_SUCCESS && result != VK_ERROR_DEVICE_LOST) {
const auto idle = state->DeviceFunction<PFN_vkDeviceWaitIdle>("vkDeviceWaitIdle")(state->device);
check(idle, "vkDeviceWaitIdle after fence failure");
}
check(result, "vkWaitForFences");
resources.WriteBack();
timing.Mark("resources_writeback");
APS5_LOG_OUT_DEBUG("Dispatch complete groups=%ux%ux%u", x, y, z);
} catch (...) {
cleanup();

View File

@ -1,10 +1,12 @@
#include "prx/libSceAgcDriver/Graphics/include/ColorTargetTransfer.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include <vector>
namespace AgcDriver::Graphics {
void ReadColorTarget(const ColorTarget& target, std::span<std::byte> destination) {
PerformanceTimer timing("ColorTarget.Read");
const ColorTargetLayout layout(target.extent.width, target.extent.height, target.tileMode);
Require(target.bytes == layout.Bytes() && destination.size() == layout.LinearBytes(), "color target transfer size mismatch");
if (target.tileMode == ColorTileMode::Linear) {
@ -12,11 +14,15 @@ void ReadColorTarget(const ColorTarget& target, std::span<std::byte> destination
return;
}
std::vector<std::byte> tiled(layout.Bytes());
timing.Mark("allocate");
GuestMemory::Read(target.address, tiled, layout.Alignment());
timing.Mark("guest_read");
layout.Detile(tiled, destination);
timing.Mark("detile");
}
void WriteColorTarget(const ColorTarget& target, std::span<const std::byte> source) {
PerformanceTimer timing("ColorTarget.Write");
const ColorTargetLayout layout(target.extent.width, target.extent.height, target.tileMode);
Require(target.bytes == layout.Bytes() && source.size() == layout.LinearBytes(), "color target transfer size mismatch");
if (target.tileMode == ColorTileMode::Linear) {
@ -24,9 +30,13 @@ void WriteColorTarget(const ColorTarget& target, std::span<const std::byte> sour
return;
}
std::vector<std::byte> tiled(layout.Bytes());
timing.Mark("allocate");
GuestMemory::Read(target.address, tiled, layout.Alignment());
timing.Mark("guest_read");
layout.Tile(source, tiled);
timing.Mark("tile");
GuestMemory::Write(target.address, tiled, layout.Alignment());
timing.Mark("guest_write");
}
}

View File

@ -2,6 +2,7 @@
#include "prx/libSceAgcDriver/Graphics/include/ColorTargetTransfer.hpp"
#include "prx/libSceAgcDriver/Graphics/include/VertexInput.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libc/include/General.hpp"
#include <cstring>
#include <limits>
@ -26,6 +27,7 @@ void imageBarrier(const Context& context, VkCommandBuffer commands, VkImage imag
}
void Draw(const Context& context, const State& state, const Pm4::DrawParameters& draw, std::span<const CompiledShader> shaders, std::span<const GuestMemorySnapshot> snapshots) {
PerformanceTimer timing("Graphics.Draw");
APS5_LOG_OUT("Draw indices=%u instances=%u indexSize=%u flags=%u indexAddress=0x%llx", draw.indexCount, draw.instanceCount, draw.indexSize, draw.flags, static_cast<unsigned long long>(draw.indexAddress));
APS5_LOG_OUT_DEBUG("State colorTarget=%u render=%ux%u colorAddress=0x%llx colorBytes=%llu colorExtent=%ux%u", state.hasColorTarget ? 1u : 0u, state.renderExtent.width, state.renderExtent.height, static_cast<unsigned long long>(state.color.address), static_cast<unsigned long long>(state.color.bytes), state.color.extent.width, state.color.extent.height);
APS5_LOG_OUT_DEBUG("Viewport x=%f y=%f w=%f h=%f minDepth=%f maxDepth=%f", state.viewport.x, state.viewport.y, state.viewport.width, state.viewport.height, state.viewport.minDepth, state.viewport.maxDepth);
@ -68,6 +70,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
Require(meshGroups <= context.meshLimits.maxMeshWorkGroupCount[0] && draw.instanceCount <= context.meshLimits.maxMeshWorkGroupCount[1] && static_cast<std::uint64_t>(meshGroups) * draw.instanceCount <= context.meshLimits.maxMeshWorkGroupTotalCount, "mesh draw exceeds workgroup count limits");
}
if (state.stages.tessellation) Require(draw.indexCount % state.stages.tessellation->inputControlPoints == 0, "incomplete tessellation patch");
timing.Mark("validate");
std::unique_ptr<Buffer> indices;
std::uint32_t maxIndex = draw.indexed ? 0u : draw.firstVertex + draw.indexCount - 1u;
if (draw.indexed) {
@ -87,6 +90,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
}
}
APS5_LOG_CHARS_OUT_DEBUG("Index validation OK");
timing.Mark("index_upload");
const auto& attributes = shaders.front().program->vertexAttributes;
static_cast<void>(BuildVertexInputLayout(context, attributes));
std::vector<std::unique_ptr<Buffer>> vertexBuffers;
@ -103,6 +107,7 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
vertexHandles.push_back(buffer->Handle());
vertexBuffers.push_back(std::move(buffer));
}
timing.Mark("vertex_upload");
std::unique_ptr<Buffer> transfer;
std::unique_ptr<RenderTarget> target;
if (state.hasColorTarget) {
@ -110,17 +115,22 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
const ColorTargetLayout colorLayout(state.color.extent.width, state.color.extent.height, state.color.tileMode);
transfer = std::make_unique<Buffer>(context, colorLayout.LinearBytes(), VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
APS5_LOG_CHARS_OUT_DEBUG("Transfer buffer created");
timing.Mark("color_transfer_allocate");
ReadColorTarget(state.color, transfer->Bytes());
timing.Mark("color_read_detile");
APS5_LOG_CHARS_OUT_DEBUG("Color target read from guest memory");
target = std::make_unique<RenderTarget>(context, state.color, state.blend.blendEnable != 0);
timing.Mark("render_target_create");
APS5_LOG_CHARS_OUT_DEBUG("RenderTarget created");
}
APS5_LOG_OUT_DEBUG("Color target object=%u", target ? 1u : 0u);
APS5_LOG_CHARS_OUT_DEBUG("Creating ShaderResources");
ShaderResources resources(context, shaders, state.color, draw.indexAddress, static_cast<std::size_t>(indexBytes), snapshots);
timing.Mark("shader_resources");
APS5_LOG_CHARS_OUT_DEBUG("ShaderResources created");
APS5_LOG_CHARS_OUT_DEBUG("Creating Pipeline");
Pipeline pipeline(context, state, target.get(), resources, shaders);
timing.Mark("pipeline_create");
APS5_LOG_CHARS_OUT_DEBUG("Pipeline created");
APS5_LOG_CHARS_OUT_DEBUG("Creating CommandBatch");
CommandBatch batch(context);
@ -183,14 +193,19 @@ void Draw(const Context& context, const State& state, const Pm4::DrawParameters&
context.Function<PFN_vkCmdPipelineBarrier>("vkCmdPipelineBarrier")(commands, VK_PIPELINE_STAGE_TRANSFER_BIT | shaderStages, VK_PIPELINE_STAGE_HOST_BIT, 0, 1, &download, 0, nullptr, 0, nullptr);
APS5_LOG_CHARS_OUT_DEBUG("Download barrier recorded");
APS5_LOG_CHARS_OUT_DEBUG("SubmitAndWait");
timing.Mark("command_record");
batch.SubmitAndWait();
timing.Mark("submit_wait");
APS5_LOG_CHARS_OUT_DEBUG("SubmitAndWait OK");
if (state.hasColorTarget) GuestMemory::CheckRange(reinterpret_cast<const void*>(state.color.address), state.color.bytes, 256, true);
if (state.hasColorTarget) APS5_LOG_CHARS_OUT_DEBUG("Color target guest range OK");
timing.Mark("color_range_check");
APS5_LOG_CHARS_OUT_DEBUG("Shader resources WriteBack");
resources.WriteBack();
timing.Mark("resources_writeback");
APS5_LOG_CHARS_OUT_DEBUG("Shader resources WriteBack OK");
if (state.hasColorTarget) WriteColorTarget(state.color, transfer->Bytes());
timing.Mark("color_tile_writeback");
if (state.hasColorTarget) APS5_LOG_OUT_DEBUG("Color target written to guest address=0x%llx bytes=%llu", static_cast<unsigned long long>(state.color.address), static_cast<unsigned long long>(state.color.bytes));
APS5_LOG_CHARS_OUT("Draw finished");
}

View File

@ -1,4 +1,5 @@
#include "prx/libSceAgcDriver/Graphics/include/Resources.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include <exception>
namespace AgcDriver::Graphics {
@ -144,15 +145,19 @@ VkCommandBuffer CommandBatch::Handle() const {
}
void CommandBatch::SubmitAndWait() {
PerformanceTimer timing("Graphics.SubmitAndWait");
Require(!submitted, "command batch has already been submitted");
Check(context.Function<PFN_vkEndCommandBuffer>("vkEndCommandBuffer")(commands), "vkEndCommandBuffer");
VkSubmitInfo submission{VK_STRUCTURE_TYPE_SUBMIT_INFO};
submission.commandBufferCount = 1;
submission.pCommandBuffers = &commands;
timing.Mark("command_end");
Check(context.Function<PFN_vkQueueSubmit>("vkQueueSubmit")(context.queue, 1, &submission, fence), "vkQueueSubmit graphics");
timing.Mark("queue_submit");
pending = true;
submitted = true;
const auto result = context.Function<PFN_vkWaitForFences>("vkWaitForFences")(context.device, 1, &fence, VK_TRUE, 5'000'000'000ULL);
timing.Mark("fence_wait");
if (result == VK_SUCCESS || result == VK_ERROR_DEVICE_LOST) pending = false;
Check(result, "vkWaitForFences graphics");
}

View File

@ -9,7 +9,7 @@
#include "prx/libc/include/General.hpp"
#include "SaveData.hpp"
static constexpr char SAVE_DIR[] = "_SaveData";
static constexpr char SAVE_DIR[] = "_sd";
static std::atomic<std::int32_t> g_transaction_counter{1};
static bool g_initialized = false;
@ -18,10 +18,6 @@ static std::string save_root() {
return std::string(SAVE_DIR);
}
static std::string slot_mount_point(int slot) {
return std::string("/savedata") + std::to_string(slot);
}
static bool dir_name_match(const char* str, const char* pattern) {
if (pattern == nullptr || pattern[0] == '\0') {
return true;
@ -199,8 +195,22 @@ int APS5_VABI sceSaveDataMount3(const SaveDataMount3* mount, SaveDataMountResult
if (!create && !create2 && !open) {
throw std::runtime_error("sceSaveDataMount3: unknown mount_mode");
}
const std::string dir_name = mount->dir_name->data;
const std::string real_path = save_root() + "/" + dir_name;
const auto* nameEnd = static_cast<const char*>(std::memchr(mount->dir_name->data, '\0', sizeof(mount->dir_name->data)));
if (nameEnd == nullptr) {
throw std::runtime_error("sceSaveDataMount3: unterminated directory name");
}
const std::string dirName(mount->dir_name->data, static_cast<std::size_t>(nameEnd - mount->dir_name->data));
if (dirName.empty() || dirName == "." || dirName == ".." || dirName.find_first_of("/\\:") != std::string::npos) {
throw std::runtime_error("sceSaveDataMount3: invalid directory name");
}
const std::string real_path = save_root() + "/" + dirName;
const std::string mountPoint = "/" + real_path;
if (mountPoint.size() >= sizeof(mount_result->mount_point.data)) {
throw std::runtime_error("sceSaveDataMount3: directory path exceeds mount point capacity");
}
if (find_slot_by_mount_point(mountPoint.c_str()) != -1) {
return SAVE_DATA_ERROR_BUSY;
}
const bool exists = std::filesystem::is_directory(real_path);
if (create && exists) {
return SAVE_DATA_ERROR_EXISTS;
@ -215,11 +225,10 @@ int APS5_VABI sceSaveDataMount3(const SaveDataMount3* mount, SaveDataMountResult
if (create || create2) {
std::filesystem::create_directories(real_path);
}
const std::string mp = slot_mount_point(slot);
g_slots[slot].used = true;
g_slots[slot].mount_point = mp;
g_slots[slot].mount_point = mountPoint;
g_slots[slot].real_path = real_path;
std::snprintf(mount_result->mount_point.data, sizeof(mount_result->mount_point.data), "%s", mp.c_str());
std::memcpy(mount_result->mount_point.data, mountPoint.c_str(), mountPoint.size() + 1);
mount_result->required_blocks = 0;
mount_result->mount_status = (create || create2) ? 1u : 0u;
return SAVE_DATA_OK;

View File

@ -9,6 +9,8 @@
#include <stdexcept>
#include <string>
static constexpr int SCE_KERNEL_ERROR_ENOENT = -2147352574;
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
@ -92,7 +94,11 @@ int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) {
auto native = ResolvePath_nid_no_patch(path);
int fd = NativeOpen(native, MapFlags(flags), mode);
if (fd < 0) {
throw std::runtime_error(std::string(__func__) + ": failed to open " + native.string() + ", errno=" + std::to_string(errno));
const int error = errno;
if (error == ENOENT) {
return SCE_KERNEL_ERROR_ENOENT;
}
throw std::runtime_error(std::string(__func__) + ": failed to open " + native.string() + ", errno=" + std::to_string(error));
}
return fd;
}