mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-10 17:02:56 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba31eafda9 | ||
|
|
7eb0fb268b | ||
|
|
1c876d8bf6 | ||
|
|
40a461621d | ||
|
|
61b358e45b | ||
|
|
1bd36fb2cc | ||
|
|
ad70fadc8e | ||
|
|
69daac5356 | ||
|
|
f7d6650c26 | ||
|
|
5440ef5a4f | ||
|
|
ae89968e2c | ||
|
|
571832a83d | ||
|
|
a04c215003 | ||
|
|
7be6217102 | ||
|
|
a29cd2ce58 | ||
|
|
d5d3d169ca | ||
|
|
9ae2e43c68 | ||
|
|
5815c9d746 | ||
|
|
3e49cebfa4 | ||
|
|
0c8eaf0d4e | ||
|
|
f0fd88f33e | ||
|
|
827babcbbc | ||
|
|
4666cdb9b8 | ||
|
|
c9c1a0d532 | ||
|
|
d53fdb0087 | ||
|
|
c564dc7bc5 | ||
|
|
0111abc565 | ||
|
|
65264090b0 | ||
|
|
70f361094b | ||
|
|
be8fa76e7d | ||
|
|
66dbed4ee0 | ||
|
|
05c749f09b | ||
|
|
b4399c59cc | ||
|
|
37145465f7 | ||
|
|
33a00d4da0 | ||
|
|
8b88e3093e | ||
|
|
71302c0938 | ||
|
|
b702465cc1 | ||
|
|
b822fb3224 | ||
|
|
cc850c7dc1 | ||
|
|
f18c1f9c67 | ||
|
|
ff98ad031c |
No files matched your search
@@ -87,6 +87,24 @@ jobs:
|
||||
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Windows Dependencies
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
export PATH="/c/Users/runneradmin/.cargo/bin:$PATH" # correct on windows-latest as of 2024-02-19
|
||||
if [[ ${{ matrix.platform.name }} == *-arm64 ]]; then
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
rustup target add aarch64-pc-windows-msvc
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh aarch64-pc-windows-msvc
|
||||
popd
|
||||
else
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh
|
||||
popd
|
||||
fi
|
||||
- name: "macOS: recover MoltenVK cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
@@ -108,6 +126,10 @@ jobs:
|
||||
run: |
|
||||
brew install make ninja cmake
|
||||
pip install --no-input setuptools
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/SDL
|
||||
./build-SDL.sh
|
||||
@@ -115,6 +137,9 @@ jobs:
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh
|
||||
popd
|
||||
- name: Set up MSVC environment
|
||||
if: matrix.platform.msvc-arch != ''
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
+3
-1
@@ -9,4 +9,6 @@ out/
|
||||
obj-amd64/
|
||||
obj-arm64/
|
||||
out-amd64/
|
||||
out-arm64/
|
||||
out-arm64/
|
||||
thirdparty/SDL/SDL
|
||||
thirdparty/SDL/libSDL2-2.0.0.dylib
|
||||
@@ -470,3 +470,362 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
librashader
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
### 1. Definitions
|
||||
|
||||
**1.1. “Contributor”**
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
**1.2. “Contributor Version”**
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
**1.3. “Contribution”**
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
**1.4. “Covered Software”**
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
**1.5. “Incompatible With Secondary Licenses”**
|
||||
means
|
||||
|
||||
* **(a)** that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
* **(b)** that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
**1.6. “Executable Form”**
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
**1.7. “Larger Work”**
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
**1.8. “License”**
|
||||
means this document.
|
||||
|
||||
**1.9. “Licensable”**
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
**1.10. “Modifications”**
|
||||
means any of the following:
|
||||
|
||||
* **(a)** any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
* **(b)** any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
**1.11. “Patent Claims” of a Contributor**
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
**1.12. “Secondary License”**
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
**1.13. “Source Code Form”**
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
**1.14. “You” (or “Your”)**
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, “control” means **(a)** the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or **(b)** ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
|
||||
### 2. License Grants and Conditions
|
||||
|
||||
#### 2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
* **(a)** under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
* **(b)** under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
#### 2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
#### 2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
* **(a)** for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
* **(b)** for infringements caused by: **(i)** Your and any other third party's
|
||||
modifications of Covered Software, or **(ii)** the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
* **(c)** under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
#### 2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
#### 2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
#### 2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
#### 2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
|
||||
### 3. Responsibilities
|
||||
|
||||
#### 3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
#### 3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
* **(a)** such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
* **(b)** You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
#### 3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
#### 3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
#### 3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
|
||||
### 4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: **(a)** comply with
|
||||
the terms of this License to the maximum extent possible; and **(b)**
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
|
||||
### 5. Termination
|
||||
|
||||
**5.1.** The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated **(a)** provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and **(b)** on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
**5.2.** If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
**5.3.** In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
|
||||
### 6. Disclaimer of Warranty
|
||||
|
||||
> Covered Software is provided under this License on an “as is”
|
||||
> basis, without warranty of any kind, either expressed, implied, or
|
||||
> statutory, including, without limitation, warranties that the
|
||||
> Covered Software is free of defects, merchantable, fit for a
|
||||
> particular purpose or non-infringing. The entire risk as to the
|
||||
> quality and performance of the Covered Software is with You.
|
||||
> Should any Covered Software prove defective in any respect, You
|
||||
> (not any Contributor) assume the cost of any necessary servicing,
|
||||
> repair, or correction. This disclaimer of warranty constitutes an
|
||||
> essential part of this License. No use of any Covered Software is
|
||||
> authorized under this License except under this disclaimer.
|
||||
|
||||
### 7. Limitation of Liability
|
||||
|
||||
> Under no circumstances and under no legal theory, whether tort
|
||||
> (including negligence), contract, or otherwise, shall any
|
||||
> Contributor, or anyone who distributes Covered Software as
|
||||
> permitted above, be liable to You for any direct, indirect,
|
||||
> special, incidental, or consequential damages of any character
|
||||
> including, without limitation, damages for lost profits, loss of
|
||||
> goodwill, work stoppage, computer failure or malfunction, or any
|
||||
> and all other commercial damages or losses, even if such party
|
||||
> shall have been informed of the possibility of such damages. This
|
||||
> limitation of liability shall not apply to liability for death or
|
||||
> personal injury resulting from such party's negligence to the
|
||||
> extent applicable law prohibits such limitation. Some
|
||||
> jurisdictions do not allow the exclusion or limitation of
|
||||
> incidental or consequential damages, so this exclusion and
|
||||
> limitation may not apply to You.
|
||||
|
||||
|
||||
### 8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
|
||||
### 9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
### 10. Versions of the License
|
||||
|
||||
#### 10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
#### 10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
#### 10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
#### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
## Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
## Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
@@ -63,6 +63,32 @@ Once complete, open a CLANG64 terminal window and proceed with building ares.
|
||||
###### Debug Symbols
|
||||
When building with clang, by default symbols will be generated for debug builds using an MSVC compatible format (CodeView) for use with Windows debugging tools. In order to generate GDB compatible symbols, specify the following option: `symformat=gdb`
|
||||
|
||||
###### Librashader Support
|
||||
ares has introduced support for the [librashader](https://github.com/SnowflakePowered/librashader) library, which is a preprocessor, compiler, and runtime for RetroArch 'slang' shaders. If you are not interested in working with librashaders, you can pass the following option to the `make` command to skip these requirements using: `librashader=false`
|
||||
|
||||
Building the librashader library on Windows requires Rust. Follow these steps to prepare your system and build the librashader library:
|
||||
|
||||
1. Download and execute the rustup-init.exe installer from: https://rustup.rs/
|
||||
2. Open a new Windows terminal and run the following commands:
|
||||
```
|
||||
rustup toolchain install nightly
|
||||
rustup default nightly
|
||||
```
|
||||
3. With Rust successfully installed, you will need to update your PATH environment variable within your MSYS2 environment. In an MSYS2 shell, open $HOME/.bash_profile and append something like the following (note you will need to replace both instances of %USER_NAME% with the real user name you used to install Rust):
|
||||
```
|
||||
if [ -d "/C/Users/%USER_NAME%/.cargo/bin" ] ; then
|
||||
PATH="/C/Users/%USER_NAME%/.cargo/bin:${PATH}"
|
||||
fi
|
||||
```
|
||||
4. Open a new MSYS2 based shell for the compiler you are using (MinGW64|CLANG64|UCRT64), execute `rustup` to validate the path is set properly within your MSYS2 environment.
|
||||
5. In your MSYS2 compiler shell, `cd` into the `ares/thirdparty/librashader` directory
|
||||
6. Execute the build script, build should complete successfully:
|
||||
```
|
||||
./build-librashader.sh
|
||||
```
|
||||
|
||||
This only needs to be done once, or anytime the librashader library is updated. Once built, ares will build with librashader support by default requiring no additional flags.
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
fragment: AANN.fs
|
||||
|
||||
output
|
||||
filter: nearest
|
||||
@@ -1,10 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
wrap: border
|
||||
fragment: shadertoy.fs
|
||||
|
||||
output
|
||||
filter: nearest
|
||||
@@ -1,145 +0,0 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
vertex: lottes.vs
|
||||
fragment: lottes.fs
|
||||
|
||||
output
|
||||
height: 0
|
||||
width: 0
|
||||
filter: linear
|
||||
@@ -1,21 +0,0 @@
|
||||
#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));
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: border
|
||||
fragment: curvature.fs
|
||||
@@ -1,25 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: edge
|
||||
fragment: edge-detection.fs
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,99 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
@@ -1,35 +0,0 @@
|
||||
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
|
||||
@@ -1,39 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
fragment: interlace.fs
|
||||
modulo: 2
|
||||
@@ -1,11 +0,0 @@
|
||||
input
|
||||
filter: nearest
|
||||
|
||||
program
|
||||
filter: nearest
|
||||
height: 100%
|
||||
fragment: pal-r57shell.fs
|
||||
|
||||
output
|
||||
height: 0
|
||||
filter: linear
|
||||
@@ -1,368 +0,0 @@
|
||||
#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
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
program
|
||||
fragment: phosphorish.fs
|
||||
@@ -1,56 +0,0 @@
|
||||
/* 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);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
program
|
||||
filter: linear
|
||||
wrap: border
|
||||
fragment: scanline.fs
|
||||
@@ -1,20 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
input
|
||||
filter: linear
|
||||
|
||||
program
|
||||
fragment: sharp-bilinear.fs
|
||||
|
||||
output
|
||||
filter: linear
|
||||
@@ -1,33 +0,0 @@
|
||||
#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
-1
@@ -10,7 +10,7 @@ Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "136";
|
||||
const string Version = "137";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
||||
@@ -36,6 +36,7 @@ auto SH2::instruction() -> void {
|
||||
do {
|
||||
auto block = recompiler.block(PC - 4);
|
||||
block->execute(*this);
|
||||
ID = 0;
|
||||
} while (CCR < cyclesUntilRecompilerExit);
|
||||
|
||||
// Reset the count as it may have been set to 0 for an early exit
|
||||
@@ -43,7 +44,6 @@ auto SH2::instruction() -> void {
|
||||
|
||||
step(CCR);
|
||||
CCR = 0;
|
||||
ID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +113,8 @@ auto WDC65816::instructionIndexedIndirectRead8(alu8 op) -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
L W.l = readBank(V.w + 0);
|
||||
alu(W.l);
|
||||
}
|
||||
@@ -123,8 +123,8 @@ auto WDC65816::instructionIndexedIndirectRead16(alu16 op) -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
W.l = readBank(V.w + 0);
|
||||
L W.h = readBank(V.w + 1);
|
||||
alu(W.w);
|
||||
|
||||
@@ -90,8 +90,8 @@ auto WDC65816::instructionIndexedIndirectWrite8() -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
L writeBank(V.w + 0, A.l);
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ auto WDC65816::instructionIndexedIndirectWrite16() -> void {
|
||||
U.l = fetch();
|
||||
idle2();
|
||||
idle();
|
||||
V.l = readDirect(U.l + X.w + 0);
|
||||
V.h = readDirect(U.l + X.w + 1);
|
||||
V.l = readDirectX(U.l + X.w, 0);
|
||||
V.h = readDirectX(U.l + X.w, 1);
|
||||
writeBank(V.w + 0, A.l);
|
||||
L writeBank(V.w + 1, A.h);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,13 @@ inline auto WDC65816::writeDirect(u32 address, n8 data) -> void {
|
||||
write(n16(D.w + address), data);
|
||||
}
|
||||
|
||||
inline auto WDC65816::readDirectX(u32 address, u32 offset) -> n8 {
|
||||
// The (direct,X) addressing mode has a bug in which the high byte is
|
||||
// wrapped within the page if E = 1 and D&0xFF != 0.
|
||||
if(EF && D.l) return read(((D.w + address) & 0xffff00) | n8(D.w + address + offset));
|
||||
else return readDirect(address + offset);
|
||||
}
|
||||
|
||||
inline auto WDC65816::readDirectN(u32 address) -> n8 {
|
||||
return read(n16(D.w + address));
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ struct WDC65816 {
|
||||
auto pullN() -> n8;
|
||||
auto pushN(n8 data) -> void;
|
||||
auto readDirect(u32 address) -> n8;
|
||||
auto readDirectX(u32 address, u32 offset) -> n8;
|
||||
auto writeDirect(u32 address, n8 data) -> void;
|
||||
auto readDirectN(u32 address) -> n8;
|
||||
auto readBank(u32 address) -> n8;
|
||||
|
||||
@@ -12,6 +12,7 @@ auto CPU::get(u32 mode, n32 address) -> n32 {
|
||||
} else if(address & 0x0800'0000) {
|
||||
if(mode & Prefetch && wait.prefetch) {
|
||||
prefetchSync(address);
|
||||
prefetchStep(1);
|
||||
word = prefetchRead();
|
||||
if(mode & Word) word |= prefetchRead() << 16;
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,6 @@ auto CPU::prefetchReset() -> void {
|
||||
|
||||
auto CPU::prefetchRead() -> n16 {
|
||||
if(prefetch.empty()) prefetchStep(prefetch.wait);
|
||||
else prefetchStep(1);
|
||||
|
||||
if(prefetch.full()) prefetch.wait = _wait(Half | Sequential, prefetch.load);
|
||||
|
||||
|
||||
+11
-11
@@ -1,14 +1,14 @@
|
||||
#include <gba/gba.hpp>
|
||||
|
||||
//pixel: 4 cycles
|
||||
//pixel: 4 cycles
|
||||
|
||||
//hdraw: 240 pixels ( 960 cycles)
|
||||
//hblank: 68 pixels ( 272 cycles)
|
||||
//scanline: 308 pixels (1232 cycles)
|
||||
//hdraw: 46 cycle wait period, then 240 pixels (total: 1006 cycles)
|
||||
//hblank: 226 cycles
|
||||
//scanline: 1232 cycles
|
||||
|
||||
//vdraw: 160 scanlines (197120 cycles)
|
||||
//vblank: 68 scanlines ( 83776 cycles)
|
||||
//frame: 228 scanlines (280896 cycles)
|
||||
//vdraw: 160 scanlines (197120 cycles)
|
||||
//vblank: 68 scanlines ( 83776 cycles)
|
||||
//frame: 228 scanlines (280896 cycles)
|
||||
|
||||
namespace ares::GameBoyAdvance {
|
||||
|
||||
@@ -105,6 +105,8 @@ auto PPU::main() -> void {
|
||||
if(io.vcoincidence) cpu.irq.flag |= CPU::Interrupt::VCoincidence;
|
||||
}
|
||||
|
||||
step(46);
|
||||
|
||||
if(io.vcounter < 160) {
|
||||
u32 y = io.vcounter;
|
||||
bg0.scanline(y);
|
||||
@@ -135,12 +137,10 @@ auto PPU::main() -> void {
|
||||
if(io.irqhblank) cpu.irq.flag |= CPU::Interrupt::HBlank;
|
||||
if(io.vcounter < 160) cpu.dmaHblank();
|
||||
|
||||
step(240);
|
||||
step(226);
|
||||
io.hblank = 0;
|
||||
if(io.vcounter < 160) cpu.dmaHDMA();
|
||||
|
||||
step(32);
|
||||
if(++io.vcounter == 228) io.vcounter = 0;
|
||||
if(io.vcounter > 1 && io.vcounter < 162) cpu.dmaHDMA();
|
||||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
|
||||
if(address >= 0x0000'0000 && address <= 0x0000'3fff) {
|
||||
if(shm.active()) shm.step(1); if(shs.active()) shs.step(1);
|
||||
if(shm.active()) return shm.bootROM[address >> 1];
|
||||
if(shs.active()) return shs.bootROM[address >> 1];
|
||||
}
|
||||
@@ -15,10 +16,13 @@ auto M32X::readInternal(n1 upper, n1 lower, n32 address, n16 data) -> n16 {
|
||||
if(shs.active()) shs.step(1);
|
||||
}
|
||||
|
||||
// TODO: SH2 ROM accesses need to stall while the m68k is on the bus
|
||||
if(shm.active()) shm.step(6); if(shs.active()) shs.step(6);
|
||||
return cartridge.child->read(upper, lower, address, data);
|
||||
}
|
||||
|
||||
if(address >= 0x0400'0000 && address <= 0x0405'ffff) {
|
||||
if(shm.active()) shm.step(5); if(shs.active()) shs.step(5);
|
||||
return vdp.bbram[address >> 1 & 0xffff];
|
||||
}
|
||||
|
||||
@@ -37,6 +41,7 @@ auto M32X::writeInternal(n1 upper, n1 lower, n32 address, n16 data) -> void {
|
||||
if(address >= 0x0400'0000 && address <= 0x0401'ffff) {
|
||||
if (!vdp.framebufferAccess) return;
|
||||
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
|
||||
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
|
||||
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
|
||||
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
|
||||
return;
|
||||
@@ -44,6 +49,7 @@ auto M32X::writeInternal(n1 upper, n1 lower, n32 address, n16 data) -> void {
|
||||
|
||||
if(address >= 0x0402'0000 && address <= 0x0403'ffff) {
|
||||
if (!vdp.framebufferAccess) return;
|
||||
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
|
||||
if(upper && data.byte(1)) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
|
||||
if(lower && data.byte(0)) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
|
||||
return;
|
||||
@@ -52,6 +58,7 @@ auto M32X::writeInternal(n1 upper, n1 lower, n32 address, n16 data) -> void {
|
||||
if(address >= 0x0404'0000 && address <= 0x0405'ffff) {
|
||||
if (!vdp.framebufferAccess) return;
|
||||
if(!data && (!upper || !lower)) return; //8-bit 0x00 writes do not go through
|
||||
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
|
||||
if(upper) vdp.bbram[address >> 1 & 0xffff].byte(1) = data.byte(1);
|
||||
if(lower) vdp.bbram[address >> 1 & 0xffff].byte(0) = data.byte(0);
|
||||
return;
|
||||
|
||||
@@ -136,6 +136,7 @@ auto M32X::readExternalIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
|
||||
if(address == 0xa1518a) {
|
||||
data.bit( 0) = vdp.framebufferActive;
|
||||
data.bit( 1) = MegaDrive::vdp.refreshing(); //framebuffer access
|
||||
data.bit( 2, 12) = 0;
|
||||
data.bit(13) = vdp.vblank || vdp.hblank; //palette access
|
||||
data.bit(14) = vdp.hblank;
|
||||
data.bit(15) = vdp.vblank;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
if(shm.active()) shm.step(1); if(shs.active()) shs.step(1);
|
||||
|
||||
//interrupt mask
|
||||
if(address == 0x4000) {
|
||||
if(shm.active()) {
|
||||
@@ -69,8 +71,8 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
//communication
|
||||
if(address >= 0x4020 && address <= 0x402f) {
|
||||
data = communication[address >> 1 & 7];
|
||||
if(shm.active()) shm.syncOtherSh2();
|
||||
if(shs.active()) shs.syncOtherSh2();
|
||||
if(shm.active()) { shm.syncOtherSh2(); shm.syncM68k(); }
|
||||
if(shs.active()) { shs.syncOtherSh2(); shs.syncM68k(); }
|
||||
}
|
||||
|
||||
//PWM control
|
||||
@@ -136,8 +138,8 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
|
||||
//frame buffer control
|
||||
if(address == 0x410a) {
|
||||
if(shm.active()) shm.synchronize(cpu);
|
||||
if(shs.active()) shs.synchronize(cpu);
|
||||
if(shm.active()) shm.syncM68k();
|
||||
if(shs.active()) shs.syncM68k();
|
||||
data.bit( 0) = vdp.framebufferActive;
|
||||
data.bit( 1) = MegaDrive::vdp.refreshing(); //framebuffer access
|
||||
data.bit(13) = vdp.vblank || vdp.hblank; //palette access
|
||||
@@ -147,6 +149,7 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
|
||||
//palette
|
||||
if(address >= 0x4200 && address <= 0x43ff) {
|
||||
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
|
||||
data = vdp.cram[address >> 1 & 0xff];
|
||||
}
|
||||
|
||||
@@ -154,6 +157,8 @@ auto M32X::readInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> n16 {
|
||||
}
|
||||
|
||||
auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
|
||||
if(shm.active()) shm.step(1); if(shs.active()) shs.step(1);
|
||||
|
||||
//interrupt mask
|
||||
if(address == 0x4000) {
|
||||
if(lower && shm.active()) {
|
||||
@@ -220,6 +225,8 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
|
||||
|
||||
//communication
|
||||
if(address >= 0x4020 && address <= 0x402f) {
|
||||
if(shm.active()) { shm.syncOtherSh2(); shm.syncM68k(); }
|
||||
if(shs.active()) { shs.syncOtherSh2(); shs.syncM68k(); }
|
||||
if(upper) communication[address >> 1 & 7].byte(1) = data.byte(1);
|
||||
if(lower) communication[address >> 1 & 7].byte(0) = data.byte(0);
|
||||
}
|
||||
@@ -321,6 +328,7 @@ auto M32X::writeInternalIO(n1 upper, n1 lower, n29 address, n16 data) -> void {
|
||||
//palette
|
||||
if(address >= 0x4200 && address <= 0x43ff) {
|
||||
if (!vdp.framebufferAccess) return;
|
||||
if(shm.active()) shm.step(4); if(shs.active()) shs.step(4);
|
||||
if(upper) vdp.cram[address >> 1 & 0xff].byte(1) = data.byte(1);
|
||||
if(lower) vdp.cram[address >> 1 & 0xff].byte(0) = data.byte(0);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ struct M32X {
|
||||
auto power(bool reset) -> void;
|
||||
auto restart() -> void;
|
||||
auto syncOtherSh2() -> void;
|
||||
auto syncM68k() -> void;
|
||||
|
||||
auto busReadByte(u32 address) -> u32 override;
|
||||
auto busReadWord(u32 address) -> u32 override;
|
||||
@@ -66,9 +67,11 @@ struct M32X {
|
||||
} irq;
|
||||
|
||||
s32 cyclesUntilSh2Sync = 0;
|
||||
s32 cyclesUntilM68kSync = 0;
|
||||
s32 cyclesUntilFullSync = 0;
|
||||
s32 minCyclesBetweenFullSyncs = 0;
|
||||
s32 minCyclesBetweenSh2Syncs = 0;
|
||||
s32 minCyclesBetweenM68kSyncs = 0;
|
||||
s32 minCyclesBetweenFullSyncs = 0;
|
||||
};
|
||||
|
||||
struct VDP {
|
||||
|
||||
+11
-2
@@ -60,6 +60,7 @@ auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
|
||||
Thread::step(clocks);
|
||||
cyclesUntilSh2Sync -= clocks;
|
||||
cyclesUntilM68kSync -= clocks;
|
||||
cyclesUntilFullSync -= clocks;
|
||||
|
||||
if(cyclesUntilFullSync <= 0) {
|
||||
@@ -71,9 +72,10 @@ auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
|
||||
auto M32X::SH7604::power(bool reset) -> void {
|
||||
Thread::create(23'000'000, {&M32X::SH7604::main, this});
|
||||
SH2::recompilerStepCycles = 20; // Minimum cycles for recompiler to run for each batch of instructions
|
||||
SH2::recompilerStepCycles = 20; // Minimum cycles for recompiler to run for each batch of instructions
|
||||
minCyclesBetweenFullSyncs = 200; // Minimum cycles between full sync with the M68K/MD side
|
||||
minCyclesBetweenSh2Syncs = 5; // Minimum Cycles between sync with the other SH2 (syncOtherSh2)
|
||||
minCyclesBetweenSh2Syncs = 5; // Minimum Cycles between sync with the other SH2 (syncOtherSh2)
|
||||
minCyclesBetweenM68kSyncs = 10; // Minimum Cycles between sync with the M68K (syncM68k)
|
||||
SH2::power(reset);
|
||||
irq = {};
|
||||
irq.vres.enable = 1;
|
||||
@@ -94,6 +96,13 @@ auto M32X::SH7604::syncOtherSh2() -> void {
|
||||
cyclesUntilSh2Sync = minCyclesBetweenSh2Syncs;
|
||||
}
|
||||
|
||||
auto M32X::SH7604::syncM68k() -> void {
|
||||
// avoid synchronizing if we recently have
|
||||
if(cyclesUntilM68kSync > 0) return;
|
||||
Thread::synchronize(cpu);
|
||||
cyclesUntilM68kSync = minCyclesBetweenM68kSyncs;
|
||||
}
|
||||
|
||||
auto M32X::SH7604::busReadByte(u32 address) -> u32 {
|
||||
if(address & 1) {
|
||||
return m32x.readInternal(0, 1, address & ~1).byte(0);
|
||||
|
||||
@@ -37,8 +37,8 @@ auto M32X::VDP::scanline(u32 pixels[1280], u32 y) -> void {
|
||||
|
||||
auto M32X::VDP::scanlineMode1(u32 pixels[1280], u32 y) -> void {
|
||||
u16 address = fbram[y];
|
||||
for(u32 x = dotshift; x < 320 + dotshift; x++) {
|
||||
u8 color = fbram[address + (x >> 1) & 0xffff].byte(!(x & 1));
|
||||
for(u32 x : range(320)) {
|
||||
u8 color = fbram[address + (x + dotshift >> 1) & 0xffff].byte(!(x + dotshift & 1));
|
||||
plot(&pixels[x * 4], cram[color]);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -40,6 +40,7 @@ auto MCD::CDD::clock() -> void {
|
||||
} break;
|
||||
|
||||
case Status::Playing: {
|
||||
readSubcode();
|
||||
if(session.tracks[io.track].isAudio()) break;
|
||||
mcd.cdc.decode(io.sector);
|
||||
advance();
|
||||
@@ -60,7 +61,6 @@ auto MCD::CDD::advance() -> void {
|
||||
io.track = track();
|
||||
io.sector++;
|
||||
io.sample = 0;
|
||||
readSubcode();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Board {
|
||||
#include "super-system-card.cpp"
|
||||
#include "arcade-card-duo.cpp"
|
||||
#include "arcade-card-pro.cpp"
|
||||
#include "games-express.cpp"
|
||||
#include "debugger.cpp"
|
||||
|
||||
auto Interface::load(Memory::Readable<n8>& memory, string name) -> bool {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
struct GamesExpress : Interface {
|
||||
using Interface::Interface;
|
||||
Memory::Readable<n8> rom;
|
||||
|
||||
auto load() -> void override {
|
||||
Interface::load(rom, "program.rom");
|
||||
}
|
||||
|
||||
auto save() -> void override {
|
||||
}
|
||||
|
||||
auto unload() -> void override {
|
||||
}
|
||||
|
||||
auto read(n8 bank, n13 address, n8 data) -> n8 override {
|
||||
if(bank >= 0x00 && bank <= 0x67) {
|
||||
return rom.read(bank << 13 | address);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto write(n8 bank, n13 address, n8 data) -> void override {
|
||||
}
|
||||
|
||||
auto power() -> void override {
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void override {
|
||||
}
|
||||
};
|
||||
@@ -27,6 +27,7 @@ auto Cartridge::connect() -> void {
|
||||
if(information.board == "Super System Card") board = new Board::SuperSystemCard{*this};
|
||||
if(information.board == "Arcade Card Duo") board = new Board::ArcadeCardDuo{*this};
|
||||
if(information.board == "Arcade Card Pro") board = new Board::ArcadeCardPro{*this};
|
||||
if(information.board == "Games Express") board = new Board::GamesExpress{*this};
|
||||
if(!board) board = new Board::Interface{*this};
|
||||
board->pak = pak;
|
||||
board->load();
|
||||
|
||||
@@ -32,7 +32,7 @@ auto PCD::Drive::read() -> bool {
|
||||
|
||||
//print("* ", reading() ? "data" : "cdda", " read ", lba, " to ", end - 1, "\n");
|
||||
|
||||
pcd.fd->seek(2448 * (abs(session->leadIn.lba) + lba));
|
||||
pcd.fd->seek(2448 * (abs(session->leadIn.lba) + lba + 150));
|
||||
pcd.fd->read({sector, 2448});
|
||||
if(++lba == end) setInactive();
|
||||
return true;
|
||||
|
||||
+6
-3
@@ -3,7 +3,7 @@ auto PCD::read(n8 bank, n13 address, n8 data) -> n8 {
|
||||
return bios.read(address);
|
||||
}
|
||||
|
||||
if(bank >= 0x68 && bank <= 0x78 && Model::PCEngineDuo() && sramEnable()) {
|
||||
if(bank >= 0x68 && bank <= 0x7f && Model::PCEngineDuo() && sramEnable) {
|
||||
return sram.read(bank - 0x68 << 13 | address);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ auto PCD::read(n8 bank, n13 address, n8 data) -> n8 {
|
||||
}
|
||||
|
||||
auto PCD::write(n8 bank, n13 address, n8 data) -> void {
|
||||
if(bank >= 0x68 && bank <= 0x7f && Model::PCEngineDuo() && sramEnable()) {
|
||||
if(bank >= 0x68 && bank <= 0x7f && Model::PCEngineDuo() && sramEnable) {
|
||||
return sram.write(bank - 0x68 << 13 | address, data);
|
||||
}
|
||||
|
||||
@@ -146,7 +146,10 @@ auto PCD::readIO(n13 address, n8 data) -> n8 {
|
||||
}
|
||||
|
||||
auto PCD::writeIO(n13 address, n8 data) -> void {
|
||||
if(address == 0x18c0) io.sramEnable = io.sramEnable << 8 | data;
|
||||
if(address == 0x18c0) {
|
||||
io.sramEnable = io.sramEnable << 8 | data;
|
||||
if(io.sramEnable == 0xaa55) sramEnable = 1; // Remains enabled until power cycle
|
||||
}
|
||||
if(address >= 0x18c4) return;
|
||||
|
||||
address = (n4)address;
|
||||
|
||||
@@ -177,6 +177,7 @@ auto PCD::power() -> void {
|
||||
fader.power();
|
||||
clock = {};
|
||||
io = {};
|
||||
sramEnable = 0;
|
||||
|
||||
if(Model::PCEngineDuo()) {
|
||||
if(auto fp = system.pak->read("bios.rom")) {
|
||||
|
||||
@@ -28,7 +28,6 @@ struct PCD : Thread {
|
||||
static auto Present() -> bool { return true; }
|
||||
|
||||
auto title() const -> string { return information.title; }
|
||||
auto sramEnable() const -> bool { return io.sramEnable == 0xaa55; }
|
||||
auto bramEnable() const -> bool { return io.bramEnable; }
|
||||
|
||||
//pcd.cpp
|
||||
@@ -313,6 +312,8 @@ private:
|
||||
n1 bramEnable;
|
||||
} io;
|
||||
|
||||
n1 sramEnable;
|
||||
|
||||
//unserialized:
|
||||
struct Information {
|
||||
string title;
|
||||
|
||||
@@ -389,7 +389,7 @@ auto PCD::SCSI::commandGetDirectoryInformation() -> void {
|
||||
mode = track->control;
|
||||
}
|
||||
}
|
||||
auto [minute, second, frame] = CD::MSF::fromLBA(150 + lba);
|
||||
auto [minute, second, frame] = CD::MSF::fromLBA(lba);
|
||||
response.write(BCD::encode(minute));
|
||||
response.write(BCD::encode(second));
|
||||
response.write(BCD::encode(frame));
|
||||
@@ -406,7 +406,7 @@ auto PCD::SCSI::commandGetDirectoryInformation() -> void {
|
||||
mode = track->control;
|
||||
}
|
||||
}
|
||||
auto [minute, second, frame] = CD::MSF::fromLBA(150 + lba);
|
||||
auto [minute, second, frame] = CD::MSF::fromLBA(lba);
|
||||
response.write(lba >> 16);
|
||||
response.write(lba >> 8);
|
||||
response.write(lba >> 0);
|
||||
|
||||
@@ -18,6 +18,7 @@ auto PCD::serialize(serializer& s) -> void {
|
||||
s(io.cddaSampleSelect);
|
||||
s(io.sramEnable);
|
||||
s(io.bramEnable);
|
||||
s(sramEnable);
|
||||
}
|
||||
|
||||
auto PCD::Drive::serialize(serializer& s) -> void {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v131";
|
||||
static const string SerializerVersion = "v132";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
@@ -37,7 +37,8 @@ auto SA1::BWRAM::writeCPU(n24 address, n8 data) -> void {
|
||||
address = sa1.io.sbm * 0x2000 + (address & 0x1fff);
|
||||
}
|
||||
|
||||
if(!sa1.io.swen && (n18)address < 0x100 << sa1.io.bwp) return;
|
||||
//note: BW-RAM protection works only when both SWEN and CWEN are disabled.
|
||||
if(!sa1.io.swen && !sa1.io.cwen && (n18)address < 0x100 << sa1.io.bwp) return;
|
||||
return write(address, data);
|
||||
}
|
||||
|
||||
@@ -53,11 +54,6 @@ auto SA1::BWRAM::readSA1(n24 address, n8 data) -> n8 {
|
||||
}
|
||||
}
|
||||
|
||||
//note: CWEN is not emulated, as it breaks Kirby's Dream Land 3:
|
||||
//* BWPA = 02 (protect 400000-4003ff)
|
||||
//* CWEN = 00 (writes disabled)
|
||||
//KDL3 proceeds to write to 4001ax and 40032x which must succeed.
|
||||
|
||||
auto SA1::BWRAM::writeSA1(n24 address, n8 data) -> void {
|
||||
if(sa1.io.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
@@ -75,6 +71,15 @@ auto SA1::BWRAM::readLinear(n24 address, n8 data) -> n8 {
|
||||
}
|
||||
|
||||
auto SA1::BWRAM::writeLinear(n24 address, n8 data) -> void {
|
||||
//note: BW-RAM protection works only when both SWEN and CWEN are disabled.
|
||||
//this is required for Kirby's Dream Land 3 to work:
|
||||
//* BWPA = 02 (protect 400000-4003ff)
|
||||
//* SWEN = 80 (writes enabled)
|
||||
//* CWEN = 00 (writes disabled)
|
||||
//KDL3 proceeds to write to 4001ax and 40032x which must succeed.
|
||||
//note: BWPA also affects SA-1 protection
|
||||
if(!sa1.io.swen && !sa1.io.cwen && (n18)address < 0x100 << sa1.io.bwp) return;
|
||||
|
||||
return write(address, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,8 +101,16 @@ auto SA1::writeIOCPU(n24 address, n8 data) -> void {
|
||||
//(CCNT) SA-1 control
|
||||
case 0x2200:
|
||||
if(io.sa1_resb && !data.bit(5)) {
|
||||
//reset SA-1 CPU (PC bank set to 0x00)
|
||||
//reset SA-1 CPU (PC bank and data bank set to 0x00, clear STP status)
|
||||
r.pc.d = io.crv;
|
||||
r.b = 0x00;
|
||||
r.stp = false;
|
||||
//todo: probably needs a SA-1 CPU reset
|
||||
//reset r.s, r.e, r.wai ...
|
||||
|
||||
//reset io status
|
||||
//todo: reset timing is unknown, CIWP is set to 0 at reset
|
||||
io.ciwp = 0x00;
|
||||
}
|
||||
|
||||
io.smeg = data.bit(0,3);
|
||||
|
||||
@@ -37,7 +37,7 @@ auto SA1::read(n24 address) -> n8 {
|
||||
}
|
||||
|
||||
if((address & 0x40e000) == 0x006000 //00-3f,80-bf:6000-7fff
|
||||
|| (address & 0xf00000) == 0x400000 //40-4f:0000-ffff
|
||||
|| (address & 0xe00000) == 0x400000 //40-5f:0000-ffff
|
||||
|| (address & 0xf00000) == 0x600000 //60-6f:0000-ffff
|
||||
) {
|
||||
step();
|
||||
@@ -81,7 +81,7 @@ auto SA1::write(n24 address, n8 data) -> void {
|
||||
}
|
||||
|
||||
if((address & 0x40e000) == 0x006000 //00-3f,80-bf:6000-7fff
|
||||
|| (address & 0xf00000) == 0x400000 //40-4f:0000-ffff
|
||||
|| (address & 0xe00000) == 0x400000 //40-5f:0000-ffff
|
||||
|| (address & 0xf00000) == 0x600000 //60-6f:0000-ffff
|
||||
) {
|
||||
step();
|
||||
|
||||
+69
-38
@@ -37,38 +37,76 @@ auto APU::unload() -> void {
|
||||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
// further verification could always be useful
|
||||
u32 steps = accurate ? 1 : 128;
|
||||
for(u32 s = 0; s < steps; s++) {
|
||||
// TODO: is the period value (run()) updated before or after the outputs (runOutput())?
|
||||
channel1.run();
|
||||
channel2.run();
|
||||
channel3.run();
|
||||
if(++state.sweepClock == 0) channel3.sweep(); // TODO: which cycle is this, or is it separate?
|
||||
channel4.run();
|
||||
if(accurate) {
|
||||
// TODO: Are the channels ticked before or after the memory fetch?
|
||||
channel1.tick();
|
||||
channel2.tick();
|
||||
channel3.tick();
|
||||
if(++state.sweepClock == 0) channel3.sweep(); // TODO: Is there any relationship between this clock and I/O ports?
|
||||
channel4.tick();
|
||||
|
||||
// TODO: are voice/noise modes handled on different cycles than tone modes?
|
||||
switch(state.apuClock++) {
|
||||
case 0: if(channel1.io.enable) channel1.runOutput(); break;
|
||||
case 1: if(channel2.io.enable || channel2.io.voice) channel2.runOutput(); break;
|
||||
case 2: if(channel3.io.enable) channel3.runOutput(); break;
|
||||
case 3: if(channel4.io.enable) channel4.runOutput(); break;
|
||||
case 4: if(channel5.io.enable) channel5.runOutput(); break; // TODO: which cycle is this?
|
||||
case 5: dma.run(); break; // TODO: which cycle is this?
|
||||
case 6: dacRun(); break; // TODO: which cycle is this?
|
||||
case 0: apu.output(); break;
|
||||
case 122:
|
||||
// TODO: Sound DMA should be blocking the CPU instead.
|
||||
dma.run();
|
||||
apu.sequencerClear();
|
||||
break;
|
||||
case 123: if(channel5.io.enable) channel5.runOutput(); break; // TODO: Which cycle is this performed on?
|
||||
case 124: if(channel1.io.enable) channel1.output(); break;
|
||||
case 125: if(channel2.io.enable || channel2.io.voice) channel2.output(); break;
|
||||
case 126: if(channel3.io.enable) channel3.output(); break;
|
||||
case 127: if(channel4.io.enable) channel4.output(); break;
|
||||
}
|
||||
|
||||
step(1);
|
||||
} else {
|
||||
dma.run();
|
||||
apu.sequencerClear();
|
||||
|
||||
for(u32 s = 0; s < 128; s++) {
|
||||
channel1.tick();
|
||||
channel2.tick();
|
||||
channel3.tick();
|
||||
if(++state.sweepClock == 0) channel3.sweep();
|
||||
channel4.tick();
|
||||
}
|
||||
|
||||
channel5.runOutput();
|
||||
if(channel1.io.enable) channel1.output();
|
||||
if(channel2.io.enable || channel2.io.voice) channel2.output();
|
||||
if(channel3.io.enable) channel3.output();
|
||||
if(channel4.io.enable) channel4.output();
|
||||
|
||||
apu.output();
|
||||
|
||||
step(128);
|
||||
}
|
||||
step(steps);
|
||||
}
|
||||
|
||||
auto APU::sequencerClear() -> void {
|
||||
io.output = {};
|
||||
if(io.seqDbgOutputForce55) {
|
||||
io.output.left = 0x55;
|
||||
io.output.right = 0x55;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::sequencerHeld() -> bool {
|
||||
return io.seqDbgHold || io.seqDbgOutputForce55;
|
||||
}
|
||||
|
||||
auto APU::sample(u32 channel, n5 index) -> n4 {
|
||||
if(io.seqDbgChForce4) return 4;
|
||||
if(io.seqDbgChForce2) return 2;
|
||||
|
||||
n8 data = iram.read((io.waveBase << 6) + (--channel << 4) + (index >> 1));
|
||||
if(index.bit(0) == 0) return data.bit(0,3);
|
||||
if(index.bit(0) == 1) return data.bit(4,7);
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto APU::dacRun() -> void {
|
||||
auto APU::output() -> void {
|
||||
bool outputEnable = io.headphonesConnected ? io.headphonesEnable : io.speakerEnable;
|
||||
|
||||
if(!outputEnable) {
|
||||
@@ -76,25 +114,18 @@ auto APU::dacRun() -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
s32 left = 0;
|
||||
if(channel1.io.enable) left += channel1.output.left;
|
||||
if(channel2.io.enable || channel2.io.voice) left += channel2.output.left;
|
||||
if(channel3.io.enable) left += channel3.output.left;
|
||||
if(channel4.io.enable) left += channel4.output.left;
|
||||
if(channel5.io.enable) left += channel5.output.left * io.headphonesConnected;
|
||||
s32 left = io.output.left;
|
||||
s32 right = io.output.right;
|
||||
|
||||
s32 right = 0;
|
||||
if(channel1.io.enable) right += channel1.output.right;
|
||||
if(channel2.io.enable || channel2.io.voice) right += channel2.output.right;
|
||||
if(channel3.io.enable) right += channel3.output.right;
|
||||
if(channel4.io.enable) right += channel4.output.right;
|
||||
if(channel5.io.enable) right += channel5.output.right * io.headphonesConnected;
|
||||
|
||||
if(!io.headphonesConnected) {
|
||||
left = right = sclamp<16>((((left + right) >> io.speakerShift) & 0xFF) << 7);
|
||||
} else {
|
||||
if(io.headphonesConnected) {
|
||||
left = sclip<16>(left << 5);
|
||||
right = sclip<16>(right << 5);
|
||||
if(channel5.io.enable) {
|
||||
left += channel5.output.left;
|
||||
right += channel5.output.right;
|
||||
}
|
||||
} else {
|
||||
left = right = sclamp<16>((((left + right) >> io.speakerShift) & 0xFF) << 7);
|
||||
}
|
||||
|
||||
//ASWAN has three volume steps (0%, 50%, 100%); SPHINX and SPHINX2 have four (0%, 33%, 66%, 100%)
|
||||
@@ -113,8 +144,8 @@ auto APU::power() -> void {
|
||||
bus.map(this, 0x004a, 0x004c);
|
||||
bus.map(this, 0x004e, 0x0050);
|
||||
bus.map(this, 0x0052);
|
||||
bus.map(this, 0x006a, 0x006b);
|
||||
bus.map(this, 0x0080, 0x0095);
|
||||
bus.map(this, 0x0064, 0x006b);
|
||||
bus.map(this, 0x0080, 0x009b);
|
||||
bus.map(this, 0x009e);
|
||||
|
||||
dma.power();
|
||||
@@ -128,7 +159,7 @@ auto APU::power() -> void {
|
||||
io.headphonesConnected = system.headphones->value();
|
||||
io.masterVolume = SoC::ASWAN() ? 2 : 3;
|
||||
state = {};
|
||||
|
||||
|
||||
state.apuClock = 0;
|
||||
state.sweepClock = 0;
|
||||
}
|
||||
|
||||
+35
-34
@@ -12,9 +12,11 @@ struct APU : Thread, IO {
|
||||
|
||||
auto main() -> void;
|
||||
auto sample(u32 channel, n5 index) -> n4;
|
||||
auto dacRun() -> void;
|
||||
auto output() -> void;
|
||||
auto step(u32 clocks) -> void;
|
||||
auto power() -> void;
|
||||
auto sequencerClear() -> void;
|
||||
auto sequencerHeld() -> bool;
|
||||
|
||||
//io.cpp
|
||||
auto readIO(n16 address) -> n8;
|
||||
@@ -64,8 +66,8 @@ struct APU : Thread, IO {
|
||||
|
||||
struct Channel1 {
|
||||
//channel1.cpp
|
||||
auto run() -> void;
|
||||
auto runOutput() -> void;
|
||||
auto tick() -> void;
|
||||
auto output() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
@@ -82,17 +84,12 @@ struct APU : Thread, IO {
|
||||
n11 period;
|
||||
n5 sampleOffset;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
n8 left;
|
||||
n8 right;
|
||||
} output;
|
||||
} channel1;
|
||||
|
||||
struct Channel2 {
|
||||
//channel2.cpp
|
||||
auto run() -> void;
|
||||
auto runOutput() -> void;
|
||||
auto tick() -> void;
|
||||
auto output() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
@@ -114,18 +111,13 @@ struct APU : Thread, IO {
|
||||
n11 period;
|
||||
n5 sampleOffset;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
n8 left;
|
||||
n8 right;
|
||||
} output;
|
||||
} channel2;
|
||||
|
||||
struct Channel3 {
|
||||
//channel3.cpp
|
||||
auto sweep() -> void;
|
||||
auto run() -> void;
|
||||
auto runOutput() -> void;
|
||||
auto tick() -> void;
|
||||
auto output() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
@@ -146,18 +138,13 @@ struct APU : Thread, IO {
|
||||
n5 sampleOffset;
|
||||
i32 sweepCounter;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
n8 left;
|
||||
n8 right;
|
||||
} output;
|
||||
} channel3;
|
||||
|
||||
struct Channel4 {
|
||||
//channel4.cpp
|
||||
auto noiseSample() -> n4;
|
||||
auto run() -> void;
|
||||
auto runOutput() -> void;
|
||||
auto tick() -> void;
|
||||
auto output() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
@@ -180,15 +167,14 @@ struct APU : Thread, IO {
|
||||
n1 noiseOutput;
|
||||
n15 noiseLFSR;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
n8 left;
|
||||
n8 right;
|
||||
} output;
|
||||
} channel4;
|
||||
|
||||
struct Channel5 {
|
||||
//channel5.cpp
|
||||
auto dmaWrite(n8 sample) -> void;
|
||||
auto manualWrite(n8 sample) -> void;
|
||||
auto write(n8 sample) -> void;
|
||||
auto scale(i8 sample) -> i16;
|
||||
auto runOutput() -> void;
|
||||
auto power() -> void;
|
||||
|
||||
@@ -201,18 +187,21 @@ struct APU : Thread, IO {
|
||||
n3 speed;
|
||||
n1 enable;
|
||||
n4 unknown;
|
||||
n1 leftEnable;
|
||||
n1 rightEnable;
|
||||
n2 mode;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
n32 clock;
|
||||
i8 data;
|
||||
n1 channel;
|
||||
n8 left;
|
||||
n8 right;
|
||||
n1 leftChanged;
|
||||
n1 rightChanged;
|
||||
} state;
|
||||
|
||||
struct Output {
|
||||
i11 left;
|
||||
i11 right;
|
||||
i16 left;
|
||||
i16 right;
|
||||
} output;
|
||||
} channel5;
|
||||
|
||||
@@ -223,6 +212,18 @@ struct APU : Thread, IO {
|
||||
n1 headphonesEnable;
|
||||
n1 headphonesConnected;
|
||||
n2 masterVolume;
|
||||
|
||||
n1 seqDbgHold;
|
||||
n1 seqDbgOutputForce55;
|
||||
n1 seqDbgChForce4;
|
||||
n1 seqDbgChForce2;
|
||||
n4 seqDbgUnknown;
|
||||
|
||||
// This output covers Channels 1-4 (excluding Hyper Voice)
|
||||
struct Output {
|
||||
n10 left;
|
||||
n10 right;
|
||||
} output;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
auto APU::Channel1::run() -> void {
|
||||
auto APU::Channel1::tick() -> void {
|
||||
if(--state.period == io.pitch) {
|
||||
state.period = 0;
|
||||
state.sampleOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel1::runOutput() -> void {
|
||||
auto APU::Channel1::output() -> void {
|
||||
if(apu.sequencerHeld()) return;
|
||||
auto sample = apu.sample(1, state.sampleOffset);
|
||||
output.left = sample * io.volumeLeft;
|
||||
output.right = sample * io.volumeRight;
|
||||
apu.io.output.left += sample * io.volumeLeft;
|
||||
apu.io.output.right += sample * io.volumeRight;
|
||||
}
|
||||
|
||||
auto APU::Channel1::power() -> void {
|
||||
io = {};
|
||||
state = {};
|
||||
output = {};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
auto APU::Channel2::run() -> void {
|
||||
if (!io.voice) {
|
||||
auto APU::Channel2::tick() -> void {
|
||||
if(!io.voice) {
|
||||
if(--state.period == io.pitch) {
|
||||
state.period = 0;
|
||||
state.sampleOffset++;
|
||||
@@ -7,20 +7,20 @@ auto APU::Channel2::run() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel2::runOutput() -> void {
|
||||
auto APU::Channel2::output() -> void {
|
||||
if(apu.sequencerHeld()) return;
|
||||
if(io.voice) {
|
||||
n8 volume = io.volumeLeft << 4 | io.volumeRight << 0;
|
||||
output.left = io.voiceEnableLeftFull ? volume : (n8)(io.voiceEnableLeftHalf ? (volume >> 1) : 0);
|
||||
output.right = io.voiceEnableRightFull ? volume : (n8)(io.voiceEnableRightHalf ? (volume >> 1) : 0);
|
||||
apu.io.output.left += io.voiceEnableLeftFull ? volume : (n8)(io.voiceEnableLeftHalf ? (volume >> 1) : 0);
|
||||
apu.io.output.right += io.voiceEnableRightFull ? volume : (n8)(io.voiceEnableRightHalf ? (volume >> 1) : 0);
|
||||
} else {
|
||||
auto sample = apu.sample(2, state.sampleOffset);
|
||||
output.left = sample * io.volumeLeft;
|
||||
output.right = sample * io.volumeRight;
|
||||
apu.io.output.left += sample * io.volumeLeft;
|
||||
apu.io.output.right += sample * io.volumeRight;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel2::power() -> void {
|
||||
io = {};
|
||||
state = {};
|
||||
output = {};
|
||||
}
|
||||
@@ -5,21 +5,21 @@ auto APU::Channel3::sweep() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel3::run() -> void {
|
||||
auto APU::Channel3::tick() -> void {
|
||||
if(--state.period == io.pitch) {
|
||||
state.period = 0;
|
||||
state.sampleOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel3::runOutput() -> void {
|
||||
auto APU::Channel3::output() -> void {
|
||||
if(apu.sequencerHeld()) return;
|
||||
auto sample = apu.sample(3, state.sampleOffset);
|
||||
output.left = sample * io.volumeLeft;
|
||||
output.right = sample * io.volumeRight;
|
||||
apu.io.output.left += sample * io.volumeLeft;
|
||||
apu.io.output.right += sample * io.volumeRight;
|
||||
}
|
||||
|
||||
auto APU::Channel3::power() -> void {
|
||||
io = {};
|
||||
state = {};
|
||||
output = {};
|
||||
}
|
||||
@@ -2,7 +2,7 @@ auto APU::Channel4::noiseSample() -> n4 {
|
||||
return state.noiseOutput ? 0xf : 0x0;
|
||||
}
|
||||
|
||||
auto APU::Channel4::run() -> void {
|
||||
auto APU::Channel4::tick() -> void {
|
||||
if(--state.period == io.pitch) {
|
||||
state.period = 0;
|
||||
state.sampleOffset++;
|
||||
@@ -23,14 +23,14 @@ auto APU::Channel4::run() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel4::runOutput() -> void {
|
||||
auto APU::Channel4::output() -> void {
|
||||
if(apu.sequencerHeld()) return;
|
||||
auto sample = io.noise ? noiseSample() : apu.sample(4, state.sampleOffset);
|
||||
output.left = sample * io.volumeLeft;
|
||||
output.right = sample * io.volumeRight;
|
||||
apu.io.output.left += sample * io.volumeLeft;
|
||||
apu.io.output.right += sample * io.volumeRight;
|
||||
}
|
||||
|
||||
auto APU::Channel4::power() -> void {
|
||||
io = {};
|
||||
state = {};
|
||||
output = {};
|
||||
}
|
||||
@@ -1,14 +1,59 @@
|
||||
auto APU::Channel5::runOutput() -> void {
|
||||
i11 sample;
|
||||
auto APU::Channel5::dmaWrite(n8 sample) -> void {
|
||||
if (io.mode == 0) {
|
||||
write(sample);
|
||||
state.channel ^= 1;
|
||||
}
|
||||
if (io.mode == 1 || io.mode == 3) {
|
||||
state.channel = 0;
|
||||
write(sample);
|
||||
}
|
||||
if (io.mode == 2 || io.mode == 3) {
|
||||
state.channel = 1;
|
||||
write(sample);
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel5::manualWrite(n8 sample) -> void {
|
||||
write(sample);
|
||||
state.channel ^= 1;
|
||||
}
|
||||
|
||||
auto APU::Channel5::write(n8 sample) -> void {
|
||||
if (state.channel) {
|
||||
state.right = sample;
|
||||
state.rightChanged = 1;
|
||||
} else {
|
||||
state.left = sample;
|
||||
state.leftChanged = 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel5::scale(i8 sample) -> i16 {
|
||||
if (!io.volume) return (sample << 8);
|
||||
|
||||
switch(io.scale) {
|
||||
case 0: sample = (n8)state.data << 3 - io.volume; break;
|
||||
case 1: sample = (n8)state.data - 0x100 << 3 - io.volume; break;
|
||||
case 2: sample = (i8)state.data << 3 - io.volume; break;
|
||||
case 3: sample = (n8)state.data << 3; break;
|
||||
case 0: return ((n8)sample << 8 - io.volume);
|
||||
case 1: return ((n8)sample << 8 - io.volume) - (0x10000 >> io.volume);
|
||||
case 2: return (sample << 8 - io.volume);
|
||||
case 3: return (sample << 8);
|
||||
}
|
||||
|
||||
output.left = io.leftEnable ? sample : (i11)0;
|
||||
output.right = io.rightEnable ? sample : (i11)0;
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto APU::Channel5::runOutput() -> void {
|
||||
static constexpr s32 divisors[8] = {1, 2, 3, 4, 5, 6, 8, 12};
|
||||
if (++state.clock < divisors[io.speed]) return;
|
||||
state.clock = 0;
|
||||
|
||||
if (state.leftChanged) {
|
||||
output.left = scale(state.left);
|
||||
state.leftChanged = 0;
|
||||
}
|
||||
if (state.rightChanged) {
|
||||
output.right = scale(state.right);
|
||||
state.rightChanged = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto APU::Channel5::power() -> void {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ auto APU::DMA::run() -> void {
|
||||
apu.channel2.io.volumeRight = data.bit(0,3);
|
||||
apu.channel2.io.volumeLeft = data.bit(4,7);
|
||||
} else {
|
||||
apu.channel5.state.data = data;
|
||||
apu.channel5.dmaWrite(data);
|
||||
}
|
||||
|
||||
if(io.hold) return;
|
||||
|
||||
+46
-8
@@ -33,9 +33,9 @@ auto APU::readIO(n16 address) -> n8 {
|
||||
|
||||
case 0x006b: //SND_HYPER_CHAN_CTRL
|
||||
if(!system.color()) break;
|
||||
data.bit(0,3) = channel5.io.unknown;
|
||||
data.bit(5) = channel5.io.leftEnable;
|
||||
data.bit(6) = channel5.io.rightEnable;
|
||||
data.bit(0,3) = channel5.io.unknown;
|
||||
if (data.bit(4)) channel5.state.channel = 0;
|
||||
data.bit(5,6) = channel5.io.mode;
|
||||
break;
|
||||
|
||||
case range2(0x0080, 0x0081): //SND_CH1_PITCH
|
||||
@@ -120,10 +120,26 @@ auto APU::readIO(n16 address) -> n8 {
|
||||
data.bit(3) = channel2.io.voiceEnableLeftFull;
|
||||
break;
|
||||
|
||||
case 0x0095: //SND_HYPERVOICE
|
||||
if(!system.color()) break;
|
||||
data = channel5.state.data;
|
||||
case 0x0095:
|
||||
data.bit(0) = io.seqDbgHold;
|
||||
data.bit(1,4) = io.seqDbgUnknown;
|
||||
data.bit(5) = io.seqDbgOutputForce55;
|
||||
data.bit(6) = io.seqDbgChForce2;
|
||||
data.bit(7) = io.seqDbgChForce4;
|
||||
break;
|
||||
|
||||
case range2(0x0096, 0x0097): //SND_OUT_R
|
||||
data = io.output.right.byte(address - 0x0096);
|
||||
break;
|
||||
|
||||
case range2(0x0098, 0x0099): //SND_OUT_L
|
||||
data = io.output.left.byte(address - 0x0098);
|
||||
break;
|
||||
|
||||
case range2(0x009a, 0x009b): { //SND_OUT_M
|
||||
n11 outputM = io.output.left + io.output.right;
|
||||
data = outputM.byte(address - 0x009a);
|
||||
} break;
|
||||
|
||||
case 0x009e: //SND_VOLUME
|
||||
if(!SoC::ASWAN()) {
|
||||
@@ -162,6 +178,21 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
dma.io.enable = data.bit(7);
|
||||
} break;
|
||||
|
||||
case range2(0x0064, 0x0065):
|
||||
if(!system.color()) break;
|
||||
channel5.output.left.byte(address - 0x64) = data;
|
||||
break;
|
||||
|
||||
case range2(0x0066, 0x0067):
|
||||
if(!system.color()) break;
|
||||
channel5.output.right.byte(address - 0x66) = data;
|
||||
break;
|
||||
|
||||
case 0x0069:
|
||||
if(!system.color()) break;
|
||||
channel5.manualWrite(data);
|
||||
break;
|
||||
|
||||
case 0x006a: //SND_HYPER_CTRL
|
||||
if(!system.color()) break;
|
||||
channel5.io.volume = data.bit(0,1);
|
||||
@@ -173,8 +204,7 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
case 0x006b: //SND_HYPER_CHAN_CTRL
|
||||
if(!system.color()) break;
|
||||
channel5.io.unknown = data.bit(0,3);
|
||||
channel5.io.leftEnable = data.bit(5);
|
||||
channel5.io.rightEnable = data.bit(6);
|
||||
channel5.io.mode = data.bit(5,6);
|
||||
break;
|
||||
|
||||
case range2(0x0080, 0x0081): //SND_CH1_PITCH
|
||||
@@ -254,6 +284,14 @@ auto APU::writeIO(n16 address, n8 data) -> void {
|
||||
channel2.io.voiceEnableLeftFull = data.bit(3);
|
||||
break;
|
||||
|
||||
case 0x0095:
|
||||
io.seqDbgHold = data.bit(0);
|
||||
io.seqDbgUnknown = data.bit(1,4);
|
||||
io.seqDbgOutputForce55 = data.bit(5);
|
||||
io.seqDbgChForce2 = data.bit(6);
|
||||
io.seqDbgChForce4 = data.bit(7);
|
||||
break;
|
||||
|
||||
case 0x009e: //SND_VOLUME
|
||||
if(!SoC::ASWAN()) {
|
||||
io.masterVolume = data.bit(0,1);
|
||||
|
||||
@@ -14,6 +14,15 @@ auto APU::serialize(serializer& s) -> void {
|
||||
s(io.headphonesEnable);
|
||||
s(io.headphonesConnected);
|
||||
s(io.masterVolume);
|
||||
|
||||
s(io.seqDbgHold);
|
||||
s(io.seqDbgOutputForce55);
|
||||
s(io.seqDbgChForce4);
|
||||
s(io.seqDbgChForce2);
|
||||
s(io.seqDbgUnknown);
|
||||
|
||||
s(io.output.left);
|
||||
s(io.output.right);
|
||||
s(state.sweepClock);
|
||||
s(state.apuClock);
|
||||
}
|
||||
@@ -39,8 +48,6 @@ auto APU::Channel1::serialize(serializer& s) -> void {
|
||||
s(io.enable);
|
||||
s(state.period);
|
||||
s(state.sampleOffset);
|
||||
s(output.left);
|
||||
s(output.right);
|
||||
}
|
||||
|
||||
auto APU::Channel2::serialize(serializer& s) -> void {
|
||||
@@ -55,8 +62,6 @@ auto APU::Channel2::serialize(serializer& s) -> void {
|
||||
s(io.voiceEnableRightFull);
|
||||
s(state.period);
|
||||
s(state.sampleOffset);
|
||||
s(output.left);
|
||||
s(output.right);
|
||||
}
|
||||
|
||||
auto APU::Channel3::serialize(serializer& s) -> void {
|
||||
@@ -70,8 +75,6 @@ auto APU::Channel3::serialize(serializer& s) -> void {
|
||||
s(state.period);
|
||||
s(state.sampleOffset);
|
||||
s(state.sweepCounter);
|
||||
s(output.left);
|
||||
s(output.right);
|
||||
}
|
||||
|
||||
auto APU::Channel4::serialize(serializer& s) -> void {
|
||||
@@ -87,8 +90,6 @@ auto APU::Channel4::serialize(serializer& s) -> void {
|
||||
s(state.sampleOffset);
|
||||
s(state.noiseOutput);
|
||||
s(state.noiseLFSR);
|
||||
s(output.left);
|
||||
s(output.right);
|
||||
}
|
||||
|
||||
auto APU::Channel5::serialize(serializer& s) -> void {
|
||||
@@ -97,10 +98,13 @@ auto APU::Channel5::serialize(serializer& s) -> void {
|
||||
s(io.speed);
|
||||
s(io.enable);
|
||||
s(io.unknown);
|
||||
s(io.leftEnable);
|
||||
s(io.rightEnable);
|
||||
s(io.mode);
|
||||
s(state.clock);
|
||||
s(state.data);
|
||||
s(state.channel);
|
||||
s(state.left);
|
||||
s(state.right);
|
||||
s(state.leftChanged);
|
||||
s(state.rightChanged);
|
||||
s(output.left);
|
||||
s(output.right);
|
||||
}
|
||||
+40
-6
@@ -6,7 +6,8 @@ vulkan := true
|
||||
sdl2 := true
|
||||
local := true
|
||||
lto := true
|
||||
flags += -I. -I.. -I../ares -I../thirdparty -DMIA_LIBRARY
|
||||
librashader := true
|
||||
flags += -I. -I.. -I../ares -I../thirdparty -I../thirdparty/librashader/include -DMIA_LIBRARY
|
||||
|
||||
nall.path := ../nall
|
||||
include $(nall.path)/GNUmakefile
|
||||
@@ -36,6 +37,16 @@ tzxfile.path := $(thirdparty.path)/TZXFile
|
||||
ymfm.path := $(thirdparty.path)/ymfm
|
||||
include $(thirdparty.path)/GNUmakefile
|
||||
|
||||
ifeq ($(platform),macos)
|
||||
librashader.path := $(thirdparty.path)/librashader/target/optimized/librashader.dylib
|
||||
else ifeq ($(platform), windows)
|
||||
ifneq ($(wildcard $(thirdparty.path)/librashader/target/aarch64-pc-windows-msvc/optimized/librashader.dll),)
|
||||
librashader.path := $(thirdparty.path)/librashader/target/aarch64-pc-windows-msvc/optimized/librashader.dll
|
||||
else
|
||||
librashader.path := $(thirdparty.path)/librashader/target/optimized/librashader.dll
|
||||
endif
|
||||
endif
|
||||
|
||||
ruby.path := ../ruby
|
||||
include $(ruby.path)/GNUmakefile
|
||||
|
||||
@@ -79,7 +90,25 @@ all.options := $(options) $(libco.options) $(sljit.options) $(libchdr.options) $
|
||||
|
||||
$(all.objects): | $(object.path)
|
||||
|
||||
all: $(all.objects) | $(output.path)
|
||||
all: output
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifeq ($(platform),macos)
|
||||
ifeq ($(librashader), true)
|
||||
ifeq ($(wildcard $(librashader.path)),)
|
||||
$(error Tried to compile ares for macOS with librashader enabled, but no librashader library was found. Compile it with thirdparty/librashader/build-librashader.sh, or disable librashader by compiling ares with librashader=false)
|
||||
endif
|
||||
ares.dylibs += $(librashader.path)
|
||||
endif
|
||||
else ifeq ($(platform), windows)
|
||||
ifeq ($(librashader), true)
|
||||
ifeq ($(wildcard $(librashader.path)),)
|
||||
$(error Tried to compile ares for Windows with librashader enabled, but no librashader library was found. Compile it with thirdparty/librashader/build-librashader.sh, or disable librashader by compiling ares with librashader=false)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
output: $(all.objects) | $(output.path)
|
||||
$(info Linking $(output.path)/$(name)$(extension) ...)
|
||||
+@$(compiler) $(call exe,$(output.path)/$(name)$(extension)) $(all.objects) $(all.options)
|
||||
$(call copy,../LICENSE,$(output.path)/LICENSE.txt)
|
||||
@@ -98,15 +127,20 @@ ifneq ($(ares.dylibs),)
|
||||
endif
|
||||
mv $(output.path)/$(name) $(output.path)/$(name).app/Contents/MacOS/$(name)
|
||||
cp resource/$(name).plist $(output.path)/$(name).app/Contents/Info.plist
|
||||
cp -R $(ares.path)/Shaders $(output.path)/$(name).app/Contents/Resources/
|
||||
cp -R $(mia.path)/Database $(output.path)/$(name).app/Contents/Resources/
|
||||
$(call mkdir,$(output.path)/$(name).app/Contents/Resources/Shaders/)
|
||||
$(call mkdir,$(output.path)/$(name).app/Contents/Resources/Database/)
|
||||
$(call rcopy,$(thirdparty.path)/slang-shaders/*,$(output.path)/$(name).app/Contents/Resources/Shaders/)
|
||||
$(call rcopy,$(mia.path)/Database/*,$(output.path)/$(name).app/Contents/Resources/Database/)
|
||||
sips -s format icns resource/$(name).png --out $(output.path)/$(name).app/Contents/Resources/$(name).icns
|
||||
codesign --force --deep --options runtime --entitlements resource/$(name).selfsigned.entitlements --sign - $(output.path)/$(name).app
|
||||
else ifeq ($(platform),windows)
|
||||
$(call mkdir,$(output.path)/Shaders/)
|
||||
$(call mkdir,$(output.path)/Database/)
|
||||
$(call rcopy,$(ares.path)/Shaders/*,$(output.path)/Shaders/)
|
||||
$(call rcopy,$(thirdparty.path)/slang-shaders/*,$(output.path)/Shaders/)
|
||||
$(call rcopy,$(mia.path)/Database/*,$(output.path)/Database/)
|
||||
ifeq ($(librashader), true)
|
||||
$(call copy,$(librashader.path),$(output.path)/)
|
||||
endif
|
||||
endif
|
||||
|
||||
verbose: nall.verbose ruby.verbose hiro.verbose all;
|
||||
@@ -133,7 +167,7 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||
mkdir -p $(prefix)/share/$(name)/Shaders/
|
||||
mkdir -p $(prefix)/share/$(name)/Database/
|
||||
cp $(output.path)/$(name) $(prefix)/bin/$(name)
|
||||
cp -R $(ares.path)/Shaders/* $(prefix)/share/$(name)/Shaders/
|
||||
cp -R $(thirdparty.path)/slang-shaders/* $(prefix)/share/$(name)/Shaders/
|
||||
cp -R $(mia.path)/Database/* $(prefix)/share/$(name)/Database/
|
||||
cp resource/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
||||
cp resource/$(name).png $(prefix)/share/icons/hicolor/256x256/apps/$(name).png
|
||||
|
||||
@@ -5,15 +5,17 @@ struct PCEngineCD : PCEngine {
|
||||
auto pak(ares::Node::Object) -> shared_pointer<vfs::directory> override;
|
||||
|
||||
shared_pointer<mia::Pak> bios;
|
||||
u32 regionID = 0;
|
||||
u32 biosID = 0;
|
||||
};
|
||||
|
||||
PCEngineCD::PCEngineCD() {
|
||||
manufacturer = "NEC";
|
||||
name = "PC Engine CD";
|
||||
|
||||
firmware.append({"BIOS", "US", "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db"}); //NTSC-U
|
||||
firmware.append({"BIOS", "Japan", "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"}); //NTSC-J
|
||||
firmware.append({"System-Card 1.0", "Japan", "afe9f27f91ac918348555b86298b4f984643eafa2773196f2c5441ea84f0c3bb"});
|
||||
firmware.append({"System Card 3.0", "Japan", "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"});
|
||||
firmware.append({"System Card 3.0", "US", "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db"});
|
||||
firmware.append({"Games Express" , "Japan", "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d"});
|
||||
|
||||
allocatePorts();
|
||||
}
|
||||
@@ -24,18 +26,27 @@ auto PCEngineCD::load() -> bool {
|
||||
|
||||
auto region = Emulator::region();
|
||||
//if statements below are ordered by lowest to highest priority
|
||||
if(region == "NTSC-J") regionID = 1;
|
||||
if(region == "NTSC-U") regionID = 0;
|
||||
if(region == "NTSC-U") biosID = 2;
|
||||
if(region == "NTSC-J") biosID = 1;
|
||||
|
||||
// Some games require a specific card to be inserted into the system
|
||||
if(auto requiredCard = game->pak->attribute("card")) {
|
||||
if(requiredCard == "System Card 1.0") {
|
||||
biosID = 0;
|
||||
} else if(requiredCard == "Games Express") {
|
||||
biosID = 3;
|
||||
}
|
||||
}
|
||||
|
||||
bios = mia::Medium::create("PC Engine");
|
||||
if(!bios->load(firmware[regionID].location)) return errorFirmware(firmware[regionID]), false;
|
||||
if(!bios->load(firmware[biosID].location)) return errorFirmware(firmware[biosID]), false;
|
||||
|
||||
system = mia::System::create("PC Engine");
|
||||
if(!system->load()) return false;
|
||||
|
||||
ares::PCEngine::option("Pixel Accuracy", settings.video.pixelAccuracy);
|
||||
|
||||
auto name = region == "NTSC-J" ? "PC Engine" : "TurboGrafx 16";
|
||||
auto name = region == "NTSC-J" ? "PC Engine Duo" : "TurboDuo";
|
||||
if(!ares::PCEngine::load(root, {"[NEC] ", name, " (", region, ")"})) return false;
|
||||
|
||||
if(auto port = root->find<ares::Node::Port>("Cartridge Slot")) {
|
||||
|
||||
@@ -12,7 +12,7 @@ SuperGrafxCD::SuperGrafxCD() {
|
||||
manufacturer = "NEC";
|
||||
name = "SuperGrafx CD";
|
||||
|
||||
firmware.append({"BIOS", "Japan", "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"}); //NTSC-J
|
||||
firmware.append({"System Card 3.0", "Japan", "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"}); //NTSC-J
|
||||
|
||||
allocatePorts();
|
||||
}
|
||||
|
||||
@@ -576,32 +576,58 @@ auto Presentation::loadShaders() -> void {
|
||||
|
||||
Group shaders;
|
||||
|
||||
MenuRadioItem none{&videoShaderMenu};
|
||||
none.setText("None").onActivate([&] {
|
||||
MenuCheckItem none{&videoShaderMenu};
|
||||
none.setText("None").onToggle([&] {
|
||||
settings.video.shader = "None";
|
||||
ruby::video.setShader(settings.video.shader);
|
||||
loadShaders();
|
||||
});
|
||||
shaders.append(none);
|
||||
|
||||
MenuRadioItem blur{&videoShaderMenu};
|
||||
blur.setText("Blur").onActivate([&] {
|
||||
MenuCheckItem blur{&videoShaderMenu};
|
||||
blur.setText("Blur").onToggle([&] {
|
||||
settings.video.shader = "Blur";
|
||||
ruby::video.setShader(settings.video.shader);
|
||||
loadShaders();
|
||||
});
|
||||
shaders.append(blur);
|
||||
|
||||
auto location = locate("Shaders/");
|
||||
|
||||
if(ruby::video.driver() == "OpenGL 3.2") {
|
||||
for(auto shader : directory::folders(location, "*.shader")) {
|
||||
if(shaders.objectCount() == 2) videoShaderMenu.append(MenuSeparator());
|
||||
MenuRadioItem item{&videoShaderMenu};
|
||||
item.setText(string{shader}.trimRight(".shader/", 1L)).onActivate([=] {
|
||||
settings.video.shader = {location, shader};
|
||||
auto files = directory::files(location, "*.slangp");
|
||||
for(auto file : files) {
|
||||
MenuCheckItem item{&videoShaderMenu};
|
||||
item.setAttribute("file", {location, file});
|
||||
item.setText(string{file}.trimRight(".slangp", 1L)).onToggle([=] {
|
||||
settings.video.shader = {location, file};
|
||||
ruby::video.setShader(settings.video.shader);
|
||||
loadShaders();
|
||||
});
|
||||
shaders.append(item);
|
||||
}
|
||||
|
||||
for(auto dir : directory::folders(location)) {
|
||||
bool menuAdded = false;
|
||||
Menu folder;
|
||||
|
||||
for(auto file : directory::files({location, "/", dir}, "*.slangp")) {
|
||||
if(!menuAdded) {
|
||||
folder.setText(dir.trimRight("/", 1L));
|
||||
menuAdded = true;
|
||||
videoShaderMenu.append(folder);
|
||||
}
|
||||
|
||||
MenuCheckItem item{&folder};
|
||||
item.setAttribute("file", {location, dir, "/", file});
|
||||
item.setText(string{file}.trimRight(".slangp", 1L)).onToggle([=] {
|
||||
settings.video.shader = {location, dir, "/", file};
|
||||
ruby::video.setShader(settings.video.shader);
|
||||
loadShaders();
|
||||
});
|
||||
shaders.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(program.startShader) {
|
||||
@@ -609,7 +635,7 @@ auto Presentation::loadShaders() -> void {
|
||||
|
||||
if(!program.startShader.imatch("None") &&
|
||||
!program.startShader.imatch("Blur")) {
|
||||
settings.video.shader = {location, program.startShader, ".shader/"};
|
||||
settings.video.shader = {location, program.startShader, ".slangp"};
|
||||
} else {
|
||||
settings.video.shader = program.startShader;
|
||||
}
|
||||
@@ -618,6 +644,7 @@ auto Presentation::loadShaders() -> void {
|
||||
settings.video.shader.imatch("None") ||
|
||||
settings.video.shader.imatch("Blur")) {
|
||||
ruby::video.setShader(settings.video.shader);
|
||||
loadShaders();
|
||||
} else {
|
||||
hiro::MessageDialog()
|
||||
.setTitle("Warning")
|
||||
@@ -630,11 +657,10 @@ auto Presentation::loadShaders() -> void {
|
||||
|
||||
if(settings.video.shader.imatch("None")) {none.setChecked(); settings.video.shader = "None";}
|
||||
if(settings.video.shader.imatch("Blur")) {blur.setChecked(); settings.video.shader = "Blur";}
|
||||
for(auto item : shaders.objects<MenuRadioItem>()) {
|
||||
string fullPath = {location, item.text(), ".shader/"};
|
||||
if(settings.video.shader.imatch(fullPath)) {
|
||||
for(auto item : shaders.objects<MenuCheckItem>()) {
|
||||
if(settings.video.shader.imatch(item.attribute("file"))) {
|
||||
item.setChecked();
|
||||
settings.video.shader = fullPath;
|
||||
settings.video.shader = item.attribute("file");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,12 +78,12 @@ struct Presentation : Window {
|
||||
VerticalLayout layout{this};
|
||||
HorizontalLayout viewportLayout{&layout, Size{~0, ~0}, 0};
|
||||
Viewport viewport{&viewportLayout, Size{~0, ~0}, 0};
|
||||
VerticalLayout iconLayout{&viewportLayout, Size{128, ~0}, 0};
|
||||
Canvas iconSpacer{&iconLayout, Size{128, ~0}, 0};
|
||||
HorizontalLayout iconHorizontal{&iconLayout, Size{128, 128}, 0};
|
||||
Canvas iconCanvas{&iconHorizontal, Size{112, 128}, 0};
|
||||
VerticalLayout iconLayout{&viewportLayout, Size{144, ~0}, 0};
|
||||
Canvas iconSpacer{&iconLayout, Size{144, ~0}, 0};
|
||||
HorizontalLayout iconHorizontal{&iconLayout, Size{144, 128}, 0};
|
||||
Canvas iconCanvas{&iconHorizontal, Size{128, 128}, 0};
|
||||
Canvas iconPadding{&iconHorizontal, Size{16, 128}, 0};
|
||||
Canvas iconBottom{&iconLayout, Size{128, 10}, 0};
|
||||
Canvas iconBottom{&iconLayout, Size{144, 10}, 0};
|
||||
HorizontalLayout statusLayout{&layout, Size{~0, StatusHeight}, 0};
|
||||
Label spacerLeft{&statusLayout, Size{8, ~0}, 0};
|
||||
Label statusLeft{&statusLayout, Size{~0, ~0}, 0};
|
||||
|
||||
@@ -11,8 +11,9 @@ auto DriverSettings::construct() -> void {
|
||||
videoDriverLabel.setText("Driver:");
|
||||
videoDriverAssign.setText("Apply").setEnabled(false).onActivate([&] {
|
||||
settings.video.driver = videoDriverList.selected().text();
|
||||
videoDriverUpdate();
|
||||
videoDriverAssign.setEnabled(false);
|
||||
if (videoDriverUpdate()) {
|
||||
videoDriverAssign.setEnabled(false);
|
||||
}
|
||||
});
|
||||
videoMonitorLabel.setText("Fullscreen monitor:");
|
||||
videoMonitorList.onChange([&] {
|
||||
@@ -48,8 +49,9 @@ auto DriverSettings::construct() -> void {
|
||||
audioDriverLabel.setText("Driver:");
|
||||
audioDriverAssign.setText("Apply").setEnabled(false).onActivate([&] {
|
||||
settings.audio.driver = audioDriverList.selected().text();
|
||||
audioDriverUpdate();
|
||||
audioDriverAssign.setEnabled(false);
|
||||
if (audioDriverUpdate()) {
|
||||
audioDriverAssign.setEnabled(false);
|
||||
}
|
||||
});
|
||||
audioDeviceLabel.setText("Output device:");
|
||||
audioDeviceList.onChange([&] {
|
||||
@@ -91,8 +93,9 @@ auto DriverSettings::construct() -> void {
|
||||
inputDriverLabel.setText("Driver:");
|
||||
inputDriverAssign.setText("Apply").setEnabled(false).onActivate([&] {
|
||||
settings.input.driver = inputDriverList.selected().text();
|
||||
inputDriverUpdate();
|
||||
inputDriverAssign.setEnabled(false);
|
||||
if (inputDriverUpdate()) {
|
||||
inputDriverAssign.setEnabled(false);
|
||||
}
|
||||
});
|
||||
inputDefocusLabel.setText("When focus is lost:");
|
||||
inputDefocusPause.setText("Pause emulation").onActivate([&] {
|
||||
@@ -148,13 +151,14 @@ auto DriverSettings::videoRefresh() -> void {
|
||||
VerticalLayout::resize();
|
||||
}
|
||||
|
||||
auto DriverSettings::videoDriverUpdate() -> void {
|
||||
auto DriverSettings::videoDriverUpdate() -> bool {
|
||||
if(emulator && settings.video.driver != "None" && MessageDialog(
|
||||
"Warning: incompatible drivers may cause this software to crash.\n"
|
||||
"Are you sure you want to change this driver while a game is loaded?"
|
||||
).setAlignment(settingsWindow).question() != "Yes") return;
|
||||
).setAlignment(settingsWindow).question() != "Yes") return false;
|
||||
program.videoDriverUpdate();
|
||||
videoRefresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto DriverSettings::audioRefresh() -> void {
|
||||
@@ -189,13 +193,14 @@ auto DriverSettings::audioRefresh() -> void {
|
||||
VerticalLayout::resize();
|
||||
}
|
||||
|
||||
auto DriverSettings::audioDriverUpdate() -> void {
|
||||
auto DriverSettings::audioDriverUpdate() -> bool {
|
||||
if(emulator && settings.audio.driver != "None" && MessageDialog(
|
||||
"Warning: incompatible drivers may cause this software to crash.\n"
|
||||
"Are you sure you want to change this driver while a game is loaded?"
|
||||
).setAlignment(settingsWindow).question() != "Yes") return;
|
||||
).setAlignment(settingsWindow).question() != "Yes") return false;
|
||||
program.audioDriverUpdate();
|
||||
audioRefresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto DriverSettings::inputRefresh() -> void {
|
||||
@@ -208,11 +213,12 @@ auto DriverSettings::inputRefresh() -> void {
|
||||
VerticalLayout::resize();
|
||||
}
|
||||
|
||||
auto DriverSettings::inputDriverUpdate() -> void {
|
||||
auto DriverSettings::inputDriverUpdate() -> bool {
|
||||
if(emulator && settings.input.driver != "None" && MessageDialog(
|
||||
"Warning: incompatible drivers may cause this software to crash.\n"
|
||||
"Are you sure you want to change this driver while a game is loaded?"
|
||||
).setAlignment(settingsWindow).question() != "Yes") return;
|
||||
).setAlignment(settingsWindow).question() != "Yes") return false;
|
||||
program.inputDriverUpdate();
|
||||
inputRefresh();
|
||||
return true;
|
||||
}
|
||||
@@ -159,6 +159,7 @@ auto Settings::process(bool load) -> void {
|
||||
bind(string, name, emulator->configuration.game);
|
||||
for(auto& firmware : emulator->firmware) {
|
||||
string name = {base, "/Firmware/", firmware.type, ".", firmware.region};
|
||||
name.replace(" ", "-");
|
||||
bind(string, name, firmware.location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,11 +313,11 @@ struct PathSettings : VerticalLayout {
|
||||
struct DriverSettings : VerticalLayout {
|
||||
auto construct() -> void;
|
||||
auto videoRefresh() -> void;
|
||||
auto videoDriverUpdate() -> void;
|
||||
auto videoDriverUpdate() -> bool;
|
||||
auto audioRefresh() -> void;
|
||||
auto audioDriverUpdate() -> void;
|
||||
auto audioDriverUpdate() -> bool;
|
||||
auto inputRefresh() -> void;
|
||||
auto inputDriverUpdate() -> void;
|
||||
auto inputDriverUpdate() -> bool;
|
||||
|
||||
Label videoLabel{this, Size{~0, 0}, 5};
|
||||
HorizontalLayout videoDriverLayout{this, Size{~0, 0}};
|
||||
@@ -432,4 +432,4 @@ extern OptionSettings& optionSettings;
|
||||
extern FirmwareSettings& firmwareSettings;
|
||||
extern PathSettings& pathSettings;
|
||||
extern DriverSettings& driverSettings;
|
||||
extern DebugSettings& debugSettings;
|
||||
extern DebugSettings& debugSettings;
|
||||
@@ -11,6 +11,7 @@
|
||||
];
|
||||
[self addTrackingArea:area];
|
||||
}
|
||||
self.clipsToBounds = true;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@public
|
||||
hiro::mCanvas* canvas;
|
||||
}
|
||||
@property BOOL clipsToBounds;
|
||||
-(id) initWith:(hiro::mCanvas&)canvas;
|
||||
-(void) resetCursorRects;
|
||||
-(NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) {
|
||||
label = &labelReference;
|
||||
}
|
||||
self.clipsToBounds = true;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@public
|
||||
hiro::mLabel* label;
|
||||
}
|
||||
@property BOOL clipsToBounds;
|
||||
-(id) initWith:(hiro::mLabel&)label;
|
||||
-(void) resetCursorRects;
|
||||
-(void) drawRect:(NSRect)dirtyRect;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) {
|
||||
viewport = &viewportReference;
|
||||
}
|
||||
self.clipsToBounds = true;
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@public
|
||||
hiro::mViewport* viewport;
|
||||
}
|
||||
@property BOOL clipsToBounds;
|
||||
-(id) initWith:(hiro::mViewport&)viewport;
|
||||
-(void) resetCursorRects;
|
||||
-(void) drawRect:(NSRect)rect;
|
||||
|
||||
+25
-10
@@ -17,7 +17,8 @@ auto PCEngineCD::load(string location) -> bool {
|
||||
pak = new vfs::directory;
|
||||
pak->setAttribute("title", document["game/title"].string());
|
||||
pak->setAttribute("region", document["game/region"].string());
|
||||
pak->setAttribute("audio", (bool)document["game/audio"]);
|
||||
pak->setAttribute("card", document["game/card"].string());
|
||||
pak->setAttribute("audio", (bool)document["game/audio"]);
|
||||
pak->append("manifest.bml", manifest);
|
||||
if(directory::exists(location)) {
|
||||
pak->append("cd.rom", vfs::disk::open({location, "cd.rom"}, vfs::read));
|
||||
@@ -36,28 +37,42 @@ auto PCEngineCD::save(string location) -> bool {
|
||||
}
|
||||
|
||||
auto PCEngineCD::analyze(string location) -> string {
|
||||
vector<u8> sector;
|
||||
vector<u8> sectors[2];
|
||||
|
||||
if(location.iendsWith(".cue")) {
|
||||
sector = readDataSectorCUE(location, 0);
|
||||
sectors[0] = readDataSectorCUE(location, 0); // NEC
|
||||
sectors[1] = readDataSectorCUE(location, 16); // Games Express
|
||||
} else if (location.iendsWith(".chd")) {
|
||||
sector = readDataSectorCHD(location, 0);
|
||||
sectors[0] = readDataSectorCHD(location, 0); // NEC
|
||||
sectors[1] = readDataSectorCHD(location, 16); // Games Express
|
||||
}
|
||||
|
||||
if(!sector) return CompactDisc::manifestAudio(location);
|
||||
if(!sectors[0] && !sectors[1]) return CompactDisc::manifestAudio(location);
|
||||
|
||||
//yes, "Electronics" is spelled incorrectly in actual PC Engine CD games ...
|
||||
if(memory::compare(sector.data() + 0x264, "NEC Home Electoronics", 21)) {
|
||||
return CompactDisc::manifestAudio(location);
|
||||
}
|
||||
bool isNEC = sectors[0] && (memory::compare(sectors[0].data() + 0x264, "NEC Home Electoronics", 21) == 0);
|
||||
bool isGamesExpress = sectors[1] &&(memory::compare(sectors[1].data() + 0x1, "CD001", 5) == 0);
|
||||
|
||||
//note: there is no method to determine the region for PC Engine CDs ...
|
||||
if(!isGamesExpress && !isNEC) return CompactDisc::manifestAudio(location);
|
||||
|
||||
//note: there is no method to determine the region for PC Engine CDs from the data itself
|
||||
string region = "NTSC-J";
|
||||
if(location.ifind("(USA)")) region = "NTSC-U";
|
||||
|
||||
string card = "any";
|
||||
|
||||
//Altered Beast requires Card 1.0; Game doesn't specify its name in the header
|
||||
//do our best to guess based on filename
|
||||
if(location.ifind("Juuouki")) card = "System Card 1.0";
|
||||
if(location.ifind("Juoki")) card = "System Card 1.0";
|
||||
if(location.ifind("Altered Beast")) card = "System Card 1.0";
|
||||
|
||||
if(!isNEC && isGamesExpress) card = "Games Express";
|
||||
|
||||
string s;
|
||||
s += "game\n";
|
||||
s +={" name: ", Medium::name(location), "\n"};
|
||||
s +={" title: ", Medium::name(location), "\n"};
|
||||
s +={" region: ", region, "\n"};
|
||||
s +={" card: ", card, "\n"};
|
||||
return s;
|
||||
}
|
||||
@@ -121,6 +121,12 @@ auto PCEngine::analyze(vector<u8>& data) -> string {
|
||||
//TurboGrafx Super System Card 3.00
|
||||
if(digest == "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db") board = "Super System Card", region = "NTSC-U";
|
||||
|
||||
// Games Express (Blue)
|
||||
if(digest == "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d") board = "Games Express", region = "NTSC-J";
|
||||
|
||||
// Games Express (Green)
|
||||
if(digest == "da173b20694c2b52087b099b8c44e471d3ee08a666c90d4afd997f8e1382add8") board = "Games Express", region = "NTSC-J";
|
||||
|
||||
string s;
|
||||
s += "game\n";
|
||||
s +={" sha256: ", digest, "\n"};
|
||||
|
||||
@@ -155,6 +155,7 @@ endif
|
||||
# build optimization levels
|
||||
ifeq ($(build),debug)
|
||||
symbols = true
|
||||
lto = false
|
||||
ifeq ($(cl),true)
|
||||
flags += -Od
|
||||
else
|
||||
|
||||
@@ -25,7 +25,6 @@ namespace nall {
|
||||
static constexpr bool GCC = 0;
|
||||
static constexpr bool Microsoft = 0;
|
||||
};
|
||||
#pragma clang diagnostic error "-Wc++20-extensions"
|
||||
#pragma clang diagnostic error "-Wgnu-case-range"
|
||||
#pragma clang diagnostic error "-Wgnu-statement-expression"
|
||||
#pragma clang diagnostic error "-Wvla"
|
||||
|
||||
+12
-3
@@ -26,11 +26,13 @@ ifeq ($(ruby),)
|
||||
ifeq ($(wildcard $(macsdl)),)
|
||||
$(error Tried to compile ruby for macOS with SDL2 linked, but no SDL2 library was found. Compile it with thirdparty/SDL/build-sdl.sh, or disable SDL by compiling ares with sdl2=false)
|
||||
endif
|
||||
flags += -DMACOS_COMPILED_SDL
|
||||
ruby += audio.sdl
|
||||
ruby += input.sdl
|
||||
endif
|
||||
else ifeq ($(platform),linux)
|
||||
pkg_check = $(if $(shell $(pkg_config) $1 && echo 1),$2)
|
||||
ruby += video.glx video.glx2 video.xshm
|
||||
ruby += video.glx video.xshm
|
||||
ruby += $(call pkg_check,xv,video.xvideo)
|
||||
ruby += audio.oss audio.alsa
|
||||
ruby += $(call pkg_check,openal,audio.openal)
|
||||
@@ -62,14 +64,18 @@ endif
|
||||
|
||||
ifeq ($(platform),macos)
|
||||
ruby.flags := $(flags.objcpp)
|
||||
ifeq ($(sdl2),true)
|
||||
ruby.flags += -I ../thirdparty/SDL/SDL/include/
|
||||
endif
|
||||
else
|
||||
ruby.flags := $(flags.cpp)
|
||||
endif
|
||||
|
||||
ruby.flags += -I../thirdparty
|
||||
ruby.flags += $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
|
||||
ifeq ($(pkg_config),)
|
||||
# TODO: add SDL2 cflags
|
||||
else
|
||||
else ifeq ($(wildcard $(macsdl)),)
|
||||
ruby.flags += $(if $(findstring input.sdl,$(ruby)),$(shell $(pkg_config) sdl2 --cflags))
|
||||
ruby.flags += $(if $(findstring audio.sdl,$(ruby)),$(shell $(pkg_config) sdl2 --cflags))
|
||||
endif
|
||||
@@ -98,7 +104,7 @@ ifeq ($(platform),windows)
|
||||
ruby.options += $(if $(findstring input.sdl,$(ruby)),$(shell $(pkg_config) sdl2 --libs --static))
|
||||
ruby.options += $(if $(findstring audio.sdl,$(ruby)),$(shell $(pkg_config) sdl2 --libs --static))
|
||||
endif
|
||||
else
|
||||
else ifeq ($(wildcard $(macsdl)),)
|
||||
ruby.options += $(if $(findstring input.sdl,$(ruby)),$(shell $(pkg_config) sdl2 --libs))
|
||||
ruby.options += $(if $(findstring audio.sdl,$(ruby)),$(shell $(pkg_config) sdl2 --libs))
|
||||
endif
|
||||
@@ -114,6 +120,9 @@ endif
|
||||
ifeq ($(platform),macos)
|
||||
ruby.options += -framework IOKit
|
||||
ruby.options += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
|
||||
ifeq ($(sdl2),true)
|
||||
ruby.options += -L../thirdparty/SDL/ -lSDL2-2.0.0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(platform),linux)
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#if defined(MACOS_COMPILED_SDL)
|
||||
#include "SDL.h"
|
||||
#else
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
|
||||
struct AudioSDL : AudioDriver {
|
||||
AudioSDL& self = *this;
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#if defined(MACOS_COMPILED_SDL)
|
||||
#include "SDL.h"
|
||||
#else
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
#include "shared/rawinput.cpp"
|
||||
|
||||
@@ -1,417 +0,0 @@
|
||||
//Xorg/GLX OpenGL 2.0 driver
|
||||
|
||||
//note: this is a fallback driver for use when OpenGL 3.2 is not available.
|
||||
//see glx.cpp for comments on how this driver operates (they are very similar.)
|
||||
|
||||
#if defined(DISPLAY_XORG)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#ifndef glGetProcAddress
|
||||
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
|
||||
#endif
|
||||
#elif defined(DISPLAY_QUARTZ)
|
||||
#include <OpenGL/gl3.h>
|
||||
#elif defined(DISPLAY_WINDOWS)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#ifndef glGetProcAddress
|
||||
#define glGetProcAddress(name) wglGetProcAddress(name)
|
||||
#endif
|
||||
#else
|
||||
#error "ruby::OpenGL2: unsupported platform"
|
||||
#endif
|
||||
|
||||
struct VideoGLX2 : VideoDriver {
|
||||
VideoGLX2& self = *this;
|
||||
VideoGLX2(Video& super) : VideoDriver(super) { construct(); }
|
||||
~VideoGLX2() { destruct(); }
|
||||
|
||||
auto create() -> bool override {
|
||||
VideoDriver::exclusive = true;
|
||||
VideoDriver::format = "ARGB24";
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto driver() -> string override { return "OpenGL 2.0"; }
|
||||
auto ready() -> bool override { return _ready; }
|
||||
|
||||
auto hasFullScreen() -> bool override { return true; }
|
||||
auto hasMonitor() -> bool override { return true; }
|
||||
auto hasContext() -> bool override { return true; }
|
||||
auto hasBlocking() -> bool override { return true; }
|
||||
auto hasFlush() -> bool override { return true; }
|
||||
auto hasShader() -> bool override { return true; }
|
||||
|
||||
auto hasFormats() -> vector<string> override {
|
||||
if(_depth == 30) return {"ARGB30", "ARGB24"};
|
||||
if(_depth == 24) return {"ARGB24"};
|
||||
return {"ARGB24"}; //fallback
|
||||
}
|
||||
|
||||
auto setFullScreen(bool fullScreen) -> bool override {
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setMonitor(string monitor) -> bool override {
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setContext(uintptr context) -> bool override {
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool override {
|
||||
acquireContext();
|
||||
glXSwapInterval(blocking);
|
||||
releaseContext();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setFormat(string format) -> bool override {
|
||||
if(format == "ARGB24") {
|
||||
_glFormat = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
if(format == "ARGB30") {
|
||||
_glFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto setShader(string shader) -> bool override {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto focused() -> bool override {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto clear() -> void override {
|
||||
acquireContext();
|
||||
memory::fill<u32>(_glBuffer, _glWidth * _glHeight);
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glFlush();
|
||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||
releaseContext();
|
||||
}
|
||||
|
||||
auto size(u32& width, u32& height) -> void override {
|
||||
if(self.fullScreen) {
|
||||
width = _monitorWidth;
|
||||
height = _monitorHeight;
|
||||
} else {
|
||||
XWindowAttributes parent;
|
||||
XGetWindowAttributes(_display, _parent, &parent);
|
||||
width = parent.width;
|
||||
height = parent.height;
|
||||
}
|
||||
}
|
||||
|
||||
auto acquire(u32*& data, u32& pitch, u32 width, u32 height) -> bool override {
|
||||
if(width != _width || height != _height) resize(width, height);
|
||||
pitch = _glWidth * sizeof(u32);
|
||||
return data = _glBuffer;
|
||||
}
|
||||
|
||||
auto release() -> void override {
|
||||
}
|
||||
|
||||
auto output(u32 width, u32 height) -> void override {
|
||||
acquireContext();
|
||||
XWindowAttributes window;
|
||||
XGetWindowAttributes(_display, _window, &window);
|
||||
|
||||
XWindowAttributes parent;
|
||||
XGetWindowAttributes(_display, _parent, &parent);
|
||||
|
||||
if(window.width != parent.width || window.height != parent.height) {
|
||||
XResizeWindow(_display, _window, parent.width, parent.height);
|
||||
}
|
||||
|
||||
u32 viewportX = 0;
|
||||
u32 viewportY = 0;
|
||||
u32 viewportWidth = parent.width;
|
||||
u32 viewportHeight = parent.height;
|
||||
|
||||
if(self.fullScreen) {
|
||||
viewportX = _monitorX;
|
||||
viewportY = _monitorY;
|
||||
viewportWidth = _monitorWidth;
|
||||
viewportHeight = _monitorHeight;
|
||||
}
|
||||
|
||||
if(!width) width = viewportWidth;
|
||||
if(!height) height = viewportHeight;
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, self.shader == "Blur" ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, self.shader == "Blur" ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
//vertex coordinates range from (0,0) to (1,1) for the entire desktop (all monitors)
|
||||
glOrtho(0, 1, 0, 1, -1.0, 1.0);
|
||||
//set the viewport to the entire desktop (all monitors)
|
||||
glViewport(0, 0, parent.width, parent.height);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, _glWidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height, GL_BGRA, _glFormat, _glBuffer);
|
||||
|
||||
//normalize texture coordinates and adjust for NPOT textures
|
||||
f64 w = (f64)_width / (f64)_glWidth;
|
||||
f64 h = (f64)_height / (f64)_glHeight;
|
||||
|
||||
//size of the active monitor
|
||||
f64 mw = (f64)viewportWidth / (f64)parent.width;
|
||||
f64 mh = (f64)viewportHeight / (f64)parent.height;
|
||||
|
||||
//offset of the active monitor
|
||||
f64 mx = (f64)viewportX / (f64)parent.width;
|
||||
f64 my = (f64)viewportY / (f64)parent.height;
|
||||
|
||||
//size of the render area
|
||||
f64 vw = (f64)width / (f64)parent.width;
|
||||
f64 vh = (f64)height / (f64)parent.height;
|
||||
|
||||
//center the render area within the active monitor
|
||||
f64 vl = mx + (mw - vw) / 2;
|
||||
f64 vt = my + (mh - vh) / 2;
|
||||
f64 vr = vl + vw;
|
||||
f64 vb = vt + vh;
|
||||
|
||||
//OpenGL places (0,0) at the bottom left; convert our (0,0) at the top left to this form:
|
||||
vt = 1.0 - vt;
|
||||
vb = 1.0 - vb;
|
||||
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0, 0); glVertex3f(vl, vt, 0);
|
||||
glTexCoord2f(w, 0); glVertex3f(vr, vt, 0);
|
||||
glTexCoord2f(0, h); glVertex3f(vl, vb, 0);
|
||||
glTexCoord2f(w, h); glVertex3f(vr, vb, 0);
|
||||
glEnd();
|
||||
glFlush();
|
||||
|
||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||
if(self.flush) glFinish();
|
||||
releaseContext();
|
||||
}
|
||||
|
||||
auto poll() -> void override {
|
||||
while(XPending(_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
if(event.type == Expose) {
|
||||
XWindowAttributes attributes;
|
||||
XGetWindowAttributes(_display, _window, &attributes);
|
||||
super.doUpdate(attributes.width, attributes.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
auto construct() -> void {
|
||||
_display = XOpenDisplay(nullptr);
|
||||
_screen = DefaultScreen(_display);
|
||||
|
||||
XWindowAttributes attributes{};
|
||||
XGetWindowAttributes(_display, RootWindow(_display, _screen), &attributes);
|
||||
_depth = attributes.depth;
|
||||
}
|
||||
|
||||
auto destruct() -> void {
|
||||
terminate();
|
||||
XCloseDisplay(_display);
|
||||
}
|
||||
|
||||
auto acquireContext() -> void {
|
||||
if(!_glXContext) return;
|
||||
while(!glXMakeCurrent(_display, _glXWindow, _glXContext)) spinloop();
|
||||
}
|
||||
|
||||
auto releaseContext() -> void {
|
||||
if(!_glXContext) return;
|
||||
while(!glXMakeCurrent(_display, 0, nullptr)) spinloop();
|
||||
}
|
||||
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
if(!self.fullScreen && !self.context) return false;
|
||||
|
||||
s32 versionMajor = 0, versionMinor = 0;
|
||||
glXQueryVersion(_display, &versionMajor, &versionMinor);
|
||||
if(versionMajor < 1 || (versionMajor == 1 && versionMinor < 2)) return false;
|
||||
|
||||
s32 redDepth = self.format == "RGB30" ? 10 : 8;
|
||||
s32 greenDepth = self.format == "RGB30" ? 10 : 8;
|
||||
s32 blueDepth = self.format == "RGB30" ? 10 : 8;
|
||||
|
||||
s32 attributeList[] = {
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
GLX_RED_SIZE, redDepth,
|
||||
GLX_GREEN_SIZE, greenDepth,
|
||||
GLX_BLUE_SIZE, blueDepth,
|
||||
None
|
||||
};
|
||||
|
||||
s32 fbCount = 0;
|
||||
auto fbConfig = glXChooseFBConfig(_display, _screen, attributeList, &fbCount);
|
||||
if(fbCount == 0) return false;
|
||||
|
||||
auto visual = glXGetVisualFromFBConfig(_display, fbConfig[0]);
|
||||
|
||||
_parent = self.fullScreen ? RootWindow(_display, visual->screen) : (Window)self.context;
|
||||
XWindowAttributes windowAttributes;
|
||||
XGetWindowAttributes(_display, _parent, &windowAttributes);
|
||||
|
||||
auto monitor = Video::monitor(self.monitor);
|
||||
_monitorX = monitor.x;
|
||||
_monitorY = monitor.y;
|
||||
_monitorWidth = monitor.width;
|
||||
_monitorHeight = monitor.height;
|
||||
|
||||
_colormap = XCreateColormap(_display, RootWindow(_display, visual->screen), visual->visual, AllocNone);
|
||||
XSetWindowAttributes attributes{};
|
||||
attributes.border_pixel = 0;
|
||||
attributes.colormap = _colormap;
|
||||
attributes.override_redirect = self.fullScreen;
|
||||
_window = XCreateWindow(_display, _parent,
|
||||
0, 0, windowAttributes.width, windowAttributes.height,
|
||||
0, visual->depth, InputOutput, visual->visual,
|
||||
CWBorderPixel | CWColormap | CWOverrideRedirect, &attributes);
|
||||
XSelectInput(_display, _window, ExposureMask);
|
||||
XSetWindowBackground(_display, _window, 0);
|
||||
XMapWindow(_display, _window);
|
||||
XFlush(_display);
|
||||
|
||||
while(XPending(_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(_display, &event);
|
||||
}
|
||||
|
||||
_glXContext = glXCreateContext(_display, visual, 0, GL_TRUE);
|
||||
glXMakeCurrent(_display, _glXWindow = _window, _glXContext);
|
||||
|
||||
glXSwapIntervalEXT = (int (*)(Display*, GLXDrawable drawable, int))glGetProcAddress("glXSwapIntervalEXT");
|
||||
glXSwapIntervalMESA = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
|
||||
glXSwapIntervalSGI = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
|
||||
|
||||
glXSwapInterval(self.blocking);
|
||||
|
||||
s32 value = 0;
|
||||
glXGetConfig(_display, visual, GLX_DOUBLEBUFFER, &value);
|
||||
_isDoubleBuffered = value;
|
||||
_isDirect = glXIsDirect(_display, _glXContext);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_POLYGON_SMOOTH);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
glEnable(GL_DITHER);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
releaseContext();
|
||||
|
||||
resize(256, 256);
|
||||
return _ready = true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
acquireContext();
|
||||
_ready = false;
|
||||
|
||||
if(_glTexture) {
|
||||
glDeleteTextures(1, &_glTexture);
|
||||
_glTexture = 0;
|
||||
}
|
||||
|
||||
if(_glBuffer) {
|
||||
delete[] _glBuffer;
|
||||
_glBuffer = nullptr;
|
||||
}
|
||||
|
||||
_glWidth = 0;
|
||||
_glHeight = 0;
|
||||
|
||||
if(_glXContext) {
|
||||
glXDestroyContext(_display, _glXContext);
|
||||
_glXContext = nullptr;
|
||||
}
|
||||
|
||||
if(_window) {
|
||||
XUnmapWindow(_display, _window);
|
||||
_window = 0;
|
||||
}
|
||||
|
||||
if(_colormap) {
|
||||
XFreeColormap(_display, _colormap);
|
||||
_colormap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto resize(u32 width, u32 height) -> void {
|
||||
acquireContext();
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
if(_glTexture == 0) glGenTextures(1, &_glTexture);
|
||||
_glWidth = max(_glWidth, width);
|
||||
_glHeight = max(_glHeight, height);
|
||||
delete[] _glBuffer;
|
||||
_glBuffer = new u32[_glWidth * _glHeight]();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, _glTexture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, _glWidth);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _glWidth, _glHeight, 0, GL_BGRA, _glFormat, _glBuffer);
|
||||
releaseContext();
|
||||
}
|
||||
|
||||
bool _ready = false;
|
||||
bool blur = false;
|
||||
|
||||
Display* _display = nullptr;
|
||||
u32 _monitorX = 0;
|
||||
u32 _monitorY = 0;
|
||||
u32 _monitorWidth = 0;
|
||||
u32 _monitorHeight = 0;
|
||||
s32 _screen = 0;
|
||||
u32 _depth = 24; //depth of the default root window
|
||||
Window _parent = 0;
|
||||
Window _window = 0;
|
||||
Colormap _colormap = 0;
|
||||
GLXContext _glXContext = nullptr;
|
||||
GLXWindow _glXWindow = 0;
|
||||
|
||||
bool _isDoubleBuffered = false;
|
||||
bool _isDirect = false;
|
||||
|
||||
u32 _width = 256;
|
||||
u32 _height = 256;
|
||||
|
||||
GLuint _glTexture = 0;
|
||||
u32* _glBuffer = nullptr;
|
||||
u32 _glWidth = 0;
|
||||
u32 _glHeight = 0;
|
||||
u32 _glFormat = GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
|
||||
auto glXSwapInterval(int blocking) -> int {
|
||||
//note that the ordering is very important! MESA declares SGI, but the SGI function does nothing
|
||||
if(glXSwapIntervalEXT && glXSwapIntervalEXT(_display, glXGetCurrentDrawable(), blocking)) return 1;
|
||||
if(glXSwapIntervalMESA && glXSwapIntervalMESA(blocking)) return 1;
|
||||
if(glXSwapIntervalSGI && glXSwapIntervalSGI(blocking)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto (*glXSwapIntervalMESA)(int) -> int = nullptr;
|
||||
auto (*glXSwapIntervalSGI)(int) -> int = nullptr;
|
||||
auto (*glXSwapIntervalEXT)(Display *dpy, GLXDrawable drawable, int interval) -> int = nullptr;
|
||||
};
|
||||
@@ -38,6 +38,8 @@ PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr;
|
||||
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = nullptr;
|
||||
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = nullptr;
|
||||
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = nullptr;
|
||||
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = nullptr;
|
||||
PFNGLGENERATEMIPMAPPROC glGenerateMipmap = nullptr;
|
||||
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = nullptr;
|
||||
#endif
|
||||
#if defined(DISPLAY_WINDOWS)
|
||||
@@ -89,6 +91,8 @@ static bool OpenGLBind() {
|
||||
bind(PFNGLGENFRAMEBUFFERSPROC, glGenFramebuffers);
|
||||
bind(PFNGLDELETEFRAMEBUFFERSPROC, glDeleteFramebuffers);
|
||||
bind(PFNGLBINDFRAMEBUFFERPROC, glBindFramebuffer);
|
||||
bind(PFNGLBLITFRAMEBUFFERPROC, glBlitFramebuffer);
|
||||
bind(PFNGLGENERATEMIPMAPPROC, glGenerateMipmap);
|
||||
bind(PFNGLFRAMEBUFFERTEXTURE2DPROC, glFramebufferTexture2D);
|
||||
#endif
|
||||
#if defined(DISPLAY_WINDOWS)
|
||||
|
||||
+101
-142
@@ -1,82 +1,41 @@
|
||||
#if defined(PLATFORM_MACOS)
|
||||
#import <mach-o/dyld.h>
|
||||
#endif
|
||||
auto OpenGL::setShader(const string& pathname) -> void {
|
||||
for(auto& program : programs) program.release();
|
||||
programs.reset();
|
||||
|
||||
settings.reset();
|
||||
|
||||
format = inputFormat;
|
||||
filter = GL_LINEAR;
|
||||
filter = GL_NEAREST;
|
||||
wrap = GL_CLAMP_TO_BORDER;
|
||||
absoluteWidth = 0, absoluteHeight = 0;
|
||||
relativeWidth = 0, relativeHeight = 0;
|
||||
|
||||
u32 historySize = 0;
|
||||
if(pathname == "None") {
|
||||
filter = GL_NEAREST;
|
||||
} else if(pathname == "Blur") {
|
||||
filter = GL_LINEAR;
|
||||
} else if(directory::exists(pathname)) {
|
||||
auto document = BML::unserialize(file::read({pathname, "manifest.bml"}));
|
||||
|
||||
for(auto node : document["settings"]) {
|
||||
settings.insert({node.name(), node.text()});
|
||||
}
|
||||
|
||||
for(auto node : document["input"]) {
|
||||
if(node.name() == "history") historySize = node.natural();
|
||||
if(node.name() == "format") format = glrFormat(node.text());
|
||||
if(node.name() == "filter") filter = glrFilter(node.text());
|
||||
if(node.name() == "wrap") wrap = glrWrap(node.text());
|
||||
}
|
||||
|
||||
for(auto node : document["output"]) {
|
||||
string text = node.text();
|
||||
if(node.name() == "width") {
|
||||
if(text.endsWith("%")) relativeWidth = toReal(text.trimRight("%", 1L)) / 100.0;
|
||||
else absoluteWidth = text.natural();
|
||||
}
|
||||
if(node.name() == "height") {
|
||||
if(text.endsWith("%")) relativeHeight = toReal(text.trimRight("%", 1L)) / 100.0;
|
||||
else absoluteHeight = text.natural();
|
||||
}
|
||||
}
|
||||
|
||||
for(auto node : document.find("program")) {
|
||||
u32 n = programs.size();
|
||||
programs(n).bind(this, node, pathname);
|
||||
}
|
||||
if(_chain != NULL) {
|
||||
_libra.gl_filter_chain_free(&_chain);
|
||||
}
|
||||
|
||||
//changing shaders may change input format, which requires the input texture to be recreated
|
||||
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, getFormat(), getType(), buffer);
|
||||
allocateHistory(historySize);
|
||||
}
|
||||
if(_preset != NULL) {
|
||||
_libra.preset_free(&_preset);
|
||||
}
|
||||
|
||||
auto OpenGL::allocateHistory(u32 size) -> void {
|
||||
for(auto& frame : history) glDeleteTextures(1, &frame.texture);
|
||||
history.reset();
|
||||
while(size--) {
|
||||
OpenGLTexture frame;
|
||||
frame.filter = filter;
|
||||
frame.wrap = wrap;
|
||||
glGenTextures(1, &frame.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
|
||||
history.append(frame);
|
||||
if(pathname == "Blur") {
|
||||
filter = GL_LINEAR;
|
||||
} else if(file::exists(pathname)) {
|
||||
if(_libra.preset_create(pathname.data(), &_preset) != NULL) {
|
||||
print(string{"OpenGL: Failed to load shader: ", pathname, "\n"});
|
||||
setShader("");
|
||||
return;
|
||||
}
|
||||
|
||||
if(auto error = _libra.gl_filter_chain_create(&_preset, NULL, &_chain)) {
|
||||
print(string{"OpenGL: Failed to create filter chain for: ", pathname, "\n"});
|
||||
_libra.error_print(error);
|
||||
setShader("");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto OpenGL::clear() -> void {
|
||||
for(auto& p : programs) {
|
||||
glUseProgram(p.program);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
glUseProgram(0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
@@ -93,6 +52,7 @@ auto OpenGL::output() -> void {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
struct Source {
|
||||
GLuint texture;
|
||||
@@ -102,86 +62,57 @@ auto OpenGL::output() -> void {
|
||||
vector<Source> sources;
|
||||
sources.prepend({texture, width, height, filter, wrap});
|
||||
|
||||
for(auto& p : programs) {
|
||||
u32 targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
|
||||
u32 targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
|
||||
if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth;
|
||||
if(p.relativeHeight) targetHeight = sources[0].height * p.relativeHeight;
|
||||
|
||||
p.size(targetWidth, targetHeight);
|
||||
glUseProgram(p.program);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, p.framebuffer);
|
||||
|
||||
glrUniform1i("phase", p.phase);
|
||||
glrUniform1i("historyLength", history.size());
|
||||
glrUniform1i("sourceLength", sources.size());
|
||||
glrUniform1i("pixmapLength", p.pixmaps.size());
|
||||
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
||||
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
||||
|
||||
u32 aid = 0;
|
||||
for(auto& frame : history) {
|
||||
glrUniform1i({"history[", aid, "]"}, aid);
|
||||
glrUniform4f({"historySize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height);
|
||||
glActiveTexture(GL_TEXTURE0 + (aid++));
|
||||
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
||||
glrParameters(frame.filter, frame.wrap);
|
||||
}
|
||||
|
||||
u32 bid = 0;
|
||||
for(auto& source : sources) {
|
||||
glrUniform1i({"source[", bid, "]"}, aid + bid);
|
||||
glrUniform4f({"sourceSize[", bid, "]"}, source.width, source.height, 1.0 / source.width, 1.0 / source.height);
|
||||
glActiveTexture(GL_TEXTURE0 + aid + (bid++));
|
||||
glBindTexture(GL_TEXTURE_2D, source.texture);
|
||||
glrParameters(source.filter, source.wrap);
|
||||
}
|
||||
|
||||
u32 cid = 0;
|
||||
for(auto& pixmap : p.pixmaps) {
|
||||
glrUniform1i({"pixmap[", cid, "]"}, aid + bid + cid);
|
||||
glrUniform4f({"pixmapSize[", bid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height);
|
||||
glActiveTexture(GL_TEXTURE0 + aid + bid + (cid++));
|
||||
glBindTexture(GL_TEXTURE_2D, pixmap.texture);
|
||||
glrParameters(pixmap.filter, pixmap.wrap);
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glrParameters(sources[0].filter, sources[0].wrap);
|
||||
p.render(sources[0].width, sources[0].height, 0, 0, targetWidth, targetHeight);
|
||||
glBindTexture(GL_TEXTURE_2D, p.texture);
|
||||
|
||||
p.phase = (p.phase + 1) % p.modulo;
|
||||
sources.prepend({p.texture, p.width, p.height, p.filter, p.wrap});
|
||||
}
|
||||
|
||||
u32 targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
|
||||
u32 targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
|
||||
if(relativeWidth) targetWidth = sources[0].width * relativeWidth;
|
||||
if(relativeHeight) targetHeight = sources[0].height * relativeHeight;
|
||||
|
||||
glUseProgram(program);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
u32 x = (outputWidth - targetWidth) / 2;
|
||||
u32 y = (outputHeight - targetHeight) / 2;
|
||||
|
||||
glrUniform1i("source[0]", 0);
|
||||
glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
|
||||
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
|
||||
if(_chain != NULL) {
|
||||
// Shader path: our intermediate framebuffer matches the output size
|
||||
if(!framebuffer || framebufferWidth != outputWidth || framebufferHeight != outputHeight) {
|
||||
if(framebuffer) {
|
||||
glDeleteFramebuffers(1, &framebuffer);
|
||||
framebuffer = 0;
|
||||
}
|
||||
if(framebufferTexture) {
|
||||
glDeleteTextures(1, &framebufferTexture);
|
||||
framebufferTexture = 0;
|
||||
}
|
||||
|
||||
glrParameters(sources[0].filter, sources[0].wrap);
|
||||
render(sources[0].width, sources[0].height, outputX, outputY, outputWidth, outputHeight);
|
||||
framebufferWidth = outputWidth, framebufferHeight = outputHeight;
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
glGenTextures(1, &framebufferTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, framebufferTexture);
|
||||
framebufferFormat = GL_RGB;
|
||||
|
||||
if(history.size() > 0) {
|
||||
OpenGLTexture frame = history.takeRight();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, framebufferFormat, framebufferWidth, framebufferHeight, 0, framebufferFormat,
|
||||
GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, frame.texture);
|
||||
if(width == frame.width && height == frame.height) {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, getFormat(), getType(), buffer);
|
||||
} else {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, frame.width = width, frame.height = height, 0, getFormat(), getType(), buffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferTexture, 0);
|
||||
}
|
||||
} else {
|
||||
// Non-shader path: our intermediate framebuffer matches the source size and re-uses the source texture
|
||||
if(!framebuffer || framebufferWidth != width || framebufferHeight != height) {
|
||||
if(framebuffer) {
|
||||
glDeleteFramebuffers(1, &framebuffer);
|
||||
framebuffer = 0;
|
||||
}
|
||||
|
||||
history.prepend(frame);
|
||||
if(framebufferTexture) {
|
||||
glDeleteTextures(1, &framebufferTexture);
|
||||
framebufferTexture = 0;
|
||||
}
|
||||
|
||||
framebufferWidth = width, framebufferHeight = height;
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||
}
|
||||
}
|
||||
|
||||
render(sources[0].width, sources[0].height, outputX + x, outputY + y, targetWidth, targetHeight);
|
||||
}
|
||||
|
||||
auto OpenGL::initialize(const string& shader) -> bool {
|
||||
@@ -193,20 +124,48 @@ auto OpenGL::initialize(const string& shader) -> bool {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glEnable(GL_DITHER);
|
||||
|
||||
program = glCreateProgram();
|
||||
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLOutputVertexShader);
|
||||
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
|
||||
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
|
||||
OpenGLSurface::allocate();
|
||||
glrLinkProgram(program);
|
||||
_libra = librashader_load_instance();
|
||||
if(!_libra.instance_loaded) {
|
||||
print("OpenGL: Failed to load librashader: shaders will be disabled\n");
|
||||
}
|
||||
|
||||
if(_libra.gl_init_context(resolveSymbol) != NULL) {
|
||||
print("OpenGL: Failed to initialize librashader context: shaders will be disabled\n");
|
||||
};
|
||||
|
||||
setShader(shader);
|
||||
return initialized = true;
|
||||
}
|
||||
|
||||
auto OpenGL::resolveSymbol(const char* name) -> const void * {
|
||||
#if defined(PLATFORM_MACOS)
|
||||
NSSymbol symbol;
|
||||
char *symbolName;
|
||||
symbolName = (char*)malloc(strlen(name) + 2);
|
||||
strcpy(symbolName + 1, name);
|
||||
symbolName[0] = '_';
|
||||
symbol = NULL;
|
||||
if(NSIsSymbolNameDefined (symbolName)) symbol = NSLookupAndBindSymbol (symbolName);
|
||||
free(symbolName); // 5
|
||||
return (void*)(symbol ? NSAddressOfSymbol(symbol) : NULL);
|
||||
#else
|
||||
void* symbol = (void*)glGetProcAddress(name);
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
if(!symbol) {
|
||||
// (w)glGetProcAddress will not return function pointers from any OpenGL functions
|
||||
// that are directly exported by the opengl32.dll
|
||||
HMODULE module = LoadLibraryA("opengl32.dll");
|
||||
symbol = (void*)GetProcAddress(module, name);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
auto OpenGL::terminate() -> void {
|
||||
if(!initialized) return;
|
||||
setShader(""); //release shader resources (eg frame[] history)
|
||||
setShader("");
|
||||
OpenGLSurface::release();
|
||||
if(buffer) { delete[] buffer; buffer = nullptr; }
|
||||
initialized = false;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#endif
|
||||
|
||||
#include "bind.hpp"
|
||||
#include "shaders.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "librashader_ld.h"
|
||||
|
||||
struct OpenGL;
|
||||
|
||||
@@ -35,47 +35,35 @@ struct OpenGLTexture {
|
||||
};
|
||||
|
||||
struct OpenGLSurface : OpenGLTexture {
|
||||
auto allocate() -> void;
|
||||
auto size(u32 width, u32 height) -> void;
|
||||
auto release() -> void;
|
||||
auto render(u32 sourceWidth, u32 sourceHeight, u32 targetX, u32 targetY, u32 targetWidth, u32 targetHeight) -> void;
|
||||
|
||||
GLuint program = 0;
|
||||
GLuint framebuffer = 0;
|
||||
GLuint vao = 0;
|
||||
GLuint vbo[3] = {0, 0, 0};
|
||||
GLuint vertex = 0;
|
||||
GLuint geometry = 0;
|
||||
GLuint fragment = 0;
|
||||
GLuint framebufferTexture = 0;
|
||||
GLuint framebufferFormat = 0;
|
||||
GLuint framebufferWidth = 0;
|
||||
GLuint framebufferHeight = 0;
|
||||
u32* buffer = nullptr;
|
||||
|
||||
libra_instance_t _libra;
|
||||
libra_shader_preset_t _preset = NULL;
|
||||
libra_gl_filter_chain_t _chain = NULL;
|
||||
u32 frameCount = 0;
|
||||
};
|
||||
|
||||
struct OpenGLProgram : OpenGLSurface {
|
||||
auto bind(OpenGL* instance, const Markup::Node& node, const string& pathname) -> void;
|
||||
auto parse(OpenGL* instance, string& source) -> void;
|
||||
auto release() -> void;
|
||||
|
||||
u32 phase = 0; //frame counter
|
||||
u32 modulo = 0; //frame counter modulus
|
||||
u32 absoluteWidth = 0;
|
||||
u32 absoluteHeight = 0;
|
||||
f64 relativeWidth = 0;
|
||||
f64 relativeHeight = 0;
|
||||
vector<OpenGLTexture> pixmaps;
|
||||
};
|
||||
|
||||
struct OpenGL : OpenGLProgram {
|
||||
struct OpenGL : OpenGLSurface {
|
||||
auto setShader(const string& pathname) -> void;
|
||||
auto allocateHistory(u32 size) -> void;
|
||||
auto clear() -> void;
|
||||
auto lock(u32*& data, u32& pitch) -> bool;
|
||||
auto output() -> void;
|
||||
auto initialize(const string& shader) -> bool;
|
||||
auto terminate() -> void;
|
||||
static auto resolveSymbol(const char* name) -> const void*;
|
||||
|
||||
vector<OpenGLProgram> programs;
|
||||
vector<OpenGLTexture> history;
|
||||
GLuint inputFormat = GL_RGBA8;
|
||||
u32 absoluteWidth = 0;
|
||||
u32 absoluteHeight = 0;
|
||||
u32 outputX = 0;
|
||||
u32 outputY = 0;
|
||||
u32 outputWidth = 0;
|
||||
@@ -95,5 +83,4 @@ struct OpenGL : OpenGLProgram {
|
||||
|
||||
#include "texture.hpp"
|
||||
#include "surface.hpp"
|
||||
#include "program.hpp"
|
||||
#include "main.hpp"
|
||||
@@ -1,108 +0,0 @@
|
||||
auto OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const string& pathname) -> void {
|
||||
filter = glrFilter(node["filter"].text());
|
||||
wrap = glrWrap(node["wrap"].text());
|
||||
modulo = glrModulo(node["modulo"].integer());
|
||||
|
||||
string w = node["width"].text(), h = node["height"].text();
|
||||
if(w.endsWith("%")) relativeWidth = toReal(w.trimRight("%", 1L)) / 100.0;
|
||||
else absoluteWidth = w.natural();
|
||||
if(h.endsWith("%")) relativeHeight = toReal(h.trimRight("%", 1L)) / 100.0;
|
||||
else absoluteHeight = h.natural();
|
||||
|
||||
format = glrFormat(node["format"].text());
|
||||
|
||||
program = glCreateProgram();
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
|
||||
if(file::exists({pathname, node["vertex"].text()})) {
|
||||
string source = file::read({pathname, node["vertex"].text()});
|
||||
parse(instance, source);
|
||||
vertex = glrCreateShader(program, GL_VERTEX_SHADER, source);
|
||||
} else {
|
||||
vertex = glrCreateShader(program, GL_VERTEX_SHADER, OpenGLVertexShader);
|
||||
}
|
||||
|
||||
if(file::exists({pathname, node["geometry"].text()})) {
|
||||
string source = file::read({pathname, node["geometry"].text()});
|
||||
parse(instance, source);
|
||||
geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, source);
|
||||
} else {
|
||||
//geometry shaders, when attached, must pass all vertex output through to the fragment shaders
|
||||
//geometry = glrCreateShader(program, GL_GEOMETRY_SHADER, OpenGLGeometryShader);
|
||||
}
|
||||
|
||||
if(file::exists({pathname, node["fragment"].text()})) {
|
||||
string source = file::read({pathname, node["fragment"].text()});
|
||||
parse(instance, source);
|
||||
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, source);
|
||||
} else {
|
||||
fragment = glrCreateShader(program, GL_FRAGMENT_SHADER, OpenGLFragmentShader);
|
||||
}
|
||||
|
||||
for(auto& leaf : node.find("pixmap")) {
|
||||
nall::image image({pathname, leaf.text()});
|
||||
if(!image) continue;
|
||||
image.transform();
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
|
||||
u32 n = pixmaps.size();
|
||||
pixmaps(n).texture = texture;
|
||||
pixmaps(n).width = image.width();
|
||||
pixmaps(n).height = image.height();
|
||||
pixmaps(n).format = format;
|
||||
pixmaps(n).filter = filter;
|
||||
pixmaps(n).wrap = wrap;
|
||||
if(leaf["format"]) pixmaps(n).format = glrFormat(leaf["format"].text());
|
||||
if(leaf["filter"]) pixmaps(n).filter = glrFilter(leaf["filter"].text());
|
||||
if(leaf["wrap"]) pixmaps(n).wrap = glrWrap(leaf["wrap"].text());
|
||||
|
||||
u32 w = glrSize(image.width()), h = glrSize(image.height());
|
||||
u32* buffer = new u32[w * h]();
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, pixmaps(n).getFormat(), pixmaps(n).getType(), buffer);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.width(), image.height(), getFormat(), getType(), image.data());
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
OpenGLSurface::allocate();
|
||||
glrLinkProgram(program);
|
||||
}
|
||||
|
||||
//apply manifest settings to shader source #in tags
|
||||
auto OpenGLProgram::parse(OpenGL* instance, string& source) -> void {
|
||||
auto lines = source.split("\n");
|
||||
for(auto& line : lines) {
|
||||
string s = line;
|
||||
if(auto position = s.find("//")) s.resize(position()); //strip comments
|
||||
s.strip(); //remove extraneous whitespace
|
||||
if(s.match("#in ?*")) {
|
||||
s.trimLeft("#in ", 1L).strip();
|
||||
if(auto setting = instance->settings.find({s})) {
|
||||
line = {"#define ", setting().name, " ", setting().value};
|
||||
} else {
|
||||
line.reset(); //undefined variable (test in source with #ifdef)
|
||||
}
|
||||
}
|
||||
}
|
||||
source = lines.merge("\n");
|
||||
}
|
||||
|
||||
auto OpenGLProgram::release() -> void {
|
||||
OpenGLSurface::release();
|
||||
for(auto& pixmap : pixmaps) glDeleteTextures(1, &pixmap.texture);
|
||||
pixmaps.reset();
|
||||
|
||||
width = 0;
|
||||
height = 0;
|
||||
format = GL_RGBA8;
|
||||
filter = GL_LINEAR;
|
||||
wrap = GL_CLAMP_TO_BORDER;
|
||||
phase = 0;
|
||||
modulo = 0;
|
||||
absoluteWidth = 0;
|
||||
absoluteHeight = 0;
|
||||
relativeWidth = 0;
|
||||
relativeHeight = 0;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
static string OpenGLOutputVertexShader = R"(
|
||||
#version 150
|
||||
|
||||
uniform vec4 targetSize;
|
||||
uniform vec4 outputSize;
|
||||
|
||||
in vec2 texCoord;
|
||||
|
||||
out Vertex {
|
||||
vec2 texCoord;
|
||||
} vertexOut;
|
||||
|
||||
void main() {
|
||||
//center image within output window
|
||||
if(gl_VertexID == 0 || gl_VertexID == 2) {
|
||||
gl_Position.x = -(targetSize.x / outputSize.x);
|
||||
} else {
|
||||
gl_Position.x = +(targetSize.x / outputSize.x);
|
||||
}
|
||||
|
||||
//center and flip vertically (buffer[0, 0] = top-left; OpenGL[0, 0] = bottom-left)
|
||||
if(gl_VertexID == 0 || gl_VertexID == 1) {
|
||||
gl_Position.y = +(targetSize.y / outputSize.y);
|
||||
} else {
|
||||
gl_Position.y = -(targetSize.y / outputSize.y);
|
||||
}
|
||||
|
||||
//align image to even pixel boundary to prevent aliasing
|
||||
vec2 align = fract((outputSize.xy + targetSize.xy) / 2.0) * 2.0;
|
||||
gl_Position.xy -= align / outputSize.xy;
|
||||
gl_Position.zw = vec2(0.0, 1.0);
|
||||
|
||||
vertexOut.texCoord = texCoord;
|
||||
}
|
||||
)";
|
||||
|
||||
static string OpenGLVertexShader = R"(
|
||||
#version 150
|
||||
|
||||
in vec4 position;
|
||||
in vec2 texCoord;
|
||||
|
||||
out Vertex {
|
||||
vec2 texCoord;
|
||||
} vertexOut;
|
||||
|
||||
void main() {
|
||||
gl_Position = position;
|
||||
vertexOut.texCoord = texCoord;
|
||||
}
|
||||
)";
|
||||
|
||||
static string OpenGLGeometryShader = R"(
|
||||
#version 150
|
||||
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 3) out;
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
} vertexIn[];
|
||||
|
||||
out Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
void main() {
|
||||
for(int i = 0; i < gl_in.length(); i++) {
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
texCoord = vertexIn[i].texCoord;
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
)";
|
||||
|
||||
static string OpenGLFragmentShader = R"(
|
||||
#version 150
|
||||
|
||||
uniform sampler2D source[];
|
||||
|
||||
in Vertex {
|
||||
vec2 texCoord;
|
||||
};
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = texture(source[0], texCoord);
|
||||
}
|
||||
)";
|
||||
@@ -1,9 +1,3 @@
|
||||
auto OpenGLSurface::allocate() -> void {
|
||||
glGenVertexArrays(1, &vao);
|
||||
glBindVertexArray(vao);
|
||||
glGenBuffers(3, &vbo[0]);
|
||||
}
|
||||
|
||||
auto OpenGLSurface::size(u32 w, u32 h) -> void {
|
||||
if(width == w && height == h) return;
|
||||
width = w, height = h;
|
||||
@@ -16,100 +10,36 @@ auto OpenGLSurface::size(u32 w, u32 h) -> void {
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, getFormat(), getType(), buffer);
|
||||
|
||||
if(framebuffer) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto OpenGLSurface::release() -> void {
|
||||
if(vbo[0]) { glDeleteBuffers(3, &vbo[0]); for(auto &o : vbo) o = 0; }
|
||||
if(vao) { glDeleteVertexArrays(1, &vao); vao = 0; }
|
||||
if(vertex) { glDetachShader(program, vertex); glDeleteShader(vertex); vertex = 0; }
|
||||
if(geometry) { glDetachShader(program, geometry); glDeleteShader(geometry); geometry = 0; }
|
||||
if(fragment) { glDetachShader(program, fragment); glDeleteShader(fragment); fragment = 0; }
|
||||
if(texture) { glDeleteTextures(1, &texture); texture = 0; }
|
||||
if(framebuffer) { glDeleteFramebuffers(1, &framebuffer); framebuffer = 0; }
|
||||
if(program) { glDeleteProgram(program); program = 0; }
|
||||
width = 0, height = 0;
|
||||
}
|
||||
|
||||
auto OpenGLSurface::render(u32 sourceWidth, u32 sourceHeight, u32 targetX, u32 targetY, u32 targetWidth, u32 targetHeight) -> void {
|
||||
glViewport(targetX, targetY, targetWidth, targetHeight);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
f32 w = (f32)sourceWidth / (f32)glrSize(sourceWidth);
|
||||
f32 h = (f32)sourceHeight / (f32)glrSize(sourceHeight);
|
||||
if(_chain != NULL) {
|
||||
libra_source_image_gl_t input = {texture, format, sourceWidth, sourceHeight};
|
||||
libra_viewport_t viewport{(float)targetX, (float)targetY, targetWidth, targetHeight};
|
||||
libra_output_framebuffer_gl_t output = {framebuffer, framebufferTexture, framebufferFormat};
|
||||
|
||||
f32 u = (f32)targetWidth;
|
||||
f32 v = (f32)targetHeight;
|
||||
if (auto error = _libra.gl_filter_chain_frame(&_chain, frameCount++, input, viewport, output, NULL, NULL)) {
|
||||
_libra.error_print(error);
|
||||
}
|
||||
|
||||
GLfloat modelView[] = {
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
|
||||
GLfloat projection[] = {
|
||||
2.0f/u, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f/v, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, -1.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
GLfloat modelViewProjection[4 * 4];
|
||||
MatrixMultiply(modelViewProjection, modelView, 4, 4, projection, 4, 4);
|
||||
|
||||
GLfloat vertices[] = {
|
||||
0, 0, 0, 1,
|
||||
u, 0, 0, 1,
|
||||
0, v, 0, 1,
|
||||
u, v, 0, 1,
|
||||
};
|
||||
|
||||
GLfloat positions[4 * 4];
|
||||
for(u32 n = 0; n < 16; n += 4) {
|
||||
MatrixMultiply(&positions[n], &vertices[n], 1, 4, modelViewProjection, 4, 4);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBlitFramebuffer(0, framebufferHeight, framebufferWidth, 0, 0, 0, framebufferWidth, framebufferHeight, GL_COLOR_BUFFER_BIT, filter);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
} else {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBlitFramebuffer(0, framebufferHeight, framebufferWidth, 0, targetX, targetY, targetWidth + targetX, targetHeight + targetY, GL_COLOR_BUFFER_BIT, filter);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
GLfloat texCoords[] = {
|
||||
0, 0,
|
||||
w, 0,
|
||||
0, h,
|
||||
w, h,
|
||||
};
|
||||
|
||||
glrUniformMatrix4fv("modelView", modelView);
|
||||
glrUniformMatrix4fv("projection", projection);
|
||||
glrUniformMatrix4fv("modelViewProjection", modelViewProjection);
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), vertices, GL_STATIC_DRAW);
|
||||
GLint locationVertex = glGetAttribLocation(program, "vertex");
|
||||
glEnableVertexAttribArray(locationVertex);
|
||||
glVertexAttribPointer(locationVertex, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), positions, GL_STATIC_DRAW);
|
||||
GLint locationPosition = glGetAttribLocation(program, "position");
|
||||
glEnableVertexAttribArray(locationPosition);
|
||||
glVertexAttribPointer(locationPosition, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
|
||||
glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), texCoords, GL_STATIC_DRAW);
|
||||
GLint locationTexCoord = glGetAttribLocation(program, "texCoord");
|
||||
glEnableVertexAttribArray(locationTexCoord);
|
||||
glVertexAttribPointer(locationTexCoord, 2, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
|
||||
glBindFragDataLocation(program, 0, "fragColor");
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableVertexAttribArray(locationVertex);
|
||||
glDisableVertexAttribArray(locationPosition);
|
||||
glDisableVertexAttribArray(locationTexCoord);
|
||||
}
|
||||
@@ -14,10 +14,6 @@
|
||||
#include <ruby/video/glx.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_GLX2)
|
||||
#include <ruby/video/glx2.cpp>
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_WGL)
|
||||
#include <ruby/video/wgl.cpp>
|
||||
#endif
|
||||
@@ -172,10 +168,6 @@ auto Video::create(string driver) -> bool {
|
||||
if(driver == "OpenGL 3.2") self.instance = new VideoGLX(*this);
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_GLX2)
|
||||
if(driver == "OpenGL 2.0") self.instance = new VideoGLX2(*this);
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_WGL)
|
||||
if(driver == "OpenGL 3.2") self.instance = new VideoWGL(*this);
|
||||
#endif
|
||||
@@ -216,10 +208,6 @@ auto Video::hasDrivers() -> vector<string> {
|
||||
"OpenGL 3.2",
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_GLX2)
|
||||
"OpenGL 2.0",
|
||||
#endif
|
||||
|
||||
#if defined(VIDEO_XVIDEO)
|
||||
"XVideo",
|
||||
#endif
|
||||
@@ -242,8 +230,6 @@ auto Video::optimalDriver() -> string {
|
||||
return "OpenGL 3.2";
|
||||
#elif defined(VIDEO_GLX)
|
||||
return "OpenGL 3.2";
|
||||
#elif defined(VIDEO_GLX2)
|
||||
return "OpenGL 2.0";
|
||||
#elif defined(VIDEO_XVIDEO)
|
||||
return "XVideo";
|
||||
#elif defined(VIDEO_XSHM)
|
||||
@@ -266,8 +252,6 @@ auto Video::safestDriver() -> string {
|
||||
return "XShm";
|
||||
#elif defined(VIDEO_XVIDEO)
|
||||
return "XVideo";
|
||||
#elif defined(VIDEO_GLX2)
|
||||
return "OpenGL 2.0";
|
||||
#elif defined(VIDEO_GLX)
|
||||
return "OpenGL 3.2";
|
||||
#else
|
||||
|
||||
@@ -13,3 +13,5 @@ cd "$(dirname "$0")"/.. || exit 1
|
||||
git subtree pull --prefix=ares/n64/vulkan/parallel-rdp https://github.com/Themaister/parallel-rdp-standalone.git master --squash
|
||||
git subtree pull --prefix=thirdparty/sljit https://github.com/zherczeg/sljit.git master --squash
|
||||
git subtree pull --prefix=thirdparty/libchdr https://github.com/rtissera/libchdr master --squash
|
||||
git subtree pull --prefix=thirdparty/librashader https://github.com/SnowflakePowered/librashader master --squash
|
||||
git subtree pull --prefix=thirdparty/slang-shaders https://github.com/libretro/slang-shaders master --squash
|
||||
@@ -69,11 +69,7 @@ else ifneq ($(filter $(platform),linux bsd),)
|
||||
mkdir -p $(prefix)/bin/
|
||||
mkdir -p $(prefix)/share/applications/
|
||||
mkdir -p $(prefix)/share/icons/hicolor/256x256/apps/
|
||||
mkdir -p $(prefix)/share/$(name)/Shaders/
|
||||
mkdir -p $(prefix)/share/$(name)/Database/
|
||||
cp $(output.path)/$(name) $(prefix)/bin/$(name)
|
||||
cp -R $(ares.path)/Shaders/* $(prefix)/share/$(name)/Shaders/
|
||||
cp -R $(mia.path)/Database/* $(prefix)/share/$(name)/Database/
|
||||
cp resource/$(name).desktop $(prefix)/share/applications/$(name).desktop
|
||||
cp resource/$(name).png $(prefix)/share/icons/hicolor/256x256/apps/$(name).png
|
||||
endif
|
||||
|
||||
Vendored
+12
-12
@@ -35,17 +35,17 @@ $(object.path)/lzma_dec.o: $(libchdr.path)/deps/lzma-19.00/src/LzmaDec.c
|
||||
$(object.path)/lcma_enc.o: $(libchdr.path)/deps/lzma-19.00/src/LzmaEnc.c
|
||||
$(object.path)/lzma_sort.o: $(libchdr.path)/deps/lzma-19.00/src/Sort.c
|
||||
|
||||
$(object.path)/zlib_adler32.o: $(libchdr.path)/deps/zlib-1.2.13/adler32.c
|
||||
$(object.path)/zlib_compress.o: $(libchdr.path)/deps/zlib-1.2.13/compress.c
|
||||
$(object.path)/zlib_crc32.o: $(libchdr.path)/deps/zlib-1.2.13/crc32.c
|
||||
$(object.path)/zlib_deflate.o: $(libchdr.path)/deps/zlib-1.2.13/deflate.c
|
||||
$(object.path)/zlib_infback.o: $(libchdr.path)/deps/zlib-1.2.13/infback.c
|
||||
$(object.path)/zlib_inffast.o: $(libchdr.path)/deps/zlib-1.2.13/inffast.c
|
||||
$(object.path)/zlib_inflate.o: $(libchdr.path)/deps/zlib-1.2.13/inflate.c
|
||||
$(object.path)/zlib_inftrees.o: $(libchdr.path)/deps/zlib-1.2.13/inftrees.c
|
||||
$(object.path)/zlib_trees.o: $(libchdr.path)/deps/zlib-1.2.13/trees.c
|
||||
$(object.path)/zlib_uncompr.o: $(libchdr.path)/deps/zlib-1.2.13/uncompr.c
|
||||
$(object.path)/zlib_zutil.o: $(libchdr.path)/deps/zlib-1.2.13/zutil.c
|
||||
$(object.path)/zlib_adler32.o: $(libchdr.path)/deps/zlib-1.3.1/adler32.c
|
||||
$(object.path)/zlib_compress.o: $(libchdr.path)/deps/zlib-1.3.1/compress.c
|
||||
$(object.path)/zlib_crc32.o: $(libchdr.path)/deps/zlib-1.3.1/crc32.c
|
||||
$(object.path)/zlib_deflate.o: $(libchdr.path)/deps/zlib-1.3.1/deflate.c
|
||||
$(object.path)/zlib_infback.o: $(libchdr.path)/deps/zlib-1.3.1/infback.c
|
||||
$(object.path)/zlib_inffast.o: $(libchdr.path)/deps/zlib-1.3.1/inffast.c
|
||||
$(object.path)/zlib_inflate.o: $(libchdr.path)/deps/zlib-1.3.1/inflate.c
|
||||
$(object.path)/zlib_inftrees.o: $(libchdr.path)/deps/zlib-1.3.1/inftrees.c
|
||||
$(object.path)/zlib_trees.o: $(libchdr.path)/deps/zlib-1.3.1/trees.c
|
||||
$(object.path)/zlib_uncompr.o: $(libchdr.path)/deps/zlib-1.3.1/uncompr.c
|
||||
$(object.path)/zlib_zutil.o: $(libchdr.path)/deps/zlib-1.3.1/zutil.c
|
||||
|
||||
$(object.path)/zstd_common_debug.o: $(libchdr.path)/deps/zstd-1.5.5/lib/common/debug.c
|
||||
$(object.path)/zstd_common_entropy_common.o: $(libchdr.path)/deps/zstd-1.5.5/lib/common/entropy_common.c
|
||||
@@ -63,7 +63,7 @@ $(object.path)/zstd_decompress_zstd_decompress_block.o: $(libchdr.path)/deps/zst
|
||||
|
||||
flags += -I$(libchdr.path)/include
|
||||
flags += -I$(libchdr.path)/deps/lzma-19.00/include -D_7ZIP_ST
|
||||
flags += -I$(libchdr.path)/deps/zlib-1.2.13
|
||||
flags += -I$(libchdr.path)/deps/zlib-1.3.1
|
||||
flags += -I$(libchdr.path)/deps/zstd-1.5.5/lib
|
||||
flags += -DZSTD_DISABLE_ASM
|
||||
|
||||
|
||||
Vendored
+10
-3
@@ -9,11 +9,18 @@ fi
|
||||
git -C SDL reset --hard "$(cat HEAD)"
|
||||
mkdir -p SDL/build
|
||||
pushd SDL/build
|
||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
if [ -n "${GITHUB_ACTIONS+1}" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
||||
echo "Set Xcode version in order to target macOS 10.11 when building SDL."
|
||||
fi
|
||||
cmake .. "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11
|
||||
cmake --build .
|
||||
sudo cmake --install .
|
||||
sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
|
||||
|
||||
if [ -n "${GITHUB_ACTIONS+1}" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
|
||||
echo "Set Xcode to 14.2 to continue build."
|
||||
fi
|
||||
popd
|
||||
cp SDL/build/libSDL2-2.0.0.dylib .
|
||||
Loaded 100 of 3937 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user