Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f73642031 | ||
|
|
d31d5a9310 | ||
|
|
6b9844a4e4 | ||
|
|
3ca1f9ebb4 | ||
|
|
47545c7d01 | ||
|
|
d2df3a6b25 | ||
|
|
7b2ddaaab7 | ||
|
|
84d4efe09f | ||
|
|
67db4349d8 | ||
|
|
bfca0dc6ca | ||
|
|
0ae31ab8a4 | ||
|
|
c4017ede21 | ||
|
|
9f8838e4d9 | ||
|
|
2770c20007 | ||
|
|
394da44a09 | ||
|
|
9b99a961f5 | ||
|
|
d17a5ea045 | ||
|
|
741bfcda01 | ||
|
|
ddf5ccadf5 | ||
|
|
9fc5072597 | ||
|
|
151418da8d | ||
|
|
2ad3f3379c | ||
|
|
e1ad2661a7 | ||
|
|
c1f6d5702a | ||
|
|
41f7d14e75 | ||
|
|
c6d6431e82 | ||
|
|
b035ce9560 | ||
|
|
6599155ad2 | ||
|
|
ee61536d52 | ||
|
|
ff209f0f19 | ||
|
|
cac6885bfb | ||
|
|
bdfc477b8a | ||
|
|
ce43723566 | ||
|
|
04ea2a52ee | ||
|
|
01a89c3db0 | ||
|
|
2900376896 | ||
|
|
e812f9fdb9 | ||
|
|
add71b87e8 | ||
|
|
a563083096 | ||
|
|
984d2d125f | ||
|
|
c50a8a0ebe | ||
|
|
280bc39df4 | ||
|
|
878d111e52 | ||
|
|
f9e755dd04 | ||
|
|
35279ba28d | ||
|
|
815ad9a73d | ||
|
|
fb0d69e74b | ||
|
|
80c14502f3 | ||
|
|
ae8f761f46 | ||
|
|
5b1cbdb542 | ||
|
|
985e3c10fd | ||
|
|
eecdde4ee7 | ||
|
|
580b322313 | ||
|
|
641ebdaca1 | ||
|
|
ed4d21d7a3 | ||
|
|
b050c5f04c | ||
|
|
d1b00bfa6b | ||
|
|
d2ee5b9db9 | ||
|
|
f275f9f176 | ||
|
|
b069d68e05 | ||
|
|
b6d95c838f | ||
|
|
0976b64dd8 | ||
|
|
c71f131b0c | ||
|
|
8639e306c7 | ||
|
|
822186413c | ||
|
|
da53c2b905 | ||
|
|
f09cddddd5 | ||
|
|
56ea29e244 | ||
|
|
e6231d4379 | ||
|
|
8ab3f79f6a | ||
|
|
547d6658dd | ||
|
|
008486531d | ||
|
|
1e71f59bc0 | ||
|
|
80bc2ad805 | ||
|
|
ae6cbb52ae | ||
|
|
3212ce0148 | ||
|
|
e43902175c | ||
|
|
9065009045 | ||
|
|
fe94e4046c | ||
|
|
a5d8289017 | ||
|
|
11a97f6cd1 | ||
|
|
4146f9df6d | ||
|
|
530ce845c5 | ||
|
|
b199273635 | ||
|
|
9e1402d437 | ||
|
|
1c2c5845e1 | ||
|
|
fb10aba946 | ||
|
|
1d4b82f0d5 | ||
|
|
40ce138196 | ||
|
|
53c0e1c190 | ||
|
|
beea62f4c3 | ||
|
|
e6948d2c03 | ||
|
|
2c970caafe | ||
|
|
6017b3dd52 | ||
|
|
bb49f49373 | ||
|
|
206a87c82d | ||
|
|
b2e6f2b280 | ||
|
|
4e8015b663 | ||
|
|
1d4ac499e2 | ||
|
|
7ac40e85c6 | ||
|
|
2380e62c79 | ||
|
|
5f0baa9405 | ||
|
|
6f3a90b938 | ||
|
|
99183728eb | ||
|
|
ab39fe00f0 | ||
|
|
50d76e2657 | ||
|
|
d7c3f860f9 | ||
|
|
8d8de9e328 | ||
|
|
bd0d68a788 | ||
|
|
d7b57ba411 | ||
|
|
13786dec7c | ||
|
|
e5b521d4fb | ||
|
|
b44f4b57a6 | ||
|
|
5abc150d05 | ||
|
|
91b1ef6837 | ||
|
|
c1d8f2d873 | ||
|
|
4ee6492d18 | ||
|
|
8bfef958aa | ||
|
|
d50e9d2f5d | ||
|
|
8e94e664c5 | ||
|
|
9c7f114baf | ||
|
|
0056a195b1 | ||
|
|
5072497629 | ||
|
|
46675146ff | ||
|
|
65f3f507c8 | ||
|
|
9f92c633ee | ||
|
|
faa4f1854a | ||
|
|
7a55003e5f | ||
|
|
8574d0db24 | ||
|
|
c96f34ad02 | ||
|
|
6b6b6027ec | ||
|
|
0d8933988b | ||
|
|
7f5be570cd | ||
|
|
94de2e10d7 | ||
|
|
862e94b49d | ||
|
|
ad51e37222 | ||
|
|
b3a438b9eb | ||
|
|
9d2bd692f5 | ||
|
|
55f8a38d59 | ||
|
|
3288fcce4e | ||
|
|
13e23df347 | ||
|
|
409ea8d7cf | ||
|
|
7aa95db26c | ||
|
|
c2fcacb331 | ||
|
|
60e135a71d | ||
|
|
b083b32836 | ||
|
|
7adfece675 | ||
|
|
8da4623d39 | ||
|
|
189487a2ca | ||
|
|
82040066b2 | ||
|
|
e14b01fdc4 | ||
|
|
768d31d4dd | ||
|
|
4304c1c108 | ||
|
|
0054b5793e | ||
|
|
b3fd78e6f4 | ||
|
|
8046515c7a | ||
|
|
76c4d09118 | ||
|
|
4100ae0009 | ||
|
|
ab9bc590a2 | ||
|
|
87d97af20a | ||
|
|
544759dff7 | ||
|
|
595f87f3ac | ||
|
|
815f4b31d8 | ||
|
|
657b0c0f5d | ||
|
|
934202c244 | ||
|
|
99ef67b4a0 | ||
|
|
1e4683fbac | ||
|
|
27d0826e30 | ||
|
|
5168ee4089 | ||
|
|
a9f2a301b4 | ||
|
|
53dedb9a84 | ||
|
|
b73fb828c5 | ||
|
|
5f96077613 | ||
|
|
a284722486 | ||
|
|
cb9f94ae34 | ||
|
|
10856348a3 | ||
|
|
7b5026313e | ||
|
|
1cccbb67da | ||
|
|
8b09014c67 | ||
|
|
19171c2723 | ||
|
|
e130f0297e | ||
|
|
735f3ec902 | ||
|
|
109ebc4683 | ||
|
|
3da4e20905 | ||
|
|
963086edb5 | ||
|
|
b0a0640089 | ||
|
|
2e648ac845 | ||
|
|
77110f15a3 | ||
|
|
5c697600f7 | ||
|
|
bf3b824a71 | ||
|
|
cedfd160c4 | ||
|
|
9ac22b34fa | ||
|
|
fad4ef8908 | ||
|
|
cb4a79e8c1 | ||
|
|
8cca04113d | ||
|
|
e4789472e3 | ||
|
|
11c818591b | ||
|
|
ea1eef8d22 | ||
|
|
4428bb70e9 | ||
|
|
4e82321182 | ||
|
|
b2fc626854 | ||
|
|
189147fc31 | ||
|
|
532aafe074 | ||
|
|
eefd470752 | ||
|
|
9d6d3612a7 | ||
|
|
e867dbd380 | ||
|
|
cf25b04854 | ||
|
|
02028d8fe2 | ||
|
|
47c2fca103 | ||
|
|
b8dfb68f6b | ||
|
|
960d8a8dcb | ||
|
|
ca273d320e | ||
|
|
54159cf95e | ||
|
|
3cebf21a5e | ||
|
|
28a57b9347 | ||
|
|
64857906b3 | ||
|
|
c5583ade47 | ||
|
|
8c7bad148c | ||
|
|
a290c96899 | ||
|
|
034468c384 | ||
|
|
340f48d029 | ||
|
|
1b574d0bb5 | ||
|
|
25771ba202 | ||
|
|
02180bad08 | ||
|
|
8b7daf3c26 | ||
|
|
32ce9a54db | ||
|
|
f80efd1c05 | ||
|
|
88714763ca | ||
|
|
1b67ca2b79 | ||
|
|
a657ddf23d | ||
|
|
a986fafb69 | ||
|
|
f45ccd4b7d | ||
|
|
1b6b4f1ae2 | ||
|
|
bd8a1b5155 | ||
|
|
21d3983b4d | ||
|
|
c9d2f41bdb | ||
|
|
787417cf2b | ||
|
|
b3aeb41ee5 | ||
|
|
d0258c76e6 | ||
|
|
fd4eb76bf5 | ||
|
|
8c1d9840c0 | ||
|
|
fae7a763fa | ||
|
|
d6dfd20dd6 | ||
|
|
493a446168 | ||
|
|
1ca28694f9 | ||
|
|
382634e311 | ||
|
|
e6f5e75854 | ||
|
|
a17f29d4c4 | ||
|
|
a38a31e47a | ||
|
|
007b27704d | ||
|
|
c5759c503f | ||
|
|
c72292ef4b | ||
|
|
44bdc531bb | ||
|
|
43af5c298b | ||
|
|
1e577f56e7 | ||
|
|
7656765f30 | ||
|
|
fabde308e9 | ||
|
|
4af59c63f1 | ||
|
|
70cdb5b199 |
No files matched your search
@@ -0,0 +1,170 @@
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
tags: [ 'v*' ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
program:
|
||||
- ares
|
||||
os:
|
||||
- name: windows
|
||||
version: latest
|
||||
- name: macos
|
||||
version: latest
|
||||
- name: ubuntu
|
||||
version: latest
|
||||
runs-on: ${{ matrix.os.name }}-${{ matrix.os.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
if: matrix.os.name == 'ubuntu'
|
||||
run: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install libsdl2-dev libgtksourceview2.0-dev libgtk2.0-dev libao-dev libopenal-dev
|
||||
- name: Make
|
||||
run: make -j4 -C desktop-ui build=optimized local=false lto=true
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ matrix.program }}-${{ matrix.os.name }}
|
||||
path: desktop-ui/out/*
|
||||
|
||||
|
||||
release:
|
||||
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'src'
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: 'bin'
|
||||
- name: Package Artifacts
|
||||
run: |
|
||||
set -eu
|
||||
case ${GITHUB_REF} in
|
||||
refs/tags/*) suffix="-${GITHUB_REF#refs/tags/}" ;;
|
||||
refs/heads/master) suffix="-nightly" ;;
|
||||
*) suffix="" ;;
|
||||
esac
|
||||
|
||||
srcdir="${GITHUB_WORKSPACE}/src"
|
||||
bindir="${GITHUB_WORKSPACE}/bin"
|
||||
|
||||
# Hack: Workaround for GitHub artifacts losing attributes.
|
||||
chmod +x ${bindir}/ares-macos/ares.app/Contents/MacOS/ares
|
||||
|
||||
for os in windows macos
|
||||
do
|
||||
mkdir "${os}"
|
||||
cd "${os}"
|
||||
|
||||
# Package ares.
|
||||
outdir=ares${suffix}
|
||||
mkdir ${outdir}
|
||||
cp -ar ${bindir}/ares-${os}/* ${outdir}
|
||||
zip -r ../ares-${os}.zip ${outdir}
|
||||
|
||||
cd -
|
||||
done
|
||||
- name: Create Release
|
||||
id: release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -eu
|
||||
github_rest()
|
||||
{
|
||||
local method="${1}"
|
||||
local url="https://api.github.com${2}"
|
||||
shift 2
|
||||
>&2 echo "${method} ${url}"
|
||||
curl \
|
||||
--fail \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||
-X "${method}" \
|
||||
"${url}" \
|
||||
"$@"
|
||||
}
|
||||
github_get_release_id_for_tag()
|
||||
{
|
||||
payload=$(github_rest GET "/repos/${GITHUB_REPOSITORY}/releases/tags/${1}") || return
|
||||
echo "${payload}" | jq .id
|
||||
}
|
||||
github_delete_release_by_id()
|
||||
{
|
||||
github_rest DELETE "/repos/${GITHUB_REPOSITORY}/releases/${1}"
|
||||
}
|
||||
github_create_release()
|
||||
{
|
||||
local payload="{
|
||||
\"tag_name\": \"${1}\",
|
||||
\"target_commitish\": \"${2}\",
|
||||
\"name\": \"${3}\",
|
||||
\"body\": \"${4}\",
|
||||
\"draft\": ${5},
|
||||
\"prerelease\": ${6}
|
||||
}"
|
||||
github_rest POST "/repos/${GITHUB_REPOSITORY}/releases" -d "${payload}"
|
||||
}
|
||||
make_nightly_release()
|
||||
{
|
||||
github_create_release \
|
||||
nightly \
|
||||
"${GITHUB_SHA}" \
|
||||
"ares nightly $(date +"%Y-%m-%d")" \
|
||||
"Auto-generated nightly release on $(date -u +"%Y-%m-%d %T %Z")" \
|
||||
false \
|
||||
true
|
||||
}
|
||||
make_version_release()
|
||||
{
|
||||
github_create_release \
|
||||
"${1}" \
|
||||
"${GITHUB_SHA}" \
|
||||
"ares ${1}" \
|
||||
"This is ares ${1}, released on $(date +"%Y-%m-%d")." \
|
||||
false \
|
||||
false
|
||||
}
|
||||
case ${GITHUB_REF} in
|
||||
refs/tags/*)
|
||||
# Create a new version release using the current revision.
|
||||
echo "UPLOAD_URL=$(make_version_release ${GITHUB_REF#refs/tags/} | jq -r .upload_url)" >> $GITHUB_ENV
|
||||
;;
|
||||
refs/heads/master)
|
||||
# Check for an existing nightly release.
|
||||
{ release_id=$(github_get_release_id_for_tag nightly); status=$?; } || true
|
||||
# Delete existing nightly release if it exists.
|
||||
case ${status} in
|
||||
0) github_delete_release_by_id "${release_id}" ;;
|
||||
22) >&2 echo "No current nightly release; skipping tag deletion." ;;
|
||||
*) >&2 echo "API call failed unexpectedly." && exit 1 ;;
|
||||
esac
|
||||
# Create a new nightly release using the current revision.
|
||||
echo "UPLOAD_URL=$(make_nightly_release | jq -r .upload_url)" >> $GITHUB_ENV
|
||||
;;
|
||||
esac
|
||||
- name: Upload ares-windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows.zip', asset_name: 'ares-windows.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-macos
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-macos.zip', asset_name: 'ares-macos.zip', asset_content_type: 'application/zip' }
|
||||
# - name: Upload ares-ubuntu
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
# with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-ubuntu.zip', asset_name: 'ares-ubuntu.zip', asset_content_type: 'application/zip' }
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
ares
|
||||
|
||||
Copyright (c) 2004-2021 ares team, Near et al
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -0,0 +1,18 @@
|
||||
# ares
|
||||
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
**ares** is a multi-system emulator that began development on October 14th, 2004.
|
||||
It is a descendent of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
|
||||
|
||||
Official Releases
|
||||
-----------------
|
||||
|
||||
Official releases are available from
|
||||
[the ares website](https://ares-emulator.github.io).
|
||||
|
||||
Nightly Builds
|
||||
--------------
|
||||
|
||||
Automated, untested builds of ares are available for Windows and macOS as a [pre-release](https://github.com/higan-emu/ares/releases/tag/nightly).
|
||||
Only the latest nightly build is kept.
|
||||
@@ -7,12 +7,17 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||
options += -Wl,-export-dynamic
|
||||
options += -lX11 -lXext
|
||||
else
|
||||
$(error "unsupported platform")
|
||||
$(error "ares is a library and cannot be built directly.")
|
||||
endif
|
||||
|
||||
ares.objects := ares
|
||||
ares.objects := ares ares-fixed-allocator
|
||||
|
||||
$(object.path)/ares.o: $(ares.path)/ares/ares.cpp
|
||||
$(object.path)/ares-fixed-allocator.o: $(ares.path)/ares/memory/fixed-allocator.cpp
|
||||
|
||||
ifeq ($(vulkan),true)
|
||||
flags += -DVULKAN
|
||||
endif
|
||||
|
||||
ifeq ($(profile),accuracy)
|
||||
flags += -DPROFILE_ACCURACY
|
||||
@@ -46,6 +51,10 @@ ifneq ($(filter $(cores),md),)
|
||||
include $(ares.path)/md/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),saturn),)
|
||||
include $(ares.path)/saturn/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),ps1),)
|
||||
include $(ares.path)/ps1/GNUmakefile
|
||||
endif
|
||||
@@ -74,6 +83,10 @@ ifneq ($(filter $(cores),ws),)
|
||||
include $(ares.path)/ws/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),ng),)
|
||||
include $(ares.path)/ng/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),ngp),)
|
||||
include $(ares.path)/ngp/GNUmakefile
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
#version 150
|
||||
|
||||
//anti-aliased nearest-neighbor
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec4 vpow(vec4 n, float e) {
|
||||
return vec4(pow(n.x, e), pow(n.y, e), pow(n.z, e), pow(n.w, e));
|
||||
}
|
||||
|
||||
vec4 toLQV(vec3 c) {
|
||||
return vec4(c.r, c.g, c.b, c.r * 0.2989 + c.g * 0.5870 + c.b * 0.1140);
|
||||
}
|
||||
|
||||
vec3 fromLQV(vec4 c) {
|
||||
float f = c.w / (c.r * 0.2989 + c.g * 0.5870 + c.b * 0.1140);
|
||||
return vec3(c.rgb) * f;
|
||||
}
|
||||
|
||||
vec3 percent(float ssize, float tsize, float coord) {
|
||||
float minfull = (coord * tsize - 0.5) / tsize * ssize;
|
||||
float maxfull = (coord * tsize + 0.5) / tsize * ssize;
|
||||
float realfull = floor(maxfull);
|
||||
|
||||
if(minfull > realfull) {
|
||||
return vec3(
|
||||
1,
|
||||
(realfull + 0.5) / ssize,
|
||||
(realfull + 0.5) / ssize
|
||||
);
|
||||
}
|
||||
|
||||
return vec3(
|
||||
(maxfull - realfull) / (maxfull - minfull),
|
||||
(realfull - 0.5) / ssize,
|
||||
(realfull + 0.5) / ssize
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float srgb = 2.1;
|
||||
float gamma = 3.0;
|
||||
|
||||
vec3 x = percent(sourceSize[0].x, targetSize[0].x, texCoord.x);
|
||||
vec3 y = percent(sourceSize[0].y, targetSize[0].y, texCoord.y);
|
||||
|
||||
//get points to interpolate across in linear RGB
|
||||
vec4 a = toLQV(vpow(texture(source[0], vec2(x[1], y[1])), srgb).rgb);
|
||||
vec4 b = toLQV(vpow(texture(source[0], vec2(x[2], y[1])), srgb).rgb);
|
||||
vec4 c = toLQV(vpow(texture(source[0], vec2(x[1], y[2])), srgb).rgb);
|
||||
vec4 d = toLQV(vpow(texture(source[0], vec2(x[2], y[2])), srgb).rgb);
|
||||
|
||||
//use perceptual gamma for luminance component
|
||||
a.w = pow(a.w, 1 / gamma);
|
||||
b.w = pow(b.w, 1 / gamma);
|
||||
c.w = pow(c.w, 1 / gamma);
|
||||
d.w = pow(d.w, 1 / gamma);
|
||||
|
||||
//interpolate
|
||||
vec4 gammaLQV =
|
||||
(1.0 - x[0]) * (1.0 - y[0]) * a +
|
||||
(0.0 + x[0]) * (1.0 - y[0]) * b +
|
||||
(1.0 - x[0]) * (0.0 + y[0]) * c +
|
||||
(0.0 + x[0]) * (0.0 + y[0]) * d;
|
||||
|
||||
//convert luminance gamma back to linear
|
||||
gammaLQV.w = pow(gammaLQV.w, gamma);
|
||||
|
||||
//convert color back to sRGB
|
||||
fragColor = vpow(vec4(fromLQV(gammaLQV), 1), 1 / srgb);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
fragment: AANN.fs
|
||||
|
||||
output
|
||||
filter: nearest
|
||||
@@ -0,0 +1,10 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
wrap: border
|
||||
fragment: shadertoy.fs
|
||||
|
||||
output
|
||||
filter: nearest
|
||||
@@ -0,0 +1,145 @@
|
||||
//
|
||||
// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
|
||||
//
|
||||
// by Timothy Lottes
|
||||
//
|
||||
// This is more along the style of a really good CGA arcade monitor.
|
||||
// With RGB inputs instead of NTSC.
|
||||
// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
|
||||
//
|
||||
// Left it unoptimized to show the theory behind the algorithm.
|
||||
//
|
||||
// It is an example what I personally would want as a display option for pixel art games.
|
||||
// Please take and use, change, or whatever.
|
||||
//
|
||||
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform int phase;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// Emulated input resolution.
|
||||
vec2 res=sourceSize[0].xy;
|
||||
|
||||
// Hardness of scanline.
|
||||
// -8.0 = soft
|
||||
// -16.0 = medium
|
||||
float hardScan=-8.0;
|
||||
|
||||
// Hardness of pixels in scanline.
|
||||
// -2.0 = soft
|
||||
// -4.0 = hard
|
||||
float hardPix=-3.0;
|
||||
|
||||
// Display warp.
|
||||
// 0.0 = none
|
||||
// 1.0/8.0 = extreme
|
||||
vec2 warp=vec2(1.0/32.0,1.0/24.0);
|
||||
|
||||
// Amount of shadow mask.
|
||||
float maskDark=0.5;
|
||||
float maskLight=1.5;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// sRGB to Linear.
|
||||
// Assuing using sRGB typed textures this should not be needed.
|
||||
float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);}
|
||||
vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}
|
||||
|
||||
// Linear to sRGB.
|
||||
// Assuing using sRGB typed textures this should not be needed.
|
||||
float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
|
||||
vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}
|
||||
|
||||
// Nearest emulated sample given floating point position and texel offset.
|
||||
// Also zero's off screen.
|
||||
vec3 Fetch(vec2 pos,vec2 off){
|
||||
pos=(floor(pos*res+off)+vec2(0.5,0.5))/res;
|
||||
return ToLinear(1.2 * texture(source[0],pos.xy,-16.0).rgb);}
|
||||
|
||||
// Distance in emulated pixels to nearest texel.
|
||||
vec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));}
|
||||
|
||||
// 1D Gaussian.
|
||||
float Gaus(float pos,float scale){return exp2(scale*pos*pos);}
|
||||
|
||||
// 3-tap Gaussian filter along horz line.
|
||||
vec3 Horz3(vec2 pos,float off){
|
||||
vec3 b=Fetch(pos,vec2(-1.0,off));
|
||||
vec3 c=Fetch(pos,vec2( 0.0,off));
|
||||
vec3 d=Fetch(pos,vec2( 1.0,off));
|
||||
float dst=Dist(pos).x;
|
||||
// Convert distance to weight.
|
||||
float scale=hardPix;
|
||||
float wb=Gaus(dst-1.0,scale);
|
||||
float wc=Gaus(dst+0.0,scale);
|
||||
float wd=Gaus(dst+1.0,scale);
|
||||
// Return filtered sample.
|
||||
return (b*wb+c*wc+d*wd)/(wb+wc+wd);}
|
||||
|
||||
// 5-tap Gaussian filter along horz line.
|
||||
vec3 Horz5(vec2 pos,float off){
|
||||
vec3 a=Fetch(pos,vec2(-2.0,off));
|
||||
vec3 b=Fetch(pos,vec2(-1.0,off));
|
||||
vec3 c=Fetch(pos,vec2( 0.0,off));
|
||||
vec3 d=Fetch(pos,vec2( 1.0,off));
|
||||
vec3 e=Fetch(pos,vec2( 2.0,off));
|
||||
float dst=Dist(pos).x;
|
||||
// Convert distance to weight.
|
||||
float scale=hardPix;
|
||||
float wa=Gaus(dst-2.0,scale);
|
||||
float wb=Gaus(dst-1.0,scale);
|
||||
float wc=Gaus(dst+0.0,scale);
|
||||
float wd=Gaus(dst+1.0,scale);
|
||||
float we=Gaus(dst+2.0,scale);
|
||||
// Return filtered sample.
|
||||
return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);}
|
||||
|
||||
// Return scanline weight.
|
||||
float Scan(vec2 pos,float off){
|
||||
float dst=Dist(pos).y;
|
||||
return Gaus(dst+off,hardScan);}
|
||||
|
||||
// Allow nearest three lines to effect pixel.
|
||||
vec3 Tri(vec2 pos){
|
||||
vec3 a=Horz3(pos,-1.0);
|
||||
vec3 b=Horz5(pos, 0.0);
|
||||
vec3 c=Horz3(pos, 1.0);
|
||||
float wa=Scan(pos,-1.0);
|
||||
float wb=Scan(pos, 0.0);
|
||||
float wc=Scan(pos, 1.0);
|
||||
return a*wa+b*wb+c*wc;}
|
||||
|
||||
// Distortion of scanlines, and end of screen alpha.
|
||||
vec2 Warp(vec2 pos){
|
||||
pos=pos*2.0-1.0;
|
||||
pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);
|
||||
return pos*0.5+0.5;}
|
||||
|
||||
// Shadow mask.
|
||||
vec3 Mask(vec2 pos){
|
||||
pos.x+=pos.y*3.0;
|
||||
vec3 mask=vec3(maskDark,maskDark,maskDark);
|
||||
pos.x=fract(pos.x/6.0);
|
||||
if(pos.x<0.333)mask.r=maskLight;
|
||||
else if(pos.x<0.666)mask.g=maskLight;
|
||||
else mask.b=maskLight;
|
||||
return mask;}
|
||||
|
||||
void main() {
|
||||
vec2 pos = texCoord.xy * 1.0001;
|
||||
hardScan=-12.0;
|
||||
// maskDark=maskLight;
|
||||
pos=Warp(pos.xy);
|
||||
fragColor.rgb=Tri(pos)*Mask(gl_FragCoord.xy);
|
||||
fragColor.a=1.0;
|
||||
fragColor = vec4(ToSrgb(fragColor.rgb), fragColor.a);
|
||||
}
|
||||
@@ -0,0 +1,609 @@
|
||||
#version 150
|
||||
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
//
|
||||
//
|
||||
// [CRTS] PUBLIC DOMAIN CRT-STYLED SCALAR - 20180120b
|
||||
//
|
||||
// by Timothy Lottes
|
||||
// https://www.shadertoy.com/view/MtSfRK
|
||||
// adapted for quark syntax by hunterk
|
||||
//
|
||||
//
|
||||
//==============================================================
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
//
|
||||
// WHAT'S NEW
|
||||
//
|
||||
//--------------------------------------------------------------
|
||||
// Evolution of prior shadertoy example
|
||||
//--------------------------------------------------------------
|
||||
// This one is semi-optimized
|
||||
// - Less texture fetches
|
||||
// - Didn't get to instruction level optimization
|
||||
// - Could likely use texture fetch to generate phosphor mask
|
||||
//--------------------------------------------------------------
|
||||
// Added options to disable unused features
|
||||
//--------------------------------------------------------------
|
||||
// Added in exposure matching
|
||||
// - Given scan-line effect and mask always darkens image
|
||||
// - Uses generalized tonemapper to boost mid-level
|
||||
// - Note this can compress highlights
|
||||
// - And won't get back peak brightness
|
||||
// - But best option if one doesn't want as much darkening
|
||||
//--------------------------------------------------------------
|
||||
// Includes option saturation and contrast controls
|
||||
//--------------------------------------------------------------
|
||||
// Added in subtractive aperture grille
|
||||
// - This is a bit brighter than prior
|
||||
//--------------------------------------------------------------
|
||||
// Make sure input to this filter is already low-resolution
|
||||
// - This is not designed to work on titles doing the following
|
||||
// - Rendering to hi-res with nearest sampling
|
||||
//--------------------------------------------------------------
|
||||
// Added a fast and more pixely option for 2 tap/pixel
|
||||
//--------------------------------------------------------------
|
||||
// Improved the vignette when WARP is enabled
|
||||
//--------------------------------------------------------------
|
||||
// Didn't test HLSL or CPU options
|
||||
// - Will incorportate patches if they are broken
|
||||
// - But out of time to try them myself
|
||||
//==============================================================
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
//
|
||||
// LICENSE = UNLICENSE (aka PUBLIC DOMAIN)
|
||||
//
|
||||
//--------------------------------------------------------------
|
||||
// This is free and unencumbered software released into the
|
||||
// public domain.
|
||||
//--------------------------------------------------------------
|
||||
// Anyone is free to copy, modify, publish, use, compile, sell,
|
||||
// or distribute this software, either in source code form or as
|
||||
// a compiled binary, for any purpose, commercial or
|
||||
// non-commercial, and by any means.
|
||||
//--------------------------------------------------------------
|
||||
// In jurisdictions that recognize copyright laws, the author or
|
||||
// authors of this software dedicate any and all copyright
|
||||
// interest in the software to the public domain. We make this
|
||||
// dedication for the benefit of the public at large and to the
|
||||
// detriment of our heirs and successors. We intend this
|
||||
// dedication to be an overt act of relinquishment in perpetuity
|
||||
// of all present and future rights to this software under
|
||||
// copyright law.
|
||||
//--------------------------------------------------------------
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||
// KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||||
// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//--------------------------------------------------------------
|
||||
// For more information, please refer to
|
||||
// <http://unlicense.org/>
|
||||
//==============================================================
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#define MASK 1.0
|
||||
#define MASK_INTENSITY 0.5
|
||||
#define SCANLINE_THINNESS 0.5
|
||||
#define SCAN_BLUR 2.5
|
||||
#define CURVATURE 0.02
|
||||
#define TRINITRON_CURVE 0.0
|
||||
#define CORNER 3.0
|
||||
#define CRT_GAMMA 2.4
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 outputSize;
|
||||
|
||||
in Vertex {
|
||||
vec2 vTexCoord;
|
||||
};
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
//
|
||||
// GAMMA FUNCTIONS
|
||||
//
|
||||
//--------------------------------------------------------------
|
||||
//--------------------------------------------------------------
|
||||
// Since shadertoy doesn't have sRGB textures
|
||||
// And we need linear input into shader
|
||||
// Don't do this in your code
|
||||
float FromSrgb1(float c){
|
||||
return (c<=0.04045)?c*(1.0/12.92):
|
||||
pow(c*(1.0/1.055)+(0.055/1.055),CRT_GAMMA);}
|
||||
//--------------------------------------------------------------
|
||||
vec3 FromSrgb(vec3 c){return vec3(
|
||||
FromSrgb1(c.r),FromSrgb1(c.g),FromSrgb1(c.b));}
|
||||
|
||||
// Convert from linear to sRGB
|
||||
// Since shader toy output is not linear
|
||||
float ToSrgb1(float c){
|
||||
return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
|
||||
//--------------------------------------------------------------
|
||||
vec3 ToSrgb(vec3 c){return vec3(
|
||||
ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
//
|
||||
// DEFINES
|
||||
//
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_CPU - CPU code
|
||||
// CRTS_GPU - GPU code
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_GLSL - GLSL
|
||||
// CRTS_HLSL - HLSL (not tested yet)
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_DEBUG - Define to see on/off split screen
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_WARP - Apply screen warp
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_2_TAP - Faster very pixely 2-tap filter (off is 8)
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_MASK_GRILLE - Aperture grille (aka Trinitron)
|
||||
// CRTS_MASK_GRILLE_LITE - Brighter (subtractive channels)
|
||||
// CRTS_MASK_NONE - No mask
|
||||
// CRTS_MASK_SHADOW - Horizontally stretched shadow mask
|
||||
//--------------------------------------------------------------
|
||||
// CRTS_TONE - Normalize mid-level and process color
|
||||
// CRTS_CONTRAST - Process color - enable contrast control
|
||||
// CRTS_SATURATION - Process color - enable saturation control
|
||||
//--------------------------------------------------------------
|
||||
#define CRTS_STATIC
|
||||
#define CrtsPow
|
||||
#define CRTS_RESTRICT
|
||||
//==============================================================
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
//==============================================================
|
||||
// SETUP FOR CRTS
|
||||
//--------------------------------------------------------------
|
||||
//==============================================================
|
||||
//#define CRTS_DEBUG 1
|
||||
#define CRTS_GPU 1
|
||||
#define CRTS_GLSL 1
|
||||
//--------------------------------------------------------------
|
||||
//#define CRTS_2_TAP 1
|
||||
//--------------------------------------------------------------
|
||||
#define CRTS_TONE 1
|
||||
#define CRTS_CONTRAST 0
|
||||
#define CRTS_SATURATION 0
|
||||
//--------------------------------------------------------------
|
||||
#define CRTS_WARP 1
|
||||
//--------------------------------------------------------------
|
||||
// Try different masks -> moved to runtime parameters
|
||||
//#define CRTS_MASK_GRILLE 1
|
||||
//#define CRTS_MASK_GRILLE_LITE 1
|
||||
//#define CRTS_MASK_NONE 1
|
||||
//#define CRTS_MASK_SHADOW 1
|
||||
//--------------------------------------------------------------
|
||||
// Scanline thinness
|
||||
// 0.50 = fused scanlines
|
||||
// 0.70 = recommended default
|
||||
// 1.00 = thinner scanlines (too thin)
|
||||
#define INPUT_THIN (0.5 + (0.5 * SCANLINE_THINNESS))
|
||||
//--------------------------------------------------------------
|
||||
// Horizonal scan blur
|
||||
// -3.0 = pixely
|
||||
// -2.5 = default
|
||||
// -2.0 = smooth
|
||||
// -1.0 = too blurry
|
||||
#define INPUT_BLUR (-1.0 * SCAN_BLUR)
|
||||
//--------------------------------------------------------------
|
||||
// Shadow mask effect, ranges from,
|
||||
// 0.25 = large amount of mask (not recommended, too dark)
|
||||
// 0.50 = recommended default
|
||||
// 1.00 = no shadow mask
|
||||
#define INPUT_MASK (1.0 - MASK_INTENSITY)
|
||||
//--------------------------------------------------------------
|
||||
#define INPUT_X sourceSize[0].x
|
||||
#define INPUT_Y sourceSize[0].y
|
||||
//--------------------------------------------------------------
|
||||
// Setup the function which returns input image color
|
||||
vec3 CrtsFetch(vec2 uv){
|
||||
// For shadertoy, scale to get native texels in the image
|
||||
uv*=vec2(INPUT_X,INPUT_Y)/sourceSize[0].xy;
|
||||
// Move towards intersting parts
|
||||
// uv+=vec2(0.5,0.5);
|
||||
// Non-shadertoy case would not have the color conversion
|
||||
return FromSrgb(texture(source[0],uv.xy,-16.0).rgb);}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
//
|
||||
// GPU CODE
|
||||
//
|
||||
//==============================================================
|
||||
#ifdef CRTS_GPU
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
// PORTABILITY
|
||||
//==============================================================
|
||||
#ifdef CRTS_GLSL
|
||||
#define CrtsF1 float
|
||||
#define CrtsF2 vec2
|
||||
#define CrtsF3 vec3
|
||||
#define CrtsF4 vec4
|
||||
#define CrtsFractF1 fract
|
||||
#define CrtsRcpF1(x) (1.0/(x))
|
||||
#define CrtsSatF1(x) clamp((x),0.0,1.0)
|
||||
//--------------------------------------------------------------
|
||||
CrtsF1 CrtsMax3F1(CrtsF1 a,CrtsF1 b,CrtsF1 c){
|
||||
return max(a,max(b,c));}
|
||||
#endif
|
||||
//==============================================================
|
||||
#ifdef CRTS_HLSL
|
||||
#define CrtsF1 float
|
||||
#define CrtsF2 float2
|
||||
#define CrtsF3 float3
|
||||
#define CrtsF4 float4
|
||||
#define CrtsFractF1 frac
|
||||
#define CrtsRcpF1(x) (1.0/(x))
|
||||
#define CrtsSatF1(x) saturate(x)
|
||||
//--------------------------------------------------------------
|
||||
CrtsF1 CrtsMax3F1(CrtsF1 a,CrtsF1 b,CrtsF1 c){
|
||||
return max(a,max(b,c));}
|
||||
#endif
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
// TONAL CONTROL CONSTANT GENERATION
|
||||
//--------------------------------------------------------------
|
||||
// This is in here for rapid prototyping
|
||||
// Please use the CPU code and pass in as constants
|
||||
//==============================================================
|
||||
CrtsF4 CrtsTone(
|
||||
CrtsF1 contrast,
|
||||
CrtsF1 saturation,
|
||||
CrtsF1 thin,
|
||||
CrtsF1 mask){
|
||||
//--------------------------------------------------------------
|
||||
if(MASK == 0.0) mask=1.0;
|
||||
//--------------------------------------------------------------
|
||||
if(MASK == 1.0){
|
||||
// Normal R mask is {1.0,mask,mask}
|
||||
// LITE R mask is {mask,1.0,1.0}
|
||||
mask=0.5+mask*0.5;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
CrtsF4 ret;
|
||||
CrtsF1 midOut=0.18/((1.5-thin)*(0.5*mask+0.5));
|
||||
CrtsF1 pMidIn=pow(0.18,contrast);
|
||||
ret.x=contrast;
|
||||
ret.y=((-pMidIn)+midOut)/((1.0-pMidIn)*midOut);
|
||||
ret.z=((-pMidIn)*midOut+pMidIn)/(midOut*(-pMidIn)+midOut);
|
||||
ret.w=contrast+saturation;
|
||||
return ret;}
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
// MASK
|
||||
//--------------------------------------------------------------
|
||||
// Letting LCD/OLED pixel elements function like CRT phosphors
|
||||
// So "phosphor" resolution scales with display resolution
|
||||
//--------------------------------------------------------------
|
||||
// Not applying any warp to the mask (want high frequency)
|
||||
// Real aperture grille has a mask which gets wider on ends
|
||||
// Not attempting to be "real" but instead look the best
|
||||
//--------------------------------------------------------------
|
||||
// Shadow mask is stretched horizontally
|
||||
// RRGGBB
|
||||
// GBBRRG
|
||||
// RRGGBB
|
||||
// This tends to look better on LCDs than vertical
|
||||
// Also 2 pixel width is required to get triad centered
|
||||
//--------------------------------------------------------------
|
||||
// The LITE version of the Aperture Grille is brighter
|
||||
// Uses {dark,1.0,1.0} for R channel
|
||||
// Non LITE version uses {1.0,dark,dark}
|
||||
//--------------------------------------------------------------
|
||||
// 'pos' - This is 'fragCoord.xy'
|
||||
// Pixel {0,0} should be {0.5,0.5}
|
||||
// Pixel {1,1} should be {1.5,1.5}
|
||||
//--------------------------------------------------------------
|
||||
// 'dark' - Exposure of of masked channel
|
||||
// 0.0=fully off, 1.0=no effect
|
||||
//==============================================================
|
||||
CrtsF3 CrtsMask(CrtsF2 pos,CrtsF1 dark){
|
||||
if(MASK == 2.0){
|
||||
CrtsF3 m=CrtsF3(dark,dark,dark);
|
||||
CrtsF1 x=CrtsFractF1(pos.x*(1.0/3.0));
|
||||
if(x<(1.0/3.0))m.r=1.0;
|
||||
else if(x<(2.0/3.0))m.g=1.0;
|
||||
else m.b=1.0;
|
||||
return m;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
if(MASK == 1.0){
|
||||
CrtsF3 m=CrtsF3(1.0,1.0,1.0);
|
||||
CrtsF1 x=CrtsFractF1(pos.x*(1.0/3.0));
|
||||
if(x<(1.0/3.0))m.r=dark;
|
||||
else if(x<(2.0/3.0))m.g=dark;
|
||||
else m.b=dark;
|
||||
return m;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
if(MASK == 0.0){
|
||||
return CrtsF3(1.0,1.0,1.0);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
if(MASK == 3.0){
|
||||
pos.x+=pos.y*2.9999;
|
||||
CrtsF3 m=CrtsF3(dark,dark,dark);
|
||||
CrtsF1 x=CrtsFractF1(pos.x*(1.0/6.0));
|
||||
if(x<(1.0/3.0))m.r=1.0;
|
||||
else if(x<(2.0/3.0))m.g=1.0;
|
||||
else m.b=1.0;
|
||||
return m;
|
||||
}
|
||||
}
|
||||
//_____________________________/\_______________________________
|
||||
//==============================================================
|
||||
// FILTER ENTRY
|
||||
//--------------------------------------------------------------
|
||||
// Input must be linear
|
||||
// Output color is linear
|
||||
//--------------------------------------------------------------
|
||||
// Must have fetch function setup: CrtsF3 CrtsFetch(CrtsF2 uv)
|
||||
// - The 'uv' range is {0.0 to 1.0} for input texture
|
||||
// - Output of this must be linear color
|
||||
//--------------------------------------------------------------
|
||||
// SCANLINE MATH & AUTO-EXPOSURE NOTES
|
||||
// ===================================
|
||||
// Each output line has contribution from at most 2 scanlines
|
||||
// Scanlines are shaped by a windowed cosine function
|
||||
// This shape blends together well with only 2 lines of overlap
|
||||
//--------------------------------------------------------------
|
||||
// Base scanline intensity is as follows
|
||||
// which leaves output intensity range from {0 to 1.0}
|
||||
// --------
|
||||
// thin := range {thick 0.5 to thin 1.0}
|
||||
// off := range {0.0 to <1.0},
|
||||
// sub-pixel offset between two scanlines
|
||||
// --------
|
||||
// a0=cos(min(0.5, off *thin)*2pi)*0.5+0.5;
|
||||
// a1=cos(min(0.5,(1.0-off)*thin)*2pi)*0.5+0.5;
|
||||
//--------------------------------------------------------------
|
||||
// This leads to a image darkening factor of roughly:
|
||||
// {(1.5-thin)/1.0}
|
||||
// This is further reduced by the mask:
|
||||
// {1.0/2.0+mask*1.0/2.0}
|
||||
// Reciprocal of combined effect is used for auto-exposure
|
||||
// to scale up the mid-level in the tonemapper
|
||||
//==============================================================
|
||||
CrtsF3 CrtsFilter(
|
||||
//--------------------------------------------------------------
|
||||
// SV_POSITION, fragCoord.xy
|
||||
CrtsF2 ipos,
|
||||
//--------------------------------------------------------------
|
||||
// inputSize / outputSize (in pixels)
|
||||
CrtsF2 inputSizeDivOutputSize,
|
||||
//--------------------------------------------------------------
|
||||
// 0.5 * inputSize (in pixels)
|
||||
CrtsF2 halfInputSize,
|
||||
//--------------------------------------------------------------
|
||||
// 1.0 / inputSize (in pixels)
|
||||
CrtsF2 rcpInputSize,
|
||||
//--------------------------------------------------------------
|
||||
// 1.0 / outputSize (in pixels)
|
||||
CrtsF2 rcpOutputSize,
|
||||
//--------------------------------------------------------------
|
||||
// 2.0 / outputSize (in pixels)
|
||||
CrtsF2 twoDivOutputSize,
|
||||
//--------------------------------------------------------------
|
||||
// inputSize.y
|
||||
CrtsF1 inputHeight,
|
||||
//--------------------------------------------------------------
|
||||
// Warp scanlines but not phosphor mask
|
||||
// 0.0 = no warp
|
||||
// 1.0/64.0 = light warping
|
||||
// 1.0/32.0 = more warping
|
||||
// Want x and y warping to be different (based on aspect)
|
||||
CrtsF2 warp,
|
||||
//--------------------------------------------------------------
|
||||
// Scanline thinness
|
||||
// 0.50 = fused scanlines
|
||||
// 0.70 = recommended default
|
||||
// 1.00 = thinner scanlines (too thin)
|
||||
// Shared with CrtsTone() function
|
||||
CrtsF1 thin,
|
||||
//--------------------------------------------------------------
|
||||
// Horizonal scan blur
|
||||
// -3.0 = pixely
|
||||
// -2.5 = default
|
||||
// -2.0 = smooth
|
||||
// -1.0 = too blurry
|
||||
CrtsF1 blur,
|
||||
//--------------------------------------------------------------
|
||||
// Shadow mask effect, ranges from,
|
||||
// 0.25 = large amount of mask (not recommended, too dark)
|
||||
// 0.50 = recommended default
|
||||
// 1.00 = no shadow mask
|
||||
// Shared with CrtsTone() function
|
||||
CrtsF1 mask,
|
||||
//--------------------------------------------------------------
|
||||
// Tonal curve parameters generated by CrtsTone()
|
||||
CrtsF4 tone
|
||||
//--------------------------------------------------------------
|
||||
){
|
||||
//--------------------------------------------------------------
|
||||
#ifdef CRTS_DEBUG
|
||||
CrtsF2 uv=ipos*rcpOutputSize;
|
||||
// Show second half processed, and first half un-processed
|
||||
if(uv.x<0.5){
|
||||
// Force nearest to get squares
|
||||
uv*=1.0/rcpInputSize;
|
||||
uv=floor(uv)+CrtsF2(0.5,0.5);
|
||||
uv*=rcpInputSize;
|
||||
CrtsF3 color=CrtsFetch(uv);
|
||||
return color;}
|
||||
#endif
|
||||
//--------------------------------------------------------------
|
||||
// Optional apply warp
|
||||
CrtsF2 pos;
|
||||
#ifdef CRTS_WARP
|
||||
// Convert to {-1 to 1} range
|
||||
pos=ipos*twoDivOutputSize-CrtsF2(1.0,1.0);
|
||||
// Distort pushes image outside {-1 to 1} range
|
||||
pos*=CrtsF2(
|
||||
1.0+(pos.y*pos.y)*warp.x,
|
||||
1.0+(pos.x*pos.x)*warp.y);
|
||||
// TODO: Vignette needs optimization
|
||||
CrtsF1 vin=(1.0-(
|
||||
(1.0-CrtsSatF1(pos.x*pos.x))*(1.0-CrtsSatF1(pos.y*pos.y)))) * (0.998 + (0.001 * CORNER));
|
||||
vin=CrtsSatF1((-vin)*inputHeight+inputHeight);
|
||||
// Leave in {0 to inputSize}
|
||||
pos=pos*halfInputSize+halfInputSize;
|
||||
#else
|
||||
pos=ipos*inputSizeDivOutputSize;
|
||||
#endif
|
||||
//--------------------------------------------------------------
|
||||
// Snap to center of first scanline
|
||||
CrtsF1 y0=floor(pos.y-0.5)+0.5;
|
||||
#ifdef CRTS_2_TAP
|
||||
// Using Inigo's "Improved Texture Interpolation"
|
||||
// http://iquilezles.org/www/articles/texture/texture.htm
|
||||
pos.x+=0.5;
|
||||
CrtsF1 xi=floor(pos.x);
|
||||
CrtsF1 xf=pos.x-xi;
|
||||
xf=xf*xf*xf*(xf*(xf*6.0-15.0)+10.0);
|
||||
CrtsF1 x0=xi+xf-0.5;
|
||||
CrtsF2 p=CrtsF2(x0*rcpInputSize.x,y0*rcpInputSize.y);
|
||||
// Coordinate adjusted bilinear fetch from 2 nearest scanlines
|
||||
CrtsF3 colA=CrtsFetch(p);
|
||||
p.y+=rcpInputSize.y;
|
||||
CrtsF3 colB=CrtsFetch(p);
|
||||
#else
|
||||
// Snap to center of one of four pixels
|
||||
CrtsF1 x0=floor(pos.x-1.5)+0.5;
|
||||
// Inital UV position
|
||||
CrtsF2 p=CrtsF2(x0*rcpInputSize.x,y0*rcpInputSize.y);
|
||||
// Fetch 4 nearest texels from 2 nearest scanlines
|
||||
CrtsF3 colA0=CrtsFetch(p);
|
||||
p.x+=rcpInputSize.x;
|
||||
CrtsF3 colA1=CrtsFetch(p);
|
||||
p.x+=rcpInputSize.x;
|
||||
CrtsF3 colA2=CrtsFetch(p);
|
||||
p.x+=rcpInputSize.x;
|
||||
CrtsF3 colA3=CrtsFetch(p);
|
||||
p.y+=rcpInputSize.y;
|
||||
CrtsF3 colB3=CrtsFetch(p);
|
||||
p.x-=rcpInputSize.x;
|
||||
CrtsF3 colB2=CrtsFetch(p);
|
||||
p.x-=rcpInputSize.x;
|
||||
CrtsF3 colB1=CrtsFetch(p);
|
||||
p.x-=rcpInputSize.x;
|
||||
CrtsF3 colB0=CrtsFetch(p);
|
||||
#endif
|
||||
//--------------------------------------------------------------
|
||||
// Vertical filter
|
||||
// Scanline intensity is using sine wave
|
||||
// Easy filter window and integral used later in exposure
|
||||
CrtsF1 off=pos.y-y0;
|
||||
CrtsF1 pi2=6.28318530717958;
|
||||
CrtsF1 hlf=0.5;
|
||||
CrtsF1 scanA=cos(min(0.5, off *thin )*pi2)*hlf+hlf;
|
||||
CrtsF1 scanB=cos(min(0.5,(-off)*thin+thin)*pi2)*hlf+hlf;
|
||||
//--------------------------------------------------------------
|
||||
#ifdef CRTS_2_TAP
|
||||
#ifdef CRTS_WARP
|
||||
// Get rid of wrong pixels on edge
|
||||
scanA*=vin;
|
||||
scanB*=vin;
|
||||
#endif
|
||||
// Apply vertical filter
|
||||
CrtsF3 color=(colA*scanA)+(colB*scanB);
|
||||
#else
|
||||
// Horizontal kernel is simple gaussian filter
|
||||
CrtsF1 off0=pos.x-x0;
|
||||
CrtsF1 off1=off0-1.0;
|
||||
CrtsF1 off2=off0-2.0;
|
||||
CrtsF1 off3=off0-3.0;
|
||||
CrtsF1 pix0=exp2(blur*off0*off0);
|
||||
CrtsF1 pix1=exp2(blur*off1*off1);
|
||||
CrtsF1 pix2=exp2(blur*off2*off2);
|
||||
CrtsF1 pix3=exp2(blur*off3*off3);
|
||||
CrtsF1 pixT=CrtsRcpF1(pix0+pix1+pix2+pix3);
|
||||
#ifdef CRTS_WARP
|
||||
// Get rid of wrong pixels on edge
|
||||
pixT*=vin;
|
||||
#endif
|
||||
scanA*=pixT;
|
||||
scanB*=pixT;
|
||||
// Apply horizontal and vertical filters
|
||||
CrtsF3 color=
|
||||
(colA0*pix0+colA1*pix1+colA2*pix2+colA3*pix3)*scanA +
|
||||
(colB0*pix0+colB1*pix1+colB2*pix2+colB3*pix3)*scanB;
|
||||
#endif
|
||||
//--------------------------------------------------------------
|
||||
// Apply phosphor mask
|
||||
color*=CrtsMask(ipos,mask);
|
||||
//--------------------------------------------------------------
|
||||
// Optional color processing
|
||||
#ifdef CRTS_TONE
|
||||
// Tonal control, start by protecting from /0
|
||||
CrtsF1 peak=max(1.0/(256.0*65536.0),
|
||||
CrtsMax3F1(color.r,color.g,color.b));
|
||||
// Compute the ratios of {R,G,B}
|
||||
CrtsF3 ratio=color*CrtsRcpF1(peak);
|
||||
// Apply tonal curve to peak value
|
||||
#ifdef CRTS_CONTRAST
|
||||
peak=pow(peak,tone.x);
|
||||
#endif
|
||||
peak=peak*CrtsRcpF1(peak*tone.y+tone.z);
|
||||
// Apply saturation
|
||||
#ifdef CRTS_SATURATION
|
||||
ratio=pow(ratio,CrtsF3(tone.w,tone.w,tone.w));
|
||||
#endif
|
||||
// Reconstruct color
|
||||
return ratio*peak;
|
||||
#else
|
||||
return color;
|
||||
#endif
|
||||
//--------------------------------------------------------------
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec2 warp_factor;
|
||||
warp_factor.x = CURVATURE;
|
||||
warp_factor.y = (3.0 / 4.0) * warp_factor.x; // assume 4:3 aspect
|
||||
warp_factor.x *= (1.0 - TRINITRON_CURVE);
|
||||
FragColor.rgb = CrtsFilter(vTexCoord.xy * outputSize.xy,
|
||||
sourceSize[0].xy * outputSize.zw,
|
||||
sourceSize[0].xy * vec2(0.5,0.5),
|
||||
sourceSize[0].zw,
|
||||
outputSize.zw,
|
||||
2.0 * outputSize.zw,
|
||||
sourceSize[0].y,
|
||||
warp_factor,
|
||||
INPUT_THIN,
|
||||
INPUT_BLUR,
|
||||
INPUT_MASK,
|
||||
CrtsTone(1.0,0.0,INPUT_THIN,INPUT_MASK));
|
||||
|
||||
// Shadertoy outputs non-linear color
|
||||
FragColor.rgb = ToSrgb(FragColor.rgb);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#version 150
|
||||
|
||||
in vec4 position;
|
||||
in vec2 texCoord;
|
||||
|
||||
out Vertex {
|
||||
vec2 vTexCoord;
|
||||
};
|
||||
|
||||
uniform vec4 targetSize;
|
||||
uniform vec4 sourceSize[];
|
||||
|
||||
void main() {
|
||||
gl_Position = position;
|
||||
vTexCoord = texCoord;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
vertex: lottes.vs
|
||||
fragment: lottes.fs
|
||||
|
||||
output
|
||||
height: 0
|
||||
width: 0
|
||||
filter: linear
|
||||
@@ -0,0 +1,21 @@
|
||||
#version 150
|
||||
#define distortion 0.2
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec2 radialDistortion(vec2 coord) {
|
||||
vec2 cc = coord - vec2(0.5);
|
||||
float dist = dot(cc, cc) * distortion;
|
||||
return coord + cc * (1.0 - dist) * dist;
|
||||
}
|
||||
|
||||
void main() {
|
||||
fragColor = texture(source[0], radialDistortion(texCoord));
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: border
|
||||
fragment: curvature.fs
|
||||
@@ -0,0 +1,25 @@
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 grayscale(vec3 color) {
|
||||
return vec3(dot(color, vec3(0.3, 0.59, 0.11)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 offset = fract(texCoord * sourceSize[0].xy) - 0.5;
|
||||
offset /= sourceSize[0].xy;
|
||||
|
||||
vec3 cx = texture(source[0], texCoord - offset).xyz;
|
||||
vec3 cy = texture(source[0], texCoord).xyz;
|
||||
vec3 cz = vec3(5.0 * grayscale(abs(cx - cy)));
|
||||
|
||||
fragColor = vec4(clamp(cz, 0.0, 1.0), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: edge
|
||||
fragment: edge-detection.fs
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,99 @@
|
||||
#version 150
|
||||
|
||||
// GritsScanlines by torridgristle
|
||||
// license: public domain (https://forums.libretro.com/t/lightweight-lut-based-scanline-glow-concept-prototype-glsl/18336/7)
|
||||
|
||||
/* Runtime parameters
|
||||
#in LuminanceDawnbringer
|
||||
#in LuminanceLUT
|
||||
#in ScanlinesOpacity
|
||||
#in GammaCorrection
|
||||
#in TrinitronColors
|
||||
*/
|
||||
|
||||
// static parameters
|
||||
//#define LuminanceDawnbringer
|
||||
#define LuminanceLUT
|
||||
//#define TrinitronColors
|
||||
#define ScanlinesOpacity 0.9
|
||||
//#define GammaCorrection 1.2
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize;
|
||||
uniform sampler2D pixmap[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
#ifdef LuminanceLUT
|
||||
#define LUT_SizeLum 16.0
|
||||
|
||||
// Code taken from RetroArch's LUT shader
|
||||
float luminancelut(vec4 org)
|
||||
{
|
||||
vec4 imgColorLum = org;
|
||||
float redLum = ( imgColorLum.r * (LUT_SizeLum - 1.0) + 0.4999 ) / (LUT_SizeLum * LUT_SizeLum);
|
||||
float greenLum = ( imgColorLum.g * (LUT_SizeLum - 1.0) + 0.4999 ) / LUT_SizeLum;
|
||||
float blue1Lum = (floor( imgColorLum.b * (LUT_SizeLum - 1.0) ) / LUT_SizeLum) + redLum;
|
||||
float blue2Lum = (ceil( imgColorLum.b * (LUT_SizeLum - 1.0) ) / LUT_SizeLum) + redLum;
|
||||
float mixerLum = clamp(max((imgColorLum.b - blue1Lum) / (blue2Lum - blue1Lum), 0.0), 0.0, 32.0);
|
||||
float color1Lum = texture(pixmap[1], vec2( blue1Lum, greenLum )).x;
|
||||
float color2Lum = texture(pixmap[1], vec2( blue2Lum, greenLum )).x;
|
||||
return mix(color1Lum, color2Lum, mixerLum);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TrinitronColors
|
||||
#define LUT_SizeTrinitron 32.0
|
||||
|
||||
vec4 TrinitronD50(vec4 org)
|
||||
{
|
||||
vec4 imgColorTrinitron = org;
|
||||
float redTrinitron = ( imgColorTrinitron.r * (LUT_SizeTrinitron - 1.0) + 0.4999 ) / (LUT_SizeTrinitron * LUT_SizeTrinitron);
|
||||
float greenTrinitron = ( imgColorTrinitron.g * (LUT_SizeTrinitron - 1.0) + 0.4999 ) / LUT_SizeTrinitron;
|
||||
float blue1Trinitron = (floor( imgColorTrinitron.b * (LUT_SizeTrinitron - 1.0) ) / LUT_SizeTrinitron) + redTrinitron;
|
||||
float blue2Trinitron = (ceil( imgColorTrinitron.b * (LUT_SizeTrinitron - 1.0) ) / LUT_SizeTrinitron) + redTrinitron;
|
||||
float mixerTrinitron = clamp(max((imgColorTrinitron.b - blue1Trinitron) / (blue2Trinitron - blue1Trinitron), 0.0), 0.0, 32.0);
|
||||
vec4 color1Trinitron = texture(pixmap[2], vec2( blue1Trinitron, greenTrinitron ));
|
||||
vec4 color2Trinitron = texture(pixmap[2], vec2( blue2Trinitron, greenTrinitron ));
|
||||
vec4 fragColorTrinitron = mix(color1Trinitron, color2Trinitron, mixerTrinitron);
|
||||
return vec4(pow(fragColorTrinitron.rgb,vec3(GammaCorrection,GammaCorrection,GammaCorrection)),1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
|
||||
//Source Image
|
||||
vec4 org = texture(source[0], texCoord);
|
||||
|
||||
#ifdef LuminanceLUT
|
||||
// Use a 3DLUT instead of an equation so that it can use any arbitrary mess you can come up with.
|
||||
float luminance = luminancelut(org);
|
||||
#elif defined LuminanceDawnbringer
|
||||
// Dawnbringer's brightness equation from Dawnbringer's Toolbox scripts for Grafx2
|
||||
float luminance = sqrt(org.r*org.r*0.0676 + org.g*org.g*0.3025 + org.b*org.b*0.0361) * 1.5690256395005606;
|
||||
#else
|
||||
// Plain, standard, fine; slightly faster
|
||||
float luminance = ((0.299*org.r) + (0.587*org.g) + (0.114*org.b));
|
||||
#endif
|
||||
|
||||
// Don't let it exceed 1.0
|
||||
luminance = clamp(luminance, 0.0, 1.0);
|
||||
|
||||
// Scanline Mapping, based on the Phosphor LUT shader's method of tiling a texture over the screen
|
||||
vec2 LUTeffectiveCoord = vec2(luminance,fract((texCoord.y*targetSize.y)/4.0));
|
||||
|
||||
// Scanline Layer
|
||||
vec4 screen = texture(pixmap[0], LUTeffectiveCoord);
|
||||
|
||||
// Output multiplying the scanlines into the original image, with control over opacity
|
||||
#ifdef TrinitronColors
|
||||
org = TrinitronD50(org);
|
||||
#endif
|
||||
fragColor = ((screen*ScanlinesOpacity)+(1.0 - ScanlinesOpacity)) * (org);
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,35 @@
|
||||
settings
|
||||
LuminanceDawnbringer
|
||||
active: false
|
||||
LuminanceLUT
|
||||
active: false
|
||||
TrinitronColors
|
||||
active: true
|
||||
ScanlinesOpacity: 0.9
|
||||
min: 0.0
|
||||
max: 1.0
|
||||
step: 0.05
|
||||
GammaCorrection: 1.2
|
||||
min: 0.5
|
||||
max: 2.0
|
||||
step: 0.1
|
||||
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
wrap: edge
|
||||
width: 100%
|
||||
height: 400%
|
||||
fragment: GritsScanlines.fs
|
||||
pixmap: Scanline-LUT-4px.png
|
||||
filter: linear
|
||||
wrap: edge
|
||||
pixmap: Fake-Self-Illumination.png
|
||||
filter: linear
|
||||
wrap: edge
|
||||
pixmap: Trinitron D50 Absolute Colorimetric - LUT.png
|
||||
filter: linear
|
||||
wrap: edge
|
||||
|
||||
output1
|
||||
filter: linear
|
||||
@@ -0,0 +1,39 @@
|
||||
#version 150
|
||||
|
||||
/*
|
||||
Interlacing
|
||||
Author: hunterk
|
||||
License: Public domain
|
||||
|
||||
Note: This shader is designed to work with the typical interlaced output from an emulator, which displays both even and odd fields twice.
|
||||
This shader will un-weave the image, resulting in a standard, alternating-field interlacing.
|
||||
*/
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec2 targetSize;
|
||||
uniform vec4 sourceSize[];
|
||||
uniform int phase;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// This controls how bright the intermediate lines are; 0.0 = totally black
|
||||
#define percent 0.0
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 res = texture(source[0], texCoord);
|
||||
float y = 0.0;
|
||||
|
||||
// assume anything with a vertical resolution greater than 400 lines is interlaced
|
||||
if (sourceSize[0].y > 400.0) y = sourceSize[0].y * texCoord.y + phase;
|
||||
else
|
||||
y = 2.00001 * sourceSize[0].y * texCoord.y;
|
||||
|
||||
if (mod(y, 2.0) > 0.99999) fragColor = res;
|
||||
else
|
||||
fragColor = vec4(percent) * res;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
fragment: interlace.fs
|
||||
modulo: 2
|
||||
@@ -0,0 +1,11 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
height: 100%
|
||||
fragment: pal-r57shell.fs
|
||||
|
||||
output
|
||||
height: 0
|
||||
filter: linear
|
||||
@@ -0,0 +1,368 @@
|
||||
#version 150
|
||||
|
||||
// NES PAL composite signal simulation for RetroArch
|
||||
// shader by r57shell
|
||||
// thanks to feos & HardWareMan & NewRisingSun
|
||||
|
||||
// also TV subpixels and scanlines
|
||||
|
||||
// LICENSE: PUBLIC DOMAIN
|
||||
|
||||
// NOTE: for nice TV subpixels and scanlines I recommend to
|
||||
// disable this features here and apply CRT-specialized shader.
|
||||
|
||||
// Quality considerations
|
||||
|
||||
// there are three main options:
|
||||
// USE_RAW (R), USE_DELAY_LINE (D), USE_COLORIMETRY (C)
|
||||
// here is table of quality in decreasing order:
|
||||
// RDC, RD, RC, DC, D, C
|
||||
|
||||
// compatibility macros
|
||||
#define float2 vec2
|
||||
#define float3 vec3
|
||||
#define float4 vec4
|
||||
#define frac(c) fract(c)
|
||||
#define saturate(c) clamp(c, 0.0, 1.0)
|
||||
#define fmod(x,y) mod(x,y)
|
||||
#define mul(x,y) (y*x)
|
||||
#define float2x2 mat2
|
||||
#define float3x3 mat3
|
||||
#define float4x4 mat4
|
||||
#define bool2 bvec2
|
||||
#define bool3 bvec3
|
||||
#define bool4 bvec4
|
||||
#define static
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 outputSize;
|
||||
uniform int phase;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
// TWEAKS start
|
||||
|
||||
// use delay line technique
|
||||
// without delay line technique, color would interleave
|
||||
// to avoid this, set HueRotation to zero.
|
||||
#define USE_DELAY_LINE
|
||||
|
||||
// use this if you need to swap even/odd V sign.
|
||||
// sign of V changes each scanline
|
||||
// so if some scanline is positive, then next is negative
|
||||
// and if you want to match picture
|
||||
// to actual running PAL NES on TV
|
||||
// you may want to have this option, to change signs
|
||||
// if they don't match
|
||||
//#define SWAP_VSIGN
|
||||
|
||||
// rough simulation of scanlines
|
||||
// better if you use additional shader instead
|
||||
// if you still use it, make sure that SizeY
|
||||
// is at least twice lower than output height
|
||||
#define USE_SCANLINES // FIXME: scanlines are broken and I'm too lazy to fix it right now
|
||||
|
||||
// to change gamma of virtual TV from 2.2 to something else
|
||||
#define USE_GAMMA
|
||||
|
||||
// use sampled version. it's much more slower version of shader.
|
||||
// because it is computing x4 more values. NOT RECOMMENDED.
|
||||
//#define USE_SAMPLED
|
||||
|
||||
#ifndef PARAMETER_UNIFORM
|
||||
// NTSC standard gamma = 2.2
|
||||
// PAL standard gamma = 2.8
|
||||
// according to many sources, very unlikely gamma of TV is 2.8
|
||||
// most likely gamma of PAL TV is in range 2.4-2.5
|
||||
const float Gamma_static = 2.8; // gamma of virtual TV
|
||||
|
||||
const float Brightness_static = 0.25;
|
||||
const float Contrast_static = 1.1;
|
||||
const float Saturation_static = 1.1;
|
||||
|
||||
const int
|
||||
Ywidth_static = 12,
|
||||
Uwidth_static = 23,
|
||||
Vwidth_static = 23;
|
||||
|
||||
#define Brightness Brightness_static
|
||||
#define Gamma Gamma_static
|
||||
|
||||
#define Ywidth Ywidth_static
|
||||
#define Uwidth Uwidth_static
|
||||
#define Vwidth Vwidth_static
|
||||
|
||||
#define SCANLINE_MUL (sw.x*dark_scanline+sw.y)
|
||||
|
||||
int Mwidth = int(max(float(Ywidth), max(float(Uwidth), float(Vwidth))));
|
||||
|
||||
// correct one is -2.5
|
||||
// works only with USE_RAW
|
||||
const float HueShift = -2.5;
|
||||
|
||||
// rotation of hue due to luma level.
|
||||
const float HueRotation = 2.;
|
||||
|
||||
// touch this only if you know what you doing
|
||||
const float Phase_Y = 2.; // fmod(341*10,12)
|
||||
const float Phase_One = 0.; // alternating phases.
|
||||
const float Phase_Two = 8.;
|
||||
|
||||
// screen size, scanlines = y*2; y one field, and y other field.
|
||||
const int SizeX = 256;
|
||||
const int SizeY = 240;
|
||||
|
||||
// count of pixels of virtual TV.
|
||||
// value close to 1000 produce small artifacts
|
||||
const int TV_Pixels = 400;
|
||||
|
||||
const float dark_scanline = 0.5; // half
|
||||
#endif
|
||||
|
||||
// this is using following matrixes.
|
||||
// it provides more scientific approach
|
||||
// by conversion into linear XYZ space
|
||||
// and back to sRGB.
|
||||
// it's using Gamma setting too.
|
||||
// define USE_GAMMA is not required.
|
||||
#define USE_COLORIMETRY
|
||||
|
||||
const float3x3 RGB_to_XYZ =
|
||||
mat3(
|
||||
0.4306190, 0.3415419, 0.1783091,
|
||||
0.2220379, 0.7066384, 0.0713236,
|
||||
0.0201853, 0.1295504, 0.9390944
|
||||
);
|
||||
|
||||
const float3x3 XYZ_to_sRGB =
|
||||
mat3(
|
||||
3.2406, -1.5372, -0.4986,
|
||||
-0.9689, 1.8758, 0.0415,
|
||||
0.0557, -0.2040, 1.0570
|
||||
);
|
||||
|
||||
// TWEAKS end
|
||||
|
||||
const float YUV_u = 0.492;
|
||||
const float YUV_v = 0.877;
|
||||
|
||||
const mat3 RGB_to_YUV =
|
||||
mat3(
|
||||
float3( 0.299, 0.587, 0.114), //Y
|
||||
float3(-0.299,-0.587, 0.886)*YUV_u, //B-Y
|
||||
float3( 0.701,-0.587,-0.114)*YUV_v //R-Y
|
||||
);
|
||||
|
||||
#ifdef USE_DELAY_LINE
|
||||
const float comb_line = 1.;
|
||||
#else
|
||||
const float comb_line = 2.;
|
||||
#endif
|
||||
|
||||
static const float Voltage_0 = 0.518;
|
||||
static const float Voltage_1 = 1.962;
|
||||
static const float DeltaV = (Voltage_1-Voltage_0);
|
||||
|
||||
float RGB_y = Contrast_static/Ywidth_static/DeltaV;
|
||||
float RGB_u = comb_line*Contrast_static*Saturation_static/YUV_u/Uwidth_static/DeltaV;
|
||||
float RGB_v = comb_line*Contrast_static*Saturation_static/YUV_v/Vwidth_static/DeltaV;
|
||||
|
||||
mat3 YUV_to_RGB =
|
||||
mat3(
|
||||
float3(1., 1., 1.)*RGB_y,
|
||||
float3(0., -0.114/0.587, 1.)*RGB_u,
|
||||
float3(1., -0.299/0.587, 0.)*RGB_v
|
||||
);
|
||||
|
||||
const float pi = 3.1415926535897932384626433832795;
|
||||
|
||||
float sinn(float x)
|
||||
{
|
||||
return sin(/*fmod(x,24)*/x*(pi*2./24.));
|
||||
}
|
||||
|
||||
float coss(float x)
|
||||
{
|
||||
return cos(/*fmod(x,24)*/x*(pi*2./24.));
|
||||
}
|
||||
|
||||
float3 monitor(sampler2D tex, float2 p)
|
||||
{
|
||||
float2 size = sourceSize[0].xy;//float2(SizeX,SizeY);
|
||||
// align vertical coord to center of texel
|
||||
float2 uv = float2(
|
||||
|
||||
p.x,
|
||||
|
||||
(floor(p.y*sourceSize[0].y)+0.5)/sourceSize[0].y);
|
||||
#ifdef USE_DELAY_LINE
|
||||
float2 sh = (sourceSize[0].xy/sourceSize[0].xy/size)*float2(14./10.,-1.0);
|
||||
#endif
|
||||
float2 pc = uv*sourceSize[0].xy/sourceSize[0].xy*size*float2(10.,1.);
|
||||
float alpha = dot(floor(float2(pc.x,pc.y)),float2(2.,Phase_Y*2.));
|
||||
alpha += Phase_One*2.;
|
||||
|
||||
// 1/size.x of screen in uv coords = sourceSize[0].x/sourceSize[0].x/size.x;
|
||||
// then 1/10*size.x of screen:
|
||||
float ustep = sourceSize[0].x/sourceSize[0].x/size.x/10.;
|
||||
|
||||
float border = sourceSize[0].x/sourceSize[0].x;
|
||||
float ss = 2.0;
|
||||
#ifdef SWAP_VSIGN
|
||||
#define PAL_SWITCH(A) A < 1.
|
||||
#else
|
||||
#define PAL_SWITCH(A) A > 1.
|
||||
#endif
|
||||
if (PAL_SWITCH(fmod(uv.y*sourceSize[0].y/sourceSize[0].y*size.y,2.0)))
|
||||
{
|
||||
// cos(pi-alpha) = -cos(alpha)
|
||||
// sin(pi-alpha) = sin(alpha)
|
||||
// pi - alpha
|
||||
alpha = -alpha+12012.0;
|
||||
ss = -2.0;
|
||||
}
|
||||
|
||||
float ysum = 0., usum = 0., vsum = 0.;
|
||||
for (int i=0; i<Mwidth; ++i)
|
||||
{
|
||||
float4 res = texture(tex, uv);
|
||||
float3 yuv = mul(RGB_to_YUV, res.xyz);
|
||||
float a1 = alpha+(HueShift+2.5)*2.-yuv.x*ss*HueRotation;
|
||||
float sig = yuv.x+dot(yuv.yz,sign(float2(sinn(a1),coss(a1))));
|
||||
#ifdef USE_DELAY_LINE
|
||||
float4 res1 = texture(tex, uv+sh);
|
||||
float3 yuv1 = mul(RGB_to_YUV, res1.xyz);
|
||||
float a2 = (HueShift+2.5)*2.+12012.0-alpha+yuv.x*ss*HueRotation;
|
||||
float sig1 = yuv1.x+dot(yuv1.yz,sign(float2(sinn(a2),coss(a2))));
|
||||
#endif
|
||||
if (i < Ywidth)
|
||||
ysum += sig;
|
||||
|
||||
#ifdef USE_DELAY_LINE
|
||||
if (i < Uwidth)
|
||||
usum += (sig+sig1)*sinn(alpha);
|
||||
if (i < Vwidth)
|
||||
vsum += (sig-sig1)*coss(alpha);
|
||||
#else
|
||||
if (i < Uwidth)
|
||||
usum += sig*sinn(alpha);
|
||||
if (i < Vwidth)
|
||||
vsum += sig*coss(alpha);
|
||||
#endif
|
||||
alpha -= ss;
|
||||
uv.x -= ustep;
|
||||
}
|
||||
|
||||
float3 rgb = mul(float3(ysum+Brightness*Ywidth_static,usum,vsum), YUV_to_RGB);
|
||||
#if defined(USE_GAMMA) && !defined(USE_COLORIMETRY)
|
||||
float3 rgb1 = saturate(rgb);
|
||||
rgb = pow(rgb1, Gamma/2.2);
|
||||
#endif
|
||||
|
||||
#ifdef USE_COLORIMETRY
|
||||
float3 rgb1 = saturate(rgb);
|
||||
rgb = pow(rgb1, float3(Gamma, Gamma, Gamma));
|
||||
#endif
|
||||
|
||||
#if (defined(USE_SCANLINES))
|
||||
float2 q = (p*sourceSize[0].xy/sourceSize[0].xy)*float2(TV_Pixels*3.,size.y*2.);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SCANLINES
|
||||
float scanlines = size.y * outputSize.z;
|
||||
float top = fmod(q.y-0.5*scanlines*2.,2.);
|
||||
float bottom = top+frac(scanlines)*2.;
|
||||
float2 sw = saturate(min(float2(1.,2.),float2(bottom, bottom))
|
||||
-max(float2(0.,1.),float2(top)))
|
||||
+saturate(min(float2(3.,4.),float2(bottom, bottom))
|
||||
-max(float2(2.,3.),float2(top)))
|
||||
+floor(scanlines);
|
||||
|
||||
rgb = rgb*SCANLINE_MUL/(sw.x+sw.y);
|
||||
|
||||
/*
|
||||
//old stupid method
|
||||
float z =
|
||||
#ifdef ANIMATE_SCANLINE
|
||||
fmod(phase,2.0)+
|
||||
#endif
|
||||
0.5;
|
||||
|
||||
if (abs(fmod(q.y+0.5,2)-z)<0.5)
|
||||
rgb *= dark_scanline;
|
||||
*/
|
||||
#endif
|
||||
|
||||
// size of pixel screen in texture coords:
|
||||
//float output_pixel_size = sourceSize[0].x/(outputSize.x*sourceSize[0].x);
|
||||
|
||||
// correctness check
|
||||
//if (fmod(p.x*output_pixel_size,2.0) < 1.0)
|
||||
// rgb = float3(0.,0.,0.);
|
||||
|
||||
#ifdef USE_COLORIMETRY
|
||||
float3 xyz1 = mul(RGB_to_XYZ,rgb);
|
||||
float3 srgb = saturate(mul(XYZ_to_sRGB,xyz1));
|
||||
float3 a1 = 12.92*srgb;
|
||||
float3 a2 = 1.055*pow(srgb,float3(1./2.4))-0.055;
|
||||
float3 ssrgb;
|
||||
ssrgb.x = (srgb.x<0.0031308?a1.x:a2.x);
|
||||
ssrgb.y = (srgb.y<0.0031308?a1.y:a2.y);
|
||||
ssrgb.z = (srgb.z<0.0031308?a1.z:a2.z);
|
||||
return ssrgb;
|
||||
#else
|
||||
return rgb;
|
||||
#endif
|
||||
}
|
||||
|
||||
// pos (left corner, sample size)
|
||||
float4 monitor_sample(sampler2D tex, float2 p, float2 sample_)
|
||||
{
|
||||
// linear interpolation was...
|
||||
// now other thing.
|
||||
// http://imgur.com/m8Z8trV
|
||||
// AT LAST IT WORKS!!!!
|
||||
// going to check in retroarch...
|
||||
float2 size = sourceSize[0].xy;
|
||||
float2 next = float2(.25,1.)/size;
|
||||
float2 f = frac(float2(4.,1.)*size*p);
|
||||
sample_ *= float2(4.,1.)*size;
|
||||
float2 l;
|
||||
float2 r;
|
||||
if (f.x+sample_.x < 1.)
|
||||
{
|
||||
l.x = f.x+sample_.x;
|
||||
r.x = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
l.x = 1.-f.x;
|
||||
r.x = min(1.,f.x+sample_.x-1.);
|
||||
}
|
||||
if (f.y+sample_.y < 1.)
|
||||
{
|
||||
l.y = f.y+sample_.y;
|
||||
r.y = 0.;
|
||||
}
|
||||
else
|
||||
{
|
||||
l.y = 1.-f.y;
|
||||
r.y = min(1.,f.y+sample_.y-1.);
|
||||
}
|
||||
float3 top = mix(monitor(tex, p), monitor(tex, p+float2(next.x,0.)), r.x/(l.x+r.x));
|
||||
float3 bottom = mix(monitor(tex, p+float2(0.,next.y)), monitor(tex, p+next), r.x/(l.x+r.x));
|
||||
return float4(mix(top,bottom, r.y/(l.y+r.y)),1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef USE_SAMPLED
|
||||
fragColor = vec4(monitor_sample(source[0], texCoord, outputSize.zw).rgb, 1.0);
|
||||
#else
|
||||
fragColor = float4(monitor(source[0], texCoord), 1.);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
program
|
||||
fragment: phosphorish.fs
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Phosphorish
|
||||
|
||||
Copyright (C) 2011 Themaister
|
||||
|
||||
This program is licensed in the public domain
|
||||
*/
|
||||
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
vec3 to_focus(float pixel)
|
||||
{
|
||||
pixel = mod(pixel + 3.0, 3.0);
|
||||
if (pixel >= 2.0) // Blue
|
||||
return vec3(pixel - 2.0, 0.0, 3.0 - pixel);
|
||||
else if (pixel >= 1.0) // Green
|
||||
return vec3(0.0, 2.0 - pixel, pixel - 1.0);
|
||||
else // Red
|
||||
return vec3(1.0 - pixel, pixel, 0.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float y = mod(texCoord.y * sourceSize[0].y, 1.0);
|
||||
float intensity = exp(-0.2 * y);
|
||||
|
||||
vec2 one_x = vec2(1.0 / (3.0 * sourceSize[0].x), 0.0);
|
||||
|
||||
vec3 color = texture(source[0], texCoord.xy - 0.0 * one_x).rgb;
|
||||
vec3 color_prev = texture(source[0], texCoord.xy - 1.0 * one_x).rgb;
|
||||
vec3 color_prev_prev = texture(source[0], texCoord.xy - 2.0 * one_x).rgb;
|
||||
|
||||
float pixel_x = 3.0 * texCoord.x * sourceSize[0].x;
|
||||
|
||||
vec3 focus = to_focus(pixel_x - 0.0);
|
||||
vec3 focus_prev = to_focus(pixel_x - 1.0);
|
||||
vec3 focus_prev_prev = to_focus(pixel_x - 2.0);
|
||||
|
||||
vec3 result =
|
||||
0.8 * color * focus +
|
||||
0.6 * color_prev * focus_prev +
|
||||
0.3 * color_prev_prev * focus_prev_prev;
|
||||
|
||||
result = 2.3 * pow(result, vec3(1.4));
|
||||
|
||||
fragColor = vec4(intensity * result, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: border
|
||||
fragment: scanline.fs
|
||||
@@ -0,0 +1,20 @@
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 rgba = texture(source[0], texCoord);
|
||||
vec4 intensity;
|
||||
if(fract(gl_FragCoord.y * (0.5 * 4.0 / 3.0)) > 0.5) {
|
||||
intensity = vec4(0);
|
||||
} else {
|
||||
intensity = smoothstep(0.2, 0.8, rgba) + normalize(rgba);
|
||||
}
|
||||
fragColor = intensity * -0.25 + rgba * 1.1;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
input
|
||||
filter: linear
|
||||
|
||||
program
|
||||
fragment: sharp-bilinear.fs
|
||||
|
||||
output
|
||||
filter: linear
|
||||
@@ -0,0 +1,33 @@
|
||||
#version 150
|
||||
|
||||
// Sharp Bilinear
|
||||
// Author: Themaister
|
||||
// License: Public Domain
|
||||
|
||||
uniform sampler2D source[];
|
||||
uniform vec4 sourceSize[];
|
||||
uniform vec4 targetSize;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
float prescale = floor(targetSize.y / sourceSize[0].y);
|
||||
vec2 texel = texCoord.xy * sourceSize[0].xy;
|
||||
vec2 texel_floored = floor(texel);
|
||||
vec2 s = fract(texel);
|
||||
float region_range = 0.5 - 0.5 / prescale;
|
||||
|
||||
// Figure out where in the texel to sample to get correct pre-scaled bilinear.
|
||||
// Uses the hardware bilinear interpolator to avoid having to sample 4 times manually.
|
||||
|
||||
vec2 center_dist = s - 0.5;
|
||||
vec2 f = (center_dist - clamp(center_dist, -region_range, region_range)) * prescale + 0.5;
|
||||
|
||||
vec2 mod_texel = texel_floored + f;
|
||||
|
||||
fragColor = texture(source[0], mod_texel / sourceSize[0].xy);;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
database
|
||||
revision: 2020-06-07
|
||||
revision: 2020-11-09
|
||||
|
||||
//Boards (Production)
|
||||
|
||||
@@ -587,7 +587,7 @@ board: SHVC-YJ0N-01
|
||||
//Boards (Generic)
|
||||
|
||||
database
|
||||
revision: 2020-06-07
|
||||
revision: 2020-11-09
|
||||
|
||||
board: ARM-LOROM-RAM
|
||||
memory type=ROM content=Program
|
||||
@@ -706,7 +706,6 @@ board: EXHIROM-RAM
|
||||
map address=c0-ff:0000-ffff mask=0xc00000
|
||||
memory type=RAM content=Save
|
||||
map address=20-3f,a0-bf:6000-7fff mask=0xe000
|
||||
map address=70-7d:0000-7fff
|
||||
|
||||
board: EXHIROM-RAM-SHARPRTC
|
||||
memory type=ROM content=Program
|
||||
@@ -716,7 +715,6 @@ board: EXHIROM-RAM-SHARPRTC
|
||||
map address=c0-ff:0000-ffff mask=0xc00000
|
||||
memory type=RAM content=Save
|
||||
map address=20-3f,a0-bf:6000-7fff mask=0xe000
|
||||
map address=70-7d:0000-7fff
|
||||
rtc manufacturer=Sharp
|
||||
map address=00-3f,80-bf:2800-2801
|
||||
memory type=RTC content=Time manufacturer=Sharp
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/literals.hpp>
|
||||
#include <nall/priority-queue.hpp>
|
||||
#include <nall/queue.hpp>
|
||||
#include <nall/random.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/set.hpp>
|
||||
#include <nall/shared-pointer.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/terminal.hpp>
|
||||
#include <nall/thread.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
#include <nall/unique-pointer.hpp>
|
||||
#include <nall/variant.hpp>
|
||||
@@ -33,37 +36,45 @@
|
||||
using namespace nall;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "124";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
static const string Website = "ares-emulator.github.io";
|
||||
static const string WebsiteURI = "https://ares-emulator.github.io";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
static const string SerializerVersion = "123.1";
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
using File = shared_pointer<vfs::file>;
|
||||
}
|
||||
|
||||
namespace Video {
|
||||
static constexpr bool Threaded = true;
|
||||
}
|
||||
|
||||
namespace Constants {
|
||||
namespace Colorburst {
|
||||
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
||||
static constexpr f64 NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||
static constexpr f64 PAL = 283.75 * 15'625.0 + 25.0;
|
||||
}
|
||||
}
|
||||
|
||||
//nall/vfs shorthand constants
|
||||
namespace File {
|
||||
static const auto Read = vfs::file::mode::read;
|
||||
static const auto Write = vfs::file::mode::write;
|
||||
static const auto Optional = false;
|
||||
static const auto Required = true;
|
||||
}
|
||||
|
||||
namespace Shared {
|
||||
using File = shared_pointer<vfs::file>;
|
||||
}
|
||||
|
||||
extern bool _runAhead;
|
||||
inline auto runAhead() -> bool { return _runAhead; }
|
||||
inline auto setRunAhead(bool runAhead) -> void { _runAhead = runAhead; }
|
||||
}
|
||||
|
||||
#include <ares/information.hpp>
|
||||
#include <ares/types.hpp>
|
||||
#include <ares/random.hpp>
|
||||
#include <ares/debug/debug.hpp>
|
||||
#include <ares/node/node.hpp>
|
||||
#include <ares/platform.hpp>
|
||||
#include <ares/interface.hpp>
|
||||
#include <ares/memory/fixed-allocator.hpp>
|
||||
#include <ares/memory/readable.hpp>
|
||||
#include <ares/memory/writable.hpp>
|
||||
#include <ares/resource/resource.hpp>
|
||||
@@ -3,15 +3,43 @@ namespace ares {
|
||||
Debug _debug;
|
||||
|
||||
auto Debug::reset() -> void {
|
||||
_totalNotices = 0;
|
||||
_unhandledNotices.reset();
|
||||
_unimplementedNotices.reset();
|
||||
_unusualNotices.reset();
|
||||
_unverifiedNotices.reset();
|
||||
}
|
||||
|
||||
auto Debug::_unhandled(const string& text) -> void {
|
||||
if(_unhandledNotices.find(text)) return;
|
||||
if(_totalNotices++ > 256) return;
|
||||
_unhandledNotices.append(text);
|
||||
|
||||
print(terminal::color::yellow("[unhandled] "), text, "\n");
|
||||
}
|
||||
|
||||
auto Debug::_unimplemented(const string& text) -> void {
|
||||
//only warn about missing unimplemented functionality once per type
|
||||
if(_unimplementedNotices.find(text)) return;
|
||||
if(_totalNotices++ > 256) return;
|
||||
_unimplementedNotices.append(text);
|
||||
|
||||
print(terminal::color::magenta("[unimplemented] "), text, "\n");
|
||||
}
|
||||
|
||||
auto Debug::_unusual(const string& text) -> void {
|
||||
if(_unusualNotices.find(text)) return;
|
||||
if(_totalNotices++ > 256) return;
|
||||
_unusualNotices.append(text);
|
||||
|
||||
print(terminal::color::cyan("[unusual] "), text, "\n");
|
||||
}
|
||||
|
||||
auto Debug::_unverified(const string& text) -> void {
|
||||
if(_unverifiedNotices.find(text)) return;
|
||||
if(_totalNotices++ > 256) return;
|
||||
_unverifiedNotices.append(text);
|
||||
|
||||
print(terminal::color::gray("[unverified] "), text, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,39 @@
|
||||
namespace ares {
|
||||
|
||||
struct Debug {
|
||||
static constexpr bool enabled = true;
|
||||
|
||||
auto reset() -> void;
|
||||
|
||||
template<typename... P> auto unhandled(P&&... p) -> void {
|
||||
return _unhandled({forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unimplemented(P&&... p) -> void {
|
||||
return _unimplemented({forward<P>(p)...});
|
||||
}
|
||||
|
||||
private:
|
||||
auto _unimplemented(const string&) -> void;
|
||||
template<typename... P> auto unusual(P&&... p) -> void {
|
||||
return _unusual({forward<P>(p)...});
|
||||
}
|
||||
|
||||
template<typename... P> auto unverified(P&&... p) -> void {
|
||||
return _unverified({forward<P>(p)...});
|
||||
}
|
||||
|
||||
private:
|
||||
auto _unhandled(const string&) -> void;
|
||||
auto _unimplemented(const string&) -> void;
|
||||
auto _unusual(const string&) -> void;
|
||||
auto _unverified(const string&) -> void;
|
||||
|
||||
u64 _totalNotices = 0;
|
||||
vector<string> _unhandledNotices;
|
||||
vector<string> _unimplementedNotices;
|
||||
vector<string> _unusualNotices;
|
||||
vector<string> _unverifiedNotices;
|
||||
};
|
||||
|
||||
extern Debug _debug;
|
||||
|
||||
}
|
||||
|
||||
#define debug(function, ...) if constexpr(ares::_debug.enabled) ares::_debug.function(__VA_ARGS__)
|
||||
#define debug(function, ...) if constexpr(1) ares::_debug.function(__VA_ARGS__)
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "115";
|
||||
static const string Copyright = "ares team";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
static const string Website = "ares.dev";
|
||||
static const string WebsiteURI = "https://ares.dev";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "115";
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct Interface {
|
||||
virtual ~Interface() = default;
|
||||
|
||||
//information
|
||||
virtual auto name() -> string { return {}; }
|
||||
virtual auto game() -> string { return {}; }
|
||||
|
||||
//system interface
|
||||
virtual auto root() -> Node::Object { return {}; }
|
||||
virtual auto load(Node::Object&) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto run() -> void {}
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
|
||||
//state functions
|
||||
virtual auto serialize(bool synchronize = true) -> serializer { return {}; }
|
||||
virtual auto unserialize(serializer&) -> bool { return false; }
|
||||
|
||||
//debugging functions
|
||||
virtual auto exportMemory() -> bool { return false; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
alignas(4096) u8 fixedBuffer[1_GiB];
|
||||
|
||||
FixedAllocator::FixedAllocator() {
|
||||
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
|
||||
}
|
||||
|
||||
auto FixedAllocator::get() -> bump_allocator& {
|
||||
static FixedAllocator allocator;
|
||||
return allocator._allocator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
struct FixedAllocator {
|
||||
static auto get() -> bump_allocator&;
|
||||
|
||||
private:
|
||||
FixedAllocator();
|
||||
|
||||
bump_allocator _allocator;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
inline auto mirror(uint address, uint size) -> uint {
|
||||
inline auto mirror(u32 address, u32 size) -> u32 {
|
||||
if(size == 0) return 0;
|
||||
uint base = 0;
|
||||
uint mask = 1 << 31;
|
||||
u32 base = 0;
|
||||
u32 mask = 1 << 31;
|
||||
while(address >= size) {
|
||||
while(!(address & mask)) mask >>= 1;
|
||||
address -= mask;
|
||||
@@ -18,9 +18,9 @@ inline auto mirror(uint address, uint size) -> uint {
|
||||
return base + address;
|
||||
}
|
||||
|
||||
inline auto reduce(uint address, uint mask) -> uint {
|
||||
inline auto reduce(u32 address, u32 mask) -> u32 {
|
||||
while(mask) {
|
||||
uint bits = (mask & -mask) - 1;
|
||||
u32 bits = (mask & -mask) - 1;
|
||||
address = address >> 1 & ~bits | address & bits;
|
||||
mask = (mask & mask - 1) >> 1;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ struct Readable {
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
auto allocate(uint size, T fill = ~0ull) -> void {
|
||||
auto allocate(u32 size, T fill = (T)~0ull) -> void {
|
||||
if(!size) return reset();
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
@@ -25,33 +25,32 @@ struct Readable {
|
||||
}
|
||||
|
||||
auto fill(T fill = ~0ull) -> void {
|
||||
for(uint address : range(self.size)) {
|
||||
for(u32 address : range(self.size)) {
|
||||
self.data[address] = fill;
|
||||
}
|
||||
}
|
||||
|
||||
auto load(shared_pointer<vfs::file> fp) -> void {
|
||||
if(!self.size) return;
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
auto load(VFS::File fp) -> void {
|
||||
if(!self.size) allocate(fp->size());
|
||||
fp->read({self.data, min(fp->size(), self.size * sizeof(T))});
|
||||
for(u32 address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
auto save(shared_pointer<vfs::file> fp) -> void {
|
||||
if(!self.size) return;
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
auto save(VFS::File fp) -> void {
|
||||
fp->write({self.data, min(fp->size(), self.size * sizeof(T))});
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
auto data() const -> const T* { return self.data; }
|
||||
auto size() const -> uint { return self.size; }
|
||||
auto mask() const -> uint { return self.mask; }
|
||||
auto size() const -> u32 { return self.size; }
|
||||
auto mask() const -> u32 { return self.mask; }
|
||||
|
||||
auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||
auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||
auto write(uint address, T data) const -> void {}
|
||||
auto program(uint address, T data) const -> void { self.data[address & self.mask] = data; }
|
||||
auto operator[](u32 address) const -> T { return self.data[address & self.mask]; }
|
||||
auto read(u32 address) const -> T { return self.data[address & self.mask]; }
|
||||
auto write(u32 address, T data) const -> void {}
|
||||
auto program(u32 address, T data) const -> void { self.data[address & self.mask] = data; }
|
||||
|
||||
auto begin() -> T* { return &self.data[0]; }
|
||||
auto end() -> T* { return &self.data[self.size]; }
|
||||
@@ -60,18 +59,14 @@ struct Readable {
|
||||
auto end() const -> const T* { return &self.data[self.size]; }
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
const uint size = self.size;
|
||||
s.integer(self.size);
|
||||
s.integer(self.mask);
|
||||
if(self.size != size) allocate(self.size);
|
||||
s.array(self.data, self.size);
|
||||
s(array_span<T>{self.data, self.size});
|
||||
}
|
||||
|
||||
private:
|
||||
//private:
|
||||
struct {
|
||||
T* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
u32 size = 0;
|
||||
u32 mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ struct Writable {
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
auto allocate(uint size, T fill = ~0ull) -> void {
|
||||
auto allocate(u32 size, T fill = (T)~0ull) -> void {
|
||||
if(!size) return reset();
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
@@ -25,35 +25,34 @@ struct Writable {
|
||||
}
|
||||
|
||||
auto fill(T fill = ~0ull) -> void {
|
||||
for(uint address : range(self.size)) {
|
||||
for(u32 address : range(self.size)) {
|
||||
self.data[address] = fill;
|
||||
}
|
||||
}
|
||||
|
||||
auto load(shared_pointer<vfs::file> fp) -> void {
|
||||
if(!self.size) return;
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
auto load(VFS::File fp) -> void {
|
||||
if(!self.size) allocate(fp->size());
|
||||
fp->read({self.data, min(fp->size(), self.size * sizeof(T))});
|
||||
for(u32 address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
auto save(shared_pointer<vfs::file> fp) -> void {
|
||||
if(!self.size) return;
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
auto save(VFS::File fp) -> void {
|
||||
fp->write({self.data, min(fp->size(), self.size * sizeof(T))});
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
auto data() -> T* { return self.data; }
|
||||
auto data() const -> const T* { return self.data; }
|
||||
auto size() const -> uint { return self.size; }
|
||||
auto mask() const -> uint { return self.mask; }
|
||||
auto size() const -> u32 { return self.size; }
|
||||
auto mask() const -> u32 { return self.mask; }
|
||||
|
||||
auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
|
||||
auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||
auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||
auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
|
||||
auto program(uint address, T data) -> void { self.data[address & self.mask] = data; }
|
||||
auto operator[](u32 address) -> T& { return self.data[address & self.mask]; }
|
||||
auto operator[](u32 address) const -> T { return self.data[address & self.mask]; }
|
||||
auto read(u32 address) const -> T { return self.data[address & self.mask]; }
|
||||
auto write(u32 address, T data) -> void { self.data[address & self.mask] = data; }
|
||||
auto program(u32 address, T data) -> void { self.data[address & self.mask] = data; }
|
||||
|
||||
auto begin() -> T* { return &self.data[0]; }
|
||||
auto end() -> T* { return &self.data[self.size]; }
|
||||
@@ -62,18 +61,14 @@ struct Writable {
|
||||
auto end() const -> const T* { return &self.data[self.size]; }
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
const uint size = self.size;
|
||||
s.integer(self.size);
|
||||
s.integer(self.mask);
|
||||
if(self.size != size) allocate(self.size);
|
||||
s.array(self.data, self.size);
|
||||
s(array_span<T>{self.data, self.size});
|
||||
}
|
||||
|
||||
private:
|
||||
struct {
|
||||
T* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
u32 size = 0;
|
||||
u32 mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
struct Audio : Object {
|
||||
DeclareClass(Audio, "audio")
|
||||
using Object::Object;
|
||||
};
|
||||
@@ -1,14 +1,14 @@
|
||||
auto Stream::setChannels(uint channels) -> void {
|
||||
auto Stream::setChannels(u32 channels) -> void {
|
||||
_channels.reset();
|
||||
_channels.resize(channels);
|
||||
}
|
||||
|
||||
auto Stream::setFrequency(double frequency) -> void {
|
||||
auto Stream::setFrequency(f64 frequency) -> void {
|
||||
_frequency = frequency;
|
||||
setResamplerFrequency(_resamplerFrequency);
|
||||
}
|
||||
|
||||
auto Stream::setResamplerFrequency(double resamplerFrequency) -> void {
|
||||
auto Stream::setResamplerFrequency(f64 resamplerFrequency) -> void {
|
||||
_resamplerFrequency = resamplerFrequency;
|
||||
|
||||
for(auto& channel : _channels) {
|
||||
@@ -18,12 +18,12 @@ auto Stream::setResamplerFrequency(double resamplerFrequency) -> void {
|
||||
|
||||
if(_frequency >= _resamplerFrequency * 2) {
|
||||
//add a low-pass filter to prevent aliasing during resampling
|
||||
double cutoffFrequency = min(25000.0, _resamplerFrequency / 2.0 - 2000.0);
|
||||
f64 cutoffFrequency = min(25000.0, _resamplerFrequency / 2.0 - 2000.0);
|
||||
for(auto& channel : _channels) {
|
||||
uint passes = 3;
|
||||
for(uint pass : range(passes)) {
|
||||
u32 passes = 3;
|
||||
for(u32 pass : range(passes)) {
|
||||
DSP::IIR::Biquad filter;
|
||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
f64 q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, _frequency, q);
|
||||
channel.nyquist.append(filter);
|
||||
}
|
||||
@@ -41,9 +41,9 @@ auto Stream::resetFilters() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addLowPassFilter(double cutoffFrequency, uint order, uint passes) -> void {
|
||||
auto Stream::addLowPassFilter(f64 cutoffFrequency, u32 order, u32 passes) -> void {
|
||||
for(auto& channel : _channels) {
|
||||
for(uint pass : range(passes)) {
|
||||
for(u32 pass : range(passes)) {
|
||||
if(order == 1) {
|
||||
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
|
||||
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, _frequency);
|
||||
@@ -51,7 +51,7 @@ auto Stream::addLowPassFilter(double cutoffFrequency, uint order, uint passes) -
|
||||
}
|
||||
if(order == 2) {
|
||||
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
|
||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
f64 q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, _frequency, q);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
@@ -59,9 +59,9 @@ auto Stream::addLowPassFilter(double cutoffFrequency, uint order, uint passes) -
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addHighPassFilter(double cutoffFrequency, uint order, uint passes) -> void {
|
||||
auto Stream::addHighPassFilter(f64 cutoffFrequency, u32 order, u32 passes) -> void {
|
||||
for(auto& channel : _channels) {
|
||||
for(uint pass : range(passes)) {
|
||||
for(u32 pass : range(passes)) {
|
||||
if(order == 1) {
|
||||
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
|
||||
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, _frequency);
|
||||
@@ -69,7 +69,7 @@ auto Stream::addHighPassFilter(double cutoffFrequency, uint order, uint passes)
|
||||
}
|
||||
if(order == 2) {
|
||||
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
|
||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
f64 q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, _frequency, q);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
@@ -77,22 +77,22 @@ auto Stream::addHighPassFilter(double cutoffFrequency, uint order, uint passes)
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addLowShelfFilter(double cutoffFrequency, uint order, double gain, double slope) -> void {
|
||||
auto Stream::addLowShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void {
|
||||
for(auto& channel : _channels) {
|
||||
if(order == 2) {
|
||||
Filter filter{Filter::Mode::Biquad, Filter::Type::LowShelf, Filter::Order::Second};
|
||||
double q = DSP::IIR::Biquad::shelf(gain, slope);
|
||||
f64 q = DSP::IIR::Biquad::shelf(gain, slope);
|
||||
filter.biquad.reset(DSP::IIR::Biquad::Type::LowShelf, cutoffFrequency, _frequency, q);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addHighShelfFilter(double cutoffFrequency, uint order, double gain, double slope) -> void {
|
||||
auto Stream::addHighShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void {
|
||||
for(auto& channel : _channels) {
|
||||
if(order == 2) {
|
||||
Filter filter{Filter::Mode::Biquad, Filter::Type::HighShelf, Filter::Order::Second};
|
||||
double q = DSP::IIR::Biquad::shelf(gain, slope);
|
||||
f64 q = DSP::IIR::Biquad::shelf(gain, slope);
|
||||
filter.biquad.reset(DSP::IIR::Biquad::Type::HighShelf, cutoffFrequency, _frequency, q);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
@@ -103,14 +103,16 @@ auto Stream::pending() const -> bool {
|
||||
return _channels && _channels[0].resampler.pending();
|
||||
}
|
||||
|
||||
auto Stream::read(double samples[]) -> uint {
|
||||
for(uint c : range(_channels.size())) samples[c] = _channels[c].resampler.read() * !muted();
|
||||
auto Stream::read(f64 samples[]) -> u32 {
|
||||
for(u32 c : range(_channels.size())) {
|
||||
samples[c] = _channels[c].resampler.read() * !muted();
|
||||
}
|
||||
return _channels.size();
|
||||
}
|
||||
|
||||
auto Stream::write(const double samples[]) -> void {
|
||||
for(uint c : range(_channels.size())) {
|
||||
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
|
||||
auto Stream::write(const f64 samples[]) -> void {
|
||||
for(u32 c : range(_channels.size())) {
|
||||
f64 sample = samples[c] + 1e-25; //constant offset used to suppress denormals
|
||||
for(auto& filter : _channels[c].filters) {
|
||||
switch(filter.mode) {
|
||||
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
struct Stream : Object {
|
||||
DeclareClass(Stream, "Stream")
|
||||
using Object::Object;
|
||||
struct Stream : Audio {
|
||||
DeclareClass(Stream, "audio.stream")
|
||||
using Audio::Audio;
|
||||
|
||||
auto channels() const -> uint { return _channels.size(); }
|
||||
auto frequency() const -> double { return _frequency; }
|
||||
auto resamplerFrequency() const -> double { return _resamplerFrequency; }
|
||||
auto channels() const -> u32 { return _channels.size(); }
|
||||
auto frequency() const -> f64 { return _frequency; }
|
||||
auto resamplerFrequency() const -> f64 { return _resamplerFrequency; }
|
||||
auto muted() const -> bool { return _muted; }
|
||||
|
||||
auto setChannels(uint channels) -> void;
|
||||
auto setFrequency(double frequency) -> void;
|
||||
auto setResamplerFrequency(double resamplerFrequency) -> void;
|
||||
auto setChannels(u32 channels) -> void;
|
||||
auto setFrequency(f64 frequency) -> void;
|
||||
auto setResamplerFrequency(f64 resamplerFrequency) -> void;
|
||||
auto setMuted(bool muted) -> void;
|
||||
|
||||
auto resetFilters() -> void;
|
||||
auto addLowPassFilter(double cutoffFrequency, uint order, uint passes = 1) -> void;
|
||||
auto addHighPassFilter(double cutoffFrequency, uint order, uint passes = 1) -> void;
|
||||
auto addLowShelfFilter(double cutoffFrequency, uint order, double gain, double slope) -> void;
|
||||
auto addHighShelfFilter(double cutoffFrequency, uint order, double gain, double slope) -> void;
|
||||
auto addLowPassFilter(f64 cutoffFrequency, u32 order, u32 passes = 1) -> void;
|
||||
auto addHighPassFilter(f64 cutoffFrequency, u32 order, u32 passes = 1) -> void;
|
||||
auto addLowShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void;
|
||||
auto addHighShelfFilter(f64 cutoffFrequency, u32 order, f64 gain, f64 slope) -> void;
|
||||
|
||||
auto pending() const -> bool;
|
||||
auto read(double samples[]) -> uint;
|
||||
auto write(const double samples[]) -> void;
|
||||
auto read(f64 samples[]) -> u32;
|
||||
auto write(const f64 samples[]) -> void;
|
||||
|
||||
template<typename... P>
|
||||
auto sample(P&&... p) -> void {
|
||||
auto frame(P&&... p) -> void {
|
||||
if(runAhead()) return;
|
||||
double samples[sizeof...(p)] = {forward<P>(p)...};
|
||||
f64 samples[sizeof...(p)] = {forward<P>(p)...};
|
||||
write(samples);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct Filter {
|
||||
enum class Mode : uint { OnePole, Biquad } mode;
|
||||
enum class Type : uint { None, LowPass, HighPass, LowShelf, HighShelf } type;
|
||||
enum class Order : uint { None, First, Second } order;
|
||||
enum class Mode : u32 { OnePole, Biquad } mode;
|
||||
enum class Type : u32 { None, LowPass, HighPass, LowShelf, HighShelf } type;
|
||||
enum class Order : u32 { None, First, Second } order;
|
||||
DSP::IIR::OnePole onePole;
|
||||
DSP::IIR::Biquad biquad;
|
||||
};
|
||||
@@ -43,7 +43,7 @@ protected:
|
||||
DSP::Resampler::Cubic resampler;
|
||||
};
|
||||
vector<Channel> _channels;
|
||||
double _frequency = 48000.0;
|
||||
double _resamplerFrequency = 48000.0;
|
||||
f64 _frequency = 48000.0;
|
||||
f64 _resamplerFrequency = 48000.0;
|
||||
bool _muted = false;
|
||||
};
|
||||
@@ -13,8 +13,8 @@ struct Class {
|
||||
}
|
||||
|
||||
template<typename T> static auto register() -> void {
|
||||
if(!classes().find([&](auto instance) { return instance.identifier == T::identifier; })) {
|
||||
classes().append({T::identifier, &T::create});
|
||||
if(!classes().find([&](auto instance) { return instance.identifier == T::identifier(); })) {
|
||||
classes().append({T::identifier(), &T::create});
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
struct Component : Object {
|
||||
DeclareClass(Component, "Component")
|
||||
DeclareClass(Component, "component");
|
||||
using Object::Object;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
struct RealTimeClock : Component {
|
||||
DeclareClass(RealTimeClock, "component.real-time-clock")
|
||||
using Component::Component;
|
||||
|
||||
auto update() -> void { if(_update) return _update(); }
|
||||
auto timestamp() const -> u64 { return _timestamp; }
|
||||
|
||||
auto setUpdate(function<void ()> update) -> void { _update = update; }
|
||||
auto setTimestamp(u64 timestamp) -> void { _timestamp = timestamp; }
|
||||
|
||||
auto synchronize(u64 timestamp = 0) -> void {
|
||||
if(!timestamp) timestamp = chrono::timestamp();
|
||||
_timestamp = timestamp;
|
||||
update();
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Component::serialize(output, depth);
|
||||
output.append(depth, " timestamp: ", _timestamp, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Component::unserialize(node);
|
||||
_timestamp = node["timestamp"].natural();
|
||||
}
|
||||
|
||||
protected:
|
||||
function<void ()> _update;
|
||||
u64 _timestamp;
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
struct Graphics : Object {
|
||||
DeclareClass(Graphics, "Graphics")
|
||||
|
||||
Graphics(string name = {}) : Object(name) {
|
||||
}
|
||||
|
||||
auto width() const -> uint { return _width; }
|
||||
auto height() const -> uint { return _height; }
|
||||
auto capture() const -> vector<uint32_t> { if(_capture) return _capture(); return {}; }
|
||||
|
||||
auto setSize(uint width, uint height) -> void { _width = width, _height = height; }
|
||||
auto setCapture(function<vector<uint32_t> ()> capture) -> void { _capture = capture; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint _width = 0;
|
||||
uint _height = 0;
|
||||
function<vector<uint32_t> ()> _capture;
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
struct Instruction : Tracer {
|
||||
DeclareClass(Instruction, "Instruction")
|
||||
|
||||
Instruction(string name = {}, string component = {}) : Tracer(name, component) {
|
||||
setDepth(_depth);
|
||||
}
|
||||
|
||||
auto depth() const -> uint { return _depth; }
|
||||
auto addressBits() const -> uint { return _addressBits; }
|
||||
|
||||
auto setDepth(uint depth) -> void {
|
||||
_depth = depth;
|
||||
_history.reset();
|
||||
_history.resize(depth);
|
||||
for(auto& history : _history) history = ~0ull;
|
||||
}
|
||||
|
||||
auto setAddressBits(uint addressBits) -> void {
|
||||
_addressBits = addressBits;
|
||||
}
|
||||
|
||||
auto address(uint64 address) -> bool {
|
||||
_address = address;
|
||||
if(!_depth) return false;
|
||||
|
||||
for(auto history : _history) {
|
||||
if(address == history) {
|
||||
_omitted++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto index : range(_depth - 1)) {
|
||||
_history[index] = _history[index + 1];
|
||||
}
|
||||
_history.last() = _address;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto notify(string_view instruction, string_view context, string_view extra = {}) -> void {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(_omitted) {
|
||||
PlatformLog({
|
||||
//_component, " ",
|
||||
"[Omitted: ", _omitted, "]\n"}
|
||||
);
|
||||
_omitted = 0;
|
||||
}
|
||||
|
||||
string output{
|
||||
//_component, " ",
|
||||
hex(_address, _addressBits + 3 >> 2), " ",
|
||||
instruction, " ",
|
||||
context, " ",
|
||||
extra
|
||||
};
|
||||
PlatformLog({output.strip(), "\n"});
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Tracer::serialize(output, depth);
|
||||
output.append(depth, " depth: ", _depth, "\n");
|
||||
output.append(depth, " addressBits: ", _addressBits, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Tracer::unserialize(node);
|
||||
_depth = node["depth"].natural();
|
||||
_addressBits = node["addressBits"].natural();
|
||||
|
||||
setDepth(_depth);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint _depth = 4;
|
||||
uint _addressBits = 0;
|
||||
|
||||
uint64 _address = 0;
|
||||
uint64 _omitted = 0;
|
||||
vector<uint64> _history;
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
struct Memory : Object {
|
||||
DeclareClass(Memory, "Memory")
|
||||
|
||||
Memory(string name = {}) : Object(name) {
|
||||
}
|
||||
|
||||
auto size() const -> uint { return _size; }
|
||||
auto read(uint32 address) const -> uint8 { if(_read) return _read(address); return 0; }
|
||||
auto write(uint32 address, uint8 data) const -> void { if(_write) return _write(address, data); }
|
||||
|
||||
auto setSize(uint size) -> void { _size = size; }
|
||||
auto setRead(function<uint8 (uint32)> read) -> void { _read = read; }
|
||||
auto setWrite(function<void (uint32, uint8)> write) -> void { _write = write; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
}
|
||||
|
||||
protected:
|
||||
uint _size = 0;
|
||||
function<uint8 (uint32)> _read;
|
||||
function<void (uint32, uint8)> _write;
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
struct Notification : Tracer {
|
||||
DeclareClass(Notification, "Notification")
|
||||
|
||||
Notification(string name = {}, string component = {}) : Tracer(name, component) {
|
||||
}
|
||||
|
||||
auto notify(string_view message = {}) -> void {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(message) {
|
||||
PlatformLog({_component, " [", _name, ": ", message, "]\n"});
|
||||
} else {
|
||||
PlatformLog({_component, " [", _name, "]\n"});
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
struct Debugger : Object {
|
||||
DeclareClass(Debugger, "debugger")
|
||||
using Object::Object;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
struct Graphics : Debugger {
|
||||
DeclareClass(Graphics, "debugger.graphics")
|
||||
|
||||
Graphics(string name = {}) : Debugger(name) {
|
||||
}
|
||||
|
||||
auto width() const -> u32 { return _width; }
|
||||
auto height() const -> u32 { return _height; }
|
||||
auto capture() const -> vector<u32> { if(_capture) return _capture(); return {}; }
|
||||
|
||||
auto setSize(u32 width, u32 height) -> void { _width = width, _height = height; }
|
||||
auto setCapture(function<vector<u32> ()> capture) -> void { _capture = capture; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Debugger::serialize(output, depth);
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Debugger::unserialize(node);
|
||||
}
|
||||
|
||||
protected:
|
||||
u32 _width = 0;
|
||||
u32 _height = 0;
|
||||
function<vector<u32> ()> _capture;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
struct Memory : Debugger {
|
||||
DeclareClass(Memory, "debugger.memory")
|
||||
|
||||
Memory(string name = {}) : Debugger(name) {
|
||||
}
|
||||
|
||||
auto size() const -> u32 { return _size; }
|
||||
auto read(u32 address) const -> n8 { if(_read) return _read(address); return 0; }
|
||||
auto write(u32 address, u8 data) const -> void { if(_write) return _write(address, data); }
|
||||
|
||||
auto setSize(u32 size) -> void { _size = size; }
|
||||
auto setRead(function<u8 (u32)> read) -> void { _read = read; }
|
||||
auto setWrite(function<void (u32, u8)> write) -> void { _write = write; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Debugger::serialize(output, depth);
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Debugger::unserialize(node);
|
||||
}
|
||||
|
||||
protected:
|
||||
u32 _size = 0;
|
||||
function<u8 (u32)> _read;
|
||||
function<void (u32, u8)> _write;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Properties : Object {
|
||||
DeclareClass(Properties, "Properties")
|
||||
struct Properties : Debugger {
|
||||
DeclareClass(Properties, "debugger.properties")
|
||||
|
||||
Properties(string name = {}) : Object(name) {
|
||||
Properties(string name = {}) : Debugger(name) {
|
||||
}
|
||||
|
||||
auto query() const -> string { if(_query) return _query(); return {}; }
|
||||
@@ -9,11 +9,11 @@ struct Properties : Object {
|
||||
auto setQuery(function<string ()> query) -> void { _query = query; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
Debugger::serialize(output, depth);
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
Debugger::unserialize(node);
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -0,0 +1,125 @@
|
||||
struct Instruction : Tracer {
|
||||
DeclareClass(Instruction, "debugger.tracer.instruction")
|
||||
|
||||
Instruction(string name = {}, string component = {}) : Tracer(name, component) {
|
||||
setMask(_mask);
|
||||
setDepth(_depth);
|
||||
}
|
||||
|
||||
auto addressBits() const -> u32 { return _addressBits; }
|
||||
auto addressMask() const -> u32 { return _addressMask; }
|
||||
auto mask() const -> bool { return _mask; }
|
||||
auto depth() const -> u32 { return _depth; }
|
||||
|
||||
auto setAddressBits(u32 addressBits, u32 addressMask = 0) -> void {
|
||||
_addressBits = addressBits;
|
||||
_addressMask = addressMask;
|
||||
}
|
||||
|
||||
auto setMask(bool mask) -> void {
|
||||
_mask = mask;
|
||||
}
|
||||
|
||||
auto setDepth(u32 depth) -> void {
|
||||
_depth = depth;
|
||||
_history.reset();
|
||||
_history.resize(depth);
|
||||
for(auto& history : _history) history = ~0;
|
||||
}
|
||||
|
||||
auto address(u32 address) -> bool {
|
||||
address &= (1ull << _addressBits) - 1; //mask upper bits of address
|
||||
_address = address;
|
||||
address >>= _addressMask; //clip unneeded alignment bits (to reduce _masks size)
|
||||
|
||||
if(_mask && updateMasks()) {
|
||||
if(_masks[address >> 3] & 1 << (address & 7)) return false; //do not trace twice
|
||||
_masks[address >> 3] |= 1 << (address & 7);
|
||||
}
|
||||
|
||||
if(_depth) {
|
||||
for(auto history : _history) {
|
||||
if(_address == history) {
|
||||
_omitted++;
|
||||
return false; //do not trace again if recently traced
|
||||
}
|
||||
}
|
||||
for(auto index : range(_depth - 1)) {
|
||||
_history[index] = _history[index + 1];
|
||||
}
|
||||
_history.last() = _address;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//mark an already-executed address as not executed yet for trace masking.
|
||||
//call when writing to executable RAM to support self-modifying code.
|
||||
auto invalidate(u32 address) -> void {
|
||||
if(unlikely(_mask && updateMasks())) {
|
||||
address &= (1ull << _addressBits) - 1;
|
||||
address >>= _addressMask;
|
||||
_masks[address >> 3] &= ~(1 << (address & 7));
|
||||
}
|
||||
}
|
||||
|
||||
auto notify(const string& instruction, const string& context, const string& extra = {}) -> void {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(_omitted) {
|
||||
PlatformLog({
|
||||
"[Omitted: ", _omitted, "]\n"}
|
||||
);
|
||||
_omitted = 0;
|
||||
}
|
||||
|
||||
string output{
|
||||
_component, " ",
|
||||
hex(_address, _addressBits + 3 >> 2), " ",
|
||||
instruction, " ",
|
||||
context, " ",
|
||||
extra
|
||||
};
|
||||
PlatformLog({output.strip(), "\n"});
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Tracer::serialize(output, depth);
|
||||
output.append(depth, " addressBits: ", _addressBits, "\n");
|
||||
output.append(depth, " addressMask: ", _addressMask, "\n");
|
||||
output.append(depth, " mask: ", _mask, "\n");
|
||||
output.append(depth, " depth: ", _depth, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Tracer::unserialize(node);
|
||||
_addressBits = node["addressBits"].natural();
|
||||
_addressMask = node["addressMask"].natural();
|
||||
_mask = node["mask"].boolean();
|
||||
_depth = node["depth"].natural();
|
||||
|
||||
setMask(_mask);
|
||||
setDepth(_depth);
|
||||
}
|
||||
|
||||
protected:
|
||||
auto updateMasks() -> bool {
|
||||
auto size = 1ull << _addressBits >> _addressMask >> 3;
|
||||
if(!_mask || !size) return _masks.reset(), false;
|
||||
if(_masks.size() == size) return true;
|
||||
_masks.reset();
|
||||
_masks.resize(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 _addressBits = 32;
|
||||
u32 _addressMask = 0;
|
||||
bool _mask = false;
|
||||
u32 _depth = 4;
|
||||
|
||||
//unserialized:
|
||||
n64 _address = 0;
|
||||
n64 _omitted = 0;
|
||||
vector<u32> _history;
|
||||
vector<u08> _masks;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
struct Notification : Tracer {
|
||||
DeclareClass(Notification, "debugger.tracer.notification")
|
||||
|
||||
Notification(string name = {}, string component = {}) : Tracer(name, component) {
|
||||
}
|
||||
|
||||
auto notify(const string& message = {}) -> void {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(message) {
|
||||
PlatformLog({_component, " ", _name, ": ", message, "\n"});
|
||||
} else {
|
||||
PlatformLog({_component, " ", _name, "\n"});
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Tracer : Object {
|
||||
DeclareClass(Tracer, "Tracer")
|
||||
struct Tracer : Debugger {
|
||||
DeclareClass(Tracer, "debugger.tracer")
|
||||
|
||||
Tracer(string name = {}, string component = {}) : Object(name) {
|
||||
Tracer(string name = {}, string component = {}) : Debugger(name) {
|
||||
_component = component;
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ struct Tracer : Object {
|
||||
auto setEnabled(bool enabled) -> void { _enabled = enabled; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
Debugger::serialize(output, depth);
|
||||
output.append(depth, " component: ", _component, "\n");
|
||||
output.append(depth, " enabled: ", _enabled, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
Debugger::unserialize(node);
|
||||
_component = node["component"].string();
|
||||
_enabled = node["enabled"].boolean();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
struct Input : Object {
|
||||
DeclareClass(Input, "Input")
|
||||
using Object::Object;
|
||||
};
|
||||
|
||||
struct Button : Input {
|
||||
DeclareClass(Button, "Button")
|
||||
using Input::Input;
|
||||
|
||||
auto value() const -> boolean { return _value; }
|
||||
auto setValue(boolean value) -> void { _value = value; }
|
||||
|
||||
protected:
|
||||
boolean _value;
|
||||
};
|
||||
|
||||
struct Axis : Input {
|
||||
DeclareClass(Axis, "Axis")
|
||||
using Input::Input;
|
||||
|
||||
auto value() const -> integer { return _value; }
|
||||
auto setValue(integer value) -> void { _value = value; }
|
||||
|
||||
protected:
|
||||
integer _value;
|
||||
integer _minimum = -32768;
|
||||
integer _maximum = +32767;
|
||||
};
|
||||
|
||||
struct Trigger : Input {
|
||||
DeclareClass(Trigger, "Trigger")
|
||||
using Input::Input;
|
||||
|
||||
auto value() const -> integer { return _value; }
|
||||
auto setValue(integer value) -> void { _value = value; }
|
||||
|
||||
protected:
|
||||
integer _value;
|
||||
integer _minimum = 0;
|
||||
integer _maximum = +32767;
|
||||
};
|
||||
|
||||
struct Rumble : Input {
|
||||
DeclareClass(Rumble, "Rumble")
|
||||
using Input::Input;
|
||||
|
||||
auto enable() const -> boolean { return _enable; }
|
||||
auto setEnable(boolean enable) -> void { _enable = enable; }
|
||||
|
||||
protected:
|
||||
boolean _enable;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
struct Axis : Input {
|
||||
DeclareClass(Axis, "input.axis")
|
||||
using Input::Input;
|
||||
|
||||
auto value() const -> s64 { return _value; }
|
||||
auto setValue(s64 value) -> void { _value = value; }
|
||||
|
||||
protected:
|
||||
s64 _value = 0;
|
||||
s64 _minimum = -32768;
|
||||
s64 _maximum = +32767;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
struct Button : Input {
|
||||
DeclareClass(Button, "input.button")
|
||||
using Input::Input;
|
||||
|
||||
auto value() const -> bool { return _value; }
|
||||
auto setValue(bool value) -> void { _value = value; }
|
||||
|
||||
protected:
|
||||
bool _value = 0;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
struct Input : Object {
|
||||
DeclareClass(Input, "input")
|
||||
using Object::Object;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
struct Rumble : Input {
|
||||
DeclareClass(Rumble, "input.rumble")
|
||||
using Input::Input;
|
||||
|
||||
auto enable() const -> bool { return _enable; }
|
||||
auto setEnable(bool enable) -> void { _enable = enable; }
|
||||
|
||||
protected:
|
||||
bool _enable = 0;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
struct Trigger : Input {
|
||||
DeclareClass(Trigger, "input.trigger")
|
||||
using Input::Input;
|
||||
|
||||
auto value() const -> s64 { return _value; }
|
||||
auto setValue(s64 value) -> void { _value = value; }
|
||||
|
||||
protected:
|
||||
s64 _value = 0;
|
||||
s64 _minimum = 0;
|
||||
s64 _maximum = +32767;
|
||||
};
|
||||
@@ -1,5 +1,9 @@
|
||||
namespace ares::Core {
|
||||
#include <ares/node/video/sprite.cpp>
|
||||
#include <ares/node/video/screen.cpp>
|
||||
#include <ares/node/audio/stream.cpp>
|
||||
namespace Video {
|
||||
#include <ares/node/video/sprite.cpp>
|
||||
#include <ares/node/video/screen.cpp>
|
||||
}
|
||||
namespace Audio {
|
||||
#include <ares/node/audio/stream.cpp>
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,97 @@
|
||||
namespace ares::Core {
|
||||
struct Object;
|
||||
struct System;
|
||||
struct System;
|
||||
struct Peripheral;
|
||||
struct Port;
|
||||
namespace Component {
|
||||
struct Component;
|
||||
struct RealTimeClock;
|
||||
struct Peripheral;
|
||||
struct Port;
|
||||
}
|
||||
namespace Video {
|
||||
struct Video;
|
||||
struct Sprite;
|
||||
struct Screen;
|
||||
}
|
||||
namespace Audio {
|
||||
struct Audio;
|
||||
struct Stream;
|
||||
}
|
||||
namespace Input {
|
||||
struct Input;
|
||||
struct Button;
|
||||
struct Axis;
|
||||
struct Trigger;
|
||||
struct Rumble;
|
||||
struct Settings;
|
||||
struct Button;
|
||||
struct Axis;
|
||||
struct Trigger;
|
||||
struct Rumble;
|
||||
}
|
||||
namespace Setting {
|
||||
struct Setting;
|
||||
struct Boolean;
|
||||
struct Natural;
|
||||
struct Integer;
|
||||
struct Real;
|
||||
struct String;
|
||||
//struct Video;
|
||||
struct Sprite;
|
||||
struct Screen;
|
||||
//struct Audio;
|
||||
struct Stream;
|
||||
//struct Debug;
|
||||
struct Memory;
|
||||
struct Graphics;
|
||||
struct Properties;
|
||||
struct Boolean;
|
||||
struct Natural;
|
||||
struct Integer;
|
||||
struct Real;
|
||||
struct String;
|
||||
}
|
||||
namespace Debugger {
|
||||
struct Debugger;
|
||||
struct Memory;
|
||||
struct Graphics;
|
||||
struct Properties;
|
||||
namespace Tracer {
|
||||
struct Tracer;
|
||||
struct Notification;
|
||||
struct Instruction;
|
||||
struct Notification;
|
||||
struct Instruction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ares::Node {
|
||||
using Object = shared_pointer<Core::Object>;
|
||||
using System = shared_pointer<Core::System>;
|
||||
using Component = shared_pointer<Core::Component>;
|
||||
using RealTimeClock = shared_pointer<Core::RealTimeClock>;
|
||||
using Peripheral = shared_pointer<Core::Peripheral>;
|
||||
using Port = shared_pointer<Core::Port>;
|
||||
using Input = shared_pointer<Core::Input>;
|
||||
using Button = shared_pointer<Core::Button>;
|
||||
using Axis = shared_pointer<Core::Axis>;
|
||||
using Trigger = shared_pointer<Core::Trigger>;
|
||||
using Rumble = shared_pointer<Core::Rumble>;
|
||||
using Settings = shared_pointer<Core::Settings>;
|
||||
using Setting = shared_pointer<Core::Setting>;
|
||||
using Boolean = shared_pointer<Core::Boolean>;
|
||||
using Natural = shared_pointer<Core::Natural>;
|
||||
using Integer = shared_pointer<Core::Integer>;
|
||||
using Real = shared_pointer<Core::Real>;
|
||||
using String = shared_pointer<Core::String>;
|
||||
//using Video;
|
||||
using Sprite = shared_pointer<Core::Sprite>;
|
||||
using Screen = shared_pointer<Core::Screen>;
|
||||
//using Audio;
|
||||
using Stream = shared_pointer<Core::Stream>;
|
||||
//using Debug;
|
||||
using Memory = shared_pointer<Core::Memory>;
|
||||
using Graphics = shared_pointer<Core::Graphics>;
|
||||
using Properties = shared_pointer<Core::Properties>;
|
||||
using Tracer = shared_pointer<Core::Tracer>;
|
||||
using Notification = shared_pointer<Core::Notification>;
|
||||
using Instruction = shared_pointer<Core::Instruction>;
|
||||
using Object = shared_pointer<Core::Object>;
|
||||
using System = shared_pointer<Core::System>;
|
||||
using Peripheral = shared_pointer<Core::Peripheral>;
|
||||
using Port = shared_pointer<Core::Port>;
|
||||
namespace Component {
|
||||
using Component = shared_pointer<Core::Component::Component>;
|
||||
using RealTimeClock = shared_pointer<Core::Component::RealTimeClock>;
|
||||
}
|
||||
namespace Video {
|
||||
using Video = shared_pointer<Core::Video::Video>;
|
||||
using Sprite = shared_pointer<Core::Video::Sprite>;
|
||||
using Screen = shared_pointer<Core::Video::Screen>;
|
||||
}
|
||||
namespace Audio {
|
||||
using Audio = shared_pointer<Core::Audio::Audio>;
|
||||
using Stream = shared_pointer<Core::Audio::Stream>;
|
||||
}
|
||||
namespace Input {
|
||||
using Input = shared_pointer<Core::Input::Input>;
|
||||
using Button = shared_pointer<Core::Input::Button>;
|
||||
using Axis = shared_pointer<Core::Input::Axis>;
|
||||
using Trigger = shared_pointer<Core::Input::Trigger>;
|
||||
using Rumble = shared_pointer<Core::Input::Rumble>;
|
||||
}
|
||||
namespace Setting {
|
||||
using Setting = shared_pointer<Core::Setting::Setting>;
|
||||
using Boolean = shared_pointer<Core::Setting::Boolean>;
|
||||
using Natural = shared_pointer<Core::Setting::Natural>;
|
||||
using Integer = shared_pointer<Core::Setting::Integer>;
|
||||
using Real = shared_pointer<Core::Setting::Real>;
|
||||
using String = shared_pointer<Core::Setting::String>;
|
||||
}
|
||||
namespace Debugger {
|
||||
using Debugger = shared_pointer<Core::Debugger::Debugger>;
|
||||
using Memory = shared_pointer<Core::Debugger::Memory>;
|
||||
using Graphics = shared_pointer<Core::Debugger::Graphics>;
|
||||
using Properties = shared_pointer<Core::Debugger::Properties>;
|
||||
namespace Tracer {
|
||||
using Tracer = shared_pointer<Core::Debugger::Tracer::Tracer>;
|
||||
using Notification = shared_pointer<Core::Debugger::Tracer::Notification>;
|
||||
using Instruction = shared_pointer<Core::Debugger::Tracer::Instruction>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ares::Core {
|
||||
// <emulator/platform.hpp> forward declarations (dependent on <emulator/node.hpp>)
|
||||
// <ares/platform.hpp> forward declarations
|
||||
static auto PlatformAttach(Node::Object) -> void;
|
||||
static auto PlatformDetach(Node::Object) -> void;
|
||||
static auto PlatformLog(string_view) -> void;
|
||||
@@ -74,21 +100,47 @@ namespace ares::Core {
|
||||
#include <ares/node/class.hpp>
|
||||
#include <ares/node/object.hpp>
|
||||
#include <ares/node/system.hpp>
|
||||
#include <ares/node/component.hpp>
|
||||
#include <ares/node/real-time-clock.hpp>
|
||||
#include <ares/node/peripheral.hpp>
|
||||
#include <ares/node/port.hpp>
|
||||
#include <ares/node/input.hpp>
|
||||
#include <ares/node/setting.hpp>
|
||||
#include <ares/node/video/sprite.hpp>
|
||||
#include <ares/node/video/screen.hpp>
|
||||
#include <ares/node/audio/stream.hpp>
|
||||
#include <ares/node/debug/memory.hpp>
|
||||
#include <ares/node/debug/graphics.hpp>
|
||||
#include <ares/node/debug/properties.hpp>
|
||||
#include <ares/node/debug/tracer.hpp>
|
||||
#include <ares/node/debug/notification.hpp>
|
||||
#include <ares/node/debug/instruction.hpp>
|
||||
namespace Component {
|
||||
#include <ares/node/component/component.hpp>
|
||||
#include <ares/node/component/real-time-clock.hpp>
|
||||
}
|
||||
namespace Video {
|
||||
#include <ares/node/video/video.hpp>
|
||||
#include <ares/node/video/sprite.hpp>
|
||||
#include <ares/node/video/screen.hpp>
|
||||
}
|
||||
namespace Audio {
|
||||
#include <ares/node/audio/audio.hpp>
|
||||
#include <ares/node/audio/stream.hpp>
|
||||
}
|
||||
namespace Input {
|
||||
#include <ares/node/input/input.hpp>
|
||||
#include <ares/node/input/button.hpp>
|
||||
#include <ares/node/input/axis.hpp>
|
||||
#include <ares/node/input/trigger.hpp>
|
||||
#include <ares/node/input/rumble.hpp>
|
||||
}
|
||||
namespace Setting {
|
||||
#include <ares/node/setting/setting.hpp>
|
||||
#include <ares/node/setting/boolean.hpp>
|
||||
#include <ares/node/setting/natural.hpp>
|
||||
#include <ares/node/setting/integer.hpp>
|
||||
#include <ares/node/setting/real.hpp>
|
||||
#include <ares/node/setting/string.hpp>
|
||||
}
|
||||
namespace Debugger {
|
||||
#include <ares/node/debugger/debugger.hpp>
|
||||
#include <ares/node/debugger/memory.hpp>
|
||||
#include <ares/node/debugger/graphics.hpp>
|
||||
#include <ares/node/debugger/properties.hpp>
|
||||
namespace Tracer {
|
||||
#include <ares/node/debugger/tracer/tracer.hpp>
|
||||
#include <ares/node/debugger/tracer/notification.hpp>
|
||||
#include <ares/node/debugger/tracer/instruction.hpp>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ares::Node {
|
||||
@@ -106,7 +158,7 @@ namespace ares::Node {
|
||||
static inline auto unserialize(string markup) -> Object {
|
||||
auto document = BML::unserialize(markup);
|
||||
if(!document) return {};
|
||||
auto node = Core::Class::create(document["node"].text());
|
||||
auto node = Core::Class::create(document["node"].string());
|
||||
node->unserialize(document["node"]);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
//identifier is static, allowing template<typename T> to access via T::identifier
|
||||
//identifier() is static, allowing template<typename T> to access via T::identifier()
|
||||
//identity() is virtual, allowing T* to access via T->identity()
|
||||
|
||||
#define DeclareClass(Type, Name) \
|
||||
static inline const string identifier = Name; \
|
||||
virtual auto identity() const -> string { return Name; } \
|
||||
static auto identifier() -> string { return Name; } \
|
||||
static auto create() -> Node::Object { return new Type; } \
|
||||
auto identity() const -> string override { return Name; } \
|
||||
private: static inline Class::Register<Type> register; public: \
|
||||
|
||||
struct Object : shared_pointer_this<Object> {
|
||||
DeclareClass(Object, "Object")
|
||||
static auto identifier() -> string { return "Object"; }
|
||||
static auto create() -> Node::Object { return new Object; }
|
||||
virtual auto identity() const -> string { return "Object"; }
|
||||
private: static inline Class::Register<Object> register; public:
|
||||
//DeclareClass(Object, "object")
|
||||
|
||||
Object(string name = {}) : _name(name) {}
|
||||
virtual ~Object() = default;
|
||||
@@ -86,7 +90,7 @@ struct Object : shared_pointer_this<Object> {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto find(uint index) -> shared_pointer<typename T::type> {
|
||||
auto find(u32 index) -> shared_pointer<typename T::type> {
|
||||
auto result = find<T>();
|
||||
if(index < result.size()) return result[index];
|
||||
return {};
|
||||
@@ -108,7 +112,7 @@ struct Object : shared_pointer_this<Object> {
|
||||
for(auto& node : _nodes) {
|
||||
if(node->_name != name) continue;
|
||||
if(path) return node->find<T>(path.merge("/"));
|
||||
if(node->identity() == Type::identifier) return node;
|
||||
if(node->identity() == Type::identifier()) return node;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -117,7 +121,7 @@ struct Object : shared_pointer_this<Object> {
|
||||
auto scan(string name) -> T {
|
||||
using Type = typename T::type;
|
||||
for(auto& node : _nodes) {
|
||||
if(node->identity() == Type::identifier && node->_name == name) return node;
|
||||
if(node->identity() == Type::identifier() && node->_name == name) return node;
|
||||
if(auto result = node->scan<T>(name)) return result;
|
||||
}
|
||||
return {};
|
||||
@@ -130,6 +134,15 @@ struct Object : shared_pointer_this<Object> {
|
||||
for(auto& node : _nodes) node->enumerate<T>(objects);
|
||||
}
|
||||
|
||||
auto pak() -> VFS::Pak {
|
||||
return _pak;
|
||||
}
|
||||
|
||||
auto setPak(VFS::Pak pak) -> bool {
|
||||
_pak = pak;
|
||||
return (bool)_pak;
|
||||
}
|
||||
|
||||
template<typename T = string>
|
||||
auto attribute(const string& name) const -> T {
|
||||
if(auto attribute = _attributes.find(name)) {
|
||||
@@ -138,6 +151,14 @@ struct Object : shared_pointer_this<Object> {
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T = string>
|
||||
auto hasAttribute(const string& name) const -> bool {
|
||||
if(auto attribute = _attributes.find(name)) {
|
||||
if(attribute->value.is<T>()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T = string, typename U = string>
|
||||
auto setAttribute(const string& name, const U& value = {}) -> void {
|
||||
if constexpr(is_same_v<T, string> && !is_same_v<U, string>) return setAttribute(name, string{value});
|
||||
@@ -178,13 +199,13 @@ struct Object : shared_pointer_this<Object> {
|
||||
|
||||
virtual auto unserialize(Markup::Node markup) -> void {
|
||||
if(!markup) return;
|
||||
_name = markup["name"].text();
|
||||
_name = markup["name"].string();
|
||||
_attributes.reset();
|
||||
for(auto& attribute : markup.find("attribute")) {
|
||||
_attributes.insert({attribute["name"].text(), attribute["value"].text()});
|
||||
_attributes.insert({attribute["name"].string(), attribute["value"].string()});
|
||||
}
|
||||
for(auto& leaf : markup.find("node")) {
|
||||
auto node = Class::create(leaf.text());
|
||||
auto node = Class::create(leaf.string());
|
||||
append(node);
|
||||
node->unserialize(leaf);
|
||||
}
|
||||
@@ -207,6 +228,7 @@ struct Object : shared_pointer_this<Object> {
|
||||
|
||||
protected:
|
||||
string _name;
|
||||
VFS::Pak _pak;
|
||||
set<Attribute> _attributes;
|
||||
shared_pointer_weak<Object> _parent;
|
||||
vector<Node::Object> _nodes;
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
struct Peripheral : Object {
|
||||
DeclareClass(Peripheral, "Peripheral")
|
||||
DeclareClass(Peripheral, "peripheral")
|
||||
using Object::Object;
|
||||
|
||||
auto manifest() -> string { if(_manifest) return _manifest(); return {}; }
|
||||
|
||||
auto setManifest(function<string()> manifest) -> void { _manifest = manifest; }
|
||||
|
||||
protected:
|
||||
function<string ()> _manifest;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
struct Port : Object {
|
||||
DeclareClass(Port, "Port")
|
||||
DeclareClass(Port, "port")
|
||||
using Object::Object;
|
||||
|
||||
auto type() const -> string { return _type; }
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
struct RealTimeClock : Object {
|
||||
DeclareClass(RealTimeClock, "Real Time Clock")
|
||||
using Object::Object;
|
||||
|
||||
auto update() -> void { if(_update) return _update(); }
|
||||
auto timestamp() const -> uint64 { return _timestamp; }
|
||||
|
||||
auto setUpdate(function<void ()> update) { _update = update; }
|
||||
auto setTimestamp(uint64 timestamp) -> void { _timestamp = timestamp; }
|
||||
|
||||
auto synchronize(uint64 timestamp = 0) -> void {
|
||||
if(!timestamp) timestamp = chrono::timestamp();
|
||||
_timestamp = timestamp;
|
||||
update();
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
output.append(depth, " timestamp: ", _timestamp, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
_timestamp = node["timestamp"].natural();
|
||||
}
|
||||
|
||||
protected:
|
||||
function<void ()> _update;
|
||||
uint64 _timestamp;
|
||||
};
|
||||
@@ -1,192 +0,0 @@
|
||||
struct Settings : Object {
|
||||
DeclareClass(Settings, "Settings")
|
||||
using Object::Object;
|
||||
};
|
||||
|
||||
struct Setting : Object {
|
||||
DeclareClass(Setting, "Setting")
|
||||
using Object::Object;
|
||||
|
||||
auto dynamic() const -> bool { return _dynamic; }
|
||||
|
||||
auto setDynamic(bool dynamic) -> void {
|
||||
_dynamic = dynamic;
|
||||
}
|
||||
|
||||
virtual auto setLatch() -> void {}
|
||||
|
||||
virtual auto readValue() const -> string { return {}; }
|
||||
virtual auto readLatch() const -> string { return {}; }
|
||||
virtual auto readAllowedValues() const -> vector<string> { return {}; }
|
||||
virtual auto writeValue(string value) -> void {}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
output.append(depth, " dynamic: ", _dynamic, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
_dynamic = node["dynamic"].boolean();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool _dynamic = false;
|
||||
};
|
||||
|
||||
template<typename Cast, typename Type> struct Abstract : Setting {
|
||||
using Setting::Setting;
|
||||
|
||||
Abstract(string name = {}, Type value = {}, function<void (Type)> modify = {}) : Setting(name), _defaultValue(value) {
|
||||
_currentValue = value;
|
||||
_latchedValue = value;
|
||||
if constexpr(is_same_v<Type, boolean>) _allowedValues = {false, true};
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto modify(Type value) const -> void { if(_modify) return _modify(value); }
|
||||
auto value() const -> Type { return _currentValue; }
|
||||
auto latch() const -> Type { return _latchedValue; }
|
||||
|
||||
auto setModify(function<void (Type)> modify) {
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto setValue(Type value) -> void {
|
||||
if(_allowedValues && !_allowedValues.find(value)) return;
|
||||
_currentValue = value;
|
||||
if(_dynamic) _latchedValue = _currentValue;
|
||||
modify(value);
|
||||
}
|
||||
|
||||
auto setLatch() -> void override {
|
||||
_latchedValue = _currentValue;
|
||||
}
|
||||
|
||||
auto setAllowedValues(vector<Type> allowedValues) -> void {
|
||||
_allowedValues = allowedValues;
|
||||
//if the current setting isn't an allowed value, change it to the first allowed value
|
||||
if(_allowedValues && !_allowedValues.find(_currentValue)) {
|
||||
_currentValue = _allowedValues.first();
|
||||
_latchedValue = _currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
auto readValue() const -> string override { return value(); }
|
||||
auto readLatch() const -> string override { return latch(); }
|
||||
auto readAllowedValues() const -> vector<string> override {
|
||||
vector<string> values;
|
||||
for(auto& value : _allowedValues) values.append(value);
|
||||
return values;
|
||||
}
|
||||
|
||||
auto writeValue(string value) -> void override {
|
||||
if(_allowedValues && !readAllowedValues().find(value)) return;
|
||||
if constexpr(is_same_v<Type, boolean>) _currentValue = value.boolean();
|
||||
if constexpr(is_same_v<Type, natural>) _currentValue = value.natural();
|
||||
if constexpr(is_same_v<Type, integer>) _currentValue = value.integer();
|
||||
if constexpr(is_same_v<Type, real >) _currentValue = value.real();
|
||||
if constexpr(is_same_v<Type, string >) _currentValue = value;
|
||||
if(_dynamic) _latchedValue = _currentValue;
|
||||
modify(_currentValue);
|
||||
}
|
||||
|
||||
auto load(Node::Object source) -> bool override {
|
||||
if(!Setting::load(source)) return false;
|
||||
if(auto setting = source->cast<shared_pointer<Cast>>()) {
|
||||
if(!_allowedValues || _allowedValues.find(setting->_currentValue)) {
|
||||
_currentValue = setting->_currentValue;
|
||||
_latchedValue = _currentValue;
|
||||
modify(_currentValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Setting::serialize(output, depth);
|
||||
output.append(depth, " value: ", _currentValue, "\n");
|
||||
if(_latchedValue) output.append(depth, " latch: ", _latchedValue, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Setting::unserialize(node);
|
||||
if constexpr(is_same_v<Type, boolean>) _currentValue = node["value"].boolean(), _latchedValue = node["latch"].boolean();
|
||||
if constexpr(is_same_v<Type, natural>) _currentValue = node["value"].natural(), _latchedValue = node["latch"].natural();
|
||||
if constexpr(is_same_v<Type, integer>) _currentValue = node["value"].integer(), _latchedValue = node["latch"].integer();
|
||||
if constexpr(is_same_v<Type, real>) _currentValue = node["value"].real(), _latchedValue = node["latch"].real();
|
||||
if constexpr(is_same_v<Type, string>) _currentValue = node["value"].text(), _latchedValue = node["latch"].text();
|
||||
}
|
||||
|
||||
protected:
|
||||
function<void (Type)> _modify;
|
||||
const Type _defaultValue;
|
||||
Type _currentValue;
|
||||
Type _latchedValue;
|
||||
vector<Type> _allowedValues;
|
||||
};
|
||||
|
||||
struct Boolean : Abstract<Boolean, boolean> {
|
||||
DeclareClass(Boolean, "Boolean")
|
||||
using Abstract::Abstract;
|
||||
|
||||
auto copy(Node::Object object) -> void override {
|
||||
if(auto source = object->cast<Node::Boolean>()) {
|
||||
Object::copy(source);
|
||||
setValue(source->value());
|
||||
setLatch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Natural : Abstract<Natural, natural> {
|
||||
DeclareClass(Natural, "Natural")
|
||||
using Abstract::Abstract;
|
||||
|
||||
auto copy(Node::Object object) -> void override {
|
||||
if(auto source = object->cast<Node::Natural>()) {
|
||||
Object::copy(source);
|
||||
setValue(source->value());
|
||||
setLatch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Integer : Abstract<Integer, integer> {
|
||||
DeclareClass(Integer, "Integer")
|
||||
using Abstract::Abstract;
|
||||
|
||||
auto copy(Node::Object object) -> void override {
|
||||
if(auto source = object->cast<Node::Integer>()) {
|
||||
Object::copy(source);
|
||||
setValue(source->value());
|
||||
setLatch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Real : Abstract<Real, real> {
|
||||
DeclareClass(Real, "Real")
|
||||
using Abstract::Abstract;
|
||||
|
||||
auto copy(Node::Object object) -> void override {
|
||||
if(auto source = object->cast<Node::Real>()) {
|
||||
Object::copy(source);
|
||||
setValue(source->value());
|
||||
setLatch();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct String : Abstract<String, string> {
|
||||
DeclareClass(String, "String")
|
||||
using Abstract::Abstract;
|
||||
|
||||
auto copy(Node::Object object) -> void override {
|
||||
if(auto source = object->cast<Node::String>()) {
|
||||
Object::copy(source);
|
||||
setValue(source->value());
|
||||
setLatch();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
struct Boolean : Setting {
|
||||
DeclareClass(Boolean, "setting.boolean")
|
||||
|
||||
Boolean(string name = {}, bool value = {}, function<void (bool)> modify = {}) : Setting(name) {
|
||||
_currentValue = value;
|
||||
_latchedValue = value;
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto modify(bool value) const -> void { if(_modify) return _modify(value); }
|
||||
auto value() const -> bool { return _currentValue; }
|
||||
auto latch() const -> bool { return _latchedValue; }
|
||||
|
||||
auto setModify(function<void (bool)> modify) { _modify = modify; }
|
||||
|
||||
auto setValue(bool value) -> void {
|
||||
_currentValue = value;
|
||||
if(_dynamic) setLatch();
|
||||
}
|
||||
|
||||
auto setLatch() -> void override {
|
||||
if(_latchedValue == _currentValue) return;
|
||||
_latchedValue = _currentValue;
|
||||
modify(_latchedValue);
|
||||
}
|
||||
|
||||
auto readValue() const -> string override { return value(); }
|
||||
auto readLatch() const -> string override { return latch(); }
|
||||
auto readAllowedValues() const -> vector<string> override { return {"false", "true"}; }
|
||||
auto writeValue(string value) -> void override { setValue(value.boolean()); }
|
||||
|
||||
protected:
|
||||
function<void (bool)> _modify;
|
||||
bool _currentValue = {};
|
||||
bool _latchedValue = {};
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
struct Integer : Setting {
|
||||
DeclareClass(Integer, "setting.integer")
|
||||
|
||||
Integer(string name = {}, s64 value = {}, function<void (s64)> modify = {}) : Setting(name) {
|
||||
_currentValue = value;
|
||||
_latchedValue = value;
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto modify(s64 value) const -> void { if(_modify) return _modify(value); }
|
||||
auto value() const -> s64 { return _currentValue; }
|
||||
auto latch() const -> s64 { return _latchedValue; }
|
||||
|
||||
auto setModify(function<void (s64)> modify) { _modify = modify; }
|
||||
|
||||
auto setValue(s64 value) -> void {
|
||||
if(_allowedValues && !_allowedValues.find(value)) return;
|
||||
_currentValue = value;
|
||||
if(_dynamic) setLatch();
|
||||
}
|
||||
|
||||
auto setLatch() -> void override {
|
||||
if(_latchedValue == _currentValue) return;
|
||||
_latchedValue = _currentValue;
|
||||
modify(_latchedValue);
|
||||
}
|
||||
|
||||
auto setAllowedValues(vector<s64> allowedValues) -> void {
|
||||
_allowedValues = allowedValues;
|
||||
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
|
||||
}
|
||||
|
||||
auto readValue() const -> string override { return value(); }
|
||||
auto readLatch() const -> string override { return latch(); }
|
||||
auto readAllowedValues() const -> vector<string> override {
|
||||
vector<string> values;
|
||||
for(auto value : _allowedValues) values.append(value);
|
||||
return values;
|
||||
}
|
||||
auto writeValue(string value) -> void override { setValue(value.integer()); }
|
||||
|
||||
protected:
|
||||
function<void (s64)> _modify;
|
||||
s64 _currentValue = {};
|
||||
s64 _latchedValue = {};
|
||||
vector<s64> _allowedValues;
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
struct Natural : Setting {
|
||||
DeclareClass(Natural, "setting.natural")
|
||||
|
||||
Natural(string name = {}, u64 value = {}, function<void (u64)> modify = {}) : Setting(name) {
|
||||
_currentValue = value;
|
||||
_latchedValue = value;
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto modify(u64 value) const -> void { if(_modify) return _modify(value); }
|
||||
auto value() const -> u64 { return _currentValue; }
|
||||
auto latch() const -> u64 { return _latchedValue; }
|
||||
|
||||
auto setModify(function<void (u64)> modify) { _modify = modify; }
|
||||
|
||||
auto setValue(u64 value) -> void {
|
||||
if(_allowedValues && !_allowedValues.find(value)) return;
|
||||
_currentValue = value;
|
||||
if(_dynamic) setLatch();
|
||||
}
|
||||
|
||||
auto setLatch() -> void override {
|
||||
if(_latchedValue == _currentValue) return;
|
||||
_latchedValue = _currentValue;
|
||||
modify(_latchedValue);
|
||||
}
|
||||
|
||||
auto setAllowedValues(vector<u64> allowedValues) -> void {
|
||||
_allowedValues = allowedValues;
|
||||
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
|
||||
}
|
||||
|
||||
auto readValue() const -> string override { return value(); }
|
||||
auto readLatch() const -> string override { return latch(); }
|
||||
auto readAllowedValues() const -> vector<string> override {
|
||||
vector<string> values;
|
||||
for(auto value : _allowedValues) values.append(value);
|
||||
return values;
|
||||
}
|
||||
auto writeValue(string value) -> void override { setValue(value.natural()); }
|
||||
|
||||
protected:
|
||||
function<void (u64)> _modify;
|
||||
u64 _currentValue = {};
|
||||
u64 _latchedValue = {};
|
||||
vector<u64> _allowedValues;
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
struct Real : Setting {
|
||||
DeclareClass(Real, "setting.real")
|
||||
|
||||
Real(string name = {}, f64 value = {}, function<void (f64)> modify = {}) : Setting(name) {
|
||||
_currentValue = value;
|
||||
_latchedValue = value;
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto modify(f64 value) const -> void { if(_modify) return _modify(value); }
|
||||
auto value() const -> f64 { return _currentValue; }
|
||||
auto latch() const -> f64 { return _latchedValue; }
|
||||
|
||||
auto setModify(function<void (f64)> modify) { _modify = modify; }
|
||||
|
||||
auto setValue(f64 value) -> void {
|
||||
if(_allowedValues && !_allowedValues.find(value)) return;
|
||||
_currentValue = value;
|
||||
if(_dynamic) setLatch();
|
||||
}
|
||||
|
||||
auto setLatch() -> void override {
|
||||
if(_latchedValue == _currentValue) return;
|
||||
_latchedValue = _currentValue;
|
||||
modify(_latchedValue);
|
||||
}
|
||||
|
||||
auto setAllowedValues(vector<f64> allowedValues) -> void {
|
||||
_allowedValues = allowedValues;
|
||||
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
|
||||
}
|
||||
|
||||
auto readValue() const -> string override { return value(); }
|
||||
auto readLatch() const -> string override { return latch(); }
|
||||
auto readAllowedValues() const -> vector<string> override {
|
||||
vector<string> values;
|
||||
for(auto value : _allowedValues) values.append(value);
|
||||
return values;
|
||||
}
|
||||
auto writeValue(string value) -> void override { setValue(value.real()); }
|
||||
|
||||
protected:
|
||||
function<void (f64)> _modify;
|
||||
f64 _currentValue = {};
|
||||
f64 _latchedValue = {};
|
||||
vector<f64> _allowedValues;
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
struct Setting : Object {
|
||||
DeclareClass(Setting, "setting")
|
||||
using Object::Object;
|
||||
|
||||
auto dynamic() const -> bool { return _dynamic; }
|
||||
|
||||
auto setDynamic(bool dynamic) -> void {
|
||||
_dynamic = dynamic;
|
||||
}
|
||||
|
||||
virtual auto setLatch() -> void {}
|
||||
|
||||
virtual auto readValue() const -> string { return {}; }
|
||||
virtual auto readLatch() const -> string { return {}; }
|
||||
virtual auto readAllowedValues() const -> vector<string> { return {}; }
|
||||
virtual auto writeValue(string value) -> void {}
|
||||
|
||||
auto load(Node::Object source) -> bool override {
|
||||
if(!Object::load(source)) return false;
|
||||
if(auto setting = source->cast<shared_pointer<Setting>>()) writeValue(setting->readValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto copy(Node::Object object) -> void override {
|
||||
if(auto source = object->cast<Node::Setting::Setting>()) {
|
||||
Object::copy(source);
|
||||
writeValue(source->readValue());
|
||||
setLatch();
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Object::serialize(output, depth);
|
||||
output.append(depth, " dynamic: ", _dynamic, "\n");
|
||||
output.append(depth, " value: ", readValue(), "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Object::unserialize(node);
|
||||
_dynamic = node["dynamic"].boolean();
|
||||
writeValue(node["value"].string());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool _dynamic = false;
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
struct String : Setting {
|
||||
DeclareClass(String, "setting.string")
|
||||
|
||||
String(string name = {}, string value = {}, function<void (string)> modify = {}) : Setting(name) {
|
||||
_currentValue = value;
|
||||
_latchedValue = value;
|
||||
_modify = modify;
|
||||
}
|
||||
|
||||
auto modify(string value) const -> void { if(_modify) return _modify(value); }
|
||||
auto value() const -> string { return _currentValue; }
|
||||
auto latch() const -> string { return _latchedValue; }
|
||||
|
||||
auto setModify(function<void (string)> modify) { _modify = modify; }
|
||||
|
||||
auto setValue(string value) -> void {
|
||||
if(_allowedValues && !_allowedValues.find(value)) return;
|
||||
_currentValue = value;
|
||||
if(_dynamic) setLatch();
|
||||
}
|
||||
|
||||
auto setLatch() -> void override {
|
||||
if(_latchedValue == _currentValue) return;
|
||||
_latchedValue = _currentValue;
|
||||
modify(_latchedValue);
|
||||
}
|
||||
|
||||
auto setAllowedValues(vector<string> allowedValues) -> void {
|
||||
_allowedValues = allowedValues;
|
||||
if(_allowedValues && !_allowedValues.find(_currentValue)) setValue(_allowedValues.first());
|
||||
}
|
||||
|
||||
auto readValue() const -> string override { return value(); }
|
||||
auto readLatch() const -> string override { return latch(); }
|
||||
auto readAllowedValues() const -> vector<string> override { return _allowedValues; }
|
||||
auto writeValue(string value) -> void override { setValue(value); }
|
||||
|
||||
protected:
|
||||
function<void (string)> _modify;
|
||||
string _currentValue = {};
|
||||
string _latchedValue = {};
|
||||
vector<string> _allowedValues;
|
||||
};
|
||||
@@ -1,4 +1,29 @@
|
||||
struct System : Object {
|
||||
DeclareClass(System, "System")
|
||||
DeclareClass(System, "system")
|
||||
using Object::Object;
|
||||
|
||||
auto game() -> string { if(_game) return _game(); return {}; }
|
||||
auto run() -> void { if(_run) return _run(); }
|
||||
auto power(bool reset = false) -> void { if(_power) return _power(reset); }
|
||||
auto save() -> void { if(_save) return _save(); }
|
||||
auto unload() -> void { if(_unload) return _unload(); }
|
||||
auto serialize(bool synchronize = true) -> serializer { if(_serialize) return _serialize(synchronize); return {}; }
|
||||
auto unserialize(serializer& s) -> bool { if(_unserialize) return _unserialize(s); return false; }
|
||||
|
||||
auto setGame(function<string ()> game) -> void { _game = game; }
|
||||
auto setRun(function<void ()> run) -> void { _run = run; }
|
||||
auto setPower(function<void (bool)> power) -> void { _power = power; }
|
||||
auto setSave(function<void ()> save) -> void { _save = save; }
|
||||
auto setUnload(function<void ()> unload) -> void { _unload = unload; }
|
||||
auto setSerialize(function<serializer (bool)> serialize) -> void { _serialize = serialize; }
|
||||
auto setUnserialize(function<bool (serializer&)> unserialize) -> void { _unserialize = unserialize; }
|
||||
|
||||
protected:
|
||||
function<string ()> _game;
|
||||
function<void ()> _run;
|
||||
function<void (bool)> _power;
|
||||
function<void ()> _save;
|
||||
function<void ()> _unload;
|
||||
function<serializer (bool)> _serialize;
|
||||
function<bool (serializer&)> _unserialize;
|
||||
};
|
||||
@@ -1,106 +1,237 @@
|
||||
Screen::Screen(string name, u32 width, u32 height) : Video(name) {
|
||||
_canvasWidth = width;
|
||||
_canvasHeight = height;
|
||||
|
||||
if(width && height) {
|
||||
_inputA = new u32[width * height]();
|
||||
_inputB = new u32[width * height]();
|
||||
_output = new u32[width * height]();
|
||||
_rotate = new u32[width * height]();
|
||||
|
||||
if constexpr(ares::Video::Threaded) {
|
||||
_thread = nall::thread::create({&Screen::main, this});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
if constexpr(ares::Video::Threaded) {
|
||||
if(_canvasWidth && _canvasHeight) {
|
||||
_kill = true;
|
||||
_thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Screen::main(uintptr_t) -> void {
|
||||
while(!_kill) {
|
||||
usleep(1);
|
||||
if(_frame) {
|
||||
refresh();
|
||||
_frame = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Screen::quit() -> void {
|
||||
_kill = true;
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
auto Screen::power() -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
memory::fill<u32>(_inputA.data(), _canvasWidth * _canvasHeight, _fillColor);
|
||||
memory::fill<u32>(_inputB.data(), _canvasWidth * _canvasHeight, _fillColor);
|
||||
memory::fill<u32>(_output.data(), _canvasWidth * _canvasHeight, _fillColor);
|
||||
memory::fill<u32>(_rotate.data(), _canvasWidth * _canvasHeight, _fillColor);
|
||||
}
|
||||
|
||||
auto Screen::pixels(bool frame) -> array_span<u32> {
|
||||
if(frame == 0) return {_inputA.data(), _canvasWidth * _canvasHeight};
|
||||
if(frame == 1) return {_inputB.data(), _canvasWidth * _canvasHeight};
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Screen::resetPalette() -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_palette.reset();
|
||||
}
|
||||
|
||||
auto Screen::resetSprites() -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_sprites.reset();
|
||||
}
|
||||
|
||||
auto Screen::attach(Node::Sprite sprite) -> void {
|
||||
auto Screen::setRefresh(function<void ()> refresh) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_refresh = refresh;
|
||||
}
|
||||
|
||||
auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_viewportX = x;
|
||||
_viewportY = y;
|
||||
_viewportWidth = width;
|
||||
_viewportHeight = height;
|
||||
}
|
||||
|
||||
auto Screen::setSize(u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
auto Screen::setScale(f64 scaleX, f64 scaleY) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_scaleX = scaleX;
|
||||
_scaleY = scaleY;
|
||||
}
|
||||
|
||||
auto Screen::setAspect(f64 aspectX, f64 aspectY) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_aspectX = aspectX;
|
||||
_aspectY = aspectY;
|
||||
}
|
||||
|
||||
auto Screen::setSaturation(f64 saturation) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_saturation = saturation;
|
||||
_palette.reset();
|
||||
}
|
||||
|
||||
auto Screen::setGamma(f64 gamma) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_gamma = gamma;
|
||||
_palette.reset();
|
||||
}
|
||||
|
||||
auto Screen::setLuminance(f64 luminance) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_luminance = luminance;
|
||||
_palette.reset();
|
||||
}
|
||||
|
||||
auto Screen::setFillColor(u32 fillColor) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_fillColor = fillColor;
|
||||
}
|
||||
|
||||
auto Screen::setColorBleed(bool colorBleed) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_colorBleed = colorBleed;
|
||||
}
|
||||
|
||||
auto Screen::setInterframeBlending(bool interframeBlending) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_interframeBlending = interframeBlending;
|
||||
}
|
||||
|
||||
auto Screen::setRotation(u32 rotation) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_rotation = rotation;
|
||||
}
|
||||
|
||||
auto Screen::setProgressive(bool progressiveDouble) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_interlace = false;
|
||||
_progressive = true;
|
||||
_progressiveDouble = progressiveDouble;
|
||||
}
|
||||
|
||||
auto Screen::setInterlace(bool interlaceField) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_progressive = false;
|
||||
_interlace = true;
|
||||
_interlaceField = interlaceField;
|
||||
}
|
||||
|
||||
auto Screen::attach(Node::Video::Sprite sprite) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(_sprites.find(sprite)) return;
|
||||
_sprites.append(sprite);
|
||||
}
|
||||
|
||||
auto Screen::detach(Node::Sprite sprite) -> void {
|
||||
auto Screen::detach(Node::Video::Sprite sprite) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(!_sprites.find(sprite)) return;
|
||||
_sprites.removeByValue(sprite);
|
||||
}
|
||||
|
||||
auto Screen::colors(uint colors, function<uint64 (uint32)> color) -> void {
|
||||
auto Screen::colors(u32 colors, function<n64 (n32)> color) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_colors = colors;
|
||||
_color = color;
|
||||
_palette.reset();
|
||||
}
|
||||
|
||||
auto Screen::refresh(uint32* input, uint pitch, uint width, uint height) -> void {
|
||||
auto Screen::frame() -> void {
|
||||
if(runAhead()) return;
|
||||
while(_frame) spinloop();
|
||||
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_inputA.swap(_inputB);
|
||||
_frame = true;
|
||||
if constexpr(!ares::Video::Threaded) {
|
||||
refresh();
|
||||
_frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto Screen::refresh() -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(runAhead()) return;
|
||||
|
||||
//allocate the screen buffers (only when growing)
|
||||
if(_renderWidth != width || _renderHeight != height) {
|
||||
if(_renderWidth * _renderHeight < width * height) {
|
||||
_buffer = new uint32[width * height];
|
||||
_rotate = new uint32[width * height];
|
||||
}
|
||||
}
|
||||
refreshPalette();
|
||||
if(_refresh) _refresh();
|
||||
|
||||
//generate the color lookup palettes to convert native colors to ARGB8888
|
||||
if(!_palette) {
|
||||
_palette = new uint32[_colors];
|
||||
for(uint index : range(_colors)) {
|
||||
uint64 color = _color(index);
|
||||
uint16 b = color.bit( 0,15);
|
||||
uint16 g = color.bit(16,31);
|
||||
uint16 r = color.bit(32,47);
|
||||
uint16 a = 65535;
|
||||
auto viewX = _viewportX;
|
||||
auto viewY = _viewportY;
|
||||
auto viewWidth = _viewportWidth;
|
||||
auto viewHeight = _viewportHeight;
|
||||
|
||||
if(_saturation != 1.0) {
|
||||
uint16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
double inverse = max(0.0, 1.0 - _saturation);
|
||||
r = uclamp<16>(r * _saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * _saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * _saturation + grayscale * inverse);
|
||||
}
|
||||
auto pitch = _canvasWidth;
|
||||
auto width = _canvasWidth;
|
||||
auto height = _canvasHeight;
|
||||
auto input = _inputB.data();
|
||||
auto output = _output.data();
|
||||
|
||||
if(_gamma != 1.0) {
|
||||
double reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, _gamma));
|
||||
g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, _gamma));
|
||||
b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, _gamma));
|
||||
}
|
||||
|
||||
if(_luminance != 1.0) {
|
||||
r = uclamp<16>(r * _luminance);
|
||||
g = uclamp<16>(g * _luminance);
|
||||
b = uclamp<16>(b * _luminance);
|
||||
}
|
||||
|
||||
a >>= 8;
|
||||
r >>= 8;
|
||||
g >>= 8;
|
||||
b >>= 8;
|
||||
|
||||
_palette[index] = a << 24 | r << 16 | g << 8 | b << 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto output = _buffer.data();
|
||||
pitch >>= 2; //bytes to words
|
||||
|
||||
for(uint y : range(height)) {
|
||||
auto source = input + y * pitch;
|
||||
for(u32 y : range(height)) {
|
||||
auto source = input + y * pitch;
|
||||
auto target = output + y * width;
|
||||
|
||||
//if not blending, or if previous frame resolution was different, render normally
|
||||
if(!_interframeBlending || width != _renderWidth || height != _renderHeight) {
|
||||
for(uint x : range(width)) {
|
||||
if(_interlace) {
|
||||
if((_interlaceField & 1) == (y & 1)) {
|
||||
for(u32 x : range(width)) {
|
||||
auto color = _palette[*source++];
|
||||
*target++ = color;
|
||||
}
|
||||
}
|
||||
} else if(_progressive && _progressiveDouble) {
|
||||
source = input + (y & ~1) * pitch;
|
||||
for(u32 x : range(width)) {
|
||||
auto color = _palette[*source++];
|
||||
*target++ = color;
|
||||
}
|
||||
} else {
|
||||
uint32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
for(uint x : range(width)) {
|
||||
} else if(_interframeBlending) {
|
||||
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
for(u32 x : range(width)) {
|
||||
auto a = *target;
|
||||
auto b = _palette[*source++];
|
||||
*target++ = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
}
|
||||
} else {
|
||||
for(u32 x : range(width)) {
|
||||
auto color = _palette[*source++];
|
||||
*target++ = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_colorBleed) {
|
||||
uint32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 9;
|
||||
for(uint y : range(height)) {
|
||||
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
for(u32 y : range(height)) {
|
||||
auto target = output + y * width;
|
||||
for(uint x : range(width)) {
|
||||
for(u32 x : range(width)) {
|
||||
auto a = target[x];
|
||||
auto b = target[x + (x != width - 1)];
|
||||
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
@@ -111,15 +242,15 @@ auto Screen::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
||||
for(auto& sprite : _sprites) {
|
||||
if(!sprite->visible()) continue;
|
||||
|
||||
uint32 alpha = 255u << 24;
|
||||
n32 alpha = 255u << 24;
|
||||
for(int y : range(sprite->height())) {
|
||||
int pixelY = sprite->y() + y;
|
||||
s32 pixelY = sprite->y() + y;
|
||||
if(pixelY < 0 || pixelY >= height) continue;
|
||||
|
||||
auto source = sprite->image() + y * sprite->width();
|
||||
auto source = sprite->image().data() + y * sprite->width();
|
||||
auto target = &output[pixelY * width];
|
||||
for(int x : range(sprite->width())) {
|
||||
int pixelX = sprite->x() + x;
|
||||
for(s32 x : range(sprite->width())) {
|
||||
s32 pixelX = sprite->x() + x;
|
||||
if(pixelX < 0 || pixelX >= width) continue;
|
||||
|
||||
auto pixel = source[x];
|
||||
@@ -130,22 +261,23 @@ auto Screen::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
||||
|
||||
if(_rotation == 90) {
|
||||
//rotate left
|
||||
for(uint y : range(height)) {
|
||||
for(u32 y : range(height)) {
|
||||
auto source = output + y * width;
|
||||
for(uint x : range(width)) {
|
||||
for(u32 x : range(width)) {
|
||||
auto target = _rotate.data() + (width - 1 - x) * height + y;
|
||||
*target = *source++;
|
||||
}
|
||||
}
|
||||
output = _rotate.data();
|
||||
swap(width, height);
|
||||
swap(viewWidth, viewHeight);
|
||||
}
|
||||
|
||||
if(_rotation == 180) {
|
||||
//rotate upside down
|
||||
for(uint y : range(height)) {
|
||||
for(u32 y : range(height)) {
|
||||
auto source = output + y * width;
|
||||
for(uint x : range(width)) {
|
||||
for(u32 x : range(width)) {
|
||||
auto target = _rotate.data() + (height - 1 - y) * width + (width - 1 - x);
|
||||
*target = *source++;
|
||||
}
|
||||
@@ -155,25 +287,67 @@ auto Screen::refresh(uint32* input, uint pitch, uint width, uint height) -> void
|
||||
|
||||
if(_rotation == 270) {
|
||||
//rotate right
|
||||
for(uint y : range(height)) {
|
||||
for(u32 y : range(height)) {
|
||||
auto source = output + y * width;
|
||||
for(uint x : range(width)) {
|
||||
for(u32 x : range(width)) {
|
||||
auto target = _rotate.data() + x * height + (height - 1 - y);
|
||||
*target = *source++;
|
||||
}
|
||||
}
|
||||
output = _rotate.data();
|
||||
swap(width, height);
|
||||
swap(viewWidth, viewHeight);
|
||||
}
|
||||
|
||||
platform->video(shared(), (const uint32_t*)output, width * sizeof(uint32), width, height);
|
||||
platform->video(shared(), output + viewX + viewY * width, width * sizeof(u32), viewWidth, viewHeight);
|
||||
memory::fill<u32>(_inputB.data(), width * height, _fillColor);
|
||||
}
|
||||
|
||||
_renderWidth = width;
|
||||
_renderHeight = height;
|
||||
auto Screen::refreshPalette() -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
if(_palette) return;
|
||||
|
||||
//generate the color lookup palettes to convert native colors to ARGB8888
|
||||
_palette = new u32[_colors];
|
||||
for(u32 index : range(_colors)) {
|
||||
n64 color = _color(index);
|
||||
n16 b = color.bit( 0,15);
|
||||
n16 g = color.bit(16,31);
|
||||
n16 r = color.bit(32,47);
|
||||
n16 a = 65535;
|
||||
|
||||
if(_saturation != 1.0) {
|
||||
n16 grayscale = uclamp<16>((r + g + b) / 3);
|
||||
f64 inverse = max(0.0, 1.0 - _saturation);
|
||||
r = uclamp<16>(r * _saturation + grayscale * inverse);
|
||||
g = uclamp<16>(g * _saturation + grayscale * inverse);
|
||||
b = uclamp<16>(b * _saturation + grayscale * inverse);
|
||||
}
|
||||
|
||||
if(_gamma != 1.0) {
|
||||
f64 reciprocal = 1.0 / 32767.0;
|
||||
r = r > 32767 ? r : n16(32767 * pow(r * reciprocal, _gamma));
|
||||
g = g > 32767 ? g : n16(32767 * pow(g * reciprocal, _gamma));
|
||||
b = b > 32767 ? b : n16(32767 * pow(b * reciprocal, _gamma));
|
||||
}
|
||||
|
||||
if(_luminance != 1.0) {
|
||||
r = uclamp<16>(r * _luminance);
|
||||
g = uclamp<16>(g * _luminance);
|
||||
b = uclamp<16>(b * _luminance);
|
||||
}
|
||||
|
||||
a >>= 8;
|
||||
r >>= 8;
|
||||
g >>= 8;
|
||||
b >>= 8;
|
||||
|
||||
_palette[index] = a << 24 | r << 16 | g << 8 | b << 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto Screen::serialize(string& output, string depth) -> void {
|
||||
Object::serialize(output, depth);
|
||||
Video::serialize(output, depth);
|
||||
output.append(depth, " width: ", _width, "\n");
|
||||
output.append(depth, " height: ", _height, "\n");
|
||||
output.append(depth, " scaleX: ", _scaleX, "\n");
|
||||
@@ -184,13 +358,15 @@ auto Screen::serialize(string& output, string depth) -> void {
|
||||
output.append(depth, " saturation: ", _saturation, "\n");
|
||||
output.append(depth, " gamma: ", _gamma, "\n");
|
||||
output.append(depth, " luminance: ", _luminance, "\n");
|
||||
output.append(depth, " fillColor: ", _fillColor, "\n");
|
||||
output.append(depth, " colorBleed: ", _colorBleed, "\n");
|
||||
output.append(depth, " interlace: ", _interlace, "\n");
|
||||
output.append(depth, " interframeBlending: ", _interframeBlending, "\n");
|
||||
output.append(depth, " rotation: ", _rotation, "\n");
|
||||
}
|
||||
|
||||
auto Screen::unserialize(Markup::Node node) -> void {
|
||||
Object::unserialize(node);
|
||||
Video::unserialize(node);
|
||||
_width = node["width"].natural();
|
||||
_height = node["height"].natural();
|
||||
_scaleX = node["scaleX"].real();
|
||||
@@ -201,7 +377,9 @@ auto Screen::unserialize(Markup::Node node) -> void {
|
||||
_saturation = node["saturation"].real();
|
||||
_gamma = node["gamma"].real();
|
||||
_luminance = node["luminance"].real();
|
||||
_fillColor = node["fillColor"].natural();
|
||||
_colorBleed = node["colorBleed"].boolean();
|
||||
_interlace = node["interlace"].natural();
|
||||
_interframeBlending = node["interframeBlending"].boolean();
|
||||
_rotation = node["rotation"].natural();
|
||||
resetPalette();
|
||||
|
||||
@@ -1,68 +1,106 @@
|
||||
struct Screen : Object {
|
||||
DeclareClass(Screen, "Screen")
|
||||
using Object::Object;
|
||||
struct Screen : Video {
|
||||
DeclareClass(Screen, "video.screen")
|
||||
using Video::Video;
|
||||
|
||||
auto width() const -> uint { return _width; }
|
||||
auto height() const -> uint { return _height; }
|
||||
auto scaleX() const -> double { return _scaleX; }
|
||||
auto scaleY() const -> double { return _scaleY; }
|
||||
auto aspectX() const -> double { return _aspectX; }
|
||||
auto aspectY() const -> double { return _aspectY; }
|
||||
auto colors() const -> uint { return _colors; }
|
||||
Screen(string name = {}, u32 width = 0, u32 height = 0);
|
||||
~Screen();
|
||||
auto main(uintptr_t) -> void;
|
||||
auto quit() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto canvasWidth() const -> u32 { return _canvasWidth; }
|
||||
auto canvasHeight() const -> u32 { return _canvasHeight; }
|
||||
auto width() const -> u32 { return _width; }
|
||||
auto height() const -> u32 { return _height; }
|
||||
auto scaleX() const -> f64 { return _scaleX; }
|
||||
auto scaleY() const -> f64 { return _scaleY; }
|
||||
auto aspectX() const -> f64 { return _aspectX; }
|
||||
auto aspectY() const -> f64 { return _aspectY; }
|
||||
auto colors() const -> u32 { return _colors; }
|
||||
auto pixels(bool frame = 0) -> array_span<u32>;
|
||||
|
||||
auto saturation() const -> double { return _saturation; }
|
||||
auto gamma() const -> double { return _gamma; }
|
||||
auto luminance() const -> double { return _luminance; }
|
||||
|
||||
auto fillColor() const -> u32 { return _fillColor; }
|
||||
auto colorBleed() const -> bool { return _colorBleed; }
|
||||
auto interframeBlending() const -> bool { return _interframeBlending; }
|
||||
auto rotation() const -> uint { return _rotation; }
|
||||
auto rotation() const -> u32 { return _rotation; }
|
||||
|
||||
auto resetPalette() -> void;
|
||||
auto resetSprites() -> void;
|
||||
|
||||
auto setSize(uint width, uint height) -> void { _width = width, _height = height; }
|
||||
auto setScale(double scaleX, double scaleY) -> void { _scaleX = scaleX, _scaleY = scaleY; }
|
||||
auto setAspect(double aspectX, double aspectY) -> void { _aspectX = aspectX, _aspectY = aspectY; }
|
||||
auto setRefresh(function<void ()> refresh) -> void;
|
||||
auto setViewport(u32 x, u32 y, u32 width, u32 height) -> void;
|
||||
|
||||
auto setSaturation(double saturation) -> void { _saturation = saturation; _palette.reset(); }
|
||||
auto setGamma(double gamma) -> void { _gamma = gamma; _palette.reset(); }
|
||||
auto setLuminance(double luminance) -> void { _luminance = luminance; _palette.reset(); }
|
||||
auto setSize(u32 width, u32 height) -> void;
|
||||
auto setScale(f64 scaleX, f64 scaleY) -> void;
|
||||
auto setAspect(f64 aspectX, f64 aspectY) -> void;
|
||||
|
||||
auto setColorBleed(bool colorBleed) -> void { _colorBleed = colorBleed; }
|
||||
auto setInterframeBlending(bool interframeBlending) -> void { _interframeBlending = interframeBlending; }
|
||||
auto setRotation(uint rotation) -> void { _rotation = rotation; }
|
||||
auto setSaturation(f64 saturation) -> void;
|
||||
auto setGamma(f64 gamma) -> void;
|
||||
auto setLuminance(f64 luminance) -> void;
|
||||
|
||||
auto attach(Node::Sprite) -> void;
|
||||
auto detach(Node::Sprite) -> void;
|
||||
auto setFillColor(u32 fillColor) -> void;
|
||||
auto setColorBleed(bool colorBleed) -> void;
|
||||
auto setInterframeBlending(bool interframeBlending) -> void;
|
||||
auto setRotation(u32 rotation) -> void;
|
||||
|
||||
auto colors(uint colors, function<uint64 (uint32)> color) -> void;
|
||||
auto refresh(uint32* input, uint pitch, uint width, uint height) -> void;
|
||||
auto setProgressive(bool progressiveDouble = false) -> void;
|
||||
auto setInterlace(bool interlaceField) -> void;
|
||||
|
||||
auto attach(Node::Video::Sprite) -> void;
|
||||
auto detach(Node::Video::Sprite) -> void;
|
||||
|
||||
auto colors(u32 colors, function<n64 (n32)> color) -> void;
|
||||
auto frame() -> void;
|
||||
auto refresh() -> void;
|
||||
|
||||
auto serialize(string& output, string depth) -> void override;
|
||||
auto unserialize(Markup::Node node) -> void override;
|
||||
|
||||
private:
|
||||
auto refreshPalette() -> void;
|
||||
|
||||
protected:
|
||||
uint _width = 0;
|
||||
uint _height = 0;
|
||||
double _scaleX = 1.0;
|
||||
double _scaleY = 1.0;
|
||||
double _aspectX = 1.0;
|
||||
double _aspectY = 1.0;
|
||||
uint _colors = 0;
|
||||
double _saturation = 1.0;
|
||||
double _gamma = 1.0;
|
||||
double _luminance = 1.0;
|
||||
u32 _canvasWidth = 0;
|
||||
u32 _canvasHeight = 0;
|
||||
u32 _width = 0;
|
||||
u32 _height = 0;
|
||||
f64 _scaleX = 1.0;
|
||||
f64 _scaleY = 1.0;
|
||||
f64 _aspectX = 1.0;
|
||||
f64 _aspectY = 1.0;
|
||||
u32 _colors = 0;
|
||||
f64 _saturation = 1.0;
|
||||
f64 _gamma = 1.0;
|
||||
f64 _luminance = 1.0;
|
||||
u32 _fillColor = 0;
|
||||
bool _colorBleed = false;
|
||||
bool _interframeBlending = false;
|
||||
uint _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
function<uint64 (uint32)> _color;
|
||||
unique_pointer<uint32[]> _buffer;
|
||||
unique_pointer<uint32[]> _rotate;
|
||||
unique_pointer<uint32[]> _palette;
|
||||
vector<Node::Sprite> _sprites;
|
||||
function<n64 (n32)> _color;
|
||||
unique_pointer<u32> _inputA;
|
||||
unique_pointer<u32> _inputB;
|
||||
unique_pointer<u32> _output;
|
||||
unique_pointer<u32> _rotate;
|
||||
unique_pointer<u32[]> _palette;
|
||||
vector<Node::Video::Sprite> _sprites;
|
||||
|
||||
uint _renderWidth = 0;
|
||||
uint _renderHeight = 0;
|
||||
//unserialized:
|
||||
nall::thread _thread;
|
||||
recursive_mutex _mutex;
|
||||
atomic<bool> _kill = false;
|
||||
atomic<bool> _frame = false;
|
||||
function<void ()> _refresh;
|
||||
bool _progressive = false;
|
||||
bool _progressiveDouble = false;
|
||||
bool _interlace = false;
|
||||
bool _interlaceField = false;
|
||||
u32 _viewportX = 0;
|
||||
u32 _viewportY = 0;
|
||||
u32 _viewportWidth = 0;
|
||||
u32 _viewportHeight = 0;
|
||||
};
|
||||
@@ -2,7 +2,7 @@ auto Sprite::setVisible(bool visible) -> void {
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
auto Sprite::setPosition(uint x, uint y) -> void {
|
||||
auto Sprite::setPosition(u32 x, u32 y) -> void {
|
||||
_x = x;
|
||||
_y = y;
|
||||
}
|
||||
@@ -10,14 +10,14 @@ auto Sprite::setPosition(uint x, uint y) -> void {
|
||||
auto Sprite::setImage(nall::image image, bool invert) -> void {
|
||||
_width = image.width();
|
||||
_height = image.height();
|
||||
_pixels = new uint32[_width * _height];
|
||||
_pixels = new u32[_width * _height];
|
||||
image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
|
||||
memory::copy(_pixels.data(), _width * _height * sizeof(uint32), image.data(), image.size());
|
||||
memory::copy(_pixels.data(), _width * _height * sizeof(u32), image.data(), image.size());
|
||||
if(!invert) return;
|
||||
|
||||
for(uint y : range(_height)) {
|
||||
for(u32 y : range(_height)) {
|
||||
auto data = _pixels.data() + y * _width;
|
||||
for(uint x : range(_width)) {
|
||||
for(u32 x : range(_width)) {
|
||||
auto pixel = data[x];
|
||||
pixel ^= 0xffffff;
|
||||
data[x] = pixel;
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
struct Sprite : Object {
|
||||
DeclareClass(Sprite, "Sprite")
|
||||
using Object::Object;
|
||||
struct Sprite : Video {
|
||||
DeclareClass(Sprite, "video.sprite")
|
||||
using Video::Video;
|
||||
|
||||
auto visible() const -> bool { return _visible; }
|
||||
auto x() const -> uint { return _x; }
|
||||
auto y() const -> uint { return _y; }
|
||||
auto width() const -> uint { return _width; }
|
||||
auto height() const -> uint { return _height; }
|
||||
auto image() const -> const uint32* { return _pixels.data(); }
|
||||
auto x() const -> u32 { return _x; }
|
||||
auto y() const -> u32 { return _y; }
|
||||
auto width() const -> u32 { return _width; }
|
||||
auto height() const -> u32 { return _height; }
|
||||
auto image() const -> array_view<u32> { return {_pixels.data(), _width * _height}; }
|
||||
|
||||
auto setVisible(bool visible) -> void;
|
||||
auto setPosition(uint x, uint y) -> void;
|
||||
auto setPosition(u32 x, u32 y) -> void;
|
||||
auto setImage(nall::image, bool invert = false) -> void;
|
||||
|
||||
protected:
|
||||
bool _visible = false;
|
||||
uint _x = 0;
|
||||
uint _y = 0;
|
||||
uint _width = 0;
|
||||
uint _height = 0;
|
||||
unique_pointer<uint32[]> _pixels;
|
||||
u32 _x = 0;
|
||||
u32 _y = 0;
|
||||
u32 _width = 0;
|
||||
u32 _height = 0;
|
||||
unique_pointer<u32[]> _pixels;
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
struct Video : Object {
|
||||
DeclareClass(Video, "video");
|
||||
using Object::Object;
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace ares {
|
||||
|
||||
enum class Event : uint {
|
||||
enum class Event : u32 {
|
||||
None,
|
||||
Step,
|
||||
Frame,
|
||||
@@ -13,12 +13,12 @@ enum class Event : uint {
|
||||
struct Platform {
|
||||
virtual auto attach(Node::Object) -> void {}
|
||||
virtual auto detach(Node::Object) -> void {}
|
||||
virtual auto open(Node::Object, string name, vfs::file::mode mode, bool required = false) -> shared_pointer<vfs::file> { return {}; }
|
||||
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
|
||||
virtual auto event(Event) -> void {}
|
||||
virtual auto log(string_view message) -> void {}
|
||||
virtual auto video(Node::Screen, const uint32_t* data, uint pitch, uint width, uint height) -> void {}
|
||||
virtual auto audio(Node::Stream) -> void {}
|
||||
virtual auto input(Node::Input) -> void {}
|
||||
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
|
||||
virtual auto audio(Node::Audio::Stream) -> void {}
|
||||
virtual auto input(Node::Input::Input) -> void {}
|
||||
};
|
||||
|
||||
extern Platform* platform;
|
||||
@@ -26,6 +26,7 @@ extern Platform* platform;
|
||||
}
|
||||
|
||||
namespace ares::Core {
|
||||
// <ares/node/node.hpp> forward declarations
|
||||
auto PlatformAttach(Node::Object node) -> void { if(platform && node->name()) platform->attach(node); }
|
||||
auto PlatformDetach(Node::Object node) -> void { if(platform && node->name()) platform->detach(node); }
|
||||
auto PlatformLog(string_view text) -> void { if(platform) platform->log(text); }
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace ares {
|
||||
|
||||
struct Random {
|
||||
enum class Entropy : uint { None, Low, High };
|
||||
enum class Entropy : u32 { None, Low, High };
|
||||
|
||||
auto operator()() -> uint64 {
|
||||
auto operator()() -> n64 {
|
||||
return random();
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ struct Random {
|
||||
seed();
|
||||
}
|
||||
|
||||
auto seed(maybe<uint32> seed = nothing, maybe<uint32> sequence = nothing) -> void {
|
||||
if(!seed) seed = (uint32)clock();
|
||||
auto seed(maybe<n32> seed = nothing, maybe<n32> sequence = nothing) -> void {
|
||||
if(!seed) seed = (n32)clock();
|
||||
if(!sequence) sequence = 0;
|
||||
|
||||
_state = 0;
|
||||
@@ -25,72 +25,72 @@ struct Random {
|
||||
step();
|
||||
}
|
||||
|
||||
auto random() -> uint64 {
|
||||
auto random() -> n64 {
|
||||
if(_entropy == Entropy::None) return 0;
|
||||
return (uint64)step() << 32 | (uint64)step() << 0;
|
||||
return (n64)step() << 32 | (n64)step() << 0;
|
||||
}
|
||||
|
||||
auto bias(uint64 bias) -> uint64 {
|
||||
auto bias(n64 bias) -> n64 {
|
||||
if(_entropy == Entropy::None) return bias;
|
||||
return random();
|
||||
}
|
||||
|
||||
auto bound(uint64 bound) -> uint64 {
|
||||
uint64 threshold = -bound % bound;
|
||||
auto bound(n64 bound) -> n64 {
|
||||
n64 threshold = -bound % bound;
|
||||
while(true) {
|
||||
uint64 result = random();
|
||||
n64 result = random();
|
||||
if(result >= threshold) return result % bound;
|
||||
}
|
||||
}
|
||||
|
||||
auto array(uint8* data, uint32 size) -> void {
|
||||
auto array(array_span<n8> buffer) -> void {
|
||||
if(_entropy == Entropy::None) {
|
||||
memory::fill(data, size);
|
||||
memory::fill(buffer.data(), buffer.size());
|
||||
return;
|
||||
}
|
||||
|
||||
if(_entropy == Entropy::High) {
|
||||
for(uint32 address : range(size)) {
|
||||
data[address] = random();
|
||||
for(n32 address : range(buffer.size())) {
|
||||
buffer[address] = random();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Entropy::Low
|
||||
uint lobit = random() & 3;
|
||||
uint hibit = (lobit + 8 + (random() & 3)) & 15;
|
||||
uint lovalue = random() & 255;
|
||||
uint hivalue = random() & 255;
|
||||
u32 lobit = random() & 3;
|
||||
u32 hibit = (lobit + 8 + (random() & 3)) & 15;
|
||||
u32 lovalue = random() & 255;
|
||||
u32 hivalue = random() & 255;
|
||||
if((random() & 3) == 0) lovalue = 0;
|
||||
if((random() & 1) == 0) hivalue = ~lovalue;
|
||||
|
||||
for(uint32 address : range(size)) {
|
||||
uint8 value = address.bit(lobit) ? lovalue : hivalue;
|
||||
for(n32 address : range(buffer.size())) {
|
||||
n8 value = address.bit(lobit) ? lovalue : hivalue;
|
||||
if(address.bit(hibit)) value = ~value;
|
||||
if((random() & 511) == 0) value.bit(random() & 7) ^= 1;
|
||||
if((random() & 2047) == 0) value.bit(random() & 7) ^= 1;
|
||||
data[address] = value;
|
||||
buffer[address] = value;
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer((uint&)_entropy);
|
||||
s.integer(_state);
|
||||
s.integer(_increment);
|
||||
s((u32&)_entropy);
|
||||
s(_state);
|
||||
s(_increment);
|
||||
}
|
||||
|
||||
private:
|
||||
auto step() -> uint32 {
|
||||
uint64 state = _state;
|
||||
auto step() -> n32 {
|
||||
n64 state = _state;
|
||||
_state = state * 6364136223846793005ull + _increment;
|
||||
uint32 xorshift = (state >> 18 ^ state) >> 27;
|
||||
uint32 rotate = state >> 59;
|
||||
n32 xorshift = (state >> 18 ^ state) >> 27;
|
||||
n32 rotate = state >> 59;
|
||||
return xorshift >> rotate | xorshift << (-rotate & 31);
|
||||
}
|
||||
|
||||
Entropy _entropy = Entropy::High;
|
||||
uint64 _state;
|
||||
uint64 _increment;
|
||||
n64 _state;
|
||||
n64 _increment;
|
||||
};
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -1,7 +1,9 @@
|
||||
namespace name=Resource
|
||||
namespace name=Ares
|
||||
binary name=Icon file=icon.png
|
||||
binary name=Logo file=logo.png
|
||||
binary name=Icon1x file=icon.png
|
||||
binary name=Icon2x file=icon@2x.png
|
||||
binary name=Logo1x file=logo.png
|
||||
binary name=Logo2x [email protected]
|
||||
namespace name=Sprite
|
||||
namespace name=SuperFamicom
|
||||
binary name=CrosshairBlue file=sprite/sfc/crosshair-blue.png
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
namespace Resource {
|
||||
namespace Ares {
|
||||
extern const unsigned char Icon[6562];
|
||||
extern const unsigned char Logo[13719];
|
||||
extern const unsigned char Icon1x[6562];
|
||||
extern const unsigned char Icon2x[9662];
|
||||
extern const unsigned char Logo1x[11533];
|
||||
extern const unsigned char Logo2x[18991];
|
||||
}
|
||||
namespace Sprite {
|
||||
namespace SuperFamicom {
|
||||
|
||||
@@ -2,11 +2,11 @@ inline auto Scheduler::reset() -> void {
|
||||
_threads.reset();
|
||||
}
|
||||
|
||||
inline auto Scheduler::threads() const -> uint {
|
||||
inline auto Scheduler::threads() const -> u32 {
|
||||
return _threads.size();
|
||||
}
|
||||
|
||||
inline auto Scheduler::thread(uint uniqueID) const -> maybe<Thread&> {
|
||||
inline auto Scheduler::thread(u32 uniqueID) const -> maybe<Thread&> {
|
||||
for(auto& thread : _threads) {
|
||||
if(thread->_uniqueID == uniqueID) return *thread;
|
||||
}
|
||||
@@ -16,15 +16,15 @@ inline auto Scheduler::thread(uint uniqueID) const -> maybe<Thread&> {
|
||||
//if threads A and B both have a clock value of 0, it is ambiguous which should run first.
|
||||
//to resolve this, a uniqueID is assigned to each thread when appended to the scheduler.
|
||||
//the first unused ID is selected, to avoid the uniqueID growing in an unbounded fashion.
|
||||
inline auto Scheduler::uniqueID() const -> uint {
|
||||
uint uniqueID = 0;
|
||||
inline auto Scheduler::uniqueID() const -> u32 {
|
||||
u32 uniqueID = 0;
|
||||
while(thread(uniqueID)) uniqueID++;
|
||||
return uniqueID;
|
||||
}
|
||||
|
||||
//find the clock time of the furthest behind thread.
|
||||
inline auto Scheduler::minimum() const -> uintmax {
|
||||
uintmax minimum = (uintmax)-1;
|
||||
inline auto Scheduler::minimum() const -> u64 {
|
||||
u64 minimum = (u64)-1;
|
||||
for(auto& thread : _threads) {
|
||||
minimum = min(minimum, thread->_clock - thread->_uniqueID);
|
||||
}
|
||||
@@ -32,8 +32,8 @@ inline auto Scheduler::minimum() const -> uintmax {
|
||||
}
|
||||
|
||||
//find the clock time of the furthest ahead thread.
|
||||
inline auto Scheduler::maximum() const -> uintmax {
|
||||
uintmax maximum = 0;
|
||||
inline auto Scheduler::maximum() const -> u64 {
|
||||
u64 maximum = 0;
|
||||
for(auto& thread : _threads) {
|
||||
maximum = max(maximum, thread->_clock - thread->_uniqueID);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Thread;
|
||||
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
enum class Mode : u32 {
|
||||
Run,
|
||||
Synchronize,
|
||||
SynchronizePrimary,
|
||||
@@ -13,11 +13,11 @@ struct Scheduler {
|
||||
auto operator=(const Scheduler&) = delete;
|
||||
|
||||
auto reset() -> void;
|
||||
auto threads() const -> uint;
|
||||
auto thread(uint threadID) const -> maybe<Thread&>;
|
||||
auto uniqueID() const -> uint;
|
||||
auto minimum() const -> uintmax;
|
||||
auto maximum() const -> uintmax;
|
||||
auto threads() const -> u32;
|
||||
auto thread(u32 threadID) const -> maybe<Thread&>;
|
||||
auto uniqueID() const -> u32;
|
||||
auto minimum() const -> u64;
|
||||
auto maximum() const -> u64;
|
||||
|
||||
auto append(Thread& thread) -> bool;
|
||||
auto remove(Thread& thread) -> void;
|
||||
|
||||
@@ -4,7 +4,7 @@ inline auto Thread::EntryPoints() -> vector<EntryPoint>& {
|
||||
}
|
||||
|
||||
inline auto Thread::Enter() -> void {
|
||||
for(uint64_t index : range(EntryPoints().size())) {
|
||||
for(u32 index : range(EntryPoints().size())) {
|
||||
if(co_active() == EntryPoints()[index].handle) {
|
||||
auto entryPoint = EntryPoints()[index].entryPoint;
|
||||
EntryPoints().remove(index);
|
||||
@@ -24,9 +24,9 @@ inline Thread::~Thread() {
|
||||
|
||||
inline auto Thread::active() const -> bool { return co_active() == _handle; }
|
||||
inline auto Thread::handle() const -> cothread_t { return _handle; }
|
||||
inline auto Thread::frequency() const -> uintmax { return _frequency; }
|
||||
inline auto Thread::scalar() const -> uintmax { return _scalar; }
|
||||
inline auto Thread::clock() const -> uintmax { return _clock; }
|
||||
inline auto Thread::frequency() const -> u64 { return _frequency; }
|
||||
inline auto Thread::scalar() const -> u64 { return _scalar; }
|
||||
inline auto Thread::clock() const -> u64 { return _clock; }
|
||||
|
||||
inline auto Thread::setHandle(cothread_t handle) -> void {
|
||||
_handle = handle;
|
||||
@@ -37,11 +37,11 @@ inline auto Thread::setFrequency(double frequency) -> void {
|
||||
_scalar = Second / _frequency;
|
||||
}
|
||||
|
||||
inline auto Thread::setScalar(uintmax scalar) -> void {
|
||||
inline auto Thread::setScalar(u64 scalar) -> void {
|
||||
_scalar = scalar;
|
||||
}
|
||||
|
||||
inline auto Thread::setClock(uintmax clock) -> void {
|
||||
inline auto Thread::setClock(u64 clock) -> void {
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
@@ -57,13 +57,19 @@ inline auto Thread::create(double frequency, function<void ()> entryPoint) -> vo
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
//returns a thread to its entry point (eg for a reset), without resetting the clock value
|
||||
inline auto Thread::restart(function<void()> entryPoint) -> void {
|
||||
co_derive(_handle, Thread::Size, &Thread::Enter);
|
||||
EntryPoints().append({_handle, entryPoint});
|
||||
}
|
||||
|
||||
inline auto Thread::destroy() -> void {
|
||||
scheduler.remove(*this);
|
||||
if(_handle) co_delete(_handle);
|
||||
_handle = nullptr;
|
||||
}
|
||||
|
||||
inline auto Thread::step(uint clocks) -> void {
|
||||
inline auto Thread::step(u32 clocks) -> void {
|
||||
_clock += _scalar * clocks;
|
||||
}
|
||||
|
||||
@@ -89,30 +95,25 @@ inline auto Thread::synchronize(Thread& thread, P&&... p) -> void {
|
||||
}
|
||||
|
||||
inline auto Thread::serialize(serializer& s) -> void {
|
||||
s.integer(_frequency);
|
||||
s.integer(_scalar);
|
||||
s.integer(_clock);
|
||||
s(_frequency);
|
||||
s(_scalar);
|
||||
s(_clock);
|
||||
|
||||
if(!scheduler._synchronize) {
|
||||
static uint8_t stack[Thread::Size];
|
||||
static u8 stack[Thread::Size];
|
||||
bool resume = co_active() == _handle;
|
||||
|
||||
if(s.mode() == serializer::Size) {
|
||||
s.array(stack, Thread::Size);
|
||||
s.boolean(resume);
|
||||
}
|
||||
|
||||
if(s.mode() == serializer::Load) {
|
||||
s.array(stack, Thread::Size);
|
||||
s.boolean(resume);
|
||||
if(s.reading()) {
|
||||
s(stack);
|
||||
s(resume);
|
||||
memory::copy(_handle, stack, Thread::Size);
|
||||
if(resume) scheduler._resume = _handle;
|
||||
}
|
||||
|
||||
if(s.mode() == serializer::Save) {
|
||||
if(s.writing()) {
|
||||
memory::copy(stack, _handle, Thread::Size);
|
||||
s.array(stack, Thread::Size);
|
||||
s.boolean(resume);
|
||||
s(stack);
|
||||
s(resume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
struct Scheduler;
|
||||
|
||||
struct Thread {
|
||||
enum : uintmax { Second = (uintmax)-1 >> 1 };
|
||||
enum : uintmax { Size = 8_KiB * sizeof(void*) };
|
||||
enum : u64 { Second = (u64)-1 >> 1 };
|
||||
enum : u64 { Size = 16_KiB * sizeof(void*) };
|
||||
|
||||
struct EntryPoint {
|
||||
cothread_t handle = nullptr;
|
||||
@@ -20,19 +20,20 @@ struct Thread {
|
||||
explicit operator bool() const { return _handle; }
|
||||
auto active() const -> bool;
|
||||
auto handle() const -> cothread_t;
|
||||
auto frequency() const -> uintmax;
|
||||
auto scalar() const -> uintmax;
|
||||
auto clock() const -> uintmax;
|
||||
auto frequency() const -> u64;
|
||||
auto scalar() const -> u64;
|
||||
auto clock() const -> u64;
|
||||
|
||||
auto setHandle(cothread_t handle) -> void;
|
||||
auto setFrequency(double frequency) -> void;
|
||||
auto setScalar(uintmax scalar) -> void;
|
||||
auto setClock(uintmax clock) -> void;
|
||||
auto setScalar(u64 scalar) -> void;
|
||||
auto setClock(u64 clock) -> void;
|
||||
|
||||
auto create(double frequency, function<void ()> entryPoint) -> void;
|
||||
auto restart(function<void ()> entryPoint) -> void;
|
||||
auto destroy() -> void;
|
||||
|
||||
auto step(uint clocks) -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
auto synchronize() -> void;
|
||||
template<typename... P> auto synchronize(Thread&, P&&...) -> void;
|
||||
|
||||
@@ -40,10 +41,10 @@ struct Thread {
|
||||
|
||||
protected:
|
||||
cothread_t _handle = nullptr;
|
||||
uint _uniqueID = 0;
|
||||
uintmax _frequency = 0;
|
||||
uintmax _scalar = 0;
|
||||
uintmax _clock = 0;
|
||||
u32 _uniqueID = 0;
|
||||
u64 _frequency = 0;
|
||||
u64 _scalar = 0;
|
||||
u64 _clock = 0;
|
||||
|
||||
friend class Scheduler;
|
||||
};
|
||||
@@ -1,144 +1,134 @@
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using i128 = int128_t;
|
||||
using b1 = nall::Boolean;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using u128 = uint128_t;
|
||||
using i1 = nall::Integer< 1>; using s1 = nall::IntegerPrimitive< 1>;
|
||||
using i2 = nall::Integer< 2>; using s2 = nall::IntegerPrimitive< 2>;
|
||||
using i3 = nall::Integer< 3>; using s3 = nall::IntegerPrimitive< 3>;
|
||||
using i4 = nall::Integer< 4>; using s4 = nall::IntegerPrimitive< 4>;
|
||||
using i5 = nall::Integer< 5>; using s5 = nall::IntegerPrimitive< 5>;
|
||||
using i6 = nall::Integer< 6>; using s6 = nall::IntegerPrimitive< 6>;
|
||||
using i7 = nall::Integer< 7>; using s7 = nall::IntegerPrimitive< 7>;
|
||||
using i8 = nall::Integer< 8>;
|
||||
using i9 = nall::Integer< 9>; using s9 = nall::IntegerPrimitive< 9>;
|
||||
using i10 = nall::Integer<10>; using s10 = nall::IntegerPrimitive<10>;
|
||||
using i11 = nall::Integer<11>; using s11 = nall::IntegerPrimitive<11>;
|
||||
using i12 = nall::Integer<12>; using s12 = nall::IntegerPrimitive<12>;
|
||||
using i13 = nall::Integer<13>; using s13 = nall::IntegerPrimitive<13>;
|
||||
using i14 = nall::Integer<14>; using s14 = nall::IntegerPrimitive<14>;
|
||||
using i15 = nall::Integer<15>; using s15 = nall::IntegerPrimitive<15>;
|
||||
using i16 = nall::Integer<16>;
|
||||
using i17 = nall::Integer<17>; using s17 = nall::IntegerPrimitive<17>;
|
||||
using i18 = nall::Integer<18>; using s18 = nall::IntegerPrimitive<18>;
|
||||
using i19 = nall::Integer<19>; using s19 = nall::IntegerPrimitive<19>;
|
||||
using i20 = nall::Integer<20>; using s20 = nall::IntegerPrimitive<20>;
|
||||
using i21 = nall::Integer<21>; using s21 = nall::IntegerPrimitive<21>;
|
||||
using i22 = nall::Integer<22>; using s22 = nall::IntegerPrimitive<22>;
|
||||
using i23 = nall::Integer<23>; using s23 = nall::IntegerPrimitive<23>;
|
||||
using i24 = nall::Integer<24>; using s24 = nall::IntegerPrimitive<24>;
|
||||
using i25 = nall::Integer<25>; using s25 = nall::IntegerPrimitive<25>;
|
||||
using i26 = nall::Integer<26>; using s26 = nall::IntegerPrimitive<26>;
|
||||
using i27 = nall::Integer<27>; using s27 = nall::IntegerPrimitive<27>;
|
||||
using i28 = nall::Integer<28>; using s28 = nall::IntegerPrimitive<28>;
|
||||
using i29 = nall::Integer<29>; using s29 = nall::IntegerPrimitive<29>;
|
||||
using i30 = nall::Integer<30>; using s30 = nall::IntegerPrimitive<30>;
|
||||
using i31 = nall::Integer<31>; using s31 = nall::IntegerPrimitive<31>;
|
||||
using i32 = nall::Integer<32>;
|
||||
using i33 = nall::Integer<33>; using s33 = nall::IntegerPrimitive<33>;
|
||||
using i34 = nall::Integer<34>; using s34 = nall::IntegerPrimitive<34>;
|
||||
using i35 = nall::Integer<35>; using s35 = nall::IntegerPrimitive<35>;
|
||||
using i36 = nall::Integer<36>; using s36 = nall::IntegerPrimitive<36>;
|
||||
using i37 = nall::Integer<37>; using s37 = nall::IntegerPrimitive<37>;
|
||||
using i38 = nall::Integer<38>; using s38 = nall::IntegerPrimitive<38>;
|
||||
using i39 = nall::Integer<39>; using s39 = nall::IntegerPrimitive<39>;
|
||||
using i40 = nall::Integer<40>; using s40 = nall::IntegerPrimitive<40>;
|
||||
using i41 = nall::Integer<41>; using s41 = nall::IntegerPrimitive<41>;
|
||||
using i42 = nall::Integer<42>; using s42 = nall::IntegerPrimitive<42>;
|
||||
using i43 = nall::Integer<43>; using s43 = nall::IntegerPrimitive<43>;
|
||||
using i44 = nall::Integer<44>; using s44 = nall::IntegerPrimitive<44>;
|
||||
using i45 = nall::Integer<45>; using s45 = nall::IntegerPrimitive<45>;
|
||||
using i46 = nall::Integer<46>; using s46 = nall::IntegerPrimitive<46>;
|
||||
using i47 = nall::Integer<47>; using s47 = nall::IntegerPrimitive<47>;
|
||||
using i48 = nall::Integer<48>; using s48 = nall::IntegerPrimitive<48>;
|
||||
using i49 = nall::Integer<49>; using s49 = nall::IntegerPrimitive<49>;
|
||||
using i50 = nall::Integer<50>; using s50 = nall::IntegerPrimitive<50>;
|
||||
using i51 = nall::Integer<51>; using s51 = nall::IntegerPrimitive<51>;
|
||||
using i52 = nall::Integer<52>; using s52 = nall::IntegerPrimitive<52>;
|
||||
using i53 = nall::Integer<53>; using s53 = nall::IntegerPrimitive<53>;
|
||||
using i54 = nall::Integer<54>; using s54 = nall::IntegerPrimitive<54>;
|
||||
using i55 = nall::Integer<55>; using s55 = nall::IntegerPrimitive<55>;
|
||||
using i56 = nall::Integer<56>; using s56 = nall::IntegerPrimitive<56>;
|
||||
using i57 = nall::Integer<57>; using s57 = nall::IntegerPrimitive<57>;
|
||||
using i58 = nall::Integer<58>; using s58 = nall::IntegerPrimitive<58>;
|
||||
using i59 = nall::Integer<59>; using s59 = nall::IntegerPrimitive<59>;
|
||||
using i60 = nall::Integer<60>; using s60 = nall::IntegerPrimitive<60>;
|
||||
using i61 = nall::Integer<61>; using s61 = nall::IntegerPrimitive<61>;
|
||||
using i62 = nall::Integer<62>; using s62 = nall::IntegerPrimitive<62>;
|
||||
using i63 = nall::Integer<63>; using s63 = nall::IntegerPrimitive<63>;
|
||||
using i64 = nall::Integer<64>;
|
||||
|
||||
using f32 = float32_t;
|
||||
using f64 = float64_t;
|
||||
using n1 = nall::Natural< 1>; using u1 = nall::NaturalPrimitive< 1>;
|
||||
using n2 = nall::Natural< 2>; using u2 = nall::NaturalPrimitive< 2>;
|
||||
using n3 = nall::Natural< 3>; using u3 = nall::NaturalPrimitive< 3>;
|
||||
using n4 = nall::Natural< 4>; using u4 = nall::NaturalPrimitive< 4>;
|
||||
using n5 = nall::Natural< 5>; using u5 = nall::NaturalPrimitive< 5>;
|
||||
using n6 = nall::Natural< 6>; using u6 = nall::NaturalPrimitive< 6>;
|
||||
using n7 = nall::Natural< 7>; using u7 = nall::NaturalPrimitive< 7>;
|
||||
using n8 = nall::Natural< 8>;
|
||||
using n9 = nall::Natural< 9>; using u9 = nall::NaturalPrimitive< 9>;
|
||||
using n10 = nall::Natural<10>; using u10 = nall::NaturalPrimitive<10>;
|
||||
using n11 = nall::Natural<11>; using u11 = nall::NaturalPrimitive<11>;
|
||||
using n12 = nall::Natural<12>; using u12 = nall::NaturalPrimitive<12>;
|
||||
using n13 = nall::Natural<13>; using u13 = nall::NaturalPrimitive<13>;
|
||||
using n14 = nall::Natural<14>; using u14 = nall::NaturalPrimitive<14>;
|
||||
using n15 = nall::Natural<15>; using u15 = nall::NaturalPrimitive<15>;
|
||||
using n16 = nall::Natural<16>;
|
||||
using n17 = nall::Natural<17>; using u17 = nall::NaturalPrimitive<17>;
|
||||
using n18 = nall::Natural<18>; using u18 = nall::NaturalPrimitive<18>;
|
||||
using n19 = nall::Natural<19>; using u19 = nall::NaturalPrimitive<19>;
|
||||
using n20 = nall::Natural<20>; using u20 = nall::NaturalPrimitive<20>;
|
||||
using n21 = nall::Natural<21>; using u21 = nall::NaturalPrimitive<21>;
|
||||
using n22 = nall::Natural<22>; using u22 = nall::NaturalPrimitive<22>;
|
||||
using n23 = nall::Natural<23>; using u23 = nall::NaturalPrimitive<23>;
|
||||
using n24 = nall::Natural<24>; using u24 = nall::NaturalPrimitive<24>;
|
||||
using n25 = nall::Natural<25>; using u25 = nall::NaturalPrimitive<25>;
|
||||
using n26 = nall::Natural<26>; using u26 = nall::NaturalPrimitive<26>;
|
||||
using n27 = nall::Natural<27>; using u27 = nall::NaturalPrimitive<27>;
|
||||
using n28 = nall::Natural<28>; using u28 = nall::NaturalPrimitive<28>;
|
||||
using n29 = nall::Natural<29>; using u29 = nall::NaturalPrimitive<29>;
|
||||
using n30 = nall::Natural<30>; using u30 = nall::NaturalPrimitive<30>;
|
||||
using n31 = nall::Natural<31>; using u31 = nall::NaturalPrimitive<31>;
|
||||
using n32 = nall::Natural<32>;
|
||||
using n33 = nall::Natural<33>; using u33 = nall::NaturalPrimitive<33>;
|
||||
using n34 = nall::Natural<34>; using u34 = nall::NaturalPrimitive<34>;
|
||||
using n35 = nall::Natural<35>; using u35 = nall::NaturalPrimitive<35>;
|
||||
using n36 = nall::Natural<36>; using u36 = nall::NaturalPrimitive<36>;
|
||||
using n37 = nall::Natural<37>; using u37 = nall::NaturalPrimitive<37>;
|
||||
using n38 = nall::Natural<38>; using u38 = nall::NaturalPrimitive<38>;
|
||||
using n39 = nall::Natural<39>; using u39 = nall::NaturalPrimitive<39>;
|
||||
using n40 = nall::Natural<40>; using u40 = nall::NaturalPrimitive<40>;
|
||||
using n41 = nall::Natural<41>; using u41 = nall::NaturalPrimitive<41>;
|
||||
using n42 = nall::Natural<42>; using u42 = nall::NaturalPrimitive<42>;
|
||||
using n43 = nall::Natural<43>; using u43 = nall::NaturalPrimitive<43>;
|
||||
using n44 = nall::Natural<44>; using u44 = nall::NaturalPrimitive<44>;
|
||||
using n45 = nall::Natural<45>; using u45 = nall::NaturalPrimitive<45>;
|
||||
using n46 = nall::Natural<46>; using u46 = nall::NaturalPrimitive<46>;
|
||||
using n47 = nall::Natural<47>; using u47 = nall::NaturalPrimitive<47>;
|
||||
using n48 = nall::Natural<48>; using u48 = nall::NaturalPrimitive<48>;
|
||||
using n49 = nall::Natural<49>; using u49 = nall::NaturalPrimitive<49>;
|
||||
using n50 = nall::Natural<50>; using u50 = nall::NaturalPrimitive<50>;
|
||||
using n51 = nall::Natural<51>; using u51 = nall::NaturalPrimitive<51>;
|
||||
using n52 = nall::Natural<52>; using u52 = nall::NaturalPrimitive<52>;
|
||||
using n53 = nall::Natural<53>; using u53 = nall::NaturalPrimitive<53>;
|
||||
using n54 = nall::Natural<54>; using u54 = nall::NaturalPrimitive<54>;
|
||||
using n55 = nall::Natural<55>; using u55 = nall::NaturalPrimitive<55>;
|
||||
using n56 = nall::Natural<56>; using u56 = nall::NaturalPrimitive<56>;
|
||||
using n57 = nall::Natural<57>; using u57 = nall::NaturalPrimitive<57>;
|
||||
using n58 = nall::Natural<58>; using u58 = nall::NaturalPrimitive<58>;
|
||||
using n59 = nall::Natural<59>; using u59 = nall::NaturalPrimitive<59>;
|
||||
using n60 = nall::Natural<60>; using u60 = nall::NaturalPrimitive<60>;
|
||||
using n61 = nall::Natural<61>; using u61 = nall::NaturalPrimitive<61>;
|
||||
using n62 = nall::Natural<62>; using u62 = nall::NaturalPrimitive<62>;
|
||||
using n63 = nall::Natural<63>; using u63 = nall::NaturalPrimitive<63>;
|
||||
using n64 = nall::Natural<64>;
|
||||
|
||||
using int1 = nall::Integer< 1>;
|
||||
using int2 = nall::Integer< 2>;
|
||||
using int3 = nall::Integer< 3>;
|
||||
using int4 = nall::Integer< 4>;
|
||||
using int5 = nall::Integer< 5>;
|
||||
using int6 = nall::Integer< 6>;
|
||||
using int7 = nall::Integer< 7>;
|
||||
using int8 = nall::Integer< 8>;
|
||||
using int9 = nall::Integer< 9>;
|
||||
using int10 = nall::Integer<10>;
|
||||
using int11 = nall::Integer<11>;
|
||||
using int12 = nall::Integer<12>;
|
||||
using int13 = nall::Integer<13>;
|
||||
using int14 = nall::Integer<14>;
|
||||
using int15 = nall::Integer<15>;
|
||||
using int16 = nall::Integer<16>;
|
||||
using int17 = nall::Integer<17>;
|
||||
using int18 = nall::Integer<18>;
|
||||
using int19 = nall::Integer<19>;
|
||||
using int20 = nall::Integer<20>;
|
||||
using int21 = nall::Integer<21>;
|
||||
using int22 = nall::Integer<22>;
|
||||
using int23 = nall::Integer<23>;
|
||||
using int24 = nall::Integer<24>;
|
||||
using int25 = nall::Integer<25>;
|
||||
using int26 = nall::Integer<26>;
|
||||
using int27 = nall::Integer<27>;
|
||||
using int28 = nall::Integer<28>;
|
||||
using int29 = nall::Integer<29>;
|
||||
using int30 = nall::Integer<30>;
|
||||
using int31 = nall::Integer<31>;
|
||||
using int32 = nall::Integer<32>;
|
||||
using int33 = nall::Integer<33>;
|
||||
using int34 = nall::Integer<34>;
|
||||
using int35 = nall::Integer<35>;
|
||||
using int36 = nall::Integer<36>;
|
||||
using int37 = nall::Integer<37>;
|
||||
using int38 = nall::Integer<38>;
|
||||
using int39 = nall::Integer<39>;
|
||||
using int40 = nall::Integer<40>;
|
||||
using int41 = nall::Integer<41>;
|
||||
using int42 = nall::Integer<42>;
|
||||
using int43 = nall::Integer<43>;
|
||||
using int44 = nall::Integer<44>;
|
||||
using int45 = nall::Integer<45>;
|
||||
using int46 = nall::Integer<46>;
|
||||
using int47 = nall::Integer<47>;
|
||||
using int48 = nall::Integer<48>;
|
||||
using int49 = nall::Integer<49>;
|
||||
using int50 = nall::Integer<50>;
|
||||
using int51 = nall::Integer<51>;
|
||||
using int52 = nall::Integer<52>;
|
||||
using int53 = nall::Integer<53>;
|
||||
using int54 = nall::Integer<54>;
|
||||
using int55 = nall::Integer<55>;
|
||||
using int56 = nall::Integer<56>;
|
||||
using int57 = nall::Integer<57>;
|
||||
using int58 = nall::Integer<58>;
|
||||
using int59 = nall::Integer<59>;
|
||||
using int60 = nall::Integer<60>;
|
||||
using int61 = nall::Integer<61>;
|
||||
using int62 = nall::Integer<62>;
|
||||
using int63 = nall::Integer<63>;
|
||||
using int64 = nall::Integer<64>;
|
||||
|
||||
using uint1 = nall::Natural< 1>;
|
||||
using uint2 = nall::Natural< 2>;
|
||||
using uint3 = nall::Natural< 3>;
|
||||
using uint4 = nall::Natural< 4>;
|
||||
using uint5 = nall::Natural< 5>;
|
||||
using uint6 = nall::Natural< 6>;
|
||||
using uint7 = nall::Natural< 7>;
|
||||
using uint8 = nall::Natural< 8>;
|
||||
using uint9 = nall::Natural< 9>;
|
||||
using uint10 = nall::Natural<10>;
|
||||
using uint11 = nall::Natural<11>;
|
||||
using uint12 = nall::Natural<12>;
|
||||
using uint13 = nall::Natural<13>;
|
||||
using uint14 = nall::Natural<14>;
|
||||
using uint15 = nall::Natural<15>;
|
||||
using uint16 = nall::Natural<16>;
|
||||
using uint17 = nall::Natural<17>;
|
||||
using uint18 = nall::Natural<18>;
|
||||
using uint19 = nall::Natural<19>;
|
||||
using uint20 = nall::Natural<20>;
|
||||
using uint21 = nall::Natural<21>;
|
||||
using uint22 = nall::Natural<22>;
|
||||
using uint23 = nall::Natural<23>;
|
||||
using uint24 = nall::Natural<24>;
|
||||
using uint25 = nall::Natural<25>;
|
||||
using uint26 = nall::Natural<26>;
|
||||
using uint27 = nall::Natural<27>;
|
||||
using uint28 = nall::Natural<28>;
|
||||
using uint29 = nall::Natural<29>;
|
||||
using uint30 = nall::Natural<30>;
|
||||
using uint31 = nall::Natural<31>;
|
||||
using uint32 = nall::Natural<32>;
|
||||
using uint33 = nall::Natural<33>;
|
||||
using uint34 = nall::Natural<34>;
|
||||
using uint35 = nall::Natural<35>;
|
||||
using uint36 = nall::Natural<36>;
|
||||
using uint37 = nall::Natural<37>;
|
||||
using uint38 = nall::Natural<38>;
|
||||
using uint39 = nall::Natural<39>;
|
||||
using uint40 = nall::Natural<40>;
|
||||
using uint41 = nall::Natural<41>;
|
||||
using uint42 = nall::Natural<42>;
|
||||
using uint43 = nall::Natural<43>;
|
||||
using uint44 = nall::Natural<44>;
|
||||
using uint45 = nall::Natural<45>;
|
||||
using uint46 = nall::Natural<46>;
|
||||
using uint47 = nall::Natural<47>;
|
||||
using uint48 = nall::Natural<48>;
|
||||
using uint49 = nall::Natural<49>;
|
||||
using uint50 = nall::Natural<50>;
|
||||
using uint51 = nall::Natural<51>;
|
||||
using uint52 = nall::Natural<52>;
|
||||
using uint53 = nall::Natural<53>;
|
||||
using uint54 = nall::Natural<54>;
|
||||
using uint55 = nall::Natural<55>;
|
||||
using uint56 = nall::Natural<56>;
|
||||
using uint57 = nall::Natural<57>;
|
||||
using uint58 = nall::Natural<58>;
|
||||
using uint59 = nall::Natural<59>;
|
||||
using uint60 = nall::Natural<60>;
|
||||
using uint61 = nall::Natural<61>;
|
||||
using uint62 = nall::Natural<62>;
|
||||
using uint63 = nall::Natural<63>;
|
||||
using uint64 = nall::Natural<64>;
|
||||
using r32 = nall::Real<32>;
|
||||
using r64 = nall::Real<64>;
|
||||
@@ -6,18 +6,24 @@ ares.objects += $(if $(findstring sn76489,$(ares.components)),ares-audio-sn76489
|
||||
ares.objects += $(if $(findstring t6w28,$(ares.components)),ares-audio-t6w28)
|
||||
ares.objects += $(if $(findstring ym2149,$(ares.components)),ares-audio-ym2149)
|
||||
ares.objects += $(if $(findstring ym2413,$(ares.components)),ares-audio-ym2413)
|
||||
ares.objects += $(if $(findstring ym2610,$(ares.components)),ares-audio-ym2610)
|
||||
ares.objects += $(if $(findstring ym2612,$(ares.components)),ares-audio-ym2612)
|
||||
|
||||
ares.objects += $(if $(findstring m24c,$(ares.components)),ares-eeprom-m24c)
|
||||
ares.objects += $(if $(findstring m93lcx6,$(ares.components)),ares-eeprom-m93lcx6)
|
||||
ares.objects += $(if $(findstring x24c01,$(ares.components)),ares-eeprom-x24c01)
|
||||
|
||||
ares.objects += $(if $(findstring arm7tdmi,$(ares.components)),ares-processor-arm7tdmi)
|
||||
ares.objects += $(if $(findstring gsu,$(ares.components)),ares-processor-gsu)
|
||||
ares.objects += $(if $(findstring hg51b,$(ares.components)),ares-processor-hg51b)
|
||||
ares.objects += $(if $(findstring huc6280,$(ares.components)),ares-processor-huc6280)
|
||||
ares.objects += $(if $(findstring m68k,$(ares.components)),ares-processor-m68k)
|
||||
ares.objects += $(if $(findstring m68hc05,$(ares.components)),ares-processor-m68hc05)
|
||||
ares.objects += $(if $(findstring m68000,$(ares.components)),ares-processor-m68000)
|
||||
ares.objects += $(if $(findstring mos6502,$(ares.components)),ares-processor-mos6502)
|
||||
ares.objects += $(if $(findstring sh2,$(ares.components)),ares-processor-sh2)
|
||||
ares.objects += $(if $(findstring sm5k,$(ares.components)),ares-processor-sm5k)
|
||||
ares.objects += $(if $(findstring sm83,$(ares.components)),ares-processor-sm83)
|
||||
ares.objects += $(if $(findstring spc700,$(ares.components)),ares-processor-spc700)
|
||||
ares.objects += $(if $(findstring ssp1601,$(ares.components)),ares-processor-ssp1601)
|
||||
ares.objects += $(if $(findstring tlcs900h,$(ares.components)),ares-processor-tlcs900h)
|
||||
ares.objects += $(if $(findstring upd96050,$(ares.components)),ares-processor-upd96050)
|
||||
ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz)
|
||||
@@ -33,18 +39,24 @@ $(object.path)/ares-audio-sn76489.o: $(ares.path)/component/audio/sn76489/s
|
||||
$(object.path)/ares-audio-t6w28.o: $(ares.path)/component/audio/t6w28/t6w28.cpp
|
||||
$(object.path)/ares-audio-ym2149.o: $(ares.path)/component/audio/ym2149/ym2149.cpp
|
||||
$(object.path)/ares-audio-ym2413.o: $(ares.path)/component/audio/ym2413/ym2413.cpp
|
||||
$(object.path)/ares-audio-ym2610.o: $(ares.path)/component/audio/ym2610/ym2610.cpp
|
||||
$(object.path)/ares-audio-ym2612.o: $(ares.path)/component/audio/ym2612/ym2612.cpp
|
||||
|
||||
$(object.path)/ares-eeprom-m24c.o: $(ares.path)/component/eeprom/m24c/m24c.cpp
|
||||
$(object.path)/ares-eeprom-m93lcx6.o: $(ares.path)/component/eeprom/m93lcx6/m93lcx6.cpp
|
||||
$(object.path)/ares-eeprom-x24c01.o: $(ares.path)/component/eeprom/x24c01/x24c01.cpp
|
||||
|
||||
$(object.path)/ares-processor-arm7tdmi.o: $(ares.path)/component/processor/arm7tdmi/arm7tdmi.cpp
|
||||
$(object.path)/ares-processor-gsu.o: $(ares.path)/component/processor/gsu/gsu.cpp
|
||||
$(object.path)/ares-processor-hg51b.o: $(ares.path)/component/processor/hg51b/hg51b.cpp
|
||||
$(object.path)/ares-processor-huc6280.o: $(ares.path)/component/processor/huc6280/huc6280.cpp
|
||||
$(object.path)/ares-processor-m68k.o: $(ares.path)/component/processor/m68k/m68k.cpp
|
||||
$(object.path)/ares-processor-m68hc05.o: $(ares.path)/component/processor/m68hc05/m68hc05.cpp
|
||||
$(object.path)/ares-processor-m68000.o: $(ares.path)/component/processor/m68000/m68000.cpp
|
||||
$(object.path)/ares-processor-mos6502.o: $(ares.path)/component/processor/mos6502/mos6502.cpp
|
||||
$(object.path)/ares-processor-sh2.o: $(ares.path)/component/processor/sh2/sh2.cpp
|
||||
$(object.path)/ares-processor-sm5k.o: $(ares.path)/component/processor/sm5k/sm5k.cpp
|
||||
$(object.path)/ares-processor-sm83.o: $(ares.path)/component/processor/sm83/sm83.cpp
|
||||
$(object.path)/ares-processor-spc700.o: $(ares.path)/component/processor/spc700/spc700.cpp
|
||||
$(object.path)/ares-processor-ssp1601.o: $(ares.path)/component/processor/ssp1601/ssp1601.cpp
|
||||
$(object.path)/ares-processor-tlcs900h.o: $(ares.path)/component/processor/tlcs900h/tlcs900h.cpp
|
||||
$(object.path)/ares-processor-upd96050.o: $(ares.path)/component/processor/upd96050/upd96050.cpp
|
||||
$(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz/v30mz.cpp
|
||||
|
||||