feat(agc): add timing logs for path after recompling
This commit is contained in:
parent
10e4d5e469
commit
630d918b4a
|
|
@ -29,6 +29,8 @@
|
|||
*.app
|
||||
|
||||
_SaveData/
|
||||
_sd/
|
||||
|
||||
build/
|
||||
b/
|
||||
Build/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue