Compare commits
144 commits
0974254c0e
...
30bb747e94
Author | SHA1 | Date | |
---|---|---|---|
Alex Janka | 30bb747e94 | ||
d7665cac9b | |||
e8ffd8fdf3 | |||
d893b6ec97 | |||
8c8e386a6c | |||
b7071958bd | |||
5feac91af2 | |||
9c895caa51 | |||
7593f9f9b5 | |||
af3ea252ba | |||
9dc0cf26fd | |||
2550bf7ed5 | |||
35fc21bbef | |||
89b620a7c1 | |||
66a0ee21e3 | |||
31b7a6f33f | |||
ed7216990a | |||
623c6776f7 | |||
bacfbf0791 | |||
e02e1ae26a | |||
d72519b9fd | |||
752417f320 | |||
10ad2d927c | |||
8a9adebb96 | |||
7719b939f9 | |||
a849f5e745 | |||
f61bed3a22 | |||
4ef4b8762b | |||
a5c684a7ee | |||
8ba4b72cf1 | |||
7a3a690166 | |||
b5bc3c11e1 | |||
c7dd7796db | |||
9741ab2cd1 | |||
b378c45039 | |||
c7d1d347a4 | |||
95ac8adc20 | |||
372315022d | |||
699243c0ab | |||
6d25a653a9 | |||
be11953516 | |||
e38f2636d9 | |||
f073c76ade | |||
7ef3780222 | |||
d60ff76fb2 | |||
50aa582fa8 | |||
e8eee02bfb | |||
22aa59b598 | |||
b75a614873 | |||
a8ae407ddb | |||
fe84e6a490 | |||
913ede3852 | |||
1f5b4380a3 | |||
ce3a8c6e52 | |||
c22328f025 | |||
af49128ee7 | |||
d3d8e85461 | |||
017a1a6232 | |||
e622479c76 | |||
b47b27fadb | |||
ba6c32e858 | |||
edca0f1749 | |||
efdfd56e0e | |||
c0ecae844c | |||
cbac011969 | |||
350508a7aa | |||
d6f1af8691 | |||
adeb9435fc | |||
499b8f5791 | |||
e944330692 | |||
f7dd955c0a | |||
227e6e743d | |||
6fbc4b3075 | |||
05467c2c78 | |||
b7673de811 | |||
4247e64336 | |||
b348e8591f | |||
dc1ab35d89 | |||
7f4a883288 | |||
a2987555a2 | |||
3afcd6223c | |||
fb62a1e3f4 | |||
dca93a1310 | |||
4259b65ee0 | |||
f058134944 | |||
ad4e72f359 | |||
5c08205360 | |||
cc93e37701 | |||
76aa5ce4c6 | |||
d89839be16 | |||
a1696813aa | |||
004b073b1a | |||
bceb0623a3 | |||
ab31abb3d7 | |||
3b0531dc62 | |||
363657deef | |||
05f634a9b9 | |||
43da6e60c6 | |||
30dfa1a655 | |||
325e39063a | |||
5554703af7 | |||
a7b1682a37 | |||
ba3154b92d | |||
6780397d49 | |||
1aedb1bea7 | |||
8dc0e0d100 | |||
f40df9a54a | |||
12d55e928e | |||
d5ef5904f3 | |||
aca5b5420c | |||
c121087348 | |||
2d98ebec1b | |||
849a749f1a | |||
daf30c83c0 | |||
3c3f024ef8 | |||
4762055dc1 | |||
d0a5224c10 | |||
178790a202 | |||
528dd1b53c | |||
a495b693a6 | |||
c67e9f4801 | |||
e1f62fc984 | |||
b98d86a940 | |||
252f685967 | |||
a7ca391ef6 | |||
11d12730eb | |||
4733831500 | |||
abbec84594 | |||
12af3c3f3a | |||
b9a6b869e3 | |||
fa8ee5d143 | |||
8f89b3e720 | |||
c34fa4195b | |||
2fbc7f92da | |||
9732812b91 | |||
4da6c98655 | |||
a14b36e05b | |||
b2d8d084be | |||
f1524f6049 | |||
6d4e6590de | |||
e776ee2823 | |||
3d74f27d77 | |||
f9fdb93c0c | |||
7f0f985a14 |
44
.github/workflows/build-linux-arm64.yml
vendored
44
.github/workflows/build-linux-arm64.yml
vendored
|
@ -1,44 +0,0 @@
|
|||
name: build librashader-capi for ARM64 Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "0 0 * * 6"
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
profile: ['debug', 'release', 'optimized']
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Install nightly Rust
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
target: aarch64-unknown-linux-gnu
|
||||
- name: Install ARM64 cross-compilation dependencies
|
||||
continue-on-error: true
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo dpkg --add-architecture arm64
|
||||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
sudo apt-get update || true
|
||||
sudo apt-get -y install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
|
||||
- name: Build dynamic library
|
||||
run: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc cargo run -p librashader-build-script -- --profile ${{ matrix.profile }} --target aarch64-unknown-linux-gnu
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: ${{ format('build-outputs-aarch64-unknown-linux-gnu-{0}', matrix.profile) }}
|
||||
path: ${{ format('target/aarch64-unknown-linux-gnu/{0}/librashader.*', matrix.profile) }}
|
75
.github/workflows/build.yml
vendored
75
.github/workflows/build.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: build librashader-capi for x86_64
|
||||
name: build librashader-capi
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -14,22 +14,85 @@ jobs:
|
|||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
profile: ['debug', 'release', 'optimized']
|
||||
os: ['windows-latest', 'ubuntu-latest', 'macos-latest', 'macos-14']
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
output: x86_64-ubuntu
|
||||
- os: windows-latest
|
||||
output: x86_64-windows
|
||||
- os: macos-latest
|
||||
output: x86_64-macos
|
||||
- os: macos-14
|
||||
output: aarch64-macos
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.output }} (${{ matrix.profile }})
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install nightly Rust
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly
|
||||
override: true
|
||||
- name: Build dynamic library
|
||||
run: cargo run -p librashader-build-script -- --profile ${{ matrix.profile }}
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: ${{ format('build-outputs-{0}-{1}', matrix.os, matrix.profile) }}
|
||||
name: ${{ format('librashader-{0}-{1}-{2}', matrix.output, github.sha, matrix.profile) }}
|
||||
path: ${{ format('target/{0}/librashader.*', matrix.profile) }}
|
||||
build-ubuntu-arm64:
|
||||
strategy:
|
||||
matrix:
|
||||
profile: ['debug', 'release', 'optimized']
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
name: aarch64-ubuntu
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly
|
||||
targets: aarch64-unknown-linux-gnu
|
||||
- name: Install ARM64 cross-compilation dependencies
|
||||
continue-on-error: true
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo dpkg --add-architecture arm64
|
||||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted" | sudo tee -a /etc/apt/sources.list
|
||||
sudo apt-get update || true
|
||||
sudo apt-get -y install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
|
||||
- name: Build dynamic library
|
||||
run: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc cargo run -p librashader-build-script -- --profile ${{ matrix.profile }} --target aarch64-unknown-linux-gnu
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: ${{ format('librashader-aarch64-ubuntu-{0}-{1}', github.sha, matrix.profile) }}
|
||||
path: ${{ format('target/aarch64-unknown-linux-gnu/{0}/librashader.*', matrix.profile) }}
|
||||
build-windows-arm64:
|
||||
strategy:
|
||||
matrix:
|
||||
profile: ['debug', 'release', 'optimized']
|
||||
fail-fast: false
|
||||
runs-on: windows-latest
|
||||
name: aarch64-windows
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2024-01-15 # pinned because it seems like there's a segfault on nightly
|
||||
override: true
|
||||
targets: aarch64-pc-windows-msvc
|
||||
- name: Build dynamic library
|
||||
run: cargo run -p librashader-build-script -- --profile ${{ matrix.profile }} --target aarch64-pc-windows-msvc
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: ${{ format('librashader-aarch64-windows-{0}-{1}', github.sha, matrix.profile) }}
|
||||
path: ${{ format('target/aarch64-pc-windows-msvc/{0}/librashader.*', matrix.profile) }}
|
||||
|
|
21
.github/workflows/package-obs.yml
vendored
21
.github/workflows/package-obs.yml
vendored
|
@ -1,29 +1,46 @@
|
|||
name: build Linux packages with Open Build Service
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
branches: [ "master" ]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
approve-obs-build:
|
||||
name: "approval"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Approve
|
||||
run: echo OBS build CI test runs need to be approved by a maintainer.
|
||||
build-obs-binary:
|
||||
environment:
|
||||
name: obs-build-env
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- repo: Fedora_39
|
||||
spec: librashader.spec
|
||||
can_fail: true
|
||||
name: Fedora 39 (.rpm)
|
||||
- repo: xUbuntu_23.10
|
||||
spec: librashader.spec
|
||||
can_fail: true
|
||||
name: Ubuntu 23.10 (.deb)
|
||||
- repo: Arch
|
||||
spec: PKGBUILD
|
||||
can_fail: false
|
||||
name: Arch (PKGBUILD)
|
||||
runs-on: ubuntu-latest
|
||||
needs: [approve-obs-build]
|
||||
continue-on-error: ${{ matrix.can_fail }}
|
||||
name: ${{ matrix.name }}
|
||||
container:
|
||||
image: fedora:39
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install OSC and dependencies
|
||||
env:
|
||||
OBS_CONFIG: ${{ secrets.OBS_CONFIG }}
|
||||
|
|
83
.github/workflows/pr-full-test.yml
vendored
Normal file
83
.github/workflows/pr-full-test.yml
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
name: integration test shader reflection
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
approve-full-test:
|
||||
name: "approval"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Approve
|
||||
run: echo Full test suite for PRs needs approval by a maintainer
|
||||
test-presets:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: false
|
||||
environment:
|
||||
name: full-test
|
||||
needs: [approve-full-test]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test preset processing
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture preprocess_all_slang_presets_parsed
|
||||
test-naga:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: false
|
||||
environment:
|
||||
name: full-test
|
||||
needs: [ approve-full-test ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test Naga reflection
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture compile_all_slang_presets_wgsl_naga compile_all_slang_presets_msl_naga compile_all_slang_presets_spirv_naga
|
||||
test-cross:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: false
|
||||
environment:
|
||||
name: full-test
|
||||
needs: [ approve-full-test ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test SPIRV-Cross
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture compile_all_slang_presets_msl_cross compile_all_slang_presets_glsl_cross compile_all_slang_presets_hlsl_cross compile_all_slang_presets_spirv_cross
|
||||
test-dxil:
|
||||
runs-on: windows-latest
|
||||
continue-on-error: false
|
||||
environment:
|
||||
name: full-test
|
||||
needs: [ approve-full-test ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test DXIL
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture compile_all_slang_presets_dxil_cross
|
10
.github/workflows/publish-obs.yml
vendored
10
.github/workflows/publish-obs.yml
vendored
|
@ -8,22 +8,27 @@ env:
|
|||
|
||||
jobs:
|
||||
build-obs-binary:
|
||||
if: github.repository == 'SnowflakePowered/librashader'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- repo: Fedora_39
|
||||
spec: librashader.spec
|
||||
can_fail: true
|
||||
- repo: xUbuntu_23.10
|
||||
spec: librashader.spec
|
||||
can_fail: true
|
||||
- repo: Arch
|
||||
spec: PKGBUILD
|
||||
can_fail: false
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
continue-on-error: ${{ matrix.can_fail }}
|
||||
container:
|
||||
image: fedora:39
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Install OSC and dependencies
|
||||
env:
|
||||
OBS_CONFIG: ${{ secrets.OBS_CONFIG }}
|
||||
|
@ -49,6 +54,7 @@ jobs:
|
|||
osc build --no-verify --trust-all-projects ${{ matrix.repo }} x86_64 ${{ matrix.spec }}
|
||||
publish-obs:
|
||||
needs: build-obs-binary
|
||||
if: github.repository == 'SnowflakePowered/librashader'
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:39
|
||||
steps:
|
||||
|
|
67
.github/workflows/push-full-test.yml
vendored
Normal file
67
.github/workflows/push-full-test.yml
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
name: integration test shader reflection
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "0 0 * * 6"
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
jobs:
|
||||
test-presets:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test preset preprocessing
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture preprocess_all_slang_presets_parsed
|
||||
test-naga:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test Naga Reflection
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture compile_all_slang_presets_wgsl_naga compile_all_slang_presets_msl_naga compile_all_slang_presets_spirv_naga
|
||||
test-cross:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test SPIRV-Cross reflection
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture compile_all_slang_presets_msl_cross compile_all_slang_presets_glsl_cross compile_all_slang_presets_hlsl_cross compile_all_slang_presets_spirv_cross
|
||||
test-dxil:
|
||||
runs-on: windows-latest
|
||||
continue-on-error: false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install nightly Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: nightly
|
||||
- name: Test DXIL
|
||||
run: cargo test -p librashader --features=github-ci --test reflect -- --nocapture compile_all_slang_presets_dxil_cross
|
||||
|
1698
Cargo.lock
generated
1698
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -10,12 +10,16 @@ members = [
|
|||
"librashader-runtime-d3d12",
|
||||
"librashader-runtime-gl",
|
||||
"librashader-runtime-vk",
|
||||
"librashader-runtime-mtl",
|
||||
"librashader-runtime-wgpu",
|
||||
"librashader-cache",
|
||||
"librashader-capi",
|
||||
"librashader-build-script"
|
||||
, "librashader-runtime-wgpu"]
|
||||
"librashader-build-script", "librashader-runtime-d3d9"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
windows = "0.52.0"
|
||||
|
||||
[workspace.metadata.release]
|
||||
|
||||
[profile.optimized]
|
||||
|
|
93
README.md
93
README.md
|
@ -11,24 +11,32 @@ librashader (*/ˈli:brəʃeɪdɚ/*) is a preprocessor, compiler, and runtime for
|
|||
![Nightly rust](https://img.shields.io/badge/rust-nightly-orange.svg)
|
||||
|
||||
## Installation
|
||||
For end-users, librashader is available from the [Open Build Service](https://software.opensuse.org//download.html?project=home%3Achyyran%3Alibrashader&package=librashader) for a variety of Linux distributions and platforms. Windows users can grab the latest DLL from [GitHub Releases](https://github.com/SnowflakePowered/librashader/releases).
|
||||
For end-users, librashader is available from the [Open Build Service](https://software.opensuse.org//download.html?project=home%3Achyyran%3Alibrashader&package=librashader) for a variety of Linux distributions and platforms.
|
||||
Windows and macOS users can grab the latest binaries from [GitHub Releases](https://github.com/SnowflakePowered/librashader/releases).
|
||||
|
||||
## Supported Render APIs
|
||||
librashader supports OpenGL 3, OpenGL 4.6, Vulkan, Direct3D 11, and Direct3D 12. Metal and WebGPU
|
||||
are not currently supported (but pull-requests are welcome). librashader does not support legacy render
|
||||
APIs such as older versions of OpenGL, or legacy versions of Direct3D.
|
||||
librashader supports all modern graphics runtimes, including wgpu, Vulkan, OpenGL 3.3+ and 4.6 (with DSA),
|
||||
Direct3D 11, Direct3D 12, and Metal.
|
||||
|
||||
librashader does not support legacy render APIs such as older versions of OpenGL or Direct3D, except for experimental
|
||||
support for Direct3D 9.
|
||||
|
||||
| **API** | **Status** | **`librashader` feature** |
|
||||
|-------------|------------|---------------------------|
|
||||
| OpenGL 3.3+ | ✔ | `gl` |
|
||||
| OpenGL 4.6 | ✔ | `gl` |
|
||||
| Vulkan | ✔ | `vk` |
|
||||
| Direct3D 11 | ✔ | `d3d11` |
|
||||
| Direct3D 12 | ✔ | `d3d12` |
|
||||
| wgpu | ✔ | `wgpu` |
|
||||
| Metal | ❌ | |
|
||||
| OpenGL 3.3+ | ✅ | `gl` |
|
||||
| OpenGL 4.6 | ✅ | `gl` |
|
||||
| Vulkan | ✅ | `vk` |
|
||||
| Direct3D 9 | ⚠️ | `d3d9` |
|
||||
| Direct3D 11 | ✅ | `d3d11` |
|
||||
| Direct3D 12 | ✅ | `d3d12` |
|
||||
| Metal | ✅ | `metal` |
|
||||
| wgpu | 🆗 | `wgpu` |
|
||||
|
||||
✔ = Render API is supported — ❌ Render API is not supported
|
||||
✅ Full Support — 🆗 Secondary Support — ⚠️ ️Experimental Support
|
||||
|
||||
wgpu may not support all shaders due to restrictions from WGSL. Direct3D 9 support is experimental and does not fully
|
||||
support features such as previous frame feedback or history, as well as being unable to support shaders that need Direct3D 10+
|
||||
only features.
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -37,20 +45,21 @@ The C API is geared more towards integration with existing projects. The Rust `l
|
|||
of the internals if you wish to use parts of librashader piecemeal.
|
||||
|
||||
The librashader C API is best used by including `librashader_ld.h` in your project, which implements a loader that dynamically
|
||||
loads the librashader (`librashader.so` or `librashader.dll`) implementation in the search path.
|
||||
loads the librashader (`librashader.so`, `librashader.dll`, or `librashader.dylib`) implementation in the search path.
|
||||
|
||||
|
||||
### C compatibility
|
||||
The recommended way of integrating `librashader` is by the `librashader_ld` single header library which implements
|
||||
a dynamic loader for `librashader.dll` / `librashader.so`. See the [versioning policy](https://github.com/SnowflakePowered/librashader#versioning)
|
||||
for details on how librashader handles C ABI and API stability with regards to library updates.
|
||||
a dynamic loader for `librashader.dll` / `librashader.so` / `librashader.dylib`. See the [versioning policy](https://github.com/SnowflakePowered/librashader#versioning)
|
||||
for details on how librashader handles C ABI and API stability with regards to library updates. You can also link dynamically
|
||||
with just `librashader.h` and the equivalent of `-lrashader`.
|
||||
|
||||
Linking statically against `librashader.h` is possible, but is not officially supported. You will need to ensure
|
||||
linkage parameters are correct in order to successfully link with `librashader.lib` or `librashader.a`.
|
||||
The [corrosion](https://github.com/corrosion-rs/) CMake package is highly recommended.
|
||||
|
||||
### Thread safety
|
||||
In general, it is **safe** to create a filter chain instance from a different thread, but drawing frames requires
|
||||
Except for the Metal runtime, in general, it is **safe** to create a filter chain instance from a different thread, but drawing frames requires
|
||||
**external synchronization** of the filter chain object.
|
||||
|
||||
Filter chains can be created from any thread, but requires external synchronization of the graphics device queue where applicable
|
||||
|
@ -62,8 +71,11 @@ OpenGL has an additional restriction where creating the filter chain instance in
|
|||
the thread local OpenGL context is initialized to the same context as the drawing thread. Support for deferral of GPU resource initialization
|
||||
is not available to OpenGL.
|
||||
|
||||
The Metal runtime is **not thread safe**. However you can still defer submission of GPU resource initialization through the
|
||||
`filter_chain_create_deferred` function.
|
||||
|
||||
### Quad vertices and rotations
|
||||
All runtimes except OpenGL render with an identity matrix MVP and a VBO for with range `[-1, 1]`. The final pass uses a
|
||||
All runtimes render intermediate passes with an identity matrix MVP and a VBO for with range `[-1, 1]`. The final pass uses a
|
||||
Quad VBO with range `[0, 1]` and the following projection matrix by default.
|
||||
|
||||
```rust
|
||||
|
@ -78,17 +90,6 @@ static DEFAULT_MVP: &[f32; 16] = &[
|
|||
As with RetroArch, a rotation on this MVP will be applied only on the final pass for these runtimes. This is the only way to
|
||||
pass orientation information to shaders.
|
||||
|
||||
The OpenGL runtime uses a VBO for range `[0, 1]` for all passes and the following MVP for all passes.
|
||||
|
||||
```rust
|
||||
static GL_DEFAULT_MVP: &[f32; 16] = &[
|
||||
2.0, 0.0, 0.0, 0.0,
|
||||
0.0, 2.0, 0.0, 0.0,
|
||||
0.0, 0.0, 2.0, 0.0,
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
];
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
For Rust projects, simply add the crate to your `Cargo.toml`.
|
||||
|
@ -125,9 +126,12 @@ The following Rust examples show how to use each librashader runtime.
|
|||
* [OpenGL](https://github.com/SnowflakePowered/librashader/blob/master/librashader-runtime-gl/tests/triangle.rs)
|
||||
* [Direct3D 11](https://github.com/SnowflakePowered/librashader/blob/master/librashader-runtime-d3d11/tests/triangle.rs)
|
||||
* [Direct3D 12](https://github.com/SnowflakePowered/librashader/blob/master/librashader-runtime-d3d12/tests/triangle.rs)
|
||||
* [wgpu](https://github.com/SnowflakePowered/librashader/blob/master/librashader-runtime-wgpu/tests/hello_triangle.rs)
|
||||
|
||||
Some basic examples on using the C API are also provided in the [librashader-capi-tests](https://github.com/SnowflakePowered/librashader/tree/master/test/capi-tests/librashader-capi-tests)
|
||||
directory.
|
||||
Some basic examples on using the C API are also provided.
|
||||
|
||||
* [Direct3D 11](https://github.com/SnowflakePowered/librashader/tree/master/test/capi-tests/librashader-capi-tests)
|
||||
* [Metal with Objective-C](https://github.com/SnowflakePowered/librashader/tree/master/test/capi-tests/objctest)
|
||||
|
||||
## Compatibility
|
||||
|
||||
|
@ -153,14 +157,16 @@ Please report an issue if you run into a shader that works in RetroArch, but not
|
|||
* Sampler objects are used rather than `glTexParameter`.
|
||||
* Sampler inputs and outputs are not renamed. This is useful for debugging shaders in RenderDoc.
|
||||
* UBO and Push Constant Buffer sizes are padded to 16-byte boundaries.
|
||||
* The OpenGL runtime uses the same VBOs as the other runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's OpenGL driver uses only the final VBO.
|
||||
* OpenGL 4.6+
|
||||
* All caveats from the OpenGL 3.3+ section should be considered.
|
||||
* Should work on OpenGL 4.5 but this is not guaranteed. The OpenGL 4.6 runtime may eventually switch to using `ARB_spirv_extensions` for loading shaders, and this will not be marked as a breaking change.
|
||||
* The OpenGL 4.6 runtime uses Direct State Access to minimize changes to the OpenGL state. For GPUs released within the last 5 years, this may improve performance.
|
||||
* The OpenGL runtime uses the same VBOs as the other runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's OpenGL driver uses only the final VBO.
|
||||
* Vulkan
|
||||
* The Vulkan runtime uses [`VK_KHR_dynamic_rendering`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_dynamic_rendering.html) by default.
|
||||
This extension must be enabled at device creation. Explicit render passes can be used by configuring filter chain options, but may have reduced performance
|
||||
compared to dynamic rendering.
|
||||
* The Vulkan runtime can use [`VK_KHR_dynamic_rendering`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_dynamic_rendering.html).
|
||||
This extension must be enabled at device creation.
|
||||
Dynamic rendering may have improved performance when enabled, and supported by the host hardware.
|
||||
* Allocations within the runtime are done through [gpu-allocator](https://github.com/Traverse-Research/gpu-allocator) rather than handled manually.
|
||||
* Direct3D 11
|
||||
* Framebuffer copies are done via `ID3D11DeviceContext::CopySubresourceRegion` rather than a CPU conversion + copy.
|
||||
|
@ -169,7 +175,9 @@ Please report an issue if you run into a shader that works in RetroArch, but not
|
|||
which was released in late 2018.
|
||||
* For maximum compatibility with shaders, a shader compile pipeline based on [`spirv-to-dxil`](https://github.com/SnowflakePowered/spirv-to-dxil-rs) is used, with the SPIRV-Cross HLSL pipeline used as a fallback.
|
||||
This brings shader compatibility beyond what the RetroArch Direct3D 12 driver provides. The HLSL pipeline fallback may be removed in the future as `spirv-to-dxil` improves.
|
||||
* The Direct3D 12 runtime requires `dxil.dll` and `dxcompiler.dll` from the [DirectX Shader Compiler](https://github.com/microsoft/DirectXShaderCompiler).
|
||||
* The Direct3D 12 runtime requires `dxcompiler.dll` from the [DirectX Shader Compiler](https://github.com/microsoft/DirectXShaderCompiler), which may already be installed as part of Direct3D12. `dxil.dll` is not required.
|
||||
* Metal
|
||||
* The Metal runtime uses the same VBOs as the other runtimes as well as the identity matrix MVP for intermediate passes. RetroArch's Metal driver uses only the final VBO.
|
||||
|
||||
Most, if not all shader presets should work fine on librashader. The runtime specific differences should not affect the output,
|
||||
and are more a heads-up for integrating librashader into your project.
|
||||
|
@ -177,7 +185,7 @@ and are more a heads-up for integrating librashader into your project.
|
|||
## Versioning
|
||||
[![Latest Version](https://img.shields.io/crates/v/librashader.svg)](https://crates.io/crates/librashader)
|
||||
![C ABI](https://img.shields.io/badge/ABI%20version-1-yellowgreen)
|
||||
![C API](https://img.shields.io/badge/API%20version-0-blue)
|
||||
![C API](https://img.shields.io/badge/API%20version-1-blue)
|
||||
|
||||
|
||||
librashader typically follows [Semantic Versioning](https://semver.org/) with respect to the Rust API, where a minor version
|
||||
|
@ -209,6 +217,15 @@ The `SONAME` of `librashader.so` when installed via package manager is set to `L
|
|||
The above does not apply to releases of librashader prior to `0.1.0`, which were allowed to break API and ABI compatibility
|
||||
in both the Rust and C API without an increase to either `LIBRASHADER_CURRENT_VERSION` or `LIBRASHADER_CURRENT_ABI`.
|
||||
|
||||
### MSRV Policy
|
||||
|
||||
While librashader requires nightly Rust, the following MSRV policy is enforced for unstable library features.
|
||||
|
||||
* Windows and macOS: **latest** nightly
|
||||
* Linux: **1.70**
|
||||
|
||||
A CI job runs weekly to ensure librashader continues to build on nightly. Note that the MSRV is only intended to ease distribution on Linux and is allowed to change any time. It generally tracks the latest version of Rust available in the latest version of Ubuntu, but this may change with no warning in a patch release.
|
||||
|
||||
## License
|
||||
The core parts of librashader such as the preprocessor, the preset parser,
|
||||
the reflection library, and the runtimes, are all licensed under the Mozilla Public License version 2.0.
|
||||
|
@ -218,13 +235,13 @@ are more permissively licensed, and may allow you to use librashader in your per
|
|||
licensed or proprietary project.
|
||||
|
||||
To facilitate easier use of librashader in projects incompatible with MPL-2.0, `librashader_ld`
|
||||
implements a loader which thunks its calls to any `librashader.so` or `librashader.dll`
|
||||
implements a loader which thunks its calls to any `librashader.so`, `librashader.dll`, or `librashader.dylib`.
|
||||
library found in the load path. A non-MPL-2.0 compatible project may link against
|
||||
`librashader_ld` to use the librashader runtime, *provided that `librashader.so` or `librashader.dll`
|
||||
`librashader_ld` to use the librashader runtime, *provided that `librashader.so`, `librashader.dll` or `librashader.dylib`
|
||||
are distributed under the restrictions of MPLv2*.
|
||||
|
||||
Note that this means that if your project is unable to comply with the requirements of MPL-2.0,
|
||||
you **can not distribute `librashader.so` or `librashader.dll`** alongside your project.
|
||||
you **can not distribute `librashader.so`, `librashader.dll` or `librashader.dylib`** alongside your project.
|
||||
The end user must obtain the implementation of librashader themselves. For more information,
|
||||
see the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/).
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -7,7 +7,7 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
cbindgen = "0.26.0"
|
||||
clap = { version = "4.1.0", features = ["derive"] }
|
||||
clap = { version = "=4.1.0", features = ["derive"] }
|
||||
|
||||
|
||||
[package.metadata.release]
|
||||
|
|
|
@ -2,7 +2,7 @@ use clap::Parser;
|
|||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::process::{Command, ExitCode};
|
||||
use std::{env, fs};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -12,12 +12,14 @@ struct Args {
|
|||
profile: String,
|
||||
#[arg(long, global = true)]
|
||||
target: Option<String>,
|
||||
#[arg(last = true)]
|
||||
cargoflags: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
pub fn main() -> ExitCode {
|
||||
// Do not update files on docsrs
|
||||
if env::var("DOCS_RS").is_ok() {
|
||||
return;
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
let args = Args::parse();
|
||||
|
@ -35,11 +37,23 @@ pub fn main() {
|
|||
if profile == "debug" { "dev" } else { &profile }
|
||||
));
|
||||
|
||||
// If we're on RUSTC_BOOTSTRAP, it's likely because we're building for a package..
|
||||
if env::var("RUSTC_BOOTSTRAP").is_ok() {
|
||||
cmd.arg("--ignore-rust-version");
|
||||
}
|
||||
|
||||
if let Some(target) = &args.target {
|
||||
cmd.arg(format!("--target={}", &target));
|
||||
}
|
||||
|
||||
Some(cmd.status().expect("Failed to build librashader-capi"));
|
||||
if !args.cargoflags.is_empty() {
|
||||
cmd.args(args.cargoflags);
|
||||
}
|
||||
|
||||
let status = cmd.status().expect("Failed to build librashader-capi");
|
||||
if !status.success() {
|
||||
return ExitCode::from(status.code().unwrap_or(1) as u8);
|
||||
}
|
||||
|
||||
let mut output_dir = PathBuf::from(format!("target/{}", profile));
|
||||
if let Some(target) = &args.target {
|
||||
|
@ -66,7 +80,14 @@ pub fn main() {
|
|||
.expect("Unable to write bindings.");
|
||||
|
||||
println!("Moving artifacts...");
|
||||
if cfg!(target_os = "linux") {
|
||||
if cfg!(target_os = "macos") {
|
||||
let artifacts = &["liblibrashader_capi.dylib", "liblibrashader_capi.a"];
|
||||
for artifact in artifacts {
|
||||
let ext = artifact.strip_prefix("lib").unwrap();
|
||||
let ext = ext.replace("_capi", "");
|
||||
fs::rename(output_dir.join(artifact), output_dir.join(ext)).unwrap();
|
||||
}
|
||||
} else if cfg!(target_family = "unix") {
|
||||
let artifacts = &["liblibrashader_capi.so", "liblibrashader_capi.a"];
|
||||
for artifact in artifacts {
|
||||
let ext = artifact.strip_prefix("lib").unwrap();
|
||||
|
@ -98,4 +119,6 @@ pub fn main() {
|
|||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "librashader-cache"
|
||||
edition = "2021"
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -12,18 +12,18 @@ description = "RetroArch shaders for all."
|
|||
|
||||
[dependencies]
|
||||
serde = { version = "1.0" }
|
||||
librashader-reflect = { path = "../librashader-reflect", version = "0.2.0-beta.7", features = ["serialize"] }
|
||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.0-beta.7" }
|
||||
librashader-reflect = { path = "../librashader-reflect", version = "0.2.7", features = ["serialize"] }
|
||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.7" }
|
||||
platform-dirs = "0.3.0"
|
||||
blake3 = { version = "1.3.3" }
|
||||
thiserror = "1.0.38"
|
||||
bincode = { version = "2.0.0-rc.2", features = ["serde"] }
|
||||
rusqlite = { version = "0.28.0", features = ["bundled"] }
|
||||
persy = "1.4.7"
|
||||
|
||||
bytemuck = "1.13.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.48.0"
|
||||
workspace = true
|
||||
features = [
|
||||
"Win32_Graphics_Direct3D",
|
||||
"Win32_Graphics_Direct3D_Fxc",
|
||||
|
@ -35,7 +35,7 @@ optional = true
|
|||
d3d = ["windows", "librashader-reflect/dxil"]
|
||||
|
||||
# hack to get building on docsrs
|
||||
docsrs = ["blake3/pure", "rusqlite/in_gecko"]
|
||||
docsrs = ["blake3/pure"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["docsrs"]
|
||||
|
|
|
@ -3,10 +3,11 @@ use crate::key::CacheKey;
|
|||
|
||||
pub(crate) mod internal {
|
||||
use platform_dirs::AppDirs;
|
||||
use rusqlite::{Connection, DatabaseName};
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use persy::{ByteVec, Config, Persy, ValueMode};
|
||||
|
||||
pub(crate) fn get_cache_dir() -> Result<PathBuf, Box<dyn Error>> {
|
||||
let cache_dir = if let Some(cache_dir) =
|
||||
AppDirs::new(Some("librashader"), false).map(|a| a.cache_dir)
|
||||
|
@ -23,46 +24,73 @@ pub(crate) mod internal {
|
|||
Ok(cache_dir)
|
||||
}
|
||||
|
||||
pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
|
||||
let cache_dir = get_cache_dir()?;
|
||||
let mut conn = Connection::open(&cache_dir.join("librashader.db"))?;
|
||||
// pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
|
||||
// let cache_dir = get_cache_dir()?;
|
||||
// let mut conn = Connection::open(&cache_dir.join("librashader.db"))?;
|
||||
//
|
||||
// let tx = conn.transaction()?;
|
||||
// tx.pragma_update(Some(DatabaseName::Main), "journal_mode", "wal2")?;
|
||||
// tx.execute(
|
||||
// r#"create table if not exists cache (
|
||||
// type text not null,
|
||||
// id blob not null,
|
||||
// value blob not null unique,
|
||||
// primary key (id, type)
|
||||
// )"#,
|
||||
// [],
|
||||
// )?;
|
||||
// tx.commit()?;
|
||||
// Ok(conn)
|
||||
// }
|
||||
|
||||
let tx = conn.transaction()?;
|
||||
tx.pragma_update(Some(DatabaseName::Main), "journal_mode", "wal2")?;
|
||||
tx.execute(
|
||||
r#"create table if not exists cache (
|
||||
type text not null,
|
||||
id blob not null,
|
||||
value blob not null unique,
|
||||
primary key (id, type)
|
||||
)"#,
|
||||
[],
|
||||
)?;
|
||||
tx.commit()?;
|
||||
Ok(conn)
|
||||
pub(crate) fn get_cache() -> Result<Persy, Box<dyn Error>> {
|
||||
let cache_dir = get_cache_dir()?;
|
||||
match Persy::open_or_create_with(
|
||||
&cache_dir.join("librashader.db.1"),
|
||||
Config::new(),
|
||||
|persy| {
|
||||
let tx = persy.begin()?;
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
Ok(conn) => Ok(conn),
|
||||
Err(e) => {
|
||||
let path = &cache_dir.join("librashader.db.1");
|
||||
let _ = std::fs::remove_file(path).ok();
|
||||
Err(e)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_blob(
|
||||
conn: &Connection,
|
||||
conn: &Persy,
|
||||
index: &str,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let value = conn.query_row(
|
||||
&*format!("select value from cache where (type = (?1) and id = (?2))"),
|
||||
rusqlite::params![index, key],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
Ok(value)
|
||||
) -> Result<Option<Vec<u8>>, Box<dyn Error>> {
|
||||
if !conn.exists_index(index)? {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let value = conn.get::<_, ByteVec>(index, &ByteVec::from(key))?.next();
|
||||
Ok(value.map(|v| v.to_vec()))
|
||||
}
|
||||
|
||||
pub(crate) fn set_blob(conn: &Connection, index: &str, key: &[u8], value: &[u8]) {
|
||||
match conn.execute(
|
||||
&*format!("insert or replace into cache (type, id, value) values (?1, ?2, ?3)"),
|
||||
rusqlite::params![index, key, value],
|
||||
) {
|
||||
Ok(_) => return,
|
||||
Err(e) => println!("err: {:?}", e),
|
||||
pub(crate) fn set_blob(
|
||||
conn: &Persy,
|
||||
index: &str,
|
||||
key: &[u8],
|
||||
value: &[u8],
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut tx = conn.begin()?;
|
||||
if !tx.exists_index(index)? {
|
||||
tx.create_index::<ByteVec, ByteVec>(index, ValueMode::Replace)?;
|
||||
}
|
||||
|
||||
tx.put(index, ByteVec::from(key), ByteVec::from(value))?;
|
||||
tx.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +129,7 @@ where
|
|||
};
|
||||
|
||||
'attempt: {
|
||||
if let Ok(blob) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
||||
if let Ok(Some(blob)) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
||||
let cached = T::from_bytes(&blob).map(&load);
|
||||
|
||||
match cached {
|
||||
|
@ -115,7 +143,7 @@ where
|
|||
let blob = factory(keys)?;
|
||||
|
||||
if let Some(slice) = T::to_bytes(&blob) {
|
||||
internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
||||
let _ = internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
||||
}
|
||||
Ok(load(blob)?)
|
||||
}
|
||||
|
@ -157,7 +185,7 @@ where
|
|||
};
|
||||
|
||||
let pipeline = 'attempt: {
|
||||
if let Ok(blob) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
||||
if let Ok(Some(blob)) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
||||
let cached = restore_pipeline(Some(blob));
|
||||
match cached {
|
||||
Ok(res) => {
|
||||
|
@ -173,7 +201,8 @@ where
|
|||
// update the pso every time just in case.
|
||||
if let Ok(state) = fetch_pipeline_state(&pipeline) {
|
||||
if let Some(slice) = T::to_bytes(&state) {
|
||||
internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
||||
// We don't really care if the transaction fails, just try again next time.
|
||||
let _ = internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,21 +6,29 @@ use librashader_reflect::back::targets::{GLSL, HLSL, SPIRV};
|
|||
|
||||
use librashader_reflect::back::{CompilerBackend, FromCompilation};
|
||||
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
|
||||
use librashader_reflect::front::{GlslangCompilation, ShaderCompilation};
|
||||
use librashader_reflect::front::{
|
||||
Glslang, ShaderInputCompiler, ShaderReflectObject, SpirvCompilation,
|
||||
};
|
||||
|
||||
pub struct CachedCompilation<T> {
|
||||
compilation: T,
|
||||
}
|
||||
|
||||
impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
|
||||
ShaderCompilation for CachedCompilation<T>
|
||||
impl<T: ShaderReflectObject> ShaderReflectObject for CachedCompilation<T> {
|
||||
type Compiler = T::Compiler;
|
||||
}
|
||||
|
||||
impl<T: ShaderReflectObject + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
|
||||
ShaderInputCompiler<CachedCompilation<T>> for Glslang
|
||||
where
|
||||
Glslang: ShaderInputCompiler<T>,
|
||||
{
|
||||
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
|
||||
fn compile(source: &ShaderSource) -> Result<CachedCompilation<T>, ShaderCompileError> {
|
||||
let cache = crate::cache::internal::get_cache();
|
||||
|
||||
let Ok(cache) = cache else {
|
||||
return Ok(CachedCompilation {
|
||||
compilation: T::compile(source)?,
|
||||
compilation: Glslang::compile(source)?,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -33,7 +41,9 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
|
|||
};
|
||||
|
||||
let compilation = 'cached: {
|
||||
if let Ok(cached) = crate::cache::internal::get_blob(&cache, "spirv", key.as_bytes()) {
|
||||
if let Ok(Some(cached)) =
|
||||
crate::cache::internal::get_blob(&cache, "spirv", key.as_bytes())
|
||||
{
|
||||
let decoded =
|
||||
bincode::serde::decode_from_slice(&cached, bincode::config::standard())
|
||||
.map(|(compilation, _)| CachedCompilation { compilation })
|
||||
|
@ -45,14 +55,18 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
|
|||
}
|
||||
|
||||
CachedCompilation {
|
||||
compilation: T::compile(source)?,
|
||||
compilation: Glslang::compile(source)?,
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(updated) =
|
||||
bincode::serde::encode_to_vec(&compilation.compilation, bincode::config::standard())
|
||||
{
|
||||
crate::cache::internal::set_blob(&cache, "spirv", key.as_bytes(), &updated)
|
||||
let Ok(()) =
|
||||
crate::cache::internal::set_blob(&cache, "spirv", key.as_bytes(), &updated)
|
||||
else {
|
||||
return Ok(compilation);
|
||||
};
|
||||
}
|
||||
|
||||
Ok(compilation)
|
||||
|
@ -60,53 +74,65 @@ impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize
|
|||
}
|
||||
|
||||
#[cfg(all(target_os = "windows", feature = "d3d"))]
|
||||
impl FromCompilation<CachedCompilation<GlslangCompilation>> for DXIL {
|
||||
type Target = <DXIL as FromCompilation<GlslangCompilation>>::Target;
|
||||
type Options = <DXIL as FromCompilation<GlslangCompilation>>::Options;
|
||||
type Context = <DXIL as FromCompilation<GlslangCompilation>>::Context;
|
||||
type Output = <DXIL as FromCompilation<GlslangCompilation>>::Output;
|
||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for DXIL
|
||||
where
|
||||
DXIL: FromCompilation<SpirvCompilation, T>,
|
||||
{
|
||||
type Target = <DXIL as FromCompilation<SpirvCompilation, T>>::Target;
|
||||
type Options = <DXIL as FromCompilation<SpirvCompilation, T>>::Options;
|
||||
type Context = <DXIL as FromCompilation<SpirvCompilation, T>>::Context;
|
||||
type Output = <DXIL as FromCompilation<SpirvCompilation, T>>::Output;
|
||||
|
||||
fn from_compilation(
|
||||
compile: CachedCompilation<GlslangCompilation>,
|
||||
compile: CachedCompilation<SpirvCompilation>,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
DXIL::from_compilation(compile.compilation)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromCompilation<CachedCompilation<GlslangCompilation>> for HLSL {
|
||||
type Target = <HLSL as FromCompilation<GlslangCompilation>>::Target;
|
||||
type Options = <HLSL as FromCompilation<GlslangCompilation>>::Options;
|
||||
type Context = <HLSL as FromCompilation<GlslangCompilation>>::Context;
|
||||
type Output = <HLSL as FromCompilation<GlslangCompilation>>::Output;
|
||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for HLSL
|
||||
where
|
||||
HLSL: FromCompilation<SpirvCompilation, T>,
|
||||
{
|
||||
type Target = <HLSL as FromCompilation<SpirvCompilation, T>>::Target;
|
||||
type Options = <HLSL as FromCompilation<SpirvCompilation, T>>::Options;
|
||||
type Context = <HLSL as FromCompilation<SpirvCompilation, T>>::Context;
|
||||
type Output = <HLSL as FromCompilation<SpirvCompilation, T>>::Output;
|
||||
|
||||
fn from_compilation(
|
||||
compile: CachedCompilation<GlslangCompilation>,
|
||||
compile: CachedCompilation<SpirvCompilation>,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
HLSL::from_compilation(compile.compilation)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromCompilation<CachedCompilation<GlslangCompilation>> for GLSL {
|
||||
type Target = <GLSL as FromCompilation<GlslangCompilation>>::Target;
|
||||
type Options = <GLSL as FromCompilation<GlslangCompilation>>::Options;
|
||||
type Context = <GLSL as FromCompilation<GlslangCompilation>>::Context;
|
||||
type Output = <GLSL as FromCompilation<GlslangCompilation>>::Output;
|
||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for GLSL
|
||||
where
|
||||
GLSL: FromCompilation<SpirvCompilation, T>,
|
||||
{
|
||||
type Target = <GLSL as FromCompilation<SpirvCompilation, T>>::Target;
|
||||
type Options = <GLSL as FromCompilation<SpirvCompilation, T>>::Options;
|
||||
type Context = <GLSL as FromCompilation<SpirvCompilation, T>>::Context;
|
||||
type Output = <GLSL as FromCompilation<SpirvCompilation, T>>::Output;
|
||||
|
||||
fn from_compilation(
|
||||
compile: CachedCompilation<GlslangCompilation>,
|
||||
compile: CachedCompilation<SpirvCompilation>,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
GLSL::from_compilation(compile.compilation)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromCompilation<CachedCompilation<GlslangCompilation>> for SPIRV {
|
||||
type Target = <SPIRV as FromCompilation<GlslangCompilation>>::Target;
|
||||
type Options = <SPIRV as FromCompilation<GlslangCompilation>>::Options;
|
||||
type Context = <SPIRV as FromCompilation<GlslangCompilation>>::Context;
|
||||
type Output = <SPIRV as FromCompilation<GlslangCompilation>>::Output;
|
||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for SPIRV
|
||||
where
|
||||
SPIRV: FromCompilation<SpirvCompilation, T>,
|
||||
{
|
||||
type Target = <SPIRV as FromCompilation<SpirvCompilation, T>>::Target;
|
||||
type Options = <SPIRV as FromCompilation<SpirvCompilation, T>>::Options;
|
||||
type Context = <SPIRV as FromCompilation<SpirvCompilation, T>>::Context;
|
||||
type Output = <SPIRV as FromCompilation<SpirvCompilation, T>>::Output;
|
||||
|
||||
fn from_compilation(
|
||||
compile: CachedCompilation<GlslangCompilation>,
|
||||
compile: CachedCompilation<SpirvCompilation>,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
SPIRV::from_compilation(compile.compilation)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "librashader-capi"
|
|||
edition = "2021"
|
||||
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -16,25 +16,50 @@ crate-type = [ "cdylib", "staticlib" ]
|
|||
|
||||
[features]
|
||||
default = ["runtime-all" ]
|
||||
runtime-all = ["runtime-opengl", "runtime-d3d11", "runtime-d3d12", "runtime-vulkan"]
|
||||
runtime-all = ["runtime-opengl", "runtime-d3d9", "runtime-d3d11", "runtime-d3d12", "runtime-vulkan", "runtime-metal"]
|
||||
runtime-opengl = ["gl", "librashader/runtime-gl"]
|
||||
runtime-d3d11 = ["windows", "librashader/runtime-d3d11", "windows/Win32_Graphics_Direct3D11"]
|
||||
runtime-d3d12 = ["windows", "librashader/runtime-d3d12", "windows/Win32_Graphics_Direct3D12"]
|
||||
runtime-d3d9 = ["windows", "librashader/runtime-d3d9", "windows/Win32_Graphics_Direct3D9"]
|
||||
|
||||
runtime-vulkan = ["ash", "librashader/runtime-vk"]
|
||||
runtime-metal = ["__cbindgen_internal_objc", "librashader/runtime-metal"]
|
||||
|
||||
__cbindgen_internal = ["runtime-all"]
|
||||
|
||||
# make runtime-metal depend on this, so its automatically implied.
|
||||
# this will make cbindgen generate __OBJC__ ifdefs for metal functions.
|
||||
__cbindgen_internal_objc = ["icrate", "objc2"]
|
||||
|
||||
[dependencies]
|
||||
librashader = { path = "../librashader", version = "0.2.0-beta.7", features = ["internal"] }
|
||||
thiserror = "1.0.37"
|
||||
paste = "1.0.9"
|
||||
gl = { version = "0.14.0", optional = true }
|
||||
rustc-hash = "1.1.0"
|
||||
ash = { version = "0.37", optional = true }
|
||||
spirv_cross = { package = "librashader-spirv-cross", version = "0.23" }
|
||||
spirv_cross = { package = "librashader-spirv-cross", version = "0.25.1" }
|
||||
sptr = "0.3.2"
|
||||
|
||||
[dependencies.librashader]
|
||||
path = "../librashader"
|
||||
version = "0.2.7"
|
||||
default-features = false
|
||||
features = ["reflect", "presets", "preprocess"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.48.0"
|
||||
workspace = true
|
||||
optional = true
|
||||
|
||||
[target.'cfg(target_vendor="apple")'.dependencies]
|
||||
icrate = { version = "0.1.0" , features = [ "Metal", "Metal_all" ], optional = true }
|
||||
objc2 = { version = "0.5.0", features = ["apple"] , optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"]
|
||||
targets = [ "x86_64-pc-windows-msvc",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-apple-ios",
|
||||
"i686-pc-windows-msvc",
|
||||
"i686-unknown-linux-gnu", ]
|
||||
features = ["librashader/docsrs"]
|
||||
|
|
|
@ -34,28 +34,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
after_includes = """
|
||||
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D11)
|
||||
#include <d3d11.h>
|
||||
#else
|
||||
typedef void ID3D11Device;
|
||||
typedef void ID3D11DeviceContext;
|
||||
typedef void ID3D11RenderTargetView;
|
||||
typedef void ID3D11ShaderResourceView;
|
||||
#endif
|
||||
#if defined(LIBRA_RUNTIME_VULKAN)
|
||||
#include <vulkan\\vulkan.h>
|
||||
#else
|
||||
typedef int32_t VkFormat;
|
||||
typedef uint64_t VkImage;
|
||||
typedef void* VkPhysicalDevice;
|
||||
typedef void* VkInstance;
|
||||
typedef void* VkCommandBuffer;
|
||||
#endif
|
||||
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12)
|
||||
#include <d3d12.h>
|
||||
#else
|
||||
typedef void ID3D12GraphicsCommandList;
|
||||
typedef void ID3D12Device;
|
||||
typedef void ID3D12Resource;
|
||||
typedef void D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||
#endif
|
||||
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D9)
|
||||
#include <D3D9.h>
|
||||
#endif
|
||||
#if defined(__APPLE__) && defined(LIBRA_RUNTIME_METAL) && defined(__OBJC__)
|
||||
#import <Metal/Metal.h>
|
||||
#endif
|
||||
#if defined(LIBRA_RUNTIME_VULKAN)
|
||||
#include <vulkan\\vulkan.h>
|
||||
#endif
|
||||
"""
|
||||
|
||||
|
@ -64,20 +54,20 @@ typedef void D3D12_CPU_DESCRIPTOR_HANDLE;
|
|||
"feature = runtime-vulkan" = "LIBRA_RUNTIME_VULKAN"
|
||||
"feature = runtime-d3d11" = "LIBRA_RUNTIME_D3D11"
|
||||
"feature = runtime-d3d12" = "LIBRA_RUNTIME_D3D12"
|
||||
"feature = runtime-d3d9" = "LIBRA_RUNTIME_D3D9"
|
||||
"feature = runtime-metal" = "LIBRA_RUNTIME_METAL"
|
||||
"feature = __cbindgen_internal_objc" = "__OBJC__"
|
||||
|
||||
"target_os = windows" = "_WIN32"
|
||||
"target_vendor = apple" = "__APPLE__"
|
||||
|
||||
[parse]
|
||||
parse_deps = true
|
||||
include = ["librashader",
|
||||
"librashader-presets",
|
||||
"librashader-preprocess",
|
||||
"librashader-reflect",
|
||||
"librashader-runtime-gl",
|
||||
"librashader-runtime-vk",
|
||||
"librashader-runtime-d3d11",
|
||||
"librashader-runtime-d3d12",
|
||||
]
|
||||
expand = ["librashader-capi"]
|
||||
parse_deps = false
|
||||
include = ["librashader"]
|
||||
|
||||
[parse.expand]
|
||||
crates = ["librashader-capi"]
|
||||
features = ["__cbindgen_internal"]
|
||||
|
||||
[struct]
|
||||
|
||||
|
@ -99,6 +89,20 @@ include = [
|
|||
"PFN_libra_preset_print",
|
||||
"PFN_libra_preset_get_runtime_params",
|
||||
"PFN_libra_preset_free_runtime_params",
|
||||
"PFN_libra_preset_create_with_context",
|
||||
|
||||
"PFN_libra_preset_ctx_create",
|
||||
"PFN_libra_preset_ctx_free",
|
||||
"PFN_libra_preset_ctx_set_core_name",
|
||||
"PFN_libra_preset_ctx_set_content_dir",
|
||||
"PFN_libra_preset_ctx_set_param",
|
||||
"PFN_libra_preset_ctx_set_core_rotation",
|
||||
"PFN_libra_preset_ctx_set_user_rotation",
|
||||
"PFN_libra_preset_ctx_set_screen_orientation",
|
||||
"PFN_libra_preset_ctx_set_allow_rotation",
|
||||
"PFN_libra_preset_ctx_set_view_aspect_orientation",
|
||||
"PFN_libra_preset_ctx_set_core_aspect_orientation",
|
||||
"PFN_libra_preset_ctx_set_runtime",
|
||||
|
||||
# error
|
||||
"PFN_libra_error_errno",
|
||||
|
@ -137,6 +141,14 @@ include = [
|
|||
"PFN_libra_d3d11_filter_chain_get_active_pass_count",
|
||||
"PFN_libra_d3d11_filter_chain_free",
|
||||
|
||||
# d3d11
|
||||
"PFN_libra_d3d9_filter_chain_create",
|
||||
"PFN_libra_d3d9_filter_chain_frame",
|
||||
"PFN_libra_d3d9_filter_chain_set_param",
|
||||
"PFN_libra_d3d9_filter_chain_get_param",
|
||||
"PFN_libra_d3d9_filter_chain_set_active_pass_count",
|
||||
"PFN_libra_d3d9_filter_chain_get_active_pass_count",
|
||||
"PFN_libra_d3d9_filter_chain_free",
|
||||
|
||||
# d3d12
|
||||
"PFN_libra_d3d12_filter_chain_create",
|
||||
|
@ -147,17 +159,37 @@ include = [
|
|||
"PFN_libra_d3d12_filter_chain_set_active_pass_count",
|
||||
"PFN_libra_d3d12_filter_chain_get_active_pass_count",
|
||||
"PFN_libra_d3d12_filter_chain_free",
|
||||
|
||||
# metal
|
||||
"PFN_libra_mtl_filter_chain_create",
|
||||
"PFN_libra_mtl_filter_chain_create_deferred",
|
||||
"PFN_libra_mtl_filter_chain_frame",
|
||||
"PFN_libra_mtl_filter_chain_set_param",
|
||||
"PFN_libra_mtl_filter_chain_get_param",
|
||||
"PFN_libra_mtl_filter_chain_set_active_pass_count",
|
||||
"PFN_libra_mtl_filter_chain_get_active_pass_count",
|
||||
"PFN_libra_mtl_filter_chain_free",
|
||||
]
|
||||
|
||||
exclude = ["Option_ID3D11DeviceContext"]
|
||||
exclude = [
|
||||
"Option_ID3D11DeviceContext",
|
||||
"PMTLCommandQueue",
|
||||
"PMTLCommandBuffer",
|
||||
"PMTLTexture"
|
||||
]
|
||||
|
||||
[export.rename]
|
||||
"LibrashaderError" = "_libra_error"
|
||||
"ShaderPreset" = "_shader_preset"
|
||||
|
||||
"WildcardContext" = "_preset_ctx"
|
||||
|
||||
"FilterChainGL" = "_filter_chain_gl"
|
||||
"FilterChainVulkan" = "_filter_chain_vk"
|
||||
"FilterChainD3D11" = "_filter_chain_d3d11"
|
||||
"FilterChainD3D12" = "_filter_chain_d3d12"
|
||||
"FilterChainD3D9" = "_filter_chain_d3d9"
|
||||
"FilterChainMetal" = "_filter_chain_mtl"
|
||||
|
||||
# vulkan renames
|
||||
"PhysicalDevice" = "VkPhysicalDevice"
|
||||
|
@ -174,10 +206,19 @@ exclude = ["Option_ID3D11DeviceContext"]
|
|||
"ID3D11RenderTargetView" = "ID3D11RenderTargetView *"
|
||||
"ID3D11ShaderResourceView" = "ID3D11ShaderResourceView *"
|
||||
|
||||
# hack to get proper pointer indirection for COM pointers
|
||||
"IDirect3DDevice9" = "IDirect3DDevice9 *"
|
||||
"IDirect3DSurface9" = "IDirect3DSurface9 *"
|
||||
"IDirect3DTexture9" = "IDirect3DTexture9 *"
|
||||
|
||||
# hack to force cbindgen to not generate option type for nullable ID3D11DeviceContext.
|
||||
"Option_ID3D11DeviceContext" = "ID3D11DeviceContext *"
|
||||
|
||||
# hack to get proper pointer indirection for COM pointers
|
||||
"ID3D12Device" = "ID3D12Device *"
|
||||
"ID3D12Resource" = "ID3D12Resource *"
|
||||
"ID3D12GraphicsCommandList" = "ID3D12GraphicsCommandList *"
|
||||
"ID3D12GraphicsCommandList" = "ID3D12GraphicsCommandList *"
|
||||
|
||||
"PMTLCommandQueue" = "id<MTLCommandQueue>"
|
||||
"PMTLCommandBuffer" = "id<MTLCommandBuffer>"
|
||||
"PMTLTexture" = "id<MTLTexture>"
|
|
@ -1,5 +1,6 @@
|
|||
//! Binding types for the librashader C API.
|
||||
use crate::error::LibrashaderError;
|
||||
use librashader::presets::context::{Orientation, VideoDriver, WildcardContext};
|
||||
use librashader::presets::ShaderPreset;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
|
@ -7,31 +8,122 @@ use std::ptr::NonNull;
|
|||
/// A handle to a shader preset object.
|
||||
pub type libra_shader_preset_t = Option<NonNull<ShaderPreset>>;
|
||||
|
||||
/// A handle to a preset wildcard context object.
|
||||
pub type libra_preset_ctx_t = Option<NonNull<WildcardContext>>;
|
||||
|
||||
/// A handle to a librashader error object.
|
||||
pub type libra_error_t = Option<NonNull<LibrashaderError>>;
|
||||
|
||||
/// An enum representing orientation for use in preset contexts.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum LIBRA_PRESET_CTX_ORIENTATION {
|
||||
Vertical = 0,
|
||||
Horizontal,
|
||||
}
|
||||
impl From<LIBRA_PRESET_CTX_ORIENTATION> for Orientation {
|
||||
fn from(value: LIBRA_PRESET_CTX_ORIENTATION) -> Self {
|
||||
match value {
|
||||
LIBRA_PRESET_CTX_ORIENTATION::Vertical => Orientation::Vertical,
|
||||
LIBRA_PRESET_CTX_ORIENTATION::Horizontal => Orientation::Horizontal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An enum representing graphics runtimes (video drivers) for use in preset contexts.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum LIBRA_PRESET_CTX_RUNTIME {
|
||||
None = 0,
|
||||
GlCore,
|
||||
Vulkan,
|
||||
D3D11,
|
||||
D3D12,
|
||||
Metal,
|
||||
}
|
||||
|
||||
impl From<LIBRA_PRESET_CTX_RUNTIME> for VideoDriver {
|
||||
fn from(value: LIBRA_PRESET_CTX_RUNTIME) -> Self {
|
||||
match value {
|
||||
LIBRA_PRESET_CTX_RUNTIME::None => VideoDriver::None,
|
||||
LIBRA_PRESET_CTX_RUNTIME::GlCore => VideoDriver::GlCore,
|
||||
LIBRA_PRESET_CTX_RUNTIME::Vulkan => VideoDriver::Vulkan,
|
||||
LIBRA_PRESET_CTX_RUNTIME::D3D11 => VideoDriver::Direct3D11,
|
||||
LIBRA_PRESET_CTX_RUNTIME::D3D12 => VideoDriver::Direct3D12,
|
||||
LIBRA_PRESET_CTX_RUNTIME::Metal => VideoDriver::Metal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-opengl")]
|
||||
use librashader::runtime::gl::FilterChain as FilterChainGL;
|
||||
|
||||
/// A handle to a OpenGL filter chain.
|
||||
#[cfg(feature = "runtime-opengl")]
|
||||
#[doc(cfg(feature = "runtime-opengl"))]
|
||||
pub type libra_gl_filter_chain_t = Option<NonNull<librashader::runtime::gl::capi::FilterChainGL>>;
|
||||
pub type libra_gl_filter_chain_t = Option<NonNull<FilterChainGL>>;
|
||||
|
||||
/// A handle to a Direct3D 11 filter chain.
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d11")
|
||||
))]
|
||||
use librashader::runtime::d3d11::FilterChain as FilterChainD3D11;
|
||||
|
||||
/// A handle to a Direct3D 11 filter chain.
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))]
|
||||
pub type libra_d3d11_filter_chain_t =
|
||||
Option<NonNull<librashader::runtime::d3d11::capi::FilterChainD3D11>>;
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d11")
|
||||
))]
|
||||
pub type libra_d3d11_filter_chain_t = Option<NonNull<FilterChainD3D11>>;
|
||||
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d12")
|
||||
))]
|
||||
use librashader::runtime::d3d12::FilterChain as FilterChainD3D12;
|
||||
/// A handle to a Direct3D 12 filter chain.
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))]
|
||||
pub type libra_d3d12_filter_chain_t =
|
||||
Option<NonNull<librashader::runtime::d3d12::capi::FilterChainD3D12>>;
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d12")
|
||||
))]
|
||||
pub type libra_d3d12_filter_chain_t = Option<NonNull<FilterChainD3D12>>;
|
||||
|
||||
/// A handle to a Direct3D 9 filter chain.
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d9")
|
||||
))]
|
||||
use librashader::runtime::d3d9::FilterChain as FilterChainD3D9;
|
||||
|
||||
/// A handle to a Direct3D 11 filter chain.
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d9")))]
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d9")
|
||||
))]
|
||||
pub type libra_d3d9_filter_chain_t = Option<NonNull<FilterChainD3D9>>;
|
||||
|
||||
#[cfg(feature = "runtime-vulkan")]
|
||||
use librashader::runtime::vk::FilterChain as FilterChainVulkan;
|
||||
/// A handle to a Vulkan filter chain.
|
||||
#[cfg(feature = "runtime-vulkan")]
|
||||
#[doc(cfg(feature = "runtime-vulkan"))]
|
||||
pub type libra_vk_filter_chain_t =
|
||||
Option<NonNull<librashader::runtime::vk::capi::FilterChainVulkan>>;
|
||||
pub type libra_vk_filter_chain_t = Option<NonNull<FilterChainVulkan>>;
|
||||
|
||||
#[cfg(all(target_os = "macos", feature = "runtime-metal"))]
|
||||
use librashader::runtime::mtl::FilterChain as FilterChainMetal;
|
||||
#[doc(cfg(all(target_vendor = "apple", feature = "runtime-metal")))]
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(
|
||||
target_vendor = "apple",
|
||||
feature = "runtime-metal",
|
||||
feature = "__cbindgen_internal_objc"
|
||||
)
|
||||
))]
|
||||
pub type libra_mtl_filter_chain_t = Option<NonNull<FilterChainMetal>>;
|
||||
|
||||
/// Defines the output viewport for a rendered frame.
|
||||
#[repr(C)]
|
||||
|
@ -87,3 +179,39 @@ macro_rules! config_struct {
|
|||
pub(crate) use config_set_field;
|
||||
pub(crate) use config_struct;
|
||||
pub(crate) use config_version_set;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[deny(deprecated)]
|
||||
#[deprecated = "Forward declarations for cbindgen, do not use."]
|
||||
mod __cbindgen_opaque_forward_declarations {
|
||||
macro_rules! typedef_struct {
|
||||
($($(#[$($attrss:tt)*])* $name:ident;)*) => {
|
||||
$($(#[$($attrss)*])*
|
||||
#[allow(unused)]
|
||||
#[doc(hidden)]
|
||||
#[deny(deprecated)]
|
||||
#[deprecated]
|
||||
pub struct $name;
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
typedef_struct! {
|
||||
/// Opaque struct for a preset context.
|
||||
WildcardContext;
|
||||
/// Opaque struct for a shader preset.
|
||||
ShaderPreset;
|
||||
/// Opaque struct for an OpenGL filter chain.
|
||||
FilterChainGL;
|
||||
/// Opaque struct for a Direct3D 11 filter chain.
|
||||
FilterChainD3D11;
|
||||
/// Opaque struct for a Direct3D 12 filter chain.
|
||||
FilterChainD3D12;
|
||||
/// Opaque struct for a Direct3D 9 filter chain.
|
||||
FilterChainD3D9;
|
||||
/// Opaque struct for a Vulkan filter chain.
|
||||
FilterChainVulkan;
|
||||
/// Opaque struct for a Metal filter chain.
|
||||
FilterChainMetal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::mem::MaybeUninit;
|
|||
use std::ptr::NonNull;
|
||||
use thiserror::Error;
|
||||
|
||||
/// The error type for librashader.
|
||||
/// The error type for librashader C API.
|
||||
#[non_exhaustive]
|
||||
#[derive(Error, Debug)]
|
||||
pub enum LibrashaderError {
|
||||
|
@ -37,10 +37,18 @@ pub enum LibrashaderError {
|
|||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))]
|
||||
#[error("There was an error in the D3D12 filter chain.")]
|
||||
D3D12FilterError(#[from] librashader::runtime::d3d12::error::FilterChainError),
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d9"))]
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d9")))]
|
||||
#[error("There was an error in the D3D9 filter chain.")]
|
||||
D3D9FilterError(#[from] librashader::runtime::d3d9::error::FilterChainError),
|
||||
#[cfg(feature = "runtime-vulkan")]
|
||||
#[doc(cfg(feature = "runtime-vulkan"))]
|
||||
#[error("There was an error in the Vulkan filter chain.")]
|
||||
VulkanFilterError(#[from] librashader::runtime::vk::error::FilterChainError),
|
||||
#[doc(cfg(all(target_vendor = "apple", feature = "runtime-metal")))]
|
||||
#[cfg(all(target_vendor = "apple", feature = "runtime-metal"))]
|
||||
#[error("There was an error in the D3D12 filter chain.")]
|
||||
MetalFilterError(#[from] librashader::runtime::mtl::error::FilterChainError),
|
||||
}
|
||||
|
||||
/// Error codes for librashader error types.
|
||||
|
@ -185,8 +193,12 @@ impl LibrashaderError {
|
|||
LibrashaderError::D3D11FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||
LibrashaderError::D3D12FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d9"))]
|
||||
LibrashaderError::D3D9FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||
#[cfg(feature = "runtime-vulkan")]
|
||||
LibrashaderError::VulkanFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||
#[cfg(all(target_vendor = "apple", feature = "runtime-metal"))]
|
||||
LibrashaderError::MetalFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||
}
|
||||
}
|
||||
pub(crate) const fn ok() -> libra_error_t {
|
||||
|
@ -200,12 +212,12 @@ impl LibrashaderError {
|
|||
|
||||
macro_rules! assert_non_null {
|
||||
($value:ident) => {
|
||||
if $value.is_null() || !$value.is_aligned() {
|
||||
if $value.is_null() || !$crate::ffi::ptr_is_aligned($value) {
|
||||
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export();
|
||||
}
|
||||
};
|
||||
(noexport $value:ident) => {
|
||||
if $value.is_null() || !$value.is_aligned() {
|
||||
if $value.is_null() || !$crate::ffi::ptr_is_aligned($value) {
|
||||
return Err($crate::error::LibrashaderError::InvalidParameter(
|
||||
stringify!($value),
|
||||
));
|
||||
|
|
|
@ -222,5 +222,25 @@ macro_rules! extern_fn {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn boxed_slice_into_raw_parts<T>(vec: Box<[T]>) -> (*mut T, usize) {
|
||||
let mut me = ManuallyDrop::new(vec);
|
||||
(me.as_mut_ptr(), me.len())
|
||||
}
|
||||
|
||||
pub unsafe fn boxed_slice_from_raw_parts<T>(ptr: *mut T, len: usize) -> Box<[T]> {
|
||||
unsafe { Box::from_raw(std::slice::from_raw_parts_mut(ptr, len)) }
|
||||
}
|
||||
|
||||
#[allow(unstable_name_collisions)]
|
||||
pub fn ptr_is_aligned<T: Sized>(ptr: *const T) -> bool {
|
||||
use sptr::Strict;
|
||||
let align = std::mem::align_of::<T>();
|
||||
if !align.is_power_of_two() {
|
||||
panic!("is_aligned_to: align is not a power-of-two");
|
||||
}
|
||||
ptr.addr() & (align - 1) == 0
|
||||
}
|
||||
|
||||
pub(crate) use extern_fn;
|
||||
pub(crate) use ffi_body;
|
||||
use std::mem::ManuallyDrop;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! possible by linking against `librashader.h` as well as any static libraries used by `librashader`.
|
||||
//!
|
||||
//! ## Usage
|
||||
//! ⚠ Rust consumers use [librashader](https://docs.rs/librashader/) directly instead. ⚠
|
||||
//! ⚠ Rust consumers should use [librashader](https://docs.rs/librashader/) directly instead. ⚠
|
||||
//!
|
||||
//! The librashader C API is designed to be easy to use and safe. Most objects are only accessible behind an opaque pointer.
|
||||
//! Every allocated object can be freed with a corresponding `free` function **for that specific object type**.
|
||||
|
@ -54,20 +54,21 @@
|
|||
//!
|
||||
//! ## Thread safety
|
||||
//!
|
||||
//! In general, it is **safe** to create a filter chain instance from a different thread, but drawing filter passes must be
|
||||
//! synchronized externally. The exception to filter chain creation are in OpenGL, where creating the filter chain instance
|
||||
//! is safe **if and only if** the thread local OpenGL context is initialized to the same context as the drawing thread, and
|
||||
//! in Direct3D 11, where filter chain creation is unsafe if the `ID3D11Device` was created with
|
||||
//! `D3D11_CREATE_DEVICE_SINGLETHREADED`.
|
||||
//! Except for the metal runtime, it is in general, **safe** to create a filter chain instance from a different thread,
|
||||
//! but drawing filter passes must be synchronized externally. The exception to filter chain creation are in OpenGL,
|
||||
//! where creating the filter chain instance is safe **if and only if** the thread local OpenGL context is initialized
|
||||
//! to the same context as the drawing thread, and in Direct3D 11, where filter chain creation is unsafe
|
||||
//! if the `ID3D11Device` was created with `D3D11_CREATE_DEVICE_SINGLETHREADED`. Metal is entirely thread unsafe.
|
||||
//!
|
||||
//! You must ensure that only thread has access to a created filter pass **before** you call `*_frame`. `*_frame` may only be
|
||||
//! called from one thread at a time.
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(vec_into_raw_parts)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![deny(deprecated)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod ctypes;
|
||||
pub mod error;
|
||||
|
@ -80,6 +81,7 @@ pub mod reflect;
|
|||
|
||||
pub mod runtime;
|
||||
pub mod version;
|
||||
pub mod wildcard;
|
||||
|
||||
pub use version::LIBRASHADER_ABI_VERSION;
|
||||
pub use version::LIBRASHADER_API_VERSION;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! librashader preset C API (`libra_preset_*`).
|
||||
use crate::ctypes::libra_shader_preset_t;
|
||||
use crate::ctypes::{libra_preset_ctx_t, libra_shader_preset_t};
|
||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||
use crate::ffi::extern_fn;
|
||||
use librashader::presets::ShaderPreset;
|
||||
|
@ -55,7 +55,6 @@ extern_fn! {
|
|||
|
||||
let filename = unsafe { CStr::from_ptr(filename) };
|
||||
let filename = filename.to_str()?;
|
||||
println!("loading {filename}");
|
||||
|
||||
let preset = ShaderPreset::try_parse(filename)?;
|
||||
unsafe {
|
||||
|
@ -66,6 +65,49 @@ extern_fn! {
|
|||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Load a preset with the given wildcard context.
|
||||
///
|
||||
/// The wildcard context is immediately invalidated and must be recreated after
|
||||
/// the preset is created.
|
||||
///
|
||||
/// Path information variables `PRESET_DIR` and `PRESET` will automatically be filled in.
|
||||
/// ## Safety
|
||||
/// - `filename` must be either null or a valid, aligned pointer to a string path to the shader preset.
|
||||
/// - `context` must be either null or a valid, aligned pointer to a initialized `libra_preset_ctx_t`.
|
||||
/// - `context` is invalidated after this function returns.
|
||||
/// - `out` must be either null, or an aligned pointer to an uninitialized or invalid `libra_shader_preset_t`.
|
||||
/// ## Returns
|
||||
/// - If any parameters are null, `out` is unchanged, and this function returns `LIBRA_ERR_INVALID_PARAMETER`.
|
||||
fn libra_preset_create_with_context(
|
||||
filename: *const c_char,
|
||||
context: *mut libra_preset_ctx_t,
|
||||
out: *mut MaybeUninit<libra_shader_preset_t>
|
||||
) {
|
||||
assert_non_null!(filename);
|
||||
assert_non_null!(context);
|
||||
assert_non_null!(out);
|
||||
|
||||
let filename = unsafe { CStr::from_ptr(filename) };
|
||||
let filename = filename.to_str()?;
|
||||
|
||||
let mut context = unsafe {
|
||||
let context_ptr = &mut *context;
|
||||
let context = context_ptr.take();
|
||||
Box::from_raw(context.unwrap().as_ptr())
|
||||
};
|
||||
|
||||
context.add_path_defaults(filename);
|
||||
|
||||
let preset = ShaderPreset::try_parse_with_context(filename, *context)?;
|
||||
unsafe {
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
preset,
|
||||
)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Free the preset.
|
||||
///
|
||||
|
@ -73,7 +115,7 @@ extern_fn! {
|
|||
/// null.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `preset` must be a valid and aligned pointer to a shader preset.
|
||||
/// - `preset` must be a valid and aligned pointer to a `libra_shader_preset_t`.
|
||||
fn libra_preset_free(preset: *mut libra_shader_preset_t) {
|
||||
assert_non_null!(preset);
|
||||
unsafe {
|
||||
|
@ -88,7 +130,7 @@ extern_fn! {
|
|||
/// Set the value of the parameter in the preset.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `preset` must be null or a valid and aligned pointer to a shader preset.
|
||||
/// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
|
||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||
fn libra_preset_set_param(
|
||||
preset: *mut libra_shader_preset_t,
|
||||
|
@ -135,7 +177,7 @@ extern_fn! {
|
|||
/// Pretty print the shader preset.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `preset` must be null or a valid and aligned pointer to a shader preset.
|
||||
/// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
|
||||
fn libra_preset_print(preset: *mut libra_shader_preset_t) |preset| {
|
||||
assert_some_ptr!(preset);
|
||||
println!("{preset:#?}");
|
||||
|
@ -146,7 +188,7 @@ extern_fn! {
|
|||
/// Get a list of runtime parameters.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `preset` must be null or a valid and aligned pointer to a shader preset.
|
||||
/// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
|
||||
/// - `out` must be an aligned pointer to a `libra_preset_parameter_list_t`.
|
||||
/// - The output struct should be treated as immutable. Mutating any struct fields
|
||||
/// in the returned struct may at best cause memory leaks, and at worse
|
||||
|
@ -176,12 +218,15 @@ extern_fn! {
|
|||
step: param.step
|
||||
})
|
||||
}
|
||||
let (parts, len, cap) = values.into_raw_parts();
|
||||
|
||||
let values = values.into_boxed_slice();
|
||||
let (parts, len) = crate::ffi::boxed_slice_into_raw_parts(values);
|
||||
|
||||
unsafe {
|
||||
out.write(MaybeUninit::new(libra_preset_param_list_t {
|
||||
parameters: parts,
|
||||
length: len as u64,
|
||||
_internal_alloc: cap as u64,
|
||||
_internal_alloc: 0,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -209,9 +254,9 @@ extern_fn! {
|
|||
/// in undefined behaviour.
|
||||
fn libra_preset_free_runtime_params(preset: libra_preset_param_list_t) {
|
||||
unsafe {
|
||||
let values = Vec::from_raw_parts(preset.parameters.cast_mut(),
|
||||
preset.length as usize,
|
||||
preset._internal_alloc as usize);
|
||||
let values =
|
||||
crate::ffi::boxed_slice_from_raw_parts(preset.parameters.cast_mut(),
|
||||
preset.length as usize).into_vec();
|
||||
|
||||
for value in values {
|
||||
let name = CString::from_raw(value.name.cast_mut());
|
||||
|
|
|
@ -6,8 +6,8 @@ use librashader::reflect::targets::SPIRV;
|
|||
use librashader::reflect::{CompileShader, ReflectShader, ShaderCompilerOutput, ShaderReflection};
|
||||
use librashader::{FilterMode, WrapMode};
|
||||
|
||||
use librashader::reflect::cross::GlslangCompilation;
|
||||
use librashader::reflect::helper::image::{Image, UVDirection, RGBA8};
|
||||
use librashader::reflect::SpirvCompilation;
|
||||
|
||||
pub(crate) struct LookupTexture {
|
||||
wrap_mode: WrapMode,
|
||||
|
@ -38,8 +38,9 @@ impl FilterReflection {
|
|||
let (passes, textures) = (preset.shaders, preset.textures);
|
||||
|
||||
let (passes, semantics) = librashader::reflect::helper::compile_preset_passes::<
|
||||
Glslang,
|
||||
SPIRV,
|
||||
GlslangCompilation,
|
||||
SpirvCompilation,
|
||||
error::LibrashaderError,
|
||||
>(passes, &textures)?;
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@ use crate::ctypes::{
|
|||
};
|
||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||
use crate::ffi::extern_fn;
|
||||
use librashader::runtime::d3d11::{D3D11InputView, D3D11OutputView};
|
||||
use librashader::runtime::d3d11::{
|
||||
D3D11InputView, D3D11OutputView, FilterChain, FilterChainOptions, FrameOptions,
|
||||
};
|
||||
use std::ffi::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::{ManuallyDrop, MaybeUninit};
|
||||
|
@ -13,9 +15,6 @@ use windows::Win32::Graphics::Direct3D11::{
|
|||
ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, ID3D11ShaderResourceView,
|
||||
};
|
||||
|
||||
use librashader::runtime::d3d11::capi::options::FilterChainOptionsD3D11;
|
||||
use librashader::runtime::d3d11::capi::options::FrameOptionsD3D11;
|
||||
|
||||
use crate::LIBRASHADER_API_VERSION;
|
||||
use librashader::runtime::{FilterChainParameters, Size, Viewport};
|
||||
|
||||
|
@ -58,7 +57,7 @@ pub struct filter_chain_d3d11_opt_t {
|
|||
}
|
||||
|
||||
config_struct! {
|
||||
impl FilterChainOptionsD3D11 => filter_chain_d3d11_opt_t {
|
||||
impl FilterChainOptions => filter_chain_d3d11_opt_t {
|
||||
0 => [force_no_mipmaps, disable_cache];
|
||||
}
|
||||
}
|
||||
|
@ -74,11 +73,18 @@ pub struct frame_d3d11_opt_t {
|
|||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 4 = 270deg.
|
||||
pub rotation: u32,
|
||||
/// The total number of subframes ran. Default is 1.
|
||||
pub total_subframes: u32,
|
||||
/// The current sub frame. Default is 1.
|
||||
pub current_subframe: u32,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FrameOptionsD3D11 => frame_d3d11_opt_t {
|
||||
impl FrameOptions => frame_d3d11_opt_t {
|
||||
0 => [clear_history, frame_direction];
|
||||
1 => [rotation, total_subframes, current_subframe]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +120,7 @@ extern_fn! {
|
|||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
unsafe {
|
||||
let chain = librashader::runtime::d3d11::capi::FilterChainD3D11::load_from_preset(
|
||||
let chain = FilterChain::load_from_preset(
|
||||
*preset,
|
||||
&device,
|
||||
options.as_ref(),
|
||||
|
@ -174,7 +180,7 @@ extern_fn! {
|
|||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
unsafe {
|
||||
let chain = librashader::runtime::d3d11::capi::FilterChainD3D11::load_from_preset_deferred(
|
||||
let chain = FilterChain::load_from_preset_deferred(
|
||||
*preset,
|
||||
&device,
|
||||
&device_context,
|
||||
|
|
|
@ -13,11 +13,10 @@ use windows::Win32::Graphics::Direct3D12::{
|
|||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT;
|
||||
|
||||
use librashader::runtime::d3d12::capi::options::FilterChainOptionsD3D12;
|
||||
use librashader::runtime::d3d12::capi::options::FrameOptionsD3D12;
|
||||
|
||||
use crate::LIBRASHADER_API_VERSION;
|
||||
use librashader::runtime::d3d12::{D3D12InputImage, D3D12OutputView};
|
||||
use librashader::runtime::d3d12::{
|
||||
D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, FrameOptions,
|
||||
};
|
||||
use librashader::runtime::{FilterChainParameters, Size, Viewport};
|
||||
|
||||
/// Direct3D 12 parameters for the source image.
|
||||
|
@ -55,11 +54,18 @@ pub struct frame_d3d12_opt_t {
|
|||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 4 = 270deg.
|
||||
pub rotation: u32,
|
||||
/// The total number of subframes ran. Default is 1.
|
||||
pub total_subframes: u32,
|
||||
/// The current sub frame. Default is 1.
|
||||
pub current_subframe: u32,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FrameOptionsD3D12 => frame_d3d12_opt_t {
|
||||
impl FrameOptions => frame_d3d12_opt_t {
|
||||
0 => [clear_history, frame_direction];
|
||||
1 => [rotation, total_subframes, current_subframe]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +90,7 @@ pub struct filter_chain_d3d12_opt_t {
|
|||
}
|
||||
|
||||
config_struct! {
|
||||
impl FilterChainOptionsD3D12 => filter_chain_d3d12_opt_t {
|
||||
impl FilterChainOptions => filter_chain_d3d12_opt_t {
|
||||
0 => [force_hlsl_pipeline, force_no_mipmaps, disable_cache];
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +142,7 @@ extern_fn! {
|
|||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
unsafe {
|
||||
let chain = librashader::runtime::d3d12::capi::FilterChainD3D12::load_from_preset(
|
||||
let chain = FilterChain::load_from_preset(
|
||||
*preset,
|
||||
&device,
|
||||
options.as_ref(),
|
||||
|
@ -188,7 +194,7 @@ extern_fn! {
|
|||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
unsafe {
|
||||
let chain = librashader::runtime::d3d12::capi::FilterChainD3D12::load_from_preset_deferred(
|
||||
let chain = FilterChain::load_from_preset_deferred(
|
||||
*preset,
|
||||
&device,
|
||||
&command_list,
|
||||
|
|
259
librashader-capi/src/runtime/d3d9/filter_chain.rs
Normal file
259
librashader-capi/src/runtime/d3d9/filter_chain.rs
Normal file
|
@ -0,0 +1,259 @@
|
|||
use crate::ctypes::{
|
||||
config_struct, libra_d3d9_filter_chain_t, libra_shader_preset_t, libra_viewport_t, FromUninit,
|
||||
};
|
||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||
use crate::ffi::extern_fn;
|
||||
use librashader::runtime::d3d9::{FilterChain, FilterChainOptions, FrameOptions};
|
||||
use std::ffi::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::{ManuallyDrop, MaybeUninit};
|
||||
use std::ptr::NonNull;
|
||||
use std::slice;
|
||||
use windows::Win32::Graphics::Direct3D9::{IDirect3DDevice9, IDirect3DSurface9, IDirect3DTexture9};
|
||||
|
||||
use crate::LIBRASHADER_API_VERSION;
|
||||
use librashader::runtime::{FilterChainParameters, Viewport};
|
||||
|
||||
/// Options for Direct3D 11 filter chain creation.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct filter_chain_d3d9_opt_t {
|
||||
/// The librashader API version.
|
||||
pub version: LIBRASHADER_API_VERSION,
|
||||
/// Whether or not to explicitly disable mipmap
|
||||
/// generation regardless of shader preset settings.
|
||||
pub force_no_mipmaps: bool,
|
||||
/// Disable the shader object cache. Shaders will be
|
||||
/// recompiled rather than loaded from the cache.
|
||||
pub disable_cache: bool,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FilterChainOptions => filter_chain_d3d9_opt_t {
|
||||
0 => [force_no_mipmaps, disable_cache];
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for each Direct3D 11 shader frame.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct frame_d3d9_opt_t {
|
||||
/// The librashader API version.
|
||||
pub version: LIBRASHADER_API_VERSION,
|
||||
/// Whether or not to clear the history buffers.
|
||||
pub clear_history: bool,
|
||||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 4 = 270deg.
|
||||
pub rotation: u32,
|
||||
/// The total number of subframes ran. Default is 1.
|
||||
pub total_subframes: u32,
|
||||
/// The current sub frame. Default is 1.
|
||||
pub current_subframe: u32,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FrameOptions => frame_d3d9_opt_t {
|
||||
0 => [clear_history, frame_direction];
|
||||
1 => [rotation, total_subframes, current_subframe]
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Create the filter chain given the shader preset.
|
||||
///
|
||||
/// The shader preset is immediately invalidated and must be recreated after
|
||||
/// the filter chain is created.
|
||||
///
|
||||
/// ## Safety:
|
||||
/// - `preset` must be either null, or valid and aligned.
|
||||
/// - `options` must be either null, or valid and aligned.
|
||||
/// - `device` must not be null.
|
||||
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
||||
fn libra_d3d9_filter_chain_create(
|
||||
preset: *mut libra_shader_preset_t,
|
||||
device: ManuallyDrop<IDirect3DDevice9>,
|
||||
options: *const MaybeUninit<filter_chain_d3d9_opt_t>,
|
||||
out: *mut MaybeUninit<libra_d3d9_filter_chain_t>
|
||||
) {
|
||||
assert_non_null!(preset);
|
||||
let preset = unsafe {
|
||||
let preset_ptr = &mut *preset;
|
||||
let preset = preset_ptr.take();
|
||||
Box::from_raw(preset.unwrap().as_ptr())
|
||||
};
|
||||
|
||||
let options = if options.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { options.read() })
|
||||
};
|
||||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
unsafe {
|
||||
let chain = FilterChain::load_from_preset(
|
||||
*preset,
|
||||
&device,
|
||||
options.as_ref(),
|
||||
)?;
|
||||
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
chain,
|
||||
)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Draw a frame with the given parameters for the given filter chain.
|
||||
///
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
||||
/// function will return an error.
|
||||
/// - `mvp` may be null, or if it is not null, must be an aligned pointer to 16 consecutive `float`
|
||||
/// values for the model view projection matrix.
|
||||
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_d3d9_opt_t`
|
||||
/// struct.
|
||||
/// - `out` must not be null.
|
||||
/// - `image` must not be null.
|
||||
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
|
||||
/// thread at a time may call this function.
|
||||
nopanic fn libra_d3d9_filter_chain_frame(
|
||||
chain: *mut libra_d3d9_filter_chain_t,
|
||||
frame_count: usize,
|
||||
image: ManuallyDrop<IDirect3DTexture9>,
|
||||
viewport: libra_viewport_t,
|
||||
out: ManuallyDrop<IDirect3DSurface9>,
|
||||
mvp: *const f32,
|
||||
options: *const MaybeUninit<frame_d3d9_opt_t>
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
|
||||
let mvp = if mvp.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(<&[f32; 16]>::try_from(unsafe { slice::from_raw_parts(mvp, 16) }).unwrap())
|
||||
};
|
||||
|
||||
let options = if options.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { options.read() })
|
||||
};
|
||||
|
||||
let viewport = Viewport {
|
||||
x: viewport.x,
|
||||
y: viewport.y,
|
||||
output: ManuallyDrop::into_inner(out.clone()),
|
||||
mvp,
|
||||
};
|
||||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
|
||||
|
||||
unsafe {
|
||||
chain.frame(ManuallyDrop::into_inner(image.clone()), &viewport, frame_count, options.as_ref())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Sets a parameter for the filter chain.
|
||||
///
|
||||
/// If the parameter does not exist, returns an error.
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
|
||||
/// - `param_name` must be either null or a null terminated string.
|
||||
fn libra_d3d9_filter_chain_set_param(
|
||||
chain: *mut libra_d3d9_filter_chain_t,
|
||||
param_name: *const c_char,
|
||||
value: f32
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
assert_non_null!(param_name);
|
||||
unsafe {
|
||||
let name = CStr::from_ptr(param_name);
|
||||
let name = name.to_str()?;
|
||||
|
||||
if chain.set_parameter(name, value).is_none() {
|
||||
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Gets a parameter for the filter chain.
|
||||
///
|
||||
/// If the parameter does not exist, returns an error.
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
|
||||
/// - `param_name` must be either null or a null terminated string.
|
||||
fn libra_d3d9_filter_chain_get_param(
|
||||
chain: *mut libra_d3d9_filter_chain_t,
|
||||
param_name: *const c_char,
|
||||
out: *mut MaybeUninit<f32>
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
assert_non_null!(param_name);
|
||||
unsafe {
|
||||
let name = CStr::from_ptr(param_name);
|
||||
let name = name.to_str()?;
|
||||
|
||||
let Some(value) = chain.get_parameter(name) else {
|
||||
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||
};
|
||||
|
||||
out.write(MaybeUninit::new(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Sets the number of active passes for this chain.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
|
||||
fn libra_d3d9_filter_chain_set_active_pass_count(
|
||||
chain: *mut libra_d3d9_filter_chain_t,
|
||||
value: u32
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
chain.set_enabled_pass_count(value as usize);
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Gets the number of active passes for this chain.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
|
||||
fn libra_d3d9_filter_chain_get_active_pass_count(
|
||||
chain: *mut libra_d3d9_filter_chain_t,
|
||||
out: *mut MaybeUninit<u32>
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
unsafe {
|
||||
let value = chain.get_enabled_pass_count();
|
||||
out.write(MaybeUninit::new(value as u32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Free a d3d9 filter chain.
|
||||
///
|
||||
/// The resulting value in `chain` then becomes null.
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
|
||||
fn libra_d3d9_filter_chain_free(chain: *mut libra_d3d9_filter_chain_t) {
|
||||
assert_non_null!(chain);
|
||||
unsafe {
|
||||
let chain_ptr = &mut *chain;
|
||||
let chain = chain_ptr.take();
|
||||
drop(Box::from_raw(chain.unwrap().as_ptr()))
|
||||
};
|
||||
}
|
||||
}
|
5
librashader-capi/src/runtime/d3d9/mod.rs
Normal file
5
librashader-capi/src/runtime/d3d9/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
//! C API for the librashader D3D9 Runtime (`libra_d3d9_*`).
|
||||
|
||||
mod filter_chain;
|
||||
pub use filter_chain::*;
|
||||
const _: () = crate::assert_thread_safe::<librashader::runtime::d3d11::FilterChain>();
|
|
@ -3,7 +3,9 @@ use crate::ctypes::{
|
|||
};
|
||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||
use crate::ffi::extern_fn;
|
||||
use librashader::runtime::gl::{GLFramebuffer, GLImage};
|
||||
use librashader::runtime::gl::{
|
||||
FilterChain, FilterChainOptions, FrameOptions, GLFramebuffer, GLImage,
|
||||
};
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{c_char, c_void, CString};
|
||||
use std::mem::MaybeUninit;
|
||||
|
@ -11,8 +13,6 @@ use std::ptr::NonNull;
|
|||
use std::slice;
|
||||
|
||||
use crate::LIBRASHADER_API_VERSION;
|
||||
use librashader::runtime::gl::capi::options::FilterChainOptionsGL;
|
||||
use librashader::runtime::gl::capi::options::FrameOptionsGL;
|
||||
use librashader::runtime::FilterChainParameters;
|
||||
use librashader::runtime::{Size, Viewport};
|
||||
|
||||
|
@ -64,11 +64,18 @@ pub struct frame_gl_opt_t {
|
|||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 4 = 270deg.
|
||||
pub rotation: u32,
|
||||
/// The total number of subframes ran. Default is 1.
|
||||
pub total_subframes: u32,
|
||||
/// The current sub frame. Default is 1.
|
||||
pub current_subframe: u32,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FrameOptionsGL => frame_gl_opt_t {
|
||||
impl FrameOptions => frame_gl_opt_t {
|
||||
0 => [clear_history, frame_direction];
|
||||
1 => [rotation, total_subframes, current_subframe]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +99,7 @@ pub struct filter_chain_gl_opt_t {
|
|||
}
|
||||
|
||||
config_struct! {
|
||||
impl FilterChainOptionsGL => filter_chain_gl_opt_t {
|
||||
impl FilterChainOptions => filter_chain_gl_opt_t {
|
||||
0 => [glsl_version, use_dsa, force_no_mipmaps, disable_cache];
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +156,7 @@ extern_fn! {
|
|||
let options = options.map(FromUninit::from_uninit);
|
||||
|
||||
unsafe {
|
||||
let chain = librashader::runtime::gl::capi::FilterChainGL::load_from_preset(*preset, options.as_ref())?;
|
||||
let chain = FilterChain::load_from_preset(*preset, options.as_ref())?;
|
||||
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
chain,
|
||||
|
|
|
@ -8,9 +8,33 @@ pub mod gl;
|
|||
pub mod vk;
|
||||
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))]
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d11")
|
||||
))]
|
||||
pub mod d3d11;
|
||||
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d9")))]
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d9")
|
||||
))]
|
||||
pub mod d3d9;
|
||||
|
||||
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))]
|
||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(target_os = "windows", feature = "runtime-d3d12")
|
||||
))]
|
||||
pub mod d3d12;
|
||||
|
||||
#[doc(cfg(all(target_vendor = "apple", feature = "runtime-metal")))]
|
||||
#[cfg(any(
|
||||
feature = "__cbindgen_internal",
|
||||
all(
|
||||
target_vendor = "apple",
|
||||
feature = "runtime-metal",
|
||||
feature = "__cbindgen_internal_objc"
|
||||
)
|
||||
))]
|
||||
pub mod mtl;
|
||||
|
|
319
librashader-capi/src/runtime/mtl/filter_chain.rs
Normal file
319
librashader-capi/src/runtime/mtl/filter_chain.rs
Normal file
|
@ -0,0 +1,319 @@
|
|||
use crate::ctypes::{
|
||||
config_struct, libra_mtl_filter_chain_t, libra_shader_preset_t, libra_viewport_t, FromUninit,
|
||||
};
|
||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||
use crate::ffi::extern_fn;
|
||||
use librashader::runtime::mtl::{FilterChain, FilterChainOptions, FrameOptions};
|
||||
use std::ffi::c_char;
|
||||
use std::ffi::CStr;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
use std::slice;
|
||||
|
||||
use librashader::runtime::FilterChainParameters;
|
||||
use librashader::runtime::{Size, Viewport};
|
||||
|
||||
use icrate::Metal::{MTLCommandBuffer, MTLCommandQueue, MTLTexture};
|
||||
use objc2::runtime::ProtocolObject;
|
||||
|
||||
use crate::LIBRASHADER_API_VERSION;
|
||||
|
||||
/// An alias to a `id<MTLCommandQueue>` protocol object pointer.
|
||||
pub type PMTLCommandQueue = *const ProtocolObject<dyn MTLCommandQueue>;
|
||||
|
||||
/// An alias to a `id<MTLCommandBuffer>` protocol object pointer.
|
||||
pub type PMTLCommandBuffer = *const ProtocolObject<dyn MTLCommandBuffer>;
|
||||
|
||||
/// An alias to a `id<MTLTexture>` protocol object pointer.
|
||||
pub type PMTLTexture = *const ProtocolObject<dyn MTLTexture>;
|
||||
|
||||
/// Options for each Metal shader frame.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct frame_mtl_opt_t {
|
||||
/// The librashader API version.
|
||||
pub version: LIBRASHADER_API_VERSION,
|
||||
/// Whether or not to clear the history buffers.
|
||||
pub clear_history: bool,
|
||||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 4 = 270deg.
|
||||
pub rotation: u32,
|
||||
/// The total number of subframes ran. Default is 1.
|
||||
pub total_subframes: u32,
|
||||
/// The current sub frame. Default is 1.
|
||||
pub current_subframe: u32,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FrameOptions => frame_mtl_opt_t {
|
||||
0 => [clear_history, frame_direction];
|
||||
1 => [rotation, total_subframes, current_subframe]
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for filter chain creation.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct filter_chain_mtl_opt_t {
|
||||
/// The librashader API version.
|
||||
pub version: LIBRASHADER_API_VERSION,
|
||||
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
|
||||
pub force_no_mipmaps: bool,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FilterChainOptions => filter_chain_mtl_opt_t {
|
||||
0 => [force_no_mipmaps];
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Create the filter chain given the shader preset.
|
||||
///
|
||||
/// The shader preset is immediately invalidated and must be recreated after
|
||||
/// the filter chain is created.
|
||||
///
|
||||
/// ## Safety:
|
||||
/// - `queue` must be valid for the command buffers
|
||||
/// that `libra_mtl_filter_chain_frame` will write to.
|
||||
/// - `queue` must be a reference to a `id<MTLCommandQueue>`.
|
||||
/// - `preset` must be either null, or valid and aligned.
|
||||
/// - `options` must be either null, or valid and aligned.
|
||||
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
||||
fn libra_mtl_filter_chain_create(
|
||||
preset: *mut libra_shader_preset_t,
|
||||
queue: PMTLCommandQueue,
|
||||
options: *const MaybeUninit<filter_chain_mtl_opt_t>,
|
||||
out: *mut MaybeUninit<libra_mtl_filter_chain_t>
|
||||
) |queue| {
|
||||
assert_non_null!(preset);
|
||||
|
||||
let preset = unsafe {
|
||||
let preset_ptr = &mut *preset;
|
||||
let preset = preset_ptr.take();
|
||||
Box::from_raw(preset.unwrap().as_ptr())
|
||||
};
|
||||
|
||||
let options = if options.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { options.read() })
|
||||
};
|
||||
|
||||
let queue = queue.as_ref();
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
|
||||
unsafe {
|
||||
let chain = FilterChain::load_from_preset(*preset, queue, options.as_ref())?;
|
||||
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
chain,
|
||||
)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Create the filter chain given the shader preset deferring and GPU-side initialization
|
||||
/// to the caller. This function therefore requires no external synchronization of the device queue.
|
||||
///
|
||||
/// The shader preset is immediately invalidated and must be recreated after
|
||||
/// the filter chain is created.
|
||||
///
|
||||
/// ## Safety:
|
||||
/// - `queue` must be valid for the command buffers
|
||||
/// that `libra_mtl_filter_chain_frame` will write to.
|
||||
/// - `queue` must be a reference to a `id<MTLCommandQueue>`.
|
||||
/// - `command_buffer` must be a valid reference to a `MTLCommandBuffer` that is not already encoding.
|
||||
/// - `preset` must be either null, or valid and aligned.
|
||||
/// - `options` must be either null, or valid and aligned.
|
||||
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
||||
///
|
||||
/// The provided command buffer must be ready for recording and contain no prior commands.
|
||||
/// The caller is responsible for ending the command buffer and immediately submitting it to a
|
||||
/// graphics queue. The command buffer must be completely executed before calling `libra_mtl_filter_chain_frame`.
|
||||
fn libra_mtl_filter_chain_create_deferred(
|
||||
preset: *mut libra_shader_preset_t,
|
||||
queue: PMTLCommandQueue,
|
||||
command_buffer: PMTLCommandBuffer,
|
||||
options: *const MaybeUninit<filter_chain_mtl_opt_t>,
|
||||
out: *mut MaybeUninit<libra_mtl_filter_chain_t>
|
||||
) |queue, command_buffer| {
|
||||
assert_non_null!(preset);
|
||||
|
||||
let preset = unsafe {
|
||||
let preset_ptr = &mut *preset;
|
||||
let preset = preset_ptr.take();
|
||||
Box::from_raw(preset.unwrap().as_ptr())
|
||||
};
|
||||
|
||||
let options = if options.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { options.read() })
|
||||
};
|
||||
|
||||
let options = options.map(FromUninit::from_uninit);
|
||||
|
||||
unsafe {
|
||||
let chain = FilterChain::load_from_preset_deferred(*preset,
|
||||
queue,
|
||||
command_buffer,
|
||||
options.as_ref())?;
|
||||
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
chain,
|
||||
)))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Records rendering commands for a frame with the given parameters for the given filter chain
|
||||
/// to the input command buffer.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `command_buffer` must be a valid reference to a `MTLCommandBuffer` that is not already encoding.
|
||||
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
||||
/// function will return an error.
|
||||
/// - `mvp` may be null, or if it is not null, must be an aligned pointer to 16 consecutive `float`
|
||||
/// values for the model view projection matrix.
|
||||
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_mtl_opt_t`
|
||||
/// struct.
|
||||
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
|
||||
/// thread at a time may call this function.
|
||||
nopanic fn libra_mtl_filter_chain_frame(
|
||||
chain: *mut libra_mtl_filter_chain_t,
|
||||
command_buffer: PMTLCommandBuffer,
|
||||
frame_count: usize,
|
||||
image: PMTLTexture,
|
||||
viewport: libra_viewport_t,
|
||||
output: PMTLTexture,
|
||||
mvp: *const f32,
|
||||
opt: *const MaybeUninit<frame_mtl_opt_t>
|
||||
) |command_buffer, image, output|; mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
|
||||
let mvp = if mvp.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(<&[f32; 16]>::try_from(unsafe { slice::from_raw_parts(mvp, 16) }).unwrap())
|
||||
};
|
||||
let opt = if opt.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { opt.read() })
|
||||
};
|
||||
let opt = opt.map(FromUninit::from_uninit);
|
||||
let viewport = Viewport {
|
||||
x: viewport.x,
|
||||
y: viewport.y,
|
||||
output,
|
||||
mvp,
|
||||
};
|
||||
|
||||
chain.frame(&image, &viewport, command_buffer, frame_count, opt.as_ref())?;
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Sets a parameter for the filter chain.
|
||||
///
|
||||
/// If the parameter does not exist, returns an error.
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
|
||||
/// - `param_name` must be either null or a null terminated string.
|
||||
fn libra_mtl_filter_chain_set_param(
|
||||
chain: *mut libra_mtl_filter_chain_t,
|
||||
param_name: *const c_char,
|
||||
value: f32
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
unsafe {
|
||||
let name = CStr::from_ptr(param_name);
|
||||
let name = name.to_str()?;
|
||||
|
||||
if chain.set_parameter(name, value).is_none() {
|
||||
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Gets a parameter for the filter chain.
|
||||
///
|
||||
/// If the parameter does not exist, returns an error.
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
|
||||
/// - `param_name` must be either null or a null terminated string.
|
||||
fn libra_mtl_filter_chain_get_param(
|
||||
chain: *mut libra_mtl_filter_chain_t,
|
||||
param_name: *const c_char,
|
||||
out: *mut MaybeUninit<f32>
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
|
||||
unsafe {
|
||||
let name = CStr::from_ptr(param_name);
|
||||
let name = name.to_str()?;
|
||||
|
||||
let Some(value) = chain.get_parameter(name) else {
|
||||
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||
};
|
||||
|
||||
out.write(MaybeUninit::new(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Sets the number of active passes for this chain.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
|
||||
fn libra_mtl_filter_chain_set_active_pass_count(
|
||||
chain: *mut libra_mtl_filter_chain_t,
|
||||
value: u32
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
chain.set_enabled_pass_count(value as usize);
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Gets the number of active passes for this chain.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
|
||||
fn libra_mtl_filter_chain_get_active_pass_count(
|
||||
chain: *mut libra_mtl_filter_chain_t,
|
||||
out: *mut MaybeUninit<u32>
|
||||
) mut |chain| {
|
||||
assert_some_ptr!(mut chain);
|
||||
let value = chain.get_enabled_pass_count();
|
||||
unsafe {
|
||||
out.write(MaybeUninit::new(value as u32))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Free a Metal filter chain.
|
||||
///
|
||||
/// The resulting value in `chain` then becomes null.
|
||||
/// ## Safety
|
||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
|
||||
fn libra_mtl_filter_chain_free(
|
||||
chain: *mut libra_mtl_filter_chain_t
|
||||
) {
|
||||
assert_non_null!(chain);
|
||||
unsafe {
|
||||
let chain_ptr = &mut *chain;
|
||||
let chain = chain_ptr.take();
|
||||
drop(Box::from_raw(chain.unwrap().as_ptr()))
|
||||
};
|
||||
}
|
||||
}
|
5
librashader-capi/src/runtime/mtl/mod.rs
Normal file
5
librashader-capi/src/runtime/mtl/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
//! C API for the librashader Metal Runtime (`libra_mtl_*`).
|
||||
|
||||
mod filter_chain;
|
||||
|
||||
pub use filter_chain::*;
|
|
@ -3,20 +3,21 @@ use crate::ctypes::{
|
|||
};
|
||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||
use crate::ffi::extern_fn;
|
||||
use librashader::runtime::vk::{VulkanImage, VulkanInstance};
|
||||
use librashader::runtime::vk::{
|
||||
FilterChain, FilterChainOptions, FrameOptions, VulkanImage, VulkanInstance,
|
||||
};
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{c_char, c_void};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
use std::slice;
|
||||
|
||||
use librashader::runtime::vk::capi::options::FilterChainOptionsVulkan;
|
||||
use librashader::runtime::vk::capi::options::FrameOptionsVulkan;
|
||||
use librashader::runtime::FilterChainParameters;
|
||||
use librashader::runtime::{Size, Viewport};
|
||||
|
||||
use ash::vk;
|
||||
|
||||
use crate::LIBRASHADER_API_VERSION;
|
||||
pub use ash::vk::PFN_vkGetInstanceProcAddr;
|
||||
|
||||
/// A Vulkan instance function loader that the Vulkan filter chain needs to be initialized with.
|
||||
|
@ -87,17 +88,24 @@ impl From<libra_device_vk_t> for VulkanInstance {
|
|||
#[derive(Default, Debug, Clone)]
|
||||
pub struct frame_vk_opt_t {
|
||||
/// The librashader API version.
|
||||
pub version: usize,
|
||||
pub version: LIBRASHADER_API_VERSION,
|
||||
/// Whether or not to clear the history buffers.
|
||||
pub clear_history: bool,
|
||||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 4 = 270deg.
|
||||
pub rotation: u32,
|
||||
/// The total number of subframes ran. Default is 1.
|
||||
pub total_subframes: u32,
|
||||
/// The current sub frame. Default is 1.
|
||||
pub current_subframe: u32,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FrameOptionsVulkan => frame_vk_opt_t {
|
||||
impl FrameOptions => frame_vk_opt_t {
|
||||
0 => [clear_history, frame_direction];
|
||||
1 => [rotation, total_subframes, current_subframe]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,22 +114,23 @@ config_struct! {
|
|||
#[derive(Default, Debug, Clone)]
|
||||
pub struct filter_chain_vk_opt_t {
|
||||
/// The librashader API version.
|
||||
pub version: usize,
|
||||
pub version: LIBRASHADER_API_VERSION,
|
||||
/// The number of frames in flight to keep. If zero, defaults to three.
|
||||
pub frames_in_flight: u32,
|
||||
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
|
||||
pub force_no_mipmaps: bool,
|
||||
/// Use explicit render pass objects It is recommended if possible to use dynamic rendering,
|
||||
/// Use dynamic rendering over explicit render pass objects.
|
||||
/// It is recommended if possible to use dynamic rendering,
|
||||
/// because render-pass mode will create new framebuffers per pass.
|
||||
pub use_render_pass: bool,
|
||||
pub use_dynamic_rendering: bool,
|
||||
/// Disable the shader object cache. Shaders will be
|
||||
/// recompiled rather than loaded from the cache.
|
||||
pub disable_cache: bool,
|
||||
}
|
||||
|
||||
config_struct! {
|
||||
impl FilterChainOptionsVulkan => filter_chain_vk_opt_t {
|
||||
0 => [frames_in_flight, force_no_mipmaps, use_render_pass, disable_cache];
|
||||
impl FilterChainOptions => filter_chain_vk_opt_t {
|
||||
0 => [frames_in_flight, force_no_mipmaps, use_dynamic_rendering, disable_cache];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +142,7 @@ extern_fn! {
|
|||
///
|
||||
/// ## Safety:
|
||||
/// - The handles provided in `vulkan` must be valid for the command buffers that
|
||||
/// `libra_vk_filter_chain_frame` will write to. Namely, the VkDevice must have been
|
||||
/// `libra_vk_filter_chain_frame` will write to.
|
||||
/// created with the `VK_KHR_dynamic_rendering` extension.
|
||||
/// - `preset` must be either null, or valid and aligned.
|
||||
/// - `options` must be either null, or valid and aligned.
|
||||
|
@ -161,7 +170,7 @@ extern_fn! {
|
|||
let options = options.map(FromUninit::from_uninit);
|
||||
|
||||
unsafe {
|
||||
let chain = librashader::runtime::vk::capi::FilterChainVulkan::load_from_preset(*preset, vulkan, options.as_ref())?;
|
||||
let chain = FilterChain::load_from_preset(*preset, vulkan, options.as_ref())?;
|
||||
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
chain,
|
||||
|
@ -179,8 +188,7 @@ extern_fn! {
|
|||
///
|
||||
/// ## Safety:
|
||||
/// - The handles provided in `vulkan` must be valid for the command buffers that
|
||||
/// `libra_vk_filter_chain_frame` will write to. Namely, the VkDevice must have been
|
||||
/// created with the `VK_KHR_dynamic_rendering` extension.
|
||||
/// `libra_vk_filter_chain_frame` will write to.
|
||||
/// - `preset` must be either null, or valid and aligned.
|
||||
/// - `options` must be either null, or valid and aligned.
|
||||
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
||||
|
@ -212,7 +220,7 @@ extern_fn! {
|
|||
let options = options.map(FromUninit::from_uninit);
|
||||
|
||||
unsafe {
|
||||
let chain = librashader::runtime::vk::capi::FilterChainVulkan::load_from_preset_deferred(*preset,
|
||||
let chain = FilterChain::load_from_preset_deferred(*preset,
|
||||
vulkan,
|
||||
command_buffer,
|
||||
options.as_ref())?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! C API for the librashader OpenGL Runtime (`libra_vk_*`).
|
||||
//! C API for the librashader Vulkan Runtime (`libra_vk_*`).
|
||||
|
||||
mod filter_chain;
|
||||
pub use filter_chain::*;
|
||||
|
|
|
@ -13,7 +13,11 @@ pub type LIBRASHADER_ABI_VERSION = usize;
|
|||
/// versions must remain backwards compatible.
|
||||
/// ## API Versions
|
||||
/// - API version 0: 0.1.0
|
||||
pub const LIBRASHADER_CURRENT_VERSION: LIBRASHADER_API_VERSION = 0;
|
||||
/// - API version 1: 0.2.0
|
||||
/// - Added rotation, total_subframes, current_subframes to frame options
|
||||
/// - Added preset context API
|
||||
/// - Added Metal runtime API
|
||||
pub const LIBRASHADER_CURRENT_VERSION: LIBRASHADER_API_VERSION = 1;
|
||||
|
||||
/// The current version of the librashader ABI.
|
||||
/// Used by the loader to check ABI compatibility.
|
||||
|
|
263
librashader-capi/src/wildcard.rs
Normal file
263
librashader-capi/src/wildcard.rs
Normal file
|
@ -0,0 +1,263 @@
|
|||
//! librashader preset wildcard context C API (`libra_preset_ctx_*`).
|
||||
|
||||
use crate::ctypes::{libra_preset_ctx_t, LIBRA_PRESET_CTX_ORIENTATION, LIBRA_PRESET_CTX_RUNTIME};
|
||||
use crate::error::{assert_non_null, assert_some_ptr};
|
||||
|
||||
use librashader::presets::context::{
|
||||
ContextItem, PresetExtension, Rotation, ShaderExtension, WildcardContext,
|
||||
};
|
||||
use std::ffi::{c_char, CStr};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use crate::ffi::extern_fn;
|
||||
|
||||
const _: () = crate::assert_thread_safe::<WildcardContext>();
|
||||
|
||||
extern_fn! {
|
||||
/// Create a wildcard context
|
||||
///
|
||||
/// The C API does not allow directly setting certain variables
|
||||
///
|
||||
/// - `PRESET_DIR` and `PRESET` are inferred on preset creation.
|
||||
/// - `VID-DRV-SHADER-EXT` and `VID-DRV-PRESET-EXT` are always set to `slang` and `slangp` for librashader.
|
||||
/// - `VID-FINAL-ROT` is automatically calculated as the sum of `VID-USER-ROT` and `CORE-REQ-ROT` if either are present.
|
||||
///
|
||||
/// These automatically inferred variables, as well as all other variables can be overridden with
|
||||
/// `libra_preset_ctx_set_param`, but the expected string values must be provided.
|
||||
/// See https://github.com/libretro/RetroArch/pull/15023 for a list of expected string values.
|
||||
///
|
||||
/// No variables can be removed once added to the context, however subsequent calls to set the same
|
||||
/// variable will overwrite the expected variable.
|
||||
/// ## Safety
|
||||
/// - `out` must be either null, or an aligned pointer to an uninitialized or invalid `libra_preset_ctx_t`.
|
||||
/// ## Returns
|
||||
/// - If any parameters are null, `out` is unchanged, and this function returns `LIBRA_ERR_INVALID_PARAMETER`.
|
||||
fn libra_preset_ctx_create(
|
||||
out: *mut MaybeUninit<libra_preset_ctx_t>
|
||||
) {
|
||||
assert_non_null!(out);
|
||||
|
||||
unsafe {
|
||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||
WildcardContext::new(),
|
||||
)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Free the wildcard context.
|
||||
///
|
||||
/// If `context` is null, this function does nothing. The resulting value in `context` then becomes
|
||||
/// null.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be a valid and aligned pointer to a `libra_preset_ctx_t`
|
||||
fn libra_preset_ctx_free(context: *mut libra_preset_ctx_t) {
|
||||
assert_non_null!(context);
|
||||
unsafe {
|
||||
let context_ptr = &mut *context;
|
||||
let context = context_ptr.take();
|
||||
drop(Box::from_raw(context.unwrap().as_ptr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the core name (`CORE`) variable in the context
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||
fn libra_preset_ctx_set_core_name(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
name: *const c_char,
|
||||
) |name|; mut |context| {
|
||||
let name = unsafe {
|
||||
CStr::from_ptr(name)
|
||||
};
|
||||
|
||||
let name = name.to_str()?;
|
||||
assert_some_ptr!(mut context);
|
||||
|
||||
context.append_item(ContextItem::CoreName(String::from(name)));
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the content directory (`CONTENT-DIR`) variable in the context.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||
fn libra_preset_ctx_set_content_dir(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
name: *const c_char,
|
||||
) |name|; mut |context| {
|
||||
let name = unsafe {
|
||||
CStr::from_ptr(name)
|
||||
};
|
||||
|
||||
let name = name.to_str()?;
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::ContentDirectory(String::from(name)));
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set a custom string variable in context.
|
||||
///
|
||||
/// If the path contains this variable when loading a preset, it will be replaced with the
|
||||
/// provided contents.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||
/// - `value` must be null or a valid and aligned pointer to a string.
|
||||
fn libra_preset_ctx_set_param(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
name: *const c_char,
|
||||
value: *const c_char,
|
||||
) |name, value|; mut |context| {
|
||||
let name = unsafe {
|
||||
CStr::from_ptr(name)
|
||||
};
|
||||
let name = name.to_str()?;
|
||||
|
||||
let value = unsafe {
|
||||
CStr::from_ptr(value)
|
||||
};
|
||||
let value = value.to_str()?;
|
||||
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::ExternContext(String::from(name), String::from(value)));
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the graphics runtime (`VID-DRV`) variable in the context.
|
||||
///
|
||||
/// Note that librashader only supports the following runtimes.
|
||||
///
|
||||
/// - Vulkan
|
||||
/// - GLCore
|
||||
/// - Direct3D11
|
||||
/// - Direct3D12
|
||||
///
|
||||
/// This will also set the appropriate video driver extensions.
|
||||
///
|
||||
/// For librashader, `VID-DRV-SHADER-EXT` and `VID-DRV-PRESET-EXT` are always `slang` and `slangp`.
|
||||
/// To override this, use `libra_preset_ctx_set_param`.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||
fn libra_preset_ctx_set_runtime(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: LIBRA_PRESET_CTX_RUNTIME,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
|
||||
context.append_item(ContextItem::VideoDriverPresetExtension(
|
||||
PresetExtension::Slangp,
|
||||
));
|
||||
context.append_item(ContextItem::VideoDriverShaderExtension(
|
||||
ShaderExtension::Slang,
|
||||
));
|
||||
context.append_item(ContextItem::VideoDriver(value.into()));
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the core requested rotation (`CORE-REQ-ROT`) variable in the context.
|
||||
///
|
||||
/// Rotation is represented by quarter rotations around the unit circle.
|
||||
/// For example. `0` = 0deg, `1` = 90deg, `2` = 180deg, `3` = 270deg, `4` = 0deg.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
fn libra_preset_ctx_set_core_rotation(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: u32,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::CoreRequestedRotation(Rotation::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the user rotation (`VID-USER-ROT`) variable in the context.
|
||||
///
|
||||
/// Rotation is represented by quarter rotations around the unit circle.
|
||||
/// For example. `0` = 0deg, `1` = 90deg, `2` = 180deg, `3` = 270deg, `4` = 0deg.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
fn libra_preset_ctx_set_user_rotation(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: u32,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::UserRotation(Rotation::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the screen orientation (`SCREEN-ORIENT`) variable in the context.
|
||||
///
|
||||
/// Orientation is represented by quarter rotations around the unit circle.
|
||||
/// For example. `0` = 0deg, `1` = 90deg, `2` = 180deg, `3` = 270deg, `4` = 0deg.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
fn libra_preset_ctx_set_screen_orientation(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: u32,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::ScreenOrientation(Rotation::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set whether or not to allow rotation (`VID-ALLOW-CORE-ROT`) variable in the context.
|
||||
///
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
fn libra_preset_ctx_set_allow_rotation(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: bool,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::AllowCoreRotation(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the view aspect orientation (`VIEW-ASPECT-ORIENT`) variable in the context.
|
||||
//////
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
fn libra_preset_ctx_set_view_aspect_orientation(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: LIBRA_PRESET_CTX_ORIENTATION,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::ViewAspectOrientation(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
extern_fn! {
|
||||
/// Set the core aspect orientation (`CORE-ASPECT-ORIENT`) variable in the context.
|
||||
//////
|
||||
/// ## Safety
|
||||
/// - `context` must be null or a valid and aligned pointer to a `libra_preset_ctx_t`.
|
||||
fn libra_preset_ctx_set_core_aspect_orientation(
|
||||
context: *mut libra_preset_ctx_t,
|
||||
value: LIBRA_PRESET_CTX_ORIENTATION,
|
||||
) mut |context| {
|
||||
assert_some_ptr!(mut context);
|
||||
context.append_item(ContextItem::CoreAspectOrientation(value.into()))
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ name = "librashader-common"
|
|||
edition = "2021"
|
||||
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -14,26 +14,35 @@ description = "RetroArch shaders for all."
|
|||
[features]
|
||||
default = []
|
||||
opengl = ["gl"]
|
||||
d3d9 = ["windows"]
|
||||
d3d11 = ["windows", "dxgi"]
|
||||
d3d12 = ["windows", "dxgi"]
|
||||
dxgi = ["windows"]
|
||||
vulkan = ["ash"]
|
||||
wgpu = ["wgpu-types"]
|
||||
metal = ["icrate"]
|
||||
|
||||
[dependencies]
|
||||
gl = { version = "0.14.0", optional = true }
|
||||
ash = { version = "0.37", optional = true }
|
||||
wgpu-types = { version = "0.19.0", optional = true }
|
||||
|
||||
wgpu-types = { version = "0.20.0", optional = true }
|
||||
num-traits = "0.2.15"
|
||||
rustc-hash = "1.1.0"
|
||||
halfbrown = "0.2.4"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
optional = true
|
||||
version = "0.48.0"
|
||||
workspace = true
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dxgi_Common",
|
||||
"Win32_Graphics_Direct3D",
|
||||
"Win32_Graphics_Direct3D9",
|
||||
"Win32_Graphics_Direct3D11",
|
||||
"Win32_Graphics_Direct3D12",
|
||||
]
|
||||
|
||||
[target.'cfg(target_vendor="apple")'.dependencies.icrate]
|
||||
optional = true
|
||||
version = "0.1.0"
|
||||
features = ["Metal", "Metal_all"]
|
||||
|
|
77
librashader-common/src/d3d9.rs
Normal file
77
librashader-common/src/d3d9.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use crate::{FilterMode, ImageFormat, WrapMode};
|
||||
use windows::Win32::Graphics::Direct3D9;
|
||||
//
|
||||
impl From<ImageFormat> for Direct3D9::D3DFORMAT {
|
||||
fn from(format: ImageFormat) -> Self {
|
||||
match format {
|
||||
ImageFormat::Unknown => Direct3D9::D3DFMT_UNKNOWN,
|
||||
ImageFormat::R8Unorm => Direct3D9::D3DFMT_R8G8B8,
|
||||
ImageFormat::R8Uint => Direct3D9::D3DFMT_R8G8B8,
|
||||
ImageFormat::R8Sint => Direct3D9::D3DFMT_R8G8B8,
|
||||
ImageFormat::R8G8B8A8Unorm => Direct3D9::D3DFMT_A8R8G8B8,
|
||||
ImageFormat::R8G8B8A8Uint => Direct3D9::D3DFMT_A8R8G8B8,
|
||||
ImageFormat::R8G8B8A8Sint => Direct3D9::D3DFMT_A8R8G8B8,
|
||||
ImageFormat::R8G8B8A8Srgb => Direct3D9::D3DFMT_A8R8G8B8,
|
||||
ImageFormat::A2B10G10R10UnormPack32 => Direct3D9::D3DFMT_A2B10G10R10,
|
||||
ImageFormat::A2B10G10R10UintPack32 => Direct3D9::D3DFMT_A2B10G10R10,
|
||||
ImageFormat::R16Sfloat => Direct3D9::D3DFMT_R16F,
|
||||
ImageFormat::R16G16Uint => Direct3D9::D3DFMT_G16R16,
|
||||
ImageFormat::R16G16Sint => Direct3D9::D3DFMT_G16R16,
|
||||
ImageFormat::R16G16Sfloat => Direct3D9::D3DFMT_G16R16F,
|
||||
ImageFormat::R16G16B16A16Uint => Direct3D9::D3DFMT_A16B16G16R16,
|
||||
ImageFormat::R16G16B16A16Sint => Direct3D9::D3DFMT_A16B16G16R16,
|
||||
ImageFormat::R16G16B16A16Sfloat => Direct3D9::D3DFMT_A16B16G16R16F,
|
||||
ImageFormat::R32Sfloat => Direct3D9::D3DFMT_R32F,
|
||||
_ => Direct3D9::D3DFMT_UNKNOWN,
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
impl From<Direct3D9::D3DFORMAT> for ImageFormat {
|
||||
fn from(format: Direct3D9::D3DFORMAT) -> Self {
|
||||
match format {
|
||||
Direct3D9::D3DFMT_R8G8B8 => ImageFormat::R8Unorm,
|
||||
Direct3D9::D3DFMT_A8R8G8B8 => ImageFormat::R8G8B8A8Unorm,
|
||||
Direct3D9::D3DFMT_A2B10G10R10 => ImageFormat::A2B10G10R10UnormPack32,
|
||||
Direct3D9::D3DFMT_R16F => ImageFormat::R16Sfloat,
|
||||
Direct3D9::D3DFMT_G16R16 => ImageFormat::R16G16Uint,
|
||||
Direct3D9::D3DFMT_G16R16F => ImageFormat::R16G16Sfloat,
|
||||
Direct3D9::D3DFMT_A16B16G16R16 => ImageFormat::R16G16B16A16Uint,
|
||||
Direct3D9::D3DFMT_A16B16G16R16F => ImageFormat::R16G16B16A16Sfloat,
|
||||
Direct3D9::D3DFMT_R32F => ImageFormat::R32Sfloat,
|
||||
_ => ImageFormat::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapMode> for Direct3D9::D3DTEXTUREADDRESS {
|
||||
fn from(value: WrapMode) -> Self {
|
||||
match value {
|
||||
WrapMode::ClampToBorder => Direct3D9::D3DTADDRESS_BORDER,
|
||||
WrapMode::ClampToEdge => Direct3D9::D3DTADDRESS_CLAMP,
|
||||
WrapMode::Repeat => Direct3D9::D3DTADDRESS_WRAP,
|
||||
WrapMode::MirroredRepeat => Direct3D9::D3DTADDRESS_MIRROR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilterMode> for Direct3D9::D3DTEXTUREFILTER {
|
||||
fn from(value: FilterMode) -> Self {
|
||||
match value {
|
||||
FilterMode::Linear => Direct3D9::D3DFILTER_LINEAR,
|
||||
FilterMode::Nearest => Direct3D9::D3DFILTER_NEAREST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl FilterMode {
|
||||
// /// Get the mipmap filtering mode for the given combination.
|
||||
// pub fn d3d9_mip(&self, mip: FilterMode) -> Direct3D9::D3DTEXTUREFILTER {
|
||||
// match (self, mip) {
|
||||
// (FilterMode::Linear, FilterMode::Linear) => Direct3D9::D3DFILTER_MIPLINEAR,
|
||||
// (FilterMode::Linear, FilterMode::Nearest) => Direct3D9::D3DFILTER_LINEARMIPNEAREST,
|
||||
// (FilterMode::Nearest, FilterMode::Linear) => Direct3D9::D3DFILTER_MIPNEAREST,
|
||||
// _ => Direct3D9::D3DFILTER_MIPNEAREST
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -16,6 +16,10 @@ pub mod wgpu;
|
|||
#[cfg(all(target_os = "windows", feature = "dxgi"))]
|
||||
pub mod dxgi;
|
||||
|
||||
/// Direct3D 9 common conversions.
|
||||
#[cfg(all(target_os = "windows", feature = "d3d9"))]
|
||||
pub mod d3d9;
|
||||
|
||||
/// Direct3D 11 common conversions.
|
||||
#[cfg(all(target_os = "windows", feature = "d3d11"))]
|
||||
pub mod d3d11;
|
||||
|
@ -24,14 +28,26 @@ pub mod d3d11;
|
|||
#[cfg(all(target_os = "windows", feature = "d3d12"))]
|
||||
pub mod d3d12;
|
||||
|
||||
#[cfg(all(target_vendor = "apple", feature = "metal"))]
|
||||
pub mod metal;
|
||||
|
||||
mod viewport;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod map;
|
||||
|
||||
pub use viewport::Viewport;
|
||||
|
||||
use num_traits::AsPrimitive;
|
||||
use std::convert::Infallible;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ShaderStorage {
|
||||
Path(std::path::PathBuf),
|
||||
String(String),
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
/// Supported image formats for textures.
|
||||
|
|
5
librashader-common/src/map.rs
Normal file
5
librashader-common/src/map.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// Fast optimized hash map type for small sets.
|
||||
pub type FastHashMap<K, V> =
|
||||
halfbrown::SizedHashMap<K, V, core::hash::BuildHasherDefault<rustc_hash::FxHasher>, 32>;
|
||||
|
||||
pub use halfbrown;
|
92
librashader-common/src/metal.rs
Normal file
92
librashader-common/src/metal.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use crate::{FilterMode, ImageFormat, Size, WrapMode};
|
||||
use icrate::Metal;
|
||||
|
||||
impl From<ImageFormat> for Metal::MTLPixelFormat {
|
||||
fn from(format: ImageFormat) -> Self {
|
||||
match format {
|
||||
ImageFormat::Unknown => 0 as Metal::MTLPixelFormat,
|
||||
ImageFormat::R8Unorm => Metal::MTLPixelFormatR8Unorm,
|
||||
ImageFormat::R8Uint => Metal::MTLPixelFormatR8Uint,
|
||||
ImageFormat::R8Sint => Metal::MTLPixelFormatR8Sint,
|
||||
ImageFormat::R8G8Unorm => Metal::MTLPixelFormatRG8Unorm,
|
||||
ImageFormat::R8G8Uint => Metal::MTLPixelFormatRG8Uint,
|
||||
ImageFormat::R8G8Sint => Metal::MTLPixelFormatRG8Sint,
|
||||
ImageFormat::R8G8B8A8Unorm => Metal::MTLPixelFormatRGBA8Unorm,
|
||||
ImageFormat::R8G8B8A8Uint => Metal::MTLPixelFormatRGBA8Uint,
|
||||
ImageFormat::R8G8B8A8Sint => Metal::MTLPixelFormatRGBA8Sint,
|
||||
ImageFormat::R8G8B8A8Srgb => Metal::MTLPixelFormatRGBA8Unorm_sRGB,
|
||||
ImageFormat::A2B10G10R10UnormPack32 => Metal::MTLPixelFormatRGB10A2Unorm,
|
||||
ImageFormat::A2B10G10R10UintPack32 => Metal::MTLPixelFormatRGB10A2Uint,
|
||||
ImageFormat::R16Uint => Metal::MTLPixelFormatR16Uint,
|
||||
ImageFormat::R16Sint => Metal::MTLPixelFormatR16Sint,
|
||||
ImageFormat::R16Sfloat => Metal::MTLPixelFormatR16Float,
|
||||
ImageFormat::R16G16Uint => Metal::MTLPixelFormatRG16Uint,
|
||||
ImageFormat::R16G16Sint => Metal::MTLPixelFormatRG16Sint,
|
||||
ImageFormat::R16G16Sfloat => Metal::MTLPixelFormatRG16Float,
|
||||
ImageFormat::R16G16B16A16Uint => Metal::MTLPixelFormatRGBA16Uint,
|
||||
ImageFormat::R16G16B16A16Sint => Metal::MTLPixelFormatRGBA16Sint,
|
||||
ImageFormat::R16G16B16A16Sfloat => Metal::MTLPixelFormatRGBA16Float,
|
||||
ImageFormat::R32Uint => Metal::MTLPixelFormatR32Uint,
|
||||
ImageFormat::R32Sint => Metal::MTLPixelFormatR32Sint,
|
||||
ImageFormat::R32Sfloat => Metal::MTLPixelFormatR32Float,
|
||||
ImageFormat::R32G32Uint => Metal::MTLPixelFormatRG32Uint,
|
||||
ImageFormat::R32G32Sint => Metal::MTLPixelFormatRG32Sint,
|
||||
ImageFormat::R32G32Sfloat => Metal::MTLPixelFormatRG32Float,
|
||||
ImageFormat::R32G32B32A32Uint => Metal::MTLPixelFormatRGBA32Uint,
|
||||
ImageFormat::R32G32B32A32Sint => Metal::MTLPixelFormatRGBA32Sint,
|
||||
ImageFormat::R32G32B32A32Sfloat => Metal::MTLPixelFormatRGBA32Float,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Metal::MTLViewport> for Size<u32> {
|
||||
fn from(value: Metal::MTLViewport) -> Self {
|
||||
Size {
|
||||
width: value.width as u32,
|
||||
height: value.height as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Size<u32>> for Metal::MTLViewport {
|
||||
fn from(value: Size<u32>) -> Self {
|
||||
Metal::MTLViewport {
|
||||
originX: 0.0,
|
||||
originY: 0.0,
|
||||
width: value.width as f64,
|
||||
height: value.height as f64,
|
||||
znear: -1.0,
|
||||
zfar: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WrapMode> for Metal::MTLSamplerAddressMode {
|
||||
fn from(value: WrapMode) -> Self {
|
||||
match value {
|
||||
WrapMode::ClampToBorder => Metal::MTLSamplerAddressModeClampToBorderColor,
|
||||
WrapMode::ClampToEdge => Metal::MTLSamplerAddressModeClampToEdge,
|
||||
WrapMode::Repeat => Metal::MTLSamplerAddressModeRepeat,
|
||||
WrapMode::MirroredRepeat => Metal::MTLSamplerAddressModeMirrorRepeat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilterMode> for Metal::MTLSamplerMinMagFilter {
|
||||
fn from(value: FilterMode) -> Self {
|
||||
match value {
|
||||
FilterMode::Linear => Metal::MTLSamplerMinMagFilterLinear,
|
||||
_ => Metal::MTLSamplerMipFilterNearest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FilterMode {
|
||||
/// Get the mipmap filtering mode for the given combination.
|
||||
pub fn mtl_mip(&self, mip: FilterMode) -> Metal::MTLSamplerMipFilter {
|
||||
match self {
|
||||
FilterMode::Linear => Metal::MTLSamplerMipFilterLinear,
|
||||
FilterMode::Nearest => Metal::MTLSamplerMipFilterNearest,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -94,58 +94,6 @@ impl From<wgpu_types::Extent3d> for Size<u32> {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl From<Size<u32>> for vk::Extent2D {
|
||||
// fn from(value: Size<u32>) -> Self {
|
||||
// vk::Extent2D {
|
||||
// width: value.width,
|
||||
// height: value.height,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
//
|
||||
// impl From<vk::Extent2D> for Size<u32> {
|
||||
// fn from(value: vk::Extent2D) -> Self {
|
||||
// Size {
|
||||
// width: value.width,
|
||||
// height: value.height,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<vk::Viewport> for Size<u32> {
|
||||
// fn from(value: vk::Viewport) -> Self {
|
||||
// Size {
|
||||
// width: value.width as u32,
|
||||
// height: value.height as u32,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<&vk::Viewport> for Size<u32> {
|
||||
// fn from(value: &vk::Viewport) -> Self {
|
||||
// Size {
|
||||
// width: value.width as u32,
|
||||
// height: value.height as u32,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl From<Size<u32>> for vk::Viewport {
|
||||
// fn from(value: Size<u32>) -> Self {
|
||||
// vk::Viewport {
|
||||
// x: 0.0,
|
||||
// y: 0.0,
|
||||
// width: value.width as f32,
|
||||
// height: value.height as f32,
|
||||
// min_depth: 0.0,
|
||||
// max_depth: 1.0,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<FilterMode> for wgpu_types::FilterMode {
|
||||
fn from(value: FilterMode) -> Self {
|
||||
match value {
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "librashader-preprocess"
|
|||
edition = "2021"
|
||||
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -14,8 +14,7 @@ description = "RetroArch shaders for all."
|
|||
[dependencies]
|
||||
thiserror = "1.0.37"
|
||||
nom = "7.1.1"
|
||||
librashader-common = { path = "../librashader-common", version = "0.2.0-beta.7" }
|
||||
rustc-hash = "1.1.0"
|
||||
librashader-common = { path = "../librashader-common", version = "0.2.7" }
|
||||
encoding_rs = "0.8.31"
|
||||
|
||||
[features]
|
||||
|
@ -23,6 +22,5 @@ default = [ "line_directives" ]
|
|||
line_directives = []
|
||||
|
||||
[dev-dependencies]
|
||||
librashader-presets = "0.1.0-rc.3"
|
||||
glob = "0.3.1"
|
||||
rayon = "1.6.1"
|
||||
|
|
|
@ -15,9 +15,8 @@ mod stage;
|
|||
|
||||
use crate::include::read_source;
|
||||
pub use error::*;
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_common::ImageFormat;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::path::Path;
|
||||
|
||||
/// The source file for a single shader pass.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -32,7 +31,7 @@ pub struct ShaderSource {
|
|||
pub name: Option<String>,
|
||||
|
||||
/// The list of shader parameters found in the shader source.
|
||||
pub parameters: FxHashMap<String, ShaderParameter>,
|
||||
pub parameters: FastHashMap<String, ShaderParameter>,
|
||||
|
||||
/// The image format the shader expects.
|
||||
pub format: ImageFormat,
|
||||
|
@ -58,8 +57,8 @@ pub struct ShaderParameter {
|
|||
impl ShaderSource {
|
||||
/// Load the source file at the given path, resolving includes relative to the location of the
|
||||
/// source file.
|
||||
pub fn load(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> {
|
||||
load_shader_source(path)
|
||||
pub fn load(file: &librashader_common::ShaderStorage) -> Result<ShaderSource, PreprocessError> {
|
||||
load_shader_source(file)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,11 +77,17 @@ impl SourceOutput for String {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_shader_source(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> {
|
||||
let source = read_source(path)?;
|
||||
pub(crate) fn load_shader_source(
|
||||
file: &librashader_common::ShaderStorage,
|
||||
) -> Result<ShaderSource, PreprocessError> {
|
||||
let source = match file {
|
||||
librashader_common::ShaderStorage::Path(path) => read_source(path)?,
|
||||
librashader_common::ShaderStorage::String(s) => s.to_string(),
|
||||
};
|
||||
|
||||
let meta = pragma::parse_pragma_meta(&source)?;
|
||||
let text = stage::process_stages(&source)?;
|
||||
let parameters = FxHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p)));
|
||||
let parameters = FastHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p)));
|
||||
|
||||
Ok(ShaderSource {
|
||||
vertex: text.vertex,
|
||||
|
|
|
@ -132,4 +132,16 @@ mod test {
|
|||
step: 0.25
|
||||
}, parse_parameter_string(r#"#pragma parameter exc "orizontal correction hack (games where players stay at center)" 0.0 -10.0 10.0 0.25"#).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_parameter_pragma_test() {
|
||||
assert_eq!(ShaderParameter {
|
||||
id: "HSM_CORE_RES_SAMPLING_MULT_SCANLINE_DIR".to_string(),
|
||||
description: " Scanline Dir Multiplier".to_string(),
|
||||
initial: 100.0,
|
||||
minimum: 25.0,
|
||||
maximum: 1600.0,
|
||||
step: 25.0
|
||||
}, parse_parameter_string(r#"#pragma parameter HSM_CORE_RES_SAMPLING_MULT_SCANLINE_DIR " Scanline Dir Multiplier" 100 25 1600 25"#).unwrap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,10 @@ pub(crate) fn process_stages(source: &str) -> Result<ShaderOutput, PreprocessErr
|
|||
continue;
|
||||
}
|
||||
|
||||
if line.starts_with("#pragma name ") || line.starts_with("#pragma format ") {
|
||||
if line.starts_with("#pragma name ")
|
||||
|| line.starts_with("#pragma format ")
|
||||
|| line.starts_with("#pragma parameter ")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
use glob::glob;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use librashader_presets::ShaderPreset;
|
||||
use rayon::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn preprocess_all_slang_presets_parsed() {
|
||||
for entry in glob("../test/slang-shaders/**/*.slangp").unwrap() {
|
||||
if let Ok(path) = entry {
|
||||
if let Ok(preset) = ShaderPreset::try_parse(&path) {
|
||||
preset.shaders.into_par_iter().for_each(|shader| {
|
||||
if let Err(e) = ShaderSource::load(&shader.name) {
|
||||
println!(
|
||||
"Failed to preprocess shader {} from preset {}: {:?}",
|
||||
shader.name.display(),
|
||||
path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ name = "librashader-presets"
|
|||
edition = "2021"
|
||||
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -15,8 +15,14 @@ description = "RetroArch shaders for all."
|
|||
thiserror = "1.0.37"
|
||||
nom = "7.1.1"
|
||||
nom_locate = "4.0.0"
|
||||
librashader-common = { path = "../librashader-common", version = "0.2.0-beta.7" }
|
||||
librashader-common = { path = "../librashader-common", version = "0.2.7" }
|
||||
num-traits = "0.2"
|
||||
once_cell = "1"
|
||||
# we don't need unicode
|
||||
regex = { version = "1", default-features = false, features = ["perf"] }
|
||||
|
||||
rustversion = "1.0"
|
||||
os_str_bytes = { version = "6", features = ["conversions"] }
|
||||
|
||||
[features]
|
||||
parse_legacy_glsl = []
|
||||
|
|
459
librashader-presets/src/context.rs
Normal file
459
librashader-presets/src/context.rs
Normal file
|
@ -0,0 +1,459 @@
|
|||
// pub use librashader_presets_context::*;
|
||||
|
||||
//! Shader preset wildcard replacement context handling.
|
||||
//!
|
||||
//! Implements wildcard replacement of shader paths specified in
|
||||
//! [RetroArch#15023](https://github.com/libretro/RetroArch/pull/15023).
|
||||
use librashader_common::map::FastHashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::bytes::Regex;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::ops::Add;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
/// Valid video driver or runtime. This list is non-exhaustive.
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum VideoDriver {
|
||||
/// None (`null`)
|
||||
None = 0,
|
||||
/// OpenGL Core (`glcore`)
|
||||
GlCore,
|
||||
/// Legacy OpenGL (`gl`)
|
||||
Gl,
|
||||
/// Vulkan (`vulkan`)
|
||||
Vulkan,
|
||||
/// Direct3D 9 (`d3d9_hlsl`)
|
||||
Direct3D9Hlsl,
|
||||
/// Direct3D 11 (`d3d11`)
|
||||
Direct3D11,
|
||||
/// Direct3D12 (`d3d12`)
|
||||
Direct3D12,
|
||||
/// Metal (`metal`)
|
||||
Metal,
|
||||
}
|
||||
|
||||
impl Display for VideoDriver {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VideoDriver::None => f.write_str("null"),
|
||||
VideoDriver::GlCore => f.write_str("glcore"),
|
||||
VideoDriver::Gl => f.write_str("gl"),
|
||||
VideoDriver::Vulkan => f.write_str("vulkan"),
|
||||
VideoDriver::Direct3D11 => f.write_str("d3d11"),
|
||||
VideoDriver::Direct3D9Hlsl => f.write_str("d3d9_hlsl"),
|
||||
VideoDriver::Direct3D12 => f.write_str("d3d12"),
|
||||
VideoDriver::Metal => f.write_str("metal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid extensions for shader extensions.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ShaderExtension {
|
||||
/// `.slang`
|
||||
Slang = 0,
|
||||
/// `.glsl`
|
||||
Glsl,
|
||||
/// `.cg`
|
||||
Cg,
|
||||
}
|
||||
|
||||
impl Display for ShaderExtension {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ShaderExtension::Slang => f.write_str("slang"),
|
||||
ShaderExtension::Glsl => f.write_str("glsl"),
|
||||
ShaderExtension::Cg => f.write_str("cg"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid extensions for shader presets
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum PresetExtension {
|
||||
/// `.slangp`
|
||||
Slangp = 0,
|
||||
/// `.glslp`
|
||||
Glslp,
|
||||
/// `.cgp`
|
||||
Cgp,
|
||||
}
|
||||
|
||||
impl Display for PresetExtension {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PresetExtension::Slangp => f.write_str("slangp"),
|
||||
PresetExtension::Glslp => f.write_str("glslp"),
|
||||
PresetExtension::Cgp => f.write_str("cgp"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rotation of the viewport.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Rotation {
|
||||
/// Zero
|
||||
Zero = 0,
|
||||
/// 90 degrees
|
||||
Right = 1,
|
||||
/// 180 degrees
|
||||
Straight = 2,
|
||||
/// 270 degrees
|
||||
Reflex = 3,
|
||||
}
|
||||
|
||||
impl From<u32> for Rotation {
|
||||
fn from(value: u32) -> Self {
|
||||
let value = value % 4;
|
||||
match value {
|
||||
0 => Rotation::Zero,
|
||||
1 => Rotation::Right,
|
||||
2 => Rotation::Straight,
|
||||
3 => Rotation::Reflex,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Rotation {
|
||||
type Output = Rotation;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
let lhs = self as u32;
|
||||
let out = lhs + rhs as u32;
|
||||
Rotation::from(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rotation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Rotation::Zero => f.write_str("0"),
|
||||
Rotation::Right => f.write_str("90"),
|
||||
Rotation::Straight => f.write_str("180"),
|
||||
Rotation::Reflex => f.write_str("270"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Orientation of aspect ratios
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Orientation {
|
||||
/// Vertical orientation.
|
||||
Vertical = 0,
|
||||
/// Horizontal orientation.
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
impl Display for Orientation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Orientation::Vertical => f.write_str("VERT"),
|
||||
Orientation::Horizontal => f.write_str("HORZ"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An item representing a variable that can be replaced in a path preset.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ContextItem {
|
||||
/// The content directory of the game (`CONTENT-DIR`)
|
||||
ContentDirectory(String),
|
||||
/// The name of the libretro core (`CORE`)
|
||||
CoreName(String),
|
||||
/// The filename of the game (`GAME`)
|
||||
GameName(String),
|
||||
/// The name of the preset (`PRESET`)
|
||||
Preset(String),
|
||||
/// The name of the preset directory (`PRESET_DIR`)
|
||||
PresetDirectory(String),
|
||||
/// The video driver (runtime) (`VID-DRV`)
|
||||
VideoDriver(VideoDriver),
|
||||
/// The extension of shader types supported by the driver (`VID-DRV-SHADER-EXT`)
|
||||
VideoDriverShaderExtension(ShaderExtension),
|
||||
/// The extension of shader presets supported by the driver (`VID-DRV-PRESET-EXT`)
|
||||
VideoDriverPresetExtension(PresetExtension),
|
||||
/// The rotation that the core is requesting (`CORE-REQ-ROT`)
|
||||
CoreRequestedRotation(Rotation),
|
||||
/// Whether or not to allow core-requested rotation (`VID-ALLOW-CORE-ROT`)
|
||||
AllowCoreRotation(bool),
|
||||
/// The rotation the user is requesting (`VID-USER-ROT`)
|
||||
UserRotation(Rotation),
|
||||
/// The final rotation (`VID-FINAL-ROT`) calculated by the sum of `VID-USER-ROT` and `CORE-REQ-ROT`
|
||||
FinalRotation(Rotation),
|
||||
/// The user-adjusted screen orientation (`SCREEN-ORIENT`)
|
||||
ScreenOrientation(Rotation),
|
||||
/// The orientation of the viewport aspect ratio (`VIEW-ASPECT-ORIENT`)
|
||||
ViewAspectOrientation(Orientation),
|
||||
/// The orientation of the aspect ratio requested by the core (`CORE-ASPECT-ORIENT`)
|
||||
CoreAspectOrientation(Orientation),
|
||||
/// An external, arbitrary context variable.
|
||||
ExternContext(String, String),
|
||||
}
|
||||
|
||||
impl ContextItem {
|
||||
fn toggle_str(v: bool) -> &'static str {
|
||||
if v {
|
||||
"ON"
|
||||
} else {
|
||||
"OFF"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key(&self) -> &str {
|
||||
match self {
|
||||
ContextItem::ContentDirectory(_) => "CONTENT-DIR",
|
||||
ContextItem::CoreName(_) => "CORE",
|
||||
ContextItem::GameName(_) => "GAME",
|
||||
ContextItem::Preset(_) => "PRESET",
|
||||
ContextItem::PresetDirectory(_) => "PRESET_DIR",
|
||||
ContextItem::VideoDriver(_) => "VID-DRV",
|
||||
ContextItem::CoreRequestedRotation(_) => "CORE-REQ-ROT",
|
||||
ContextItem::AllowCoreRotation(_) => "VID-ALLOW-CORE-ROT",
|
||||
ContextItem::UserRotation(_) => "VID-USER-ROT",
|
||||
ContextItem::FinalRotation(_) => "VID-FINAL-ROT",
|
||||
ContextItem::ScreenOrientation(_) => "SCREEN-ORIENT",
|
||||
ContextItem::ViewAspectOrientation(_) => "VIEW-ASPECT-ORIENT",
|
||||
ContextItem::CoreAspectOrientation(_) => "CORE-ASPECT-ORIENT",
|
||||
ContextItem::VideoDriverShaderExtension(_) => "VID-DRV-SHADER-EXT",
|
||||
ContextItem::VideoDriverPresetExtension(_) => "VID-DRV-PRESET-EXT",
|
||||
ContextItem::ExternContext(key, _) => key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ContextItem {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ContextItem::ContentDirectory(v) => f.write_str(v),
|
||||
ContextItem::CoreName(v) => f.write_str(v),
|
||||
ContextItem::GameName(v) => f.write_str(v),
|
||||
ContextItem::Preset(v) => f.write_str(v),
|
||||
ContextItem::PresetDirectory(v) => f.write_str(v),
|
||||
ContextItem::VideoDriver(v) => f.write_fmt(format_args!("{}", v)),
|
||||
ContextItem::CoreRequestedRotation(v) => {
|
||||
f.write_fmt(format_args!("{}-{}", self.key(), v))
|
||||
}
|
||||
ContextItem::AllowCoreRotation(v) => f.write_fmt(format_args!(
|
||||
"{}-{}",
|
||||
self.key(),
|
||||
ContextItem::toggle_str(*v)
|
||||
)),
|
||||
ContextItem::UserRotation(v) => f.write_fmt(format_args!("{}-{}", self.key(), v)),
|
||||
ContextItem::FinalRotation(v) => f.write_fmt(format_args!("{}-{}", self.key(), v)),
|
||||
ContextItem::ScreenOrientation(v) => f.write_fmt(format_args!("{}-{}", self.key(), v)),
|
||||
ContextItem::ViewAspectOrientation(v) => {
|
||||
f.write_fmt(format_args!("{}-{}", self.key(), v))
|
||||
}
|
||||
ContextItem::CoreAspectOrientation(v) => {
|
||||
f.write_fmt(format_args!("{}-{}", self.key(), v))
|
||||
}
|
||||
ContextItem::VideoDriverShaderExtension(v) => f.write_fmt(format_args!("{}", v)),
|
||||
ContextItem::VideoDriverPresetExtension(v) => f.write_fmt(format_args!("{}", v)),
|
||||
ContextItem::ExternContext(_, v) => f.write_fmt(format_args!("{}", v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A preset wildcard context.
|
||||
///
|
||||
/// Any items added after will have higher priority
|
||||
/// when passed to the shader preset parser.
|
||||
///
|
||||
/// When passed to the preset parser, the preset parser
|
||||
/// will automatically add inferred items at lowest priority.
|
||||
///
|
||||
/// Any items added by the user will override the automatically
|
||||
/// inferred items.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WildcardContext(VecDeque<ContextItem>);
|
||||
|
||||
impl WildcardContext {
|
||||
/// Create a new wildcard context.
|
||||
pub fn new() -> Self {
|
||||
Self(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Prepend an item to the context builder.
|
||||
pub fn prepend_item(&mut self, item: ContextItem) {
|
||||
self.0.push_front(item);
|
||||
}
|
||||
|
||||
/// Append an item to the context builder.
|
||||
/// The new item will take precedence over all items added before it.
|
||||
pub fn append_item(&mut self, item: ContextItem) {
|
||||
self.0.push_back(item);
|
||||
}
|
||||
|
||||
/// Prepend sensible defaults for the given video driver.
|
||||
///
|
||||
/// Any values added, either previously or afterwards will not be overridden.
|
||||
pub fn add_video_driver_defaults(&mut self, video_driver: VideoDriver) {
|
||||
self.prepend_item(ContextItem::VideoDriverPresetExtension(
|
||||
PresetExtension::Slangp,
|
||||
));
|
||||
self.prepend_item(ContextItem::VideoDriverShaderExtension(
|
||||
ShaderExtension::Slang,
|
||||
));
|
||||
self.prepend_item(ContextItem::VideoDriver(video_driver));
|
||||
}
|
||||
|
||||
/// Prepend default entries from the path of the preset.
|
||||
///
|
||||
/// Any values added, either previously or afterwards will not be overridden.
|
||||
pub fn add_path_defaults(&mut self, path: impl AsRef<Path>) {
|
||||
let path = path.as_ref();
|
||||
if let Some(preset_name) = path.file_stem() {
|
||||
let preset_name = preset_name.to_string_lossy();
|
||||
self.prepend_item(ContextItem::Preset(preset_name.into()))
|
||||
}
|
||||
|
||||
if let Some(preset_dir_name) = path.parent().and_then(|p| {
|
||||
if !p.is_dir() {
|
||||
return None;
|
||||
};
|
||||
p.file_name()
|
||||
}) {
|
||||
let preset_dir_name = preset_dir_name.to_string_lossy();
|
||||
self.prepend_item(ContextItem::PresetDirectory(preset_dir_name.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_hashmap(mut self) -> FastHashMap<String, String> {
|
||||
let mut map = FastHashMap::default();
|
||||
let last_user_rot = self
|
||||
.0
|
||||
.iter()
|
||||
.rfind(|i| matches!(i, ContextItem::UserRotation(_)));
|
||||
let last_core_rot = self
|
||||
.0
|
||||
.iter()
|
||||
.rfind(|i| matches!(i, ContextItem::CoreRequestedRotation(_)));
|
||||
|
||||
let final_rot = match (last_core_rot, last_user_rot) {
|
||||
(Some(ContextItem::UserRotation(u)), None) => Some(ContextItem::FinalRotation(*u)),
|
||||
(None, Some(ContextItem::CoreRequestedRotation(c))) => {
|
||||
Some(ContextItem::FinalRotation(*c))
|
||||
}
|
||||
(Some(ContextItem::UserRotation(u)), Some(ContextItem::CoreRequestedRotation(c))) => {
|
||||
Some(ContextItem::FinalRotation(*u + *c))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(final_rot) = final_rot {
|
||||
self.prepend_item(final_rot);
|
||||
}
|
||||
|
||||
for item in self.0 {
|
||||
map.insert(String::from(item.key()), item.to_string());
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
#[rustversion::since(1.74)]
|
||||
pub(crate) fn apply_context(path: &mut PathBuf, context: &FastHashMap<String, String>) {
|
||||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
static WILDCARD_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("\\$([A-Z-_]+)\\$").unwrap());
|
||||
if context.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Don't want to do any extra work if there's no match.
|
||||
if !WILDCARD_REGEX.is_match(path.as_os_str().as_encoded_bytes()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_path = PathBuf::with_capacity(path.capacity());
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Normal(path) => {
|
||||
let haystack = path.as_encoded_bytes();
|
||||
|
||||
let replaced =
|
||||
WILDCARD_REGEX.replace_all(haystack, |caps: ®ex::bytes::Captures| {
|
||||
let Some(name) = caps.get(1) else {
|
||||
return caps[0].to_vec();
|
||||
};
|
||||
|
||||
let Ok(key) = std::str::from_utf8(name.as_bytes()) else {
|
||||
return caps[0].to_vec();
|
||||
};
|
||||
if let Some(replacement) = context.get(key) {
|
||||
return OsString::from(replacement.to_string()).into_encoded_bytes();
|
||||
}
|
||||
return caps[0].to_vec();
|
||||
});
|
||||
|
||||
// SAFETY: The original source is valid encoded bytes, and our replacement is
|
||||
// valid encoded bytes. This upholds the safety requirements of `from_encoded_bytes_unchecked`.
|
||||
new_path.push(unsafe { OsStr::from_encoded_bytes_unchecked(&replaced.as_ref()) })
|
||||
}
|
||||
_ => new_path.push(component),
|
||||
}
|
||||
}
|
||||
|
||||
// If no wildcards are found within the path, or the path after replacing the wildcards does not exist on disk, the path returned will be unaffected.
|
||||
if let Ok(true) = new_path.try_exists() {
|
||||
*path = new_path;
|
||||
}
|
||||
}
|
||||
|
||||
#[rustversion::before(1.74)]
|
||||
pub(crate) fn apply_context(path: &mut PathBuf, context: &FastHashMap<String, String>) {
|
||||
use os_str_bytes::RawOsStr;
|
||||
static WILDCARD_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("\\$([A-Z-_]+)\\$").unwrap());
|
||||
if context.is_empty() {
|
||||
return;
|
||||
}
|
||||
let path_str = RawOsStr::new(path.as_os_str());
|
||||
let path_bytes = path_str.to_raw_bytes();
|
||||
// Don't want to do any extra work if there's no match.
|
||||
if !WILDCARD_REGEX.is_match(&path_bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_path = PathBuf::with_capacity(path.capacity());
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Normal(path) => {
|
||||
let haystack = RawOsStr::new(path);
|
||||
let haystack = haystack.to_raw_bytes();
|
||||
|
||||
let replaced =
|
||||
WILDCARD_REGEX.replace_all(&haystack, |caps: ®ex::bytes::Captures| {
|
||||
let Some(name) = caps.get(1) else {
|
||||
return caps[0].to_vec();
|
||||
};
|
||||
|
||||
let Ok(key) = std::str::from_utf8(name.as_bytes()) else {
|
||||
return caps[0].to_vec();
|
||||
};
|
||||
if let Some(replacement) = context.get(key) {
|
||||
return RawOsStr::from_str(replacement).to_raw_bytes().to_vec();
|
||||
}
|
||||
return caps[0].to_vec();
|
||||
});
|
||||
|
||||
// SAFETY: The original source is valid encoded bytes, and our replacement is
|
||||
// valid encoded bytes. This upholds the safety requirements of `from_encoded_bytes_unchecked`.
|
||||
new_path.push(RawOsStr::assert_cow_from_raw_bytes(&replaced.as_ref()).to_os_str())
|
||||
}
|
||||
_ => new_path.push(component),
|
||||
}
|
||||
}
|
||||
|
||||
// If no wildcards are found within the path, or the path after replacing the wildcards does not exist on disk, the path returned will be unaffected.
|
||||
if let Ok(true) = new_path.try_exists() {
|
||||
*path = new_path;
|
||||
}
|
||||
}
|
|
@ -148,7 +148,7 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::MakeExtractIf;
|
||||
use crate::extract_if::MakeExtractIf;
|
||||
#[test]
|
||||
fn drain_filter_empty() {
|
||||
let mut vec: Vec<i32> = vec![];
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
//!
|
||||
//! Re-exported as [`librashader::presets`](https://docs.rs/librashader/latest/librashader/presets/index.html).
|
||||
|
||||
#![allow(stable_features)]
|
||||
#![allow(unstable_name_collisions)]
|
||||
|
||||
pub mod context;
|
||||
mod error;
|
||||
mod extract_if;
|
||||
mod parse;
|
||||
mod preset;
|
||||
|
||||
pub use context::WildcardContext;
|
||||
pub use error::*;
|
||||
pub use preset::*;
|
||||
|
|
404
librashader-presets/src/parse/context.rs
Normal file
404
librashader-presets/src/parse/context.rs
Normal file
|
@ -0,0 +1,404 @@
|
|||
//! Shader preset wildcard replacement context handling.
|
||||
//!
|
||||
//! Implements wildcard replacement of shader paths specified in
|
||||
//! [RetroArch#15023](https://github.com/libretro/RetroArch/pull/15023).
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::bytes::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::ops::Add;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
/// Valid video driver or runtime. This list is non-exhaustive.
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum VideoDriver {
|
||||
/// None (`null`)
|
||||
None = 0,
|
||||
/// OpenGL Core (`glcore`)
|
||||
GlCore,
|
||||
/// Legacy OpenGL (`gl`)
|
||||
Gl,
|
||||
/// Vulkan (`vulkan`)
|
||||
Vulkan,
|
||||
/// Direct3D 9 (`d3d9_hlsl`)
|
||||
Direct3D9Hlsl,
|
||||
/// Direct3D 11 (`d3d11`)
|
||||
Direct3D11,
|
||||
/// Direct3D12 (`d3d12`)
|
||||
Direct3D12,
|
||||
/// Metal (`metal`)
|
||||
Metal,
|
||||
}
|
||||
|
||||
impl Display for VideoDriver {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VideoDriver::None => f.write_str("null"),
|
||||
VideoDriver::GlCore => f.write_str("glcore"),
|
||||
VideoDriver::Gl => f.write_str("gl"),
|
||||
VideoDriver::Vulkan => f.write_str("vulkan"),
|
||||
VideoDriver::Direct3D11 => f.write_str("d3d11"),
|
||||
VideoDriver::Direct3D9Hlsl => f.write_str("d3d9_hlsl"),
|
||||
VideoDriver::Direct3D12 => f.write_str("d3d12"),
|
||||
VideoDriver::Metal => f.write_str("metal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid extensions for shader extensions.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ShaderExtension {
|
||||
/// `.slang`
|
||||
Slang = 0,
|
||||
/// `.glsl`
|
||||
Glsl,
|
||||
/// `.cg`
|
||||
Cg,
|
||||
}
|
||||
|
||||
impl Display for ShaderExtension {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ShaderExtension::Slang => f.write_str("slang"),
|
||||
ShaderExtension::Glsl => f.write_str("glsl"),
|
||||
ShaderExtension::Cg => f.write_str("cg"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid extensions for shader presets
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum PresetExtension {
|
||||
/// `.slangp`
|
||||
Slangp = 0,
|
||||
/// `.glslp`
|
||||
Glslp,
|
||||
/// `.cgp`
|
||||
Cgp,
|
||||
}
|
||||
|
||||
impl Display for PresetExtension {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
PresetExtension::Slangp => f.write_str("slangp"),
|
||||
PresetExtension::Glslp => f.write_str("glslp"),
|
||||
PresetExtension::Cgp => f.write_str("cgp"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rotation of the viewport.
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Rotation {
|
||||
/// Zero
|
||||
Zero = 0,
|
||||
/// 90 degrees
|
||||
Right = 1,
|
||||
/// 180 degrees
|
||||
Straight = 2,
|
||||
/// 270 degrees
|
||||
Reflex = 3,
|
||||
}
|
||||
|
||||
impl From<u32> for Rotation {
|
||||
fn from(value: u32) -> Self {
|
||||
let value = value % 4;
|
||||
match value {
|
||||
0 => Rotation::Zero,
|
||||
1 => Rotation::Right,
|
||||
2 => Rotation::Straight,
|
||||
3 => Rotation::Reflex,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Rotation {
|
||||
type Output = Rotation;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
let lhs = self as u32;
|
||||
let out = lhs + rhs as u32;
|
||||
Rotation::from(out)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rotation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Rotation::Zero => f.write_str("0"),
|
||||
Rotation::Right => f.write_str("90"),
|
||||
Rotation::Straight => f.write_str("180"),
|
||||
Rotation::Reflex => f.write_str("270"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Orientation of aspect ratios
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Orientation {
|
||||
/// Vertical orientation.
|
||||
Vertical = 0,
|
||||
/// Horizontal orientation.
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
impl Display for Orientation {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Orientation::Vertical => f.write_str("VERT"),
|
||||
Orientation::Horizontal => f.write_str("HORZ"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An item representing a variable that can be replaced in a path preset.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ContextItem {
|
||||
/// The content directory of the game (`CONTENT-DIR`)
|
||||
ContentDirectory(String),
|
||||
/// The name of the libretro core (`CORE`)
|
||||
CoreName(String),
|
||||
/// The filename of the game (`GAME`)
|
||||
GameName(String),
|
||||
/// The name of the preset (`PRESET`)
|
||||
Preset(String),
|
||||
/// The name of the preset directory (`PRESET_DIR`)
|
||||
PresetDirectory(String),
|
||||
/// The video driver (runtime) (`VID-DRV`)
|
||||
VideoDriver(VideoDriver),
|
||||
/// The extension of shader types supported by the driver (`VID-DRV-SHADER-EXT`)
|
||||
VideoDriverShaderExtension(ShaderExtension),
|
||||
/// The extension of shader presets supported by the driver (`VID-DRV-PRESET-EXT`)
|
||||
VideoDriverPresetExtension(PresetExtension),
|
||||
/// The rotation that the core is requesting (`CORE-REQ-ROT`)
|
||||
CoreRequestedRotation(Rotation),
|
||||
/// Whether or not to allow core-requested rotation (`VID-ALLOW-CORE-ROT`)
|
||||
AllowCoreRotation(bool),
|
||||
/// The rotation the user is requesting (`VID-USER-ROT`)
|
||||
UserRotation(Rotation),
|
||||
/// The final rotation (`VID-FINAL-ROT`) calculated by the sum of `VID-USER-ROT` and `CORE-REQ-ROT`
|
||||
FinalRotation(Rotation),
|
||||
/// The user-adjusted screen orientation (`SCREEN-ORIENT`)
|
||||
ScreenOrientation(Rotation),
|
||||
/// The orientation of the viewport aspect ratio (`VIEW-ASPECT-ORIENT`)
|
||||
ViewAspectOrientation(Orientation),
|
||||
/// The orientation of the aspect ratio requested by the core (`CORE-ASPECT-ORIENT`)
|
||||
CoreAspectOrientation(Orientation),
|
||||
/// An external, arbitrary context variable.
|
||||
ExternContext(String, String),
|
||||
}
|
||||
|
||||
impl ContextItem {
|
||||
fn toggle_str(v: bool) -> &'static str {
|
||||
if v {
|
||||
"ON"
|
||||
} else {
|
||||
"OFF"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key(&self) -> &str {
|
||||
match self {
|
||||
ContextItem::ContentDirectory(_) => "CONTENT-DIR",
|
||||
ContextItem::CoreName(_) => "CORE",
|
||||
ContextItem::GameName(_) => "GAME",
|
||||
ContextItem::Preset(_) => "PRESET",
|
||||
ContextItem::PresetDirectory(_) => "PRESET_DIR",
|
||||
ContextItem::VideoDriver(_) => "VID-DRV",
|
||||
ContextItem::CoreRequestedRotation(_) => "CORE-REQ-ROT",
|
||||
ContextItem::AllowCoreRotation(_) => "VID-ALLOW-CORE-ROT",
|
||||
ContextItem::UserRotation(_) => "VID-USER-ROT",
|
||||
ContextItem::FinalRotation(_) => "VID-FINAL-ROT",
|
||||
ContextItem::ScreenOrientation(_) => "SCREEN-ORIENT",
|
||||
ContextItem::ViewAspectOrientation(_) => "VIEW-ASPECT-ORIENT",
|
||||
ContextItem::CoreAspectOrientation(_) => "CORE-ASPECT-ORIENT",
|
||||
ContextItem::VideoDriverShaderExtension(_) => "VID-DRV-SHADER-EXT",
|
||||
ContextItem::VideoDriverPresetExtension(_) => "VID-DRV-PRESET-EXT",
|
||||
ContextItem::ExternContext(key, _) => key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ContextItem {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ContextItem::ContentDirectory(v) => f.write_str(v),
|
||||
ContextItem::CoreName(v) => f.write_str(v),
|
||||
ContextItem::GameName(v) => f.write_str(v),
|
||||
ContextItem::Preset(v) => f.write_str(v),
|
||||
ContextItem::PresetDirectory(v) => f.write_str(v),
|
||||
ContextItem::VideoDriver(v) => f.write_fmt(format_args!("{}", v)),
|
||||
ContextItem::CoreRequestedRotation(v) => {
|
||||
f.write_fmt(format_args!("{}-{}", self.key(), v))
|
||||
}
|
||||
ContextItem::AllowCoreRotation(v) => f.write_fmt(format_args!(
|
||||
"{}-{}",
|
||||
self.key(),
|
||||
ContextItem::toggle_str(*v)
|
||||
)),
|
||||
ContextItem::UserRotation(v) => f.write_fmt(format_args!("{}-{}", self.key(), v)),
|
||||
ContextItem::FinalRotation(v) => f.write_fmt(format_args!("{}-{}", self.key(), v)),
|
||||
ContextItem::ScreenOrientation(v) => f.write_fmt(format_args!("{}-{}", self.key(), v)),
|
||||
ContextItem::ViewAspectOrientation(v) => {
|
||||
f.write_fmt(format_args!("{}-{}", self.key(), v))
|
||||
}
|
||||
ContextItem::CoreAspectOrientation(v) => {
|
||||
f.write_fmt(format_args!("{}-{}", self.key(), v))
|
||||
}
|
||||
ContextItem::VideoDriverShaderExtension(v) => f.write_fmt(format_args!("{}", v)),
|
||||
ContextItem::VideoDriverPresetExtension(v) => f.write_fmt(format_args!("{}", v)),
|
||||
ContextItem::ExternContext(_, v) => f.write_fmt(format_args!("{}", v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A preset wildcard context.
|
||||
///
|
||||
/// Any items added after will have higher priority
|
||||
/// when passed to the shader preset parser.
|
||||
///
|
||||
/// When passed to the preset parser, the preset parser
|
||||
/// will automatically add inferred items at lowest priority.
|
||||
///
|
||||
/// Any items added by the user will override the automatically
|
||||
/// inferred items.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WildcardContext(VecDeque<ContextItem>);
|
||||
|
||||
impl WildcardContext {
|
||||
/// Create a new wildcard context.
|
||||
pub fn new() -> Self {
|
||||
Self(VecDeque::new())
|
||||
}
|
||||
|
||||
/// Prepend an item to the context builder.
|
||||
pub fn prepend_item(&mut self, item: ContextItem) {
|
||||
self.0.push_front(item);
|
||||
}
|
||||
|
||||
/// Append an item to the context builder.
|
||||
/// The new item will take precedence over all items added before it.
|
||||
pub fn append_item(&mut self, item: ContextItem) {
|
||||
self.0.push_back(item);
|
||||
}
|
||||
|
||||
/// Prepend sensible defaults for the given video driver.
|
||||
///
|
||||
/// Any values added, either previously or afterwards will not be overridden.
|
||||
pub fn add_video_driver_defaults(&mut self, video_driver: VideoDriver) {
|
||||
self.prepend_item(ContextItem::VideoDriverPresetExtension(
|
||||
PresetExtension::Slangp,
|
||||
));
|
||||
self.prepend_item(ContextItem::VideoDriverShaderExtension(
|
||||
ShaderExtension::Slang,
|
||||
));
|
||||
self.prepend_item(ContextItem::VideoDriver(video_driver));
|
||||
}
|
||||
|
||||
/// Prepend default entries from the path of the preset.
|
||||
///
|
||||
/// Any values added, either previously or afterwards will not be overridden.
|
||||
pub fn add_path_defaults(&mut self, path: impl AsRef<Path>) {
|
||||
let path = path.as_ref();
|
||||
if let Some(preset_name) = path.file_stem() {
|
||||
let preset_name = preset_name.to_string_lossy();
|
||||
self.prepend_item(ContextItem::Preset(preset_name.into()))
|
||||
}
|
||||
|
||||
if let Some(preset_dir_name) = path.parent().and_then(|p| {
|
||||
if !p.is_dir() {
|
||||
return None;
|
||||
};
|
||||
p.file_name()
|
||||
}) {
|
||||
let preset_dir_name = preset_dir_name.to_string_lossy();
|
||||
self.prepend_item(ContextItem::PresetDirectory(preset_dir_name.into()))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_hashmap(mut self) -> FxHashMap<String, String> {
|
||||
let mut map = FxHashMap::default();
|
||||
let last_user_rot = self
|
||||
.0
|
||||
.iter()
|
||||
.rfind(|i| matches!(i, ContextItem::UserRotation(_)));
|
||||
let last_core_rot = self
|
||||
.0
|
||||
.iter()
|
||||
.rfind(|i| matches!(i, ContextItem::CoreRequestedRotation(_)));
|
||||
|
||||
let final_rot = match (last_core_rot, last_user_rot) {
|
||||
(Some(ContextItem::UserRotation(u)), None) => Some(ContextItem::FinalRotation(*u)),
|
||||
(None, Some(ContextItem::CoreRequestedRotation(c))) => {
|
||||
Some(ContextItem::FinalRotation(*c))
|
||||
}
|
||||
(Some(ContextItem::UserRotation(u)), Some(ContextItem::CoreRequestedRotation(c))) => {
|
||||
Some(ContextItem::FinalRotation(*u + *c))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(final_rot) = final_rot {
|
||||
self.prepend_item(final_rot);
|
||||
}
|
||||
|
||||
for item in self.0 {
|
||||
map.insert(String::from(item.key()), item.to_string());
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_context(path: &mut PathBuf, context: &FxHashMap<String, String>) {
|
||||
static WILDCARD_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new("\\$([A-Z-_]+)\\$").unwrap());
|
||||
if context.is_empty() {
|
||||
return;
|
||||
}
|
||||
// Don't want to do any extra work if there's no match.
|
||||
if !WILDCARD_REGEX.is_match(path.as_os_str().as_encoded_bytes()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut new_path = PathBuf::with_capacity(path.capacity());
|
||||
for component in path.components() {
|
||||
match component {
|
||||
Component::Normal(path) => {
|
||||
let haystack = path.as_encoded_bytes();
|
||||
let replaced =
|
||||
WILDCARD_REGEX.replace_all(haystack, |caps: ®ex::bytes::Captures| {
|
||||
let Some(name) = caps.get(1) else {
|
||||
return caps[0].to_vec();
|
||||
};
|
||||
|
||||
let Ok(key) = std::str::from_utf8(name.as_bytes()) else {
|
||||
return caps[0].to_vec();
|
||||
};
|
||||
if let Some(replacement) = context.get(key) {
|
||||
return OsString::from(replacement.to_string()).into_encoded_bytes();
|
||||
}
|
||||
return caps[0].to_vec();
|
||||
});
|
||||
|
||||
// SAFETY: The original source is valid encoded bytes, and our replacement is
|
||||
// valid encoded bytes. This upholds the safety requirements of `from_encoded_bytes_unchecked`.
|
||||
new_path.push(unsafe { OsStr::from_encoded_bytes_unchecked(&replaced.as_ref()) })
|
||||
}
|
||||
_ => new_path.push(component),
|
||||
}
|
||||
}
|
||||
|
||||
// If no wildcards are found within the path, or the path after replacing the wildcards does not exist on disk, the path returned will be unaffected.
|
||||
if let Ok(true) = new_path.try_exists() {
|
||||
*path = new_path;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ mod value;
|
|||
pub(crate) type Span<'a> = LocatedSpan<&'a str>;
|
||||
pub(crate) use token::Token;
|
||||
|
||||
use crate::context::{VideoDriver, WildcardContext};
|
||||
use crate::error::ParsePresetError;
|
||||
use crate::parse::preset::resolve_values;
|
||||
use crate::parse::value::parse_preset;
|
||||
|
@ -21,8 +22,38 @@ pub(crate) fn remove_if<T>(values: &mut Vec<T>, f: impl FnMut(&T) -> bool) -> Op
|
|||
|
||||
impl ShaderPreset {
|
||||
/// Try to parse the shader preset at the given path.
|
||||
///
|
||||
/// This will add path defaults to the wildcard resolution context.
|
||||
pub fn try_parse(path: impl AsRef<Path>) -> Result<ShaderPreset, ParsePresetError> {
|
||||
let values = parse_preset(path)?;
|
||||
let mut context = WildcardContext::new();
|
||||
context.add_path_defaults(path.as_ref());
|
||||
let values = parse_preset(path, WildcardContext::new())?;
|
||||
Ok(resolve_values(values))
|
||||
}
|
||||
|
||||
/// Try to parse the shader preset at the given path.
|
||||
///
|
||||
/// This will add path and driver defaults to the wildcard resolution context.
|
||||
pub fn try_parse_with_driver_context(
|
||||
path: impl AsRef<Path>,
|
||||
driver: VideoDriver,
|
||||
) -> Result<ShaderPreset, ParsePresetError> {
|
||||
let mut context = WildcardContext::new();
|
||||
context.add_path_defaults(path.as_ref());
|
||||
context.add_video_driver_defaults(driver);
|
||||
let values = parse_preset(path, context)?;
|
||||
Ok(resolve_values(values))
|
||||
}
|
||||
|
||||
/// Try to parse the shader preset at the given path, with the exact provided context.
|
||||
///
|
||||
/// This function does not change any of the values in the provided context, except calculating `VID-FINAL-ROT`
|
||||
/// if `CORE-REQ-ROT` and `VID-USER-ROT` is present.
|
||||
pub fn try_parse_with_context(
|
||||
path: impl AsRef<Path>,
|
||||
context: WildcardContext,
|
||||
) -> Result<ShaderPreset, ParsePresetError> {
|
||||
let values = parse_preset(path, context)?;
|
||||
Ok(resolve_values(values))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
|
|||
|
||||
let shader = ShaderPassConfig {
|
||||
id,
|
||||
name,
|
||||
name: librashader_common::ShaderStorage::Path(name),
|
||||
alias: shader_values.iter().find_map(|f| match f {
|
||||
Value::Alias(_, value) => Some(value.to_string()),
|
||||
_ => None,
|
||||
|
|
|
@ -171,7 +171,7 @@ pub fn do_lex(input: &str) -> Result<Vec<Token>, ParsePresetError> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::parse::token::{do_lex, single_comment};
|
||||
use crate::parse::token::single_comment;
|
||||
|
||||
#[test]
|
||||
fn parses_single_line_comment() {
|
||||
|
|
|
@ -10,12 +10,14 @@ use nom::IResult;
|
|||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
use crate::parse::token::do_lex;
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_common::{FilterMode, WrapMode};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::context::{apply_context, WildcardContext};
|
||||
use crate::extract_if::MakeExtractIf;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -150,9 +152,11 @@ fn parse_indexed_key<'a>(key: &'static str, input: Span<'a>) -> IResult<Span<'a>
|
|||
|
||||
pub const SHADER_MAX_REFERENCE_DEPTH: usize = 16;
|
||||
|
||||
// prereq: root_path must be contextualized
|
||||
fn load_child_reference_strings(
|
||||
root_references: Vec<PathBuf>,
|
||||
root_path: impl AsRef<Path>,
|
||||
context: &FastHashMap<String, String>,
|
||||
) -> Result<Vec<(PathBuf, String)>, ParsePresetError> {
|
||||
let root_path = root_path.as_ref();
|
||||
|
||||
|
@ -161,13 +165,14 @@ fn load_child_reference_strings(
|
|||
let root_references = vec![(root_path.to_path_buf(), root_references)];
|
||||
let mut root_references = VecDeque::from(root_references);
|
||||
// search needs to be depth first to allow for overrides.
|
||||
while let Some((reference_root, referenced_paths)) = root_references.pop_front() {
|
||||
while let Some((mut reference_root, referenced_paths)) = root_references.pop_front() {
|
||||
if reference_depth > SHADER_MAX_REFERENCE_DEPTH {
|
||||
return Err(ParsePresetError::ExceededReferenceDepth);
|
||||
}
|
||||
// enter the current root
|
||||
reference_depth += 1;
|
||||
// canonicalize current root
|
||||
apply_context(&mut reference_root, context);
|
||||
let reference_root = reference_root
|
||||
.canonicalize()
|
||||
.map_err(|e| ParsePresetError::IOError(reference_root.to_path_buf(), e))?;
|
||||
|
@ -176,8 +181,10 @@ fn load_child_reference_strings(
|
|||
// println!("Resolving {referenced_paths:?} against {reference_root:?}.");
|
||||
|
||||
for path in referenced_paths {
|
||||
let mut path = reference_root
|
||||
.join(path.clone())
|
||||
let mut path = reference_root.join(path.clone());
|
||||
apply_context(&mut path, context);
|
||||
|
||||
let mut path = path
|
||||
.canonicalize()
|
||||
.map_err(|e| ParsePresetError::IOError(path.clone(), e))?;
|
||||
// println!("Opening {:?}", path);
|
||||
|
@ -204,8 +211,16 @@ fn load_child_reference_strings(
|
|||
Ok(reference_strings.into())
|
||||
}
|
||||
|
||||
pub fn parse_preset(path: impl AsRef<Path>) -> Result<Vec<Value>, ParsePresetError> {
|
||||
pub(crate) fn parse_preset(
|
||||
path: impl AsRef<Path>,
|
||||
context: WildcardContext,
|
||||
) -> Result<Vec<Value>, ParsePresetError> {
|
||||
let path = path.as_ref();
|
||||
let mut path = path.to_path_buf();
|
||||
let context = context.to_hashmap();
|
||||
|
||||
apply_context(&mut path, &context);
|
||||
|
||||
let path = path
|
||||
.canonicalize()
|
||||
.map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?;
|
||||
|
@ -216,12 +231,14 @@ pub fn parse_preset(path: impl AsRef<Path>) -> Result<Vec<Value>, ParsePresetErr
|
|||
.map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?;
|
||||
|
||||
let tokens = super::token::do_lex(&contents)?;
|
||||
parse_values(tokens, path)
|
||||
parse_values(tokens, path, context)
|
||||
}
|
||||
|
||||
// prereq: root_path must be contextualized
|
||||
pub fn parse_values(
|
||||
mut tokens: Vec<Token>,
|
||||
root_path: impl AsRef<Path>,
|
||||
context: FastHashMap<String, String>,
|
||||
) -> Result<Vec<Value>, ParsePresetError> {
|
||||
let mut root_path = root_path.as_ref().to_path_buf();
|
||||
if root_path.is_relative() {
|
||||
|
@ -239,7 +256,9 @@ pub fn parse_values(
|
|||
.collect();
|
||||
|
||||
// unfortunately we need to lex twice because there's no way to know the references ahead of time.
|
||||
let child_strings = load_child_reference_strings(references, &root_path)?;
|
||||
// the returned references should have context applied
|
||||
|
||||
let child_strings = load_child_reference_strings(references, &root_path, &context)?;
|
||||
let mut all_tokens: Vec<(&Path, Vec<Token>)> = Vec::new();
|
||||
|
||||
for (path, string) in child_strings.iter() {
|
||||
|
@ -596,13 +615,14 @@ pub fn parse_values(
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::parse::value::parse_preset;
|
||||
use crate::WildcardContext;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
pub fn parse_basic() {
|
||||
let root =
|
||||
PathBuf::from("../test/slang-shaders/bezel/Mega_Bezel/Presets/Base_CRT_Presets/MBZ__3__STD__MEGATRON-NTSC.slangp");
|
||||
let basic = parse_preset(root);
|
||||
let basic = parse_preset(root, WildcardContext::new());
|
||||
eprintln!("{basic:?}");
|
||||
assert!(basic.is_ok());
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ pub struct ShaderPassConfig {
|
|||
/// The index of the shader pass relative to its parent preset.
|
||||
pub id: i32,
|
||||
/// The fully qualified path to the shader pass source file.
|
||||
pub name: PathBuf,
|
||||
pub name: librashader_common::ShaderStorage,
|
||||
/// The alias of the shader pass if available.
|
||||
pub alias: Option<String>,
|
||||
/// The filtering mode that this shader pass should expect.
|
||||
|
@ -63,6 +63,8 @@ pub enum ScaleType {
|
|||
Absolute,
|
||||
/// Scale by the size of the viewport.
|
||||
Viewport,
|
||||
/// Scale by the size of the original input quad.
|
||||
Original,
|
||||
}
|
||||
|
||||
/// The scaling factor for framebuffer scaling.
|
||||
|
@ -119,6 +121,7 @@ impl FromStr for ScaleType {
|
|||
"source" => Ok(ScaleType::Input),
|
||||
"viewport" => Ok(ScaleType::Viewport),
|
||||
"absolute" => Ok(ScaleType::Absolute),
|
||||
"original" => Ok(ScaleType::Original),
|
||||
_ => Err(ParsePresetError::InvalidScaleType(s.to_string())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use glob::glob;
|
||||
use librashader_presets::context::{ContextItem, VideoDriver, WildcardContext};
|
||||
use librashader_presets::ShaderPreset;
|
||||
|
||||
#[test]
|
||||
|
@ -17,3 +18,17 @@ fn parses_problematic() {
|
|||
let path = "../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_NDS_DREZ/NDS-[DREZ]-[Native]-[ADV]-[Guest]-[Night].slangp";
|
||||
ShaderPreset::try_parse(path).expect(&format!("Failed to parse {}", path));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_wildcard() {
|
||||
let path =
|
||||
"../test/shaders_slang/bezel/Mega_Bezel/resource/wildcard-examples/Preset-01-Core.slangp";
|
||||
let mut context = WildcardContext::new();
|
||||
|
||||
context.add_video_driver_defaults(VideoDriver::Vulkan);
|
||||
|
||||
context.append_item(ContextItem::CoreName(String::from("image display")));
|
||||
|
||||
ShaderPreset::try_parse_with_context(path, context)
|
||||
.expect(&format!("Failed to parse {}", path));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "librashader-reflect"
|
|||
edition = "2021"
|
||||
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -12,35 +12,37 @@ keywords = ["shader", "retroarch", "SPIR-V"]
|
|||
description = "RetroArch shaders for all."
|
||||
|
||||
[dependencies]
|
||||
glslang = "0.2"
|
||||
glslang = "0.3"
|
||||
bytemuck = "1.13.0"
|
||||
|
||||
thiserror = "1.0.37"
|
||||
bitflags = "1.3.2"
|
||||
rustc-hash = "1.1.0"
|
||||
bitflags = "2.4.2"
|
||||
|
||||
librashader-common = { path = "../librashader-common", version = "0.2.0-beta.7" }
|
||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.0-beta.7" }
|
||||
librashader-presets = { path = "../librashader-presets", version = "0.2.0-beta.7" }
|
||||
librashader-common = { path = "../librashader-common", version = "0.2.7" }
|
||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.7" }
|
||||
librashader-presets = { path = "../librashader-presets", version = "0.2.7" }
|
||||
|
||||
spirv_cross = { package = "librashader-spirv-cross", version = "0.23", optional = true }
|
||||
spirv_cross = { package = "librashader-spirv-cross", version = "0.25.1", optional = true }
|
||||
|
||||
naga = { version = "0.19.0", features = ["spv-in", "wgsl-out"], optional = true }
|
||||
rspirv = { version = "0.12.0+sdk-1.3.268.0", optional = true }
|
||||
spirv = { version = "0.3.0+sdk-1.3.268.0", optional = true}
|
||||
naga = { version = "0.20.0", optional = true }
|
||||
rspirv = { version = "0.12.0", optional = true }
|
||||
spirv = { version = "0.3.0", optional = true}
|
||||
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
||||
indexmap = { version = "2.1.0", features = [] }
|
||||
matches = { version = "0.1.10", features = [] }
|
||||
rustc-hash = "1.1.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.spirv-to-dxil]
|
||||
version = "0.4"
|
||||
version = "0.4.7"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["cross", "wgsl", "serialize"]
|
||||
dxil = ["cross", "spirv-to-dxil"]
|
||||
wgsl = ["cross", "naga", "spirv", "rspirv"]
|
||||
cross = [ "spirv_cross", "spirv_cross/glsl", "spirv_cross/hlsl" ]
|
||||
default = ["cross", "naga", "serialize", "wgsl", "msl"]
|
||||
dxil = ["spirv_cross/hlsl", "spirv-to-dxil"]
|
||||
wgsl = ["cross", "naga/wgsl-out", "spirv", "rspirv"]
|
||||
cross = [ "spirv_cross", "spirv_cross/glsl", "spirv_cross/hlsl", "spirv_cross/msl" ]
|
||||
naga = [ "rspirv", "spirv", "naga/spv-in", "naga/spv-out", "naga/wgsl-out", "naga/msl-out" ]
|
||||
serialize = [ "serde" ]
|
||||
msl = [ "spirv_cross/msl", "naga/msl-out" ]
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
use crate::back::targets::{GLSL, HLSL};
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation};
|
||||
use crate::error::ShaderReflectError;
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::reflect::cross::{CompiledProgram, GlslReflect, HlslReflect};
|
||||
use crate::reflect::ReflectShader;
|
||||
|
||||
/// The GLSL version to target.
|
||||
pub use spirv_cross::glsl::Version as GlslVersion;
|
||||
|
||||
/// The HLSL shader model version to target.
|
||||
pub use spirv_cross::hlsl::ShaderModel as HlslShaderModel;
|
||||
|
||||
/// The context for a GLSL compilation via spirv-cross.
|
||||
pub struct CrossGlslContext {
|
||||
/// A map of bindings of sampler names to binding locations.
|
||||
pub sampler_bindings: Vec<(String, u32)>,
|
||||
/// The compiled program artifact after compilation.
|
||||
pub artifact: CompiledProgram<spirv_cross::glsl::Target>,
|
||||
}
|
||||
|
||||
impl FromCompilation<GlslangCompilation> for GLSL {
|
||||
type Target = GLSL;
|
||||
type Options = GlslVersion;
|
||||
type Context = CrossGlslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = GlslVersion, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: GlslangCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: GlslReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The context for a HLSL compilation via spirv-cross.
|
||||
pub struct CrossHlslContext {
|
||||
/// The compiled HLSL program.
|
||||
pub artifact: CompiledProgram<spirv_cross::hlsl::Target>,
|
||||
}
|
||||
|
||||
impl FromCompilation<GlslangCompilation> for HLSL {
|
||||
type Target = HLSL;
|
||||
type Options = Option<HlslShaderModel>;
|
||||
type Context = CrossHlslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: GlslangCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: HlslReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,22 +1,22 @@
|
|||
use crate::back::spirv::WriteSpirV;
|
||||
use crate::back::targets::{OutputTarget, DXIL};
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation, ShaderCompilerOutput};
|
||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::cross::glsl::GlslReflect;
|
||||
use crate::reflect::cross::SpirvCross;
|
||||
use crate::reflect::ReflectShader;
|
||||
pub use spirv_to_dxil::DxilObject;
|
||||
pub use spirv_to_dxil::ShaderModel;
|
||||
use spirv_to_dxil::{
|
||||
PushConstantBufferConfig, RuntimeConfig, RuntimeDataBufferConfig, ShaderStage, ValidatorVersion,
|
||||
};
|
||||
|
||||
use crate::back::targets::{OutputTarget, DXIL};
|
||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::reflect::cross::GlslReflect;
|
||||
use crate::reflect::ReflectShader;
|
||||
|
||||
impl OutputTarget for DXIL {
|
||||
type Output = DxilObject;
|
||||
}
|
||||
|
||||
impl FromCompilation<GlslangCompilation> for DXIL {
|
||||
impl FromCompilation<SpirvCompilation, SpirvCross> for DXIL {
|
||||
type Target = DXIL;
|
||||
type Options = Option<ShaderModel>;
|
||||
type Context = ();
|
||||
|
@ -24,17 +24,15 @@ impl FromCompilation<GlslangCompilation> for DXIL {
|
|||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: GlslangCompilation,
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
let reflect = GlslReflect::try_from(&compile)?;
|
||||
let vertex = compile.vertex;
|
||||
let fragment = compile.fragment;
|
||||
Ok(CompilerBackend {
|
||||
// we can just reuse WriteSpirV as the backend.
|
||||
backend: WriteSpirV {
|
||||
reflect,
|
||||
vertex,
|
||||
fragment,
|
||||
vertex: compile.vertex,
|
||||
fragment: compile.fragment,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
35
librashader-reflect/src/back/glsl.rs
Normal file
35
librashader-reflect/src/back/glsl.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use crate::back::targets::GLSL;
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation};
|
||||
use crate::error::ShaderReflectError;
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::cross::{CompiledProgram, SpirvCross};
|
||||
use crate::reflect::ReflectShader;
|
||||
|
||||
/// The GLSL version to target.
|
||||
pub use spirv_cross::glsl::Version as GlslVersion;
|
||||
|
||||
use crate::reflect::cross::glsl::GlslReflect;
|
||||
|
||||
/// The context for a GLSL compilation via spirv-cross.
|
||||
pub struct CrossGlslContext {
|
||||
/// A map of bindings of sampler names to binding locations.
|
||||
pub sampler_bindings: Vec<(String, u32)>,
|
||||
/// The compiled program artifact after compilation.
|
||||
pub artifact: CompiledProgram<spirv_cross::glsl::Target>,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, SpirvCross> for GLSL {
|
||||
type Target = GLSL;
|
||||
type Options = GlslVersion;
|
||||
type Context = CrossGlslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = GlslVersion, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: GlslReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
143
librashader-reflect/src/back/hlsl.rs
Normal file
143
librashader-reflect/src/back/hlsl.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use crate::back::targets::HLSL;
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation};
|
||||
use crate::error::ShaderReflectError;
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::cross::hlsl::HlslReflect;
|
||||
use crate::reflect::cross::{CompiledProgram, SpirvCross};
|
||||
use crate::reflect::ReflectShader;
|
||||
|
||||
/// The HLSL shader model version to target.
|
||||
pub use spirv_cross::hlsl::ShaderModel as HlslShaderModel;
|
||||
|
||||
/// Buffer assignment information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HlslBufferAssignment {
|
||||
/// The name of the buffer
|
||||
pub name: String,
|
||||
/// The id of the buffer
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
/// Buffer assignment information
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct HlslBufferAssignments {
|
||||
/// Buffer assignment information for UBO
|
||||
pub ubo: Option<HlslBufferAssignment>,
|
||||
/// Buffer assignment information for Push
|
||||
pub push: Option<HlslBufferAssignment>,
|
||||
}
|
||||
|
||||
impl HlslBufferAssignments {
|
||||
fn find_mangled_id(mangled_name: &str) -> Option<u32> {
|
||||
if !mangled_name.starts_with("_") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Some(next_underscore) = mangled_name[1..].find("_") else {
|
||||
return None;
|
||||
};
|
||||
|
||||
mangled_name[1..next_underscore + 1].parse().ok()
|
||||
}
|
||||
|
||||
fn find_mangled_name(buffer_name: &str, uniform_name: &str, mangled_name: &str) -> bool {
|
||||
// name prependded
|
||||
if mangled_name[buffer_name.len()..].starts_with("_")
|
||||
&& &mangled_name[buffer_name.len() + 1..] == uniform_name
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the mangled name matches.
|
||||
pub fn contains_uniform(&self, uniform_name: &str, mangled_name: &str) -> bool {
|
||||
let is_likely_id_mangled = mangled_name.starts_with("_");
|
||||
if !mangled_name.ends_with(uniform_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(ubo) = &self.ubo {
|
||||
if is_likely_id_mangled {
|
||||
if let Some(id) = Self::find_mangled_id(mangled_name) {
|
||||
if id == ubo.id {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// name prependded
|
||||
if Self::find_mangled_name(&ubo.name, uniform_name, mangled_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(push) = &self.push {
|
||||
if is_likely_id_mangled {
|
||||
if let Some(id) = Self::find_mangled_id(mangled_name) {
|
||||
if id == push.id {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// name prependded
|
||||
if Self::find_mangled_name(&push.name, uniform_name, mangled_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// The context for a HLSL compilation via spirv-cross.
|
||||
pub struct CrossHlslContext {
|
||||
/// The compiled HLSL program.
|
||||
pub artifact: CompiledProgram<spirv_cross::hlsl::Target>,
|
||||
pub vertex_buffers: HlslBufferAssignments,
|
||||
pub fragment_buffers: HlslBufferAssignments,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, SpirvCross> for HLSL {
|
||||
type Target = HLSL;
|
||||
type Options = Option<HlslShaderModel>;
|
||||
type Context = CrossHlslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: HlslReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::back::hlsl::HlslBufferAssignments;
|
||||
|
||||
#[test]
|
||||
pub fn mangled_id_test() {
|
||||
assert_eq!(HlslBufferAssignments::find_mangled_id("_19_MVP"), Some(19));
|
||||
assert_eq!(HlslBufferAssignments::find_mangled_id("_19"), None);
|
||||
assert_eq!(HlslBufferAssignments::find_mangled_id("_19_"), Some(19));
|
||||
assert_eq!(HlslBufferAssignments::find_mangled_id("19_"), None);
|
||||
assert_eq!(HlslBufferAssignments::find_mangled_id("_19MVP"), None);
|
||||
assert_eq!(
|
||||
HlslBufferAssignments::find_mangled_id("_19_29_MVP"),
|
||||
Some(19)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn mangled_name_test() {
|
||||
assert!(HlslBufferAssignments::find_mangled_name(
|
||||
"params",
|
||||
"MVP",
|
||||
"params_MVP"
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
pub mod cross;
|
||||
#[cfg(all(target_os = "windows", feature = "dxil"))]
|
||||
pub mod dxil;
|
||||
mod spirv;
|
||||
pub mod glsl;
|
||||
pub mod hlsl;
|
||||
pub mod msl;
|
||||
pub mod spirv;
|
||||
pub mod targets;
|
||||
pub mod wgsl;
|
||||
|
||||
|
@ -41,26 +43,26 @@ pub trait CompileShader<T: OutputTarget> {
|
|||
///
|
||||
/// This trait is automatically implemented for reflected outputs that have [`FromCompilation`](crate::back::FromCompilation) implement
|
||||
/// for a given target that also implement [`CompileShader`](crate::back::CompileShader) for that target.
|
||||
pub trait CompileReflectShader<T: OutputTarget, C>:
|
||||
pub trait CompileReflectShader<T: OutputTarget, C, S>:
|
||||
CompileShader<
|
||||
T,
|
||||
Options = <T as FromCompilation<C>>::Options,
|
||||
Context = <T as FromCompilation<C>>::Context,
|
||||
Options = <T as FromCompilation<C, S>>::Options,
|
||||
Context = <T as FromCompilation<C, S>>::Context,
|
||||
> + ReflectShader
|
||||
where
|
||||
T: FromCompilation<C>,
|
||||
T: FromCompilation<C, S>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T, C, O> CompileReflectShader<T, C> for O
|
||||
impl<T, C, O, S> CompileReflectShader<T, C, S> for O
|
||||
where
|
||||
T: OutputTarget,
|
||||
T: FromCompilation<C>,
|
||||
T: FromCompilation<C, S>,
|
||||
O: ReflectShader,
|
||||
O: CompileShader<
|
||||
T,
|
||||
Options = <T as FromCompilation<C>>::Options,
|
||||
Context = <T as FromCompilation<C>>::Context,
|
||||
Options = <T as FromCompilation<C, S>>::Options,
|
||||
Context = <T as FromCompilation<C, S>>::Context,
|
||||
>,
|
||||
{
|
||||
}
|
||||
|
@ -81,8 +83,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// A trait for reflectable compilations that can be transformed into an object ready for reflection or compilation.
|
||||
pub trait FromCompilation<T> {
|
||||
/// A trait for reflectable compilations that can be transformed
|
||||
/// into an object ready for reflection or compilation.
|
||||
///
|
||||
/// `T` is the compiled reflectable form of the shader.
|
||||
/// `S` is the semantics under which the shader is reflected.
|
||||
///
|
||||
/// librashader currently supports two semantics, [`SpirvCross`](crate::reflect::cross::SpirvCross)
|
||||
pub trait FromCompilation<T, S> {
|
||||
/// The target that the transformed object is expected to compile for.
|
||||
type Target: OutputTarget;
|
||||
/// Options provided to the compiler.
|
||||
|
@ -118,11 +126,11 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::front::{Glslang, ShaderInputCompiler};
|
||||
use librashader_preprocess::ShaderSource;
|
||||
|
||||
pub fn test() {
|
||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
||||
let _cross = GlslangCompilation::compile(&result).unwrap();
|
||||
let _cross = Glslang::compile(&result).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
70
librashader-reflect/src/back/msl.rs
Normal file
70
librashader-reflect/src/back/msl.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use crate::back::targets::MSL;
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation};
|
||||
use crate::error::ShaderReflectError;
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::cross::msl::MslReflect;
|
||||
use crate::reflect::cross::{CompiledProgram, SpirvCross};
|
||||
use crate::reflect::naga::{Naga, NagaReflect};
|
||||
use crate::reflect::ReflectShader;
|
||||
use naga::back::msl::TranslationInfo;
|
||||
use naga::Module;
|
||||
|
||||
/// The HLSL shader model version to target.
|
||||
pub use spirv_cross::msl::Version as MslVersion;
|
||||
|
||||
/// Compiler options for MSL
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MslNagaCompileOptions {
|
||||
// pub write_pcb_as_ubo: bool,
|
||||
pub sampler_bind_group: u32,
|
||||
}
|
||||
|
||||
/// The context for a MSL compilation via spirv-cross.
|
||||
pub struct CrossMslContext {
|
||||
/// The compiled HLSL program.
|
||||
pub artifact: CompiledProgram<spirv_cross::msl::Target>,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, SpirvCross> for MSL {
|
||||
type Target = MSL;
|
||||
type Options = Option<self::MslVersion>;
|
||||
type Context = CrossMslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: MslReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The naga module for a shader after compilation
|
||||
pub struct NagaMslModule {
|
||||
pub translation_info: TranslationInfo,
|
||||
pub module: Module,
|
||||
}
|
||||
|
||||
pub struct NagaMslContext {
|
||||
pub vertex: NagaMslModule,
|
||||
pub fragment: NagaMslModule,
|
||||
pub next_free_binding: u32,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, Naga> for MSL {
|
||||
type Target = MSL;
|
||||
type Options = Option<self::MslVersion>;
|
||||
type Context = NagaMslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: NagaReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
use crate::back::targets::SPIRV;
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation, ShaderCompilerOutput};
|
||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::reflect::cross::GlslReflect;
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::cross::glsl::GlslReflect;
|
||||
use crate::reflect::cross::SpirvCross;
|
||||
use crate::reflect::naga::{Naga, NagaLoweringOptions, NagaReflect};
|
||||
use crate::reflect::semantics::ShaderSemantics;
|
||||
use crate::reflect::{ReflectShader, ShaderReflection};
|
||||
use naga::Module;
|
||||
|
||||
pub(crate) struct WriteSpirV {
|
||||
// rely on GLSL to provide out reflection but we don't actually need the AST.
|
||||
|
@ -13,7 +16,7 @@ pub(crate) struct WriteSpirV {
|
|||
pub(crate) fragment: Vec<u32>,
|
||||
}
|
||||
|
||||
impl FromCompilation<GlslangCompilation> for SPIRV {
|
||||
impl FromCompilation<SpirvCompilation, SpirvCross> for SPIRV {
|
||||
type Target = SPIRV;
|
||||
type Options = Option<()>;
|
||||
type Context = ();
|
||||
|
@ -21,7 +24,7 @@ impl FromCompilation<GlslangCompilation> for SPIRV {
|
|||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: GlslangCompilation,
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
let reflect = GlslReflect::try_from(&compile)?;
|
||||
let vertex = compile.vertex;
|
||||
|
@ -61,3 +64,30 @@ impl CompileShader<SPIRV> for WriteSpirV {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The context for a SPIRV compilation via Naga
|
||||
pub struct NagaSpirvContext {
|
||||
pub fragment: Module,
|
||||
pub vertex: Module,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, Naga> for SPIRV {
|
||||
type Target = SPIRV;
|
||||
type Options = NagaSpirvOptions;
|
||||
type Context = NagaSpirvContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: NagaReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NagaSpirvOptions {
|
||||
pub lowering: NagaLoweringOptions,
|
||||
pub version: (u8, u8),
|
||||
}
|
||||
|
|
|
@ -5,17 +5,22 @@ pub trait OutputTarget {
|
|||
}
|
||||
|
||||
/// Shader compiler target for GLSL.
|
||||
#[derive(Debug)]
|
||||
pub struct GLSL;
|
||||
/// Shader compiler target for HLSL.
|
||||
#[derive(Debug)]
|
||||
pub struct HLSL;
|
||||
/// Shader compiler target for SPIR-V.
|
||||
#[derive(Debug)]
|
||||
pub struct SPIRV;
|
||||
/// Shader compiler target for MSL.
|
||||
#[derive(Debug)]
|
||||
pub struct MSL;
|
||||
/// Shader compiler target for DXIL.
|
||||
///
|
||||
/// The resulting DXIL object is always unvalidated and
|
||||
/// must be validated using platform APIs before usage.
|
||||
#[derive(Debug)]
|
||||
pub struct DXIL;
|
||||
|
||||
/// Shader compiler target for WGSL.
|
||||
|
@ -34,6 +39,9 @@ impl OutputTarget for HLSL {
|
|||
impl OutputTarget for WGSL {
|
||||
type Output = String;
|
||||
}
|
||||
impl OutputTarget for MSL {
|
||||
type Output = String;
|
||||
}
|
||||
impl OutputTarget for SPIRV {
|
||||
type Output = Vec<u32>;
|
||||
}
|
||||
|
@ -42,9 +50,9 @@ impl OutputTarget for SPIRV {
|
|||
mod test {
|
||||
use crate::back::targets::GLSL;
|
||||
use crate::back::FromCompilation;
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::front::SpirvCompilation;
|
||||
#[allow(dead_code)]
|
||||
pub fn test_compile(value: GlslangCompilation) {
|
||||
pub fn test_compile(value: SpirvCompilation) {
|
||||
let _x = GLSL::from_compilation(value).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
95
librashader-reflect/src/back/wgsl.rs
Normal file
95
librashader-reflect/src/back/wgsl.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use crate::back::targets::WGSL;
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation};
|
||||
use crate::error::ShaderReflectError;
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::naga::{Naga, NagaLoweringOptions, NagaReflect};
|
||||
use crate::reflect::ReflectShader;
|
||||
use naga::Module;
|
||||
|
||||
/// The context for a WGSL compilation via Naga
|
||||
pub struct NagaWgslContext {
|
||||
pub fragment: Module,
|
||||
pub vertex: Module,
|
||||
}
|
||||
|
||||
impl FromCompilation<SpirvCompilation, Naga> for WGSL {
|
||||
type Target = WGSL;
|
||||
type Options = NagaLoweringOptions;
|
||||
type Context = NagaWgslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: SpirvCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
Ok(CompilerBackend {
|
||||
backend: NagaReflect::try_from(&compile)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::back::targets::WGSL;
|
||||
use crate::back::{CompileShader, FromCompilation};
|
||||
use crate::reflect::naga::NagaLoweringOptions;
|
||||
use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics};
|
||||
use crate::reflect::ReflectShader;
|
||||
use bitflags::Flags;
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
|
||||
#[test]
|
||||
pub fn test_into() {
|
||||
// let result = ShaderSource::load("../test/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang").unwrap();
|
||||
// let result = ShaderSource::load("../test/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang").unwrap();
|
||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
||||
|
||||
let mut uniform_semantics: FastHashMap<String, UniformSemantic> = Default::default();
|
||||
|
||||
for (_index, param) in result.parameters.iter().enumerate() {
|
||||
uniform_semantics.insert(
|
||||
param.1.id.clone(),
|
||||
UniformSemantic::Unique(Semantic {
|
||||
semantics: UniqueSemantics::FloatParameter,
|
||||
index: (),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let compilation = crate::front::SpirvCompilation::try_from(&result).unwrap();
|
||||
|
||||
let mut wgsl = WGSL::from_compilation(compilation).unwrap();
|
||||
|
||||
wgsl.reflect(
|
||||
0,
|
||||
&ShaderSemantics {
|
||||
uniform_semantics,
|
||||
texture_semantics: Default::default(),
|
||||
},
|
||||
)
|
||||
.expect("");
|
||||
|
||||
let compiled = wgsl
|
||||
.compile(NagaLoweringOptions {
|
||||
write_pcb_as_ubo: true,
|
||||
sampler_bind_group: 1,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("{}", compiled.fragment);
|
||||
|
||||
// println!("{}", compiled.fragment);
|
||||
// let mut loader = rspirv::dr::Loader::new();
|
||||
// rspirv::binary::parse_words(compilation.vertex.as_binary(), &mut loader).unwrap();
|
||||
// let module = loader.module();
|
||||
//
|
||||
// let outputs: Vec<&Instruction> = module
|
||||
// .types_global_values
|
||||
// .iter()
|
||||
// .filter(|i| i.class.opcode == Op::Variable)
|
||||
// .collect();
|
||||
//
|
||||
// println!("{outputs:#?}");
|
||||
}
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
mod lower_samplers;
|
||||
|
||||
use crate::back::targets::WGSL;
|
||||
use crate::back::wgsl::lower_samplers::LowerCombinedImageSamplerPass;
|
||||
use crate::back::{CompileShader, CompilerBackend, FromCompilation, ShaderCompilerOutput};
|
||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::reflect::naga::NagaReflect;
|
||||
use crate::reflect::ReflectShader;
|
||||
use naga::back::wgsl::WriterFlags;
|
||||
use naga::valid::{Capabilities, ValidationFlags};
|
||||
use naga::{AddressSpace, Module};
|
||||
use rspirv::binary::Assemble;
|
||||
use rspirv::dr::Builder;
|
||||
|
||||
/// The context for a WGSL compilation via Naga
|
||||
pub struct NagaWgslContext {
|
||||
pub fragment: Module,
|
||||
pub vertex: Module,
|
||||
}
|
||||
|
||||
/// Compiler options for WGSL
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct WgslCompileOptions {
|
||||
pub write_pcb_as_ubo: bool,
|
||||
pub sampler_bind_group: u32,
|
||||
}
|
||||
|
||||
impl FromCompilation<GlslangCompilation> for WGSL {
|
||||
type Target = WGSL;
|
||||
type Options = WgslCompileOptions;
|
||||
type Context = NagaWgslContext;
|
||||
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||
+ ReflectShader;
|
||||
|
||||
fn from_compilation(
|
||||
compile: GlslangCompilation,
|
||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||
fn lower_fragment_shader(words: &[u32]) -> Vec<u32> {
|
||||
let mut loader = rspirv::dr::Loader::new();
|
||||
rspirv::binary::parse_words(words, &mut loader).unwrap();
|
||||
let module = loader.module();
|
||||
let mut builder = Builder::new_from_module(module);
|
||||
|
||||
let mut pass = LowerCombinedImageSamplerPass::new(&mut builder);
|
||||
|
||||
pass.ensure_op_type_sampler();
|
||||
pass.do_pass();
|
||||
|
||||
let module = builder.module();
|
||||
|
||||
module.assemble()
|
||||
}
|
||||
|
||||
let options = naga::front::spv::Options {
|
||||
adjust_coordinate_space: true,
|
||||
strict_capabilities: false,
|
||||
block_ctx_dump_prefix: None,
|
||||
};
|
||||
|
||||
let vertex =
|
||||
naga::front::spv::parse_u8_slice(bytemuck::cast_slice(&compile.vertex), &options)?;
|
||||
|
||||
let fragment = lower_fragment_shader(&compile.fragment);
|
||||
let fragment = naga::front::spv::parse_u8_slice(bytemuck::cast_slice(&fragment), &options)?;
|
||||
|
||||
Ok(CompilerBackend {
|
||||
backend: NagaReflect { vertex, fragment },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl CompileShader<WGSL> for NagaReflect {
|
||||
type Options = WgslCompileOptions;
|
||||
type Context = NagaWgslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
|
||||
fn write_wgsl(module: &Module) -> Result<String, ShaderCompileError> {
|
||||
let mut valid =
|
||||
naga::valid::Validator::new(ValidationFlags::all(), Capabilities::empty());
|
||||
let info = valid.validate(&module)?;
|
||||
|
||||
let wgsl = naga::back::wgsl::write_string(&module, &info, WriterFlags::EXPLICIT_TYPES)?;
|
||||
Ok(wgsl)
|
||||
}
|
||||
|
||||
if options.write_pcb_as_ubo {
|
||||
for (_, gv) in self.fragment.global_variables.iter_mut() {
|
||||
if gv.space == AddressSpace::PushConstant {
|
||||
gv.space = AddressSpace::Uniform;
|
||||
}
|
||||
}
|
||||
|
||||
for (_, gv) in self.vertex.global_variables.iter_mut() {
|
||||
if gv.space == AddressSpace::PushConstant {
|
||||
gv.space = AddressSpace::Uniform;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (_, gv) in self.fragment.global_variables.iter_mut() {
|
||||
if gv.space == AddressSpace::PushConstant {
|
||||
gv.binding = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reassign shit.
|
||||
let images = self
|
||||
.fragment
|
||||
.global_variables
|
||||
.iter()
|
||||
.filter(|&(_, gv)| {
|
||||
let ty = &self.fragment.types[gv.ty];
|
||||
match ty.inner {
|
||||
naga::TypeInner::Image { .. } => true,
|
||||
naga::TypeInner::BindingArray { base, .. } => {
|
||||
let ty = &self.fragment.types[base];
|
||||
matches!(ty.inner, naga::TypeInner::Image { .. })
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.map(|(_, gv)| (gv.binding.clone(), gv.space))
|
||||
.collect::<naga::FastHashSet<_>>();
|
||||
|
||||
self.fragment
|
||||
.global_variables
|
||||
.iter_mut()
|
||||
.filter(|(_, gv)| {
|
||||
let ty = &self.fragment.types[gv.ty];
|
||||
match ty.inner {
|
||||
naga::TypeInner::Sampler { .. } => true,
|
||||
naga::TypeInner::BindingArray { base, .. } => {
|
||||
let ty = &self.fragment.types[base];
|
||||
matches!(ty.inner, naga::TypeInner::Sampler { .. })
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.for_each(|(_, gv)| {
|
||||
if images.contains(&(gv.binding.clone(), gv.space)) {
|
||||
if let Some(binding) = &mut gv.binding {
|
||||
binding.group = options.sampler_bind_group;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let fragment = write_wgsl(&self.fragment)?;
|
||||
let vertex = write_wgsl(&self.vertex)?;
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex,
|
||||
fragment,
|
||||
context: NagaWgslContext {
|
||||
fragment: self.fragment,
|
||||
vertex: self.vertex,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::back::targets::WGSL;
|
||||
use crate::back::wgsl::WgslCompileOptions;
|
||||
use crate::back::{CompileShader, FromCompilation};
|
||||
use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics};
|
||||
use crate::reflect::ReflectShader;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[test]
|
||||
pub fn test_into() {
|
||||
// let result = ShaderSource::load("../test/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang").unwrap();
|
||||
// let result = ShaderSource::load("../test/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang").unwrap();
|
||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
||||
|
||||
let mut uniform_semantics: FxHashMap<String, UniformSemantic> = Default::default();
|
||||
|
||||
for (_index, param) in result.parameters.iter().enumerate() {
|
||||
uniform_semantics.insert(
|
||||
param.1.id.clone(),
|
||||
UniformSemantic::Unique(Semantic {
|
||||
semantics: UniqueSemantics::FloatParameter,
|
||||
index: (),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let compilation = crate::front::GlslangCompilation::try_from(&result).unwrap();
|
||||
|
||||
let mut wgsl = WGSL::from_compilation(compilation).unwrap();
|
||||
|
||||
wgsl.reflect(
|
||||
0,
|
||||
&ShaderSemantics {
|
||||
uniform_semantics,
|
||||
texture_semantics: Default::default(),
|
||||
},
|
||||
)
|
||||
.expect("");
|
||||
|
||||
let compiled = wgsl
|
||||
.compile(WgslCompileOptions {
|
||||
write_pcb_as_ubo: true,
|
||||
sampler_bind_group: 1,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("{}", compiled.fragment);
|
||||
|
||||
// println!("{}", compiled.fragment);
|
||||
// let mut loader = rspirv::dr::Loader::new();
|
||||
// rspirv::binary::parse_words(compilation.vertex.as_binary(), &mut loader).unwrap();
|
||||
// let module = loader.module();
|
||||
//
|
||||
// let outputs: Vec<&Instruction> = module
|
||||
// .types_global_values
|
||||
// .iter()
|
||||
// .filter(|i| i.class.opcode == Op::Variable)
|
||||
// .collect();
|
||||
//
|
||||
// println!("{outputs:#?}");
|
||||
}
|
||||
}
|
|
@ -11,29 +11,40 @@ pub enum ShaderCompileError {
|
|||
NagaCompileError(Vec<naga::front::glsl::Error>),
|
||||
|
||||
/// Compilation error from glslang.
|
||||
#[error("glslang")]
|
||||
#[error("error when compiling with glslang: {0}")]
|
||||
GlslangError(#[from] glslang::error::GlslangError),
|
||||
|
||||
/// Error when initializing the glslang compiler.
|
||||
#[error("glslang init")]
|
||||
#[error("error when initializing glslang")]
|
||||
CompilerInitError,
|
||||
|
||||
/// Error when transpiling from spirv-cross.
|
||||
#[error("cross")]
|
||||
#[error("spirv-cross error: {0:?}")]
|
||||
SpirvCrossCompileError(#[from] spirv_cross::ErrorCode),
|
||||
|
||||
/// Error when transpiling from spirv-to-dxil
|
||||
#[cfg(all(target_os = "windows", feature = "dxil"))]
|
||||
#[error("spirv-to-dxil")]
|
||||
#[error("spirv-to-dxil error: {0:?}")]
|
||||
SpirvToDxilCompileError(#[from] spirv_to_dxil::SpirvToDxilError),
|
||||
|
||||
/// Error when transpiling from naga
|
||||
#[cfg(feature = "wgsl")]
|
||||
#[error("naga-wgsl")]
|
||||
#[cfg(all(feature = "wgsl", feature = "naga"))]
|
||||
#[error("naga error when compiling wgsl: {0:?}")]
|
||||
NagaWgslError(#[from] naga::back::wgsl::Error),
|
||||
|
||||
/// Error when transpiling from naga
|
||||
#[cfg(feature = "wgsl")]
|
||||
#[error("naga-wgsl")]
|
||||
#[cfg(feature = "naga")]
|
||||
#[error("naga error when compiling spirv: {0:?}")]
|
||||
NagaSpvError(#[from] naga::back::spv::Error),
|
||||
|
||||
/// Error when transpiling from naga
|
||||
#[cfg(all(feature = "naga", feature = "msl"))]
|
||||
#[error("naga error when compiling msl: {0:?}")]
|
||||
NagaMslError(#[from] naga::back::msl::Error),
|
||||
|
||||
/// Error when transpiling from naga
|
||||
#[cfg(any(feature = "naga", feature = "wgsl"))]
|
||||
#[error("naga validation error: {0}")]
|
||||
NagaValidationError(#[from] naga::WithSpan<naga::valid::ValidationError>),
|
||||
}
|
||||
|
||||
|
@ -75,23 +86,23 @@ pub enum SemanticsErrorKind {
|
|||
#[derive(Error, Debug)]
|
||||
pub enum ShaderReflectError {
|
||||
/// Reflection error from spirv-cross.
|
||||
#[error("spirv")]
|
||||
#[error("spirv cross error: {0}")]
|
||||
SpirvCrossError(#[from] spirv_cross::ErrorCode),
|
||||
/// Error when validating vertex shader semantics.
|
||||
#[error("error when verifying vertex semantics")]
|
||||
#[error("error when verifying vertex semantics: {0:?}")]
|
||||
VertexSemanticError(SemanticsErrorKind),
|
||||
/// Error when validating fragment shader semantics.
|
||||
#[error("error when verifying texture semantics")]
|
||||
#[error("error when verifying texture semantics {0:?}")]
|
||||
FragmentSemanticError(SemanticsErrorKind),
|
||||
/// The vertex and fragment shader must have the same UBO binding location.
|
||||
#[error("vertex and fragment shader must have same binding")]
|
||||
#[error("vertex and fragment shader must have same UBO binding. declared {vertex} in vertex, got {fragment} in fragment")]
|
||||
MismatchedUniformBuffer { vertex: u32, fragment: u32 },
|
||||
/// The filter chain was found to be non causal. A pass tried to access the target output
|
||||
/// in the future.
|
||||
#[error("filter chain is non causal")]
|
||||
#[error("filter chain is non causal: tried to access target {target} in pass {pass}")]
|
||||
NonCausalFilterChain { pass: usize, target: usize },
|
||||
/// The offset of the given uniform did not match up in both the vertex and fragment shader.
|
||||
#[error("mismatched offset")]
|
||||
#[error("the offset of {semantic} was declared as {expected} but found as {received} in pass {pass}")]
|
||||
MismatchedOffset {
|
||||
semantic: String,
|
||||
expected: usize,
|
||||
|
@ -100,7 +111,7 @@ pub enum ShaderReflectError {
|
|||
pass: usize,
|
||||
},
|
||||
/// The size of the given uniform did not match up in both the vertex and fragment shader.
|
||||
#[error("mismatched component")]
|
||||
#[error("the size of {semantic} was found declared as {vertex} in vertex shader but found as {fragment} in fragment in pass {pass}")]
|
||||
MismatchedSize {
|
||||
semantic: String,
|
||||
vertex: u32,
|
||||
|
@ -108,12 +119,16 @@ pub enum ShaderReflectError {
|
|||
pass: usize,
|
||||
},
|
||||
/// The binding number is already in use.
|
||||
#[error("the binding is already in use")]
|
||||
#[error("binding {0} is already in use")]
|
||||
BindingInUse(u32),
|
||||
/// Error when transpiling from naga
|
||||
#[cfg(feature = "wgsl")]
|
||||
#[error("naga-spv")]
|
||||
#[cfg(feature = "naga")]
|
||||
#[error("naga spirv error: {0}")]
|
||||
NagaInputError(#[from] naga::front::spv::Error),
|
||||
/// Error when transpiling from naga
|
||||
#[cfg(feature = "naga")]
|
||||
#[error("naga validation error: {0}")]
|
||||
NagaReflectError(#[from] naga::WithSpan<naga::valid::ValidationError>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-naga")]
|
||||
|
|
|
@ -1,67 +1,54 @@
|
|||
use crate::error::ShaderCompileError;
|
||||
use glslang::input::{CompilerOptions, ShaderInput};
|
||||
use glslang::limits::ResourceLimits;
|
||||
use glslang::{CompilerOptions, ShaderInput};
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use rspirv::binary::Assemble;
|
||||
use rspirv::dr::Builder;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::front::spirv_passes::{link_input_outputs, load_module};
|
||||
use crate::front::{ShaderInputCompiler, SpirvCompilation};
|
||||
|
||||
/// A reflectable shader compilation via glslang.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlslangCompilation {
|
||||
pub(crate) vertex: Vec<u32>,
|
||||
pub(crate) fragment: Vec<u32>,
|
||||
}
|
||||
/// glslang compiler
|
||||
pub struct Glslang;
|
||||
|
||||
impl GlslangCompilation {
|
||||
/// Tries to compile SPIR-V from the provided shader source.
|
||||
pub fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
|
||||
impl ShaderInputCompiler<SpirvCompilation> for Glslang {
|
||||
fn compile(source: &ShaderSource) -> Result<SpirvCompilation, ShaderCompileError> {
|
||||
compile_spirv(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&ShaderSource> for GlslangCompilation {
|
||||
type Error = ShaderCompileError;
|
||||
|
||||
/// Tries to compile SPIR-V from the provided shader source.
|
||||
fn try_from(source: &ShaderSource) -> Result<Self, Self::Error> {
|
||||
GlslangCompilation::compile(source)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compile_spirv(
|
||||
source: &ShaderSource,
|
||||
) -> Result<GlslangCompilation, ShaderCompileError> {
|
||||
pub(crate) fn compile_spirv(source: &ShaderSource) -> Result<SpirvCompilation, ShaderCompileError> {
|
||||
let compiler = glslang::Compiler::acquire().ok_or(ShaderCompileError::CompilerInitError)?;
|
||||
let options = CompilerOptions {
|
||||
source_language: glslang::SourceLanguage::GLSL,
|
||||
target: glslang::Target::Vulkan {
|
||||
version: glslang::VulkanVersion::Vulkan1_0,
|
||||
spirv_version: glslang::SpirvVersion::SPIRV1_0,
|
||||
},
|
||||
version_profile: None,
|
||||
};
|
||||
|
||||
let limits = ResourceLimits::default();
|
||||
let options = CompilerOptions::default();
|
||||
|
||||
let vertex = glslang::input::ShaderSource::from(source.vertex.as_str());
|
||||
let vertex = ShaderInput::new(
|
||||
&vertex,
|
||||
&limits,
|
||||
glslang::ShaderStage::Vertex,
|
||||
&options,
|
||||
None,
|
||||
);
|
||||
let vertex = glslang::ShaderSource::from(source.vertex.as_str());
|
||||
let vertex = ShaderInput::new(&vertex, glslang::ShaderStage::Vertex, &options, None)?;
|
||||
let vertex = compiler.create_shader(vertex)?;
|
||||
|
||||
let fragment = glslang::input::ShaderSource::from(source.fragment.as_str());
|
||||
let fragment = ShaderInput::new(
|
||||
&fragment,
|
||||
&limits,
|
||||
glslang::ShaderStage::Fragment,
|
||||
&options,
|
||||
None,
|
||||
);
|
||||
let fragment = glslang::ShaderSource::from(source.fragment.as_str());
|
||||
let fragment = ShaderInput::new(&fragment, glslang::ShaderStage::Fragment, &options, None)?;
|
||||
let fragment = compiler.create_shader(fragment)?;
|
||||
|
||||
let vertex = Vec::from(vertex.compile()?);
|
||||
let fragment = Vec::from(fragment.compile()?);
|
||||
let vertex = vertex.compile()?;
|
||||
let fragment = fragment.compile()?;
|
||||
|
||||
Ok(GlslangCompilation { vertex, fragment })
|
||||
let vertex = load_module(&vertex);
|
||||
let fragment = load_module(&fragment);
|
||||
let mut fragment = Builder::new_from_module(fragment);
|
||||
|
||||
let mut pass = link_input_outputs::LinkInputs::new(&vertex, &mut fragment);
|
||||
pass.do_pass();
|
||||
|
||||
let vertex = vertex.assemble();
|
||||
let fragment = fragment.module().assemble();
|
||||
|
||||
Ok(SpirvCompilation { vertex, fragment })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,18 +1,49 @@
|
|||
use crate::error::ShaderCompileError;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub(crate) mod spirv_passes;
|
||||
|
||||
mod glslang;
|
||||
|
||||
pub use crate::front::glslang::GlslangCompilation;
|
||||
/// The output of a shader compiler that is reflectable.
|
||||
pub trait ShaderReflectObject: Sized {
|
||||
/// The compiler that produces this reflect object.
|
||||
type Compiler;
|
||||
}
|
||||
|
||||
pub use crate::front::glslang::Glslang;
|
||||
|
||||
/// Trait for types that can compile shader sources into a compilation unit.
|
||||
pub trait ShaderCompilation: Sized {
|
||||
pub trait ShaderInputCompiler<O: ShaderReflectObject>: Sized {
|
||||
/// Compile the input shader source file into a compilation unit.
|
||||
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError>;
|
||||
fn compile(source: &ShaderSource) -> Result<O, ShaderCompileError>;
|
||||
}
|
||||
|
||||
impl<T: for<'a> TryFrom<&'a ShaderSource, Error = ShaderCompileError>> ShaderCompilation for T {
|
||||
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
|
||||
source.try_into()
|
||||
/// Marker trait for types that are the reflectable outputs of a shader compilation.
|
||||
impl ShaderReflectObject for SpirvCompilation {
|
||||
type Compiler = Glslang;
|
||||
}
|
||||
|
||||
/// A reflectable shader compilation via glslang.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpirvCompilation {
|
||||
pub(crate) vertex: Vec<u32>,
|
||||
pub(crate) fragment: Vec<u32>,
|
||||
}
|
||||
|
||||
impl SpirvCompilation {
|
||||
/// Tries to compile SPIR-V from the provided shader source.
|
||||
pub fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
|
||||
glslang::compile_spirv(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&ShaderSource> for SpirvCompilation {
|
||||
type Error = ShaderCompileError;
|
||||
|
||||
/// Tries to compile SPIR-V from the provided shader source.
|
||||
fn try_from(source: &ShaderSource) -> Result<Self, Self::Error> {
|
||||
Glslang::compile(source)
|
||||
}
|
||||
}
|
||||
|
|
151
librashader-reflect/src/front/spirv_passes/link_input_outputs.rs
Normal file
151
librashader-reflect/src/front/spirv_passes/link_input_outputs.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use rspirv::dr::{Builder, Module, Operand};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use spirv::{Decoration, Op, StorageClass};
|
||||
|
||||
pub struct LinkInputs<'a> {
|
||||
pub frag_builder: &'a mut Builder,
|
||||
pub inputs: FxHashSet<spirv::Word>,
|
||||
}
|
||||
|
||||
impl<'a> LinkInputs<'a> {
|
||||
fn find_location(module: &Module, id: spirv::Word) -> Option<u32> {
|
||||
module.annotations.iter().find_map(|op| {
|
||||
if op.class.opcode != Op::Decorate {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Some(Operand::Decoration(Decoration::Location)) = op.operands.get(1) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(&Operand::IdRef(target)) = op.operands.get(0) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if target != id {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Some(&Operand::LiteralBit32(binding)) = op.operands.get(2) else {
|
||||
return None;
|
||||
};
|
||||
return Some(binding);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(vert: &'a Module, frag: &'a mut Builder) -> Self {
|
||||
let mut inputs = FxHashSet::default();
|
||||
let mut bindings = FxHashMap::default();
|
||||
for global in frag.module_ref().types_global_values.iter() {
|
||||
if global.class.opcode == spirv::Op::Variable
|
||||
&& global.operands[0] == Operand::StorageClass(StorageClass::Input)
|
||||
{
|
||||
if let Some(id) = global.result_id {
|
||||
let Some(location) = Self::find_location(frag.module_ref(), id) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
inputs.insert(id);
|
||||
bindings.insert(location, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for global in vert.types_global_values.iter() {
|
||||
if global.class.opcode == spirv::Op::Variable
|
||||
&& global.operands[0] == Operand::StorageClass(StorageClass::Output)
|
||||
{
|
||||
if let Some(id) = global.result_id {
|
||||
let Some(location) = Self::find_location(vert, id) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(frag_ref) = bindings.get(&location) {
|
||||
// if something is bound to the same location in the vertex shader,
|
||||
// we're good.
|
||||
inputs.remove(&frag_ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
frag_builder: frag,
|
||||
inputs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_pass(&mut self) {
|
||||
let functions = &self.frag_builder.module_ref().functions;
|
||||
|
||||
// literally if it has any reference at all we can keep it
|
||||
for function in functions {
|
||||
for param in &function.parameters {
|
||||
for op in ¶m.operands {
|
||||
if let Some(word) = op.id_ref_any() {
|
||||
if self.inputs.contains(&word) {
|
||||
self.inputs.remove(&word);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for block in &function.blocks {
|
||||
for inst in &block.instructions {
|
||||
for op in &inst.operands {
|
||||
if let Some(word) = op.id_ref_any() {
|
||||
if self.inputs.contains(&word) {
|
||||
self.inputs.remove(&word);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ok well guess we dont
|
||||
|
||||
self.frag_builder.module_mut().debug_names.retain(|instr| {
|
||||
for op in &instr.operands {
|
||||
if let Some(word) = op.id_ref_any() {
|
||||
if self.inputs.contains(&word) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
self.frag_builder.module_mut().annotations.retain(|instr| {
|
||||
for op in &instr.operands {
|
||||
if let Some(word) = op.id_ref_any() {
|
||||
if self.inputs.contains(&word) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
for entry_point in self.frag_builder.module_mut().entry_points.iter_mut() {
|
||||
entry_point.operands.retain(|op| {
|
||||
if let Some(word) = op.id_ref_any() {
|
||||
if self.inputs.contains(&word) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
}
|
||||
|
||||
self.frag_builder
|
||||
.module_mut()
|
||||
.types_global_values
|
||||
.retain(|instr| {
|
||||
let Some(id) = instr.result_id else {
|
||||
return true;
|
||||
};
|
||||
|
||||
!self.inputs.contains(&id)
|
||||
});
|
||||
}
|
||||
}
|
|
@ -818,10 +818,11 @@ impl<'a> LowerCombinedImageSamplerPass<'a> {
|
|||
function_call_operands.push(operand);
|
||||
function_call_operands.push(Operand::IdRef(op_access_chain_sampler));
|
||||
|
||||
let original_type = self
|
||||
.find_global_instruction(original_result_type)
|
||||
.cloned()
|
||||
.expect("huh");
|
||||
let Some(original_type) =
|
||||
self.find_global_instruction(original_result_type).cloned()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let sampled_image = CombinedImageSampler {
|
||||
sampler_variable: op_access_chain_sampler,
|
10
librashader-reflect/src/front/spirv_passes/mod.rs
Normal file
10
librashader-reflect/src/front/spirv_passes/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod link_input_outputs;
|
||||
pub mod lower_samplers;
|
||||
|
||||
// Load SPIR-V as an rspirv module
|
||||
pub(crate) fn load_module(words: &[u32]) -> rspirv::dr::Module {
|
||||
let mut loader = rspirv::dr::Loader::new();
|
||||
rspirv::binary::parse_words(words, &mut loader).unwrap();
|
||||
let module = loader.module();
|
||||
module
|
||||
}
|
|
@ -13,10 +13,11 @@
|
|||
//! use librashader_presets::ShaderPreset;
|
||||
//! use librashader_reflect::back::{CompileReflectShader, FromCompilation};
|
||||
//! use librashader_reflect::back::targets::SPIRV;
|
||||
//! use librashader_reflect::front::GlslangCompilation;
|
||||
//! use librashader_reflect::front::{Glslang, ShaderInputCompiler, SpirvCompilation};
|
||||
//! use librashader_reflect::reflect::cross::SpirvCross;
|
||||
//! use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
|
||||
//! use librashader_reflect::reflect::semantics::ShaderSemantics;
|
||||
//! type Artifact = impl CompileReflectShader<SPIRV, GlslangCompilation>;
|
||||
//! type Artifact = impl CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross>;
|
||||
//! type ShaderPassMeta = ShaderPassArtifact<Artifact>;
|
||||
//!
|
||||
//! // Compile single shader
|
||||
|
@ -24,15 +25,15 @@
|
|||
//! source: &ShaderSource,
|
||||
//! ) -> Result<Artifact, Box<dyn Error>>
|
||||
//! {
|
||||
//! let compilation = GlslangCompilation::compile(&source)?;
|
||||
//! let spirv = SPIRV::from_compilation(artifact)?;
|
||||
//! let compilation = SpirvCompilation::try_from(&source);
|
||||
//! let spirv = SPIRV::from_compilation(compilation)?;
|
||||
//! Ok(spirv)
|
||||
//! }
|
||||
//!
|
||||
//! // Compile preset
|
||||
//! pub fn compile_preset(preset: ShaderPreset) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), Box<dyn Error>>
|
||||
//! {
|
||||
//! let (passes, semantics) = SPIRV::compile_preset_passes::<GlslangCompilation, Box<dyn Error>>(
|
||||
//! let (passes, semantics) = SPIRV::compile_preset_passes::<SpirvCompilation, SpirvCross, Box<dyn Error>>(
|
||||
//! preset.shaders, &preset.textures)?;
|
||||
//! Ok((passes, semantics))
|
||||
//! }
|
||||
|
@ -43,9 +44,8 @@
|
|||
//! [naga](https://docs.rs/naga/latest/naga/index.html), a pure-Rust shader compiler, when it has
|
||||
//! matured enough to support [the features librashader needs](https://github.com/gfx-rs/naga/issues/1012).
|
||||
//!
|
||||
//! In the meanwhile, the only supported compilation type is [GlslangCompilation](crate::front::GlslangCompilation),
|
||||
//! In the meanwhile, the only supported compilation type is [GlslangCompilation](crate::front::SpirvCompilation),
|
||||
//! which does transpilation via [glslang](https://github.com/KhronosGroup/glslang) and [SPIRV-Cross](https://github.com/KhronosGroup/SPIRV-Cross).
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
#![feature(let_chains)]
|
||||
|
||||
|
|
139
librashader-reflect/src/reflect/cross/glsl.rs
Normal file
139
librashader-reflect/src/reflect/cross/glsl.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use crate::back::glsl::CrossGlslContext;
|
||||
use crate::back::targets::GLSL;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::error::ShaderCompileError;
|
||||
use crate::reflect::cross::{CompiledAst, CompiledProgram, CrossReflect};
|
||||
use spirv_cross::spirv::Decoration;
|
||||
use spirv_cross::ErrorCode;
|
||||
|
||||
pub(crate) type GlslReflect = CrossReflect<spirv_cross::glsl::Target>;
|
||||
|
||||
impl CompileShader<GLSL> for CrossReflect<spirv_cross::glsl::Target> {
|
||||
type Options = spirv_cross::glsl::Version;
|
||||
type Context = CrossGlslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
version: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
|
||||
let mut options: spirv_cross::glsl::CompilerOptions = Default::default();
|
||||
options.version = version;
|
||||
options.fragment.default_float_precision = spirv_cross::glsl::Precision::High;
|
||||
options.fragment.default_int_precision = spirv_cross::glsl::Precision::High;
|
||||
options.enable_420_pack_extension = false;
|
||||
|
||||
self.vertex.set_compiler_options(&options)?;
|
||||
self.fragment.set_compiler_options(&options)?;
|
||||
|
||||
let vertex_resources = self.vertex.get_shader_resources()?;
|
||||
let fragment_resources = self.fragment.get_shader_resources()?;
|
||||
|
||||
for res in &vertex_resources.stage_outputs {
|
||||
// let location = self.vertex.get_decoration(res.id, Decoration::Location)?;
|
||||
// self.vertex
|
||||
// .set_name(res.id, &format!("LIBRA_VARYING_{location}"))?;
|
||||
self.vertex.unset_decoration(res.id, Decoration::Location)?;
|
||||
}
|
||||
for res in &fragment_resources.stage_inputs {
|
||||
// let location = self.fragment.get_decoration(res.id, Decoration::Location)?;
|
||||
// self.fragment
|
||||
// .set_name(res.id, &format!("LIBRA_VARYING_{location}"))?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::Location)?;
|
||||
}
|
||||
|
||||
if vertex_resources.push_constant_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one push constant buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
for res in &vertex_resources.push_constant_buffers {
|
||||
self.vertex.set_name(res.id, "LIBRA_PUSH_VERTEX_INSTANCE")?;
|
||||
self.vertex
|
||||
.set_name(res.base_type_id, "LIBRA_PUSH_VERTEX")?;
|
||||
}
|
||||
|
||||
// todo: options
|
||||
let _flatten = false;
|
||||
|
||||
if vertex_resources.uniform_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one uniform buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
for res in &vertex_resources.uniform_buffers {
|
||||
// if flatten {
|
||||
// self.vertex.flatten_buffer_block(res.id)?;
|
||||
// }
|
||||
self.vertex.set_name(res.id, "LIBRA_UBO_VERTEX_INSTANCE")?;
|
||||
self.vertex.set_name(res.base_type_id, "LIBRA_UBO_VERTEX")?;
|
||||
self.vertex
|
||||
.unset_decoration(res.id, Decoration::DescriptorSet)?;
|
||||
self.vertex.unset_decoration(res.id, Decoration::Binding)?;
|
||||
}
|
||||
|
||||
if fragment_resources.push_constant_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one push constant buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
for res in &fragment_resources.push_constant_buffers {
|
||||
self.fragment
|
||||
.set_name(res.id, "LIBRA_PUSH_FRAGMENT_INSTANCE")?;
|
||||
self.fragment
|
||||
.set_name(res.base_type_id, "LIBRA_PUSH_FRAGMENT")?;
|
||||
}
|
||||
|
||||
if fragment_resources.uniform_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one uniform buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
for res in &fragment_resources.uniform_buffers {
|
||||
// if flatten {
|
||||
// self.fragment.flatten_buffer_block(res.id)?;
|
||||
// }
|
||||
self.fragment
|
||||
.set_name(res.id, "LIBRA_UBO_FRAGMENT_INSTANCE")?;
|
||||
self.fragment
|
||||
.set_name(res.base_type_id, "LIBRA_UBO_FRAGMENT")?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::DescriptorSet)?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::Binding)?;
|
||||
}
|
||||
|
||||
let mut texture_fixups = Vec::new();
|
||||
for res in fragment_resources.sampled_images {
|
||||
let binding = self.fragment.get_decoration(res.id, Decoration::Binding)?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::DescriptorSet)?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::Binding)?;
|
||||
let mut name = res.name;
|
||||
name.push('\0');
|
||||
texture_fixups.push((name, binding));
|
||||
}
|
||||
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: self.vertex.compile()?,
|
||||
fragment: self.fragment.compile()?,
|
||||
context: CrossGlslContext {
|
||||
sampler_bindings: texture_fixups,
|
||||
artifact: CompiledProgram {
|
||||
vertex: CompiledAst(self.vertex),
|
||||
fragment: CompiledAst(self.fragment),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
119
librashader-reflect/src/reflect/cross/hlsl.rs
Normal file
119
librashader-reflect/src/reflect/cross/hlsl.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use crate::back::hlsl::{CrossHlslContext, HlslBufferAssignment, HlslBufferAssignments};
|
||||
use crate::back::targets::HLSL;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::error::ShaderCompileError;
|
||||
use crate::reflect::cross::{CompiledAst, CompiledProgram, CrossReflect};
|
||||
use spirv_cross::hlsl::ShaderModel as HlslShaderModel;
|
||||
use spirv_cross::spirv::Decoration;
|
||||
use spirv_cross::ErrorCode;
|
||||
|
||||
pub(crate) type HlslReflect = CrossReflect<spirv_cross::hlsl::Target>;
|
||||
|
||||
impl CompileShader<HLSL> for CrossReflect<spirv_cross::hlsl::Target> {
|
||||
type Options = Option<HlslShaderModel>;
|
||||
type Context = CrossHlslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, CrossHlslContext>, ShaderCompileError> {
|
||||
let sm = options.unwrap_or(HlslShaderModel::V5_0);
|
||||
let mut options = spirv_cross::hlsl::CompilerOptions::default();
|
||||
options.shader_model = sm;
|
||||
|
||||
self.vertex.set_compiler_options(&options)?;
|
||||
self.fragment.set_compiler_options(&options)?;
|
||||
|
||||
// todo: options
|
||||
|
||||
let vertex_resources = self.vertex.get_shader_resources()?;
|
||||
let fragment_resources = self.fragment.get_shader_resources()?;
|
||||
|
||||
let mut vertex_buffer_assignment = HlslBufferAssignments::default();
|
||||
let mut fragment_buffer_assignment = HlslBufferAssignments::default();
|
||||
|
||||
if vertex_resources.uniform_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one uniform buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(buf) = vertex_resources.uniform_buffers.first() {
|
||||
vertex_buffer_assignment.ubo = Some(HlslBufferAssignment {
|
||||
name: buf.name.clone(),
|
||||
id: buf.id,
|
||||
})
|
||||
}
|
||||
|
||||
if vertex_resources.push_constant_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one push constant buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(buf) = vertex_resources.push_constant_buffers.first() {
|
||||
vertex_buffer_assignment.push = Some(HlslBufferAssignment {
|
||||
name: buf.name.clone(),
|
||||
id: buf.id,
|
||||
})
|
||||
}
|
||||
|
||||
if fragment_resources.uniform_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one uniform buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(buf) = fragment_resources.uniform_buffers.first() {
|
||||
fragment_buffer_assignment.ubo = Some(HlslBufferAssignment {
|
||||
name: buf.name.clone(),
|
||||
id: buf.id,
|
||||
})
|
||||
}
|
||||
|
||||
if fragment_resources.push_constant_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one push constant buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(buf) = fragment_resources.push_constant_buffers.first() {
|
||||
fragment_buffer_assignment.push = Some(HlslBufferAssignment {
|
||||
name: buf.name.clone(),
|
||||
id: buf.id,
|
||||
})
|
||||
}
|
||||
|
||||
if sm == HlslShaderModel::V3_0 {
|
||||
for res in &fragment_resources.sampled_images {
|
||||
let binding = self.fragment.get_decoration(res.id, Decoration::Binding)?;
|
||||
self.fragment
|
||||
.set_name(res.id, &format!("LIBRA_SAMPLER2D_{binding}"))?;
|
||||
// self.fragment
|
||||
// .unset_decoration(res.id, Decoration::Binding)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: self.vertex.compile()?,
|
||||
fragment: self.fragment.compile()?,
|
||||
context: CrossHlslContext {
|
||||
artifact: CompiledProgram {
|
||||
vertex: CompiledAst(self.vertex),
|
||||
fragment: CompiledAst(self.fragment),
|
||||
},
|
||||
|
||||
vertex_buffers: vertex_buffer_assignment,
|
||||
fragment_buffers: fragment_buffer_assignment,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
use crate::error::{SemanticsErrorKind, ShaderCompileError, ShaderReflectError};
|
||||
use crate::front::GlslangCompilation;
|
||||
pub mod glsl;
|
||||
pub mod hlsl;
|
||||
pub mod msl;
|
||||
|
||||
use crate::error::{SemanticsErrorKind, ShaderReflectError};
|
||||
use crate::front::SpirvCompilation;
|
||||
use crate::reflect::semantics::{
|
||||
BindingMeta, BindingStage, BufferReflection, MemberOffset, ShaderReflection, ShaderSemantics,
|
||||
TextureBinding, TextureSemanticMap, TextureSemantics, TextureSizeMeta, TypeInfo,
|
||||
|
@ -7,22 +11,31 @@ use crate::reflect::semantics::{
|
|||
MAX_BINDINGS_COUNT, MAX_PUSH_BUFFER_SIZE,
|
||||
};
|
||||
use crate::reflect::{align_uniform_size, ReflectShader};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
|
||||
use spirv_cross::spirv::{Ast, Decoration, Module, Resource, ShaderResources, Type};
|
||||
use spirv_cross::{glsl, hlsl, ErrorCode};
|
||||
use spirv_cross::ErrorCode;
|
||||
|
||||
use crate::back::cross::{CrossGlslContext, CrossHlslContext, HlslShaderModel};
|
||||
use crate::back::targets::{GLSL, HLSL};
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::reflect::helper::{SemanticErrorBlame, TextureData, UboData};
|
||||
|
||||
/// Reflect shaders under SPIRV-Cross semantics.
|
||||
#[derive(Debug)]
|
||||
pub struct SpirvCross;
|
||||
|
||||
// This is "probably" OK.
|
||||
unsafe impl<T: Send + spirv_cross::spirv::Target> Send for CrossReflect<T> {}
|
||||
unsafe impl<T: Send + spirv_cross::spirv::Target> Send for CrossReflect<T>
|
||||
where
|
||||
Ast<T>: spirv_cross::spirv::Compile<T>,
|
||||
Ast<T>: spirv_cross::spirv::Parse<T>,
|
||||
{
|
||||
}
|
||||
|
||||
pub(crate) struct CrossReflect<T>
|
||||
where
|
||||
T: spirv_cross::spirv::Target,
|
||||
Ast<T>: spirv_cross::spirv::Compile<T>,
|
||||
Ast<T>: spirv_cross::spirv::Parse<T>,
|
||||
{
|
||||
vertex: Ast<T>,
|
||||
fragment: Ast<T>,
|
||||
|
@ -49,9 +62,6 @@ where
|
|||
pub fragment: CompiledAst<T>,
|
||||
}
|
||||
|
||||
pub(crate) type HlslReflect = CrossReflect<hlsl::Target>;
|
||||
pub(crate) type GlslReflect = CrossReflect<glsl::Target>;
|
||||
|
||||
impl ValidateTypeSemantics<Type> for UniqueSemantics {
|
||||
fn validate_type(&self, ty: &Type) -> Option<TypeInfo> {
|
||||
let (Type::Float {
|
||||
|
@ -84,7 +94,10 @@ impl ValidateTypeSemantics<Type> for UniqueSemantics {
|
|||
UniqueSemantics::MVP => {
|
||||
matches!(ty, Type::Float { .. }) && vecsize == 4 && columns == 4
|
||||
}
|
||||
UniqueSemantics::FrameCount => {
|
||||
UniqueSemantics::FrameCount
|
||||
| UniqueSemantics::Rotation
|
||||
| UniqueSemantics::TotalSubFrames
|
||||
| UniqueSemantics::CurrentSubFrame => {
|
||||
matches!(ty, Type::UInt { .. }) && vecsize == 1 && columns == 1
|
||||
}
|
||||
UniqueSemantics::FrameDirection => {
|
||||
|
@ -134,7 +147,7 @@ impl ValidateTypeSemantics<Type> for TextureSemantics {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> TryFrom<&GlslangCompilation> for CrossReflect<T>
|
||||
impl<T> TryFrom<&SpirvCompilation> for CrossReflect<T>
|
||||
where
|
||||
T: spirv_cross::spirv::Target,
|
||||
Ast<T>: spirv_cross::spirv::Compile<T>,
|
||||
|
@ -142,7 +155,7 @@ where
|
|||
{
|
||||
type Error = ShaderReflectError;
|
||||
|
||||
fn try_from(value: &GlslangCompilation) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: &SpirvCompilation) -> Result<Self, Self::Error> {
|
||||
let vertex_module = Module::from_words(&value.vertex);
|
||||
let fragment_module = Module::from_words(&value.fragment);
|
||||
|
||||
|
@ -722,184 +735,27 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl CompileShader<GLSL> for CrossReflect<glsl::Target> {
|
||||
type Options = glsl::Version;
|
||||
type Context = CrossGlslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
version: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
|
||||
let mut options: glsl::CompilerOptions = Default::default();
|
||||
options.version = version;
|
||||
options.fragment.default_float_precision = glsl::Precision::High;
|
||||
options.fragment.default_int_precision = glsl::Precision::High;
|
||||
options.enable_420_pack_extension = false;
|
||||
|
||||
self.vertex.set_compiler_options(&options)?;
|
||||
self.fragment.set_compiler_options(&options)?;
|
||||
|
||||
let vertex_resources = self.vertex.get_shader_resources()?;
|
||||
let fragment_resources = self.fragment.get_shader_resources()?;
|
||||
|
||||
for res in &vertex_resources.stage_inputs {
|
||||
self.vertex.unset_decoration(res.id, Decoration::Location)?;
|
||||
}
|
||||
for res in &vertex_resources.stage_outputs {
|
||||
// let location = self.vertex.get_decoration(res.id, Decoration::Location)?;
|
||||
// self.vertex
|
||||
// .set_name(res.id, &format!("LIBRA_VARYING_{location}"))?;
|
||||
self.vertex.unset_decoration(res.id, Decoration::Location)?;
|
||||
}
|
||||
for res in &fragment_resources.stage_inputs {
|
||||
// let location = self.fragment.get_decoration(res.id, Decoration::Location)?;
|
||||
// self.fragment
|
||||
// .set_name(res.id, &format!("LIBRA_VARYING_{location}"))?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::Location)?;
|
||||
}
|
||||
|
||||
if vertex_resources.push_constant_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one push constant buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
for res in &vertex_resources.push_constant_buffers {
|
||||
self.vertex.set_name(res.id, "LIBRA_PUSH_VERTEX_INSTANCE")?;
|
||||
self.vertex
|
||||
.set_name(res.base_type_id, "LIBRA_PUSH_VERTEX")?;
|
||||
}
|
||||
|
||||
// todo: options
|
||||
let _flatten = false;
|
||||
|
||||
if vertex_resources.uniform_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one uniform buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
for res in &vertex_resources.uniform_buffers {
|
||||
// if flatten {
|
||||
// self.vertex.flatten_buffer_block(res.id)?;
|
||||
// }
|
||||
self.vertex.set_name(res.id, "LIBRA_UBO_VERTEX_INSTANCE")?;
|
||||
self.vertex.set_name(res.base_type_id, "LIBRA_UBO_VERTEX")?;
|
||||
self.vertex
|
||||
.unset_decoration(res.id, Decoration::DescriptorSet)?;
|
||||
self.vertex.unset_decoration(res.id, Decoration::Binding)?;
|
||||
}
|
||||
|
||||
if fragment_resources.push_constant_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one push constant buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
for res in &fragment_resources.push_constant_buffers {
|
||||
self.fragment
|
||||
.set_name(res.id, "LIBRA_PUSH_FRAGMENT_INSTANCE")?;
|
||||
self.fragment
|
||||
.set_name(res.base_type_id, "LIBRA_PUSH_FRAGMENT")?;
|
||||
}
|
||||
|
||||
if fragment_resources.uniform_buffers.len() > 1 {
|
||||
return Err(ShaderCompileError::SpirvCrossCompileError(
|
||||
ErrorCode::CompilationError(String::from(
|
||||
"Cannot have more than one uniform buffer",
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
||||
for res in &fragment_resources.uniform_buffers {
|
||||
// if flatten {
|
||||
// self.fragment.flatten_buffer_block(res.id)?;
|
||||
// }
|
||||
self.fragment
|
||||
.set_name(res.id, "LIBRA_UBO_FRAGMENT_INSTANCE")?;
|
||||
self.fragment
|
||||
.set_name(res.base_type_id, "LIBRA_UBO_FRAGMENT")?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::DescriptorSet)?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::Binding)?;
|
||||
}
|
||||
|
||||
let mut texture_fixups = Vec::new();
|
||||
for res in fragment_resources.sampled_images {
|
||||
let binding = self.fragment.get_decoration(res.id, Decoration::Binding)?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::DescriptorSet)?;
|
||||
self.fragment
|
||||
.unset_decoration(res.id, Decoration::Binding)?;
|
||||
let mut name = res.name;
|
||||
name.push('\0');
|
||||
texture_fixups.push((name, binding));
|
||||
}
|
||||
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: self.vertex.compile()?,
|
||||
fragment: self.fragment.compile()?,
|
||||
context: CrossGlslContext {
|
||||
sampler_bindings: texture_fixups,
|
||||
artifact: CompiledProgram {
|
||||
vertex: CompiledAst(self.vertex),
|
||||
fragment: CompiledAst(self.fragment),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl CompileShader<HLSL> for CrossReflect<hlsl::Target> {
|
||||
type Options = Option<HlslShaderModel>;
|
||||
type Context = CrossHlslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, CrossHlslContext>, ShaderCompileError> {
|
||||
let sm = options.unwrap_or(HlslShaderModel::V5_0);
|
||||
let mut options = hlsl::CompilerOptions::default();
|
||||
options.shader_model = sm;
|
||||
|
||||
self.vertex.set_compiler_options(&options)?;
|
||||
self.fragment.set_compiler_options(&options)?;
|
||||
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: self.vertex.compile()?,
|
||||
fragment: self.fragment.compile()?,
|
||||
context: CrossHlslContext {
|
||||
artifact: CompiledProgram {
|
||||
vertex: CompiledAst(self.vertex),
|
||||
fragment: CompiledAst(self.fragment),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::reflect::cross::CrossReflect;
|
||||
use crate::reflect::ReflectShader;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::back::CompileShader;
|
||||
use crate::front::GlslangCompilation;
|
||||
use crate::back::hlsl::CrossHlslContext;
|
||||
use crate::back::targets::HLSL;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::front::{Glslang, ShaderInputCompiler};
|
||||
use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics};
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use spirv_cross::glsl;
|
||||
use spirv_cross::glsl::{CompilerOptions, Version};
|
||||
use spirv_cross::hlsl::ShaderModel;
|
||||
use spirv_cross::{glsl, hlsl};
|
||||
|
||||
#[test]
|
||||
pub fn test_into() {
|
||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
||||
let mut uniform_semantics: FxHashMap<String, UniformSemantic> = Default::default();
|
||||
let mut uniform_semantics: FastHashMap<String, UniformSemantic> = Default::default();
|
||||
|
||||
for (_index, param) in result.parameters.iter().enumerate() {
|
||||
uniform_semantics.insert(
|
||||
|
@ -910,9 +766,9 @@ mod test {
|
|||
}),
|
||||
);
|
||||
}
|
||||
let spirv = GlslangCompilation::compile(&result).unwrap();
|
||||
let mut reflect = CrossReflect::<glsl::Target>::try_from(&spirv).unwrap();
|
||||
let _shader_reflection = reflect
|
||||
let spirv = Glslang::compile(&result).unwrap();
|
||||
let mut reflect = CrossReflect::<hlsl::Target>::try_from(&spirv).unwrap();
|
||||
let shader_reflection = reflect
|
||||
.reflect(
|
||||
0,
|
||||
&ShaderSemantics {
|
||||
|
@ -921,10 +777,20 @@ mod test {
|
|||
},
|
||||
)
|
||||
.unwrap();
|
||||
let mut opts = CompilerOptions::default();
|
||||
opts.version = Version::V4_60;
|
||||
opts.enable_420_pack_extension = false;
|
||||
// let compiled: ShaderCompilerOutput<String, CrossWgslContext> = <CrossReflect<glsl::Target> as CompileShader<WGSL>>::compile(reflect, Version::V3_30).unwrap();
|
||||
let mut opts = hlsl::CompilerOptions::default();
|
||||
opts.shader_model = ShaderModel::V3_0;
|
||||
|
||||
let compiled: ShaderCompilerOutput<String, CrossHlslContext> =
|
||||
<CrossReflect<hlsl::Target> as CompileShader<HLSL>>::compile(
|
||||
reflect,
|
||||
Some(ShaderModel::V3_0),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("{:?}", shader_reflection.meta);
|
||||
println!("{}", compiled.fragment);
|
||||
println!("{}", compiled.vertex);
|
||||
|
||||
// // eprintln!("{shader_reflection:#?}");
|
||||
// eprintln!("{}", compiled.fragment)
|
||||
// let mut loader = rspirv::dr::Loader::new();
|
155
librashader-reflect/src/reflect/cross/msl.rs
Normal file
155
librashader-reflect/src/reflect/cross/msl.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
use crate::back::msl::CrossMslContext;
|
||||
use crate::back::targets::MSL;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::error::ShaderCompileError;
|
||||
use crate::reflect::cross::{CompiledAst, CompiledProgram, CrossReflect};
|
||||
use spirv_cross::msl;
|
||||
use spirv_cross::msl::{ResourceBinding, ResourceBindingLocation};
|
||||
use spirv_cross::spirv::{Ast, Decoration, ExecutionModel};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub(crate) type MslReflect = CrossReflect<spirv_cross::msl::Target>;
|
||||
|
||||
impl CompileShader<MSL> for CrossReflect<spirv_cross::msl::Target> {
|
||||
type Options = Option<spirv_cross::msl::Version>;
|
||||
type Context = CrossMslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, CrossMslContext>, ShaderCompileError> {
|
||||
let version = options.unwrap_or(msl::Version::V2_0);
|
||||
let mut vert_options = spirv_cross::msl::CompilerOptions::default();
|
||||
let mut frag_options = spirv_cross::msl::CompilerOptions::default();
|
||||
|
||||
vert_options.version = version;
|
||||
frag_options.version = version;
|
||||
|
||||
fn set_bindings(
|
||||
ast: &Ast<msl::Target>,
|
||||
stage: ExecutionModel,
|
||||
binding_map: &mut BTreeMap<ResourceBindingLocation, ResourceBinding>,
|
||||
) -> Result<(), ShaderCompileError> {
|
||||
let resources = ast.get_shader_resources()?;
|
||||
for resource in &resources.push_constant_buffers {
|
||||
let location = ResourceBindingLocation {
|
||||
stage,
|
||||
desc_set: msl::PUSH_CONSTANT_DESCRIPTOR_SET,
|
||||
binding: msl::PUSH_CONSTANT_BINDING,
|
||||
};
|
||||
let overridden = ResourceBinding {
|
||||
buffer_id: ast.get_decoration(resource.id, Decoration::Binding)?,
|
||||
texture_id: 0,
|
||||
sampler_id: 0,
|
||||
base_type: None,
|
||||
count: 0, // no arrays allowed in slang shaders, otherwise we'd have to get the type and get the array length
|
||||
};
|
||||
|
||||
binding_map.insert(location, overridden);
|
||||
}
|
||||
|
||||
for resource in resources
|
||||
.uniform_buffers
|
||||
.iter()
|
||||
.chain(resources.sampled_images.iter())
|
||||
{
|
||||
let binding = ast.get_decoration(resource.id, Decoration::Binding)?;
|
||||
let location = ResourceBindingLocation {
|
||||
stage,
|
||||
desc_set: ast.get_decoration(resource.id, Decoration::DescriptorSet)?,
|
||||
binding,
|
||||
};
|
||||
|
||||
let overridden = ResourceBinding {
|
||||
buffer_id: binding,
|
||||
texture_id: binding,
|
||||
sampler_id: binding,
|
||||
base_type: None,
|
||||
count: 0, // no arrays allowed in slang shaders, otherwise we'd have to get the type and get the array length
|
||||
};
|
||||
|
||||
binding_map.insert(location, overridden);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
set_bindings(
|
||||
&self.vertex,
|
||||
ExecutionModel::Vertex,
|
||||
&mut vert_options.resource_binding_overrides,
|
||||
)?;
|
||||
|
||||
set_bindings(
|
||||
&self.fragment,
|
||||
ExecutionModel::Fragment,
|
||||
&mut frag_options.resource_binding_overrides,
|
||||
)?;
|
||||
|
||||
self.vertex.set_compiler_options(&vert_options)?;
|
||||
self.fragment.set_compiler_options(&frag_options)?;
|
||||
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: self.vertex.compile()?,
|
||||
fragment: self.fragment.compile()?,
|
||||
context: CrossMslContext {
|
||||
artifact: CompiledProgram {
|
||||
vertex: CompiledAst(self.vertex),
|
||||
fragment: CompiledAst(self.fragment),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::back::targets::{MSL, WGSL};
|
||||
use crate::back::{CompileShader, FromCompilation};
|
||||
use crate::reflect::cross::SpirvCross;
|
||||
use crate::reflect::naga::{Naga, NagaLoweringOptions};
|
||||
use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics};
|
||||
use crate::reflect::ReflectShader;
|
||||
use bitflags::Flags;
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use rustc_hash::FxHashMap;
|
||||
use spirv_cross::msl;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
pub fn test_into() {
|
||||
// let result = ShaderSource::load("../test/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang").unwrap();
|
||||
// let result = ShaderSource::load("../test/shaders_slang/crt/shaders/crt-royale/src/crt-royale-scanlines-horizontal-apply-mask.slang").unwrap();
|
||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
||||
|
||||
let mut uniform_semantics: FastHashMap<String, UniformSemantic> = Default::default();
|
||||
|
||||
for (_index, param) in result.parameters.iter().enumerate() {
|
||||
uniform_semantics.insert(
|
||||
param.1.id.clone(),
|
||||
UniformSemantic::Unique(Semantic {
|
||||
semantics: UniqueSemantics::FloatParameter,
|
||||
index: (),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let compilation = crate::front::SpirvCompilation::try_from(&result).unwrap();
|
||||
|
||||
let mut msl =
|
||||
<MSL as FromCompilation<_, SpirvCross>>::from_compilation(compilation).unwrap();
|
||||
|
||||
msl.reflect(
|
||||
0,
|
||||
&ShaderSemantics {
|
||||
uniform_semantics,
|
||||
texture_semantics: Default::default(),
|
||||
},
|
||||
)
|
||||
.expect("");
|
||||
|
||||
let compiled = msl.compile(Some(msl::Version::V2_0)).unwrap();
|
||||
|
||||
println!("{}", compiled.vertex);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ pub mod presets;
|
|||
|
||||
mod helper;
|
||||
|
||||
#[cfg(feature = "wgsl")]
|
||||
#[cfg(feature = "naga")]
|
||||
pub mod naga;
|
||||
|
||||
/// A trait for compilation outputs that can provide reflection information.
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
pub mod msl;
|
||||
pub mod spirv;
|
||||
pub mod wgsl;
|
||||
|
||||
use crate::error::{SemanticsErrorKind, ShaderReflectError};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::front::SpirvCompilation;
|
||||
use naga::{
|
||||
AddressSpace, Binding, GlobalVariable, Handle, ImageClass, Module, ResourceBinding, Scalar,
|
||||
ScalarKind, TypeInner, VectorSize,
|
||||
AddressSpace, Binding, Expression, GlobalVariable, Handle, ImageClass, Module, ResourceBinding,
|
||||
Scalar, ScalarKind, StructMember, TypeInner, VectorSize,
|
||||
};
|
||||
use rspirv::binary::Assemble;
|
||||
use rspirv::dr::Builder;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::front::spirv_passes::lower_samplers;
|
||||
use crate::reflect::helper::{SemanticErrorBlame, TextureData, UboData};
|
||||
use crate::reflect::semantics::{
|
||||
BindingMeta, BindingStage, BufferReflection, MemberOffset, ShaderSemantics, TextureBinding,
|
||||
|
@ -14,12 +24,125 @@ use crate::reflect::semantics::{
|
|||
};
|
||||
use crate::reflect::{align_uniform_size, ReflectShader, ShaderReflection};
|
||||
|
||||
/// Reflect under Naga semantics
|
||||
///
|
||||
/// The Naga reflector will lower combined image samplers to split,
|
||||
/// with the same bind point on descriptor group 1.
|
||||
#[derive(Debug)]
|
||||
pub struct Naga;
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NagaReflect {
|
||||
pub(crate) vertex: Module,
|
||||
pub(crate) fragment: Module,
|
||||
}
|
||||
|
||||
/// Options to lower samplers and pcbs
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NagaLoweringOptions {
|
||||
/// Whether to write the PCB as a UBO.
|
||||
pub write_pcb_as_ubo: bool,
|
||||
/// The bind group to assign samplers to. This is to ensure that samplers will
|
||||
/// maintain the same bindings as textures.
|
||||
pub sampler_bind_group: u32,
|
||||
}
|
||||
|
||||
impl NagaReflect {
|
||||
pub fn do_lowering(&mut self, options: &NagaLoweringOptions) {
|
||||
if options.write_pcb_as_ubo {
|
||||
for (_, gv) in self.fragment.global_variables.iter_mut() {
|
||||
if gv.space == AddressSpace::PushConstant {
|
||||
gv.space = AddressSpace::Uniform;
|
||||
}
|
||||
}
|
||||
|
||||
for (_, gv) in self.vertex.global_variables.iter_mut() {
|
||||
if gv.space == AddressSpace::PushConstant {
|
||||
gv.space = AddressSpace::Uniform;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (_, gv) in self.fragment.global_variables.iter_mut() {
|
||||
if gv.space == AddressSpace::PushConstant {
|
||||
gv.binding = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reassign shit.
|
||||
let images = self
|
||||
.fragment
|
||||
.global_variables
|
||||
.iter()
|
||||
.filter(|&(_, gv)| {
|
||||
let ty = &self.fragment.types[gv.ty];
|
||||
match ty.inner {
|
||||
naga::TypeInner::Image { .. } => true,
|
||||
naga::TypeInner::BindingArray { base, .. } => {
|
||||
let ty = &self.fragment.types[base];
|
||||
matches!(ty.inner, naga::TypeInner::Image { .. })
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.map(|(_, gv)| (gv.binding.clone(), gv.space))
|
||||
.collect::<naga::FastHashSet<_>>();
|
||||
|
||||
self.fragment
|
||||
.global_variables
|
||||
.iter_mut()
|
||||
.filter(|(_, gv)| {
|
||||
let ty = &self.fragment.types[gv.ty];
|
||||
match ty.inner {
|
||||
naga::TypeInner::Sampler { .. } => true,
|
||||
naga::TypeInner::BindingArray { base, .. } => {
|
||||
let ty = &self.fragment.types[base];
|
||||
matches!(ty.inner, naga::TypeInner::Sampler { .. })
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.for_each(|(_, gv)| {
|
||||
if images.contains(&(gv.binding.clone(), gv.space)) {
|
||||
if let Some(binding) = &mut gv.binding {
|
||||
binding.group = options.sampler_bind_group;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&SpirvCompilation> for NagaReflect {
|
||||
type Error = ShaderReflectError;
|
||||
|
||||
fn try_from(compile: &SpirvCompilation) -> Result<Self, Self::Error> {
|
||||
fn lower_fragment_shader(builder: &mut Builder) {
|
||||
let mut pass = lower_samplers::LowerCombinedImageSamplerPass::new(builder);
|
||||
pass.ensure_op_type_sampler();
|
||||
pass.do_pass();
|
||||
}
|
||||
|
||||
let options = naga::front::spv::Options {
|
||||
adjust_coordinate_space: true,
|
||||
strict_capabilities: false,
|
||||
block_ctx_dump_prefix: None,
|
||||
};
|
||||
|
||||
let vertex = crate::front::spirv_passes::load_module(&compile.vertex);
|
||||
let fragment = crate::front::spirv_passes::load_module(&compile.fragment);
|
||||
|
||||
let mut fragment = Builder::new_from_module(fragment);
|
||||
lower_fragment_shader(&mut fragment);
|
||||
|
||||
let vertex = vertex.assemble();
|
||||
let fragment = fragment.module().assemble();
|
||||
|
||||
let vertex = naga::front::spv::parse_u8_slice(bytemuck::cast_slice(&vertex), &options)?;
|
||||
let fragment = naga::front::spv::parse_u8_slice(bytemuck::cast_slice(&fragment), &options)?;
|
||||
|
||||
Ok(NagaReflect { vertex, fragment })
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidateTypeSemantics<&TypeInner> for UniqueSemantics {
|
||||
fn validate_type(&self, ty: &&TypeInner) -> Option<TypeInfo> {
|
||||
let (TypeInner::Vector { .. } | TypeInner::Scalar { .. } | TypeInner::Matrix { .. }) = *ty
|
||||
|
@ -38,7 +161,10 @@ impl ValidateTypeSemantics<&TypeInner> for UniqueSemantics {
|
|||
});
|
||||
}
|
||||
}
|
||||
UniqueSemantics::FrameCount => {
|
||||
UniqueSemantics::FrameCount
|
||||
| UniqueSemantics::Rotation
|
||||
| UniqueSemantics::CurrentSubFrame
|
||||
| UniqueSemantics::TotalSubFrames => {
|
||||
// Uint32 == width 4
|
||||
if matches!(ty, TypeInner::Scalar( Scalar { kind, width }) if *kind == ScalarKind::Uint && *width == 4)
|
||||
{
|
||||
|
@ -49,7 +175,7 @@ impl ValidateTypeSemantics<&TypeInner> for UniqueSemantics {
|
|||
}
|
||||
}
|
||||
UniqueSemantics::FrameDirection => {
|
||||
// Uint32 == width 4
|
||||
// iint32 == width 4
|
||||
if matches!(ty, TypeInner::Scalar( Scalar { kind, width }) if *kind == ScalarKind::Sint && *width == 4)
|
||||
{
|
||||
return Some(TypeInfo {
|
||||
|
@ -238,13 +364,13 @@ impl NagaReflect {
|
|||
let binding = self.get_next_binding(0);
|
||||
// Reassign to UBO later if we want during compilation.
|
||||
if let Some(vertex_pcb) = vertex_pcb {
|
||||
let ubo = &mut self.vertex.global_variables[vertex_pcb];
|
||||
ubo.binding = Some(ResourceBinding { group: 0, binding });
|
||||
let pcb = &mut self.vertex.global_variables[vertex_pcb];
|
||||
pcb.binding = Some(ResourceBinding { group: 0, binding });
|
||||
}
|
||||
|
||||
if let Some(fragment_pcb) = fragment_pcb {
|
||||
let ubo = &mut self.fragment.global_variables[fragment_pcb];
|
||||
ubo.binding = Some(ResourceBinding { group: 0, binding });
|
||||
let pcb = &mut self.fragment.global_variables[fragment_pcb];
|
||||
pcb.binding = Some(ResourceBinding { group: 0, binding });
|
||||
};
|
||||
|
||||
match (vertex_pcb, fragment_pcb) {
|
||||
|
@ -474,6 +600,41 @@ impl NagaReflect {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_uniform_names(
|
||||
module: &Module,
|
||||
buffer_handle: Handle<GlobalVariable>,
|
||||
blame: SemanticErrorBlame,
|
||||
) -> Result<FxHashSet<&StructMember>, ShaderReflectError> {
|
||||
let mut names = FxHashSet::default();
|
||||
let ubo = &module.global_variables[buffer_handle];
|
||||
|
||||
let TypeInner::Struct { members, .. } = &module.types[ubo.ty].inner else {
|
||||
return Err(blame.error(SemanticsErrorKind::InvalidResourceType));
|
||||
};
|
||||
|
||||
// struct access is AccessIndex
|
||||
for (_, fun) in module.functions.iter() {
|
||||
for (_, expr) in fun.expressions.iter() {
|
||||
let &Expression::AccessIndex { base, index } = expr else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let &Expression::GlobalVariable(base) = &fun.expressions[base] else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if base == buffer_handle {
|
||||
let member = members
|
||||
.get(index as usize)
|
||||
.ok_or(blame.error(SemanticsErrorKind::InvalidRange(index)))?;
|
||||
names.insert(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(names)
|
||||
}
|
||||
|
||||
fn reflect_buffer_struct_members(
|
||||
module: &Module,
|
||||
resource: Handle<GlobalVariable>,
|
||||
|
@ -483,7 +644,10 @@ impl NagaReflect {
|
|||
offset_type: UniformMemberBlock,
|
||||
blame: SemanticErrorBlame,
|
||||
) -> Result<(), ShaderReflectError> {
|
||||
let reachable = Self::collect_uniform_names(&module, resource, blame)?;
|
||||
|
||||
let resource = &module.global_variables[resource];
|
||||
|
||||
let TypeInner::Struct { members, .. } = &module.types[resource.ty].inner else {
|
||||
return Err(blame.error(SemanticsErrorKind::InvalidResourceType));
|
||||
};
|
||||
|
@ -492,6 +656,11 @@ impl NagaReflect {
|
|||
let Some(name) = member.name.clone() else {
|
||||
return Err(blame.error(SemanticsErrorKind::InvalidRange(member.offset)));
|
||||
};
|
||||
|
||||
if !reachable.contains(member) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let member_type = &module.types[member.ty].inner;
|
||||
|
||||
if let Some(parameter) = semantics.uniform_semantics.get_unique_semantic(&name) {
|
||||
|
@ -756,7 +925,6 @@ impl ReflectShader for NagaReflect {
|
|||
});
|
||||
|
||||
let push_constant = self.reflect_push_constant_buffer(vertex_push, fragment_push)?;
|
||||
|
||||
let mut meta = BindingMeta::default();
|
||||
|
||||
if let Some(ubo) = vertex_ubo {
|
||||
|
@ -837,6 +1005,10 @@ impl ReflectShader for NagaReflect {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::reflect::semantics::{Semantic, TextureSemantics, UniformSemantic};
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use librashader_presets::ShaderPreset;
|
||||
|
||||
// #[test]
|
||||
// pub fn test_into() {
|
||||
|
@ -855,4 +1027,19 @@ mod test {
|
|||
//
|
||||
// println!("{outputs:#?}");
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// pub fn mega_bezel_reflect() {
|
||||
// let preset = ShaderPreset::try_parse(
|
||||
// "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let mut uniform_semantics: FastHashMap<String, UniformSemantic> = Default::default();
|
||||
// let mut texture_semantics: FastHashMap<String, Semantic<TextureSemantics>> = Default::default();
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// }
|
||||
}
|
207
librashader-reflect/src/reflect/naga/msl.rs
Normal file
207
librashader-reflect/src/reflect/naga/msl.rs
Normal file
|
@ -0,0 +1,207 @@
|
|||
use crate::back::msl::{MslVersion, NagaMslContext, NagaMslModule};
|
||||
use crate::back::targets::MSL;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::error::ShaderCompileError;
|
||||
use crate::reflect::naga::{NagaLoweringOptions, NagaReflect};
|
||||
use naga::back::msl::{
|
||||
BindSamplerTarget, BindTarget, EntryPointResources, Options, PipelineOptions, TranslationInfo,
|
||||
};
|
||||
use naga::valid::{Capabilities, ValidationFlags};
|
||||
use naga::{Module, TypeInner};
|
||||
use spirv_cross::msl::Version;
|
||||
|
||||
fn msl_version_to_naga_msl(version: MslVersion) -> (u8, u8) {
|
||||
match version {
|
||||
Version::V1_0 => (1, 0),
|
||||
Version::V1_1 => (1, 1),
|
||||
Version::V1_2 => (1, 2),
|
||||
Version::V2_0 => (2, 0),
|
||||
Version::V2_1 => (2, 1),
|
||||
Version::V2_2 => (2, 2),
|
||||
Version::V2_3 => (2, 3),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
impl CompileShader<MSL> for NagaReflect {
|
||||
type Options = Option<crate::back::msl::MslVersion>;
|
||||
type Context = NagaMslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
|
||||
// https://github.com/libretro/RetroArch/blob/434e94c782af2e4d4277a24b7ed8e5fc54870088/gfx/drivers_shader/slang_process.cpp#L524
|
||||
|
||||
let lang_version = msl_version_to_naga_msl(options.unwrap_or(MslVersion::V2_0));
|
||||
|
||||
let mut vert_options = Options {
|
||||
lang_version,
|
||||
per_entry_point_map: Default::default(),
|
||||
inline_samplers: vec![],
|
||||
spirv_cross_compatibility: true,
|
||||
fake_missing_bindings: false,
|
||||
bounds_check_policies: Default::default(),
|
||||
zero_initialize_workgroup_memory: false,
|
||||
};
|
||||
|
||||
let mut frag_options = vert_options.clone();
|
||||
|
||||
fn write_msl(
|
||||
module: &Module,
|
||||
options: Options,
|
||||
) -> Result<(String, TranslationInfo), ShaderCompileError> {
|
||||
let mut valid =
|
||||
naga::valid::Validator::new(ValidationFlags::all(), Capabilities::empty());
|
||||
let info = valid.validate(&module)?;
|
||||
|
||||
let pipeline_options = PipelineOptions {
|
||||
allow_and_force_point_size: false,
|
||||
};
|
||||
|
||||
let msl = naga::back::msl::write_string(&module, &info, &options, &pipeline_options)?;
|
||||
Ok(msl)
|
||||
}
|
||||
|
||||
fn generate_bindings(module: &Module) -> EntryPointResources {
|
||||
let mut resources = EntryPointResources::default();
|
||||
let binding_map = &mut resources.resources;
|
||||
// Don't set PCB because they'll be gone after lowering..
|
||||
// resources.push_constant_buffer = Some(1u8);
|
||||
|
||||
for (_, variable) in module.global_variables.iter() {
|
||||
let Some(binding) = &variable.binding else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(ty) = module.types.get_handle(variable.ty) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match ty.inner {
|
||||
TypeInner::Sampler { .. } => {
|
||||
binding_map.insert(
|
||||
binding.clone(),
|
||||
BindTarget {
|
||||
buffer: None,
|
||||
texture: None,
|
||||
sampler: Some(BindSamplerTarget::Resource(binding.binding as u8)),
|
||||
binding_array_size: None,
|
||||
mutable: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
TypeInner::Struct { .. } => {
|
||||
binding_map.insert(
|
||||
binding.clone(),
|
||||
BindTarget {
|
||||
buffer: Some(binding.binding as u8),
|
||||
texture: None,
|
||||
sampler: None,
|
||||
binding_array_size: None,
|
||||
mutable: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
TypeInner::Image { .. } => {
|
||||
binding_map.insert(
|
||||
binding.clone(),
|
||||
BindTarget {
|
||||
buffer: None,
|
||||
texture: Some(binding.binding as u8),
|
||||
sampler: None,
|
||||
binding_array_size: None,
|
||||
mutable: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
resources
|
||||
}
|
||||
|
||||
self.do_lowering(&NagaLoweringOptions {
|
||||
write_pcb_as_ubo: true,
|
||||
sampler_bind_group: 1,
|
||||
});
|
||||
|
||||
frag_options
|
||||
.per_entry_point_map
|
||||
.insert(String::from("main"), generate_bindings(&self.fragment));
|
||||
vert_options
|
||||
.per_entry_point_map
|
||||
.insert(String::from("main"), generate_bindings(&self.vertex));
|
||||
|
||||
let fragment = write_msl(&self.fragment, frag_options)?;
|
||||
let vertex = write_msl(&self.vertex, vert_options)?;
|
||||
|
||||
let vertex_binding = self.get_next_binding(0);
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex: vertex.0,
|
||||
fragment: fragment.0,
|
||||
context: NagaMslContext {
|
||||
fragment: NagaMslModule {
|
||||
translation_info: fragment.1,
|
||||
module: self.fragment,
|
||||
},
|
||||
vertex: NagaMslModule {
|
||||
translation_info: vertex.1,
|
||||
module: self.vertex,
|
||||
},
|
||||
next_free_binding: vertex_binding,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::back::targets::MSL;
|
||||
use crate::back::{CompileShader, FromCompilation};
|
||||
use crate::reflect::naga::{Naga, NagaLoweringOptions};
|
||||
use crate::reflect::semantics::{Semantic, ShaderSemantics, UniformSemantic, UniqueSemantics};
|
||||
use crate::reflect::ReflectShader;
|
||||
use bitflags::Flags;
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use spirv_cross::msl;
|
||||
|
||||
#[test]
|
||||
pub fn test_into() {
|
||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
||||
|
||||
let mut uniform_semantics: FastHashMap<String, UniformSemantic> = Default::default();
|
||||
|
||||
for (_index, param) in result.parameters.iter().enumerate() {
|
||||
uniform_semantics.insert(
|
||||
param.1.id.clone(),
|
||||
UniformSemantic::Unique(Semantic {
|
||||
semantics: UniqueSemantics::FloatParameter,
|
||||
index: (),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let compilation = crate::front::SpirvCompilation::try_from(&result).unwrap();
|
||||
|
||||
let mut msl = <MSL as FromCompilation<_, Naga>>::from_compilation(compilation).unwrap();
|
||||
|
||||
msl.reflect(
|
||||
0,
|
||||
&ShaderSemantics {
|
||||
uniform_semantics,
|
||||
texture_semantics: Default::default(),
|
||||
},
|
||||
)
|
||||
.expect("");
|
||||
|
||||
let compiled = msl.compile(Some(msl::Version::V2_0)).unwrap();
|
||||
|
||||
println!(
|
||||
"{:?}",
|
||||
compiled.context.fragment.translation_info.entry_point_names
|
||||
);
|
||||
}
|
||||
}
|
54
librashader-reflect/src/reflect/naga/spirv.rs
Normal file
54
librashader-reflect/src/reflect/naga/spirv.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use crate::back::spirv::{NagaSpirvContext, NagaSpirvOptions};
|
||||
use crate::back::targets::SPIRV;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::error::ShaderCompileError;
|
||||
use crate::reflect::naga::NagaReflect;
|
||||
use naga::back::spv::PipelineOptions;
|
||||
use naga::valid::{Capabilities, ValidationFlags};
|
||||
use naga::Module;
|
||||
|
||||
impl CompileShader<SPIRV> for NagaReflect {
|
||||
type Options = NagaSpirvOptions;
|
||||
type Context = NagaSpirvContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<Vec<u32>, Self::Context>, ShaderCompileError> {
|
||||
fn write_spv(
|
||||
module: &Module,
|
||||
stage: naga::ShaderStage,
|
||||
version: (u8, u8),
|
||||
) -> Result<Vec<u32>, ShaderCompileError> {
|
||||
let mut valid =
|
||||
naga::valid::Validator::new(ValidationFlags::all(), Capabilities::empty());
|
||||
let info = valid.validate(&module)?;
|
||||
let mut options = naga::back::spv::Options::default();
|
||||
options.lang_version = version;
|
||||
|
||||
let spv = naga::back::spv::write_vec(
|
||||
&module,
|
||||
&info,
|
||||
&options,
|
||||
Some(&PipelineOptions {
|
||||
shader_stage: stage,
|
||||
entry_point: "main".to_string(),
|
||||
}),
|
||||
)?;
|
||||
Ok(spv)
|
||||
}
|
||||
|
||||
self.do_lowering(&options.lowering);
|
||||
|
||||
let fragment = write_spv(&self.fragment, naga::ShaderStage::Fragment, options.version)?;
|
||||
let vertex = write_spv(&self.vertex, naga::ShaderStage::Vertex, options.version)?;
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex,
|
||||
fragment,
|
||||
context: NagaSpirvContext {
|
||||
fragment: self.fragment,
|
||||
vertex: self.vertex,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
41
librashader-reflect/src/reflect/naga/wgsl.rs
Normal file
41
librashader-reflect/src/reflect/naga/wgsl.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use crate::back::targets::WGSL;
|
||||
use crate::back::wgsl::NagaWgslContext;
|
||||
use crate::back::{CompileShader, ShaderCompilerOutput};
|
||||
use crate::error::ShaderCompileError;
|
||||
use crate::reflect::naga::{NagaLoweringOptions, NagaReflect};
|
||||
use naga::back::wgsl::WriterFlags;
|
||||
use naga::valid::{Capabilities, ModuleInfo, ValidationFlags, Validator};
|
||||
use naga::Module;
|
||||
|
||||
impl CompileShader<WGSL> for NagaReflect {
|
||||
type Options = NagaLoweringOptions;
|
||||
type Context = NagaWgslContext;
|
||||
|
||||
fn compile(
|
||||
mut self,
|
||||
options: Self::Options,
|
||||
) -> Result<ShaderCompilerOutput<String, Self::Context>, ShaderCompileError> {
|
||||
fn write_wgsl(module: &Module, info: &ModuleInfo) -> Result<String, ShaderCompileError> {
|
||||
let wgsl = naga::back::wgsl::write_string(&module, &info, WriterFlags::empty())?;
|
||||
Ok(wgsl)
|
||||
}
|
||||
|
||||
self.do_lowering(&options);
|
||||
|
||||
let mut valid = Validator::new(ValidationFlags::all(), Capabilities::empty());
|
||||
|
||||
let vertex_info = valid.validate(&self.vertex)?;
|
||||
let fragment_info = valid.validate(&self.fragment)?;
|
||||
|
||||
let fragment = write_wgsl(&self.fragment, &fragment_info)?;
|
||||
let vertex = write_wgsl(&self.vertex, &vertex_info)?;
|
||||
Ok(ShaderCompilerOutput {
|
||||
vertex,
|
||||
fragment,
|
||||
context: NagaWgslContext {
|
||||
fragment: self.fragment,
|
||||
vertex: self.vertex,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
use crate::back::targets::OutputTarget;
|
||||
use crate::back::{CompilerBackend, FromCompilation};
|
||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
||||
use crate::front::ShaderCompilation;
|
||||
use crate::front::{ShaderInputCompiler, ShaderReflectObject};
|
||||
use crate::reflect::semantics::{
|
||||
Semantic, ShaderSemantics, TextureSemantics, UniformSemantic, UniqueSemantics,
|
||||
};
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_preprocess::{PreprocessError, ShaderSource};
|
||||
use librashader_presets::{ShaderPassConfig, TextureConfig};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
/// Artifacts of a reflected and compiled shader pass.
|
||||
///
|
||||
|
@ -18,11 +18,12 @@ use rustc_hash::FxHashMap;
|
|||
/// ```rust
|
||||
/// #![feature(type_alias_impl_trait)]
|
||||
/// use librashader_reflect::back::CompileReflectShader;
|
||||
/// use librashader_reflect::back::targets::SPIRV;
|
||||
/// use librashader_reflect::front::GlslangCompilation;
|
||||
/// use librashader_reflect::back::targets::{GLSL, SPIRV};
|
||||
/// use librashader_reflect::front::SpirvCompilation;
|
||||
/// use librashader_reflect::reflect::cross::SpirvCross;
|
||||
/// use librashader_reflect::reflect::presets::ShaderPassArtifact;
|
||||
///
|
||||
/// type VulkanPassMeta = ShaderPassArtifact<impl CompileReflectShader<SPIRV, GlslangCompilation>>;
|
||||
/// type VulkanPassMeta = ShaderPassArtifact<impl CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross>>;
|
||||
/// ```
|
||||
///
|
||||
/// This allows a runtime to not name the backing type of the compiled artifact if not necessary.
|
||||
|
@ -35,57 +36,59 @@ impl<T: OutputTarget> CompilePresetTarget for T {}
|
|||
pub trait CompilePresetTarget: OutputTarget {
|
||||
/// Compile passes of a shader preset given the applicable
|
||||
/// shader output target, compilation type, and resulting error.
|
||||
fn compile_preset_passes<C, E>(
|
||||
fn compile_preset_passes<I, R, E>(
|
||||
passes: Vec<ShaderPassConfig>,
|
||||
textures: &[TextureConfig],
|
||||
) -> Result<
|
||||
(
|
||||
Vec<ShaderPassArtifact<<Self as FromCompilation<C>>::Output>>,
|
||||
Vec<ShaderPassArtifact<<Self as FromCompilation<I, R>>::Output>>,
|
||||
ShaderSemantics,
|
||||
),
|
||||
E,
|
||||
>
|
||||
where
|
||||
I: ShaderReflectObject,
|
||||
Self: Sized,
|
||||
Self: FromCompilation<C>,
|
||||
C: ShaderCompilation,
|
||||
Self: FromCompilation<I, R>,
|
||||
I::Compiler: ShaderInputCompiler<I>,
|
||||
E: From<PreprocessError>,
|
||||
E: From<ShaderReflectError>,
|
||||
E: From<ShaderCompileError>,
|
||||
{
|
||||
compile_preset_passes::<Self, C, E>(passes, textures)
|
||||
compile_preset_passes::<Self, I, R, E>(passes, textures)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile passes of a shader preset given the applicable
|
||||
/// shader output target, compilation type, and resulting error.
|
||||
fn compile_preset_passes<T, C, E>(
|
||||
fn compile_preset_passes<T, I, R, E>(
|
||||
passes: Vec<ShaderPassConfig>,
|
||||
textures: &[TextureConfig],
|
||||
) -> Result<
|
||||
(
|
||||
Vec<ShaderPassArtifact<<T as FromCompilation<C>>::Output>>,
|
||||
Vec<ShaderPassArtifact<<T as FromCompilation<I, R>>::Output>>,
|
||||
ShaderSemantics,
|
||||
),
|
||||
E,
|
||||
>
|
||||
where
|
||||
I: ShaderReflectObject,
|
||||
T: OutputTarget,
|
||||
T: FromCompilation<C>,
|
||||
C: ShaderCompilation,
|
||||
T: FromCompilation<I, R>,
|
||||
I::Compiler: ShaderInputCompiler<I>,
|
||||
E: From<PreprocessError>,
|
||||
E: From<ShaderReflectError>,
|
||||
E: From<ShaderCompileError>,
|
||||
{
|
||||
let mut uniform_semantics: FxHashMap<String, UniformSemantic> = Default::default();
|
||||
let mut texture_semantics: FxHashMap<String, Semantic<TextureSemantics>> = Default::default();
|
||||
let mut uniform_semantics: FastHashMap<String, UniformSemantic> = Default::default();
|
||||
let mut texture_semantics: FastHashMap<String, Semantic<TextureSemantics>> = Default::default();
|
||||
|
||||
let passes = passes
|
||||
.into_iter()
|
||||
.map(|shader| {
|
||||
let source: ShaderSource = ShaderSource::load(&shader.name)?;
|
||||
|
||||
let compiled = C::compile(&source)?;
|
||||
let compiled = I::Compiler::compile(&source)?;
|
||||
let reflect = T::from_compilation(compiled)?;
|
||||
|
||||
for parameter in source.parameters.values() {
|
||||
|
@ -116,8 +119,8 @@ where
|
|||
|
||||
/// Insert the available semantics for the input pass config into the provided semantic maps.
|
||||
fn insert_pass_semantics(
|
||||
uniform_semantics: &mut FxHashMap<String, UniformSemantic>,
|
||||
texture_semantics: &mut FxHashMap<String, Semantic<TextureSemantics>>,
|
||||
uniform_semantics: &mut FastHashMap<String, UniformSemantic>,
|
||||
texture_semantics: &mut FastHashMap<String, Semantic<TextureSemantics>>,
|
||||
config: &ShaderPassConfig,
|
||||
) {
|
||||
let Some(alias) = &config.alias else {
|
||||
|
@ -167,8 +170,8 @@ fn insert_pass_semantics(
|
|||
/// Insert the available semantics for the input texture config into the provided semantic maps.
|
||||
fn insert_lut_semantics(
|
||||
textures: &[TextureConfig],
|
||||
uniform_semantics: &mut FxHashMap<String, UniformSemantic>,
|
||||
texture_semantics: &mut FxHashMap<String, Semantic<TextureSemantics>>,
|
||||
uniform_semantics: &mut FastHashMap<String, UniformSemantic>,
|
||||
texture_semantics: &mut FastHashMap<String, Semantic<TextureSemantics>>,
|
||||
) {
|
||||
for (index, texture) in textures.iter().enumerate() {
|
||||
texture_semantics.insert(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bitflags::bitflags;
|
||||
use rustc_hash::FxHashMap;
|
||||
use librashader_common::map::FastHashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// The maximum number of bindings allowed in a shader.
|
||||
|
@ -40,11 +40,18 @@ pub enum UniqueSemantics {
|
|||
/// The frame count, possibly with shader-defined modulo.
|
||||
FrameCount = 3,
|
||||
// int, frame direction
|
||||
/// The frame direction.
|
||||
/// The direction in time where frames are rendered
|
||||
FrameDirection = 4,
|
||||
//int, rotation (glUniform1i(uni->rotation, retroarch_get_rotation());)
|
||||
/// The rotation index (0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg)
|
||||
Rotation = 5,
|
||||
/// Total number of subframes.
|
||||
TotalSubFrames = 6,
|
||||
/// The current subframe (default 1)
|
||||
CurrentSubFrame = 7,
|
||||
/// A user defined float parameter.
|
||||
// float, user defined parameter, array
|
||||
FloatParameter = 5,
|
||||
FloatParameter = 8,
|
||||
}
|
||||
|
||||
impl UniqueSemantics {
|
||||
|
@ -64,6 +71,9 @@ impl UniqueSemantics {
|
|||
UniqueSemantics::FinalViewport => UniformType::Vec4,
|
||||
UniqueSemantics::FrameCount => UniformType::Unsigned,
|
||||
UniqueSemantics::FrameDirection => UniformType::Signed,
|
||||
UniqueSemantics::Rotation => UniformType::Unsigned,
|
||||
UniqueSemantics::TotalSubFrames => UniformType::Unsigned,
|
||||
UniqueSemantics::CurrentSubFrame => UniformType::Unsigned,
|
||||
UniqueSemantics::FloatParameter => UniformType::Float,
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +172,7 @@ pub struct Semantic<T, I = usize> {
|
|||
|
||||
bitflags! {
|
||||
/// The pipeline stage for which a uniform is bound.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
|
||||
pub struct BindingStage: u8 {
|
||||
const NONE = 0b00000000;
|
||||
const VERTEX = 0b00000001;
|
||||
|
@ -310,7 +321,7 @@ pub trait TextureSemanticMap {
|
|||
fn get_texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>>;
|
||||
}
|
||||
|
||||
impl TextureSemanticMap for FxHashMap<String, UniformSemantic> {
|
||||
impl TextureSemanticMap for FastHashMap<String, UniformSemantic> {
|
||||
fn get_texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>> {
|
||||
match self.get(name) {
|
||||
None => {
|
||||
|
@ -342,7 +353,7 @@ impl TextureSemanticMap for FxHashMap<String, UniformSemantic> {
|
|||
}
|
||||
}
|
||||
|
||||
impl TextureSemanticMap for FxHashMap<String, Semantic<TextureSemantics>> {
|
||||
impl TextureSemanticMap for FastHashMap<String, Semantic<TextureSemantics>> {
|
||||
fn get_texture_semantic(&self, name: &str) -> Option<Semantic<TextureSemantics>> {
|
||||
match self.get(name) {
|
||||
None => {
|
||||
|
@ -379,7 +390,7 @@ pub trait UniqueSemanticMap {
|
|||
fn get_unique_semantic(&self, name: &str) -> Option<Semantic<UniqueSemantics, ()>>;
|
||||
}
|
||||
|
||||
impl UniqueSemanticMap for FxHashMap<String, UniformSemantic> {
|
||||
impl UniqueSemanticMap for FastHashMap<String, UniformSemantic> {
|
||||
fn get_unique_semantic(&self, name: &str) -> Option<Semantic<UniqueSemantics, ()>> {
|
||||
match self.get(name) {
|
||||
// existing uniforms in the semantic map have priority
|
||||
|
@ -404,6 +415,18 @@ impl UniqueSemanticMap for FxHashMap<String, UniformSemantic> {
|
|||
semantics: UniqueSemantics::FrameDirection,
|
||||
index: (),
|
||||
}),
|
||||
"Rotation" => Some(Semantic {
|
||||
semantics: UniqueSemantics::Rotation,
|
||||
index: (),
|
||||
}),
|
||||
"TotalSubFrames" => Some(Semantic {
|
||||
semantics: UniqueSemantics::TotalSubFrames,
|
||||
index: (),
|
||||
}),
|
||||
"CurrentSubFrame" => Some(Semantic {
|
||||
semantics: UniqueSemantics::TotalSubFrames,
|
||||
index: (),
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
Some(UniformSemantic::Unique(variable)) => Some(*variable),
|
||||
|
@ -425,9 +448,9 @@ pub enum UniformSemantic {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct ShaderSemantics {
|
||||
/// A map of uniform names to filter chain semantics.
|
||||
pub uniform_semantics: FxHashMap<String, UniformSemantic>,
|
||||
pub uniform_semantics: FastHashMap<String, UniformSemantic>,
|
||||
/// A map of texture names to filter chain semantics.
|
||||
pub texture_semantics: FxHashMap<String, Semantic<TextureSemantics>>,
|
||||
pub texture_semantics: FastHashMap<String, Semantic<TextureSemantics>>,
|
||||
}
|
||||
|
||||
/// The binding of a uniform after the shader has been linked.
|
||||
|
@ -460,11 +483,11 @@ impl From<Semantic<TextureSemantics>> for UniformBinding {
|
|||
#[derive(Debug, Default)]
|
||||
pub struct BindingMeta {
|
||||
/// A map of parameter names to uniform binding metadata.
|
||||
pub parameter_meta: FxHashMap<String, VariableMeta>,
|
||||
pub parameter_meta: FastHashMap<String, VariableMeta>,
|
||||
/// A map of unique semantics to uniform binding metadata.
|
||||
pub unique_meta: FxHashMap<UniqueSemantics, VariableMeta>,
|
||||
pub unique_meta: FastHashMap<UniqueSemantics, VariableMeta>,
|
||||
/// A map of texture semantics to texture binding points.
|
||||
pub texture_meta: FxHashMap<Semantic<TextureSemantics>, TextureBinding>,
|
||||
pub texture_meta: FastHashMap<Semantic<TextureSemantics>, TextureBinding>,
|
||||
/// A map of texture semantics to texture size uniform binding metadata.
|
||||
pub texture_size_meta: FxHashMap<Semantic<TextureSemantics>, TextureSizeMeta>,
|
||||
pub texture_size_meta: FastHashMap<Semantic<TextureSemantics>, TextureSizeMeta>,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "librashader-runtime-d3d11"
|
|||
edition = "2021"
|
||||
|
||||
license = "MPL-2.0 OR GPL-3.0-only"
|
||||
version = "0.2.0-beta.7"
|
||||
version = "0.2.7"
|
||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||
repository = "https://github.com/SnowflakePowered/librashader"
|
||||
readme = "../README.md"
|
||||
|
@ -12,22 +12,20 @@ keywords = ["shader", "retroarch", "SPIR-V"]
|
|||
description = "RetroArch shaders for all."
|
||||
|
||||
[dependencies]
|
||||
librashader-common = { path = "../librashader-common", features = ["d3d11"], version = "0.2.0-beta.7" }
|
||||
librashader-presets = { path = "../librashader-presets", version = "0.2.0-beta.7" }
|
||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.0-beta.7" }
|
||||
librashader-reflect = { path = "../librashader-reflect", version = "0.2.0-beta.7" }
|
||||
librashader-runtime = { path = "../librashader-runtime", version = "0.2.0-beta.7" }
|
||||
spirv_cross = { package = "librashader-spirv-cross", version = "0.23" }
|
||||
librashader-cache = { path = "../librashader-cache", version = "0.2.0-beta.7", features = ["d3d"] }
|
||||
librashader-common = { path = "../librashader-common", features = ["d3d11"], version = "0.2.7" }
|
||||
librashader-presets = { path = "../librashader-presets", version = "0.2.7" }
|
||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.7" }
|
||||
librashader-reflect = { path = "../librashader-reflect", version = "0.2.7" }
|
||||
librashader-runtime = { path = "../librashader-runtime", version = "0.2.7" }
|
||||
librashader-cache = { path = "../librashader-cache", version = "0.2.7", features = ["d3d"] }
|
||||
|
||||
thiserror = "1.0.37"
|
||||
rustc-hash = "1.1.0"
|
||||
bytemuck = "1.12.3"
|
||||
rayon = "1.6.1"
|
||||
array-concat = "0.5.2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.48.0"
|
||||
workspace = true
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dxgi_Common",
|
||||
|
@ -39,7 +37,7 @@ features = [
|
|||
]
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.windows]
|
||||
version = "0.48.0"
|
||||
workspace = true
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Dxgi_Common",
|
||||
|
@ -51,6 +49,7 @@ features = [
|
|||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Threading",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_UI",
|
||||
]
|
||||
|
||||
[[test]]
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::error;
|
|||
use crate::error::assume_d3d11_init;
|
||||
use array_concat::concat_arrays;
|
||||
use bytemuck::offset_of;
|
||||
use librashader_runtime::quad::QuadType;
|
||||
use librashader_runtime::quad::{QuadType, VertexInput};
|
||||
use windows::core::PCSTR;
|
||||
use windows::Win32::Graphics::Direct3D::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
|
@ -12,63 +12,45 @@ use windows::Win32::Graphics::Direct3D11::{
|
|||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R32G32_FLOAT;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
struct D3D11Vertex {
|
||||
position: [f32; 2],
|
||||
texcoord: [f32; 2],
|
||||
color: [f32; 4],
|
||||
}
|
||||
|
||||
const CLEAR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
const OFFSCREEN_VBO_DATA: [D3D11Vertex; 4] = [
|
||||
D3D11Vertex {
|
||||
position: [-1.0, -1.0],
|
||||
const OFFSCREEN_VBO_DATA: [VertexInput; 4] = [
|
||||
VertexInput {
|
||||
position: [-1.0, -1.0, 0.0, 1.0],
|
||||
texcoord: [0.0, 1.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
D3D11Vertex {
|
||||
position: [-1.0, 1.0],
|
||||
VertexInput {
|
||||
position: [-1.0, 1.0, 0.0, 1.0],
|
||||
texcoord: [0.0, 0.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
D3D11Vertex {
|
||||
position: [1.0, -1.0],
|
||||
VertexInput {
|
||||
position: [1.0, -1.0, 0.0, 1.0],
|
||||
texcoord: [1.0, 1.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
D3D11Vertex {
|
||||
position: [1.0, 1.0],
|
||||
VertexInput {
|
||||
position: [1.0, 1.0, 0.0, 1.0],
|
||||
texcoord: [1.0, 0.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
];
|
||||
|
||||
const FINAL_VBO_DATA: [D3D11Vertex; 4] = [
|
||||
D3D11Vertex {
|
||||
position: [0.0, 0.0],
|
||||
const FINAL_VBO_DATA: [VertexInput; 4] = [
|
||||
VertexInput {
|
||||
position: [0.0, 0.0, 0.0, 1.0],
|
||||
texcoord: [0.0, 1.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
D3D11Vertex {
|
||||
position: [0.0, 1.0],
|
||||
VertexInput {
|
||||
position: [0.0, 1.0, 0.0, 1.0],
|
||||
texcoord: [0.0, 0.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
D3D11Vertex {
|
||||
position: [1.0, 0.0],
|
||||
VertexInput {
|
||||
position: [1.0, 0.0, 0.0, 1.0],
|
||||
texcoord: [1.0, 1.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
D3D11Vertex {
|
||||
position: [1.0, 1.0],
|
||||
VertexInput {
|
||||
position: [1.0, 1.0, 0.0, 1.0],
|
||||
texcoord: [1.0, 0.0],
|
||||
color: CLEAR,
|
||||
},
|
||||
];
|
||||
|
||||
static VBO_DATA: &[D3D11Vertex; 8] = &concat_arrays!(OFFSCREEN_VBO_DATA, FINAL_VBO_DATA);
|
||||
static VBO_DATA: &[VertexInput; 8] = &concat_arrays!(OFFSCREEN_VBO_DATA, FINAL_VBO_DATA);
|
||||
|
||||
pub(crate) struct DrawQuad {
|
||||
stride: u32,
|
||||
|
@ -81,9 +63,9 @@ impl DrawQuad {
|
|||
let mut vbo = None;
|
||||
device.CreateBuffer(
|
||||
&D3D11_BUFFER_DESC {
|
||||
ByteWidth: 2 * std::mem::size_of::<[D3D11Vertex; 4]>() as u32,
|
||||
ByteWidth: 2 * std::mem::size_of::<[VertexInput; 4]>() as u32,
|
||||
Usage: D3D11_USAGE_IMMUTABLE,
|
||||
BindFlags: D3D11_BIND_VERTEX_BUFFER,
|
||||
BindFlags: D3D11_BIND_VERTEX_BUFFER.0 as u32,
|
||||
CPUAccessFlags: Default::default(),
|
||||
MiscFlags: Default::default(),
|
||||
StructureByteStride: 0,
|
||||
|
@ -99,7 +81,7 @@ impl DrawQuad {
|
|||
|
||||
Ok(DrawQuad {
|
||||
vbo,
|
||||
stride: std::mem::size_of::<D3D11Vertex>() as u32,
|
||||
stride: std::mem::size_of::<VertexInput>() as u32,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +118,7 @@ impl DrawQuad {
|
|||
SemanticIndex: 0,
|
||||
Format: DXGI_FORMAT_R32G32_FLOAT,
|
||||
InputSlot: 0,
|
||||
AlignedByteOffset: offset_of!(D3D11Vertex, position) as u32,
|
||||
AlignedByteOffset: offset_of!(VertexInput, position) as u32,
|
||||
InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
|
||||
InstanceDataStepRate: 0,
|
||||
},
|
||||
|
@ -145,7 +127,7 @@ impl DrawQuad {
|
|||
SemanticIndex: 1,
|
||||
Format: DXGI_FORMAT_R32G32_FLOAT,
|
||||
InputSlot: 0,
|
||||
AlignedByteOffset: offset_of!(D3D11Vertex, texcoord) as u32,
|
||||
AlignedByteOffset: offset_of!(VertexInput, texcoord) as u32,
|
||||
InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
|
||||
InstanceDataStepRate: 0,
|
||||
},
|
||||
|
|
|
@ -13,8 +13,6 @@ pub enum FilterChainError {
|
|||
Direct3DOperationError(&'static str),
|
||||
#[error("direct3d driver error")]
|
||||
Direct3DError(#[from] windows::core::Error),
|
||||
#[error("SPIRV reflection error")]
|
||||
SpirvCrossReflectError(#[from] spirv_cross::ErrorCode),
|
||||
#[error("shader preset parse error")]
|
||||
ShaderPresetError(#[from] ParsePresetError),
|
||||
#[error("shader preprocess error")]
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use crate::texture::{D3D11InputView, InputTexture, LutTexture};
|
||||
use crate::texture::{D3D11InputView, InputTexture};
|
||||
use librashader_common::{ImageFormat, Size, Viewport};
|
||||
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_presets::{ShaderPassConfig, ShaderPreset, TextureConfig};
|
||||
use librashader_reflect::back::targets::HLSL;
|
||||
use librashader_reflect::back::{CompileReflectShader, CompileShader};
|
||||
use librashader_reflect::front::GlslangCompilation;
|
||||
use librashader_reflect::front::SpirvCompilation;
|
||||
use librashader_reflect::reflect::semantics::ShaderSemantics;
|
||||
use librashader_reflect::reflect::ReflectShader;
|
||||
use librashader_runtime::image::{Image, ImageError, UVDirection};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use std::path::Path;
|
||||
|
@ -18,12 +18,15 @@ use crate::error::{assume_d3d11_init, FilterChainError};
|
|||
use crate::filter_pass::{ConstantBufferBinding, FilterPass};
|
||||
use crate::framebuffer::OwnedImage;
|
||||
use crate::graphics_pipeline::D3D11State;
|
||||
use crate::luts::LutTexture;
|
||||
use crate::options::{FilterChainOptionsD3D11, FrameOptionsD3D11};
|
||||
use crate::samplers::SamplerSet;
|
||||
use crate::util::d3d11_compile_bound_shader;
|
||||
use crate::{error, util, D3D11OutputView};
|
||||
use librashader_cache::cache_shader_object;
|
||||
use librashader_cache::CachedCompilation;
|
||||
use librashader_presets::context::VideoDriver;
|
||||
use librashader_reflect::reflect::cross::SpirvCross;
|
||||
use librashader_reflect::reflect::presets::{CompilePresetTarget, ShaderPassArtifact};
|
||||
use librashader_runtime::binding::{BindingUtil, TextureInput};
|
||||
use librashader_runtime::framebuffer::FramebufferInit;
|
||||
|
@ -34,15 +37,14 @@ use librashader_runtime::uniforms::UniformStorage;
|
|||
use rayon::prelude::*;
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
ID3D11Buffer, ID3D11Device, ID3D11DeviceContext, D3D11_BIND_CONSTANT_BUFFER, D3D11_BUFFER_DESC,
|
||||
D3D11_CPU_ACCESS_WRITE, D3D11_CREATE_DEVICE_SINGLETHREADED, D3D11_RESOURCE_MISC_FLAG,
|
||||
D3D11_RESOURCE_MISC_GENERATE_MIPS, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT,
|
||||
D3D11_USAGE_DYNAMIC,
|
||||
D3D11_CPU_ACCESS_WRITE, D3D11_CREATE_DEVICE_SINGLETHREADED, D3D11_RESOURCE_MISC_GENERATE_MIPS,
|
||||
D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11_USAGE_DYNAMIC,
|
||||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
pub struct FilterMutable {
|
||||
pub(crate) passes_enabled: usize,
|
||||
pub(crate) parameters: FxHashMap<String, f32>,
|
||||
pub(crate) parameters: FastHashMap<String, f32>,
|
||||
}
|
||||
|
||||
/// A Direct3D 11 filter chain.
|
||||
|
@ -53,6 +55,7 @@ pub struct FilterChainD3D11 {
|
|||
feedback_framebuffers: Box<[OwnedImage]>,
|
||||
history_framebuffers: VecDeque<OwnedImage>,
|
||||
state: D3D11State,
|
||||
default_options: FrameOptionsD3D11,
|
||||
}
|
||||
|
||||
pub(crate) struct Direct3D11 {
|
||||
|
@ -62,7 +65,7 @@ pub(crate) struct Direct3D11 {
|
|||
|
||||
pub(crate) struct FilterCommon {
|
||||
pub(crate) d3d11: Direct3D11,
|
||||
pub(crate) luts: FxHashMap<usize, LutTexture>,
|
||||
pub(crate) luts: FastHashMap<usize, LutTexture>,
|
||||
pub samplers: SamplerSet,
|
||||
pub output_textures: Box<[Option<InputTexture>]>,
|
||||
pub feedback_textures: Box<[Option<InputTexture>]>,
|
||||
|
@ -73,18 +76,22 @@ pub(crate) struct FilterCommon {
|
|||
}
|
||||
|
||||
type ShaderPassMeta =
|
||||
ShaderPassArtifact<impl CompileReflectShader<HLSL, GlslangCompilation> + Send>;
|
||||
ShaderPassArtifact<impl CompileReflectShader<HLSL, SpirvCompilation, SpirvCross> + Send>;
|
||||
fn compile_passes(
|
||||
shaders: Vec<ShaderPassConfig>,
|
||||
textures: &[TextureConfig],
|
||||
disable_cache: bool,
|
||||
) -> Result<(Vec<ShaderPassMeta>, ShaderSemantics), FilterChainError> {
|
||||
let (passes, semantics) = if !disable_cache {
|
||||
HLSL::compile_preset_passes::<CachedCompilation<GlslangCompilation>, FilterChainError>(
|
||||
HLSL::compile_preset_passes::<
|
||||
CachedCompilation<SpirvCompilation>,
|
||||
SpirvCross,
|
||||
FilterChainError,
|
||||
>(shaders, &textures)?
|
||||
} else {
|
||||
HLSL::compile_preset_passes::<SpirvCompilation, SpirvCross, FilterChainError>(
|
||||
shaders, &textures,
|
||||
)?
|
||||
} else {
|
||||
HLSL::compile_preset_passes::<GlslangCompilation, FilterChainError>(shaders, &textures)?
|
||||
};
|
||||
|
||||
Ok((passes, semantics))
|
||||
|
@ -98,7 +105,7 @@ impl FilterChainD3D11 {
|
|||
options: Option<&FilterChainOptionsD3D11>,
|
||||
) -> error::Result<FilterChainD3D11> {
|
||||
// load passes from preset
|
||||
let preset = ShaderPreset::try_parse(path)?;
|
||||
let preset = ShaderPreset::try_parse_with_driver_context(path, VideoDriver::Direct3D11)?;
|
||||
|
||||
unsafe { Self::load_from_preset(preset, device, options) }
|
||||
}
|
||||
|
@ -196,6 +203,7 @@ impl FilterChainD3D11 {
|
|||
draw_quad,
|
||||
},
|
||||
state,
|
||||
default_options: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -208,9 +216,9 @@ impl FilterChainD3D11 {
|
|||
&D3D11_BUFFER_DESC {
|
||||
ByteWidth: size,
|
||||
Usage: D3D11_USAGE_DYNAMIC,
|
||||
BindFlags: D3D11_BIND_CONSTANT_BUFFER,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE,
|
||||
MiscFlags: D3D11_RESOURCE_MISC_FLAG(0),
|
||||
BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
|
||||
MiscFlags: 0,
|
||||
StructureByteStride: 0,
|
||||
},
|
||||
None,
|
||||
|
@ -347,8 +355,8 @@ impl FilterChainD3D11 {
|
|||
device: &ID3D11Device,
|
||||
context: &ID3D11DeviceContext,
|
||||
textures: &[TextureConfig],
|
||||
) -> error::Result<FxHashMap<usize, LutTexture>> {
|
||||
let mut luts = FxHashMap::default();
|
||||
) -> error::Result<FastHashMap<usize, LutTexture>> {
|
||||
let mut luts = FastHashMap::default();
|
||||
let images = textures
|
||||
.par_iter()
|
||||
.map(|texture| Image::load(&texture.path, UVDirection::TopLeft))
|
||||
|
@ -361,9 +369,9 @@ impl FilterChainD3D11 {
|
|||
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
Usage: D3D11_USAGE_DEFAULT,
|
||||
MiscFlags: if texture.mipmap {
|
||||
D3D11_RESOURCE_MISC_GENERATE_MIPS
|
||||
D3D11_RESOURCE_MISC_GENERATE_MIPS.0 as u32
|
||||
} else {
|
||||
D3D11_RESOURCE_MISC_FLAG(0)
|
||||
0
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
@ -409,7 +417,7 @@ impl FilterChainD3D11 {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let frame_direction = options.map_or(1, |f| f.frame_direction);
|
||||
let options = options.unwrap_or(&self.default_options);
|
||||
let filter = passes[0].config.filter;
|
||||
let wrap_mode = passes[0].config.wrap_mode;
|
||||
|
||||
|
@ -434,6 +442,7 @@ impl FilterChainD3D11 {
|
|||
OwnedImage::scale_framebuffers(
|
||||
source.size(),
|
||||
viewport.output.size,
|
||||
original.view.size,
|
||||
&mut self.output_framebuffers,
|
||||
&mut self.feedback_framebuffers,
|
||||
passes,
|
||||
|
@ -472,7 +481,7 @@ impl FilterChainD3D11 {
|
|||
index,
|
||||
&self.common,
|
||||
pass.config.get_frame_count(frame_count),
|
||||
frame_direction,
|
||||
options,
|
||||
viewport,
|
||||
&original,
|
||||
&source,
|
||||
|
@ -501,7 +510,7 @@ impl FilterChainD3D11 {
|
|||
passes_len - 1,
|
||||
&self.common,
|
||||
pass.config.get_frame_count(frame_count),
|
||||
frame_direction,
|
||||
options,
|
||||
viewport,
|
||||
&original,
|
||||
&source,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use crate::filter_chain::FilterCommon;
|
||||
use crate::options::FrameOptionsD3D11;
|
||||
use crate::texture::InputTexture;
|
||||
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_common::{ImageFormat, Size, Viewport};
|
||||
use librashader_preprocess::ShaderSource;
|
||||
use librashader_presets::ShaderPassConfig;
|
||||
|
@ -7,9 +10,8 @@ use librashader_reflect::reflect::semantics::{
|
|||
BindingStage, MemberOffset, TextureBinding, UniformBinding,
|
||||
};
|
||||
use librashader_reflect::reflect::ShaderReflection;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use librashader_runtime::binding::{BindSemantics, TextureInput};
|
||||
use librashader_runtime::binding::{BindSemantics, TextureInput, UniformInputs};
|
||||
use librashader_runtime::filter_pass::FilterPassMeta;
|
||||
use librashader_runtime::quad::QuadType;
|
||||
use librashader_runtime::render_target::RenderTarget;
|
||||
|
@ -37,7 +39,7 @@ pub struct FilterPass {
|
|||
pub vertex_layout: ID3D11InputLayout,
|
||||
pub pixel_shader: ID3D11PixelShader,
|
||||
|
||||
pub uniform_bindings: FxHashMap<UniformBinding, MemberOffset>,
|
||||
pub uniform_bindings: FastHashMap<UniformBinding, MemberOffset>,
|
||||
|
||||
pub uniform_storage: UniformStorage,
|
||||
pub uniform_buffer: Option<ConstantBufferBinding>,
|
||||
|
@ -100,7 +102,7 @@ impl FilterPass {
|
|||
parent: &FilterCommon,
|
||||
mvp: &[f32; 16],
|
||||
frame_count: u32,
|
||||
frame_direction: i32,
|
||||
options: &FrameOptionsD3D11,
|
||||
fb_size: Size<u32>,
|
||||
viewport_size: Size<u32>,
|
||||
mut descriptors: (
|
||||
|
@ -115,11 +117,16 @@ impl FilterPass {
|
|||
&parent.samplers,
|
||||
&mut self.uniform_storage,
|
||||
&mut descriptors,
|
||||
mvp,
|
||||
frame_count,
|
||||
frame_direction,
|
||||
fb_size,
|
||||
viewport_size,
|
||||
UniformInputs {
|
||||
mvp,
|
||||
frame_count,
|
||||
rotation: options.rotation,
|
||||
total_subframes: options.total_subframes,
|
||||
current_subframe: options.current_subframe,
|
||||
frame_direction: options.frame_direction,
|
||||
framebuffer_size: fb_size,
|
||||
viewport_size,
|
||||
},
|
||||
original,
|
||||
source,
|
||||
&self.uniform_bindings,
|
||||
|
@ -141,7 +148,7 @@ impl FilterPass {
|
|||
pass_index: usize,
|
||||
parent: &FilterCommon,
|
||||
frame_count: u32,
|
||||
frame_direction: i32,
|
||||
options: &FrameOptionsD3D11,
|
||||
viewport: &Viewport<D3D11OutputView>,
|
||||
original: &InputTexture,
|
||||
source: &InputTexture,
|
||||
|
@ -168,7 +175,7 @@ impl FilterPass {
|
|||
parent,
|
||||
output.mvp,
|
||||
frame_count,
|
||||
frame_direction,
|
||||
options,
|
||||
output.output.size,
|
||||
viewport.output.size,
|
||||
descriptors,
|
||||
|
|
|
@ -10,7 +10,7 @@ use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
|
|||
use windows::Win32::Graphics::Direct3D11::{
|
||||
ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, ID3D11ShaderResourceView,
|
||||
ID3D11Texture2D, D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_BOX,
|
||||
D3D11_CPU_ACCESS_FLAG, D3D11_FORMAT_SUPPORT_RENDER_TARGET, D3D11_FORMAT_SUPPORT_SHADER_SAMPLE,
|
||||
D3D11_FORMAT_SUPPORT_RENDER_TARGET, D3D11_FORMAT_SUPPORT_SHADER_SAMPLE,
|
||||
D3D11_FORMAT_SUPPORT_TEXTURE2D, D3D11_RENDER_TARGET_VIEW_DESC, D3D11_RENDER_TARGET_VIEW_DESC_0,
|
||||
D3D11_RESOURCE_MISC_GENERATE_MIPS, D3D11_RTV_DIMENSION_TEXTURE2D,
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC, D3D11_SHADER_RESOURCE_VIEW_DESC_0, D3D11_TEX2D_RTV,
|
||||
|
@ -69,9 +69,10 @@ impl OwnedImage {
|
|||
format: ImageFormat,
|
||||
viewport_size: &Size<u32>,
|
||||
source_size: &Size<u32>,
|
||||
original_size: &Size<u32>,
|
||||
should_mipmap: bool,
|
||||
) -> error::Result<Size<u32>> {
|
||||
let size = source_size.scale_viewport(scaling, *viewport_size);
|
||||
let size = source_size.scale_viewport(scaling, *viewport_size, *original_size);
|
||||
|
||||
if self.size != size
|
||||
|| (should_mipmap && self.max_mipmap == 1)
|
||||
|
@ -99,7 +100,7 @@ impl OwnedImage {
|
|||
pub fn clear(&mut self, ctx: &ID3D11DeviceContext) -> error::Result<()> {
|
||||
let rtv = self.create_render_target_view()?;
|
||||
unsafe {
|
||||
ctx.ClearRenderTargetView(&rtv, CLEAR.as_ptr());
|
||||
ctx.ClearRenderTargetView(&rtv, CLEAR);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -239,9 +240,9 @@ fn default_desc(size: Size<u32>, format: DXGI_FORMAT, mip_levels: u32) -> D3D11_
|
|||
Quality: 0,
|
||||
},
|
||||
Usage: D3D11_USAGE_DEFAULT,
|
||||
BindFlags: D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_FLAG(0),
|
||||
MiscFlags: D3D11_RESOURCE_MISC_GENERATE_MIPS,
|
||||
BindFlags: (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET).0 as u32,
|
||||
CPUAccessFlags: 0,
|
||||
MiscFlags: D3D11_RESOURCE_MISC_GENERATE_MIPS.0 as u32,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,9 +256,17 @@ impl ScaleFramebuffer for OwnedImage {
|
|||
format: ImageFormat,
|
||||
viewport_size: &Size<u32>,
|
||||
source_size: &Size<u32>,
|
||||
original_size: &Size<u32>,
|
||||
should_mipmap: bool,
|
||||
_context: &Self::Context,
|
||||
) -> Result<Size<u32>, Self::Error> {
|
||||
self.scale(scaling, format, viewport_size, source_size, should_mipmap)
|
||||
self.scale(
|
||||
scaling,
|
||||
format,
|
||||
viewport_size,
|
||||
source_size,
|
||||
original_size,
|
||||
should_mipmap,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ impl D3D11State {
|
|||
let mut saved_blend_mask = 0;
|
||||
context.OMGetBlendState(
|
||||
Some(&mut saved_blend),
|
||||
Some(saved_blend_factor.as_mut_ptr()),
|
||||
Some(&mut saved_blend_factor),
|
||||
Some(&mut saved_blend_mask),
|
||||
);
|
||||
let saved_rs = context.RSGetState().ok();
|
||||
|
@ -113,7 +113,7 @@ impl Drop for D3D11StateSaveGuard<'_> {
|
|||
self.ctx.RSSetState(self.saved_rs.as_ref());
|
||||
self.ctx.OMSetBlendState(
|
||||
self.saved_blend.as_ref(),
|
||||
Some(self.saved_blend_factor.as_ptr()),
|
||||
Some(&self.saved_blend_factor),
|
||||
self.saved_blend_mask,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,14 +13,17 @@ mod filter_chain;
|
|||
mod filter_pass;
|
||||
mod framebuffer;
|
||||
mod graphics_pipeline;
|
||||
mod parameters;
|
||||
mod samplers;
|
||||
mod texture;
|
||||
mod util;
|
||||
|
||||
pub mod error;
|
||||
mod luts;
|
||||
pub mod options;
|
||||
|
||||
use librashader_runtime::impl_filter_chain_parameters;
|
||||
impl_filter_chain_parameters!(FilterChainD3D11);
|
||||
|
||||
pub use filter_chain::FilterChainD3D11;
|
||||
pub use texture::D3D11InputView;
|
||||
pub use texture::D3D11OutputView;
|
||||
|
|
152
librashader-runtime-d3d11/src/luts.rs
Normal file
152
librashader-runtime-d3d11/src/luts.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
use crate::error::assume_d3d11_init;
|
||||
use crate::texture::InputTexture;
|
||||
use crate::{error, D3D11InputView};
|
||||
use librashader_common::{FilterMode, WrapMode};
|
||||
use librashader_runtime::image::Image;
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, D3D11_BIND_RENDER_TARGET,
|
||||
D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE,
|
||||
D3D11_RESOURCE_MISC_GENERATE_MIPS, D3D11_SHADER_RESOURCE_VIEW_DESC,
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC_0, D3D11_SUBRESOURCE_DATA, D3D11_TEX2D_SRV,
|
||||
D3D11_TEXTURE2D_DESC, D3D11_USAGE_DYNAMIC, D3D11_USAGE_STAGING,
|
||||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct LutTexture {
|
||||
// The handle to the Texture2D must be kept alive.
|
||||
#[allow(dead_code)]
|
||||
pub handle: ID3D11Texture2D,
|
||||
#[allow(dead_code)]
|
||||
pub desc: D3D11_TEXTURE2D_DESC,
|
||||
pub image: InputTexture,
|
||||
}
|
||||
|
||||
impl AsRef<InputTexture> for LutTexture {
|
||||
fn as_ref(&self) -> &InputTexture {
|
||||
&self.image
|
||||
}
|
||||
}
|
||||
|
||||
impl LutTexture {
|
||||
pub fn new(
|
||||
device: &ID3D11Device,
|
||||
context: &ID3D11DeviceContext,
|
||||
source: &Image,
|
||||
desc: D3D11_TEXTURE2D_DESC,
|
||||
filter: FilterMode,
|
||||
wrap_mode: WrapMode,
|
||||
) -> error::Result<LutTexture> {
|
||||
let mut desc = D3D11_TEXTURE2D_DESC {
|
||||
Width: source.size.width,
|
||||
Height: source.size.height,
|
||||
// todo: set this to 0
|
||||
MipLevels: if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS.0 as u32) != 0 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
ArraySize: 1,
|
||||
SampleDesc: DXGI_SAMPLE_DESC {
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
CPUAccessFlags: if desc.Usage == D3D11_USAGE_DYNAMIC {
|
||||
D3D11_CPU_ACCESS_WRITE.0 as u32
|
||||
} else {
|
||||
0
|
||||
},
|
||||
..desc
|
||||
};
|
||||
desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE.0 as u32;
|
||||
|
||||
// determine number of mipmaps required
|
||||
if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS.0 as u32) != 0 {
|
||||
desc.BindFlags |= D3D11_BIND_RENDER_TARGET.0 as u32;
|
||||
desc.MipLevels = source.size.calculate_miplevels();
|
||||
}
|
||||
|
||||
// Don't need to determine format support because LUTs are always DXGI_FORMAT_R8G8B8A8_UNORM
|
||||
// since we load them with the Image module.
|
||||
|
||||
unsafe {
|
||||
let mut handle = None;
|
||||
device.CreateTexture2D(&desc, None, Some(&mut handle))?;
|
||||
assume_d3d11_init!(handle, "CreateTexture2D");
|
||||
|
||||
// need a staging texture to defer mipmap generation
|
||||
let mut staging = None;
|
||||
device.CreateTexture2D(
|
||||
&D3D11_TEXTURE2D_DESC {
|
||||
MipLevels: 1,
|
||||
BindFlags: 0,
|
||||
MiscFlags: 0,
|
||||
Usage: D3D11_USAGE_STAGING,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
|
||||
..desc
|
||||
},
|
||||
Some(&D3D11_SUBRESOURCE_DATA {
|
||||
pSysMem: source.bytes.as_ptr().cast(),
|
||||
SysMemPitch: source.pitch as u32,
|
||||
SysMemSlicePitch: 0,
|
||||
}),
|
||||
Some(&mut staging),
|
||||
)?;
|
||||
assume_d3d11_init!(staging, "CreateTexture2D");
|
||||
|
||||
context.CopySubresourceRegion(
|
||||
&handle,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&staging,
|
||||
0,
|
||||
Some(&D3D11_BOX {
|
||||
left: 0,
|
||||
top: 0,
|
||||
front: 0,
|
||||
right: source.size.width,
|
||||
bottom: source.size.height,
|
||||
back: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
let mut srv = None;
|
||||
device.CreateShaderResourceView(
|
||||
&handle,
|
||||
Some(&D3D11_SHADER_RESOURCE_VIEW_DESC {
|
||||
Format: desc.Format,
|
||||
ViewDimension: D3D_SRV_DIMENSION_TEXTURE2D,
|
||||
Anonymous: D3D11_SHADER_RESOURCE_VIEW_DESC_0 {
|
||||
Texture2D: D3D11_TEX2D_SRV {
|
||||
MostDetailedMip: 0,
|
||||
MipLevels: u32::MAX,
|
||||
},
|
||||
},
|
||||
}),
|
||||
Some(&mut srv),
|
||||
)?;
|
||||
assume_d3d11_init!(srv, "CreateShaderResourceView");
|
||||
|
||||
if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS.0 as u32) != 0 {
|
||||
context.GenerateMips(&srv)
|
||||
}
|
||||
|
||||
Ok(LutTexture {
|
||||
handle,
|
||||
desc,
|
||||
image: InputTexture {
|
||||
view: D3D11InputView {
|
||||
handle: srv,
|
||||
size: source.size,
|
||||
},
|
||||
filter,
|
||||
wrap_mode,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,7 @@
|
|||
//! Direct3D 11 shader runtime options.
|
||||
|
||||
/// Options for each Direct3D 11 shader frame.
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct FrameOptionsD3D11 {
|
||||
/// Whether or not to clear the history buffers.
|
||||
pub clear_history: bool,
|
||||
/// The direction of rendering.
|
||||
/// -1 indicates that the frames are played in reverse order.
|
||||
pub frame_direction: i32,
|
||||
}
|
||||
use librashader_runtime::impl_default_frame_options;
|
||||
impl_default_frame_options!(FrameOptionsD3D11);
|
||||
|
||||
/// Options for Direct3D 11 filter chain creation.
|
||||
#[repr(C)]
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
use crate::FilterChainD3D11;
|
||||
use librashader_runtime::parameters::FilterChainParameters;
|
||||
use std::collections::hash_map::Iter;
|
||||
|
||||
impl FilterChainParameters for FilterChainD3D11 {
|
||||
fn get_enabled_pass_count(&self) -> usize {
|
||||
self.common.config.passes_enabled
|
||||
}
|
||||
|
||||
fn set_enabled_pass_count(&mut self, count: usize) {
|
||||
self.common.config.passes_enabled = count
|
||||
}
|
||||
|
||||
fn enumerate_parameters(&self) -> Iter<String, f32> {
|
||||
self.common.config.parameters.iter()
|
||||
}
|
||||
|
||||
fn get_parameter(&self, parameter: &str) -> Option<f32> {
|
||||
self.common
|
||||
.config
|
||||
.parameters
|
||||
.get::<str>(parameter.as_ref())
|
||||
.copied()
|
||||
}
|
||||
|
||||
fn set_parameter(&mut self, parameter: &str, new_value: f32) -> Option<f32> {
|
||||
if let Some(value) = self
|
||||
.common
|
||||
.config
|
||||
.parameters
|
||||
.get_mut::<str>(parameter.as_ref())
|
||||
{
|
||||
let old = *value;
|
||||
*value = new_value;
|
||||
Some(old)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
use crate::error::{assume_d3d11_init, Result};
|
||||
use librashader_common::map::FastHashMap;
|
||||
use librashader_common::{FilterMode, WrapMode};
|
||||
use rustc_hash::FxHashMap;
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
ID3D11Device, ID3D11SamplerState, D3D11_COMPARISON_NEVER, D3D11_FLOAT32_MAX,
|
||||
D3D11_SAMPLER_DESC, D3D11_TEXTURE_ADDRESS_MODE,
|
||||
};
|
||||
pub struct SamplerSet {
|
||||
samplers: FxHashMap<(WrapMode, FilterMode), ID3D11SamplerState>,
|
||||
samplers: FastHashMap<(WrapMode, FilterMode), ID3D11SamplerState>,
|
||||
}
|
||||
|
||||
impl SamplerSet {
|
||||
|
@ -18,7 +18,7 @@ impl SamplerSet {
|
|||
}
|
||||
|
||||
pub fn new(device: &ID3D11Device) -> Result<SamplerSet> {
|
||||
let mut samplers = FxHashMap::default();
|
||||
let mut samplers = FastHashMap::default();
|
||||
let wrap_modes = &[
|
||||
WrapMode::ClampToBorder,
|
||||
WrapMode::ClampToEdge,
|
||||
|
|
|
@ -1,19 +1,7 @@
|
|||
use librashader_common::{FilterMode, Size, WrapMode};
|
||||
use librashader_runtime::image::Image;
|
||||
use librashader_runtime::scaling::MipmapSize;
|
||||
use windows::Win32::Graphics::Direct3D::D3D_SRV_DIMENSION_TEXTURE2D;
|
||||
use windows::Win32::Graphics::Direct3D11::{
|
||||
ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, ID3D11ShaderResourceView,
|
||||
ID3D11Texture2D, D3D11_BIND_FLAG, D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE,
|
||||
D3D11_BOX, D3D11_CPU_ACCESS_FLAG, D3D11_CPU_ACCESS_WRITE, D3D11_RESOURCE_MISC_FLAG,
|
||||
D3D11_RESOURCE_MISC_GENERATE_MIPS, D3D11_SHADER_RESOURCE_VIEW_DESC,
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC_0, D3D11_SUBRESOURCE_DATA, D3D11_TEX2D_SRV,
|
||||
D3D11_TEXTURE2D_DESC, D3D11_USAGE_DYNAMIC, D3D11_USAGE_STAGING,
|
||||
};
|
||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
|
||||
|
||||
use crate::error::{assume_d3d11_init, Result};
|
||||
use crate::error::Result;
|
||||
use crate::framebuffer::OwnedImage;
|
||||
use librashader_common::{FilterMode, Size, WrapMode};
|
||||
use windows::Win32::Graphics::Direct3D11::{ID3D11RenderTargetView, ID3D11ShaderResourceView};
|
||||
|
||||
/// An image view for use as a shader resource.
|
||||
///
|
||||
|
@ -66,140 +54,3 @@ impl AsRef<InputTexture> for InputTexture {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct LutTexture {
|
||||
// The handle to the Texture2D must be kept alive.
|
||||
#[allow(dead_code)]
|
||||
pub handle: ID3D11Texture2D,
|
||||
#[allow(dead_code)]
|
||||
pub desc: D3D11_TEXTURE2D_DESC,
|
||||
pub image: InputTexture,
|
||||
}
|
||||
|
||||
impl AsRef<InputTexture> for LutTexture {
|
||||
fn as_ref(&self) -> &InputTexture {
|
||||
&self.image
|
||||
}
|
||||
}
|
||||
|
||||
impl LutTexture {
|
||||
pub fn new(
|
||||
device: &ID3D11Device,
|
||||
context: &ID3D11DeviceContext,
|
||||
source: &Image,
|
||||
desc: D3D11_TEXTURE2D_DESC,
|
||||
filter: FilterMode,
|
||||
wrap_mode: WrapMode,
|
||||
) -> Result<LutTexture> {
|
||||
let mut desc = D3D11_TEXTURE2D_DESC {
|
||||
Width: source.size.width,
|
||||
Height: source.size.height,
|
||||
// todo: set this to 0
|
||||
MipLevels: if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS).0 != 0 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
ArraySize: 1,
|
||||
SampleDesc: DXGI_SAMPLE_DESC {
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
CPUAccessFlags: if desc.Usage == D3D11_USAGE_DYNAMIC {
|
||||
D3D11_CPU_ACCESS_WRITE
|
||||
} else {
|
||||
D3D11_CPU_ACCESS_FLAG(0)
|
||||
},
|
||||
..desc
|
||||
};
|
||||
desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
// determine number of mipmaps required
|
||||
if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS).0 != 0 {
|
||||
desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
|
||||
desc.MipLevels = source.size.calculate_miplevels();
|
||||
}
|
||||
|
||||
// Don't need to determine format support because LUTs are always DXGI_FORMAT_R8G8B8A8_UNORM
|
||||
// since we load them with the Image module.
|
||||
|
||||
unsafe {
|
||||
let mut handle = None;
|
||||
device.CreateTexture2D(&desc, None, Some(&mut handle))?;
|
||||
assume_d3d11_init!(handle, "CreateTexture2D");
|
||||
|
||||
// need a staging texture to defer mipmap generation
|
||||
let mut staging = None;
|
||||
device.CreateTexture2D(
|
||||
&D3D11_TEXTURE2D_DESC {
|
||||
MipLevels: 1,
|
||||
BindFlags: D3D11_BIND_FLAG(0),
|
||||
MiscFlags: D3D11_RESOURCE_MISC_FLAG(0),
|
||||
Usage: D3D11_USAGE_STAGING,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE,
|
||||
..desc
|
||||
},
|
||||
Some(&D3D11_SUBRESOURCE_DATA {
|
||||
pSysMem: source.bytes.as_ptr().cast(),
|
||||
SysMemPitch: source.pitch as u32,
|
||||
SysMemSlicePitch: 0,
|
||||
}),
|
||||
Some(&mut staging),
|
||||
)?;
|
||||
assume_d3d11_init!(staging, "CreateTexture2D");
|
||||
|
||||
context.CopySubresourceRegion(
|
||||
&handle,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
&staging,
|
||||
0,
|
||||
Some(&D3D11_BOX {
|
||||
left: 0,
|
||||
top: 0,
|
||||
front: 0,
|
||||
right: source.size.width,
|
||||
bottom: source.size.height,
|
||||
back: 1,
|
||||
}),
|
||||
);
|
||||
|
||||
let mut srv = None;
|
||||
device.CreateShaderResourceView(
|
||||
&handle,
|
||||
Some(&D3D11_SHADER_RESOURCE_VIEW_DESC {
|
||||
Format: desc.Format,
|
||||
ViewDimension: D3D_SRV_DIMENSION_TEXTURE2D,
|
||||
Anonymous: D3D11_SHADER_RESOURCE_VIEW_DESC_0 {
|
||||
Texture2D: D3D11_TEX2D_SRV {
|
||||
MostDetailedMip: 0,
|
||||
MipLevels: u32::MAX,
|
||||
},
|
||||
},
|
||||
}),
|
||||
Some(&mut srv),
|
||||
)?;
|
||||
assume_d3d11_init!(srv, "CreateShaderResourceView");
|
||||
|
||||
if (desc.MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS).0 != 0 {
|
||||
context.GenerateMips(&srv)
|
||||
}
|
||||
|
||||
Ok(LutTexture {
|
||||
handle,
|
||||
desc,
|
||||
image: InputTexture {
|
||||
view: D3D11InputView {
|
||||
handle: srv,
|
||||
size: source.size,
|
||||
},
|
||||
filter,
|
||||
wrap_mode,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue