Browse Source
[docs, tools] Add 3rd party links, debug and testing guidelines; add extra tools for maintaining strings, rewrite lanczos generator in perl (#2749)
[docs, tools] Add 3rd party links, debug and testing guidelines; add extra tools for maintaining strings, rewrite lanczos generator in perl (#2749)
RenderDoc checklist inspired by writeup of Charles G. of LunarG Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2749 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>pull/2769/head
committed by
crueter
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
14 changed files with 308 additions and 101 deletions
-
10docs/CrossCompile.md
-
68docs/Debug.md
-
45docs/Development.md
-
4docs/README.md
-
1docs/User.md
-
2docs/build/Android.md
-
32docs/user/Testing.md
-
8docs/user/ThirdParty.md
-
7tools/README.md
-
131tools/dtrace-tool.pl
-
7tools/find-unused-strings.sh
-
40tools/lanczos-gen.pl
-
48tools/lanczos_gen.c
-
6tools/optimize-assets.sh
@ -0,0 +1,10 @@ |
|||
# Cross Compile |
|||
|
|||
## ARM64 |
|||
|
|||
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main. |
|||
|
|||
- Install QEMU: `sudo pkg install qemu` |
|||
- Download Debian 13: `wget https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-13.0.0-arm64-netinst.iso` |
|||
- Create a system disk: `qemu-img create -f qcow2 debian-13-arm64-ci.qcow2 30G` |
|||
- Run the VM: `qemu-system-aarch64 -M virt -m 2G -cpu max -bios /usr/local/share/qemu/edk2-aarch64-code.fd -drive if=none,file=debian-13.0.0-arm64-netinst.iso,format=raw,id=cdrom -device scsi-cd,drive=cdrom -drive if=none,file=debian-13-arm64-ci.qcow2,id=hd0,format=qcow2 -device virtio-blk-device,drive=hd0 -device virtio-gpu-pci -device usb-ehci -device usb-kbd -device intel-hda -device hda-output -nic user,model=virtio-net-pci` |
|||
@ -0,0 +1,68 @@ |
|||
# Debug Guidelines |
|||
|
|||
## Debugging (host code) |
|||
|
|||
Ignoring SIGSEGV when debugging in host: |
|||
|
|||
- **gdb**: `handle all nostop pass`. |
|||
- **lldb**: `pro hand -p true -s false -n false SIGSEGV`. |
|||
|
|||
## Debugging (guest code) |
|||
|
|||
### gdb |
|||
|
|||
Run `./build/bin/eden-cli -c <path to your config file (see logs where you run eden normally to see where it is)> -d -g <path to game>` |
|||
|
|||
Then hook up an aarch64-gdb (use `yay aarch64-gdb` or `sudo pkg in arch64-gdb` to install) |
|||
Then type `target remote localhost:1234` and type `c` (for continue) - and then if it crashes just do a `bt` (backtrace) and `layout asm`. |
|||
|
|||
### gdb cheatsheet |
|||
|
|||
- `mo <cmd>`: Monitor commands, `get info`, `get fastmem` and `get mappings` are available. Type `mo help` for more info. |
|||
- `detach`: Detach from remote (i.e restarting the emulator). |
|||
- `c`: Continue |
|||
- `p <expr>`: Print variable, `p/x <expr>` for hexadecimal. |
|||
- `r`: Run |
|||
- `bt`: Print backtrace |
|||
- `info threads`: Print all active threads |
|||
- `thread <number>`: Switch to the given thread (see `info threads`) |
|||
- `layout asm`: Display in assembly mode (TUI) |
|||
- `si`: Step assembly instruction |
|||
- `s` or `step`: Step over LINE OF CODE (not assembly) |
|||
- `display <expr>`: Display variable each step. |
|||
- `n`: Next (skips over call frame of a function) |
|||
- `frame <number>`: Switches to the given frame (from `bt`) |
|||
- `br <expr>`: Set breakpoint at `<expr>`. |
|||
- `delete`: Deletes all breakpoints. |
|||
- `catch throw`: Breakpoint at throw. Can also use `br __cxa_throw` |
|||
- `br _mesa_error`: Break on mesa errors (set environment variable `MESA_DEBUG=1` beforehand), see [MESA_DEBUG](https://mesa-docs.readthedocs.io/en/latest/debugging.html). |
|||
|
|||
Expressions can be `variable_names` or `1234` (numbers) or `*var` (dereference of a pointer) or `*(1 + var)` (computed expression). |
|||
|
|||
For more information type `info gdb` and read [the man page](https://man7.org/linux/man-pages/man1/gdb.1.html). |
|||
|
|||
## Simple checklist for debugging black screens using Renderdoc |
|||
|
|||
Renderdoc is a free, cross platform, multi-graphics API debugger. It is an invaluable tool for diagnosing issues with graphics applications, and includes support for Vulkan. Get it [here](https://renderdoc.org). |
|||
|
|||
Before using renderdoc to diagnose issues, it is always good to make sure there are no validation errors. Any errors means the behavior of the application is undefined. That said, renderdoc can help debug validation errors if you do have them. |
|||
|
|||
When debugging a black screen, there are many ways the application could have setup Vulkan wrong. |
|||
Here is a short checklist of items to look at to make sure are appropriate: |
|||
* Draw call counts are correct (aka not zero, or if rendering many triangles, not 3) |
|||
* Vertex buffers are bound |
|||
* vertex attributes are correct - Make sure the size & offset of each attribute matches what should it should be |
|||
* Any bound push constants and descriptors have the right data - including: |
|||
* Matrices have correct values - double check the model, view, & projection matrices are uploaded correctly |
|||
* Pipeline state is correct |
|||
* viewport range is correct - x,y are 0,0; width & height are screen dimensions, minDepth is 0, maxDepth is 1, NDCDepthRange is 0,1 |
|||
* Fill mode matches expected - usually solid |
|||
* Culling mode makes sense - commonly back or none |
|||
* The winding direction is correct - typically CCW (counter clockwise) |
|||
* Scissor region is correct - usually same as viewport's x,y,width, &height |
|||
* Blend state is correct |
|||
* Depth state is correct - typically enabled with Function set to Less than or Equal |
|||
* Swapchain images are bound when rendering to the swapchain |
|||
* Image being rendered to is the same as the one being presented when rendering to the swapchain |
|||
|
|||
Alternatively, a [RenderDoc Extension](https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw) ([Archive](https://web.archive.org/web/20250000000000*/https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw)) exists which automates doing a lot of these manual steps. |
|||
@ -0,0 +1,32 @@ |
|||
# User Handbook - Testing |
|||
|
|||
While this is mainly aimed for testers - normal users can benefit from these guidelines to make their life easier when trying to outline and/or report an issue. |
|||
|
|||
## How to Test a PR Against the Based Master When Issues Arise |
|||
|
|||
When you're testing a pull request (PR) and encounter unexpected behavior, it's important to determine whether the issue was introduced by the PR or if it already exists in the base code. To do this, compare the behavior against the based master branch. |
|||
|
|||
Even before an issue occurs, it is best practice to keep the same settings and delete the shader cache. Using an already made shader cache can make the PR look like it is having a regression in some rare cases. |
|||
|
|||
### What to Do When Something Seems Off |
|||
If you notice something odd during testing: |
|||
- Reproduce the issue using the based master branch. |
|||
- Observe whether the same behavior occurs. |
|||
|
|||
### Two Possible Outcomes |
|||
- If the issue exists in the based master: This means the problem was already present before the PR. The PR most likely did not introduce the regression. |
|||
- If the issue does not exist in the based master: This suggests the PR most likely introduced the regression and needs further investigation. |
|||
|
|||
### Report your findings |
|||
When you report your results: |
|||
- Clearly state whether the behavior was observed in the based master. |
|||
- Indicate whether the result is good (expected behavior) or bad (unexpected or broken behavior). Without mentioning if your post/report/log is good or bad it may confuse the Developer of the PR. |
|||
- Example: |
|||
``` |
|||
1. "Tested on based master — issue not present. Bad result for PR, likely regression introduced." |
|||
2. "Tested on based master — issue already present. Good result for PR, not a regression." |
|||
``` |
|||
|
|||
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently. If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master. |
|||
|
|||
If a master build for the PR' based master does not exist. It will be helpful to just test past and future builds nearby. That would help with gathering more information about the problem. |
|||
@ -0,0 +1,8 @@ |
|||
# User Handbook - Third party tools and extras |
|||
|
|||
The Eden emulator by itself lacks some functionality - or otherwise requires external files (such as packaging) to operate correctly in a given OS. Addendum to that some repositories provide nightly or specialised builds of the emulator. |
|||
|
|||
While most of the links mentioned in this guide are relatively "safe"; we urge users to use their due diligence and appropriatedly verify the integrity of all files downloaded and ensure they're not compromised. |
|||
|
|||
- [Nightly Eden builds](https://github.com/pflyly/eden-nightly) |
|||
- [NixOS Eden Flake](https://github.com/Grantimatter/eden-flake) |
|||
@ -0,0 +1,131 @@ |
|||
#!/usr/bin/perl |
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
# Basic script to run dtrace sampling over the program (requires Flamegraph) |
|||
# Usage is either running as: ./dtrace-tool.sh pid (then input the pid of the process) |
|||
# Or just run directly with: ./dtrace-tool.sh <command> |
|||
use strict; |
|||
use warnings; |
|||
use POSIX qw(strftime); |
|||
|
|||
my $input; |
|||
my $sampling_hz = '4000'; |
|||
my $sampling_time = '5'; |
|||
my $sampling_pid = `pgrep eden`; |
|||
my $sampling_program = 'eden'; |
|||
my $sampling_type = 0; |
|||
|
|||
sub dtrace_ask_params { |
|||
my $is_ok = 'Y'; |
|||
do { |
|||
print "Sampling HZ [" . $sampling_hz . "]: "; |
|||
chomp($input = <STDIN>); |
|||
$sampling_hz = $input || $sampling_hz; |
|||
|
|||
print "Sampling time [" . $sampling_time . "]: "; |
|||
chomp($input = <STDIN>); |
|||
$sampling_time = $input || $sampling_time; |
|||
|
|||
print "Sampling pid [" . $sampling_pid . "]: "; |
|||
chomp($input = <STDIN>); |
|||
$sampling_pid = $input || $sampling_pid; |
|||
|
|||
print "Are these settings correct?: [" . $is_ok . "]\n"; |
|||
print "HZ = " . $sampling_hz . "\nTime = " . $sampling_time . "\nPID = " . $sampling_pid . "\n"; |
|||
chomp($input = <STDIN>); |
|||
$is_ok = $input || $is_ok; |
|||
} while ($is_ok eq 'n'); |
|||
} |
|||
|
|||
sub dtrace_probe_profiling { |
|||
if ($sampling_type eq 0) { |
|||
return " |
|||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { |
|||
@[stack(100)] = count(); |
|||
} |
|||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { |
|||
@[ustack(100)] = count(); |
|||
} |
|||
tick-".$sampling_time."s { |
|||
exit(0); |
|||
}"; |
|||
} elsif ($sampling_type eq 1) { |
|||
return " |
|||
syscall:::entry /pid == ".$sampling_pid."/ { |
|||
\@traces[ustack(100)] = count(); |
|||
} |
|||
tick-".$sampling_time."s { |
|||
exit(0); |
|||
}"; |
|||
} elsif ($sampling_type eq 2) { |
|||
return " |
|||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg0/ { |
|||
@[stringof(curthread->td_name), stack(100)] = count(); |
|||
} |
|||
profile-".$sampling_hz." /pid == ".$sampling_pid." && arg1/ { |
|||
@[stringof(curthread->td_name), ustack(100)] = count(); |
|||
} |
|||
tick-".$sampling_time."s { |
|||
exit(0); |
|||
}"; |
|||
} elsif ($sampling_type eq 3) { |
|||
return " |
|||
io::start /pid == ".$sampling_pid."/ { |
|||
@[ustack(100)] = count(); |
|||
} |
|||
tick-".$sampling_time."s { |
|||
exit(0); |
|||
}"; |
|||
} else { |
|||
die "idk"; |
|||
} |
|||
} |
|||
|
|||
sub dtrace_generate { |
|||
my @date = (localtime(time))[5, 4, 3, 2, 1, 0]; |
|||
$date[0] += 1900; |
|||
$date[1]++; |
|||
my $fmt_date = sprintf "%4d-%02d-%02d_%02d-%02d-%02d", @date; |
|||
my $trace_dir = "dtrace-out"; |
|||
my $trace_file = $trace_dir . "/" . $fmt_date . ".user_stacks"; |
|||
my $trace_fold = $trace_dir . "/" . $fmt_date . ".fold"; |
|||
my $trace_svg = $trace_dir . "/" . $fmt_date . ".svg"; |
|||
my $trace_probe = dtrace_probe_profiling; |
|||
|
|||
print $trace_probe . "\n"; |
|||
system "sudo", "dtrace", "-Z", "-n", $trace_probe, "-o", $trace_file; |
|||
die "$!" if $?; |
|||
|
|||
open (my $trace_fold_handle, ">", $trace_fold) or die "$!"; |
|||
#run ["perl", "../FlameGraph/stackcollapse.pl", $trace_file], ">", \my $fold_output; |
|||
my $fold_output = `perl ../FlameGraph/stackcollapse.pl $trace_file`; |
|||
print $trace_fold_handle $fold_output; |
|||
|
|||
open (my $trace_svg_handle, ">", $trace_svg) or die "$!"; |
|||
#run ["perl", "../FlameGraph/flamegraph.pl", $trace_fold], ">", \my $svg_output; |
|||
my $svg_output = `perl ../FlameGraph/flamegraph.pl $trace_fold`; |
|||
print $trace_svg_handle $svg_output; |
|||
|
|||
system "sudo", "chmod", "0666", $trace_file; |
|||
} |
|||
|
|||
foreach my $i (0 .. $#ARGV) { |
|||
if ($ARGV[$i] eq '-h') { |
|||
print "Usage: $0\n"; |
|||
printf "%-20s%s\n", "-p", "Prompt for parameters"; |
|||
printf "%-20s%s\n", "-g", "Generate dtrace output"; |
|||
printf "%-20s%s\n", "-s", "Continously generate output until Ctrl^C"; |
|||
printf "%-20s%s\n", "-<n>", "Select dtrace type"; |
|||
} elsif ($ARGV[$i] eq '-g') { |
|||
dtrace_generate; |
|||
} elsif ($ARGV[$i] eq '-s') { |
|||
while (1) { |
|||
dtrace_generate; |
|||
} |
|||
} elsif ($ARGV[$i] eq '-p') { |
|||
dtrace_ask_params; |
|||
} else { |
|||
$sampling_type = substr $ARGV[$i], 1; |
|||
print "Select: ".$sampling_type."\n"; |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
#!/bin/sh -e |
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
cat src/android/app/src/main/res/values/strings.xml \ |
|||
| grep 'string name="' | awk -F'"' '$0=$2' \ |
|||
| xargs -I {} sh -c 'grep -qirnw R.string.'{}' src/android/app/src || echo '{} |
|||
@ -0,0 +1,40 @@ |
|||
#!/usr/bin/perl |
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
use strict; |
|||
use warnings; |
|||
sub generate_lanczos { |
|||
my $pi = 3.14159265358979; |
|||
sub sinc { |
|||
if ($_[0] eq 0.0) { |
|||
return 1.0; |
|||
} else { |
|||
return sin($pi * $_[0]) / ($pi * $_[0]); |
|||
} |
|||
} |
|||
sub lanczos { |
|||
my $d = sqrt($_[0] * $_[0] + $_[1] * $_[1]); |
|||
return sinc($d) / sinc($d / $_[2]); |
|||
} |
|||
my $r = 3.0; #radius (1 = 3 steps) |
|||
my $k_size = ($r * 2.0 + 1.0) * ($r * 2.0 + 1.0); |
|||
my $w_sum = \0.0; |
|||
my $factor = 1.0 / ($r + 1.0); |
|||
#kernel size = (r * 2 + 1) ^ 2 |
|||
printf("const float w_kernel[%i] = float[] (\n ", $k_size); |
|||
for (my $x = -$r; $x <= $r; $x++) { |
|||
for (my $y = -$r; $y <= $r; $y++) { |
|||
my $w = lanczos($x, $y, $r); |
|||
printf("%f, ", $w); |
|||
$w_sum += $w; |
|||
} |
|||
} |
|||
printf("\n);\nconst vec2 w_pos[%i] = vec[](\n ", $k_size); |
|||
for (my $x = -$r; $x <= $r; $x++) { |
|||
for (my $y = -$r; $y <= $r; $y++) { |
|||
printf("vec2(%f, %f), ", $x * $factor, $y * $factor); |
|||
} |
|||
} |
|||
printf("\n);\nconst float w_sum = %f;\n", $w_sum); |
|||
} |
|||
generate_lanczos; |
|||
@ -1,48 +0,0 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
// clang -lm tools/lanczos_gen.c -o tools/lanczos_gen && ./tools/lanczos_gen |
|||
#include <stdio.h> |
|||
#include <math.h> |
|||
|
|||
double sinc(double x) { |
|||
return x == 0.0f ? 1.0f : sin(M_PI * x) / (M_PI * x); |
|||
} |
|||
|
|||
typedef struct vec2 { |
|||
double x; |
|||
double y; |
|||
} vec2; |
|||
|
|||
double lanczos(vec2 v, float a) { |
|||
double d = sqrt(v.x * v.x + v.y * v.y); |
|||
return sinc(d) / sinc(d / a); |
|||
} |
|||
|
|||
int main(int argc, char* argv[]) { |
|||
const int r = 3; //radius (1 = 3 steps) |
|||
const int k_size = (r * 2 + 1) * (r * 2 + 1); |
|||
double w_sum = 0.0f; |
|||
// kernel size = (r * 2 + 1) ^ 2 |
|||
printf("const float w_kernel[%i] = float[] (\n ", k_size); |
|||
double factor = 1.0f / ((double)r + 1.0f); |
|||
for (int x = -r; x <= r; x++) |
|||
for (int y = -r; y <= r; y++) { |
|||
double w = lanczos((vec2){ .x = x, .y = y }, (double)r); |
|||
printf("%lff, ", w); |
|||
w_sum += w; |
|||
} |
|||
printf("\n);\n"); |
|||
printf("const vec2 w_pos[%i] = vec2[] (\n ", k_size); |
|||
for (int x = -r; x <= r; x++) |
|||
for (int y = -r; y <= r; y++) { |
|||
vec2 kp = (vec2){ |
|||
.x = x * factor, |
|||
.y = y * factor |
|||
}; |
|||
printf("vec2(%lff, %lff), ", kp.x, kp.y); |
|||
} |
|||
printf("\n);\n"); |
|||
printf("const float w_sum = %lff;\n", w_sum); |
|||
return 0; |
|||
} |
|||
@ -1,9 +1,5 @@ |
|||
#!/bin/sh -e |
|||
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project |
|||
# SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
# Optimizes assets of Eden (requires OptiPng) |
|||
|
|||
which optipng || exit |
|||
find . -type f -name "*.png" -exec optipng -o7 {} \; |
|||
find . -type f -iname '*.png' -print0 | xargs -0 -P 16 -I {} optipng -o7 {} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue