* [Gpu] Astro Bot performance: 14 -> ~60 fps at the title scene - Defer RELEASE_MEM command-buffer flushes instead of submitting per packet - Resolve indexed indirect draws and dispatches on the GPU - Async buffer readbacks on a dedicated transfer/compute queue - Pool image backings, flip snapshots and tiler resources - One-frame command-stream run-ahead - Lock-free backed-range and image page-owner checks on CPU writes - Scalar value cache with generation-stamped reset; memoized invariant phis - Shader compiler: structured forward-only CFGs with function-scope registers, EXEC-full guard elision, bounded V_MOVREL offsets - Trivial-leaf import dispatch and strchr/strrchr fast paths - SHARPEMU_GUEST_REFRESH_RATE option * [Tests] Fix full-suite flaky tests - Red-zone patcher: when every fixed trampoline candidate is taken, look for any free host range within relative-jump reach (PhysicalVirtualMemory.EnumerateFreeHostRanges); the test places its image in a probed free hole - Kernel memory tests: probe a free PRT-area address instead of the fixed 0x1026C00000; the reservation-extent test only rejects picks inside the reservation - Release checks tolerate another thread reusing the freed range - Allocation tests measure on a dedicated thread and keep the least allocating of several rounds (AllocationMeasurement.SteadyState) * [Gpu] Astro Bot: structured loops, buffer slabs, pad motion - Shader: emit natural loops as structured SPIR-V loops (OpLoopMerge) instead of the block dispatcher, and merge SCC-uniform if/else joins as selects so descriptors picked on a branch stay evaluable per draw. - Shader: decode V_FFBH_I32 and V_MIN3_F16; read the DS GDS bit from bit 17 (GFX10) and route GDS append/consume through shared atomics. - Gpu: sub-allocate small device-local buffers from shared memory slabs. - Pad: report motion data by default (Astro Bot never imports scePadSetMotionSensorState), convert acceleration from m/s^2 to G, and add scePadResetOrientation / scePadSetAngularVelocityDeadbandState. * [CPU] Stop pinning guest threads to host cores by default Guest affinity packed Demon's Souls' 13 spinning BPE job workers onto 10 host lanes and put its renderer next to them at equal priority, so the renderer and lock holders waited out whole scheduler quanta. Intro 10.6 -> 44.7 fps, menus 11.6 -> 47.5 fps. SHARPEMU_GUEST_AFFINITY=1 restores pinning. * [Media] Convert host movie frames to YUV on the decoder thread The render thread converted every decoded Bink frame from BGRA back to YUV 4:2:0 (6.7 ms per frame). The decoder thread now produces the planes and the presenter uploads them as-is. PS Studios logo 33 -> ~50 fps, attract movie 44 -> 60 fps. * [Agc] Accept a zero destination in sceAgcDcbWriteData Demon's Souls emits WRITE_DATA with address 0 into a template and patches it after copying. Returning null made the title patch a relocated null pointer, which wrote a stack-derived value into its label and was then parsed as an unknown NOP marker (GPU FATAL). * [Loader] Patch short red-zone faulting accesses next to branches A faulting access shorter than a jump could not be relocated when followed by a branch or a branch target, so a handled fault let Windows overwrite the red zone. Spans may now end with a direct branch (run after the stack pointer is restored) or start up to three instructions earlier. Fixes a Demon's Souls crash after the new-game intro movie. * [GPU] Astro Bot rendering fixes: compute subgroups, DCC clears, PS inputs, texture MIN_LOD - Compute: pin non-emulated compute pipelines to a 32-lane subgroup (VK_EXT_subgroup_size_control) so wave64 groups other than 64 invocations keep lanes 32..63 active on AMD (striped froxel volume). - Shader translator: 64-bit AND for the wave32 S_*_SAVEEXEC_B64 mask. - PS inputs: slots that read one VS parameter share one host input; a smooth/flat slot next to a V_INTERP_MOV slot is rebuilt from the per-vertex values (black save cards, NaN normals, black props). - Render targets: drop unwritten color targets of depth passes from both the attachments and the pixel outputs, keeping locations aligned. - DCC: materialize pending zero fast clears before shader access so a later attachment bind does not wipe compute writes. - Textures: honour the T# MIN_LOD with VK_EXT_image_view_min_lod so streamed textures never sample unloaded mips (blown-out stadium sky). - Device: enable VK_KHR_maintenance5 for point lists without PointSize. * [Audio][GPU] ATRAC9 independent mono streams, BC storage fallback, 32-bit bit counts - LibAtrac9: decode config header 0x30 streams (PS5 ambisonics), where every channel is an independent mono stream interleaved frame by frame. - Images: when the driver refuses storage usage on a block-compressed image, compute writes go through an uncompressed replacement of the same memory. - SPIR-V: count 64-bit masks with two 32-bit OpBitCount, which Vulkan requires without maintenance9. - Agc: drop the indirect-draw emit debug log; report the draw modifier in the unsupported indirect draw error. * [GPU] Partial texture-pool uploads and cheaper per-draw preparation - A CPU write into a large layered texture (Demon's Souls' 256-layer BC3 pools) re-uploaded the whole image, ~750 ms of GPU detiling in one frame. Each (mip, layer) piece's guest bytes are hashed at upload; a refresh uploads only the pieces that changed. - Device-address preparation touched and checked every registered buffer for each dispatch. Recency touches now run once per retirement tick, and uploads only visit buffers in blocks the tracker does not know to be clean; hot pages stay dirty, so they are still re-uploaded every time. - Resource materialization cache: when the only changed words are ones the flattened table alone reads (per-frame inline constants), only the table is evaluated again. - A full batch no longer splits an open render pass before 512 draws. - Lock-free lookups for shader headers, device format properties and the idle host movie bridge; the SPIR-V dump toggle is read once per program cache. * Demon soul - fix texture * Delete runahead tests * Screcrow fix for nvidia GPU
82 lines
3.2 KiB
C#
82 lines
3.2 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Buffers.Binary;
|
|
using SharpEmu.HLE;
|
|
using SharpEmu.Libs.Agc;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.Agc;
|
|
|
|
public sealed class AgcWriteDataTests
|
|
{
|
|
private const ulong BaseAddress = 0x2_3000_0000;
|
|
private const ulong CommandBufferAddress = BaseAddress + 0x100;
|
|
private const ulong PacketAddress = BaseAddress + 0x400;
|
|
private const ulong DataAddress = BaseAddress + 0x800;
|
|
private const ulong StackAddress = BaseAddress + 0xC00;
|
|
private const ulong LabelAddress = BaseAddress + 0xE00;
|
|
|
|
// Demon's Souls builds WRITE_DATA with a zero address into a template, copies
|
|
// it into the ring and only then patches the destination to its label.
|
|
[Fact]
|
|
public void DcbWriteData_WithZeroDestination_EmitsPacketThatPatchTargets()
|
|
{
|
|
var memory = new FakeCpuMemory(BaseAddress, 0x1000);
|
|
var ctx = new CpuContext(memory, Generation.Gen5);
|
|
WriteUInt64(memory, CommandBufferAddress + 0x10, PacketAddress);
|
|
WriteUInt64(memory, CommandBufferAddress + 0x18, PacketAddress + 0x100);
|
|
WriteUInt32(memory, DataAddress, 0);
|
|
WriteUInt64(memory, StackAddress + 8, 0);
|
|
WriteUInt64(memory, StackAddress + 16, 1);
|
|
WriteUInt32(memory, LabelAddress, 0x5A5A_5A5A);
|
|
|
|
ctx[CpuRegister.Rdi] = CommandBufferAddress;
|
|
ctx[CpuRegister.Rsi] = 4;
|
|
ctx[CpuRegister.Rdx] = 0;
|
|
ctx[CpuRegister.Rcx] = 0;
|
|
ctx[CpuRegister.R8] = DataAddress;
|
|
ctx[CpuRegister.R9] = 1;
|
|
ctx[CpuRegister.Rsp] = StackAddress;
|
|
AgcExports.DcbWriteData(ctx);
|
|
|
|
Assert.Equal(PacketAddress, ctx[CpuRegister.Rax]);
|
|
Assert.Equal(0xC0031054u, ReadUInt32(memory, PacketAddress));
|
|
Assert.Equal(0UL, ReadUInt64(memory, PacketAddress + 8));
|
|
|
|
ctx[CpuRegister.Rdi] = PacketAddress;
|
|
ctx[CpuRegister.Rsi] = LabelAddress;
|
|
AgcExports.WriteDataPatchSetAddressOrOffset(ctx);
|
|
|
|
Assert.Equal(LabelAddress, ReadUInt64(memory, PacketAddress + 8));
|
|
Assert.Equal(0x5A5A_5A5Au, ReadUInt32(memory, LabelAddress));
|
|
}
|
|
|
|
private static uint ReadUInt32(ICpuMemory memory, ulong address)
|
|
{
|
|
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
|
Assert.True(memory.TryRead(address, buffer));
|
|
return BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
|
}
|
|
|
|
private static ulong ReadUInt64(ICpuMemory memory, ulong address)
|
|
{
|
|
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
|
Assert.True(memory.TryRead(address, buffer));
|
|
return BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
|
}
|
|
|
|
private static void WriteUInt32(ICpuMemory memory, ulong address, uint value)
|
|
{
|
|
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
|
Assert.True(memory.TryWrite(address, buffer));
|
|
}
|
|
|
|
private static void WriteUInt64(ICpuMemory memory, ulong address, ulong value)
|
|
{
|
|
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
|
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
|
|
Assert.True(memory.TryWrite(address, buffer));
|
|
}
|
|
}
|