mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16a7fe689a | |||
| 6f0cddd440 | |||
| 4084a008ea | |||
| c0ebf22476 | |||
| cb375ec35a | |||
| 6f3bebcb50 | |||
| 1029511bbc | |||
| 65144ce4ac | |||
| f848d2761b | |||
| 730e1a3cf3 | |||
| d4bb7fd786 | |||
| fbea51c684 |
+8
-3
@@ -40,6 +40,11 @@ if (NOT (WIN32 OR LINUX OR APPLE))
|
||||
message(FATAL_ERROR "only Windows, Linux, and macOS builds are supported")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Prevent Windows headers from defining min/max macros in all targets.
|
||||
add_compile_definitions(NOMINMAX)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
@@ -399,7 +404,7 @@ add_executable(scalar_provenance_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
|
||||
)
|
||||
target_link_libraries(scalar_provenance_tests fmt::fmt)
|
||||
target_link_libraries(scalar_provenance_tests fmt::fmt xbyak::xbyak)
|
||||
target_include_directories(scalar_provenance_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(page_manager_tests EXCLUDE_FROM_ALL
|
||||
@@ -467,7 +472,7 @@ add_executable(resource_materialization_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/SrtWalker.cpp"
|
||||
)
|
||||
target_link_libraries(resource_materialization_tests fmt::fmt)
|
||||
target_link_libraries(resource_materialization_tests fmt::fmt xbyak::xbyak)
|
||||
target_include_directories(resource_materialization_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||
@@ -480,7 +485,7 @@ add_executable(resource_tracking_tests EXCLUDE_FROM_ALL
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp"
|
||||
"${KYTY_SOURCE_DIR}/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp"
|
||||
)
|
||||
target_link_libraries(resource_tracking_tests fmt::fmt)
|
||||
target_link_libraries(resource_tracking_tests fmt::fmt xbyak::xbyak)
|
||||
target_include_directories(resource_tracking_tests PRIVATE ${inc_headers})
|
||||
|
||||
add_executable(audio_out2_port_tests EXCLUDE_FROM_ALL
|
||||
|
||||
@@ -248,6 +248,43 @@ sources, so it needs network access; a fully sandboxed `nix build` would require
|
||||
inputs. A Vulkan 1.3 driver must be available at runtime (on NixOS,
|
||||
`hardware.graphics.enable = true`).
|
||||
|
||||
### Building on Fedora and Fedora Atomic (Bazzite, Bluefin, Silverblue)
|
||||
|
||||
On regular Fedora, install dependencies on the host. On Fedora Atomic variants such as Bazzite,
|
||||
Bluefin and Silverblue, use [Distrobox](https://distrobox.it/) and run the install and build steps
|
||||
inside the container. Keep the checkout in your home directory so it is shared with the container.
|
||||
Add `--nvidia` to the create command for hosts using the proprietary NVIDIA driver:
|
||||
|
||||
```bash
|
||||
distrobox create --name kyty-build --image registry.fedoraproject.org/fedora:latest
|
||||
distrobox enter kyty-build
|
||||
```
|
||||
|
||||
Install the toolchain, Qt 6 and SDL3 dependencies:
|
||||
|
||||
```bash
|
||||
sudo dnf install -y \
|
||||
clang lld ninja-build cmake git glslang python3 pkgconf-pkg-config \
|
||||
mesa-libGL-devel libX11-devel libXcursor-devel libXext-devel libXfixes-devel \
|
||||
libXi-devel libXrandr-devel libXScrnSaver-devel libXtst-devel libxkbcommon-devel \
|
||||
alsa-lib-devel pulseaudio-libs-devel systemd-devel dbus-devel \
|
||||
wayland-devel wayland-protocols-devel qt6-qtbase-devel
|
||||
```
|
||||
|
||||
From the repository root, follow the launcher configure, build and install steps in
|
||||
[Building on Linux](#building-on-linux), omitting `-DCMAKE_PREFIX_PATH="$Qt6_DIR"`; CMake finds
|
||||
Fedora's packaged Qt 6 automatically.
|
||||
|
||||
Run `./_Build/linux/install/launcher` on regular Fedora or inside the container. To launch a
|
||||
container build from the host, leave the container with `exit`, then run from the repository root:
|
||||
|
||||
```bash
|
||||
distrobox enter kyty-build -- ./_Build/linux/install/launcher
|
||||
```
|
||||
|
||||
Qt libraries and plugins are bundled. You can also launch directly on a Fedora Atomic host
|
||||
if its other system libraries are compatible with the container build.
|
||||
|
||||
### Building on macOS
|
||||
|
||||
macOS builds target x86-64 and run under Rosetta 2 on Apple Silicon, so the PS5's x86-64 game
|
||||
|
||||
@@ -182,4 +182,8 @@ const Keymap& GetKeymap() {
|
||||
return g_config->keymap;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetGuestArguments() {
|
||||
return g_config->guest_arguments;
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
|
||||
@@ -83,6 +83,8 @@ struct ConfigOptions {
|
||||
bool red_zone_protection_enabled = false;
|
||||
#endif
|
||||
Keymap keymap;
|
||||
// Ordinary boots need a non-null argv[0]; --guest-exec supplies the caller's arguments.
|
||||
std::vector<std::string> guest_arguments {""};
|
||||
};
|
||||
|
||||
void Load(const ConfigOptions& cfg);
|
||||
@@ -136,6 +138,7 @@ bool RedZoneProtectionEnabled();
|
||||
#endif
|
||||
|
||||
const Keymap& GetKeymap();
|
||||
const std::vector<std::string>& GetGuestArguments();
|
||||
|
||||
} // namespace Config
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ struct RunOptions {
|
||||
};
|
||||
|
||||
void Run(const RunOptions& options);
|
||||
int LoadExec(const char* path, const char* const argv[]);
|
||||
|
||||
} // namespace Emulator
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ struct GraphicContext {
|
||||
// bool fp64_denorm_preserve = false; // Temporarily disabled.
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool provoking_vertex_last_enabled = false;
|
||||
bool depth_color_copy_enabled = false;
|
||||
bool supports_block_texel_view = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
|
||||
@@ -394,26 +394,20 @@ static void RcPrint(const char* func, const HW::RenderControl& c) {
|
||||
|
||||
LOGF("\t depth_clear_enable = %s\n"
|
||||
"\t stencil_clear_enable = %s\n"
|
||||
"\t copy_depth_to_color = %s\n"
|
||||
"\t copy_stencil_to_color = %s\n"
|
||||
"\t resummarize_enable = %s\n"
|
||||
"\t stencil_compress_disable = %s\n"
|
||||
"\t depth_compress_disable = %s\n"
|
||||
"\t copy_centroid = %s\n"
|
||||
"\t copy_sample = %" PRIu8 "\n",
|
||||
c.depth_clear_enable ? "true" : "false", c.stencil_clear_enable ? "true" : "false",
|
||||
c.copy_depth_to_color ? "true" : "false", c.copy_stencil_to_color ? "true" : "false",
|
||||
c.resummarize_enable ? "true" : "false", c.stencil_compress_disable ? "true" : "false",
|
||||
c.depth_compress_disable ? "true" : "false", c.copy_centroid ? "true" : "false",
|
||||
c.copy_sample);
|
||||
}
|
||||
|
||||
static void RcCheck(const HW::RenderControl& c) {
|
||||
// EXIT_NOT_IMPLEMENTED(c.depth_clear_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencil_clear_enable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.stencil_compress_disable != false);
|
||||
// EXIT_NOT_IMPLEMENTED(c.depth_compress_disable != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.copy_centroid != false);
|
||||
EXIT_NOT_IMPLEMENTED(c.copy_sample != 0);
|
||||
}
|
||||
|
||||
static void McPrint(const char* func, const HW::ModeControl& c) {
|
||||
LOGF("%s\n", func);
|
||||
|
||||
@@ -869,9 +863,6 @@ void hw_check(const CommandBuffer& buffer) {
|
||||
VpCheck(vp, smc);
|
||||
log_phase("clip");
|
||||
ClipCheck(c);
|
||||
log_phase("rc");
|
||||
RcCheck(rc);
|
||||
log_phase("depth");
|
||||
log_phase("mode");
|
||||
McCheck(mc);
|
||||
log_phase("blend");
|
||||
|
||||
@@ -298,13 +298,20 @@ void RenderExecutor::ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepth
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (rc.copy_depth_to_color || rc.copy_stencil_to_color || rc.copy_centroid ||
|
||||
rc.copy_sample != 0 || dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
if (rc.copy_stencil_to_color ||
|
||||
dc.zfunc > static_cast<uint8_t>(vk::CompareOp::eAlways) ||
|
||||
(!z.depth_view.depth_write_disable && z.z_write_base_addr != z.z_read_base_addr) ||
|
||||
(has_stencil && !z.depth_view.stencil_write_disable &&
|
||||
z.stencil_write_base_addr != z.stencil_read_base_addr)) {
|
||||
DepthFatal("unsupported depth register state");
|
||||
}
|
||||
if (rc.copy_depth_to_color &&
|
||||
(z.z_info.num_samples != 0 || rc.copy_sample != 0 ||
|
||||
z.z_info.format != Prospero::DepthFormat::kZ32F ||
|
||||
dc.z_enable || dc.z_write_enable || dc.depth_bounds_enable || dc.stencil_enable ||
|
||||
rc.depth_clear_enable || rc.stencil_clear_enable)) {
|
||||
DepthFatal("unsupported depth-to-color copy state");
|
||||
}
|
||||
r.desc = MakeDepthTargetDesc(buffer, z);
|
||||
r.depth_clear_enable = rc.depth_clear_enable;
|
||||
r.depth_load_clear_enable = r.depth_clear_enable;
|
||||
|
||||
@@ -186,6 +186,8 @@ private:
|
||||
bool ignore_target_mask = false, bool exact_format = false);
|
||||
void ResolveRenderDepthTarget(CommandBuffer& buffer, RenderDepthInfo& target);
|
||||
[[nodiscard]] bool DepthStencilCopy(CommandBuffer& buffer);
|
||||
[[nodiscard]] bool CopyDepthToColor(CommandBuffer& buffer, const DrawCallInfo& draw,
|
||||
const DrawEmitInfo& emit, DrawRenderState& state);
|
||||
[[nodiscard]] bool PrepareDrawRenderState(CommandBuffer& buffer,
|
||||
const DrawCallInfo& draw,
|
||||
uint32_t render_target_slice_offset,
|
||||
|
||||
@@ -1029,12 +1029,71 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderExecutor::CopyDepthToColor(CommandBuffer& buffer, const DrawCallInfo& draw,
|
||||
const DrawEmitInfo& emit, DrawRenderState& state) {
|
||||
if (!buffer.GetRegisters().GetRenderControl().copy_depth_to_color) {
|
||||
return false;
|
||||
}
|
||||
auto& ucfg = buffer.GetUserConfig();
|
||||
const auto& hw = buffer.GetRegisters();
|
||||
auto& depth = state.depth_info;
|
||||
auto& color = state.color_info[0];
|
||||
EXIT_NOT_IMPLEMENTED(!m_context.GetGraphics().depth_color_copy_enabled ||
|
||||
!Prospero::IsRectList(ucfg.GetPrimType()) ||
|
||||
draw.IsIndexed() || draw.index_count != 3 || draw.instance_count != 1 ||
|
||||
emit.first_vertex != 0 || emit.first_instance != 0 ||
|
||||
state.color_count != 1 || color.target_slot != 0 || !depth.image_id ||
|
||||
color.desc.info.samples != 1 ||
|
||||
depth.desc.info.extent.width != color.Extent().width ||
|
||||
depth.desc.info.extent.height != color.Extent().height ||
|
||||
depth.desc.view_info.layer_count != 1 ||
|
||||
color.desc.view_info.layer_count != 1 ||
|
||||
hw.GetBlendControl(0).enable || state.ps_input_info.ps_pixel_kill_enable ||
|
||||
state.ps_input_info.ps_depth_export_enable ||
|
||||
state.ps_input_info.ps_sample_mask_export_enable);
|
||||
vk::ImageAspectFlags feedback;
|
||||
const auto rendering = AcquireRenderTargets(buffer, &color, 1, depth, feedback);
|
||||
if (depth.depth_load_clear_enable) {
|
||||
m_context.GetCommandScheduler().BeginRendering(rendering);
|
||||
}
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
auto& source = cache.GetImage(depth.image_id);
|
||||
auto& destination = cache.GetImage(color.image_id);
|
||||
EXIT_NOT_IMPLEMENTED((source.backing.format != vk::Format::eD32Sfloat &&
|
||||
source.backing.format != vk::Format::eD32SfloatS8Uint) ||
|
||||
destination.backing.format != vk::Format::eR32Sfloat);
|
||||
const auto rect = calc_final_scissor(hw.GetScreenViewport(), hw.GetScanModeControl(),
|
||||
{rendering.width, rendering.height}, 0);
|
||||
if (rect.right > rect.left && rect.bottom > rect.top) {
|
||||
const vk::ImageCopy region {
|
||||
{vk::ImageAspectFlagBits::eDepth, depth.desc.view_info.base_level,
|
||||
depth.desc.view_info.base_layer, 1}, {rect.left, rect.top, 0},
|
||||
{vk::ImageAspectFlagBits::eColor, color.desc.view_info.base_level,
|
||||
color.desc.view_info.base_layer, 1}, {rect.left, rect.top, 0},
|
||||
{static_cast<uint32_t>(rect.right - rect.left),
|
||||
static_cast<uint32_t>(rect.bottom - rect.top), 1}};
|
||||
m_context.GetCommandScheduler().EndRendering();
|
||||
const auto command = buffer.Handle();
|
||||
source.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
|
||||
{}, command);
|
||||
destination.Transit(vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::AccessFlagBits2::eTransferWrite, {}, command);
|
||||
command.copyImage(source.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
destination.backing.image, vk::ImageLayout::eTransferDstOptimal,
|
||||
1, ®ion);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buffer,
|
||||
const DrawCallInfo& draw, DrawRenderState& state,
|
||||
vk::PrimitiveTopology topology, const DrawEmitInfo& emit,
|
||||
const DrawIndexBufferSource& index_source,
|
||||
bool primitive_restart_enable) {
|
||||
auto& ucfg = buffer.GetUserConfig();
|
||||
if (CopyDepthToColor(buffer, draw, emit, state)) {
|
||||
return;
|
||||
}
|
||||
const auto vertex_stages =
|
||||
std::span {state.vertex_info.data(), state.programs.VertexStageCount()};
|
||||
const bool mesh_active = state.vertex_info[0].stage.program->stage == ShaderType::Mesh;
|
||||
|
||||
@@ -478,6 +478,13 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
provoking_vertex.pNext = supported_features2.pNext;
|
||||
supported_features2.pNext = &provoking_vertex;
|
||||
}
|
||||
const bool maintenance8_extension =
|
||||
HasExtension(device_extensions, VK_KHR_MAINTENANCE_8_EXTENSION_NAME);
|
||||
vk::PhysicalDeviceMaintenance8FeaturesKHR maintenance8 {};
|
||||
if (maintenance8_extension) {
|
||||
maintenance8.pNext = supported_features2.pNext;
|
||||
supported_features2.pNext = &maintenance8;
|
||||
}
|
||||
const bool image_atomic_int64_extension =
|
||||
HasExtension(device_extensions, VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME);
|
||||
vk::PhysicalDeviceShaderImageAtomicInt64FeaturesEXT image_atomic_int64 {};
|
||||
@@ -542,6 +549,9 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
graphics.attachment_feedback_loop_enabled =
|
||||
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
|
||||
feedback_dynamic.attachmentFeedbackLoopDynamicState;
|
||||
graphics.depth_color_copy_enabled = maintenance8_extension && maintenance8.maintenance8;
|
||||
LOGF("Vulkan depth/color copy support: %s\n",
|
||||
graphics.depth_color_copy_enabled ? "true" : "false");
|
||||
LOGF("Vulkan depth feedback support: %s\n",
|
||||
graphics.attachment_feedback_loop_enabled ? "true" : "false");
|
||||
if (graphics.mesh_shader_enabled) {
|
||||
@@ -638,6 +648,10 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
||||
image_atomic_int64.sparseImageInt64Atomics = VK_FALSE;
|
||||
create_info.pNext = &image_atomic_int64;
|
||||
}
|
||||
if (graphics.depth_color_copy_enabled) {
|
||||
maintenance8.pNext = const_cast<void*>(create_info.pNext);
|
||||
create_info.pNext = &maintenance8;
|
||||
}
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
|
||||
@@ -1002,6 +1016,7 @@ void WindowContext::CreateVulkan() {
|
||||
graphic_ctx.memory_budget_ext_enabled = true;
|
||||
}
|
||||
for (const auto* extension: {VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
|
||||
VK_KHR_MAINTENANCE_8_EXTENSION_NAME,
|
||||
VK_EXT_SHADER_IMAGE_ATOMIC_INT64_EXTENSION_NAME,
|
||||
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME,
|
||||
VK_EXT_MESH_SHADER_EXTENSION_NAME,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "graphics/shader/recompiler/BufferFormat.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <unordered_map>
|
||||
@@ -99,6 +100,9 @@ bool EquivalentValue(const ResourcePlan& program, Value left, Value right,
|
||||
|
||||
} // namespace
|
||||
|
||||
ResourcePlan::ResourcePlan() = default;
|
||||
ResourcePlan::ResourcePlan(ResourcePlan&&) noexcept = default;
|
||||
|
||||
ResourcePlan::~ResourcePlan() {
|
||||
for (auto& inst: value_storage) {
|
||||
inst.Invalidate();
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Xbyak {
|
||||
class CodeGenerator;
|
||||
}
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
enum class ResourceKind {
|
||||
@@ -573,12 +577,12 @@ struct ResourcePlan {
|
||||
uint64_t generation = 0;
|
||||
};
|
||||
|
||||
ResourcePlan() = default;
|
||||
ResourcePlan();
|
||||
~ResourcePlan();
|
||||
|
||||
ResourcePlan(const ResourcePlan&) = delete;
|
||||
ResourcePlan& operator=(const ResourcePlan&) = delete;
|
||||
ResourcePlan(ResourcePlan&&) noexcept = default;
|
||||
ResourcePlan(ResourcePlan&&) noexcept;
|
||||
ResourcePlan& operator=(ResourcePlan&& other) noexcept;
|
||||
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
@@ -597,6 +601,7 @@ struct ResourcePlan {
|
||||
bool resource_tracking_complete = false;
|
||||
ShaderInfo info;
|
||||
UniformFillPlan uniform_fill;
|
||||
std::unique_ptr<Xbyak::CodeGenerator> srt_code;
|
||||
// GPU-thread scratch for nested clean/EXEC memos, activity and material keys.
|
||||
mutable std::deque<EvaluationContext> evaluation_contexts;
|
||||
mutable uint32_t evaluation_value_count = 0;
|
||||
|
||||
@@ -1064,6 +1064,7 @@ ResourcePlan ExtractResourcePlan(const Program& program) {
|
||||
}
|
||||
if (capture_indirect_reads || !plan.source_reads.empty())
|
||||
plan.resource_tracking_complete &= !program.has_address_writes;
|
||||
CompileSrtWalker(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
@@ -359,6 +364,131 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
void CompileSrtWalker(ResourcePlan& program) {
|
||||
if (!program.resource_tracking_complete || !program.srt_plan_complete ||
|
||||
program.capture_specialization_reads || !program.control_flow.empty() ||
|
||||
program.srt_reads.empty()) return;
|
||||
|
||||
using Reads = std::map<int32_t, const SrtRead*>;
|
||||
std::map<uint32_t, Reads> roots;
|
||||
std::unordered_map<const Inst*, Reads> pointer_uses;
|
||||
std::unordered_map<const Inst*, int32_t> offsets;
|
||||
for (const auto& read: program.srt_reads) {
|
||||
const auto* inst = read.value.Resolve().TryInstruction();
|
||||
if (inst == nullptr || inst->GetOpcode() != ValueOpcode::LoadAddressU32 ||
|
||||
inst->NumArgs() < 2 || !IsRawRead(program, *inst) ||
|
||||
read.flat_offset >= program.srt_reads.size()) return;
|
||||
const auto offset = inst->Arg(1).Resolve();
|
||||
if (!offset.IsImmediate() || offset.GetType() != Type::U32) return;
|
||||
const auto& memory = program.memory_info[inst->Flags<SrtReadFlags>().index];
|
||||
const auto relative =
|
||||
(static_cast<int64_t>(static_cast<int32_t>(memory.offset)) & ~int64_t {3}) +
|
||||
static_cast<int64_t>(offset.U32() & ~3u);
|
||||
if (relative < INT32_MIN || relative > INT32_MAX ||
|
||||
!offsets.emplace(inst, static_cast<int32_t>(relative)).second) return;
|
||||
}
|
||||
uint32_t user_data_count = 0;
|
||||
for (const auto& read: program.srt_reads) {
|
||||
const auto* inst = read.value.ResolveInstruction();
|
||||
const auto* handle = inst->Arg(0).Resolve().TryInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != ValueOpcode::GetAddressResource ||
|
||||
handle->NumArgs() != 2) return;
|
||||
const auto* low = handle->Arg(0).Resolve().TryInstruction();
|
||||
const auto* high = handle->Arg(1).Resolve().TryInstruction();
|
||||
if (low == nullptr || high == nullptr) return;
|
||||
Reads* uses = nullptr;
|
||||
if (low->GetOpcode() == ValueOpcode::GetUserData &&
|
||||
high->GetOpcode() == ValueOpcode::GetUserData && low->NumArgs() == 1 &&
|
||||
high->NumArgs() == 1 && low->Arg(0).GetType() == Type::ScalarReg &&
|
||||
high->Arg(0).GetType() == Type::ScalarReg) {
|
||||
const auto reg = RegIndex(low->Arg(0).ScalarRegister());
|
||||
if (reg < program.user_data_base ||
|
||||
RegIndex(high->Arg(0).ScalarRegister()) != reg + 1u) return;
|
||||
const auto base = reg - program.user_data_base;
|
||||
user_data_count = std::max(user_data_count, base + 2u);
|
||||
uses = &roots[base];
|
||||
} else {
|
||||
const auto lo = offsets.find(low);
|
||||
const auto hi = offsets.find(high);
|
||||
if (lo == offsets.end() || hi == offsets.end() ||
|
||||
static_cast<int64_t>(lo->second) + 4 != hi->second ||
|
||||
!EquivalentValue(program, low->Arg(0), high->Arg(0))) return;
|
||||
uses = &pointer_uses[low];
|
||||
}
|
||||
if (!uses->emplace(offsets.at(inst), &read).second) return;
|
||||
}
|
||||
std::unordered_set<const Inst*> visited;
|
||||
const auto reachable = [&](auto&& self, const Reads& reads) -> bool {
|
||||
for (const auto& [offset, read]: reads) {
|
||||
const auto* inst = read->value.ResolveInstruction();
|
||||
const auto child = pointer_uses.find(inst);
|
||||
if (child != pointer_uses.end() &&
|
||||
(!visited.insert(inst).second || !self(self, child->second))) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
for (const auto& [reg, reads]: roots) {
|
||||
if (!reachable(reachable, reads)) return;
|
||||
}
|
||||
if (visited.size() != pointer_uses.size()) return;
|
||||
|
||||
program.srt_code = std::make_unique<Xbyak::CodeGenerator>(4096, Xbyak::AutoGrow);
|
||||
auto& code = *program.srt_code;
|
||||
Xbyak::Label failed, finished;
|
||||
code.push(code.rbp);
|
||||
code.mov(code.rbp, code.rsp);
|
||||
code.cmp(code.r8, user_data_count);
|
||||
code.jb(failed, Xbyak::CodeGenerator::T_NEAR);
|
||||
code.mov(code.r9, AddressMask & ~uint64_t {3});
|
||||
// Follow shadPS4's VisitPointer: copy one table level in offset order, then
|
||||
// descend through its child pointers. Seed the descriptor evaluator's memo.
|
||||
const auto visit = [&](auto&& self, int32_t pointer_offset, const Reads& reads) -> void {
|
||||
code.push(code.rdi);
|
||||
code.mov(code.rdi, code.qword[code.rdi + pointer_offset]);
|
||||
code.and_(code.rdi, code.r9);
|
||||
const auto first = reads.begin()->first;
|
||||
const auto last = reads.rbegin()->first;
|
||||
if (first < 0) {
|
||||
code.mov(code.r10, -static_cast<int64_t>(first));
|
||||
code.cmp(code.rdi, code.r10);
|
||||
code.jb(failed, Xbyak::CodeGenerator::T_NEAR);
|
||||
}
|
||||
if (last > 0) {
|
||||
code.mov(code.r10, AddressMask - last);
|
||||
code.cmp(code.rdi, code.r10);
|
||||
code.ja(failed, Xbyak::CodeGenerator::T_NEAR);
|
||||
}
|
||||
for (const auto& [offset, read]: reads) {
|
||||
const auto index = read->value.ResolveInstruction()->EvaluationIndex(program.evaluation_value_count);
|
||||
using Entry = ResourcePlan::EvaluationContext::Entry;
|
||||
const auto slot = code.rdx + index * sizeof(Entry);
|
||||
code.mov(code.r10d, code.dword[code.rdi + offset]);
|
||||
code.mov(code.dword[code.rsi + read->flat_offset * sizeof(uint32_t)], code.r10d);
|
||||
code.mov(code.qword[slot + offsetof(Entry, value)], code.r10);
|
||||
code.mov(code.qword[slot + offsetof(Entry, generation)], code.rcx);
|
||||
}
|
||||
for (const auto& [offset, read]: reads) {
|
||||
const auto child = pointer_uses.find(read->value.ResolveInstruction());
|
||||
if (child == pointer_uses.end()) continue;
|
||||
self(self, offset, child->second);
|
||||
}
|
||||
code.pop(code.rdi);
|
||||
};
|
||||
for (const auto& [reg, reads]: roots) {
|
||||
visit(visit, reg * sizeof(uint32_t), reads);
|
||||
}
|
||||
code.mov(code.eax, 1);
|
||||
code.jmp(finished, Xbyak::CodeGenerator::T_NEAR);
|
||||
code.L(failed);
|
||||
code.xor_(code.eax, code.eax);
|
||||
code.L(finished);
|
||||
code.mov(code.rsp, code.rbp);
|
||||
code.pop(code.rbp);
|
||||
code.ret();
|
||||
code.readyRE();
|
||||
EXIT_IF(Xbyak::GetError() != 0);
|
||||
}
|
||||
|
||||
SrtWalker::SrtWalker(const ResourcePlan& program, const SrtRuntime& runtime,
|
||||
SrtWalker* clean_evaluator, Value active_mask)
|
||||
: m_program(program), m_runtime(runtime), m_clean_evaluator(clean_evaluator),
|
||||
@@ -954,6 +1084,15 @@ bool SrtWalker::EvaluateDescriptor(uint32_t source, DescriptorValue& result) {
|
||||
|
||||
bool SrtWalker::RefreshFlatBuffer(std::vector<uint32_t>& flat) {
|
||||
if (!m_program.srt_plan_complete) return false;
|
||||
if (m_program.srt_code && m_runtime.read_memory == nullptr) {
|
||||
flat.resize(m_program.srt_reads.size());
|
||||
m_program.active_sources.clear();
|
||||
m_context.values.resize(m_program.evaluation_value_count);
|
||||
using Walker = bool KYTY_SYSV_ABI (*)(const uint32_t*, uint32_t*,
|
||||
ResourcePlan::EvaluationContext::Entry*, uint64_t, size_t);
|
||||
return m_program.srt_code->getCode<Walker>()(m_runtime.user_data.data(), flat.data(),
|
||||
m_context.values.data(), m_context.generation, m_runtime.user_data.size());
|
||||
}
|
||||
const auto refresh = [&](uint32_t slot) {
|
||||
if (slot >= m_program.srt_reads.size()) return false;
|
||||
const auto& read = m_program.srt_reads[slot];
|
||||
|
||||
@@ -45,6 +45,7 @@ enum class RuntimeValueType { Any, Integer, ImmutableInteger };
|
||||
|
||||
bool ValidateRuntimeValue(const ResourcePlan& program, Value value,
|
||||
RuntimeValueType type = RuntimeValueType::Any);
|
||||
void CompileSrtWalker(ResourcePlan& program);
|
||||
// Uses the strict reader for values that affect shader specialization.
|
||||
SrtRuntime CleanRuntime(SrtRuntime runtime);
|
||||
|
||||
|
||||
+14
-104
@@ -259,29 +259,6 @@ struct CommandBuffer {
|
||||
void* user_data;
|
||||
uint32_t reserved_dw;
|
||||
|
||||
void DbgDump() const {
|
||||
if (!Config::GraphicsDebugDumpEnabled() ||
|
||||
Config::GetPrintfDirection() == Config::LogDirection::Silent) {
|
||||
return;
|
||||
}
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
if (log_count.fetch_add(1) >= 64) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGF("\t bottom = 0x%016" PRIx64 "\n"
|
||||
"\t top = 0x%016" PRIx64 "\n"
|
||||
"\t cursor_up = 0x%016" PRIx64 "\n"
|
||||
"\t cursor_down = 0x%016" PRIx64 "\n"
|
||||
"\t callback = 0x%016" PRIx64 "\n"
|
||||
"\t user_data = 0x%016" PRIx64 "\n"
|
||||
"\t reserved_dw = %" PRIu32 "\n",
|
||||
reinterpret_cast<uint64_t>(bottom), reinterpret_cast<uint64_t>(top),
|
||||
reinterpret_cast<uint64_t>(cursor_up), reinterpret_cast<uint64_t>(cursor_down),
|
||||
reinterpret_cast<uint64_t>(callback), reinterpret_cast<uint64_t>(user_data),
|
||||
reserved_dw);
|
||||
}
|
||||
|
||||
[[nodiscard]] KYTY_SYSV_ABI uint64_t GetAvailableSizeDW() const {
|
||||
// Interpret the signed cursor distance as an unsigned 64-bit DWORD count.
|
||||
const auto distance = static_cast<int64_t>(reinterpret_cast<uintptr_t>(cursor_down) -
|
||||
@@ -302,7 +279,6 @@ struct CommandBuffer {
|
||||
"\t command buffer exhausted and has no grow callback: requested = %" PRIu32
|
||||
", remaining = %" PRIu64 ", reserved_dw = %" PRIu32 "\n",
|
||||
num_dw, remaining, reserved_dw);
|
||||
DbgDump();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -312,7 +288,6 @@ struct CommandBuffer {
|
||||
"\t command buffer grow callback failed: requested = %" PRIu32
|
||||
", remaining = %" PRIu64 ", reserved_dw = %" PRIu32 "\n",
|
||||
num_dw, remaining, reserved_dw);
|
||||
DbgDump();
|
||||
return false;
|
||||
}
|
||||
if (GetAvailableSizeDW() < num_dw) {
|
||||
@@ -321,7 +296,6 @@ struct CommandBuffer {
|
||||
"requested = %" PRIu32 ", remaining = %" PRIu64
|
||||
", reserved_dw = %" PRIu32 "\n",
|
||||
num_dw, GetAvailableSizeDW(), reserved_dw);
|
||||
DbgDump();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1598,8 +1572,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbContextStateOp(CommandBuffer* buf, uint32_t operat
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
uint32_t* first = nullptr;
|
||||
const auto append = [&](uint32_t segment_size_dw) {
|
||||
auto* segment = buf->AllocateDW(segment_size_dw);
|
||||
@@ -1902,8 +1874,6 @@ uint32_t* KYTY_SYSV_ABI AgcCbSetShRegisterRangeDirect(CommandBuffer* buf, uint32
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(num_values + 2);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -1945,8 +1915,6 @@ uint32_t* KYTY_SYSV_ABI AgcCbSetShRegistersDirect(CommandBuffer*
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(regs == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
std::vector<ShaderRegister> local_regs(num_regs);
|
||||
for (uint32_t i = 0; i < num_regs; i++) {
|
||||
local_regs[i].offset = regs[i].offset;
|
||||
@@ -2012,8 +1980,6 @@ uint32_t* KYTY_SYSV_ABI AgcCbSetUcRegistersDirect(CommandBuffer*
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(regs == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
// Original implementation stages register values in a temporary DWORD array. It reads each new run only
|
||||
// after the preceding run has been allocated, so preserve that ordering around grow callbacks.
|
||||
std::vector<uint32_t> values(num_regs);
|
||||
@@ -2104,8 +2070,6 @@ uint32_t* KYTY_SYSV_ABI AgcCbReleaseMem(CommandBuffer* buf, uint8_t action, uint
|
||||
data_sel != 5);
|
||||
EXIT_NOT_IMPLEMENTED(interrupt > 4);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(8);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -2158,8 +2122,6 @@ uint32_t* KYTY_SYSV_ABI AgcAcbResetQueue(CommandBuffer* buf, uint32_t op) {
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED((op & ~0x1c2u) != 0);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(2);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2180,8 +2142,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbResetQueue(CommandBuffer* buf, uint32_t op, uint32
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED((op & ~0xfffu) != 0);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(2);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2203,8 +2163,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitUntilSafeForRendering(CommandBuffer* buf,
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(7);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2257,8 +2215,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetWorkloadsActive(CommandBuffer* buf, uint32_t st
|
||||
}
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(WORKLOAD_ACTIVE_PACKET_SIZE_DW);
|
||||
if (cmd == nullptr) {
|
||||
return nullptr;
|
||||
@@ -2294,8 +2250,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetWorkloadComplete(CommandBuffer* buf, uint32_t s
|
||||
}
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(WORKLOAD_COMPLETE_PACKET_SIZE_DW);
|
||||
if (cmd == nullptr) {
|
||||
return nullptr;
|
||||
@@ -2319,8 +2273,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegisterDirect(CommandBuffer* buf, ShaderRegi
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -2340,8 +2292,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegisterDirect(CommandBuffer* buf, ShaderRegi
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -2367,8 +2317,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegisterDirect(CommandBuffer* buf, ShaderRegi
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -2394,8 +2342,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirect(CommandBuffer*
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(RegIndirectPacketSizeDw);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2423,8 +2369,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer*
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(RegIndirectPacketSizeDw);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2453,8 +2397,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegistersIndirect(CommandBuffer*
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(RegIndirectPacketSizeDw);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2475,8 +2417,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexSize(CommandBuffer* buf, uint8_t index_siz
|
||||
index_size, cache_policy);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2496,8 +2436,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexBuffer(CommandBuffer* buf, uint64_t index_
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED((index_addr & 1u) != 0);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2516,8 +2454,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetIndexCount(CommandBuffer* buf, uint32_t index_c
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(2);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2537,8 +2473,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetNumInstances(CommandBuffer* buf, uint32_t num_i
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(2);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -2635,8 +2569,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndex(CommandBuffer* buf, uint32_t index_count
|
||||
EXIT_NOT_IMPLEMENTED(index_addr == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED((reinterpret_cast<uint64_t>(index_addr) & 1u) != 0);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(6);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2678,8 +2610,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexMultiInstanced(CommandBuffer* buf, uint32
|
||||
EXIT_NOT_IMPLEMENTED(object_ids == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED((reinterpret_cast<uint64_t>(index_addr) & 1u) != 0);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(9);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2747,8 +2677,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexAuto(CommandBuffer* buf, uint32_t index_c
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2777,8 +2705,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexOffset(CommandBuffer* buf, uint32_t index
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(5);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2808,8 +2734,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetBaseIndirectArgs(CommandBuffer* buf, uint32_t s
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(4);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2834,8 +2758,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t dat
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(5);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2863,8 +2785,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirect(CommandBuffer* buf, uint32_t data_off
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(5);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -2901,8 +2821,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirectMulti(CommandBuffer* buf,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
constexpr uint32_t draw_packet_size_dw = 10u;
|
||||
constexpr uint32_t total_size_dw =
|
||||
AGC_INTERNAL_DATA_PACKET_SIZE_DW + draw_packet_size_dw +
|
||||
@@ -2963,8 +2881,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirectMulti(CommandBuffer* buf,
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED((count_indirect & ~1u) != 0);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(10);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -3013,8 +2929,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDispatchIndirect(CommandBuffer* buf, uint32_t data
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(3);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -3046,8 +2960,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbEventWrite(CommandBuffer* buf, uint8_t event_type,
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(event_type > 0x3fu);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
const bool addressed_event = ((event_type & 0xfeu) == 0x38u);
|
||||
auto* cmd = buf->AllocateDW(addressed_event ? 4u : 2u);
|
||||
|
||||
@@ -3143,8 +3055,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbAcquireMem(CommandBuffer* buf, uint8_t engine, uin
|
||||
}
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(8);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -3203,8 +3113,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbCondExec(CommandBuffer* buf, const volatile uint32
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(5);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -3376,8 +3284,6 @@ uint32_t* KYTY_SYSV_ABI AgcAcbDispatchIndirect(CommandBuffer* buf,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(4);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -3426,8 +3332,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbStallCommandBufferParser(CommandBuffer* buf) {
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(2);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
@@ -3714,6 +3618,20 @@ int KYTY_SYSV_ABI AgcSetPacketPredication(uint32_t* packet, uint32_t predication
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AgcSetNop(uint32_t* packet) {
|
||||
PRINT_NAME();
|
||||
|
||||
const auto size = AgcGetPacketSize(packet);
|
||||
|
||||
packet[0] = (packet[0] & 0xffff0003u) | (Pm4::IT_NOP << 8u);
|
||||
// Prevent the disabled payload from being treated as an executable marker.
|
||||
if (size > 1 && (packet[1] & 0xffff0000u) == 0x68750000u) {
|
||||
packet[1] &= 0xffffu;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI AgcSetRangePredication(uint32_t* start, const volatile uint32_t* end,
|
||||
uint32_t predication) {
|
||||
PRINT_NAME();
|
||||
@@ -3833,8 +3751,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWriteData(CommandBuffer* buf, uint8_t dst, uint8_t
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(4 + num_dwords);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -3887,8 +3803,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbGetLodStats(CommandBuffer* buf, uint8_t cache_poli
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(5);
|
||||
|
||||
if (cmd == nullptr) {
|
||||
@@ -4091,8 +4005,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8
|
||||
cache_policy);
|
||||
}
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto address_value = reinterpret_cast<uint64_t>(address);
|
||||
bool wait32 = (size == 0);
|
||||
auto poll = wait_reg_mem_poll_cycles_to_packet(poll_cycles);
|
||||
@@ -4222,8 +4134,6 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetFlip(CommandBuffer* buf, uint32_t video_out_han
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(buf == nullptr);
|
||||
|
||||
buf->DbgDump();
|
||||
|
||||
auto* cmd = buf->AllocateDW(6);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(cmd == nullptr);
|
||||
|
||||
@@ -270,6 +270,7 @@ uint32_t KYTY_SYSV_ABI AgcGetPacketSize(uint32_t* packet);
|
||||
int KYTY_SYSV_ABI AgcSetPacketPredication(uint32_t* packet, uint32_t predication);
|
||||
int KYTY_SYSV_ABI AgcSetRangePredication(uint32_t* start, const volatile uint32_t* end,
|
||||
uint32_t predication);
|
||||
int KYTY_SYSV_ABI AgcSetNop(uint32_t* packet);
|
||||
int KYTY_SYSV_ABI AgcRewindPatchSetRewindState(uint32_t* cmd, uint8_t state);
|
||||
int KYTY_SYSV_ABI AgcCondExecPatchSetEnd(uint32_t* cmd, const volatile uint32_t* buffer);
|
||||
int KYTY_SYSV_ABI AgcCondExecPatchSetCommandAddress(uint32_t* cmd,
|
||||
|
||||
@@ -143,6 +143,7 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("Lkf86B98qPc", Gen5::AgcGetPacketSize);
|
||||
LIB_FUNC("w6Dj1VJt5qY", Gen5::AgcSetPacketPredication);
|
||||
LIB_FUNC("n8vgpaQg6dA", Gen5::AgcSetRangePredication);
|
||||
LIB_FUNC("K2mciNVxUCE", Gen5::AgcSetNop);
|
||||
LIB_FUNC("i1jyy49AjXU", Gen5::AgcDcbWriteData);
|
||||
LIB_FUNC("p9tI+yTvx68", Gen5::AgcDcbWriteDataGetSize);
|
||||
LIB_FUNC("vuSXe69VILM", Gen5::AgcDcbGetLodStats);
|
||||
|
||||
+2
-8
@@ -54,11 +54,7 @@ struct CContext {
|
||||
std::list<atexit_func_t> atexit;
|
||||
};
|
||||
|
||||
struct InitEnvParams {
|
||||
int argc;
|
||||
uint32_t pad;
|
||||
const char* argv[3];
|
||||
};
|
||||
using InitEnvParams = Loader::EntryParams;
|
||||
|
||||
static int g_argc = 0;
|
||||
static const char* const* g_argv = nullptr;
|
||||
@@ -271,9 +267,7 @@ static KYTY_SYSV_ABI void init_env(const InitEnvParams* params) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int argv_capacity = static_cast<int>(sizeof(params->argv) / sizeof(params->argv[0]));
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(params->argc < 0 || params->argc >= argv_capacity);
|
||||
EXIT_NOT_IMPLEMENTED(params->argc < 0);
|
||||
|
||||
g_argc = params->argc;
|
||||
g_argv = params->argv;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "emulator.h"
|
||||
#include "graphics/presentation/systemOverlay.h"
|
||||
#include "libs/dialog.h"
|
||||
#include "libs/errno.h"
|
||||
@@ -99,6 +100,11 @@ static int KYTY_SYSV_ABI SystemServiceHideSplashScreen() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI SystemServiceLoadExec(const char* path, const char* const argv[]) {
|
||||
PRINT_NAME();
|
||||
return Emulator::LoadExec(path, argv);
|
||||
}
|
||||
|
||||
static int KYTY_SYSV_ABI SystemServiceParamGetInt(int param_id, int* value) {
|
||||
PRINT_NAME();
|
||||
|
||||
@@ -559,6 +565,7 @@ LIB_DEFINE(InitSystemGesture_1) {
|
||||
} // namespace SystemGesture
|
||||
|
||||
LIB_DEFINE(InitSystemService_1) {
|
||||
LIB_FUNC("JoBqSQt1yyA", SystemService::SystemServiceLoadExec);
|
||||
LIB_FUNC("Vo5V8KAwCmk", SystemService::SystemServiceHideSplashScreen);
|
||||
LIB_FUNC("fZo48un7LK4", SystemService::SystemServiceParamGetInt);
|
||||
LIB_FUNC("SsC-m-S9JTA", SystemService::SystemServiceParamGetString);
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "common/file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "emulator.h"
|
||||
#include "kernel/fileSystem.h"
|
||||
#include "libs/errno.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__)
|
||||
#include <linux/close_range.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
static std::wstring QuoteArgument(std::wstring_view argument) {
|
||||
std::wstring quoted = L"\"";
|
||||
size_t slashes = 0;
|
||||
for (wchar_t c: argument) {
|
||||
if (c == L'\\') {
|
||||
++slashes;
|
||||
continue;
|
||||
}
|
||||
quoted.append(c == L'"' ? slashes * 2 + 1 : slashes, L'\\');
|
||||
quoted += c;
|
||||
slashes = 0;
|
||||
}
|
||||
quoted.append(slashes * 2, L'\\');
|
||||
return quoted + L'"';
|
||||
}
|
||||
#endif
|
||||
|
||||
int LoadExec(const char* path, const char* const argv[]) {
|
||||
using namespace Libs::SystemService;
|
||||
if (path == nullptr || !std::string_view(path).starts_with("/app0/") ||
|
||||
!Common::File::IsFileExisting(Libs::LibKernel::FileSystem::GetRealFilename(path))) {
|
||||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS || defined(__linux__)
|
||||
std::vector<std::string> arguments {"--guest-exec", path};
|
||||
size_t bytes = 0;
|
||||
for (size_t i = 0; argv != nullptr && argv[i] != nullptr; ++i) {
|
||||
const auto size = strnlen(argv[i], 4096 - bytes) + 1;
|
||||
if (size > 4096 - bytes) {
|
||||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
}
|
||||
bytes += size;
|
||||
arguments.emplace_back(argv[i], size - 1);
|
||||
}
|
||||
LOGF("LoadExec: %s\n", path);
|
||||
Log::Flush();
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
wchar_t executable[32768] {};
|
||||
const auto length = GetModuleFileNameW(nullptr, executable, 32768);
|
||||
if (length == 0 || length >= 32768) {
|
||||
return SYSTEM_SERVICE_ERROR_INTERNAL;
|
||||
}
|
||||
if (__wargv == nullptr) {
|
||||
return SYSTEM_SERVICE_ERROR_INTERNAL;
|
||||
}
|
||||
std::wstring command = QuoteArgument(executable);
|
||||
for (int i = 1; i < __argc && __wargv[i] != nullptr; ++i) {
|
||||
if (std::wstring_view(__wargv[i]) == L"--guest-exec") {
|
||||
break;
|
||||
}
|
||||
command += L" " + QuoteArgument(__wargv[i]);
|
||||
}
|
||||
for (const auto& argument: arguments) {
|
||||
const int size =
|
||||
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argument.c_str(), -1, nullptr, 0);
|
||||
if (size == 0) {
|
||||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
}
|
||||
std::wstring wide(size, L'\0');
|
||||
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, argument.c_str(), -1, wide.data(),
|
||||
size) == 0) {
|
||||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
}
|
||||
wide.resize(size - 1);
|
||||
command += L" " + QuoteArgument(wide);
|
||||
}
|
||||
STARTUPINFOW startup {};
|
||||
startup.cb = sizeof(startup);
|
||||
PROCESS_INFORMATION process {};
|
||||
if (!CreateProcessW(executable, command.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW,
|
||||
nullptr, nullptr, &startup, &process)) {
|
||||
LOGF("LoadExec: process creation failed: %lu\n", GetLastError());
|
||||
return SYSTEM_SERVICE_ERROR_INTERNAL;
|
||||
}
|
||||
CloseHandle(process.hThread);
|
||||
CloseHandle(process.hProcess);
|
||||
std::fflush(nullptr);
|
||||
std::quick_exit(0);
|
||||
#else
|
||||
std::ifstream command("/proc/self/cmdline", std::ios::binary);
|
||||
std::vector<std::string> host_arguments;
|
||||
for (std::string argument;
|
||||
std::getline(command, argument, '\0') && argument != "--guest-exec";) {
|
||||
host_arguments.push_back(std::move(argument));
|
||||
}
|
||||
if (host_arguments.empty()) {
|
||||
return SYSTEM_SERVICE_ERROR_INTERNAL;
|
||||
}
|
||||
host_arguments.insert(host_arguments.end(), arguments.begin(), arguments.end());
|
||||
std::vector<char*> host_argv;
|
||||
for (auto& argument: host_arguments) {
|
||||
host_argv.push_back(argument.data());
|
||||
}
|
||||
host_argv.push_back(nullptr);
|
||||
if (close_range(3, ~0u, CLOSE_RANGE_UNSHARE | CLOSE_RANGE_CLOEXEC) == 0) {
|
||||
execvp(host_argv[0], host_argv.data());
|
||||
}
|
||||
LOGF("LoadExec failed: %s\n", std::strerror(errno));
|
||||
#endif
|
||||
return SYSTEM_SERVICE_ERROR_INTERNAL;
|
||||
#else
|
||||
return SYSTEM_SERVICE_ERROR_UNAVAILABLE;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Emulator
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -88,16 +89,6 @@ ThreadLocalStorage::~ThreadLocalStorage() {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct EntryParams {
|
||||
int argc;
|
||||
uint32_t pad;
|
||||
const char* argv[3];
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
using atexit_func_t = KYTY_SYSV_ABI void (*)();
|
||||
using entry_func_t = KYTY_SYSV_ABI void (*)(EntryParams* params, atexit_func_t atexit_func);
|
||||
using module_ini_fini_func_t = KYTY_SYSV_ABI int (*)(size_t args, const void* argp,
|
||||
@@ -1283,11 +1274,16 @@ void RuntimeLinker::Execute(const std::filesystem::path& game_patch) {
|
||||
LOGF_COLOR(Log::Color::BrightYellow, "---\n--- Execute: %s\n---\n", "Main");
|
||||
|
||||
if (auto entry = GetEntry(); entry != 0) {
|
||||
const auto& arguments = Config::GetGuestArguments();
|
||||
const auto params_size = offsetof(EntryParams, argv) +
|
||||
(arguments.size() + 2) * sizeof(const char*);
|
||||
auto* params = reinterpret_cast<EntryParams*>(
|
||||
(reinterpret_cast<uintptr_t>(main_stack_top) - 0x100u) & ~static_cast<uintptr_t>(0x0f));
|
||||
std::memset(params, 0, sizeof(EntryParams));
|
||||
params->argc = 1;
|
||||
params->argv[0] = "KytyEmu";
|
||||
(reinterpret_cast<uintptr_t>(main_stack_top) - params_size - 0x100u) &
|
||||
~static_cast<uintptr_t>(0x0f));
|
||||
std::memset(params, 0, params_size);
|
||||
for (const auto& argument: arguments) {
|
||||
params->argv[params->argc++] = argument.c_str();
|
||||
}
|
||||
|
||||
LOGF("stack_addr = %" PRIx64 "\n", reinterpret_cast<uint64_t>(params));
|
||||
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
|
||||
namespace Loader {
|
||||
|
||||
struct EntryParams {
|
||||
int argc;
|
||||
uint32_t pad;
|
||||
const char* argv[1];
|
||||
};
|
||||
|
||||
class Elf64;
|
||||
struct Elf64_Sym;
|
||||
struct Elf64_Rela;
|
||||
|
||||
@@ -360,10 +360,12 @@ void PlayGo::Open(const std::filesystem::path& file_name) {
|
||||
}
|
||||
|
||||
uint32_t magic1 = 0;
|
||||
constexpr uint32_t PLGO_MAGIC = 0x6f676c70;
|
||||
constexpr uint32_t PLGX_MAGIC = 0x78676c70;
|
||||
|
||||
m_f.Read(&magic1, 4);
|
||||
|
||||
if (magic1 != 0x6f676c70) {
|
||||
if (magic1 != PLGO_MAGIC && magic1 != PLGX_MAGIC) {
|
||||
LOGF("invalid file: magic1 = %08" PRIx32 "\n", magic1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -300,6 +300,10 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
||||
value.c_str());
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--guest-exec") {
|
||||
options.elf = Common::PathFromUtf8(Common::FixFilenameSlash(value));
|
||||
options.config.guest_arguments.assign(argv + i + 1, argv + argc);
|
||||
break;
|
||||
} else if (arg == "--game-patch") {
|
||||
if (!options.game_patch.empty()) {
|
||||
::printf("--game-patch can only be specified once\n");
|
||||
|
||||
@@ -168,6 +168,117 @@ void TestMappedSrtUsesDirectReaderByDefault() {
|
||||
"cache rematerialization did not use the direct reader by default");
|
||||
}
|
||||
|
||||
void TestNativeNestedSrtRefresh() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
std::array<uint32_t, 3> first{0u, 0x12345678u, 0xabcdef01u};
|
||||
std::array<uint32_t, 3> second{0u, 0x87654321u, 0x10fedcbau};
|
||||
const auto pointer_words = [](const void *pointer) {
|
||||
const auto address = reinterpret_cast<uint64_t>(pointer);
|
||||
return std::array<uint32_t, 2>{static_cast<uint32_t>(address) | 3u,
|
||||
static_cast<uint32_t>(address >> 32u) | 0xabcd0000u};
|
||||
};
|
||||
auto table = pointer_words(first.data());
|
||||
const auto user_data = pointer_words(table.data());
|
||||
auto program = SrtProgram(0u);
|
||||
program.user_data_base = 4;
|
||||
program.user_data_count = 2;
|
||||
auto &block = *program.blocks[0];
|
||||
const auto emit = [&](ValueOpcode opcode, std::initializer_list<Value> args) {
|
||||
return Value(&block.AppendNewInst(opcode, args));
|
||||
};
|
||||
const auto low = emit(ValueOpcode::GetUserData, {Value(ScalarReg(4))});
|
||||
const auto high = emit(ValueOpcode::GetUserData, {Value(ScalarReg(5))});
|
||||
const auto root = emit(ValueOpcode::GetAddressResource, {low, high});
|
||||
const auto read = [&](Value address, uint32_t offset) {
|
||||
const auto value = emit(ValueOpcode::LoadAddressU32,
|
||||
{address, Value(offset), Value(0u), Value(true)});
|
||||
value.Instruction()->SetFlags(MemoryFlags{.index = 0});
|
||||
program.srt_reads.push_back(
|
||||
{value, static_cast<uint32_t>(program.srt_reads.size())});
|
||||
return value;
|
||||
};
|
||||
const auto pointer_low = read(root, 0u);
|
||||
const auto pointer_high = read(root, 4u);
|
||||
auto *leaf = program.srt_reads[0].value.Instruction();
|
||||
leaf->Arg(0).Instruction()->SetArg(0, pointer_low);
|
||||
leaf->Arg(0).Instruction()->SetArg(1, pointer_high);
|
||||
leaf->SetArg(1, Value(7u));
|
||||
read(leaf->Arg(0), 11u);
|
||||
auto plan = ExtractResourcePlan(program);
|
||||
Check(plan.srt_code != nullptr, "nested user-data tree did not compile an x86 walker");
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceSpecialization specialization;
|
||||
const SrtRuntime runtime{.user_data = user_data};
|
||||
{
|
||||
SrtWalker walker(plan, runtime);
|
||||
Check(walker.RefreshFlatBuffer(snapshot.flattened_srt),
|
||||
"native SRT refresh failed");
|
||||
const auto loaded = first[1];
|
||||
first[1] ^= 0xffffffffu;
|
||||
uint32_t value = 0;
|
||||
Check(walker.Evaluate(plan.srt_reads[0].value, value) && value == loaded,
|
||||
"descriptor evaluation reloaded a value already copied by the native walker");
|
||||
first[1] = loaded;
|
||||
}
|
||||
for (const auto *data : {&first, &second}) {
|
||||
table = pointer_words(data->data());
|
||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||
snapshot.flattened_srt ==
|
||||
std::vector<uint32_t>{(*data)[1], table[0], table[1], (*data)[2]},
|
||||
"native SRT refresh lost pointer masking, alignment, or current values");
|
||||
}
|
||||
Check(!MaterializeResources(plan, {.user_data = std::span(user_data).first(1)},
|
||||
snapshot, specialization),
|
||||
"native SRT walk accepted truncated user data");
|
||||
const std::array<uint32_t, 2> fake_user_data{0x1003u, 0xabcd0000u};
|
||||
uint32_t reads = 0;
|
||||
const SrtRuntime callback_runtime{
|
||||
.user_data = fake_user_data,
|
||||
.read_memory = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
++*static_cast<uint32_t *>(data);
|
||||
switch (address) {
|
||||
case 0x1000u: words[0] = 0x2003u; break;
|
||||
case 0x1004u: words[0] = 0xabcd0000u; break;
|
||||
case 0x2004u: words[0] = 0x1234u; break;
|
||||
case 0x2008u: words[0] = 0x5678u; break;
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
.userdata = &reads};
|
||||
Check(MaterializeResources(plan, callback_runtime, snapshot, specialization) && reads == 4 &&
|
||||
snapshot.flattened_srt ==
|
||||
std::vector<uint32_t>{0x1234u, 0x2003u, 0xabcd0000u, 0x5678u},
|
||||
"native-eligible SRT bypassed its callback or repeated nested pointer reads");
|
||||
leaf->SetArg(0, root);
|
||||
leaf->SetArg(1, Value(0u));
|
||||
program.srt_reads.resize(1);
|
||||
for (const int32_t offset : {-1, 7}) {
|
||||
program.memory_info[0].offset = static_cast<uint32_t>(offset);
|
||||
auto bounded = ExtractResourcePlan(program);
|
||||
auto base = pointer_words(second.data() + 1);
|
||||
Check(bounded.srt_code != nullptr &&
|
||||
MaterializeResources(bounded, {.user_data = base}, snapshot, specialization) &&
|
||||
snapshot.flattened_srt[0] == second[offset < 0 ? 0 : 2],
|
||||
"native SRT applied a signed immediate offset incorrectly");
|
||||
base = offset < 0 ? std::array<uint32_t, 2>{0u, 0u}
|
||||
: std::array<uint32_t, 2>{0xfffffffcu, 0xffffu};
|
||||
Check(!MaterializeResources(bounded, {.user_data = base}, snapshot, specialization),
|
||||
"native SRT accepted address underflow or 48-bit overflow");
|
||||
}
|
||||
high.Instruction()->SetArg(0, Value(ScalarReg(6)));
|
||||
Check(ExtractResourcePlan(program).srt_code == nullptr,
|
||||
"native SRT accepted nonadjacent user-data pointer words");
|
||||
high.Instruction()->SetArg(0, Value(ScalarReg(5)));
|
||||
program.memory_info[0].offset = 0;
|
||||
program.srt_reads.push_back({pointer_low, 1});
|
||||
program.srt_reads.push_back({pointer_high, 2});
|
||||
leaf->SetArg(0, emit(ValueOpcode::GetAddressResource, {pointer_low, pointer_high}));
|
||||
pointer_high.Instruction()->SetArg(1, Value(8u));
|
||||
Check(ExtractResourcePlan(program).srt_code == nullptr,
|
||||
"native SRT accepted nonadjacent nested pointer words");
|
||||
}
|
||||
|
||||
void TestIntegerRuntimeValueFollowsSrtReads() {
|
||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||
auto plan = SrtProgram(0x10000);
|
||||
@@ -256,6 +367,8 @@ void TestSrtAliasesRetainReadPolicy() {
|
||||
plan.srt_reads[1].value.Instruction()->Flags<SrtReadFlags>().clean == 0u &&
|
||||
plan.srt_reads[2].value.Instruction()->Flags<SrtReadFlags>().clean == 1u,
|
||||
"alias normalization lost read identity or retained discarded EXEC provenance");
|
||||
Check(plan.srt_code == nullptr,
|
||||
"captured strict reads compiled an unused x86 walker");
|
||||
struct Reads { uint32_t strict = 0; uint32_t ordinary = 0; bool dirty = false; } reads;
|
||||
const SrtRuntime runtime{
|
||||
.read_memory = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||
@@ -671,6 +784,7 @@ void DbgExit(int) { std::abort(); }
|
||||
|
||||
int main() {
|
||||
TestMappedSrtUsesDirectReaderByDefault();
|
||||
TestNativeNestedSrtRefresh();
|
||||
TestIntegerRuntimeValueFollowsSrtReads();
|
||||
TestSrtAliasesRetainReadPolicy();
|
||||
TestUniformVectorDescriptorRead();
|
||||
|
||||
@@ -17925,9 +17925,9 @@ public:
|
||||
captured.mip_layout[0].pitch == 512 &&
|
||||
captured.mip_layout[0].height == 512,
|
||||
"R64 BC1 texture lost its compressed blocks or array stride");
|
||||
const auto small =
|
||||
const auto small_array =
|
||||
MakeTilingImage(format, 67, 51, 1, layers, tile, 0, false);
|
||||
check_round_trip("R64 BC1 array", small.data.size, small);
|
||||
check_round_trip("R64 BC1 array", small_array.data.size, small_array);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -42630,6 +42630,43 @@ void CheckPm4IndirectControlFlow(RenderContext &renderer) {
|
||||
Gen5::AgcCbNop(reinterpret_cast<Gen5::CommandBuffer*>(&payload_dcb), 3) == payload.data() &&
|
||||
payload == std::array<uint32_t, 3>{0xc0011000u, 0x12345678u, 0x9abcdef0u},
|
||||
"NOP emitter overwrote the caller's reserved payload");
|
||||
alignas(8) uint64_t released = 0;
|
||||
std::array<uint32_t, 20> disabled_commands{};
|
||||
const auto disabled_write = write(&returned, 99);
|
||||
std::copy(disabled_write.begin(), disabled_write.end(), disabled_commands.begin());
|
||||
disabled_commands[5] = KYTY_PM4(2, Pm4::IT_NUM_INSTANCES, 0);
|
||||
disabled_commands[6] = 0x68750123u;
|
||||
CommandBufferLayout release_dcb{disabled_commands.data() + 7, disabled_commands.data() + 15,
|
||||
disabled_commands.data() + 7, disabled_commands.data() + 15, nullptr, nullptr, 0};
|
||||
auto* release = Gen5::AgcCbReleaseMem(
|
||||
reinterpret_cast<Gen5::CommandBuffer*>(&release_dcb), 0x28, 0, 0, 0,
|
||||
reinterpret_cast<const volatile Gen5::Label*>(&released), 2, 77, 0, 0, 0, 0);
|
||||
const auto enabled_write = write(&selected, 88);
|
||||
std::copy(enabled_write.begin(), enabled_write.end(), disabled_commands.begin() + 15);
|
||||
const auto unpatched_commands = disabled_commands;
|
||||
Require(name, "SetNop packets",
|
||||
release == disabled_commands.data() + 7 &&
|
||||
Gen5::AgcSetNop(disabled_commands.data()) == 0 &&
|
||||
Gen5::AgcSetNop(disabled_commands.data() + 5) == 0 && Gen5::AgcSetNop(release) == 0 &&
|
||||
disabled_commands[0] == ((disabled_write[0] & 0xffff00ffu) | (Pm4::IT_NOP << 8u)) &&
|
||||
disabled_commands[5] == KYTY_PM4(2, Pm4::IT_NOP, Pm4::R_ZERO) &&
|
||||
disabled_commands[6] == 0x123u &&
|
||||
release[0] == KYTY_PM4(8, Pm4::IT_NOP, Pm4::R_ZERO) &&
|
||||
unpatched_commands[7] == KYTY_PM4(8, Pm4::IT_NOP, Pm4::R_RELEASE_MEM) &&
|
||||
Gen5::AgcGetPacketSize(disabled_commands.data()) == disabled_write.size() &&
|
||||
Gen5::AgcGetPacketSize(disabled_commands.data() + 5) == 2 &&
|
||||
Gen5::AgcGetPacketSize(release) == 8 &&
|
||||
std::equal(disabled_commands.begin() + 1, disabled_commands.begin() + 5,
|
||||
unpatched_commands.begin() + 1) &&
|
||||
std::equal(disabled_commands.begin() + 8, disabled_commands.end(),
|
||||
unpatched_commands.begin() + 8),
|
||||
"SetNop changed a packet length, kept its operation or marker, or touched its payload");
|
||||
selected = returned = 0;
|
||||
Pm4Execution disabled_execution;
|
||||
Require(name, "SetNop execution",
|
||||
processor.Process(disabled_execution, disabled_commands) == Pm4ProcessResult::Complete &&
|
||||
returned == 0 && released == 0 && selected == 88,
|
||||
"a disabled packet still executed or hid the packet after it");
|
||||
const auto then_commands = write(&selected, 11);
|
||||
const auto else_commands = write(&selected, 33);
|
||||
const auto branch_suffix = write(&selected, 44);
|
||||
|
||||
Reference in New Issue
Block a user