Compare commits
1 commit
master
...
fix-unbala
Author | SHA1 | Date | |
---|---|---|---|
b4b22237e2 |
140
.github/workflows/build.yml
vendored
140
.github/workflows/build.yml
vendored
|
@ -14,138 +14,34 @@ jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
profile: ['debug', 'release', 'optimized']
|
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
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
name: ${{ matrix.output }} (${{ matrix.profile }})
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
- name: Install nightly Rust
|
- name: Install nightly Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: actions-rs/toolchain@v1.0.6
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
- name: Setup Vulkan SDK
|
||||||
|
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||||
|
with:
|
||||||
|
vulkan-query-version: latest
|
||||||
|
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Cross, SPIRV-Headers, SPIRV-Reflect, SPIRV-Tools, Glslang
|
||||||
|
vulkan-use-cache: true
|
||||||
|
- uses: actions/setup-python@v1
|
||||||
|
name: Setup Python
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- run: pip install meson ninja mako
|
||||||
|
name: Install Meson for spirv-to-dxil-sys
|
||||||
- name: Build dynamic library
|
- name: Build dynamic library
|
||||||
run: cargo run -p librashader-build-script -- --profile ${{ matrix.profile }}
|
run: cargo run -p librashader-build-script -- --profile ${{ matrix.profile }}
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4.4.0
|
uses: actions/upload-artifact@v3.1.2
|
||||||
with:
|
with:
|
||||||
name: ${{ format('librashader-{0}-{1}-{2}', matrix.output, github.sha, matrix.profile) }}
|
name: ${{ format('build-outputs-{0}-{1}', matrix.os, matrix.profile) }}
|
||||||
path: ${{ format('target/{0}/librashader.*', matrix.profile) }}
|
path: ${{ format('target/{0}/librashader.*', matrix.profile) }}
|
||||||
- name: Install Ubuntu librashader CLI build dependencies
|
|
||||||
if: matrix.profile == 'release' && matrix.os == 'ubuntu-latest'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update || true
|
|
||||||
sudo apt-get -y install xorg-dev
|
|
||||||
- name: Build librashader CLI
|
|
||||||
if: matrix.profile == 'release'
|
|
||||||
run: cargo build -p librashader-cli --release
|
|
||||||
- name: Upload librashader-cli
|
|
||||||
uses: actions/upload-artifact@v4.4.0
|
|
||||||
if: matrix.profile == 'release'
|
|
||||||
with:
|
|
||||||
name: ${{ format('librashader-cli-{0}-{1}', matrix.output, github.sha) }}
|
|
||||||
path: ${{ format('target/{0}/librashader-cli*', 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 xorg-dev libx11-dev:arm64 libxrandr-dev:arm64
|
|
||||||
- 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@v4.4.0
|
|
||||||
with:
|
|
||||||
name: ${{ format('librashader-aarch64-ubuntu-{0}-{1}', github.sha, matrix.profile) }}
|
|
||||||
path: ${{ format('target/aarch64-unknown-linux-gnu/{0}/librashader.*', matrix.profile) }}
|
|
||||||
- name: Build librashader CLI
|
|
||||||
if: matrix.profile == 'release'
|
|
||||||
run: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc cargo build -p librashader-cli --release --target aarch64-unknown-linux-gnu
|
|
||||||
- name: Upload librashader-cli
|
|
||||||
uses: actions/upload-artifact@v4.4.0
|
|
||||||
if: matrix.profile == 'release'
|
|
||||||
with:
|
|
||||||
name: ${{ format('librashader-cli-aarch64-ubuntu-{0}', github.sha) }}
|
|
||||||
path: ${{ format('target/aarch64-unknown-linux-gnu/{0}/librashader-cli', 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
|
|
||||||
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@v4.4.0
|
|
||||||
with:
|
|
||||||
name: ${{ format('librashader-aarch64-windows-{0}-{1}', github.sha, matrix.profile) }}
|
|
||||||
path: ${{ format('target/aarch64-pc-windows-msvc/{0}/librashader.*', matrix.profile) }}
|
|
||||||
- name: Build librashader CLI
|
|
||||||
if: matrix.profile == 'release'
|
|
||||||
run: cargo build -p librashader-cli --release --target aarch64-pc-windows-msvc
|
|
||||||
- name: Upload librashader-cli
|
|
||||||
uses: actions/upload-artifact@v4.4.0
|
|
||||||
if: matrix.profile == 'release'
|
|
||||||
with:
|
|
||||||
name: ${{ format('librashader-cli-aarch64-pc-windows-msvc-{0}', github.sha) }}
|
|
||||||
path: ${{ format('target/aarch64-pc-windows-msvc/{0}/librashader-cli.exe', matrix.profile) }}
|
|
||||||
build-windows-7:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
profile: ['release', 'optimized']
|
|
||||||
fail-fast: false
|
|
||||||
runs-on: windows-latest
|
|
||||||
name: x86_64-win7-windows
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install nightly Rust
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: nightly
|
|
||||||
components: rust-src
|
|
||||||
- name: Build dynamic library
|
|
||||||
run: cargo run -p librashader-build-script -- --profile ${{ matrix.profile }} --target x86_64-win7-windows-msvc -- -Zbuild-std
|
|
||||||
- name: Upload build artifacts
|
|
||||||
uses: actions/upload-artifact@v4.4.0
|
|
||||||
with:
|
|
||||||
name: ${{ format('librashader-x86_64-win7-windows-{0}-{1}', github.sha, matrix.profile) }}
|
|
||||||
path: ${{ format('target/x86_64-win7-windows-msvc/{0}/librashader.*', matrix.profile) }}
|
|
||||||
|
|
40
.github/workflows/publish-obs.yml
vendored
40
.github/workflows/publish-obs.yml
vendored
|
@ -1,40 +0,0 @@
|
||||||
name: publish packages using Open Build Service
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "master" ]
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-obs:
|
|
||||||
if: github.repository == 'SnowflakePowered/librashader'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: fedora:39
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Install OSC and dependencies
|
|
||||||
env:
|
|
||||||
OBS_CONFIG: ${{ secrets.OBS_CONFIG }}
|
|
||||||
run: |
|
|
||||||
sudo dnf install -y osc obs-service-obs_scm obs-service-cargo_vendor cargo python3-zstandard
|
|
||||||
mkdir -p ~/.config/osc
|
|
||||||
echo "$OBS_CONFIG" > ~/.config/osc/oscrc
|
|
||||||
- name: Checkout Open Build Service repository
|
|
||||||
run: |
|
|
||||||
osc co home:chyyran:librashader/librashader
|
|
||||||
- name: Copy spec from repository
|
|
||||||
run: |
|
|
||||||
cp -r ./pkg/obs/ home:chyyran:librashader/librashader/
|
|
||||||
sed -r -i 's/(<param name="revision">)(.+)(<\/param>)/<param name="revision">${{ github.sha }}<\/param>/g' home:chyyran:librashader/librashader/_service
|
|
||||||
- name: Vendor sources for Open Build Service
|
|
||||||
run: |
|
|
||||||
cd home:chyyran:librashader/librashader/
|
|
||||||
rm *.obscpio
|
|
||||||
osc service mr
|
|
||||||
- name: Commit source artifacts to Open Build Service
|
|
||||||
run: |
|
|
||||||
cd home:chyyran:librashader/librashader/
|
|
||||||
osc ar
|
|
||||||
osc commit -f -m "git-rev ${{ github.sha }}"
|
|
67
.github/workflows/push-full-test.yml
vendored
67
.github/workflows/push-full-test.yml
vendored
|
@ -1,67 +0,0 @@
|
||||||
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
|
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,7 +5,6 @@
|
||||||
*.rdc
|
*.rdc
|
||||||
*.cap
|
*.cap
|
||||||
/.vs/
|
/.vs/
|
||||||
.idea/
|
|
||||||
librashader_runtime_*.exe
|
librashader_runtime_*.exe
|
||||||
/test/capi-tests/librashader-capi-tests/.vs/
|
/test/capi-tests/librashader-capi-tests/.vs/
|
||||||
/test/capi-tests/librashader-capi-tests/x64/
|
/test/capi-tests/librashader-capi-tests/x64/
|
||||||
|
@ -13,4 +12,3 @@ librashader_runtime_*.exe
|
||||||
/test/capi-tests/librashader-capi-tests/**/*.so
|
/test/capi-tests/librashader-capi-tests/**/*.so
|
||||||
/test/capi-tests/librashader-capi-tests/**/*.out
|
/test/capi-tests/librashader-capi-tests/**/*.out
|
||||||
/test/Mega_Bezel_Packs/Duimon-Mega-Bezel/
|
/test/Mega_Bezel_Packs/Duimon-Mega-Bezel/
|
||||||
.DS_Store
|
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
6
.idea/misc.xml
Normal file
6
.idea/misc.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakeWorkspace">
|
||||||
|
<contentRoot DIR="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
2
.idea/src.iml
Normal file
2
.idea/src.iml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
8
.idea/vcs.xml
Normal file
8
.idea/vcs.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/test/shaders_slang" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -3,13 +3,38 @@
|
||||||
|
|
||||||
The following shaders are known to be broken due to various issues.
|
The following shaders are known to be broken due to various issues.
|
||||||
|
|
||||||
This list is updated as of [slang-shaders@`33876b3`](https://github.com/libretro/slang-shaders/commit/33876b3578baac8302b6189ac7acbb052013919e)
|
This list is updated as of [slang-shaders@`356678e`](https://github.com/libretro/slang-shaders/commit/356678ec53ca940a53fa509eff0b65bb63a403bb)
|
||||||
|
|
||||||
## Broken due to parsing errors
|
## Broken due to parsing errors
|
||||||
librashader's preset parser is somewhat stricter than RetroArch in what it accepts. All shaders and textures in a preset must
|
librashader's preset parser is somewhat stricter than RetroArch in what it accepts. All shaders and textures in a preset must
|
||||||
resolve to a fully canonical path to properly parse. The following shaders have broken paths.
|
resolve to a fully canonical path to properly parse. The following shaders have broken paths.
|
||||||
|
|
||||||
* No known broken presets.
|
* `bezel/Mega_Bezel/shaders/hyllian/crt-super-xbr/crt-super-xbr.slangp`: Missing `bezel/Mega_Bezel/shaders/hyllian/crt-super-xbr/shaders/linearize.slang`
|
||||||
|
* `crt/crt-maximus-royale-fast-mode.slangp`: Missing `crt/shaders/crt-maximus-royale/FrameTextures/16_9/TV_decor_1.png`
|
||||||
|
* `crt/crt-maximus-royale-half-res-mode.slangp`: Missing `crt/shaders/crt-maximus-royale/FrameTextures/16_9/TV_decor_1.png`
|
||||||
|
* `crt/crt-maximus-royale.slangp`: Missing `crt/shaders/crt-maximus-royale/FrameTextures/16_9/TV_decor_1.png`
|
||||||
|
* `crt/mame_hlsl.slangp`: Missing `crt/shaders/mame_hlsl/shaders/lut.slang`
|
||||||
|
* `denoisers/fast-bilateral-super-2xbr-3d-3p.slangp`: Missing `xbr/shaders/super-xbr/super-2xbr-3d-pass0.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-256px-composite.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-3phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-256px-svideo.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-3phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-2phase-composite.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-2phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-2phase-svideo.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-2phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-320px-composite.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-2phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-320px-svideo.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-2phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-3phase-composite.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-3phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-3phase-svideo.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-3phase.slang`
|
||||||
|
* `presets/tvout/tvout+ntsc-nes.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-3phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-256px-composite+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-3phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-256px-svideo+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-3phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-2phase-composite+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-2phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-2phase-svideo+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-2phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-320px-composite+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-2phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-320px-svideo+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-2phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-3phase-composite+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-3phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-3phase-svideo+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-svideo-3phase.slang`
|
||||||
|
* `presets/tvout+interlacing/tvout+ntsc-nes+interlacing.slangp`: Missing `ntsc/shaders/ntsc-pass1-composite-3phase.slang`
|
||||||
|
* `scalefx/shaders/old/scalefx-9x.slangp`: Missing `../stock.slang`
|
||||||
|
* `scalefx/shaders/old/scalefx.slangp`: Missing `../stock.slang`
|
||||||
|
|
||||||
librashader's parser is fuzzed with slang-shaders and will accept invalid keys like `mipmap1` or `filter_texture = linear`
|
librashader's parser is fuzzed with slang-shaders and will accept invalid keys like `mipmap1` or `filter_texture = linear`
|
||||||
to account for shader presets that use these invalid constructs. No known shader presets fail to parse due to syntax errors
|
to account for shader presets that use these invalid constructs. No known shader presets fail to parse due to syntax errors
|
||||||
|
@ -19,4 +44,5 @@ that haven't already been accounted for.
|
||||||
|
|
||||||
The preprocessor resolves `#include` pragmas in each `.slang` shader and recursively flattens files into a single compute unit.
|
The preprocessor resolves `#include` pragmas in each `.slang` shader and recursively flattens files into a single compute unit.
|
||||||
|
|
||||||
* `bezel/Mega_Bezel/shaders/hyllian/crt-super-xbr/crt-super-xbr.slangp`: GLSL error: `ERROR: common-functions-bezel.inc:63: 'HSM_GetInverseScaledCoord' : no matching overloaded function found `
|
* `misc/shaders/glass.slang`: Missing `misc/include/img/param_floats.h`.
|
||||||
|
* Looks like this was moved too deep, it references `../include/img/param_floats.h`, but the shader lives in the `misc/shaders/` folder.
|
3603
Cargo.lock
generated
3603
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
31
Cargo.toml
31
Cargo.toml
|
@ -8,41 +8,12 @@ members = [
|
||||||
"librashader-runtime",
|
"librashader-runtime",
|
||||||
"librashader-runtime-d3d11",
|
"librashader-runtime-d3d11",
|
||||||
"librashader-runtime-d3d12",
|
"librashader-runtime-d3d12",
|
||||||
"librashader-runtime-d3d9",
|
|
||||||
"librashader-runtime-gl",
|
"librashader-runtime-gl",
|
||||||
"librashader-runtime-vk",
|
"librashader-runtime-vk",
|
||||||
"librashader-runtime-mtl",
|
|
||||||
"librashader-runtime-wgpu",
|
|
||||||
"librashader-cache",
|
"librashader-cache",
|
||||||
"librashader-capi",
|
"librashader-capi",
|
||||||
"librashader-build-script",
|
"librashader-build-script"
|
||||||
"librashader-cli", "librashader-pack"]
|
|
||||||
resolver = "2"
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
windows = "0.58.0"
|
|
||||||
ash = "0.38"
|
|
||||||
spirv-cross2 = { version = "0.4", default-features = false }
|
|
||||||
objc2-metal = { version = "0.2" }
|
|
||||||
objc2 = { version = "0.5.0" }
|
|
||||||
glow = { version = "0.14.1" }
|
|
||||||
glfw = { version = "0.58.0"}
|
|
||||||
|
|
||||||
wgpu = { version = "22", default-features = false }
|
|
||||||
wgpu-types = { version = "22" }
|
|
||||||
|
|
||||||
clap = { version = "=4.3.0", features = ["derive"] }
|
|
||||||
rayon = { version = "1.10.0"}
|
|
||||||
|
|
||||||
|
|
||||||
[workspace.dependencies.image]
|
|
||||||
version = "0.25.2"
|
|
||||||
features = [
|
|
||||||
"gif", "jpeg", "png",
|
|
||||||
"tga", "pnm", "tiff",
|
|
||||||
"webp", "bmp", "dds",
|
|
||||||
]
|
]
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[workspace.metadata.release]
|
[workspace.metadata.release]
|
||||||
|
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
# Migrating to librashader ABI 2
|
|
||||||
|
|
||||||
librashader version 0.5.0 introduces an ABI change to the C ABI that is incompatible with prior versions.
|
|
||||||
|
|
||||||
If a version of librashader prior to 0.5.0 was linked via `libashader_ld.h`, it should _correctly_ fail to load when attempting
|
|
||||||
to load an instance of librashader 0.5.0 or later.
|
|
||||||
|
|
||||||
This document is only relevant to consumers of the librashader C ABI. Rust users should consult [docs.rs/librashader](https://docs.rs/librashader/latest/librashader/).
|
|
||||||
|
|
||||||
## `LIBRASHADER_CURRENT_ABI` change
|
|
||||||
|
|
||||||
`LIBRASHADER_CURRENT_ABI` has changed from `1` to `2`. There is no change to `LIBRASHADER_CURRENT_API`, as new features
|
|
||||||
have not been added. Option structs should continue to pass `LIBRASHADER_CURRENT_API` for the latest available feature set.
|
|
||||||
|
|
||||||
## `SONAME` change
|
|
||||||
|
|
||||||
On Linux, the canonical `SONAME` of `librashader.so` has changed from `librashader.so.1` to `librashader.so.2`. This will
|
|
||||||
affect consumers that link via the linkage table with `-lrashader` or equivalent rather than through `libashader_ld.h`.
|
|
||||||
|
|
||||||
## `libra_preset` changes
|
|
||||||
|
|
||||||
* The `_internal_alloc` field from `libra_preset_param_list_t` has been removed. This change was made to reduce the potential
|
|
||||||
surface area for undefined behaviour to occur. If librashader was used correctly, there is no code change required.
|
|
||||||
|
|
||||||
## `LIBRA_RUNTIME_VULKAN` changes
|
|
||||||
The following changes are applicable if `LIBRA_RUNTIME_VULKAN` is defined.
|
|
||||||
|
|
||||||
* The `libra_output_image_vk_t` and `libra_source_image_vk_t` structs have been replaced with `libra_image_vk_t`.
|
|
||||||
* `libra_image_vk_t`has the same layout and semantics for `libra_source_image_vk_t`.
|
|
||||||
* When passed as `out`, you should now pass what was previously `.width` and `.height` of `libra_viewport_t` to the same fields in `libra_image_vk_t`. The `handle` and `format`
|
|
||||||
fields retain the same semantics as `libra_output_image_vk_t`.
|
|
||||||
* A field `queue` of type `VkQueue` was added to `libra_device_vk_t`. This field can be `NULL`. If not `NULL`, it is the handle to the `VkQueue` graphics queue
|
|
||||||
to use for the filter chain. If `NULL`, a suitable queue will be chosen.
|
|
||||||
* The `image` and `out` parameters of `libra_vk_filter_chain_frame` has changed from `libra_source_image_vk_t` and `libra_output_image_vk_t`, to `libra_image_vk_t`.
|
|
||||||
* In `libra_vk_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
|
|
||||||
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
|
|
||||||
* The `chain` parameter of `libra_vk_filter_chain_get_param` has been made `const`.
|
|
||||||
* It is always thread safe to call `libra_vk_filter_chain_set_param` from any thread [^1].
|
|
||||||
|
|
||||||
## `LIBRA_RUNTIME_OPENGL` changes
|
|
||||||
The following changes are applicable if `LIBRA_RUNTIME_OPENGL` is defined.
|
|
||||||
* The `libra_gl_init_context` function has been removed.
|
|
||||||
* The function `libra_gl_filter_chain_create` now accepts a `loader` parameter of type `libra_gl_loader_t`. This will be the OpenGL loader used to create the filter chain, previously passed to `libra_gl_init_context`
|
|
||||||
The filter chain will be created against the current OpenGL context.
|
|
||||||
* The `libra_output_framebuffer_gl_t` and `libra_source_image_gl_t` structs have been replaced with `libra_image_gl_t`.
|
|
||||||
* `libra_image_gl_t`has the same layout and semantics for `libra_source_image_gl_t`.
|
|
||||||
* When passed as `out`, you should now pass what was previously `.width` and `.height` of `libra_viewport_t` to the same fields in `libra_image_gl_t`. The `handle` and `format`
|
|
||||||
fields retain the same semantics as `libra_output_image_gl_t`.
|
|
||||||
* The `fbo` field previously in `libra_output_image_gl_t` is no longer necessary. librashader will now internally manage a framebuffer object to write to the provided texture.
|
|
||||||
* In `libra_gl_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
|
|
||||||
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
|
|
||||||
* The `chain` parameter of `libra_gl_filter_chain_get_param` has been made `const`.
|
|
||||||
* It is always thread safe to call `libra_gl_filter_chain_set_param` from any thread [^1].
|
|
||||||
|
|
||||||
## `LIBRA_RUNTIME_D3D11` changes
|
|
||||||
The following changes are applicable if `LIBRA_RUNTIME_D3D11` is defined.
|
|
||||||
|
|
||||||
* The `image` parameter of `libra_d3d11_filter_chain_frame` has changed from `libra_source_image_d3d11_t` to `ID3D11ShaderResourceView *`.
|
|
||||||
* You should now pass what was previously the `.handle` field of `libra_source_image_d3d11_t` field directly as `image` to `libra_d3d11_filter_chain_frame`.
|
|
||||||
* The `libra_source_image_d3d11_t` struct has been removed.
|
|
||||||
* In `libra_d3d11_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
|
|
||||||
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
|
|
||||||
* The `chain` parameter of `libra_d3d11_filter_chain_get_param` has been made `const`.
|
|
||||||
* It is always thread safe to call `libra_d3d11_filter_chain_set_param` from any thread [^1].
|
|
||||||
|
|
||||||
## `LIBRA_RUNTIME_D3D12` changes
|
|
||||||
The following changes are applicable if `LIBRA_RUNTIME_D3D12` is defined.
|
|
||||||
|
|
||||||
* The lifetime of resources will not be extended past the call to the `libra_d3d12_filter_chain_frame` function. In other words, the refcount for any resources passed into the function
|
|
||||||
will no longer be changed, and it is explicitly the responsibility of the caller to ensure any resources remain alive until the `ID3D12GraphicsCommandList` provided is submitted.
|
|
||||||
* The fields `format`, `width`, and `height` have been removed from `libra_source_image_d3d12_t`.
|
|
||||||
* The field `descriptor` now comes before the field `resource` in the layout of `libra_source_image_d3d12_t`.
|
|
||||||
* The fields `width` and `height` have been added to `libra_output_image_d3d12_t`.
|
|
||||||
* You should now pass what was previously `.width` and `.height` of `libra_viewport_t` to these new fields in `libra_output_image_d3d12_t`.
|
|
||||||
* The `image` parameter of `libra_d3d12_filter_chain_frame` has changed from `libra_source_image_d3d12_t` to `libra_image_d3d12_t`.
|
|
||||||
* To maintain the previous behaviour, `.image_type` of the `libra_image_d3d12_t` should be set to `LIBRA_D3D12_IMAGE_TYPE_SOURCE_IMAGE`, and `.handle.source` should be the `libra_source_image_d3d12_t`struct.
|
|
||||||
* The `out` parameter of `libra_d3d12_filter_chain_frame` has changed from `libra_output_image_d3d11_t` to `libra_image_d3d12_t`.
|
|
||||||
* To maintain the previous behaviour, `.image_type` of the `libra_image_d3d12_t` should be set to `LIBRA_D3D12_IMAGE_TYPE_OUTPUT_IMAGE`, and `.handle.output` should be the `libra_output_image_d3d12_t`struct.
|
|
||||||
* Any `libra_image_d3d12_t` can now optionally pass only the `ID3D12Resource *` for the texture by setting `.image_type` to `LIBRA_D3D12_IMAGE_TYPE_RESOURCE` and setting `.handle.resource` to the resource pointer.
|
|
||||||
* If using `LIBRA_D3D12_IMAGE_TYPE_RESOURCE`, shader resource view and render target view descriptors for the input and output images will be internally allocated by the filter chain. This may result in marginally worse performance.
|
|
||||||
* In `libra_d3d12_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
|
|
||||||
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
|
|
||||||
* The `chain` parameter of `libra_d3d12_filter_chain_get_param` has been made `const`.
|
|
||||||
* It is always thread safe to call `libra_d3d12_filter_chain_set_param` from any thread [^1].
|
|
||||||
|
|
||||||
## `LIBRA_RUNTIME_D3D9` changes
|
|
||||||
The following changes are applicable if `LIBRA_RUNTIME_D3D9` is defined.
|
|
||||||
|
|
||||||
* In `libra_d3d9_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
|
|
||||||
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
|
|
||||||
* The `chain` parameter of `libra_d3d9_filter_chain_get_param` has been made `const`.
|
|
||||||
* It is always thread safe to call `libra_d3d9_filter_chain_set_param` from any thread [^1].
|
|
||||||
|
|
||||||
## `LIBRA_RUNTIME_METAL` changes
|
|
||||||
The following changes are applicable if `LIBRA_RUNTIME_METAL` is defined.
|
|
||||||
|
|
||||||
* In `libra_mtl_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
|
|
||||||
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
|
|
||||||
* The `chain` parameter of `libra_mtl_filter_chain_get_param` has been made `const`.
|
|
||||||
* It is always thread safe to call `libra_mtl_filter_chain_set_param` from any thread [^1].
|
|
||||||
|
|
||||||
## `libra_viewport_t` changes
|
|
||||||
|
|
||||||
All `viewport` parameters for `libra_*_filter_chain_frame` now take a *pointer* to a `libra_viewport_t` struct. In ABI 1, the semantics of `libra_viewport_t`
|
|
||||||
was unspecified (but not undefined behaviour) if `width` and `height` did not match the width and height of the output texture.
|
|
||||||
|
|
||||||
This caused confusion as to what the actual purpose of the `width` and `height` fields were. The behaviour differed across runtimes:
|
|
||||||
In some runtimes, it specified the size of the output texture, in others they were used to set the clipping rect for the render target.
|
|
||||||
|
|
||||||
In ABI 2, the semantics of `viewport` as a parameter in `libra_*_filter_chain_frame` are as specified.
|
|
||||||
|
|
||||||
* If `viewport` is `NULL`, then this will be the same as the **specified** behaviour in ABI 1—that is, as if the behaviour where `width` and `height` of `libra_viewport_t` were **equal** to that of the output texture.
|
|
||||||
* In other words, if `viewport` is `NULL`, then it will set the render viewport to be equal to the width and height of the output texture.
|
|
||||||
* If `viewport` is not `NULL`, then the following occurs.
|
|
||||||
* The width and height of the viewport rectangle for the output quad will be set to `viewport.width` and `viewport.height` respectively.
|
|
||||||
* The origin point of the viewport rectangle will be set to (`x`, `y`), **in the native coordinate system of the runtime**.
|
|
||||||
* This behaviour may change over an _API_ version bump. **API version 1 will always retain the behaviour specified here.**
|
|
||||||
* The scissor rectangle will be set to the same size and origin as the viewport rectangle.
|
|
||||||
|
|
||||||
[^1]: This has been the case since librashader 0.4.0 on ABI 1. ABI 2 codifies this guarantee: any loosening in the thread-safety guarantees of `libra_*_filter_chain_set_param` in the future may only change across an _API_ version bump. **API version 1 will always retain the behaviour specified here.**
|
|
186
README.md
186
README.md
|
@ -1,43 +1,30 @@
|
||||||
# librashader
|
# librashader
|
||||||
|
|
||||||
![Mega Bezel SMOOTH-ADV](https://raw.githubusercontent.com/SnowflakePowered/librashader/master/shader_triangle.png)
|
![Mega Bezel SMOOTH-ADV](shader_triangle.png)
|
||||||
|
|
||||||
<small>*Mega Bezel SMOOTH-ADV on DirectX 11*</small>
|
<small>*Mega Bezel SMOOTH-ADV on DirectX 11*</small>
|
||||||
|
|
||||||
librashader (*/ˈli:brəʃeɪdɚ/*) is a preprocessor, compiler, and runtime for RetroArch 'slang' shaders, rewritten in pure Rust.
|
librashader (*/ˈli:brəʃeɪdɚ/*) is a preprocessor, compiler, and runtime for RetroArch 'slang' shaders, rewritten in pure Rust.
|
||||||
|
|
||||||
[![Latest Version](https://img.shields.io/crates/v/librashader.svg)](https://crates.io/crates/librashader) [![Docs](https://docs.rs/librashader/badge.svg)](https://docs.rs/librashader) [![build result](https://build.opensuse.org/projects/home:chyyran:librashader/packages/librashader/badge.svg)](https://software.opensuse.org//download.html?project=home%3Achyyran%3Alibrashader&package=librashader)
|
[![Latest Version](https://img.shields.io/crates/v/librashader.svg)](https://crates.io/crates/librashader) [![Docs](https://docs.rs/librashader/badge.svg)](https://docs.rs/librashader) ![License](https://img.shields.io/crates/l/librashader)
|
||||||
![License](https://img.shields.io/crates/l/librashader)
|
![Nightly rust](https://img.shields.io/badge/rust-nightly-orange.svg)
|
||||||
![Stable rust](https://img.shields.io/badge/rust-1.78-blue.svg) ![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 and macOS users can grab the latest binaries from [GitHub Releases](https://github.com/SnowflakePowered/librashader/releases).
|
|
||||||
|
|
||||||
## Supported Render APIs
|
## Supported Render APIs
|
||||||
librashader supports all modern graphics runtimes, including wgpu, Vulkan, OpenGL 3.3+ and 4.6 (with DSA),
|
librashader supports OpenGL 3, OpenGL 4.6, Vulkan, Direct3D 11, and Direct3D 12. Metal and WebGPU
|
||||||
Direct3D 11, Direct3D 12, and Metal.
|
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 does not support legacy render APIs such as older versions of OpenGL or Direct3D, except for limited
|
|
||||||
support for Direct3D 9.
|
|
||||||
|
|
||||||
| **API** | **Status** | **`librashader` feature** |
|
| **API** | **Status** | **`librashader` feature** |
|
||||||
|-------------|------------|--------------------------|
|
|-------------|------------|--------------------------|
|
||||||
| OpenGL 3.3+ | ✅ | `gl` |
|
| OpenGL 3.3+ | ✔ | `gl` |
|
||||||
| OpenGL 4.6 | ✅ | `gl` |
|
| OpenGL 4.6 | ✔ | `gl` |
|
||||||
| Vulkan | ✅ | `vk` |
|
| Vulkan | ✔ | `vk` |
|
||||||
| Direct3D 9 | 🆗️ |`d3d9` |
|
| Direct3D 11 | ✔ | `d3d11` |
|
||||||
| Direct3D 11 | ✅ | `d3d11` |
|
| Direct3D 12 | ✔ | `d3d12` |
|
||||||
| Direct3D 12 | ✅ | `d3d12` |
|
| Metal | ❌ | |
|
||||||
| Metal | ✅ | `metal` |
|
| WebGPU | ❌ | |
|
||||||
| wgpu | 🆗 | `wgpu` |
|
|
||||||
|
|
||||||
✅ Full Support — 🆗 Secondary Support
|
✔ = Render API is supported — ❌ Render API is not supported
|
||||||
|
|
||||||
Shader compatibility is not guaranteed on render APIs with secondary support.
|
|
||||||
|
|
||||||
wgpu has restrictions on shaders that can not be converted to WGSL, such as those that use `inverse`. Direct3D 9 does not support
|
|
||||||
shaders that need Direct3D 10+ only features, or shaders that can not be compiled to [Shader Model 3.0](https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/shader-model-3).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -46,20 +33,20 @@ 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.
|
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
|
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`, `librashader.dll`, or `librashader.dylib`) implementation in the search path.
|
loads the librashader (`librashader.so` or `librashader.dll`) implementation in the search path.
|
||||||
|
|
||||||
|
|
||||||
### C compatibility
|
### C compatibility
|
||||||
The recommended way of integrating `librashader` is by the `librashader_ld` single header library which implements
|
The recommended way of integrating `librashader` is by the `librashader_ld` single header library which implements
|
||||||
a dynamic loader for `librashader.dll` / `librashader.so` / `librashader.dylib`. See the [versioning policy](https://github.com/SnowflakePowered/librashader#versioning)
|
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. You can also link dynamically
|
for details on how librashader handles C ABI and API stability with regards to library updates.
|
||||||
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
|
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`.
|
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.
|
The [corrosion](https://github.com/corrosion-rs/) CMake package is highly recommended.
|
||||||
|
|
||||||
### Thread safety
|
### Thread safety
|
||||||
Except for the Metal runtime, in general, it is **safe** to create a filter chain instance from a different thread, but drawing frames requires
|
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.
|
**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
|
Filter chains can be created from any thread, but requires external synchronization of the graphics device queue where applicable
|
||||||
|
@ -71,13 +58,8 @@ 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
|
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.
|
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.
|
|
||||||
|
|
||||||
The Direct3D 9 API is not thread safe, unless `D3DCREATE_MULTITHREADED` is enabled at device creation.
|
|
||||||
|
|
||||||
### Quad vertices and rotations
|
### Quad vertices and rotations
|
||||||
All runtimes render intermediate passes with an identity matrix MVP and a VBO for with range `[-1, 1]`. The final pass uses a
|
All runtimes except OpenGL render 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.
|
Quad VBO with range `[0, 1]` and the following projection matrix by default.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -92,39 +74,27 @@ 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
|
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.
|
pass orientation information to shaders.
|
||||||
|
|
||||||
### Writing a librashader Runtime
|
The OpenGL runtime uses a VBO for range `[0, 1]` for all passes and the following MVP for all passes.
|
||||||
|
|
||||||
If you wish to contribute a runtime implementation not already available, see the [librashader-runtime](https://docs.rs/librashader-runtime/latest/librashader_runtime/)
|
```rust
|
||||||
crate for helpers and shared logic used across all librashader runtime implementations. Using these helpers and traits will
|
static GL_DEFAULT_MVP: &[f32; 16] = &[
|
||||||
ensure that your runtime has consistent behaviour for uniform and texture semantics bindings with the existing librashader runtimes.
|
2.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 2.0, 0.0, 0.0,
|
||||||
These types should not be exposed to the end user in the runtime's public API, and should be kept internal to the implementation of
|
0.0, 0.0, 2.0, 0.0,
|
||||||
the runtime.
|
-1.0, -1.0, 0.0, 1.0,
|
||||||
|
];
|
||||||
## Command-line interface
|
|
||||||
librashader provides a command-line interface to reflect and debug 'slang' shaders and presets.
|
|
||||||
|
|
||||||
```
|
|
||||||
Usage: librashader-cli <COMMAND>
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
render Render a shader preset against an image
|
|
||||||
compare Compare two runtimes and get a similarity score between the two runtimes rendering the same frame
|
|
||||||
parse Parse a preset and get a JSON representation of the data
|
|
||||||
pack Create a serialized preset pack from a shader preset
|
|
||||||
preprocess Get the raw GLSL output of a preprocessed shader
|
|
||||||
transpile Transpile a shader in a given preset to the given format
|
|
||||||
reflect Reflect the shader relative to a preset, giving information about semantics used in a slang shader
|
|
||||||
help Print this message or the help of the given subcommand(s)
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help Print help
|
|
||||||
-V, --version Print version
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, see [`CLI.md`](https://github.com/SnowflakePowered/librashader/blob/master/CLI.md).
|
### Building
|
||||||
|
|
||||||
## Building
|
librashader requires the following build time dependencies
|
||||||
|
|
||||||
|
* The [Vulkan SDK](https://www.lunarg.com/vulkan-sdk/)
|
||||||
|
* [Meson](https://mesonbuild.com/)
|
||||||
|
* [CMake 3.8 or later](https://cmake.org/)
|
||||||
|
* [Python 3.6 or later](https://www.python.org/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
For Rust projects, simply add the crate to your `Cargo.toml`.
|
For Rust projects, simply add the crate to your `Cargo.toml`.
|
||||||
|
|
||||||
|
@ -141,31 +111,14 @@ cargo run -p librashader-build-script -- --profile optimized
|
||||||
This will output a `librashader.dll` or `librashader.so` in the target folder. Profile can be `debug`, `release`, or
|
This will output a `librashader.dll` or `librashader.so` in the target folder. Profile can be `debug`, `release`, or
|
||||||
`optimized` for full LTO.
|
`optimized` for full LTO.
|
||||||
|
|
||||||
While librashader has no build-time dependencies, using `librashader_ld.h` may require headers from
|
### Writing a librashader Runtime
|
||||||
the relevant runtime graphics API.
|
|
||||||
|
|
||||||
|
If you wish to contribute a runtime implementation not already available, see the [librashader-runtime](https://docs.rs/librashader-runtime/latest/librashader_runtime/)
|
||||||
|
crate for helpers and shared logic used across all librashader runtime implementations. Using these helpers and traits will
|
||||||
|
ensure that your runtime has consistent behaviour for uniform and texture semantics bindings with the existing librashader runtimes.
|
||||||
|
|
||||||
### Building against stable Rust
|
These types should not be exposed to the end user in the runtime's public API, and should be kept internal to the implementation of
|
||||||
While librashader is intended to be used with nightly Rust until [required features](https://github.com/SnowflakePowered/librashader/issues/55) are stabilized, it supports being
|
the runtime.
|
||||||
built with stable Rust with the `stable` feature.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
librashader = { features = ["stable"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
If building the C API, the `--stable` flag in the build script will enable the `stable` feature.
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo +stable run -p librashader-build-script -- --profile optimized --stable
|
|
||||||
```
|
|
||||||
|
|
||||||
There are some caveats when building against stable Rust, such that building librashader against nightly Rust is still highly encouraged.
|
|
||||||
|
|
||||||
* C headers will not be regenerated when building with the `stable` feature.
|
|
||||||
* There is a minor performance hit in initial shader compilation when building against stable Rust. This is due to boxed trait objects being used instead of `impl Trait`.
|
|
||||||
* A higher MSRV is required when building against stable Rust.
|
|
||||||
|
|
||||||
When the `trait_alias_impl_trait` feature is stabilized, the `stable` feature will be removed.
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
@ -174,13 +127,9 @@ 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)
|
* [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 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)
|
* [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)
|
|
||||||
* [Direct3D 9](https://github.com/SnowflakePowered/librashader/blob/master/librashader-runtime-d3d9/tests/triangle.rs)
|
|
||||||
|
|
||||||
Some basic examples on using the C API are also provided.
|
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.
|
||||||
* [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
|
## Compatibility
|
||||||
|
|
||||||
|
@ -200,25 +149,20 @@ Please report an issue if you run into a shader that works in RetroArch, but not
|
||||||
`mipmap_input0 = "true"`.
|
`mipmap_input0 = "true"`.
|
||||||
* The preset parser is a substantially stricter implementation that the one in RetroArch. Not all shader presets may be
|
* The preset parser is a substantially stricter implementation that the one in RetroArch. Not all shader presets may be
|
||||||
compatible. If you find this is the case, please file an issue so a workaround can be added.
|
compatible. If you find this is the case, please file an issue so a workaround can be added.
|
||||||
* Shaders are [pre-linked at the SPIR-V level](https://github.com/SnowflakePowered/librashader/blob/master/librashader-reflect/src/front/spirv_passes/link_input_outputs.rs) before being
|
|
||||||
passed to the driver. Unused inputs in the fragment shader are removed, and the corresponding input in the vertex shader
|
|
||||||
is downgraded to a global variable.
|
|
||||||
### Runtime specific differences
|
### Runtime specific differences
|
||||||
* OpenGL
|
* OpenGL
|
||||||
* Copying of in-flight framebuffer contents to history is done via `glBlitFramebuffer` rather than drawing a quad into an intermediate FBO.
|
* Copying of in-flight framebuffer contents to history is done via `glBlitFramebuffer` rather than drawing a quad into an intermediate FBO.
|
||||||
* Sampler objects are used rather than `glTexParameter`.
|
* Sampler objects are used rather than `glTexParameter`.
|
||||||
* Sampler inputs and outputs are not renamed. This is useful for debugging shaders in RenderDoc.
|
* 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.
|
* 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+
|
* OpenGL 4.6+
|
||||||
* All caveats from the OpenGL 3.3+ section should be considered.
|
* 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.
|
* 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 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
|
* Vulkan
|
||||||
* 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).
|
* 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.
|
This extension must be enabled at device creation. Explicit render passes can be used by configuring filter chain options, but may have reduced performance
|
||||||
Dynamic rendering may have improved performance when enabled, and supported by the host hardware.
|
compared to dynamic rendering.
|
||||||
* Allocations within the runtime are done through [gpu-allocator](https://github.com/Traverse-Research/gpu-allocator) rather than handled manually.
|
* Allocations within the runtime are done through [gpu-allocator](https://github.com/Traverse-Research/gpu-allocator) rather than handled manually.
|
||||||
* Direct3D 11
|
* Direct3D 11
|
||||||
* Framebuffer copies are done via `ID3D11DeviceContext::CopySubresourceRegion` rather than a CPU conversion + copy.
|
* Framebuffer copies are done via `ID3D11DeviceContext::CopySubresourceRegion` rather than a CPU conversion + copy.
|
||||||
|
@ -227,17 +171,15 @@ Please report an issue if you run into a shader that works in RetroArch, but not
|
||||||
which was released in late 2018.
|
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.
|
* 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.
|
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 `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.
|
* The Direct3D 12 runtime requires `dxil.dll` and `dxcompiler.dll` from the [DirectX Shader Compiler](https://github.com/microsoft/DirectXShaderCompiler).
|
||||||
* 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,
|
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.
|
and are more a heads-up for integrating librashader into your project.
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
[![Latest Version](https://img.shields.io/crates/v/librashader.svg)](https://crates.io/crates/librashader)
|
[![Latest Version](https://img.shields.io/crates/v/librashader.svg)](https://crates.io/crates/librashader)
|
||||||
![C ABI](https://img.shields.io/badge/ABI%20version-2-yellowgreen)
|
![C ABI](https://img.shields.io/badge/ABI%20version-1-yellowgreen)
|
||||||
![C API](https://img.shields.io/badge/API%20version-1-blue)
|
![C API](https://img.shields.io/badge/API%20version-0-blue)
|
||||||
|
|
||||||
|
|
||||||
librashader typically follows [Semantic Versioning](https://semver.org/) with respect to the Rust API, where a minor version
|
librashader typically follows [Semantic Versioning](https://semver.org/) with respect to the Rust API, where a minor version
|
||||||
|
@ -264,29 +206,9 @@ will check to ensure that `LIBRASHADER_CURRENT_ABI` matches that of the loaded l
|
||||||
librashader will not load. A value of `0` for `LIBRASHADER_CURRENT_ABI` indicates the "null" instance where every operation
|
librashader will not load. A value of `0` for `LIBRASHADER_CURRENT_ABI` indicates the "null" instance where every operation
|
||||||
is a no-op, which occurs if no compatible librashader implementation could be found.
|
is a no-op, which occurs if no compatible librashader implementation could be found.
|
||||||
|
|
||||||
The `SONAME` of `librashader.so` when installed via package manager is set to `LIBRASHADER_CURRENT_ABI`.
|
|
||||||
|
|
||||||
The above does not apply to releases of librashader prior to `0.1.0`, which were allowed to break API and ABI compatibility
|
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`.
|
in both the Rust and C API without an increase to either `LIBRASHADER_CURRENT_VERSION` or `LIBRASHADER_CURRENT_ABI`.
|
||||||
|
|
||||||
The ABI version was bumped from `1` to `2` with librashader `0.5.0`. See [`MIGRATION-ABI2.md`](https://github.com/SnowflakePowered/librashader/blob/master/MIGRATION-ABI2.md)
|
|
||||||
for migration instructions.
|
|
||||||
|
|
||||||
### MSRV Policy
|
|
||||||
|
|
||||||
Building against stable Rust requires the following MSRV.
|
|
||||||
|
|
||||||
* All platforms: **1.78**
|
|
||||||
|
|
||||||
When building against nightly Rust, the following MSRV policy is enforced for unstable library features.
|
|
||||||
|
|
||||||
* All platforms: **1.78**
|
|
||||||
|
|
||||||
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 when building against nightly Rust or with `RUSTC_BOOTSTRAP=1`, 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
|
## License
|
||||||
The core parts of librashader such as the preprocessor, the preset parser,
|
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.
|
the reflection library, and the runtimes, are all licensed under the Mozilla Public License version 2.0.
|
||||||
|
@ -296,13 +218,13 @@ are more permissively licensed, and may allow you to use librashader in your per
|
||||||
licensed or proprietary project.
|
licensed or proprietary project.
|
||||||
|
|
||||||
To facilitate easier use of librashader in projects incompatible with MPL-2.0, `librashader_ld`
|
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`, `librashader.dll`, or `librashader.dylib`.
|
implements a loader which thunks its calls to any `librashader.so` or `librashader.dll`
|
||||||
library found in the load path. A non-MPL-2.0 compatible project may link against
|
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`, `librashader.dll` or `librashader.dylib`
|
`librashader_ld` to use the librashader runtime, *provided that `librashader.so` or `librashader.dll`
|
||||||
are distributed under the restrictions of MPLv2*.
|
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,
|
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`, `librashader.dll` or `librashader.dylib`** alongside your project.
|
you **can not distribute `librashader.so` or `librashader.dll`** alongside your project.
|
||||||
The end user must obtain the implementation of librashader themselves. For more information,
|
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/).
|
see the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/).
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,15 @@ libra_gl_filter_chain_t load_gl_filter_chain(libra_gl_loader_t opengl, const cha
|
||||||
std::cout << "Could not load preset\n";
|
std::cout << "Could not load preset\n";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenGL runtime needs to be initialized.
|
||||||
|
if (librashader.gl_init_context(opengl) != NULL) {
|
||||||
|
std::cout << "Could not initialize OpenGL context\n";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
libra_gl_filter_chain_t chain;
|
libra_gl_filter_chain_t chain;
|
||||||
if (librashader.gl_filter_chain_create(&preset, opengl, NULL, &chain) {
|
if (librashader.gl_filter_chain_create(&preset, NULL, &chain) {
|
||||||
std::cout << "Could not create OpenGL filter chain\n";
|
std::cout << "Could not create OpenGL filter chain\n";
|
||||||
}
|
}
|
||||||
return chain;
|
return chain;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -6,9 +6,9 @@ publish = false
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cbindgen = "0.27.0"
|
cbindgen = { git = "https://github.com/eqrion/cbindgen" }
|
||||||
clap = { workspace = true }
|
clap = { version = "4.1.0", features = ["derive"] }
|
||||||
carlog = "0.1.0"
|
|
||||||
|
|
||||||
[package.metadata.release]
|
[package.metadata.release]
|
||||||
release = false
|
release = false
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use carlog::*;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, ExitCode};
|
use std::process::Command;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
@ -11,18 +10,12 @@ use std::{env, fs};
|
||||||
struct Args {
|
struct Args {
|
||||||
#[arg(long, default_value = "debug", global = true)]
|
#[arg(long, default_value = "debug", global = true)]
|
||||||
profile: String,
|
profile: String,
|
||||||
#[arg(long, global = true)]
|
|
||||||
target: Option<String>,
|
|
||||||
#[arg(long, default_value_t = false, global = true)]
|
|
||||||
stable: bool,
|
|
||||||
#[arg(last = true)]
|
|
||||||
cargoflags: Vec<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() -> ExitCode {
|
pub fn main() {
|
||||||
// Do not update files on docsrs
|
// Do not update files on docsrs
|
||||||
if env::var("DOCS_RS").is_ok() {
|
if env::var("DOCS_RS").is_ok() {
|
||||||
return ExitCode::SUCCESS;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
@ -30,122 +23,43 @@ pub fn main() -> ExitCode {
|
||||||
let profile = args.profile;
|
let profile = args.profile;
|
||||||
|
|
||||||
let crate_dir = Path::new("librashader-capi");
|
let crate_dir = Path::new("librashader-capi");
|
||||||
carlog_info!("Building", "librashader C API");
|
println!("Building librashader C API...");
|
||||||
|
|
||||||
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
|
|
||||||
let mut cmd = Command::new(&cargo);
|
|
||||||
|
|
||||||
|
let mut cmd = Command::new("cargo");
|
||||||
cmd.arg("build");
|
cmd.arg("build");
|
||||||
cmd.args(["--package", "librashader-capi"]);
|
cmd.args(["--package", "librashader-capi"]);
|
||||||
cmd.arg(format!(
|
cmd.arg(format!(
|
||||||
"--profile={}",
|
"--profile={}",
|
||||||
if profile == "debug" { "dev" } else { &profile }
|
if profile == "debug" { "dev" } else { &profile }
|
||||||
));
|
));
|
||||||
|
Some(cmd.status().expect("Failed to build librashader-capi"));
|
||||||
|
|
||||||
// If we're on RUSTC_BOOTSTRAP, it's likely because we're building for a package..
|
let output_dir = PathBuf::from(format!("target/{}", profile))
|
||||||
if env::var("RUSTC_BOOTSTRAP").is_ok() {
|
.canonicalize()
|
||||||
cmd.arg("--ignore-rust-version");
|
.expect("Could not find output directory.");
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(target) = &args.target {
|
println!("Generating C headers...");
|
||||||
cmd.arg(format!("--target={}", &target));
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.stable {
|
// Create headers.
|
||||||
carlog_warning!("building librashader with stable Rust compatibility");
|
let mut buf = BufWriter::new(Vec::new());
|
||||||
carlog_warning!("C headers will not be generated");
|
cbindgen::generate(crate_dir)
|
||||||
cmd.args(["--features", "stable"]);
|
.expect("Unable to generate bindings")
|
||||||
}
|
.write(&mut buf);
|
||||||
if !args.cargoflags.is_empty() {
|
|
||||||
cmd.args(args.cargoflags);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ok(status) = cmd.status().inspect_err(|err| {
|
let bytes = buf.into_inner().expect("Unable to extract bytes");
|
||||||
carlog_error!("failed to build librashader-capi");
|
let string = String::from_utf8(bytes).expect("Unable to create string");
|
||||||
carlog_error!(format!("{err}"));
|
File::create(output_dir.join("librashader.h"))
|
||||||
}) else {
|
.expect("Unable to open file")
|
||||||
return ExitCode::FAILURE;
|
.write_all(string.as_bytes())
|
||||||
};
|
.expect("Unable to write bindings.");
|
||||||
|
|
||||||
if !status.success() {
|
println!("Moving artifacts...");
|
||||||
return ExitCode::from(status.code().unwrap_or(1) as u8);
|
if cfg!(target_os = "linux") {
|
||||||
}
|
|
||||||
|
|
||||||
let mut output_dir = PathBuf::from(format!("target/{}", profile));
|
|
||||||
if let Some(target) = &args.target {
|
|
||||||
output_dir = PathBuf::from(format!("target/{}/{}", target, profile));
|
|
||||||
}
|
|
||||||
|
|
||||||
let Ok(output_dir) = output_dir.canonicalize() else {
|
|
||||||
carlog_error!("could not find output directory");
|
|
||||||
println!("help: are you running the build script from the repository root?");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
|
|
||||||
if args.stable {
|
|
||||||
carlog_warning!("generating C headers is not supported when building for stable Rust");
|
|
||||||
} else {
|
|
||||||
carlog_info!("Generating", "librashader C API headers");
|
|
||||||
|
|
||||||
// Create headers.
|
|
||||||
let mut buf = BufWriter::new(Vec::new());
|
|
||||||
let Ok(bindings) = cbindgen::generate(crate_dir).inspect_err(|err| {
|
|
||||||
carlog_error!("unable to generate C API headers");
|
|
||||||
carlog_error!(format!("{err}"));
|
|
||||||
}) else {
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
|
|
||||||
bindings.write(&mut buf);
|
|
||||||
let bytes = buf.into_inner().expect("Unable to extract bytes");
|
|
||||||
let string = String::from_utf8(bytes).expect("Unable to create string");
|
|
||||||
|
|
||||||
let Ok(mut file) = File::create(output_dir.join("librashader.h")).inspect_err(|err| {
|
|
||||||
carlog_error!("unable to open librashader.h");
|
|
||||||
carlog_error!(format!("{err}"));
|
|
||||||
}) else {
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(_) = file.write_all(string.as_bytes()).inspect_err(|err| {
|
|
||||||
carlog_error!("unable to write to librashader.h");
|
|
||||||
carlog_error!(format!("{err}"));
|
|
||||||
}) else {
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
carlog_info!("Moving", "built artifacts");
|
|
||||||
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", "");
|
|
||||||
|
|
||||||
let Ok(_) =
|
|
||||||
fs::rename(output_dir.join(artifact), output_dir.join(&ext)).inspect_err(|err| {
|
|
||||||
carlog_error!(format!("Unable to rename {artifact} to {}", &ext));
|
|
||||||
carlog_error!(format!("{err}"));
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
carlog_ok!("Renamed", format!("{artifact} to {}", &ext));
|
|
||||||
}
|
|
||||||
} else if cfg!(target_family = "unix") {
|
|
||||||
let artifacts = &["liblibrashader_capi.so", "liblibrashader_capi.a"];
|
let artifacts = &["liblibrashader_capi.so", "liblibrashader_capi.a"];
|
||||||
for artifact in artifacts {
|
for artifact in artifacts {
|
||||||
let ext = artifact.strip_prefix("lib").unwrap();
|
let ext = artifact.strip_prefix("lib").unwrap();
|
||||||
let ext = ext.replace("_capi", "");
|
let ext = ext.replace("_capi", "");
|
||||||
let Ok(_) =
|
fs::rename(output_dir.join(artifact), output_dir.join(ext)).unwrap();
|
||||||
fs::rename(output_dir.join(artifact), output_dir.join(&ext)).inspect_err(|err| {
|
|
||||||
carlog_error!(format!("Unable to rename {artifact} to {}", &ext));
|
|
||||||
carlog_error!(format!("{err}"));
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
carlog_ok!("Renamed", format!("{artifact} to {}", &ext));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,29 +70,11 @@ pub fn main() -> ExitCode {
|
||||||
"librashader_capi.d",
|
"librashader_capi.d",
|
||||||
"librashader_capi.dll.exp",
|
"librashader_capi.dll.exp",
|
||||||
"librashader_capi.dll.lib",
|
"librashader_capi.dll.lib",
|
||||||
|
"librashader_capi.pdb",
|
||||||
];
|
];
|
||||||
for artifact in artifacts {
|
for artifact in artifacts {
|
||||||
let ext = artifact.replace("_capi", "");
|
let ext = artifact.replace("_capi", "");
|
||||||
let Ok(_) =
|
fs::rename(output_dir.join(artifact), output_dir.join(ext)).unwrap();
|
||||||
fs::rename(output_dir.join(artifact), output_dir.join(&ext)).inspect_err(|err| {
|
|
||||||
carlog_error!(format!("Unable to rename {artifact} to {}", &ext));
|
|
||||||
carlog_error!(format!("{err}"));
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
};
|
|
||||||
carlog_ok!("Renamed", format!("{artifact} to {}", &ext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if output_dir.join("librashader_capi.pdb").exists() {
|
|
||||||
fs::rename(
|
|
||||||
output_dir.join("librashader_capi.pdb"),
|
|
||||||
output_dir.join("librashader.pdb"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
carlog_ok!("Renamed", "librashader_capi.pdb to librashader.pdb");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitCode::SUCCESS
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
name = "librashader-cache"
|
name = "librashader-cache"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
license = "MPL-2.0 OR GPL-3.0-only"
|
||||||
version = "0.5.1"
|
version = "0.1.3"
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
repository = "https://github.com/SnowflakePowered/librashader"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
@ -12,22 +12,18 @@ description = "RetroArch shaders for all."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0" }
|
serde = { version = "1.0" }
|
||||||
librashader-reflect = { path = "../librashader-reflect", version = "0.5.1", features = ["serde"] }
|
librashader-reflect = { path = "../librashader-reflect", version = "0.1.3", features = ["serialize", "dxil"] }
|
||||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.5.1" }
|
librashader-preprocess = { path = "../librashader-preprocess", version = "0.1.3" }
|
||||||
platform-dirs = "0.3.0"
|
platform-dirs = "0.3.0"
|
||||||
blake3 = { version = "1.5.4" }
|
blake3 = { version = "1.3.3" }
|
||||||
thiserror = "1.0.38"
|
thiserror = "1.0.38"
|
||||||
bincode = { version = "2.0.0-rc.2", features = ["serde"] }
|
bincode = { version = "2.0.0-rc.2", features = ["serde"] }
|
||||||
persy = "1.4.7"
|
rusqlite = { version = "0.28.0", features = ["bundled"] }
|
||||||
|
|
||||||
bytemuck = "1.13.0"
|
bytemuck = "1.13.0"
|
||||||
|
|
||||||
[target.x86_64-win7-windows-msvc.dependencies.blake3]
|
|
||||||
version = "1.5.4"
|
|
||||||
features = ["pure"]
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
workspace = true
|
version = "0.44.0"
|
||||||
features = [
|
features = [
|
||||||
"Win32_Graphics_Direct3D",
|
"Win32_Graphics_Direct3D",
|
||||||
"Win32_Graphics_Direct3D_Fxc",
|
"Win32_Graphics_Direct3D_Fxc",
|
||||||
|
@ -36,10 +32,10 @@ features = [
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
d3d = ["windows", "librashader-reflect/dxil"]
|
d3d = ["windows"]
|
||||||
|
|
||||||
# hack to get building on docsrs
|
# hack to get building on docsrs
|
||||||
docsrs = ["blake3/pure"]
|
docsrs = ["blake3/pure", "rusqlite/in_gecko"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["docsrs"]
|
features = ["docsrs"]
|
||||||
|
|
|
@ -3,11 +3,10 @@ use crate::key::CacheKey;
|
||||||
|
|
||||||
pub(crate) mod internal {
|
pub(crate) mod internal {
|
||||||
use platform_dirs::AppDirs;
|
use platform_dirs::AppDirs;
|
||||||
|
use rusqlite::{Connection, DatabaseName};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use persy::{ByteVec, Config, Persy, ValueMode};
|
|
||||||
|
|
||||||
pub(crate) fn get_cache_dir() -> Result<PathBuf, Box<dyn Error>> {
|
pub(crate) fn get_cache_dir() -> Result<PathBuf, Box<dyn Error>> {
|
||||||
let cache_dir = if let Some(cache_dir) =
|
let cache_dir = if let Some(cache_dir) =
|
||||||
AppDirs::new(Some("librashader"), false).map(|a| a.cache_dir)
|
AppDirs::new(Some("librashader"), false).map(|a| a.cache_dir)
|
||||||
|
@ -24,73 +23,46 @@ pub(crate) mod internal {
|
||||||
Ok(cache_dir)
|
Ok(cache_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub(crate) fn get_cache() -> Result<Connection, Box<dyn Error>> {
|
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)
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub(crate) fn get_cache() -> Result<Persy, Box<dyn Error>> {
|
|
||||||
let cache_dir = get_cache_dir()?;
|
let cache_dir = get_cache_dir()?;
|
||||||
match Persy::open_or_create_with(
|
let mut conn = Connection::open(&cache_dir.join("librashader.db"))?;
|
||||||
&cache_dir.join("librashader.db.1"),
|
|
||||||
Config::new(),
|
let tx = conn.transaction()?;
|
||||||
|persy| {
|
tx.pragma_update(Some(DatabaseName::Main), "journal_mode", "wal2")?;
|
||||||
let tx = persy.begin()?;
|
tx.execute(
|
||||||
tx.commit()?;
|
r#"create table if not exists cache (
|
||||||
Ok(())
|
type text not null,
|
||||||
},
|
id blob not null,
|
||||||
) {
|
value blob not null unique,
|
||||||
Ok(conn) => Ok(conn),
|
primary key (id, type)
|
||||||
Err(e) => {
|
)"#,
|
||||||
let path = &cache_dir.join("librashader.db.1");
|
[],
|
||||||
let _ = std::fs::remove_file(path).ok();
|
)?;
|
||||||
Err(e)?
|
tx.commit()?;
|
||||||
}
|
Ok(conn)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_blob(
|
pub(crate) fn get_blob(
|
||||||
conn: &Persy,
|
conn: &Connection,
|
||||||
index: &str,
|
index: &str,
|
||||||
key: &[u8],
|
key: &[u8],
|
||||||
) -> Result<Option<Vec<u8>>, Box<dyn Error>> {
|
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
if !conn.exists_index(index)? {
|
let value = conn.query_row(
|
||||||
return Ok(None);
|
&*format!("select value from cache where (type = (?1) and id = (?2))"),
|
||||||
}
|
rusqlite::params![index, key],
|
||||||
|
|row| row.get(0),
|
||||||
let value = conn.get::<_, ByteVec>(index, &ByteVec::from(key))?.next();
|
)?;
|
||||||
Ok(value.map(|v| v.to_vec()))
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_blob(
|
pub(crate) fn set_blob(conn: &Connection, index: &str, key: &[u8], value: &[u8]) {
|
||||||
conn: &Persy,
|
match conn.execute(
|
||||||
index: &str,
|
&*format!("insert or replace into cache (type, id, value) values (?1, ?2, ?3)"),
|
||||||
key: &[u8],
|
rusqlite::params![index, key, value],
|
||||||
value: &[u8],
|
) {
|
||||||
) -> Result<(), Box<dyn Error>> {
|
Ok(_) => return,
|
||||||
let mut tx = conn.begin()?;
|
Err(e) => println!("err: {:?}", e),
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +101,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
'attempt: {
|
'attempt: {
|
||||||
if let Ok(Some(blob)) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
if let Ok(blob) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
||||||
let cached = T::from_bytes(&blob).map(&load);
|
let cached = T::from_bytes(&blob).map(&load);
|
||||||
|
|
||||||
match cached {
|
match cached {
|
||||||
|
@ -143,7 +115,7 @@ where
|
||||||
let blob = factory(keys)?;
|
let blob = factory(keys)?;
|
||||||
|
|
||||||
if let Some(slice) = T::to_bytes(&blob) {
|
if let Some(slice) = T::to_bytes(&blob) {
|
||||||
let _ = internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
||||||
}
|
}
|
||||||
Ok(load(blob)?)
|
Ok(load(blob)?)
|
||||||
}
|
}
|
||||||
|
@ -185,7 +157,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let pipeline = 'attempt: {
|
let pipeline = 'attempt: {
|
||||||
if let Ok(Some(blob)) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
if let Ok(blob) = internal::get_blob(&cache, index, hashkey.as_bytes()) {
|
||||||
let cached = restore_pipeline(Some(blob));
|
let cached = restore_pipeline(Some(blob));
|
||||||
match cached {
|
match cached {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
|
@ -201,8 +173,7 @@ where
|
||||||
// update the pso every time just in case.
|
// update the pso every time just in case.
|
||||||
if let Ok(state) = fetch_pipeline_state(&pipeline) {
|
if let Ok(state) = fetch_pipeline_state(&pipeline) {
|
||||||
if let Some(slice) = T::to_bytes(&state) {
|
if let Some(slice) = T::to_bytes(&state) {
|
||||||
// We don't really care if the transaction fails, just try again next time.
|
internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
||||||
let _ = internal::set_blob(&cache, index, hashkey.as_bytes(), &slice);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,3 @@ impl Cacheable for Vec<u8> {
|
||||||
Some(self.to_vec())
|
Some(self.to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cacheable for Option<Vec<u8>> {
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
|
||||||
Some(Some(Vec::from(bytes)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_bytes(&self) -> Option<Vec<u8>> {
|
|
||||||
self.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,35 +1,24 @@
|
||||||
//! Cache helpers for `ShaderCompilation` objects to cache compiled SPIRV.
|
//! Cache helpers for `ShaderCompilation` objects to cache compiled SPIRV.
|
||||||
use librashader_preprocess::ShaderSource;
|
use librashader_preprocess::ShaderSource;
|
||||||
#[cfg(all(target_os = "windows", feature = "d3d"))]
|
use librashader_reflect::back::targets::{DXIL, GLSL, HLSL, SPIRV};
|
||||||
use librashader_reflect::back::targets::DXIL;
|
|
||||||
use librashader_reflect::back::targets::{GLSL, HLSL, SPIRV};
|
|
||||||
|
|
||||||
use librashader_reflect::back::{CompilerBackend, FromCompilation};
|
use librashader_reflect::back::{CompilerBackend, FromCompilation};
|
||||||
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
|
use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
|
||||||
use librashader_reflect::front::{
|
use librashader_reflect::front::{GlslangCompilation, ShaderCompilation};
|
||||||
Glslang, ShaderInputCompiler, ShaderReflectObject, SpirvCompilation,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct CachedCompilation<T> {
|
pub struct CachedCompilation<T> {
|
||||||
compilation: T,
|
compilation: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ShaderReflectObject> ShaderReflectObject for CachedCompilation<T> {
|
impl<T: ShaderCompilation + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
|
||||||
type Compiler = T::Compiler;
|
ShaderCompilation for CachedCompilation<T>
|
||||||
}
|
|
||||||
|
|
||||||
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<CachedCompilation<T>, ShaderCompileError> {
|
fn compile(source: &ShaderSource) -> Result<Self, ShaderCompileError> {
|
||||||
let cache = crate::cache::internal::get_cache();
|
let cache = crate::cache::internal::get_cache();
|
||||||
|
|
||||||
let Ok(cache) = cache else {
|
let Ok(cache) = cache else {
|
||||||
return Ok(CachedCompilation {
|
return Ok(CachedCompilation {
|
||||||
compilation: Glslang::compile(source)?,
|
compilation: T::compile(source)?
|
||||||
});
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let key = {
|
let key = {
|
||||||
|
@ -41,9 +30,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let compilation = 'cached: {
|
let compilation = 'cached: {
|
||||||
if let Ok(Some(cached)) =
|
if let Ok(cached) = crate::cache::internal::get_blob(&cache, "spirv", key.as_bytes()) {
|
||||||
crate::cache::internal::get_blob(&cache, "spirv", key.as_bytes())
|
|
||||||
{
|
|
||||||
let decoded =
|
let decoded =
|
||||||
bincode::serde::decode_from_slice(&cached, bincode::config::standard())
|
bincode::serde::decode_from_slice(&cached, bincode::config::standard())
|
||||||
.map(|(compilation, _)| CachedCompilation { compilation })
|
.map(|(compilation, _)| CachedCompilation { compilation })
|
||||||
|
@ -55,84 +42,67 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedCompilation {
|
CachedCompilation {
|
||||||
compilation: Glslang::compile(source)?,
|
compilation: T::compile(source)?,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(updated) =
|
if let Ok(updated) =
|
||||||
bincode::serde::encode_to_vec(&compilation.compilation, bincode::config::standard())
|
bincode::serde::encode_to_vec(&compilation.compilation, bincode::config::standard())
|
||||||
{
|
{
|
||||||
let Ok(()) =
|
crate::cache::internal::set_blob(&cache, "spirv", key.as_bytes(), &updated)
|
||||||
crate::cache::internal::set_blob(&cache, "spirv", key.as_bytes(), &updated)
|
|
||||||
else {
|
|
||||||
return Ok(compilation);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(compilation)
|
Ok(compilation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "windows", feature = "d3d"))]
|
impl FromCompilation<CachedCompilation<GlslangCompilation>> for DXIL {
|
||||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for DXIL
|
type Target = <DXIL as FromCompilation<GlslangCompilation>>::Target;
|
||||||
where
|
type Options = <DXIL as FromCompilation<GlslangCompilation>>::Options;
|
||||||
DXIL: FromCompilation<SpirvCompilation, T>,
|
type Context = <DXIL as FromCompilation<GlslangCompilation>>::Context;
|
||||||
{
|
type Output = <DXIL as FromCompilation<GlslangCompilation>>::Output;
|
||||||
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(
|
fn from_compilation(
|
||||||
compile: CachedCompilation<SpirvCompilation>,
|
compile: CachedCompilation<GlslangCompilation>,
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||||
DXIL::from_compilation(compile.compilation)
|
DXIL::from_compilation(compile.compilation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for HLSL
|
impl FromCompilation<CachedCompilation<GlslangCompilation>> for HLSL {
|
||||||
where
|
type Target = <HLSL as FromCompilation<GlslangCompilation>>::Target;
|
||||||
HLSL: FromCompilation<SpirvCompilation, T>,
|
type Options = <HLSL as FromCompilation<GlslangCompilation>>::Options;
|
||||||
{
|
type Context = <HLSL as FromCompilation<GlslangCompilation>>::Context;
|
||||||
type Target = <HLSL as FromCompilation<SpirvCompilation, T>>::Target;
|
type Output = <HLSL as FromCompilation<GlslangCompilation>>::Output;
|
||||||
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(
|
fn from_compilation(
|
||||||
compile: CachedCompilation<SpirvCompilation>,
|
compile: CachedCompilation<GlslangCompilation>,
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||||
HLSL::from_compilation(compile.compilation)
|
HLSL::from_compilation(compile.compilation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for GLSL
|
impl FromCompilation<CachedCompilation<GlslangCompilation>> for GLSL {
|
||||||
where
|
type Target = <GLSL as FromCompilation<GlslangCompilation>>::Target;
|
||||||
GLSL: FromCompilation<SpirvCompilation, T>,
|
type Options = <GLSL as FromCompilation<GlslangCompilation>>::Options;
|
||||||
{
|
type Context = <GLSL as FromCompilation<GlslangCompilation>>::Context;
|
||||||
type Target = <GLSL as FromCompilation<SpirvCompilation, T>>::Target;
|
type Output = <GLSL as FromCompilation<GlslangCompilation>>::Output;
|
||||||
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(
|
fn from_compilation(
|
||||||
compile: CachedCompilation<SpirvCompilation>,
|
compile: CachedCompilation<GlslangCompilation>,
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||||
GLSL::from_compilation(compile.compilation)
|
GLSL::from_compilation(compile.compilation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for SPIRV
|
impl FromCompilation<CachedCompilation<GlslangCompilation>> for SPIRV {
|
||||||
where
|
type Target = <SPIRV as FromCompilation<GlslangCompilation>>::Target;
|
||||||
SPIRV: FromCompilation<SpirvCompilation, T>,
|
type Options = <SPIRV as FromCompilation<GlslangCompilation>>::Options;
|
||||||
{
|
type Context = <SPIRV as FromCompilation<GlslangCompilation>>::Context;
|
||||||
type Target = <SPIRV as FromCompilation<SpirvCompilation, T>>::Target;
|
type Output = <SPIRV as FromCompilation<GlslangCompilation>>::Output;
|
||||||
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(
|
fn from_compilation(
|
||||||
compile: CachedCompilation<SpirvCompilation>,
|
compile: CachedCompilation<GlslangCompilation>,
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||||
SPIRV::from_compilation(compile.compilation)
|
SPIRV::from_compilation(compile.compilation)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
//! here because of the orphan rule.
|
//! here because of the orphan rule.
|
||||||
|
|
||||||
use crate::{CacheKey, Cacheable};
|
use crate::{CacheKey, Cacheable};
|
||||||
use windows::core::Interface;
|
|
||||||
|
|
||||||
impl CacheKey for windows::Win32::Graphics::Direct3D::ID3DBlob {
|
impl CacheKey for windows::Win32::Graphics::Direct3D::ID3DBlob {
|
||||||
fn hash_bytes(&self) -> &[u8] {
|
fn hash_bytes(&self) -> &[u8] {
|
||||||
|
@ -18,9 +17,7 @@ impl CacheKey for windows::Win32::Graphics::Direct3D::Dxc::IDxcBlob {
|
||||||
|
|
||||||
impl Cacheable for windows::Win32::Graphics::Direct3D::ID3DBlob {
|
impl Cacheable for windows::Win32::Graphics::Direct3D::ID3DBlob {
|
||||||
fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||||
let Some(blob) =
|
let Some(blob) = (unsafe { windows::Win32::Graphics::Direct3D::Fxc::D3DCreateBlob(bytes.len()).ok() }) else {
|
||||||
(unsafe { windows::Win32::Graphics::Direct3D::Fxc::D3DCreateBlob(bytes.len()).ok() })
|
|
||||||
else {
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,23 +43,16 @@ impl Cacheable for windows::Win32::Graphics::Direct3D::Dxc::IDxcBlob {
|
||||||
fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||||
let Some(blob) = (unsafe {
|
let Some(blob) = (unsafe {
|
||||||
windows::Win32::Graphics::Direct3D::Dxc::DxcCreateInstance(
|
windows::Win32::Graphics::Direct3D::Dxc::DxcCreateInstance(
|
||||||
&windows::Win32::Graphics::Direct3D::Dxc::CLSID_DxcLibrary,
|
&windows::Win32::Graphics::Direct3D::Dxc::CLSID_DxcLibrary)
|
||||||
)
|
.and_then(|library: windows::Win32::Graphics::Direct3D::Dxc::IDxcUtils| {
|
||||||
.and_then(
|
library.CreateBlob(bytes.as_ptr().cast(), bytes.len() as u32,
|
||||||
|library: windows::Win32::Graphics::Direct3D::Dxc::IDxcUtils| {
|
windows::Win32::Graphics::Direct3D::Dxc::DXC_CP(0))
|
||||||
library.CreateBlob(
|
}).ok()
|
||||||
bytes.as_ptr().cast(),
|
|
||||||
bytes.len() as u32,
|
|
||||||
windows::Win32::Graphics::Direct3D::Dxc::DXC_CP(0),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
}) else {
|
}) else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(blob.cast().ok()?)
|
Some(blob.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_bytes(&self) -> Option<Vec<u8>> {
|
fn to_bytes(&self) -> Option<Vec<u8>> {
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "librashader-capi"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
license = "MPL-2.0 OR GPL-3.0-only"
|
||||||
version = "0.5.1"
|
version = "0.1.3"
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
repository = "https://github.com/SnowflakePowered/librashader"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
@ -16,55 +16,25 @@ crate-type = [ "cdylib", "staticlib" ]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["runtime-all" ]
|
default = ["runtime-all" ]
|
||||||
runtime-all = ["runtime-opengl", "runtime-d3d9", "runtime-d3d11", "runtime-d3d12", "runtime-vulkan", "runtime-metal"]
|
runtime-all = ["runtime-opengl", "runtime-d3d11", "runtime-d3d12", "runtime-vulkan"]
|
||||||
runtime-opengl = ["glow", "librashader/runtime-gl"]
|
runtime-opengl = ["gl", "librashader/runtime-gl"]
|
||||||
runtime-d3d11 = ["windows", "librashader/runtime-d3d11", "windows/Win32_Graphics_Direct3D11"]
|
runtime-d3d11 = ["windows", "librashader/runtime-d3d11", "windows/Win32_Graphics_Direct3D11"]
|
||||||
runtime-d3d12 = ["windows", "librashader/runtime-d3d12", "windows/Win32_Graphics_Direct3D12"]
|
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-vulkan = ["ash", "librashader/runtime-vk"]
|
||||||
runtime-metal = ["__cbindgen_internal_objc", "librashader/runtime-metal"]
|
|
||||||
|
|
||||||
reflect-unstable = []
|
|
||||||
stable = ["librashader/stable"]
|
|
||||||
docsrs = []
|
|
||||||
|
|
||||||
__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 = ["objc2-metal", "objc2"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
librashader = { path = "../librashader", version = "0.1.3", features = ["internal"] }
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
paste = "1.0.9"
|
paste = "1.0.9"
|
||||||
rustc-hash = "2.0.0"
|
gl = { version = "0.14.0", optional = true }
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
sptr = "0.3.2"
|
ash = { version = "0.37.2+1.3.238", optional = true }
|
||||||
|
spirv_cross = { package = "librashader-spirv-cross", version = "0.23" }
|
||||||
glow = { workspace = true, optional = true }
|
|
||||||
ash = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
[dependencies.librashader]
|
|
||||||
path = "../librashader"
|
|
||||||
version = "0.5.1"
|
|
||||||
default-features = false
|
|
||||||
features = ["reflect", "presets", "preprocess"]
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
workspace = true
|
version = "0.44.0"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[target.'cfg(target_vendor="apple")'.dependencies]
|
|
||||||
objc2-metal = { version = "0.2.0" , features = [ "all" ], optional = true }
|
|
||||||
objc2 = { version = "0.5.0", features = ["apple"] , optional = true }
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
targets = [ "x86_64-pc-windows-msvc",
|
targets = ["x86_64-pc-windows-msvc", "x86_64-unknown-linux-gnu"]
|
||||||
"x86_64-unknown-linux-gnu",
|
features = ["librashader/docsrs"]
|
||||||
"x86_64-apple-darwin",
|
|
||||||
"aarch64-apple-darwin",
|
|
||||||
"aarch64-apple-ios",
|
|
||||||
"i686-pc-windows-msvc",
|
|
||||||
"i686-unknown-linux-gnu", ]
|
|
||||||
features = ["docsrs", "librashader/docsrs"]
|
|
||||||
|
|
|
@ -5,4 +5,4 @@ pub fn main() {
|
||||||
println!("cargo:rustc-link-arg=/DELAYLOAD:dxcompiler.dll");
|
println!("cargo:rustc-link-arg=/DELAYLOAD:dxcompiler.dll");
|
||||||
println!("cargo:rustc-link-arg=/DELAYLOAD:d3d12.dll");
|
println!("cargo:rustc-link-arg=/DELAYLOAD:d3d12.dll");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,18 +34,28 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
after_includes = """
|
after_includes = """
|
||||||
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D11)
|
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D11)
|
||||||
#include <d3d11.h>
|
#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
|
#endif
|
||||||
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12)
|
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12)
|
||||||
#include <d3d12.h>
|
#include <d3d12.h>
|
||||||
#endif
|
#else
|
||||||
#if defined(_WIN32) && defined(LIBRA_RUNTIME_D3D9)
|
typedef void ID3D12GraphicsCommandList;
|
||||||
#include <D3D9.h>
|
typedef void ID3D12Device;
|
||||||
#endif
|
typedef void ID3D12Resource;
|
||||||
#if defined(__APPLE__) && defined(LIBRA_RUNTIME_METAL) && defined(__OBJC__)
|
typedef void D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||||
#import <Metal/Metal.h>
|
|
||||||
#endif
|
|
||||||
#if defined(LIBRA_RUNTIME_VULKAN)
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#endif
|
#endif
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -54,20 +64,20 @@ after_includes = """
|
||||||
"feature = runtime-vulkan" = "LIBRA_RUNTIME_VULKAN"
|
"feature = runtime-vulkan" = "LIBRA_RUNTIME_VULKAN"
|
||||||
"feature = runtime-d3d11" = "LIBRA_RUNTIME_D3D11"
|
"feature = runtime-d3d11" = "LIBRA_RUNTIME_D3D11"
|
||||||
"feature = runtime-d3d12" = "LIBRA_RUNTIME_D3D12"
|
"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]
|
||||||
parse_deps = false
|
parse_deps = true
|
||||||
include = ["librashader"]
|
include = ["librashader",
|
||||||
|
"librashader-presets",
|
||||||
[parse.expand]
|
"librashader-preprocess",
|
||||||
crates = ["librashader-capi"]
|
"librashader-reflect",
|
||||||
features = ["__cbindgen_internal"]
|
"librashader-runtime-gl",
|
||||||
|
"librashader-runtime-vk",
|
||||||
|
"librashader-runtime-d3d11",
|
||||||
|
"librashader-runtime-d3d12",
|
||||||
|
]
|
||||||
|
expand = ["librashader-capi"]
|
||||||
|
|
||||||
[struct]
|
[struct]
|
||||||
|
|
||||||
|
@ -89,20 +99,6 @@ include = [
|
||||||
"PFN_libra_preset_print",
|
"PFN_libra_preset_print",
|
||||||
"PFN_libra_preset_get_runtime_params",
|
"PFN_libra_preset_get_runtime_params",
|
||||||
"PFN_libra_preset_free_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
|
# error
|
||||||
"PFN_libra_error_errno",
|
"PFN_libra_error_errno",
|
||||||
|
@ -141,14 +137,6 @@ include = [
|
||||||
"PFN_libra_d3d11_filter_chain_get_active_pass_count",
|
"PFN_libra_d3d11_filter_chain_get_active_pass_count",
|
||||||
"PFN_libra_d3d11_filter_chain_free",
|
"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
|
# d3d12
|
||||||
"PFN_libra_d3d12_filter_chain_create",
|
"PFN_libra_d3d12_filter_chain_create",
|
||||||
|
@ -159,38 +147,17 @@ include = [
|
||||||
"PFN_libra_d3d12_filter_chain_set_active_pass_count",
|
"PFN_libra_d3d12_filter_chain_set_active_pass_count",
|
||||||
"PFN_libra_d3d12_filter_chain_get_active_pass_count",
|
"PFN_libra_d3d12_filter_chain_get_active_pass_count",
|
||||||
"PFN_libra_d3d12_filter_chain_free",
|
"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 = [
|
exclude = ["Option_ID3D11DeviceContext"]
|
||||||
"Option_ID3D11DeviceContext",
|
|
||||||
"Option_PFN_vkGetInstanceProcAddr",
|
|
||||||
"PMTLCommandQueue",
|
|
||||||
"PMTLCommandBuffer",
|
|
||||||
"PMTLTexture"
|
|
||||||
]
|
|
||||||
|
|
||||||
[export.rename]
|
[export.rename]
|
||||||
"LibrashaderError" = "_libra_error"
|
"LibrashaderError" = "_libra_error"
|
||||||
"ShaderPreset" = "_shader_preset"
|
"ShaderPreset" = "_shader_preset"
|
||||||
|
|
||||||
"WildcardContext" = "_preset_ctx"
|
|
||||||
|
|
||||||
"FilterChainGL" = "_filter_chain_gl"
|
"FilterChainGL" = "_filter_chain_gl"
|
||||||
"FilterChainVulkan" = "_filter_chain_vk"
|
"FilterChainVulkan" = "_filter_chain_vk"
|
||||||
"FilterChainD3D11" = "_filter_chain_d3d11"
|
"FilterChainD3D11" = "_filter_chain_d3d11"
|
||||||
"FilterChainD3D12" = "_filter_chain_d3d12"
|
"FilterChainD3D12" = "_filter_chain_d3d12"
|
||||||
"FilterChainD3D9" = "_filter_chain_d3d9"
|
|
||||||
"FilterChainMetal" = "_filter_chain_mtl"
|
|
||||||
|
|
||||||
# vulkan renames
|
# vulkan renames
|
||||||
"PhysicalDevice" = "VkPhysicalDevice"
|
"PhysicalDevice" = "VkPhysicalDevice"
|
||||||
|
@ -199,7 +166,6 @@ exclude = [
|
||||||
"CommandBuffer" = "VkCommandBuffer"
|
"CommandBuffer" = "VkCommandBuffer"
|
||||||
"Format" = "VkFormat"
|
"Format" = "VkFormat"
|
||||||
"Image" = "VkImage"
|
"Image" = "VkImage"
|
||||||
"Queue" = "VkQueue"
|
|
||||||
|
|
||||||
# hack to get proper pointer indirection for COM pointers
|
# hack to get proper pointer indirection for COM pointers
|
||||||
# we don't need one for ID3D11DeviceContext.
|
# we don't need one for ID3D11DeviceContext.
|
||||||
|
@ -208,22 +174,10 @@ exclude = [
|
||||||
"ID3D11RenderTargetView" = "ID3D11RenderTargetView *"
|
"ID3D11RenderTargetView" = "ID3D11RenderTargetView *"
|
||||||
"ID3D11ShaderResourceView" = "ID3D11ShaderResourceView *"
|
"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.
|
# hack to force cbindgen to not generate option type for nullable ID3D11DeviceContext.
|
||||||
"Option_ID3D11DeviceContext" = "ID3D11DeviceContext *"
|
"Option_ID3D11DeviceContext" = "ID3D11DeviceContext *"
|
||||||
|
|
||||||
# hack to force cbindgen to not generate option type for nullable PFN_vkGetInstanceProcAddr.
|
|
||||||
"Option_PFN_vkGetInstanceProcAddr" = "PFN_vkGetInstanceProcAddr"
|
|
||||||
|
|
||||||
# hack to get proper pointer indirection for COM pointers
|
# hack to get proper pointer indirection for COM pointers
|
||||||
"ID3D12Device" = "ID3D12Device *"
|
"ID3D12Device" = "ID3D12Device *"
|
||||||
"ID3D12Resource" = "ID3D12Resource *"
|
"ID3D12Resource" = "ID3D12Resource *"
|
||||||
"ID3D12GraphicsCommandList" = "ID3D12GraphicsCommandList *"
|
"ID3D12GraphicsCommandList" = "ID3D12GraphicsCommandList *"
|
||||||
|
|
||||||
"PMTLCommandQueue" = "id<MTLCommandQueue>"
|
|
||||||
"PMTLCommandBuffer" = "id<MTLCommandBuffer>"
|
|
||||||
"PMTLTexture" = "id<MTLTexture>"
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Binding types for the librashader C API.
|
//! Binding types for the librashader C API.
|
||||||
use crate::error::LibrashaderError;
|
use crate::error::LibrashaderError;
|
||||||
use librashader::presets::context::{Orientation, VideoDriver, WildcardContext};
|
|
||||||
use librashader::presets::ShaderPreset;
|
use librashader::presets::ShaderPreset;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
@ -8,157 +7,42 @@ use std::ptr::NonNull;
|
||||||
/// A handle to a shader preset object.
|
/// A handle to a shader preset object.
|
||||||
pub type libra_shader_preset_t = Option<NonNull<ShaderPreset>>;
|
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.
|
/// A handle to a librashader error object.
|
||||||
pub type libra_error_t = Option<NonNull<LibrashaderError>>;
|
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 {
|
|
||||||
/// Context parameter for vertical orientation.
|
|
||||||
Vertical = 0,
|
|
||||||
/// Context parameter for horizontal orientation.
|
|
||||||
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 {
|
|
||||||
/// No runtime.
|
|
||||||
None = 0,
|
|
||||||
/// OpenGL 3.3+
|
|
||||||
GlCore,
|
|
||||||
/// Vulkan
|
|
||||||
Vulkan,
|
|
||||||
/// Direct3D 11
|
|
||||||
D3D11,
|
|
||||||
/// Direct3D 12
|
|
||||||
D3D12,
|
|
||||||
/// Metal
|
|
||||||
Metal,
|
|
||||||
/// Direct3D 9
|
|
||||||
D3D9_HLSL,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
LIBRA_PRESET_CTX_RUNTIME::D3D9_HLSL => VideoDriver::Direct3D9Hlsl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "runtime-opengl")]
|
|
||||||
use librashader::runtime::gl::FilterChain as FilterChainGL;
|
|
||||||
|
|
||||||
/// A handle to a OpenGL filter chain.
|
/// A handle to a OpenGL filter chain.
|
||||||
#[cfg(feature = "runtime-opengl")]
|
#[cfg(feature = "runtime-opengl")]
|
||||||
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-opengl")))]
|
#[doc(cfg(feature = "runtime-opengl"))]
|
||||||
pub type libra_gl_filter_chain_t = Option<NonNull<FilterChainGL>>;
|
pub type libra_gl_filter_chain_t = Option<NonNull<librashader::runtime::gl::capi::FilterChainGL>>;
|
||||||
|
|
||||||
/// A handle to a Direct3D 11 filter chain.
|
/// A handle to a Direct3D 11 filter chain.
|
||||||
#[cfg(any(
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
|
||||||
feature = "__cbindgen_internal",
|
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))]
|
||||||
all(target_os = "windows", feature = "runtime-d3d11")
|
pub type libra_d3d11_filter_chain_t =
|
||||||
))]
|
Option<NonNull<librashader::runtime::d3d11::capi::FilterChainD3D11>>;
|
||||||
use librashader::runtime::d3d11::FilterChain as FilterChainD3D11;
|
|
||||||
|
|
||||||
/// A handle to a Direct3D 11 filter chain.
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "docsrs",
|
|
||||||
doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))
|
|
||||||
)]
|
|
||||||
#[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.
|
/// A handle to a Direct3D 12 filter chain.
|
||||||
#[cfg(any(
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||||
feature = "__cbindgen_internal",
|
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))]
|
||||||
all(target_os = "windows", feature = "runtime-d3d12")
|
pub type libra_d3d12_filter_chain_t =
|
||||||
))]
|
Option<NonNull<librashader::runtime::d3d12::capi::FilterChainD3D12>>;
|
||||||
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.
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "docsrs",
|
|
||||||
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")]
|
|
||||||
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-vulkan")))]
|
|
||||||
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;
|
|
||||||
|
|
||||||
/// A handle to a Vulkan filter chain.
|
/// A handle to a Vulkan filter chain.
|
||||||
#[cfg_attr(
|
#[cfg(feature = "runtime-vulkan")]
|
||||||
feature = "docsrs",
|
#[doc(cfg(feature = "runtime-vulkan"))]
|
||||||
doc(cfg(all(target_vendor = "apple", feature = "runtime-metal")))
|
pub type libra_vk_filter_chain_t =
|
||||||
)]
|
Option<NonNull<librashader::runtime::vk::capi::FilterChainVulkan>>;
|
||||||
#[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 origin for a rendered frame.
|
/// Defines the output viewport for a rendered frame.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct libra_viewport_t {
|
pub struct libra_viewport_t {
|
||||||
/// The x offset in the viewport framebuffer to begin rendering from.
|
/// The x offset in the viewport framebuffer to begin rendering from.
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
/// The y offset in the viewport framebuffer to begin rendering from.
|
/// The y offset in the viewport framebuffer to begin rendering from.
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
/// The width extent of the viewport framebuffer to end rendering, relative to
|
/// The width of the viewport framebuffer.
|
||||||
/// the origin specified by x.
|
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
/// The height extent of the viewport framebuffer to end rendering, relative to
|
/// The height of the viewport framebuffer.
|
||||||
/// the origin specified by y.
|
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,85 +54,29 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! config_set_field {
|
macro_rules! config_set_field {
|
||||||
(@POINTER $options:ident.$field:ident <- $ptr:ident) => {
|
($options:ident.$field:ident <- $ptr:ident) => {
|
||||||
$options.$field = unsafe { ::std::ptr::addr_of!((*$ptr).$field).read() };
|
$options.$field = unsafe { ::std::ptr::addr_of!((*$ptr).$field).read() };
|
||||||
};
|
};
|
||||||
(@POINTER @NEGATIVE $options:ident.$field:ident <- $ptr:ident) => {
|
|
||||||
$options.$field = unsafe { !::std::ptr::addr_of!((*$ptr).$field).read() };
|
|
||||||
};
|
|
||||||
(@LITERAL $options:ident.$field:ident <- $value:literal) => {
|
|
||||||
$options.$field = $value;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! config_version_set {
|
macro_rules! config_version_set {
|
||||||
// "optimized" version for normal behaviour
|
($version:literal => [$($field:ident),+ $(,)?] ($options:ident <- $ptr:ident)) => {
|
||||||
(@ROOT $realver:ident $version:literal => [$($field:ident),+ $(,)?] ($options:ident <- $ptr:ident)) => {
|
let version = unsafe { ::std::ptr::addr_of!((*$ptr).version).read() };
|
||||||
#[allow(unused_comparisons)]
|
#[allow(unused_comparisons)]
|
||||||
if $realver >= $version {
|
if version >= $version {
|
||||||
$($crate::ctypes::config_set_field!(@POINTER $options.$field <- $ptr);)+
|
$($crate::ctypes::config_set_field!($options.$field <- $ptr);)+
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Repeater
|
|
||||||
(@ROOT $realver:ident $version:literal => [$($field:tt),+ $(,)?] ($options:ident <- $ptr:ident)) => {
|
|
||||||
$(crate::ctypes::config_version_set!(@SINGLE $realver $version => [$field] ($options <- $ptr));)+
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allow overriding default value with a literal for older versions
|
|
||||||
(@SINGLE $realver:ident $version:literal => [($field:ident: $value:literal)] ($options:ident <- $ptr:ident)) => {
|
|
||||||
#[allow(unused_comparisons)]
|
|
||||||
if $realver >= $version {
|
|
||||||
$crate::ctypes::config_set_field!(@LITERAL $options.$field <- $value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allow negation of prior variables that is version dependent.
|
|
||||||
(@SINGLE $realver:ident $version:literal => [(!$field:ident)] ($options:ident <- $ptr:ident)) => {
|
|
||||||
#[allow(unused_comparisons)]
|
|
||||||
if $realver >= $version {
|
|
||||||
$crate::ctypes::config_set_field!(@POINTER @NEGATIVE $options.$field <- $ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(@SINGLE $realver:ident $version:literal => [$field:ident] ($options:ident <- $ptr:ident)) => {
|
|
||||||
#[allow(unused_comparisons)]
|
|
||||||
if $realver >= $version {
|
|
||||||
$crate::ctypes::config_set_field!(@POINTER $options.$field <- $ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Macro to declare a configuration struct, with options to change behaviour based on
|
|
||||||
/// API version.
|
|
||||||
///
|
|
||||||
/// For example following declaration does the following
|
|
||||||
///
|
|
||||||
/// * Declare `frames_in_flight`, `use_dynamic_rendering` for API version 0, with the following forward compatibility statements
|
|
||||||
/// * Inverts the behaviour of `use_dynamic_rendering` compared to API version 1.
|
|
||||||
/// * `disable_cache` is defaulted to `true` for API version 0, regardless of `Default::default`
|
|
||||||
/// but is not declared for API 0.
|
|
||||||
/// * Declare `use_dynamic_rendering` with normal behaviour, and `disable_cache` for API version 1.
|
|
||||||
/// * All fields that are undeclared inherit `Default::default`
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// config_struct! {
|
|
||||||
/// impl FilterChainOptions => filter_chain_vk_opt_t {
|
|
||||||
/// 0 => [frames_in_flight, (!use_dynamic_rendering), (disable_cache: true)];
|
|
||||||
/// 1 => [use_dynamic_rendering, disable_cache];
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
macro_rules! config_struct {
|
macro_rules! config_struct {
|
||||||
(impl $rust:ty => $capi:ty {$($version:literal => [$($field:tt),+]);+ $(;)?}) => {
|
(impl $rust:ty => $capi:ty {$($version:literal => [$($field:ident),+ $(,)?]);+ $(;)?}) => {
|
||||||
impl $crate::ctypes::FromUninit<$rust> for $capi {
|
impl $crate::ctypes::FromUninit<$rust> for $capi {
|
||||||
fn from_uninit(value: ::std::mem::MaybeUninit<Self>) -> $rust {
|
fn from_uninit(value: ::std::mem::MaybeUninit<Self>) -> $rust {
|
||||||
let ptr = value.as_ptr();
|
let ptr = value.as_ptr();
|
||||||
let version = unsafe { ::std::ptr::addr_of!((*ptr).version).read() };
|
|
||||||
|
|
||||||
let mut options = <$rust>::default();
|
let mut options = <$rust>::default();
|
||||||
$(
|
$(
|
||||||
$crate::ctypes::config_version_set!(@ROOT version $version => [$($field),+] (options <- ptr));
|
$crate::ctypes::config_version_set!($version => [$($field),+] (options <- ptr));
|
||||||
)+
|
)+
|
||||||
options
|
options
|
||||||
}
|
}
|
||||||
|
@ -259,39 +87,3 @@ macro_rules! config_struct {
|
||||||
pub(crate) use config_set_field;
|
pub(crate) use config_set_field;
|
||||||
pub(crate) use config_struct;
|
pub(crate) use config_struct;
|
||||||
pub(crate) use config_version_set;
|
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,120 +5,54 @@ use std::mem::MaybeUninit;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// The error type for librashader C API.
|
/// The error type for librashader.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum LibrashaderError {
|
pub enum LibrashaderError {
|
||||||
/// An unknown error or panic occurred.
|
|
||||||
#[error("There was an unknown error.")]
|
#[error("There was an unknown error.")]
|
||||||
UnknownError(Box<dyn Any + Send + 'static>),
|
UnknownError(Box<dyn Any + Send + 'static>),
|
||||||
|
|
||||||
/// An invalid parameter (likely null), was passed.
|
|
||||||
#[error("The parameter was null or invalid.")]
|
#[error("The parameter was null or invalid.")]
|
||||||
InvalidParameter(&'static str),
|
InvalidParameter(&'static str),
|
||||||
|
|
||||||
/// The string provided was not valid UTF-8.
|
|
||||||
#[error("The provided string was not valid UTF8.")]
|
#[error("The provided string was not valid UTF8.")]
|
||||||
InvalidString(#[from] std::str::Utf8Error),
|
InvalidString(#[from] std::str::Utf8Error),
|
||||||
|
|
||||||
/// An error occurred in the preset parser.
|
|
||||||
#[error("There was an error parsing the preset.")]
|
#[error("There was an error parsing the preset.")]
|
||||||
PresetError(#[from] librashader::presets::ParsePresetError),
|
PresetError(#[from] librashader::presets::ParsePresetError),
|
||||||
|
|
||||||
/// An error occurred in the shader preprocessor.
|
|
||||||
#[error("There was an error preprocessing the shader source.")]
|
#[error("There was an error preprocessing the shader source.")]
|
||||||
PreprocessError(#[from] librashader::preprocess::PreprocessError),
|
PreprocessError(#[from] librashader::preprocess::PreprocessError),
|
||||||
|
|
||||||
/// An error occurred in the shader compiler.
|
|
||||||
#[error("There was an error compiling the shader source.")]
|
#[error("There was an error compiling the shader source.")]
|
||||||
ShaderCompileError(#[from] librashader::reflect::ShaderCompileError),
|
ShaderCompileError(#[from] librashader::reflect::ShaderCompileError),
|
||||||
|
|
||||||
/// An error occrred when validating and reflecting the shader.
|
|
||||||
#[error("There was an error reflecting the shader source.")]
|
#[error("There was an error reflecting the shader source.")]
|
||||||
ShaderReflectError(#[from] librashader::reflect::ShaderReflectError),
|
ShaderReflectError(#[from] librashader::reflect::ShaderReflectError),
|
||||||
|
|
||||||
/// An invalid shader parameter name was provided.
|
|
||||||
#[error("The provided parameter name was invalid.")]
|
#[error("The provided parameter name was invalid.")]
|
||||||
UnknownShaderParameter(*const c_char),
|
UnknownShaderParameter(*const c_char),
|
||||||
|
|
||||||
/// An error occurred with the OpenGL filter chain.
|
|
||||||
#[cfg(feature = "runtime-opengl")]
|
#[cfg(feature = "runtime-opengl")]
|
||||||
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-opengl")))]
|
#[doc(cfg(feature = "runtime-opengl"))]
|
||||||
#[error("There was an error in the OpenGL filter chain.")]
|
#[error("There was an error in the OpenGL filter chain.")]
|
||||||
OpenGlFilterError(#[from] librashader::runtime::gl::error::FilterChainError),
|
OpenGlFilterError(#[from] librashader::runtime::gl::error::FilterChainError),
|
||||||
|
|
||||||
/// An error occurred with the Direct3D 11 filter chain.
|
|
||||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
|
||||||
#[cfg_attr(
|
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))]
|
||||||
feature = "docsrs",
|
|
||||||
doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))
|
|
||||||
)]
|
|
||||||
#[error("There was an error in the D3D11 filter chain.")]
|
#[error("There was an error in the D3D11 filter chain.")]
|
||||||
D3D11FilterError(#[from] librashader::runtime::d3d11::error::FilterChainError),
|
D3D11FilterError(#[from] librashader::runtime::d3d11::error::FilterChainError),
|
||||||
|
|
||||||
/// An error occurred with the Direct3D 12 filter chain.
|
|
||||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||||
#[cfg_attr(
|
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))]
|
||||||
feature = "docsrs",
|
|
||||||
doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))
|
|
||||||
)]
|
|
||||||
#[error("There was an error in the D3D12 filter chain.")]
|
#[error("There was an error in the D3D12 filter chain.")]
|
||||||
D3D12FilterError(#[from] librashader::runtime::d3d12::error::FilterChainError),
|
D3D12FilterError(#[from] librashader::runtime::d3d12::error::FilterChainError),
|
||||||
|
|
||||||
/// An error occurred with the Direct3D 9 filter chain.
|
|
||||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d9"))]
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "docsrs",
|
|
||||||
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),
|
|
||||||
|
|
||||||
/// An error occurred with the Vulkan filter chain.
|
|
||||||
|
|
||||||
#[cfg(feature = "runtime-vulkan")]
|
#[cfg(feature = "runtime-vulkan")]
|
||||||
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-vulkan")))]
|
#[doc(cfg(feature = "runtime-vulkan"))]
|
||||||
#[error("There was an error in the Vulkan filter chain.")]
|
#[error("There was an error in the Vulkan filter chain.")]
|
||||||
VulkanFilterError(#[from] librashader::runtime::vk::error::FilterChainError),
|
VulkanFilterError(#[from] librashader::runtime::vk::error::FilterChainError),
|
||||||
|
|
||||||
/// An error occurred with the Metal filter chain.
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "docsrs",
|
|
||||||
doc(cfg(all(target_vendor = "apple", feature = "runtime-metal")))
|
|
||||||
)]
|
|
||||||
#[cfg(all(target_vendor = "apple", feature = "runtime-metal"))]
|
|
||||||
#[error("There was an error in the Metal filter chain.")]
|
|
||||||
MetalFilterError(#[from] librashader::runtime::mtl::error::FilterChainError),
|
|
||||||
/// This error is unreachable.
|
|
||||||
#[error("This error is not reachable")]
|
|
||||||
Infallible(#[from] std::convert::Infallible),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error codes for librashader error types.
|
/// Error codes for librashader error types.
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum LIBRA_ERRNO {
|
pub enum LIBRA_ERRNO {
|
||||||
/// Error code for an unknown error.
|
|
||||||
UNKNOWN_ERROR = 0,
|
UNKNOWN_ERROR = 0,
|
||||||
|
|
||||||
/// Error code for an invalid parameter.
|
|
||||||
INVALID_PARAMETER = 1,
|
INVALID_PARAMETER = 1,
|
||||||
|
|
||||||
/// Error code for an invalid (non-UTF8) string.
|
|
||||||
INVALID_STRING = 2,
|
INVALID_STRING = 2,
|
||||||
|
|
||||||
/// Error code for a preset parser error.
|
|
||||||
PRESET_ERROR = 3,
|
PRESET_ERROR = 3,
|
||||||
|
|
||||||
/// Error code for a preprocessor error.
|
|
||||||
PREPROCESS_ERROR = 4,
|
PREPROCESS_ERROR = 4,
|
||||||
|
|
||||||
/// Error code for a shader parameter error.
|
|
||||||
SHADER_PARAMETER_ERROR = 5,
|
SHADER_PARAMETER_ERROR = 5,
|
||||||
|
|
||||||
/// Error code for a reflection error.
|
|
||||||
REFLECT_ERROR = 6,
|
REFLECT_ERROR = 6,
|
||||||
|
|
||||||
/// Error code for a runtime error.
|
|
||||||
RUNTIME_ERROR = 7,
|
RUNTIME_ERROR = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +67,7 @@ pub type PFN_libra_error_errno = extern "C" fn(error: libra_error_t) -> LIBRA_ER
|
||||||
/// - `error` must be valid and initialized.
|
/// - `error` must be valid and initialized.
|
||||||
pub unsafe extern "C" fn libra_error_errno(error: libra_error_t) -> LIBRA_ERRNO {
|
pub unsafe extern "C" fn libra_error_errno(error: libra_error_t) -> LIBRA_ERRNO {
|
||||||
let Some(error) = error else {
|
let Some(error) = error else {
|
||||||
return LIBRA_ERRNO::UNKNOWN_ERROR;
|
return LIBRA_ERRNO::UNKNOWN_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe { error.as_ref().get_code() }
|
unsafe { error.as_ref().get_code() }
|
||||||
|
@ -148,7 +82,9 @@ pub type PFN_libra_error_print = extern "C" fn(error: libra_error_t) -> i32;
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `error` must be a valid and initialized instance of `libra_error_t`.
|
/// - `error` must be a valid and initialized instance of `libra_error_t`.
|
||||||
pub unsafe extern "C" fn libra_error_print(error: libra_error_t) -> i32 {
|
pub unsafe extern "C" fn libra_error_print(error: libra_error_t) -> i32 {
|
||||||
let Some(error) = error else { return 1 };
|
let Some(error) = error else {
|
||||||
|
return 1
|
||||||
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
let error = error.as_ref();
|
let error = error.as_ref();
|
||||||
println!("{error:?}: {error}");
|
println!("{error:?}: {error}");
|
||||||
|
@ -194,7 +130,9 @@ pub unsafe extern "C" fn libra_error_write(
|
||||||
error: libra_error_t,
|
error: libra_error_t,
|
||||||
out: *mut MaybeUninit<*mut c_char>,
|
out: *mut MaybeUninit<*mut c_char>,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
let Some(error) = error else { return 1 };
|
let Some(error) = error else {
|
||||||
|
return 1
|
||||||
|
};
|
||||||
if out.is_null() {
|
if out.is_null() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -202,7 +140,7 @@ pub unsafe extern "C" fn libra_error_write(
|
||||||
unsafe {
|
unsafe {
|
||||||
let error = error.as_ref();
|
let error = error.as_ref();
|
||||||
let Ok(cstring) = CString::new(format!("{error:?}: {error}")) else {
|
let Ok(cstring) = CString::new(format!("{error:?}: {error}")) else {
|
||||||
return 1;
|
return 1
|
||||||
};
|
};
|
||||||
|
|
||||||
out.write(MaybeUninit::new(cstring.into_raw()))
|
out.write(MaybeUninit::new(cstring.into_raw()))
|
||||||
|
@ -251,13 +189,8 @@ impl LibrashaderError {
|
||||||
LibrashaderError::D3D11FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
LibrashaderError::D3D11FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||||
LibrashaderError::D3D12FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
LibrashaderError::D3D12FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||||
#[cfg(all(target_os = "windows", feature = "runtime-d3d9"))]
|
|
||||||
LibrashaderError::D3D9FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
|
||||||
#[cfg(feature = "runtime-vulkan")]
|
#[cfg(feature = "runtime-vulkan")]
|
||||||
LibrashaderError::VulkanFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
LibrashaderError::VulkanFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
||||||
#[cfg(all(target_vendor = "apple", feature = "runtime-metal"))]
|
|
||||||
LibrashaderError::MetalFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
|
|
||||||
LibrashaderError::Infallible(_) => LIBRA_ERRNO::UNKNOWN_ERROR,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) const fn ok() -> libra_error_t {
|
pub(crate) const fn ok() -> libra_error_t {
|
||||||
|
@ -270,13 +203,13 @@ impl LibrashaderError {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! assert_non_null {
|
macro_rules! assert_non_null {
|
||||||
(@EXPORT $value:ident) => {
|
($value:ident) => {
|
||||||
if $value.is_null() || !$crate::ffi::ptr_is_aligned($value) {
|
if $value.is_null() || !$value.is_aligned() {
|
||||||
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export();
|
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($value:ident) => {
|
(noexport $value:ident) => {
|
||||||
if $value.is_null() || !$crate::ffi::ptr_is_aligned($value) {
|
if $value.is_null() || !$value.is_aligned() {
|
||||||
return Err($crate::error::LibrashaderError::InvalidParameter(
|
return Err($crate::error::LibrashaderError::InvalidParameter(
|
||||||
stringify!($value),
|
stringify!($value),
|
||||||
));
|
));
|
||||||
|
@ -287,18 +220,14 @@ macro_rules! assert_non_null {
|
||||||
macro_rules! assert_some_ptr {
|
macro_rules! assert_some_ptr {
|
||||||
($value:ident) => {
|
($value:ident) => {
|
||||||
if $value.is_none() {
|
if $value.is_none() {
|
||||||
return Err($crate::error::LibrashaderError::InvalidParameter(
|
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export();
|
||||||
stringify!($value),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let $value = unsafe { $value.as_ref().unwrap_unchecked().as_ref() };
|
let $value = unsafe { $value.as_ref().unwrap_unchecked().as_ref() };
|
||||||
};
|
};
|
||||||
(mut $value:ident) => {
|
(mut $value:ident) => {
|
||||||
if $value.is_none() {
|
if $value.is_none() {
|
||||||
return Err($crate::error::LibrashaderError::InvalidParameter(
|
return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export();
|
||||||
stringify!($value),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let $value = unsafe { $value.as_mut().unwrap_unchecked().as_mut() };
|
let $value = unsafe { $value.as_mut().unwrap_unchecked().as_mut() };
|
||||||
|
|
|
@ -1,40 +1,9 @@
|
||||||
macro_rules! wrap_ok {
|
|
||||||
($e:expr) => {
|
|
||||||
::core::iter::empty().try_fold($e, |_, __x: ::core::convert::Infallible| match __x {})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! ffi_body {
|
macro_rules! ffi_body {
|
||||||
(nopanic $body:block) => {
|
|
||||||
{
|
|
||||||
let result: Result<(), $crate::error::LibrashaderError> = (|| $crate::ffi::wrap_ok!({
|
|
||||||
$body
|
|
||||||
}))();
|
|
||||||
|
|
||||||
let Err(e) = result else {
|
|
||||||
return $crate::error::LibrashaderError::ok()
|
|
||||||
};
|
|
||||||
e.export()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($body:block) => {
|
($body:block) => {
|
||||||
{
|
{
|
||||||
let result = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
|
let result: Result<(), $crate::error::LibrashaderError> = try {
|
||||||
$crate::ffi::ffi_body!(nopanic $body)
|
|
||||||
}));
|
|
||||||
|
|
||||||
result.unwrap_or_else(|e| $crate::error::LibrashaderError::UnknownError(e).export())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(nopanic |$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
|
|
||||||
{
|
|
||||||
$($crate::error::assert_non_null!(@EXPORT $ref_capture);)*
|
|
||||||
$(let $ref_capture = unsafe { &*$ref_capture };)*
|
|
||||||
$($crate::error::assert_non_null!(@EXPORT $mut_capture);)*
|
|
||||||
$(let $mut_capture = unsafe { &mut *$mut_capture };)*
|
|
||||||
let result: Result<(), $crate::error::LibrashaderError> = (|| $crate::ffi::wrap_ok!({
|
|
||||||
$body
|
$body
|
||||||
}))();
|
};
|
||||||
|
|
||||||
let Err(e) = result else {
|
let Err(e) = result else {
|
||||||
return $crate::error::LibrashaderError::ok()
|
return $crate::error::LibrashaderError::ok()
|
||||||
|
@ -44,20 +13,13 @@ macro_rules! ffi_body {
|
||||||
};
|
};
|
||||||
(|$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
|
(|$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
|
||||||
{
|
{
|
||||||
let result = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
|
$($crate::error::assert_non_null!($ref_capture);)*
|
||||||
$crate::ffi::ffi_body!(nopanic |$($ref_capture),*|; mut |$($mut_capture),*| $body)
|
$(let $ref_capture = unsafe { &*$ref_capture };)*
|
||||||
}));
|
$($crate::error::assert_non_null!($mut_capture);)*
|
||||||
|
|
||||||
result.unwrap_or_else(|e| $crate::error::LibrashaderError::UnknownError(e).export())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(nopanic mut |$($mut_capture:ident),*| $body:block) => {
|
|
||||||
{
|
|
||||||
$($crate::error::assert_non_null!(@EXPORT $mut_capture);)*
|
|
||||||
$(let $mut_capture = unsafe { &mut *$mut_capture };)*
|
$(let $mut_capture = unsafe { &mut *$mut_capture };)*
|
||||||
let result: Result<(), $crate::error::LibrashaderError> = (|| $crate::ffi::wrap_ok!({
|
let result: Result<(), $crate::error::LibrashaderError> = try {
|
||||||
$body
|
$body
|
||||||
}))();
|
};
|
||||||
|
|
||||||
let Err(e) = result else {
|
let Err(e) = result else {
|
||||||
return $crate::error::LibrashaderError::ok()
|
return $crate::error::LibrashaderError::ok()
|
||||||
|
@ -66,21 +28,12 @@ macro_rules! ffi_body {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(mut |$($mut_capture:ident),*| $body:block) => {
|
(mut |$($mut_capture:ident),*| $body:block) => {
|
||||||
{
|
|
||||||
let result = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
|
|
||||||
$crate::ffi::ffi_body!(nopanic mut |$($mut_capture),*| $body)
|
|
||||||
}));
|
|
||||||
|
|
||||||
result.unwrap_or_else(|e| $crate::error::LibrashaderError::UnknownError(e).export())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(nopanic |$($ref_capture:ident),*| $body:block) => {
|
|
||||||
{
|
{
|
||||||
$($crate::error::assert_non_null!(@EXPORT $ref_capture);)*
|
$($crate::error::assert_non_null!($mut_capture);)*
|
||||||
$(let $ref_capture = unsafe { &*$ref_capture };)*
|
$(let $mut_capture = unsafe { &mut *$mut_capture };)*
|
||||||
let result: Result<(), $crate::error::LibrashaderError> = (|| $crate::ffi::wrap_ok!({
|
let result: Result<(), $crate::error::LibrashaderError> = try {
|
||||||
$body
|
$body
|
||||||
}))();
|
};
|
||||||
|
|
||||||
let Err(e) = result else {
|
let Err(e) = result else {
|
||||||
return $crate::error::LibrashaderError::ok()
|
return $crate::error::LibrashaderError::ok()
|
||||||
|
@ -90,32 +43,21 @@ macro_rules! ffi_body {
|
||||||
};
|
};
|
||||||
(|$($ref_capture:ident),*| $body:block) => {
|
(|$($ref_capture:ident),*| $body:block) => {
|
||||||
{
|
{
|
||||||
let result = ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
|
$($crate::error::assert_non_null!($ref_capture);)*
|
||||||
$crate::ffi::ffi_body!(nopanic |$($ref_capture),*| $body)
|
$(let $ref_capture = unsafe { &*$ref_capture };)*
|
||||||
}));
|
let result: Result<(), $crate::error::LibrashaderError> = try {
|
||||||
|
$body
|
||||||
|
};
|
||||||
|
|
||||||
result.unwrap_or_else(|e| $crate::error::LibrashaderError::UnknownError(e).export())
|
let Err(e) = result else {
|
||||||
|
return $crate::error::LibrashaderError::ok()
|
||||||
|
};
|
||||||
|
e.export()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! extern_fn {
|
macro_rules! extern_fn {
|
||||||
// raw doesn't wrap in ffi_body
|
|
||||||
($(#[$($attrss:tt)*])* raw fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) $body:block) => {
|
|
||||||
::paste::paste! {
|
|
||||||
/// Function pointer definition for
|
|
||||||
#[doc = ::std::stringify!($func_name)]
|
|
||||||
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)* ) -> $crate::ctypes::libra_error_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
$(#[$($attrss)*])*
|
|
||||||
pub unsafe extern "C" fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
|
|
||||||
$body
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ffi_body but panic-safe
|
|
||||||
($(#[$($attrss:tt)*])* fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) $body:block) => {
|
($(#[$($attrss:tt)*])* fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) $body:block) => {
|
||||||
::paste::paste! {
|
::paste::paste! {
|
||||||
/// Function pointer definition for
|
/// Function pointer definition for
|
||||||
|
@ -130,6 +72,20 @@ macro_rules! extern_fn {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
($(#[$($attrss:tt)*])* raw fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) $body:block) => {
|
||||||
|
::paste::paste! {
|
||||||
|
/// Function pointer definition for
|
||||||
|
#[doc = ::std::stringify!($func_name)]
|
||||||
|
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)* ) -> $crate::ctypes::libra_error_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
$(#[$($attrss)*])*
|
||||||
|
pub unsafe extern "C" fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
|
||||||
|
$body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
($(#[$($attrss:tt)*])* fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) |$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
|
($(#[$($attrss:tt)*])* fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) |$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
|
||||||
::paste::paste! {
|
::paste::paste! {
|
||||||
/// Function pointer definition for
|
/// Function pointer definition for
|
||||||
|
@ -170,83 +126,7 @@ macro_rules! extern_fn {
|
||||||
$crate::ffi::ffi_body!(|$($ref_capture),*| $body)
|
$crate::ffi::ffi_body!(|$($ref_capture),*| $body)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// nopanic variants that are UB if panics
|
|
||||||
($(#[$($attrss:tt)*])* nopanic fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) $body:block) => {
|
|
||||||
::paste::paste! {
|
|
||||||
/// Function pointer definition for
|
|
||||||
#[doc = ::std::stringify!($func_name)]
|
|
||||||
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
$(#[$($attrss)*])*
|
|
||||||
pub unsafe extern "C" fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
|
|
||||||
$crate::ffi::ffi_body!(nopanic $body)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($(#[$($attrss:tt)*])* nopanic fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) |$($ref_capture:ident),*|; mut |$($mut_capture:ident),*| $body:block) => {
|
|
||||||
::paste::paste! {
|
|
||||||
/// Function pointer definition for
|
|
||||||
#[doc = ::std::stringify!($func_name)]
|
|
||||||
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
$(#[$($attrss)*])*
|
|
||||||
pub unsafe extern "C" fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
|
|
||||||
$crate::ffi::ffi_body!(nopanic |$($ref_capture),*|; mut |$($mut_capture),*| $body)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($(#[$($attrss:tt)*])* nopanic fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) mut |$($mut_capture:ident),*| $body:block) => {
|
|
||||||
::paste::paste! {
|
|
||||||
/// Function pointer definition for
|
|
||||||
#[doc = ::std::stringify!($func_name)]
|
|
||||||
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
$(#[$($attrss)*])*
|
|
||||||
pub unsafe extern "C" fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
|
|
||||||
$crate::ffi::ffi_body!(nopanic mut |$($mut_capture),*| $body)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($(#[$($attrss:tt)*])* nopanic fn $func_name:ident ($($arg_name:ident : $arg_ty:ty),* $(,)?) |$($ref_capture:ident),*| $body:block) => {
|
|
||||||
::paste::paste! {
|
|
||||||
/// Function pointer definition for
|
|
||||||
#[doc = ::std::stringify!($func_name)]
|
|
||||||
pub type [<PFN_ $func_name>] = unsafe extern "C" fn($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
$(#[$($attrss)*])*
|
|
||||||
pub unsafe extern "C" fn $func_name($($arg_name: $arg_ty,)*) -> $crate::ctypes::libra_error_t {
|
|
||||||
$crate::ffi::ffi_body!(nopanic |$($ref_capture),*| $body)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr_is_aligned<T: Sized>(ptr: *const T) -> bool {
|
|
||||||
let align = std::mem::align_of::<T>();
|
|
||||||
if !align.is_power_of_two() {
|
|
||||||
panic!("is_aligned_to: align is not a power-of-two");
|
|
||||||
}
|
|
||||||
sptr::Strict::addr(ptr) & (align - 1) == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use extern_fn;
|
pub(crate) use extern_fn;
|
||||||
pub(crate) use ffi_body;
|
pub(crate) use ffi_body;
|
||||||
pub(crate) use wrap_ok;
|
|
||||||
|
|
||||||
use std::mem::ManuallyDrop;
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#![forbid(missing_docs)]
|
#![feature(doc_cfg)]
|
||||||
//! The C API for [librashader](https://docs.rs/librashader/).
|
//! The C API for [librashader](https://docs.rs/librashader/).
|
||||||
//!
|
//!
|
||||||
//! The librashader C API is designed to be loaded dynamically via `librashader_ld.h`, but static usage is also
|
//! The librashader C API is designed to be loaded dynamically via `librashader_ld.h`, but static usage is also
|
||||||
//! possible by linking against `librashader.h` as well as any static libraries used by `librashader`.
|
//! possible by linking against `librashader.h` as well as any static libraries used by `librashader`.
|
||||||
//!
|
//!
|
||||||
//! ## Usage
|
//! ## Usage
|
||||||
//! ⚠ Rust consumers should use [librashader](https://docs.rs/librashader/) directly instead. ⚠
|
//! ⚠ Rust consumers 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.
|
//! 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**.
|
//! Every allocated object can be freed with a corresponding `free` function **for that specific object type**.
|
||||||
|
@ -54,36 +54,32 @@
|
||||||
//!
|
//!
|
||||||
//! ## Thread safety
|
//! ## Thread safety
|
||||||
//!
|
//!
|
||||||
//! Except for the metal runtime, it is in general, **safe** to create a filter chain instance from a different thread,
|
//! In general, it is **safe** to create a filter chain instance from a different thread, but drawing filter passes must be
|
||||||
//! but drawing filter passes must be synchronized externally. The exception to filter chain creation are in OpenGL,
|
//! synchronized externally. The exception to filter chain creation are in OpenGL, where creating the filter chain instance
|
||||||
//! where creating the filter chain instance is safe **if and only if** the thread local OpenGL context is initialized
|
//! is safe **if and only if** the thread local OpenGL context is initialized to the same context as the drawing thread, and
|
||||||
//! to the same context as the drawing thread, and in Direct3D 11, where filter chain creation is unsafe
|
//! in Direct3D 11, where filter chain creation is unsafe if the `ID3D11Device` was created with
|
||||||
//! if the `ID3D11Device` was created with `D3D11_CREATE_DEVICE_SINGLETHREADED`. Metal is entirely thread unsafe.
|
//! `D3D11_CREATE_DEVICE_SINGLETHREADED`.
|
||||||
//!
|
|
||||||
//! Setting and retrieving filter parameters from any thread, regardless of the lack of other thread safety-guarantees
|
|
||||||
//! of the runtime, is always thread safe.
|
|
||||||
//!
|
//!
|
||||||
//! You must ensure that only thread has access to a created filter pass **before** you call `*_frame`. `*_frame` may only be
|
//! 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.
|
//! called from one thread at a time.
|
||||||
#![cfg_attr(feature = "docsrs", feature(doc_cfg))]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![deny(deprecated)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
#![allow(non_camel_case_types)]
|
||||||
|
#![feature(try_blocks)]
|
||||||
|
#![feature(pointer_is_aligned)]
|
||||||
|
#![feature(vec_into_raw_parts)]
|
||||||
|
#![deny(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
pub mod ctypes;
|
pub mod ctypes;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod ffi;
|
mod ffi;
|
||||||
pub mod presets;
|
pub mod presets;
|
||||||
|
|
||||||
#[cfg(feature = "reflect-unstable")]
|
#[cfg(feature = "reflect")]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod reflect;
|
pub mod reflect;
|
||||||
|
|
||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
pub mod wildcard;
|
|
||||||
|
|
||||||
pub use version::LIBRASHADER_ABI_VERSION;
|
pub use version::LIBRASHADER_ABI_VERSION;
|
||||||
pub use version::LIBRASHADER_API_VERSION;
|
pub use version::LIBRASHADER_API_VERSION;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! librashader preset C API (`libra_preset_*`).
|
//! librashader preset C API (`libra_preset_*`).
|
||||||
use crate::ctypes::{libra_preset_ctx_t, libra_shader_preset_t};
|
use crate::ctypes::libra_shader_preset_t;
|
||||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||||
use crate::ffi::extern_fn;
|
use crate::ffi::extern_fn;
|
||||||
use librashader::presets::ShaderPreset;
|
use librashader::presets::ShaderPreset;
|
||||||
|
@ -14,10 +14,11 @@ const _: () = crate::assert_thread_safe::<ShaderPreset>();
|
||||||
pub struct libra_preset_param_list_t {
|
pub struct libra_preset_param_list_t {
|
||||||
/// A pointer to the parameter
|
/// A pointer to the parameter
|
||||||
pub parameters: *const libra_preset_param_t,
|
pub parameters: *const libra_preset_param_t,
|
||||||
/// The number of parameters in the list. This field
|
/// The number of parameters in the list.
|
||||||
/// is readonly, and changing it will lead to undefined
|
|
||||||
/// behaviour on free.
|
|
||||||
pub length: u64,
|
pub length: u64,
|
||||||
|
/// For internal use only.
|
||||||
|
/// Changing this causes immediate undefined behaviour on freeing this parameter list.
|
||||||
|
pub _internal_alloc: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A preset parameter.
|
/// A preset parameter.
|
||||||
|
@ -54,6 +55,7 @@ extern_fn! {
|
||||||
|
|
||||||
let filename = unsafe { CStr::from_ptr(filename) };
|
let filename = unsafe { CStr::from_ptr(filename) };
|
||||||
let filename = filename.to_str()?;
|
let filename = filename.to_str()?;
|
||||||
|
println!("loading {filename}");
|
||||||
|
|
||||||
let preset = ShaderPreset::try_parse(filename)?;
|
let preset = ShaderPreset::try_parse(filename)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -64,49 +66,6 @@ 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! {
|
extern_fn! {
|
||||||
/// Free the preset.
|
/// Free the preset.
|
||||||
///
|
///
|
||||||
|
@ -114,7 +73,7 @@ extern_fn! {
|
||||||
/// null.
|
/// null.
|
||||||
///
|
///
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `preset` must be a valid and aligned pointer to a `libra_shader_preset_t`.
|
/// - `preset` must be a valid and aligned pointer to a shader preset.
|
||||||
fn libra_preset_free(preset: *mut libra_shader_preset_t) {
|
fn libra_preset_free(preset: *mut libra_shader_preset_t) {
|
||||||
assert_non_null!(preset);
|
assert_non_null!(preset);
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -129,7 +88,7 @@ extern_fn! {
|
||||||
/// Set the value of the parameter in the preset.
|
/// Set the value of the parameter in the preset.
|
||||||
///
|
///
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
|
/// - `preset` must be null or a valid and aligned pointer to a shader preset.
|
||||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||||
fn libra_preset_set_param(
|
fn libra_preset_set_param(
|
||||||
preset: *mut libra_shader_preset_t,
|
preset: *mut libra_shader_preset_t,
|
||||||
|
@ -157,7 +116,7 @@ extern_fn! {
|
||||||
/// - `name` must be null or a valid and aligned pointer to a string.
|
/// - `name` must be null or a valid and aligned pointer to a string.
|
||||||
/// - `value` may be a pointer to a uninitialized `float`.
|
/// - `value` may be a pointer to a uninitialized `float`.
|
||||||
fn libra_preset_get_param(
|
fn libra_preset_get_param(
|
||||||
preset: *const libra_shader_preset_t,
|
preset: *mut libra_shader_preset_t,
|
||||||
name: *const c_char,
|
name: *const c_char,
|
||||||
value: *mut MaybeUninit<f32>
|
value: *mut MaybeUninit<f32>
|
||||||
) |name, preset| {
|
) |name, preset| {
|
||||||
|
@ -176,7 +135,7 @@ extern_fn! {
|
||||||
/// Pretty print the shader preset.
|
/// Pretty print the shader preset.
|
||||||
///
|
///
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
|
/// - `preset` must be null or a valid and aligned pointer to a shader preset.
|
||||||
fn libra_preset_print(preset: *mut libra_shader_preset_t) |preset| {
|
fn libra_preset_print(preset: *mut libra_shader_preset_t) |preset| {
|
||||||
assert_some_ptr!(preset);
|
assert_some_ptr!(preset);
|
||||||
println!("{preset:#?}");
|
println!("{preset:#?}");
|
||||||
|
@ -187,7 +146,7 @@ extern_fn! {
|
||||||
/// Get a list of runtime parameters.
|
/// Get a list of runtime parameters.
|
||||||
///
|
///
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `preset` must be null or a valid and aligned pointer to a `libra_shader_preset_t`.
|
/// - `preset` must be null or a valid and aligned pointer to a shader preset.
|
||||||
/// - `out` must be an aligned pointer to a `libra_preset_parameter_list_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
|
/// - 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
|
/// in the returned struct may at best cause memory leaks, and at worse
|
||||||
|
@ -195,7 +154,7 @@ extern_fn! {
|
||||||
/// - It is safe to call `libra_preset_get_runtime_params` multiple times, however
|
/// - It is safe to call `libra_preset_get_runtime_params` multiple times, however
|
||||||
/// the output struct must only be freed once per call.
|
/// the output struct must only be freed once per call.
|
||||||
fn libra_preset_get_runtime_params(
|
fn libra_preset_get_runtime_params(
|
||||||
preset: *const libra_shader_preset_t,
|
preset: *mut libra_shader_preset_t,
|
||||||
out: *mut MaybeUninit<libra_preset_param_list_t>
|
out: *mut MaybeUninit<libra_preset_param_list_t>
|
||||||
) |preset| {
|
) |preset| {
|
||||||
assert_some_ptr!(preset);
|
assert_some_ptr!(preset);
|
||||||
|
@ -204,7 +163,7 @@ extern_fn! {
|
||||||
let iter = librashader::presets::get_parameter_meta(preset)?;
|
let iter = librashader::presets::get_parameter_meta(preset)?;
|
||||||
let mut values = Vec::new();
|
let mut values = Vec::new();
|
||||||
for param in iter {
|
for param in iter {
|
||||||
let name = CString::new(param.id.to_string())
|
let name = CString::new(param.id)
|
||||||
.map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
|
.map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
|
||||||
let description = CString::new(param.description)
|
let description = CString::new(param.description)
|
||||||
.map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
|
.map_err(|err| LibrashaderError::UnknownError(Box::new(err)))?;
|
||||||
|
@ -217,14 +176,12 @@ extern_fn! {
|
||||||
step: param.step
|
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 {
|
unsafe {
|
||||||
out.write(MaybeUninit::new(libra_preset_param_list_t {
|
out.write(MaybeUninit::new(libra_preset_param_list_t {
|
||||||
parameters: parts,
|
parameters: parts,
|
||||||
length: len as u64,
|
length: len as u64,
|
||||||
|
_internal_alloc: cap as u64,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,9 +209,9 @@ extern_fn! {
|
||||||
/// in undefined behaviour.
|
/// in undefined behaviour.
|
||||||
fn libra_preset_free_runtime_params(preset: libra_preset_param_list_t) {
|
fn libra_preset_free_runtime_params(preset: libra_preset_param_list_t) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let values =
|
let values = Vec::from_raw_parts(preset.parameters.cast_mut(),
|
||||||
crate::ffi::boxed_slice_from_raw_parts(preset.parameters.cast_mut(),
|
preset.length as usize,
|
||||||
preset.length as usize).into_vec();
|
preset._internal_alloc as usize);
|
||||||
|
|
||||||
for value in values {
|
for value in values {
|
||||||
let name = CString::from_raw(value.name.cast_mut());
|
let name = CString::from_raw(value.name.cast_mut());
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::error;
|
use crate::error;
|
||||||
|
|
||||||
use librashader::presets::{PassConfig, ShaderPreset};
|
use librashader::presets::{ShaderPassConfig, ShaderPreset};
|
||||||
use librashader::reflect::semantics::ShaderSemantics;
|
use librashader::reflect::semantics::ShaderSemantics;
|
||||||
use librashader::reflect::targets::SPIRV;
|
use librashader::reflect::targets::SPIRV;
|
||||||
use librashader::reflect::{CompileShader, ReflectShader, ShaderCompilerOutput, ShaderReflection};
|
use librashader::reflect::{CompileShader, ReflectShader, ShaderCompilerOutput, ShaderReflection};
|
||||||
use librashader::{FilterMode, WrapMode};
|
use librashader::{FilterMode, WrapMode};
|
||||||
|
|
||||||
|
use librashader::reflect::cross::GlslangCompilation;
|
||||||
use librashader::reflect::helper::image::{Image, UVDirection, RGBA8};
|
use librashader::reflect::helper::image::{Image, UVDirection, RGBA8};
|
||||||
use librashader::reflect::SpirvCompilation;
|
|
||||||
|
|
||||||
pub(crate) struct LookupTexture {
|
pub(crate) struct LookupTexture {
|
||||||
wrap_mode: WrapMode,
|
wrap_mode: WrapMode,
|
||||||
|
@ -21,7 +21,7 @@ pub(crate) struct LookupTexture {
|
||||||
|
|
||||||
pub(crate) struct PassReflection {
|
pub(crate) struct PassReflection {
|
||||||
reflection: ShaderReflection,
|
reflection: ShaderReflection,
|
||||||
config: PassConfig,
|
config: ShaderPassConfig,
|
||||||
spirv: ShaderCompilerOutput<Vec<u32>>,
|
spirv: ShaderCompilerOutput<Vec<u32>>,
|
||||||
}
|
}
|
||||||
pub(crate) struct FilterReflection {
|
pub(crate) struct FilterReflection {
|
||||||
|
@ -35,12 +35,11 @@ impl FilterReflection {
|
||||||
preset: ShaderPreset,
|
preset: ShaderPreset,
|
||||||
direction: UVDirection,
|
direction: UVDirection,
|
||||||
) -> Result<FilterReflection, error::LibrashaderError> {
|
) -> Result<FilterReflection, error::LibrashaderError> {
|
||||||
let (passes, textures) = (preset.passes, preset.textures);
|
let (passes, textures) = (preset.shaders, preset.textures);
|
||||||
|
|
||||||
let (passes, semantics) = librashader::reflect::helper::compile_preset_passes::<
|
let (passes, semantics) = librashader::reflect::helper::compile_preset_passes::<
|
||||||
Glslang,
|
|
||||||
SPIRV,
|
SPIRV,
|
||||||
SpirvCompilation,
|
GlslangCompilation,
|
||||||
error::LibrashaderError,
|
error::LibrashaderError,
|
||||||
>(passes, &textures)?;
|
>(passes, &textures)?;
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,46 @@ use crate::ctypes::{
|
||||||
};
|
};
|
||||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||||
use crate::ffi::extern_fn;
|
use crate::ffi::extern_fn;
|
||||||
use librashader::runtime::d3d11::{FilterChain, FilterChainOptions, FrameOptions};
|
use librashader::runtime::d3d11::{D3D11InputView, D3D11OutputView};
|
||||||
use std::ffi::c_char;
|
use std::ffi::c_char;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::mem::{ManuallyDrop, MaybeUninit};
|
use std::mem::{ManuallyDrop, MaybeUninit};
|
||||||
use std::ops::Deref;
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use windows::Win32::Graphics::Direct3D11::{
|
use windows::Win32::Graphics::Direct3D11::{
|
||||||
ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, ID3D11ShaderResourceView,
|
ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, ID3D11ShaderResourceView,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use librashader::runtime::d3d11::capi::options::FilterChainOptionsD3D11;
|
||||||
|
use librashader::runtime::d3d11::capi::options::FrameOptionsD3D11;
|
||||||
|
|
||||||
use crate::LIBRASHADER_API_VERSION;
|
use crate::LIBRASHADER_API_VERSION;
|
||||||
use librashader::runtime::d3d11::error::FilterChainError;
|
|
||||||
use librashader::runtime::{FilterChainParameters, Size, Viewport};
|
use librashader::runtime::{FilterChainParameters, Size, Viewport};
|
||||||
|
|
||||||
|
/// Direct3D 11 parameters for the source image.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct libra_source_image_d3d11_t {
|
||||||
|
/// A shader resource view into the source image
|
||||||
|
pub handle: ManuallyDrop<ID3D11ShaderResourceView>,
|
||||||
|
/// The width of the source image.
|
||||||
|
pub width: u32,
|
||||||
|
/// The height of the source image.
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<libra_source_image_d3d11_t> for D3D11InputView {
|
||||||
|
type Error = LibrashaderError;
|
||||||
|
|
||||||
|
fn try_from(value: libra_source_image_d3d11_t) -> Result<Self, Self::Error> {
|
||||||
|
let handle = value.handle.clone();
|
||||||
|
|
||||||
|
Ok(D3D11InputView {
|
||||||
|
handle: ManuallyDrop::into_inner(handle),
|
||||||
|
size: Size::new(value.width, value.height),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Options for Direct3D 11 filter chain creation.
|
/// Options for Direct3D 11 filter chain creation.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
|
@ -33,7 +58,7 @@ pub struct filter_chain_d3d11_opt_t {
|
||||||
}
|
}
|
||||||
|
|
||||||
config_struct! {
|
config_struct! {
|
||||||
impl FilterChainOptions => filter_chain_d3d11_opt_t {
|
impl FilterChainOptionsD3D11 => filter_chain_d3d11_opt_t {
|
||||||
0 => [force_no_mipmaps, disable_cache];
|
0 => [force_no_mipmaps, disable_cache];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,18 +74,11 @@ pub struct frame_d3d11_opt_t {
|
||||||
/// The direction of rendering.
|
/// The direction of rendering.
|
||||||
/// -1 indicates that the frames are played in reverse order.
|
/// -1 indicates that the frames are played in reverse order.
|
||||||
pub frame_direction: i32,
|
pub frame_direction: i32,
|
||||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 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! {
|
config_struct! {
|
||||||
impl FrameOptions => frame_d3d11_opt_t {
|
impl FrameOptionsD3D11 => frame_d3d11_opt_t {
|
||||||
0 => [clear_history, frame_direction];
|
0 => [clear_history, frame_direction];
|
||||||
1 => [rotation, total_subframes, current_subframe]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +114,7 @@ extern_fn! {
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
unsafe {
|
unsafe {
|
||||||
let chain = FilterChain::load_from_preset(
|
let chain = librashader::runtime::d3d11::capi::FilterChainD3D11::load_from_preset(
|
||||||
*preset,
|
*preset,
|
||||||
&device,
|
&device,
|
||||||
options.as_ref(),
|
options.as_ref(),
|
||||||
|
@ -156,7 +174,7 @@ extern_fn! {
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
unsafe {
|
unsafe {
|
||||||
let chain = FilterChain::load_from_preset_deferred(
|
let chain = librashader::runtime::d3d11::capi::FilterChainD3D11::load_from_preset_deferred(
|
||||||
*preset,
|
*preset,
|
||||||
&device,
|
&device,
|
||||||
&device_context,
|
&device_context,
|
||||||
|
@ -179,33 +197,17 @@ const _: () = assert!(
|
||||||
extern_fn! {
|
extern_fn! {
|
||||||
/// Draw a frame with the given parameters for the given filter chain.
|
/// Draw a frame with the given parameters for the given filter chain.
|
||||||
///
|
///
|
||||||
/// ## Parameters
|
/// If `device_context` is null, then commands are recorded onto the immediate context. Otherwise,
|
||||||
///
|
/// it will record commands onto the provided context. If the context is deferred, librashader
|
||||||
/// - `chain` is a handle to the filter chain.
|
/// will not finalize command lists. The context must otherwise be associated with the `ID3D11Device`
|
||||||
/// - `device_context` is the ID3D11DeviceContext used to record draw commands to.
|
// the filter chain was created with.
|
||||||
/// If `device_context` is null, then commands are recorded onto the immediate context. Otherwise,
|
|
||||||
/// it will record commands onto the provided context. If the context is deferred, librashader
|
|
||||||
/// will not finalize command lists. The context must otherwise be associated with the `ID3D11Device`
|
|
||||||
/// the filter chain was created with.
|
|
||||||
///
|
|
||||||
/// - `frame_count` is the number of frames passed to the shader
|
|
||||||
/// - `image` is a pointer to a `ID3D11ShaderResourceView` that will serve as the source image for the frame.
|
|
||||||
/// - `out` is a pointer to a `ID3D11RenderTargetView` that will serve as the render target for the frame.
|
|
||||||
///
|
|
||||||
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
|
|
||||||
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
|
|
||||||
/// entire render target will be used.
|
|
||||||
/// - `mvp` is a pointer to an array of 16 `float` values to specify the model view projection matrix to
|
|
||||||
/// be passed to the shader.
|
|
||||||
/// - `options` is a pointer to options for the frame. Valid options are dependent on the `LIBRASHADER_API_VERSION`
|
|
||||||
/// passed in. It may be null, in which case default options for the filter chain are used.
|
|
||||||
///
|
///
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
||||||
/// function will return an error.
|
/// function will return an error.
|
||||||
/// - `mvp` may be null, or if it is not null, must be an aligned pointer to 16 consecutive `float`
|
/// - `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.
|
/// 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_d3d11_opt_t`
|
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t`
|
||||||
/// struct.
|
/// struct.
|
||||||
/// - `out` must not be null.
|
/// - `out` must not be null.
|
||||||
/// - `image.handle` must not be null.
|
/// - `image.handle` must not be null.
|
||||||
|
@ -214,15 +216,15 @@ extern_fn! {
|
||||||
/// the filter chain was created with.
|
/// the filter chain was created with.
|
||||||
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
|
/// - 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.
|
/// thread at a time may call this function.
|
||||||
nopanic fn libra_d3d11_filter_chain_frame(
|
fn libra_d3d11_filter_chain_frame(
|
||||||
chain: *mut libra_d3d11_filter_chain_t,
|
chain: *mut libra_d3d11_filter_chain_t,
|
||||||
// cbindgen can't discover that ID3D11DeviceContext has the niche optimization
|
// cbindgen can't discover that ID3D11DeviceContext has the niche optimization
|
||||||
// so ManuallyDrop<Option<ID3D11DeviceContext>> doesn't generate correct bindings.
|
// so ManuallyDrop<Option<ID3D11DeviceContext>> doesn't generate correct bindings.
|
||||||
device_context: Option<ManuallyDrop<ID3D11DeviceContext>>,
|
device_context: Option<ManuallyDrop<ID3D11DeviceContext>>,
|
||||||
frame_count: usize,
|
frame_count: usize,
|
||||||
image: ManuallyDrop<ID3D11ShaderResourceView>,
|
image: libra_source_image_d3d11_t,
|
||||||
|
viewport: libra_viewport_t,
|
||||||
out: ManuallyDrop<ID3D11RenderTargetView>,
|
out: ManuallyDrop<ID3D11RenderTargetView>,
|
||||||
viewport: *const libra_viewport_t,
|
|
||||||
mvp: *const f32,
|
mvp: *const f32,
|
||||||
options: *const MaybeUninit<frame_d3d11_opt_t>
|
options: *const MaybeUninit<frame_d3d11_opt_t>
|
||||||
) mut |chain| {
|
) mut |chain| {
|
||||||
|
@ -240,27 +242,22 @@ extern_fn! {
|
||||||
Some(unsafe { options.read() })
|
Some(unsafe { options.read() })
|
||||||
};
|
};
|
||||||
|
|
||||||
let viewport = if viewport.is_null() {
|
let viewport = Viewport {
|
||||||
Viewport::new_render_target_sized_origin(out.deref(), mvp)
|
x: viewport.x,
|
||||||
.map_err(|e| LibrashaderError::D3D11FilterError(FilterChainError::Direct3DError(e)))?
|
y: viewport.y,
|
||||||
} else {
|
output: D3D11OutputView {
|
||||||
let viewport = unsafe { viewport.read() };
|
size: Size::new(viewport.width, viewport.height),
|
||||||
Viewport {
|
handle: ManuallyDrop::into_inner(out.clone()),
|
||||||
x: viewport.x,
|
},
|
||||||
y: viewport.y,
|
mvp,
|
||||||
output: out.deref(),
|
|
||||||
size: Size {
|
|
||||||
height: viewport.height,
|
|
||||||
width: viewport.width
|
|
||||||
},
|
|
||||||
mvp,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
|
|
||||||
|
let image = image.try_into()?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
chain.frame(device_context.as_deref(), image.deref(), &viewport, frame_count, options.as_ref())?;
|
chain.frame(device_context.as_deref(), image, &viewport, frame_count, options.as_ref())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,15 +273,15 @@ extern_fn! {
|
||||||
chain: *mut libra_d3d11_filter_chain_t,
|
chain: *mut libra_d3d11_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
value: f32
|
value: f32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
if chain.parameters().set_parameter_value(name, value).is_none() {
|
if chain.set_parameter(name, value).is_none() {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,18 +295,18 @@ extern_fn! {
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d11_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d11_filter_chain_t`.
|
||||||
/// - `param_name` must be either null or a null terminated string.
|
/// - `param_name` must be either null or a null terminated string.
|
||||||
fn libra_d3d11_filter_chain_get_param(
|
fn libra_d3d11_filter_chain_get_param(
|
||||||
chain: *const libra_d3d11_filter_chain_t,
|
chain: *mut libra_d3d11_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
out: *mut MaybeUninit<f32>
|
out: *mut MaybeUninit<f32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
let Some(value) = chain.parameters().parameter_value(name) else {
|
let Some(value) = chain.get_parameter(name) else {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
};
|
};
|
||||||
|
|
||||||
out.write(MaybeUninit::new(value));
|
out.write(MaybeUninit::new(value));
|
||||||
|
@ -325,9 +322,9 @@ extern_fn! {
|
||||||
fn libra_d3d11_filter_chain_set_active_pass_count(
|
fn libra_d3d11_filter_chain_set_active_pass_count(
|
||||||
chain: *mut libra_d3d11_filter_chain_t,
|
chain: *mut libra_d3d11_filter_chain_t,
|
||||||
value: u32
|
value: u32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
chain.parameters().set_passes_enabled(value as usize);
|
chain.set_enabled_pass_count(value as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,12 +334,12 @@ extern_fn! {
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d11_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d11_filter_chain_t`.
|
||||||
fn libra_d3d11_filter_chain_get_active_pass_count(
|
fn libra_d3d11_filter_chain_get_active_pass_count(
|
||||||
chain: *const libra_d3d11_filter_chain_t,
|
chain: *mut libra_d3d11_filter_chain_t,
|
||||||
out: *mut MaybeUninit<u32>
|
out: *mut MaybeUninit<u32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
unsafe {
|
unsafe {
|
||||||
let value = chain.parameters().passes_enabled();
|
let value = chain.get_enabled_pass_count();
|
||||||
out.write(MaybeUninit::new(value as u32))
|
out.write(MaybeUninit::new(value as u32))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,53 +13,26 @@ use windows::Win32::Graphics::Direct3D12::{
|
||||||
};
|
};
|
||||||
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT;
|
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 crate::LIBRASHADER_API_VERSION;
|
||||||
use librashader::runtime::d3d12::{
|
use librashader::runtime::d3d12::{D3D12InputImage, D3D12OutputView};
|
||||||
D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, FrameOptions,
|
|
||||||
};
|
|
||||||
use librashader::runtime::{FilterChainParameters, Size, Viewport};
|
use librashader::runtime::{FilterChainParameters, Size, Viewport};
|
||||||
|
|
||||||
/// Tagged union for a Direct3D 12 image
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct libra_image_d3d12_t {
|
|
||||||
/// The type of the image.
|
|
||||||
pub image_type: LIBRA_D3D12_IMAGE_TYPE,
|
|
||||||
/// The handle to the image.
|
|
||||||
pub handle: libra_image_d3d12_handle_t,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handle to a Direct3D 12 image.
|
|
||||||
///
|
|
||||||
/// This must be either a pointer to a `ID3D12Resource`,
|
|
||||||
/// or a valid source or output image type.
|
|
||||||
#[repr(C)]
|
|
||||||
pub union libra_image_d3d12_handle_t {
|
|
||||||
/// A pointer to an `ID3D12Resource`, with descriptors managed by the filter chain.
|
|
||||||
pub resource: ManuallyDrop<ID3D12Resource>,
|
|
||||||
/// A source image with externally managed descriptors.
|
|
||||||
pub source: ManuallyDrop<libra_source_image_d3d12_t>,
|
|
||||||
/// An output image with externally managed descriptors.
|
|
||||||
pub output: ManuallyDrop<libra_output_image_d3d12_t>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of image passed to the image.
|
|
||||||
#[repr(i32)]
|
|
||||||
pub enum LIBRA_D3D12_IMAGE_TYPE {
|
|
||||||
/// The image handle is a pointer to a `ID3D12Resource`.
|
|
||||||
RESOURCE = 0,
|
|
||||||
/// The image handle is a `libra_source_image_d3d12_t`
|
|
||||||
SOURCE_IMAGE = 1,
|
|
||||||
/// The image handle is a `libra_output_image_d3d12_t`
|
|
||||||
OUTPUT_IMAGE = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Direct3D 12 parameters for the source image.
|
/// Direct3D 12 parameters for the source image.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct libra_source_image_d3d12_t {
|
pub struct libra_source_image_d3d12_t {
|
||||||
/// A CPU descriptor handle to a shader resource view of the image.
|
|
||||||
pub descriptor: D3D12_CPU_DESCRIPTOR_HANDLE,
|
|
||||||
/// The resource containing the image.
|
/// The resource containing the image.
|
||||||
pub resource: ManuallyDrop<ID3D12Resource>,
|
pub resource: ManuallyDrop<ID3D12Resource>,
|
||||||
|
/// A CPU descriptor handle to a shader resource view of the image.
|
||||||
|
pub descriptor: D3D12_CPU_DESCRIPTOR_HANDLE,
|
||||||
|
/// The format of the image.
|
||||||
|
pub format: DXGI_FORMAT,
|
||||||
|
/// The width of the source image.
|
||||||
|
pub width: u32,
|
||||||
|
/// The height of the source image.
|
||||||
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct3D 12 parameters for the output image.
|
/// Direct3D 12 parameters for the output image.
|
||||||
|
@ -69,10 +42,6 @@ pub struct libra_output_image_d3d12_t {
|
||||||
pub descriptor: D3D12_CPU_DESCRIPTOR_HANDLE,
|
pub descriptor: D3D12_CPU_DESCRIPTOR_HANDLE,
|
||||||
/// The format of the image.
|
/// The format of the image.
|
||||||
pub format: DXGI_FORMAT,
|
pub format: DXGI_FORMAT,
|
||||||
/// The width of the output image.
|
|
||||||
pub width: u32,
|
|
||||||
/// The height of the output image.
|
|
||||||
pub height: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for each Direct3D 12 shader frame.
|
/// Options for each Direct3D 12 shader frame.
|
||||||
|
@ -86,18 +55,11 @@ pub struct frame_d3d12_opt_t {
|
||||||
/// The direction of rendering.
|
/// The direction of rendering.
|
||||||
/// -1 indicates that the frames are played in reverse order.
|
/// -1 indicates that the frames are played in reverse order.
|
||||||
pub frame_direction: i32,
|
pub frame_direction: i32,
|
||||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 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! {
|
config_struct! {
|
||||||
impl FrameOptions => frame_d3d12_opt_t {
|
impl FrameOptionsD3D12 => frame_d3d12_opt_t {
|
||||||
0 => [clear_history, frame_direction];
|
0 => [clear_history, frame_direction];
|
||||||
1 => [rotation, total_subframes, current_subframe]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +84,26 @@ pub struct filter_chain_d3d12_opt_t {
|
||||||
}
|
}
|
||||||
|
|
||||||
config_struct! {
|
config_struct! {
|
||||||
impl FilterChainOptions => filter_chain_d3d12_opt_t {
|
impl FilterChainOptionsD3D12 => filter_chain_d3d12_opt_t {
|
||||||
0 => [force_hlsl_pipeline, force_no_mipmaps, disable_cache];
|
0 => [force_hlsl_pipeline, force_no_mipmaps, disable_cache];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<libra_source_image_d3d12_t> for D3D12InputImage {
|
||||||
|
type Error = LibrashaderError;
|
||||||
|
|
||||||
|
fn try_from(value: libra_source_image_d3d12_t) -> Result<Self, Self::Error> {
|
||||||
|
let resource = value.resource.clone();
|
||||||
|
|
||||||
|
Ok(D3D12InputImage {
|
||||||
|
resource: ManuallyDrop::into_inner(resource),
|
||||||
|
descriptor: value.descriptor,
|
||||||
|
size: Size::new(value.width, value.height),
|
||||||
|
format: value.format,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern_fn! {
|
extern_fn! {
|
||||||
/// Create the filter chain given the shader preset.
|
/// Create the filter chain given the shader preset.
|
||||||
///
|
///
|
||||||
|
@ -159,7 +136,7 @@ extern_fn! {
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
unsafe {
|
unsafe {
|
||||||
let chain = FilterChain::load_from_preset(
|
let chain = librashader::runtime::d3d12::capi::FilterChainD3D12::load_from_preset(
|
||||||
*preset,
|
*preset,
|
||||||
&device,
|
&device,
|
||||||
options.as_ref(),
|
options.as_ref(),
|
||||||
|
@ -211,7 +188,7 @@ extern_fn! {
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
unsafe {
|
unsafe {
|
||||||
let chain = FilterChain::load_from_preset_deferred(
|
let chain = librashader::runtime::d3d12::capi::FilterChainD3D12::load_from_preset_deferred(
|
||||||
*preset,
|
*preset,
|
||||||
&device,
|
&device,
|
||||||
&command_list,
|
&command_list,
|
||||||
|
@ -229,64 +206,33 @@ extern_fn! {
|
||||||
/// Records rendering commands for a frame with the given parameters for the given filter chain
|
/// Records rendering commands for a frame with the given parameters for the given filter chain
|
||||||
/// to the input command list.
|
/// to the input command list.
|
||||||
///
|
///
|
||||||
/// A resource barrier **will not** be created for the final pass. The output image will
|
/// * The input image must be in the `D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE` resource state.
|
||||||
|
/// * The output image must be in `D3D12_RESOURCE_STATE_RENDER_TARGET` resource state.
|
||||||
|
///
|
||||||
|
/// librashader **will not** create a resource barrier for the final pass. The output image will
|
||||||
/// remain in `D3D12_RESOURCE_STATE_RENDER_TARGET` after all shader passes. The caller must transition
|
/// remain in `D3D12_RESOURCE_STATE_RENDER_TARGET` after all shader passes. The caller must transition
|
||||||
/// the output image to the final resource state.
|
/// the output image to the final resource state.
|
||||||
///
|
///
|
||||||
/// The refcount of any COM pointers passed into this frame will not be changed. It is the responsibility
|
|
||||||
/// of the caller to ensure any resources remain alive until the `ID3D12GraphicsCommandList` provided is
|
|
||||||
/// submitted.
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
///
|
|
||||||
/// - `chain` is a handle to the filter chain.
|
|
||||||
/// - `command_list` is a `ID3D12GraphicsCommandList` to record draw commands to.
|
|
||||||
/// The provided command list must be open and associated with the `ID3D12Device` this filter chain was created with.
|
|
||||||
/// - `frame_count` is the number of frames passed to the shader
|
|
||||||
/// - `image` is a `libra_image_d3d12_t` with `image_type` set to `IMAGE_TYPE_SOURCE_IMAGE` or `IMAGE_TYPE_RESOURCE`,
|
|
||||||
/// with `image_handle` either a `ID3D12Resource*` or an `libra_source_image_d3d12_t` containing a CPU descriptor to a shader resource view,
|
|
||||||
/// and the image resource the view is of, which will serve as the source image for the frame.
|
|
||||||
/// The input image resource must be in the `D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE` resource state
|
|
||||||
/// or equivalent barrier layout. The image resource must have dimension `D3D12_RESOURCE_DIMENSION_TEXTURE2D`.
|
|
||||||
/// - `out` is a `libra_image_d3d12_t`, with `image_type` set to `IMAGE_TYPE_OUTPUT_IMAGE` or `IMAGE_TYPE_RESOURCE`,
|
|
||||||
/// with `image_handle` being either a `ID3D12Resource*` or an `libra_output_image_d3d12_t`, containing a CPU descriptor handle,
|
|
||||||
/// format, and size information for the render target of the frame. The output image must be in
|
|
||||||
/// `D3D12_RESOURCE_STATE_RENDER_TARGET` resource state or equivalent barrier layout.
|
|
||||||
/// The image resource must have dimension `D3D12_RESOURCE_DIMENSION_TEXTURE2D`.
|
|
||||||
///
|
|
||||||
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
|
|
||||||
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
|
|
||||||
/// entire render target will be used.
|
|
||||||
/// - `mvp` is a pointer to an array of 16 `float` values to specify the model view projection matrix to
|
|
||||||
/// be passed to the shader.
|
|
||||||
/// - `options` is a pointer to options for the frame. Valid options are dependent on the `LIBRASHADER_API_VERSION`
|
|
||||||
/// passed in. It may be null, in which case default options for the filter chain are used.
|
|
||||||
///
|
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
||||||
/// function will return an error.
|
/// function will return an error.
|
||||||
/// - `mvp` may be null, or if it is not null, must be an aligned pointer to 16 consecutive `float`
|
/// - `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.
|
/// 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_d3d12_opt_t`
|
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_gl_opt_t`
|
||||||
/// struct.
|
/// struct.
|
||||||
/// - Any resource pointers contained within a `libra_image_d3d12_t` must be non-null.
|
/// - `out` must be a descriptor handle to a render target view.
|
||||||
/// - The `handle` field of any `libra_image_d3d12_t` must be valid for it's `image_type`.
|
/// - `image.resource` must not be null.
|
||||||
/// - If `image_type` is `IMAGE_TYPE_RESOURCE`, then `handle` must be `ID3D12Resource *`.
|
|
||||||
/// - If `image_type` is `IMAGE_TYPE_SOURCE`, then `handle` must be `libra_source_image_d3d12_t`.
|
|
||||||
/// - If `image_type` is `IMAGE_TYPE_OUTPUT`, then `handle` must be `libra_output_image_d3d12_t`.
|
|
||||||
/// - `command_list` must be a non-null pointer to a `ID3D12GraphicsCommandList` that is open,
|
/// - `command_list` must be a non-null pointer to a `ID3D12GraphicsCommandList` that is open,
|
||||||
/// and must be associated with the `ID3D12Device` this filter chain was created with.
|
/// and must be associated with the `ID3D12Device` this filter chain was created with.
|
||||||
/// - All resource pointers contained within a `libra_image_d3d12_t` must remain valid until the `ID3D12GraphicsCommandList *`
|
|
||||||
/// provided is submitted after the call to this function.
|
|
||||||
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
|
/// - 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.
|
/// thread at a time may call this function.
|
||||||
nopanic fn libra_d3d12_filter_chain_frame(
|
fn libra_d3d12_filter_chain_frame(
|
||||||
chain: *mut libra_d3d12_filter_chain_t,
|
chain: *mut libra_d3d12_filter_chain_t,
|
||||||
command_list: ManuallyDrop<ID3D12GraphicsCommandList>,
|
command_list: ManuallyDrop<ID3D12GraphicsCommandList>,
|
||||||
frame_count: usize,
|
frame_count: usize,
|
||||||
image: libra_image_d3d12_t,
|
image: libra_source_image_d3d12_t,
|
||||||
out: libra_image_d3d12_t,
|
viewport: libra_viewport_t,
|
||||||
viewport: *const libra_viewport_t,
|
out: libra_output_image_d3d12_t,
|
||||||
mvp: *const f32,
|
mvp: *const f32,
|
||||||
options: *const MaybeUninit<frame_d3d12_opt_t>
|
options: *const MaybeUninit<frame_d3d12_opt_t>
|
||||||
) mut |chain| {
|
) mut |chain| {
|
||||||
|
@ -305,65 +251,14 @@ extern_fn! {
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
|
let viewport = Viewport {
|
||||||
let output = unsafe {
|
x: viewport.x,
|
||||||
match out.image_type {
|
y: viewport.y,
|
||||||
LIBRA_D3D12_IMAGE_TYPE::RESOURCE => {
|
output: unsafe { D3D12OutputView::new_from_raw(out.descriptor, Size::new(viewport.width, viewport.height), out.format) },
|
||||||
let out = out.handle.resource;
|
mvp,
|
||||||
D3D12OutputView::new_from_resource(
|
|
||||||
out,
|
|
||||||
chain,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
LIBRA_D3D12_IMAGE_TYPE::OUTPUT_IMAGE => {
|
|
||||||
let out = out.handle.output;
|
|
||||||
D3D12OutputView::new_from_raw(
|
|
||||||
out.descriptor,
|
|
||||||
Size::new(out.width, out.height),
|
|
||||||
out.format,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
LIBRA_D3D12_IMAGE_TYPE::SOURCE_IMAGE => {
|
|
||||||
return Err(LibrashaderError::InvalidParameter("out"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let viewport = if viewport.is_null() {
|
|
||||||
Viewport::new_render_target_sized_origin(output, mvp)?
|
|
||||||
} else {
|
|
||||||
let viewport = unsafe { viewport.read() };
|
|
||||||
Viewport {
|
|
||||||
x: viewport.x,
|
|
||||||
y: viewport.y,
|
|
||||||
output,
|
|
||||||
size: Size {
|
|
||||||
height: viewport.height,
|
|
||||||
width: viewport.width
|
|
||||||
},
|
|
||||||
mvp,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let image = unsafe {
|
|
||||||
match image.image_type {
|
|
||||||
LIBRA_D3D12_IMAGE_TYPE::RESOURCE => {
|
|
||||||
let image = image.handle.resource;
|
|
||||||
D3D12InputImage::Managed(image)
|
|
||||||
}
|
|
||||||
LIBRA_D3D12_IMAGE_TYPE::SOURCE_IMAGE => {
|
|
||||||
let image = ManuallyDrop::into_inner(image.handle.source);
|
|
||||||
D3D12InputImage::External {
|
|
||||||
resource: image.resource,
|
|
||||||
descriptor: image.descriptor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LIBRA_D3D12_IMAGE_TYPE::OUTPUT_IMAGE => {
|
|
||||||
return Err(LibrashaderError::InvalidParameter("image"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let image = image.try_into()?;
|
||||||
unsafe {
|
unsafe {
|
||||||
chain.frame(&command_list, image, &viewport, frame_count, options.as_ref())?;
|
chain.frame(&command_list, image, &viewport, frame_count, options.as_ref())?;
|
||||||
}
|
}
|
||||||
|
@ -381,15 +276,15 @@ extern_fn! {
|
||||||
chain: *mut libra_d3d12_filter_chain_t,
|
chain: *mut libra_d3d12_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
value: f32
|
value: f32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
if chain.parameters().set_parameter_value(name, value).is_none() {
|
if chain.set_parameter(name, value).is_none() {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,18 +298,18 @@ extern_fn! {
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d12_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d12_filter_chain_t`.
|
||||||
/// - `param_name` must be either null or a null terminated string.
|
/// - `param_name` must be either null or a null terminated string.
|
||||||
fn libra_d3d12_filter_chain_get_param(
|
fn libra_d3d12_filter_chain_get_param(
|
||||||
chain: *const libra_d3d12_filter_chain_t,
|
chain: *mut libra_d3d12_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
out: *mut MaybeUninit<f32>
|
out: *mut MaybeUninit<f32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
let Some(value) = chain.parameters().parameter_value(name) else {
|
let Some(value) = chain.get_parameter(name) else {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
};
|
};
|
||||||
|
|
||||||
out.write(MaybeUninit::new(value));
|
out.write(MaybeUninit::new(value));
|
||||||
|
@ -430,9 +325,9 @@ extern_fn! {
|
||||||
fn libra_d3d12_filter_chain_set_active_pass_count(
|
fn libra_d3d12_filter_chain_set_active_pass_count(
|
||||||
chain: *mut libra_d3d12_filter_chain_t,
|
chain: *mut libra_d3d12_filter_chain_t,
|
||||||
value: u32
|
value: u32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
chain.parameters().set_passes_enabled(value as usize);
|
chain.set_enabled_pass_count(value as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,12 +337,12 @@ extern_fn! {
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d12_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d12_filter_chain_t`.
|
||||||
fn libra_d3d12_filter_chain_get_active_pass_count(
|
fn libra_d3d12_filter_chain_get_active_pass_count(
|
||||||
chain: *const libra_d3d12_filter_chain_t,
|
chain: *mut libra_d3d12_filter_chain_t,
|
||||||
out: *mut MaybeUninit<u32>
|
out: *mut MaybeUninit<u32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
unsafe {
|
unsafe {
|
||||||
let value = chain.parameters().passes_enabled();
|
let value = chain.get_enabled_pass_count();
|
||||||
out.write(MaybeUninit::new(value as u32))
|
out.write(MaybeUninit::new(value as u32))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,285 +0,0 @@
|
||||||
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::ops::Deref;
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
use std::slice;
|
|
||||||
use windows::Win32::Graphics::Direct3D9::{IDirect3DDevice9, IDirect3DSurface9, IDirect3DTexture9};
|
|
||||||
|
|
||||||
use crate::LIBRASHADER_API_VERSION;
|
|
||||||
use librashader::runtime::d3d9::error::FilterChainError;
|
|
||||||
use librashader::runtime::{FilterChainParameters, Size, 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, 3 = 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.
|
|
||||||
///
|
|
||||||
/// ## Parameters
|
|
||||||
/// - `chain` is a handle to the filter chain.
|
|
||||||
/// - `frame_count` is the number of frames passed to the shader
|
|
||||||
/// - `image` is a pointer to a `IDirect3DTexture9` that will serve as the source image for the frame.
|
|
||||||
/// - `out` is a pointer to a `IDirect3DSurface9` that will serve as the render target for the frame.
|
|
||||||
///
|
|
||||||
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
|
|
||||||
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
|
|
||||||
/// entire render target will be used.
|
|
||||||
/// - `mvp` is a pointer to an array of 16 `float` values to specify the model view projection matrix to
|
|
||||||
/// be passed to the shader.
|
|
||||||
/// - `options` is a pointer to options for the frame. Valid options are dependent on the `LIBRASHADER_API_VERSION`
|
|
||||||
/// passed in. It may be null, in which case default options for the filter chain are used.
|
|
||||||
///
|
|
||||||
/// ## Safety
|
|
||||||
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
|
||||||
/// function will return an error.
|
|
||||||
/// - `viewport` may be null, or if it is not null, must be an aligned pointer to an instance of `libra_viewport_t`.
|
|
||||||
/// - `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>,
|
|
||||||
out: ManuallyDrop<IDirect3DSurface9>,
|
|
||||||
viewport: *const libra_viewport_t,
|
|
||||||
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 = if viewport.is_null() {
|
|
||||||
Viewport::new_render_target_sized_origin(out.deref(), mvp)
|
|
||||||
.map_err(|e| LibrashaderError::D3D9FilterError(FilterChainError::Direct3DError(e)))?
|
|
||||||
} else {
|
|
||||||
let viewport = unsafe { viewport.read() };
|
|
||||||
Viewport {
|
|
||||||
x: viewport.x,
|
|
||||||
y: viewport.y,
|
|
||||||
output: out.deref(),
|
|
||||||
size: Size {
|
|
||||||
height: viewport.height,
|
|
||||||
width: viewport.width
|
|
||||||
},
|
|
||||||
mvp,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let options = options.map(FromUninit::from_uninit);
|
|
||||||
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
chain.frame(image.deref(), &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
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
assert_non_null!(param_name);
|
|
||||||
unsafe {
|
|
||||||
let name = CStr::from_ptr(param_name);
|
|
||||||
let name = name.to_str()?;
|
|
||||||
|
|
||||||
if chain.parameters().set_parameter_value(name, value).is_none() {
|
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: *const libra_d3d9_filter_chain_t,
|
|
||||||
param_name: *const c_char,
|
|
||||||
out: *mut MaybeUninit<f32>
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
assert_non_null!(param_name);
|
|
||||||
unsafe {
|
|
||||||
let name = CStr::from_ptr(param_name);
|
|
||||||
let name = name.to_str()?;
|
|
||||||
|
|
||||||
let Some(value) = chain.parameters().parameter_value(name) else {
|
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
chain.parameters().set_passes_enabled(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: *const libra_d3d9_filter_chain_t,
|
|
||||||
out: *mut MaybeUninit<u32>
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
unsafe {
|
|
||||||
let value = chain.parameters().passes_enabled();
|
|
||||||
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()))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
//! 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,42 +3,50 @@ use crate::ctypes::{
|
||||||
};
|
};
|
||||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||||
use crate::ffi::extern_fn;
|
use crate::ffi::extern_fn;
|
||||||
use crate::LIBRASHADER_API_VERSION;
|
use librashader::runtime::gl::{GLFramebuffer, GLImage};
|
||||||
use librashader::runtime::gl::{FilterChain, FilterChainOptions, FrameOptions, GLImage};
|
|
||||||
use librashader::runtime::FilterChainParameters;
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::ffi::{c_char, c_void};
|
use std::ffi::{c_char, c_void, CString};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::num::NonZeroU32;
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::Arc;
|
|
||||||
|
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};
|
||||||
|
|
||||||
/// A GL function loader that librashader needs to be initialized with.
|
/// A GL function loader that librashader needs to be initialized with.
|
||||||
pub type libra_gl_loader_t = unsafe extern "system" fn(*const c_char) -> *const c_void;
|
pub type libra_gl_loader_t = unsafe extern "system" fn(*const c_char) -> *const c_void;
|
||||||
|
|
||||||
/// OpenGL parameters for an image.
|
/// OpenGL parameters for the source image.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct libra_image_gl_t {
|
pub struct libra_source_image_gl_t {
|
||||||
/// A texture GLuint to the texture.
|
/// A texture GLuint to the source image.
|
||||||
pub handle: u32,
|
pub handle: u32,
|
||||||
/// The format of the texture.
|
/// The format of the source image.
|
||||||
pub format: u32,
|
pub format: u32,
|
||||||
/// The width of the texture.
|
/// The width of the source image.
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
/// The height of the texture.
|
/// The height of the source image.
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<libra_image_gl_t> for GLImage {
|
/// OpenGL parameters for the output framebuffer.
|
||||||
fn from(value: libra_image_gl_t) -> Self {
|
#[repr(C)]
|
||||||
let handle = NonZeroU32::try_from(value.handle)
|
pub struct libra_output_framebuffer_gl_t {
|
||||||
.ok()
|
/// A framebuffer GLuint to the output framebuffer.
|
||||||
.map(glow::NativeTexture);
|
pub fbo: u32,
|
||||||
|
/// A texture GLuint to the logical buffer of the output framebuffer.
|
||||||
|
pub texture: u32,
|
||||||
|
/// The format of the output framebuffer.
|
||||||
|
pub format: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<libra_source_image_gl_t> for GLImage {
|
||||||
|
fn from(value: libra_source_image_gl_t) -> Self {
|
||||||
GLImage {
|
GLImage {
|
||||||
handle,
|
handle: value.handle,
|
||||||
format: value.format,
|
format: value.format,
|
||||||
size: Size::new(value.width, value.height),
|
size: Size::new(value.width, value.height),
|
||||||
}
|
}
|
||||||
|
@ -56,18 +64,11 @@ pub struct frame_gl_opt_t {
|
||||||
/// The direction of rendering.
|
/// The direction of rendering.
|
||||||
/// -1 indicates that the frames are played in reverse order.
|
/// -1 indicates that the frames are played in reverse order.
|
||||||
pub frame_direction: i32,
|
pub frame_direction: i32,
|
||||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 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! {
|
config_struct! {
|
||||||
impl FrameOptions => frame_gl_opt_t {
|
impl FrameOptionsGL => frame_gl_opt_t {
|
||||||
0 => [clear_history, frame_direction];
|
0 => [clear_history, frame_direction];
|
||||||
1 => [rotation, total_subframes, current_subframe]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,11 +92,32 @@ pub struct filter_chain_gl_opt_t {
|
||||||
}
|
}
|
||||||
|
|
||||||
config_struct! {
|
config_struct! {
|
||||||
impl FilterChainOptions => filter_chain_gl_opt_t {
|
impl FilterChainOptionsGL => filter_chain_gl_opt_t {
|
||||||
0 => [glsl_version, use_dsa, force_no_mipmaps, disable_cache];
|
0 => [glsl_version, use_dsa, force_no_mipmaps, disable_cache];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern_fn! {
|
||||||
|
/// Initialize the OpenGL Context for librashader.
|
||||||
|
///
|
||||||
|
/// This only has to be done once throughout the lifetime of the application,
|
||||||
|
/// unless for whatever reason you switch OpenGL loaders mid-flight.
|
||||||
|
///
|
||||||
|
/// ## Safety
|
||||||
|
/// Attempting to create a filter chain will fail if the GL context is not initialized.
|
||||||
|
///
|
||||||
|
/// Reinitializing the OpenGL context with a different loader immediately invalidates previous filter
|
||||||
|
/// chain objects, and drawing with them causes immediate undefined behaviour.
|
||||||
|
raw fn libra_gl_init_context(loader: libra_gl_loader_t) {
|
||||||
|
gl::load_with(|s| unsafe {
|
||||||
|
let proc_name = CString::new(s).unwrap_unchecked();
|
||||||
|
loader(proc_name.as_ptr())
|
||||||
|
});
|
||||||
|
|
||||||
|
LibrashaderError::ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern_fn! {
|
extern_fn! {
|
||||||
/// Create the filter chain given the shader preset.
|
/// Create the filter chain given the shader preset.
|
||||||
///
|
///
|
||||||
|
@ -108,7 +130,6 @@ extern_fn! {
|
||||||
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
||||||
fn libra_gl_filter_chain_create(
|
fn libra_gl_filter_chain_create(
|
||||||
preset: *mut libra_shader_preset_t,
|
preset: *mut libra_shader_preset_t,
|
||||||
loader: libra_gl_loader_t,
|
|
||||||
options: *const MaybeUninit<filter_chain_gl_opt_t>,
|
options: *const MaybeUninit<filter_chain_gl_opt_t>,
|
||||||
out: *mut MaybeUninit<libra_gl_filter_chain_t>
|
out: *mut MaybeUninit<libra_gl_filter_chain_t>
|
||||||
) {
|
) {
|
||||||
|
@ -128,11 +149,7 @@ extern_fn! {
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let context = glow::Context::from_loader_function_cstr(
|
let chain = librashader::runtime::gl::capi::FilterChainGL::load_from_preset(*preset, options.as_ref())?;
|
||||||
|proc_name| loader(proc_name.as_ptr()));
|
|
||||||
|
|
||||||
let chain = FilterChain::load_from_preset(*preset,
|
|
||||||
Arc::new(context), options.as_ref())?;
|
|
||||||
|
|
||||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||||
chain,
|
chain,
|
||||||
|
@ -144,23 +161,6 @@ extern_fn! {
|
||||||
extern_fn! {
|
extern_fn! {
|
||||||
/// Draw a frame with the given parameters for the given filter chain.
|
/// Draw a frame with the given parameters for the given filter chain.
|
||||||
///
|
///
|
||||||
/// ## Parameters
|
|
||||||
///
|
|
||||||
/// - `chain` is a handle to the filter chain.
|
|
||||||
/// - `frame_count` is the number of frames passed to the shader
|
|
||||||
/// - `image` is a `libra_image_gl_t`, containing the name of a Texture, format, and size information to
|
|
||||||
/// to an image that will serve as the source image for the frame.
|
|
||||||
/// - `out` is a `libra_output_framebuffer_gl_t`, containing the name of a Framebuffer, the name of a Texture, format,
|
|
||||||
/// and size information for the render target of the frame.
|
|
||||||
///
|
|
||||||
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
|
|
||||||
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
|
|
||||||
/// entire render target will be used.
|
|
||||||
/// - `mvp` is a pointer to an array of 16 `float` values to specify the model view projection matrix to
|
|
||||||
/// be passed to the shader.
|
|
||||||
/// - `options` is a pointer to options for the frame. Valid options are dependent on the `LIBRASHADER_API_VERSION`
|
|
||||||
/// passed in. It may be null, in which case default options for the filter chain are used.
|
|
||||||
///
|
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
/// - `chain` may be null, invalid, but not uninitialized. If `chain` is null or invalid, this
|
||||||
/// function will return an error.
|
/// function will return an error.
|
||||||
|
@ -172,19 +172,17 @@ extern_fn! {
|
||||||
/// thread at a time may call this function. The thread `libra_gl_filter_chain_frame` is called from
|
/// thread at a time may call this function. The thread `libra_gl_filter_chain_frame` is called from
|
||||||
/// must have its thread-local OpenGL context initialized with the same context used to create
|
/// must have its thread-local OpenGL context initialized with the same context used to create
|
||||||
/// the filter chain.
|
/// the filter chain.
|
||||||
nopanic fn libra_gl_filter_chain_frame(
|
fn libra_gl_filter_chain_frame(
|
||||||
chain: *mut libra_gl_filter_chain_t,
|
chain: *mut libra_gl_filter_chain_t,
|
||||||
frame_count: usize,
|
frame_count: usize,
|
||||||
image: libra_image_gl_t,
|
image: libra_source_image_gl_t,
|
||||||
out: libra_image_gl_t,
|
viewport: libra_viewport_t,
|
||||||
viewport: *const libra_viewport_t,
|
out: libra_output_framebuffer_gl_t,
|
||||||
mvp: *const f32,
|
mvp: *const f32,
|
||||||
opt: *const MaybeUninit<frame_gl_opt_t>,
|
opt: *const MaybeUninit<frame_gl_opt_t>,
|
||||||
) mut |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(mut chain);
|
assert_some_ptr!(mut chain);
|
||||||
let image: GLImage = image.into();
|
let image: GLImage = image.into();
|
||||||
let out: GLImage = out.into();
|
|
||||||
|
|
||||||
let mvp = if mvp.is_null() {
|
let mvp = if mvp.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -197,21 +195,12 @@ extern_fn! {
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt = opt.map(FromUninit::from_uninit);
|
let opt = opt.map(FromUninit::from_uninit);
|
||||||
|
let framebuffer = GLFramebuffer::new_from_raw(out.texture, out.fbo, out.format, Size::new(viewport.width, viewport.height), 1);
|
||||||
let viewport = if viewport.is_null() {
|
let viewport = Viewport {
|
||||||
Viewport::new_render_target_sized_origin(&out, mvp)?
|
x: viewport.x,
|
||||||
} else {
|
y: viewport.y,
|
||||||
let viewport = unsafe { viewport.read() };
|
output: &framebuffer,
|
||||||
Viewport {
|
mvp,
|
||||||
x: viewport.x,
|
|
||||||
y: viewport.y,
|
|
||||||
output: &out,
|
|
||||||
size: Size {
|
|
||||||
height: viewport.height,
|
|
||||||
width: viewport.width
|
|
||||||
},
|
|
||||||
mvp,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -231,15 +220,15 @@ extern_fn! {
|
||||||
chain: *mut libra_gl_filter_chain_t,
|
chain: *mut libra_gl_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
value: f32
|
value: f32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
if chain.parameters().set_parameter_value(name, value).is_none() {
|
if chain.set_parameter(name, value).is_none() {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,18 +242,18 @@ extern_fn! {
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
|
||||||
/// - `param_name` must be either null or a null terminated string.
|
/// - `param_name` must be either null or a null terminated string.
|
||||||
fn libra_gl_filter_chain_get_param(
|
fn libra_gl_filter_chain_get_param(
|
||||||
chain: *const libra_gl_filter_chain_t,
|
chain: *mut libra_gl_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
out: *mut MaybeUninit<f32>
|
out: *mut MaybeUninit<f32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
let Some(value) = chain.parameters().parameter_value(name) else {
|
let Some(value) = chain.get_parameter(name) else {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
};
|
};
|
||||||
|
|
||||||
out.write(MaybeUninit::new(value));
|
out.write(MaybeUninit::new(value));
|
||||||
|
@ -280,9 +269,9 @@ extern_fn! {
|
||||||
fn libra_gl_filter_chain_set_active_pass_count(
|
fn libra_gl_filter_chain_set_active_pass_count(
|
||||||
chain: *mut libra_gl_filter_chain_t,
|
chain: *mut libra_gl_filter_chain_t,
|
||||||
value: u32
|
value: u32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
chain.parameters().set_passes_enabled(value as usize);
|
chain.set_enabled_pass_count(value as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,11 +281,11 @@ extern_fn! {
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
|
||||||
fn libra_gl_filter_chain_get_active_pass_count(
|
fn libra_gl_filter_chain_get_active_pass_count(
|
||||||
chain: *const libra_gl_filter_chain_t,
|
chain: *mut libra_gl_filter_chain_t,
|
||||||
out: *mut MaybeUninit<u32>
|
out: *mut MaybeUninit<u32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
let value = chain.parameters().passes_enabled();
|
let value = chain.get_enabled_pass_count();
|
||||||
unsafe {
|
unsafe {
|
||||||
out.write(MaybeUninit::new(value as u32))
|
out.write(MaybeUninit::new(value as u32))
|
||||||
}
|
}
|
||||||
|
@ -309,7 +298,6 @@ extern_fn! {
|
||||||
/// The resulting value in `chain` then becomes null.
|
/// The resulting value in `chain` then becomes null.
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
|
||||||
/// - The context that the filter chain was initialized with **must be current** before freeing the filter chain.
|
|
||||||
fn libra_gl_filter_chain_free(
|
fn libra_gl_filter_chain_free(
|
||||||
chain: *mut libra_gl_filter_chain_t
|
chain: *mut libra_gl_filter_chain_t
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,52 +1,16 @@
|
||||||
//! librashader runtime C APIs.
|
//! librashader runtime C APIs.
|
||||||
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-opengl")))]
|
#[doc(cfg(feature = "runtime-opengl"))]
|
||||||
#[cfg(feature = "runtime-opengl")]
|
#[cfg(feature = "runtime-opengl")]
|
||||||
pub mod gl;
|
pub mod gl;
|
||||||
|
|
||||||
#[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-vulkan")))]
|
#[doc(cfg(feature = "runtime-vulkan"))]
|
||||||
#[cfg(feature = "runtime-vulkan")]
|
#[cfg(feature = "runtime-vulkan")]
|
||||||
pub mod vk;
|
pub mod vk;
|
||||||
|
|
||||||
#[cfg_attr(
|
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))]
|
||||||
feature = "docsrs",
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
|
||||||
doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))
|
|
||||||
)]
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "__cbindgen_internal",
|
|
||||||
all(target_os = "windows", feature = "runtime-d3d11")
|
|
||||||
))]
|
|
||||||
pub mod d3d11;
|
pub mod d3d11;
|
||||||
|
|
||||||
#[cfg_attr(
|
#[doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))]
|
||||||
feature = "docsrs",
|
#[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
|
||||||
doc(cfg(all(target_os = "windows", feature = "runtime-d3d9")))
|
|
||||||
)]
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "__cbindgen_internal",
|
|
||||||
all(target_os = "windows", feature = "runtime-d3d9")
|
|
||||||
))]
|
|
||||||
pub mod d3d9;
|
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "docsrs",
|
|
||||||
doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))
|
|
||||||
)]
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "__cbindgen_internal",
|
|
||||||
all(target_os = "windows", feature = "runtime-d3d12")
|
|
||||||
))]
|
|
||||||
pub mod d3d12;
|
pub mod d3d12;
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "docsrs",
|
|
||||||
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;
|
|
||||||
|
|
|
@ -1,345 +0,0 @@
|
||||||
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 objc2::runtime::ProtocolObject;
|
|
||||||
use objc2_metal::{MTLCommandBuffer, MTLCommandQueue, MTLTexture};
|
|
||||||
|
|
||||||
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, 3 = 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.
|
|
||||||
/// ## Parameters
|
|
||||||
///
|
|
||||||
/// - `chain` is a handle to the filter chain.
|
|
||||||
/// - `command_buffer` is a `MTLCommandBuffer` handle to record draw commands to.
|
|
||||||
/// The provided command buffer must be ready for encoding and contain no prior commands
|
|
||||||
/// - `frame_count` is the number of frames passed to the shader
|
|
||||||
/// - `image` is a `id<MTLTexture>` that will serve as the source image for the frame.
|
|
||||||
/// - `out` is a `id<MTLTexture>` that is the render target of the frame.
|
|
||||||
///
|
|
||||||
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
|
|
||||||
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
|
|
||||||
/// entire render target will be used.
|
|
||||||
/// - `mvp` is a pointer to an array of 16 `float` values to specify the model view projection matrix to
|
|
||||||
/// be passed to the shader.
|
|
||||||
/// - `options` is a pointer to options for the frame. Valid options are dependent on the `LIBRASHADER_API_VERSION`
|
|
||||||
/// passed in. It may be null, in which case default options for the filter chain are used.
|
|
||||||
///
|
|
||||||
/// ## 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,
|
|
||||||
output: PMTLTexture,
|
|
||||||
viewport: *const libra_viewport_t,
|
|
||||||
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 = if viewport.is_null() {
|
|
||||||
Viewport::new_render_target_sized_origin(output, mvp)?
|
|
||||||
} else {
|
|
||||||
let viewport = unsafe { viewport.read() };
|
|
||||||
Viewport {
|
|
||||||
x: viewport.x,
|
|
||||||
y: viewport.y,
|
|
||||||
output,
|
|
||||||
size: Size {
|
|
||||||
height: viewport.height,
|
|
||||||
width: viewport.width
|
|
||||||
},
|
|
||||||
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
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
unsafe {
|
|
||||||
let name = CStr::from_ptr(param_name);
|
|
||||||
let name = name.to_str()?;
|
|
||||||
|
|
||||||
if chain.parameters().set_parameter_value(name, value).is_none() {
|
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: *const libra_mtl_filter_chain_t,
|
|
||||||
param_name: *const c_char,
|
|
||||||
out: *mut MaybeUninit<f32>
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let name = CStr::from_ptr(param_name);
|
|
||||||
let name = name.to_str()?;
|
|
||||||
|
|
||||||
let Some(value) = chain.parameters().parameter_value(name) else {
|
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
chain.parameters().set_passes_enabled(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: *const libra_mtl_filter_chain_t,
|
|
||||||
out: *mut MaybeUninit<u32>
|
|
||||||
) |chain| {
|
|
||||||
assert_some_ptr!(chain);
|
|
||||||
let value = chain.parameters().passes_enabled();
|
|
||||||
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()))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
//! C API for the librashader Metal Runtime (`libra_mtl_*`).
|
|
||||||
|
|
||||||
mod filter_chain;
|
|
||||||
|
|
||||||
pub use filter_chain::*;
|
|
|
@ -3,38 +3,48 @@ use crate::ctypes::{
|
||||||
};
|
};
|
||||||
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
use crate::error::{assert_non_null, assert_some_ptr, LibrashaderError};
|
||||||
use crate::ffi::extern_fn;
|
use crate::ffi::extern_fn;
|
||||||
use librashader::runtime::vk::{
|
use librashader::runtime::vk::{VulkanImage, VulkanInstance};
|
||||||
FilterChain, FilterChainOptions, FrameOptions, VulkanImage, VulkanInstance,
|
|
||||||
};
|
|
||||||
use std::ffi::c_char;
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::ffi::{c_char, c_void};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
use librashader::runtime::vk::capi::options::FilterChainOptionsVulkan;
|
||||||
|
use librashader::runtime::vk::capi::options::FrameOptionsVulkan;
|
||||||
use librashader::runtime::FilterChainParameters;
|
use librashader::runtime::FilterChainParameters;
|
||||||
use librashader::runtime::{Size, Viewport};
|
use librashader::runtime::{Size, Viewport};
|
||||||
|
|
||||||
use crate::LIBRASHADER_API_VERSION;
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
use ash::vk::Handle;
|
|
||||||
|
|
||||||
/// A Vulkan instance function loader that the Vulkan filter chain needs to be initialized with.
|
|
||||||
pub use ash::vk::PFN_vkGetInstanceProcAddr;
|
pub use ash::vk::PFN_vkGetInstanceProcAddr;
|
||||||
|
|
||||||
/// Vulkan parameters for an image.
|
/// A Vulkan instance function loader that the Vulkan filter chain needs to be initialized with.
|
||||||
|
pub type libra_PFN_vkGetInstanceProcAddr =
|
||||||
|
unsafe extern "system" fn(instance: *mut c_void, p_name: *const c_char);
|
||||||
|
|
||||||
|
/// Vulkan parameters for the source image.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct libra_image_vk_t {
|
pub struct libra_source_image_vk_t {
|
||||||
/// A raw `VkImage` handle.
|
/// A raw `VkImage` handle to the source image.
|
||||||
pub handle: vk::Image,
|
pub handle: vk::Image,
|
||||||
/// The `VkFormat` of the `VkImage`.
|
/// The `VkFormat` of the source image.
|
||||||
pub format: vk::Format,
|
pub format: vk::Format,
|
||||||
/// The width of the `VkImage`.
|
/// The width of the source image.
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
/// The height of the `VkImage`.
|
/// The height of the source image.
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Vulkan parameters for the output image.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct libra_output_image_vk_t {
|
||||||
|
/// A raw `VkImage` handle to the output image.
|
||||||
|
pub handle: vk::Image,
|
||||||
|
/// The `VkFormat` of the output image.
|
||||||
|
pub format: vk::Format,
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles required to instantiate vulkan
|
/// Handles required to instantiate vulkan
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct libra_device_vk_t {
|
pub struct libra_device_vk_t {
|
||||||
|
@ -47,15 +57,12 @@ pub struct libra_device_vk_t {
|
||||||
/// A raw `VkDevice` handle
|
/// A raw `VkDevice` handle
|
||||||
/// for the device attached to the instance that will perform rendering.
|
/// for the device attached to the instance that will perform rendering.
|
||||||
pub device: vk::Device,
|
pub device: vk::Device,
|
||||||
/// The queue to use, if this is `NULL`, then
|
|
||||||
/// a suitable queue will be chosen. This must be a graphics queue.
|
|
||||||
pub queue: vk::Queue,
|
|
||||||
/// The entry loader for the Vulkan library.
|
/// The entry loader for the Vulkan library.
|
||||||
pub entry: Option<vk::PFN_vkGetInstanceProcAddr>,
|
pub entry: vk::PFN_vkGetInstanceProcAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<libra_image_vk_t> for VulkanImage {
|
impl From<libra_source_image_vk_t> for VulkanImage {
|
||||||
fn from(value: libra_image_vk_t) -> Self {
|
fn from(value: libra_source_image_vk_t) -> Self {
|
||||||
VulkanImage {
|
VulkanImage {
|
||||||
size: Size::new(value.width, value.height),
|
size: Size::new(value.width, value.height),
|
||||||
image: value.handle,
|
image: value.handle,
|
||||||
|
@ -66,45 +73,31 @@ impl From<libra_image_vk_t> for VulkanImage {
|
||||||
|
|
||||||
impl From<libra_device_vk_t> for VulkanInstance {
|
impl From<libra_device_vk_t> for VulkanInstance {
|
||||||
fn from(value: libra_device_vk_t) -> Self {
|
fn from(value: libra_device_vk_t) -> Self {
|
||||||
let queue = if value.queue.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(value.queue)
|
|
||||||
};
|
|
||||||
|
|
||||||
VulkanInstance {
|
VulkanInstance {
|
||||||
device: value.device,
|
device: value.device,
|
||||||
instance: value.instance,
|
instance: value.instance,
|
||||||
physical_device: value.physical_device,
|
physical_device: value.physical_device,
|
||||||
get_instance_proc_addr: value.entry,
|
get_instance_proc_addr: value.entry,
|
||||||
queue,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for each Vulkan shader frame.
|
/// Options for each OpenGL shader frame.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct frame_vk_opt_t {
|
pub struct frame_vk_opt_t {
|
||||||
/// The librashader API version.
|
/// The librashader API version.
|
||||||
pub version: LIBRASHADER_API_VERSION,
|
pub version: usize,
|
||||||
/// Whether or not to clear the history buffers.
|
/// Whether or not to clear the history buffers.
|
||||||
pub clear_history: bool,
|
pub clear_history: bool,
|
||||||
/// The direction of rendering.
|
/// The direction of rendering.
|
||||||
/// -1 indicates that the frames are played in reverse order.
|
/// -1 indicates that the frames are played in reverse order.
|
||||||
pub frame_direction: i32,
|
pub frame_direction: i32,
|
||||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 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! {
|
config_struct! {
|
||||||
impl FrameOptions => frame_vk_opt_t {
|
impl FrameOptionsVulkan => frame_vk_opt_t {
|
||||||
0 => [clear_history, frame_direction];
|
0 => [clear_history, frame_direction];
|
||||||
1 => [rotation, total_subframes, current_subframe]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,23 +106,22 @@ config_struct! {
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct filter_chain_vk_opt_t {
|
pub struct filter_chain_vk_opt_t {
|
||||||
/// The librashader API version.
|
/// The librashader API version.
|
||||||
pub version: LIBRASHADER_API_VERSION,
|
pub version: usize,
|
||||||
/// The number of frames in flight to keep. If zero, defaults to three.
|
/// The number of frames in flight to keep. If zero, defaults to three.
|
||||||
pub frames_in_flight: u32,
|
pub frames_in_flight: u32,
|
||||||
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
|
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
|
||||||
pub force_no_mipmaps: bool,
|
pub force_no_mipmaps: bool,
|
||||||
/// Use dynamic rendering over explicit render pass objects.
|
/// Use explicit render pass objects It is recommended if possible to use dynamic rendering,
|
||||||
/// It is recommended if possible to use dynamic rendering,
|
|
||||||
/// because render-pass mode will create new framebuffers per pass.
|
/// because render-pass mode will create new framebuffers per pass.
|
||||||
pub use_dynamic_rendering: bool,
|
pub use_render_pass: bool,
|
||||||
/// Disable the shader object cache. Shaders will be
|
/// Disable the shader object cache. Shaders will be
|
||||||
/// recompiled rather than loaded from the cache.
|
/// recompiled rather than loaded from the cache.
|
||||||
pub disable_cache: bool,
|
pub disable_cache: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
config_struct! {
|
config_struct! {
|
||||||
impl FilterChainOptions => filter_chain_vk_opt_t {
|
impl FilterChainOptionsVulkan => filter_chain_vk_opt_t {
|
||||||
0 => [frames_in_flight, force_no_mipmaps, use_dynamic_rendering, disable_cache];
|
0 => [frames_in_flight, force_no_mipmaps, use_render_pass, disable_cache];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +133,7 @@ extern_fn! {
|
||||||
///
|
///
|
||||||
/// ## Safety:
|
/// ## Safety:
|
||||||
/// - The handles provided in `vulkan` must be valid for the command buffers that
|
/// - The handles provided in `vulkan` must be valid for the command buffers that
|
||||||
/// `libra_vk_filter_chain_frame` will write to.
|
/// `libra_vk_filter_chain_frame` will write to. Namely, the VkDevice must have been
|
||||||
/// created with the `VK_KHR_dynamic_rendering` extension.
|
/// created with the `VK_KHR_dynamic_rendering` extension.
|
||||||
/// - `preset` must be either null, or valid and aligned.
|
/// - `preset` must be either null, or valid and aligned.
|
||||||
/// - `options` must be either null, or valid and aligned.
|
/// - `options` must be either null, or valid and aligned.
|
||||||
|
@ -169,7 +161,7 @@ extern_fn! {
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let chain = FilterChain::load_from_preset(*preset, vulkan, options.as_ref())?;
|
let chain = librashader::runtime::vk::capi::FilterChainVulkan::load_from_preset(*preset, vulkan, options.as_ref())?;
|
||||||
|
|
||||||
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
out.write(MaybeUninit::new(NonNull::new(Box::into_raw(Box::new(
|
||||||
chain,
|
chain,
|
||||||
|
@ -187,7 +179,8 @@ extern_fn! {
|
||||||
///
|
///
|
||||||
/// ## Safety:
|
/// ## Safety:
|
||||||
/// - The handles provided in `vulkan` must be valid for the command buffers that
|
/// - The handles provided in `vulkan` must be valid for the command buffers that
|
||||||
/// `libra_vk_filter_chain_frame` will write to.
|
/// `libra_vk_filter_chain_frame` will write to. Namely, the VkDevice must have been
|
||||||
|
/// created with the `VK_KHR_dynamic_rendering` extension.
|
||||||
/// - `preset` must be either null, or valid and aligned.
|
/// - `preset` must be either null, or valid and aligned.
|
||||||
/// - `options` 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.
|
/// - `out` must be aligned, but may be null, invalid, or uninitialized.
|
||||||
|
@ -219,7 +212,7 @@ extern_fn! {
|
||||||
let options = options.map(FromUninit::from_uninit);
|
let options = options.map(FromUninit::from_uninit);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let chain = FilterChain::load_from_preset_deferred(*preset,
|
let chain = librashader::runtime::vk::capi::FilterChainVulkan::load_from_preset_deferred(*preset,
|
||||||
vulkan,
|
vulkan,
|
||||||
command_buffer,
|
command_buffer,
|
||||||
options.as_ref())?;
|
options.as_ref())?;
|
||||||
|
@ -235,31 +228,13 @@ extern_fn! {
|
||||||
/// Records rendering commands for a frame with the given parameters for the given filter chain
|
/// Records rendering commands for a frame with the given parameters for the given filter chain
|
||||||
/// to the input command buffer.
|
/// to the input command buffer.
|
||||||
///
|
///
|
||||||
/// A pipeline barrier **will not** be created for the final pass. The output image must be
|
/// * The input image must be in the `VK_SHADER_READ_ONLY_OPTIMAL` layout.
|
||||||
/// in `VK_COLOR_ATTACHMENT_OPTIMAL`, and will remain so after all shader passes. The caller must transition
|
/// * The output image must be in `VK_COLOR_ATTACHMENT_OPTIMAL` layout.
|
||||||
|
///
|
||||||
|
/// librashader **will not** create a pipeline barrier for the final pass. The output image will
|
||||||
|
/// remain in `VK_COLOR_ATTACHMENT_OPTIMAL` after all shader passes. The caller must transition
|
||||||
/// the output image to the final layout.
|
/// the output image to the final layout.
|
||||||
///
|
///
|
||||||
/// ## Parameters
|
|
||||||
///
|
|
||||||
/// - `chain` is a handle to the filter chain.
|
|
||||||
/// - `command_buffer` is a `VkCommandBuffer` handle to record draw commands to.
|
|
||||||
/// The provided command buffer must be ready for recording and contain no prior commands
|
|
||||||
/// - `frame_count` is the number of frames passed to the shader
|
|
||||||
/// - `image` is a `libra_image_vk_t`, containing a `VkImage` handle, it's format and size information,
|
|
||||||
/// to an image that will serve as the source image for the frame. The input image must be in
|
|
||||||
/// the `VK_SHADER_READ_ONLY_OPTIMAL` layout.
|
|
||||||
/// - `out` is a `libra_image_vk_t`, containing a `VkImage` handle, it's format and size information,
|
|
||||||
/// for the render target of the frame. The output image must be in `VK_COLOR_ATTACHMENT_OPTIMAL` layout.
|
|
||||||
/// The output image will remain in `VK_COLOR_ATTACHMENT_OPTIMAL` after all shader passes.
|
|
||||||
///
|
|
||||||
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
|
|
||||||
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
|
|
||||||
/// entire render target will be used.
|
|
||||||
/// - `mvp` is a pointer to an array of 16 `float` values to specify the model view projection matrix to
|
|
||||||
/// be passed to the shader.
|
|
||||||
/// - `options` is a pointer to options for the frame. Valid options are dependent on the `LIBRASHADER_API_VERSION`
|
|
||||||
/// passed in. It may be null, in which case default options for the filter chain are used.
|
|
||||||
///
|
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `libra_vk_filter_chain_frame` **must not be called within a RenderPass**.
|
/// - `libra_vk_filter_chain_frame` **must not be called within a RenderPass**.
|
||||||
/// - `command_buffer` must be a valid handle to a `VkCommandBuffer` that is ready for recording.
|
/// - `command_buffer` must be a valid handle to a `VkCommandBuffer` that is ready for recording.
|
||||||
|
@ -271,13 +246,13 @@ extern_fn! {
|
||||||
/// struct.
|
/// struct.
|
||||||
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
|
/// - 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.
|
/// thread at a time may call this function.
|
||||||
nopanic fn libra_vk_filter_chain_frame(
|
fn libra_vk_filter_chain_frame(
|
||||||
chain: *mut libra_vk_filter_chain_t,
|
chain: *mut libra_vk_filter_chain_t,
|
||||||
command_buffer: vk::CommandBuffer,
|
command_buffer: vk::CommandBuffer,
|
||||||
frame_count: usize,
|
frame_count: usize,
|
||||||
image: libra_image_vk_t,
|
image: libra_source_image_vk_t,
|
||||||
out: libra_image_vk_t,
|
viewport: libra_viewport_t,
|
||||||
viewport: *const libra_viewport_t,
|
out: libra_output_image_vk_t,
|
||||||
mvp: *const f32,
|
mvp: *const f32,
|
||||||
opt: *const MaybeUninit<frame_vk_opt_t>
|
opt: *const MaybeUninit<frame_vk_opt_t>
|
||||||
) mut |chain| {
|
) mut |chain| {
|
||||||
|
@ -285,7 +260,7 @@ extern_fn! {
|
||||||
let image: VulkanImage = image.into();
|
let image: VulkanImage = image.into();
|
||||||
let output = VulkanImage {
|
let output = VulkanImage {
|
||||||
image: out.handle,
|
image: out.handle,
|
||||||
size: Size::new(out.width, out.height),
|
size: Size::new(viewport.width, viewport.height),
|
||||||
format: out.format
|
format: out.format
|
||||||
};
|
};
|
||||||
let mvp = if mvp.is_null() {
|
let mvp = if mvp.is_null() {
|
||||||
|
@ -299,21 +274,11 @@ extern_fn! {
|
||||||
Some(unsafe { opt.read() })
|
Some(unsafe { opt.read() })
|
||||||
};
|
};
|
||||||
let opt = opt.map(FromUninit::from_uninit);
|
let opt = opt.map(FromUninit::from_uninit);
|
||||||
|
let viewport = Viewport {
|
||||||
let viewport = if viewport.is_null() {
|
x: viewport.x,
|
||||||
Viewport::new_render_target_sized_origin(output, mvp)?
|
y: viewport.y,
|
||||||
} else {
|
output,
|
||||||
let viewport = unsafe { viewport.read() };
|
mvp,
|
||||||
Viewport {
|
|
||||||
x: viewport.x,
|
|
||||||
y: viewport.y,
|
|
||||||
output,
|
|
||||||
size: Size {
|
|
||||||
height: viewport.height,
|
|
||||||
width: viewport.width
|
|
||||||
},
|
|
||||||
mvp,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -333,15 +298,15 @@ extern_fn! {
|
||||||
chain: *mut libra_vk_filter_chain_t,
|
chain: *mut libra_vk_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
value: f32
|
value: f32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
if chain.parameters().set_parameter_value(name, value).is_none() {
|
if chain.set_parameter(name, value).is_none() {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,18 +320,18 @@ extern_fn! {
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_vk_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_vk_filter_chain_t`.
|
||||||
/// - `param_name` must be either null or a null terminated string.
|
/// - `param_name` must be either null or a null terminated string.
|
||||||
fn libra_vk_filter_chain_get_param(
|
fn libra_vk_filter_chain_get_param(
|
||||||
chain: *const libra_vk_filter_chain_t,
|
chain: *mut libra_vk_filter_chain_t,
|
||||||
param_name: *const c_char,
|
param_name: *const c_char,
|
||||||
out: *mut MaybeUninit<f32>
|
out: *mut MaybeUninit<f32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
assert_non_null!(param_name);
|
assert_non_null!(param_name);
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = CStr::from_ptr(param_name);
|
let name = CStr::from_ptr(param_name);
|
||||||
let name = name.to_str()?;
|
let name = name.to_str()?;
|
||||||
|
|
||||||
let Some(value) = chain.parameters().parameter_value(name) else {
|
let Some(value) = chain.get_parameter(name) else {
|
||||||
return Err(LibrashaderError::UnknownShaderParameter(param_name))
|
return LibrashaderError::UnknownShaderParameter(param_name).export()
|
||||||
};
|
};
|
||||||
|
|
||||||
out.write(MaybeUninit::new(value));
|
out.write(MaybeUninit::new(value));
|
||||||
|
@ -382,9 +347,9 @@ extern_fn! {
|
||||||
fn libra_vk_filter_chain_set_active_pass_count(
|
fn libra_vk_filter_chain_set_active_pass_count(
|
||||||
chain: *mut libra_vk_filter_chain_t,
|
chain: *mut libra_vk_filter_chain_t,
|
||||||
value: u32
|
value: u32
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
chain.parameters().set_passes_enabled(value as usize);
|
chain.set_enabled_pass_count(value as usize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,11 +359,11 @@ extern_fn! {
|
||||||
/// ## Safety
|
/// ## Safety
|
||||||
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_vk_filter_chain_t`.
|
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_vk_filter_chain_t`.
|
||||||
fn libra_vk_filter_chain_get_active_pass_count(
|
fn libra_vk_filter_chain_get_active_pass_count(
|
||||||
chain: *const libra_vk_filter_chain_t,
|
chain: *mut libra_vk_filter_chain_t,
|
||||||
out: *mut MaybeUninit<u32>
|
out: *mut MaybeUninit<u32>
|
||||||
) |chain| {
|
) mut |chain| {
|
||||||
assert_some_ptr!(chain);
|
assert_some_ptr!(mut chain);
|
||||||
let value = chain.parameters().passes_enabled();
|
let value = chain.get_enabled_pass_count();
|
||||||
unsafe {
|
unsafe {
|
||||||
out.write(MaybeUninit::new(value as u32))
|
out.write(MaybeUninit::new(value as u32))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! C API for the librashader Vulkan Runtime (`libra_vk_*`).
|
//! C API for the librashader OpenGL Runtime (`libra_vk_*`).
|
||||||
|
|
||||||
mod filter_chain;
|
mod filter_chain;
|
||||||
pub use filter_chain::*;
|
pub use filter_chain::*;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
/// API version type alias.
|
/// API version type alias.
|
||||||
pub type LIBRASHADER_API_VERSION = usize;
|
pub type LIBRASHADER_API_VERSION = usize;
|
||||||
/// ABI version type alias.
|
|
||||||
pub type LIBRASHADER_ABI_VERSION = usize;
|
pub type LIBRASHADER_ABI_VERSION = usize;
|
||||||
|
|
||||||
/// The current version of the librashader API.
|
/// The current version of the librashader API.
|
||||||
|
@ -14,11 +13,7 @@ pub type LIBRASHADER_ABI_VERSION = usize;
|
||||||
/// versions must remain backwards compatible.
|
/// versions must remain backwards compatible.
|
||||||
/// ## API Versions
|
/// ## API Versions
|
||||||
/// - API version 0: 0.1.0
|
/// - API version 0: 0.1.0
|
||||||
/// - API version 1: 0.2.0
|
pub const LIBRASHADER_CURRENT_VERSION: LIBRASHADER_API_VERSION = 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.
|
/// The current version of the librashader ABI.
|
||||||
/// Used by the loader to check ABI compatibility.
|
/// Used by the loader to check ABI compatibility.
|
||||||
|
@ -28,17 +23,10 @@ pub const LIBRASHADER_CURRENT_VERSION: LIBRASHADER_API_VERSION = 1;
|
||||||
/// ABI versions are not backwards compatible. It is not
|
/// ABI versions are not backwards compatible. It is not
|
||||||
/// valid to load a librashader C API instance for any ABI
|
/// valid to load a librashader C API instance for any ABI
|
||||||
/// version not equal to LIBRASHADER_CURRENT_ABI.
|
/// version not equal to LIBRASHADER_CURRENT_ABI.
|
||||||
///
|
|
||||||
/// ## ABI Versions
|
/// ## ABI Versions
|
||||||
/// - ABI version 0: null instance (unloaded)
|
/// - ABI version 0: null instance (unloaded)
|
||||||
/// - ABI version 1: 0.1.0
|
/// - ABI version 1: 0.1.0
|
||||||
/// - ABI version 2: 0.5.0
|
pub const LIBRASHADER_CURRENT_ABI: LIBRASHADER_ABI_VERSION = 1;
|
||||||
/// - Reduced texture size information needed for some runtimes.
|
|
||||||
/// - Removed wrapper structs for Direct3D 11 SRV and RTV handles.
|
|
||||||
/// - Removed `gl_context_init`.
|
|
||||||
/// - Make viewport handling consistent across runtimes, which are now
|
|
||||||
/// span the output render target if omitted.
|
|
||||||
pub const LIBRASHADER_CURRENT_ABI: LIBRASHADER_ABI_VERSION = 2;
|
|
||||||
|
|
||||||
/// Function pointer definition for libra_abi_version
|
/// Function pointer definition for libra_abi_version
|
||||||
pub type PFN_libra_instance_abi_version = extern "C" fn() -> LIBRASHADER_ABI_VERSION;
|
pub type PFN_libra_instance_abi_version = extern "C" fn() -> LIBRASHADER_ABI_VERSION;
|
||||||
|
|
|
@ -1,265 +0,0 @@
|
||||||
//! 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
|
|
||||||
/// - Metal
|
|
||||||
/// - Direct3D9 (HLSL)
|
|
||||||
///
|
|
||||||
/// 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()))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "librashader-cli"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
|
||||||
version = "0.5.1"
|
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
|
||||||
readme = "../README.md"
|
|
||||||
categories = ["emulators", "compilers", "graphics"]
|
|
||||||
keywords = ["shader", "retroarch", "SPIR-V"]
|
|
||||||
description = "RetroArch shaders for all."
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "librashader_test"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "librashader-cli"
|
|
||||||
path = "src/cli/main.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
librashader = { version = "0.5.1", path = "../librashader", features = [
|
|
||||||
"presets",
|
|
||||||
"preprocess",
|
|
||||||
"serde",
|
|
||||||
], default-features = false }
|
|
||||||
librashader-runtime = { version = "0.5.1", path = "../librashader-runtime" }
|
|
||||||
librashader-common = { version = "0.5.1", path = "../librashader-common" }
|
|
||||||
wgpu = { version = "22", default-features = false, optional = true }
|
|
||||||
wgpu-types = { version = "22", optional = true }
|
|
||||||
anyhow = "1.0.86"
|
|
||||||
image = { workspace = true }
|
|
||||||
gfx-maths = "0.2.8"
|
|
||||||
|
|
||||||
pollster = "0.3.0"
|
|
||||||
parking_lot = "0.12.3"
|
|
||||||
image-compare = "0.4.1"
|
|
||||||
gpu-allocator = "0.27.0"
|
|
||||||
bitvec = "1.0.1"
|
|
||||||
d3d12-descriptor-heap = { version = "0.2", optional = true }
|
|
||||||
glow = { workspace = true, optional = true }
|
|
||||||
glfw = { workspace = true, optional = true }
|
|
||||||
ash = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
clap = { workspace = true }
|
|
||||||
serde = "1.0"
|
|
||||||
serde_json = "1.0"
|
|
||||||
spq-spvasm = "0.1.4"
|
|
||||||
|
|
||||||
rmp-serde = "1.3.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["full"]
|
|
||||||
|
|
||||||
full = ["vulkan", "opengl", "wgpu", "d3d9", "d3d11", "d3d12", "metal"]
|
|
||||||
vulkan = ["librashader/runtime-vk", "dep:ash"]
|
|
||||||
opengl = ["librashader/runtime-gl", "dep:glow", "dep:glfw"]
|
|
||||||
wgpu = ["librashader/runtime-wgpu", "dep:wgpu", "dep:wgpu-types"]
|
|
||||||
|
|
||||||
d3d11 = ["librashader/runtime-d3d11", "dep:windows"]
|
|
||||||
d3d12 = [
|
|
||||||
"librashader/runtime-d3d12",
|
|
||||||
"dep:windows",
|
|
||||||
"dep:d3d12-descriptor-heap",
|
|
||||||
]
|
|
||||||
d3d9 = ["librashader/runtime-d3d9", "dep:windows"]
|
|
||||||
|
|
||||||
metal = ["librashader/runtime-metal", "dep:objc2", "dep:objc2-metal"]
|
|
||||||
|
|
||||||
vulkan-debug = ["vulkan"]
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
|
||||||
workspace = true
|
|
||||||
optional = true
|
|
||||||
features = [
|
|
||||||
"Win32_Foundation",
|
|
||||||
"Win32_Graphics_Dxgi_Common",
|
|
||||||
"Win32_Graphics_Direct3D",
|
|
||||||
"Win32_Graphics_Direct3D11",
|
|
||||||
"Win32_Graphics_Direct3D_Fxc",
|
|
||||||
"Win32_Graphics_Gdi",
|
|
||||||
"Win32_Security",
|
|
||||||
"Win32_System_LibraryLoader",
|
|
||||||
"Win32_System_Threading",
|
|
||||||
"Win32_UI_WindowsAndMessaging",
|
|
||||||
"Win32_UI",
|
|
||||||
]
|
|
||||||
|
|
||||||
[target.'cfg(target_vendor="apple")'.dependencies]
|
|
||||||
objc2-metal = { version = "0.2.0", features = ["all"], optional = true }
|
|
||||||
objc2 = { version = "0.5.0", features = ["apple"], optional = true }
|
|
|
@ -1,804 +0,0 @@
|
||||||
use anyhow::anyhow;
|
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
use image::codecs::png::PngEncoder;
|
|
||||||
use librashader::presets::context::ContextItem;
|
|
||||||
use librashader::presets::{ShaderPreset, ShaderPresetPack, WildcardContext};
|
|
||||||
use librashader::reflect::cross::{GlslVersion, HlslShaderModel, MslVersion, SpirvCross};
|
|
||||||
use librashader::reflect::naga::{Naga, NagaLoweringOptions};
|
|
||||||
use librashader::reflect::semantics::ShaderSemantics;
|
|
||||||
use librashader::reflect::{CompileShader, FromCompilation, ReflectShader, SpirvCompilation};
|
|
||||||
use librashader::runtime::Size;
|
|
||||||
use librashader::{FastHashMap, ShortString};
|
|
||||||
use librashader_common::StorageType;
|
|
||||||
use librashader_runtime::parameters::RuntimeParameters;
|
|
||||||
use librashader_test::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
/// Helpers and utilities to reflect and debug 'slang' shaders and presets.
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[command(version, about)]
|
|
||||||
struct Args {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Commands,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::Args, Debug)]
|
|
||||||
struct PresetArgs {
|
|
||||||
/// The path to the shader preset to load.
|
|
||||||
#[arg(short, long)]
|
|
||||||
preset: PathBuf,
|
|
||||||
/// Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR
|
|
||||||
/// wildcards are always added to the preset parsing context.
|
|
||||||
///
|
|
||||||
/// For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
|
|
||||||
#[arg(short, long, value_delimiter = ',', num_args = 1..)]
|
|
||||||
wildcards: Option<Vec<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::Args, Debug)]
|
|
||||||
struct RenderArgs {
|
|
||||||
/// The frame to render.
|
|
||||||
///
|
|
||||||
/// The renderer will run up to the number of frames specified here
|
|
||||||
/// to ensure feedback and history.
|
|
||||||
#[arg(short, long, default_value_t = 0)]
|
|
||||||
frame: usize,
|
|
||||||
/// The dimensions of the image.
|
|
||||||
///
|
|
||||||
/// This is given in either explicit dimensions `WIDTHxHEIGHT`, or a
|
|
||||||
/// percentage of the input image in `SCALE%`.
|
|
||||||
#[arg(short, long)]
|
|
||||||
dimensions: Option<String>,
|
|
||||||
/// Parameters to pass to the shader preset, comma separated with equals signs.
|
|
||||||
///
|
|
||||||
/// For example, crt_gamma=2.5,halation_weight=0.001
|
|
||||||
#[arg(long, value_delimiter = ',', num_args = 1..)]
|
|
||||||
params: Option<Vec<String>>,
|
|
||||||
/// Set the number of passes enabled for the preset.
|
|
||||||
#[arg(long)]
|
|
||||||
passes_enabled: Option<usize>,
|
|
||||||
/// The path to the input image.
|
|
||||||
#[arg(short, long)]
|
|
||||||
image: PathBuf,
|
|
||||||
#[clap(flatten)]
|
|
||||||
options: Option<FrameOptionsArgs>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FrameOptionsArgs> for CommonFrameOptions {
|
|
||||||
fn from(value: FrameOptionsArgs) -> Self {
|
|
||||||
Self {
|
|
||||||
clear_history: false,
|
|
||||||
frame_direction: value.frame_direction,
|
|
||||||
rotation: value.rotation,
|
|
||||||
total_subframes: value.total_subframes,
|
|
||||||
current_subframe: value.current_subframe,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::Args, Debug)]
|
|
||||||
struct FrameOptionsArgs {
|
|
||||||
/// The direction of rendering.
|
|
||||||
/// -1 indicates that the frames are played in reverse order.
|
|
||||||
#[arg(long, default_value_t = 1, allow_hyphen_values = true)]
|
|
||||||
pub frame_direction: i32,
|
|
||||||
/// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg.
|
|
||||||
#[arg(long, default_value_t = 0)]
|
|
||||||
pub rotation: u32,
|
|
||||||
/// The total number of subframes ran. Default is 1.
|
|
||||||
#[arg(long, default_value_t = 1)]
|
|
||||||
pub total_subframes: u32,
|
|
||||||
/// The current sub frame. Default is 1.
|
|
||||||
#[arg(long, default_value_t = 1)]
|
|
||||||
pub current_subframe: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
|
||||||
enum Commands {
|
|
||||||
/// Render a shader preset against an image
|
|
||||||
Render {
|
|
||||||
#[clap(flatten)]
|
|
||||||
preset: PresetArgs,
|
|
||||||
#[clap(flatten)]
|
|
||||||
render: RenderArgs,
|
|
||||||
/// The path to the output image
|
|
||||||
///
|
|
||||||
/// If `-`, writes the image in PNG format to stdout.
|
|
||||||
#[arg(short, long)]
|
|
||||||
out: PathBuf,
|
|
||||||
/// The runtime to use to render the shader preset.
|
|
||||||
#[arg(value_enum, short, long)]
|
|
||||||
runtime: Runtime,
|
|
||||||
},
|
|
||||||
/// Compare two runtimes and get a similarity score between the two
|
|
||||||
/// runtimes rendering the same frame
|
|
||||||
Compare {
|
|
||||||
#[clap(flatten)]
|
|
||||||
preset: PresetArgs,
|
|
||||||
#[clap(flatten)]
|
|
||||||
render: RenderArgs,
|
|
||||||
/// The runtime to compare against
|
|
||||||
#[arg(value_enum, short, long)]
|
|
||||||
left: Runtime,
|
|
||||||
/// The runtime to compare to
|
|
||||||
#[arg(value_enum, short, long)]
|
|
||||||
right: Runtime,
|
|
||||||
/// The path to write the similarity image.
|
|
||||||
///
|
|
||||||
/// If `-`, writes the image to stdout.
|
|
||||||
#[arg(short, long)]
|
|
||||||
out: Option<PathBuf>,
|
|
||||||
},
|
|
||||||
/// Parse a preset and get a JSON representation of the data.
|
|
||||||
Parse {
|
|
||||||
#[clap(flatten)]
|
|
||||||
preset: PresetArgs,
|
|
||||||
},
|
|
||||||
/// Create a serialized preset pack from a shader preset.
|
|
||||||
Pack {
|
|
||||||
#[clap(flatten)]
|
|
||||||
preset: PresetArgs,
|
|
||||||
/// The path to write the output
|
|
||||||
///
|
|
||||||
/// If `-`, writes the output to stdout
|
|
||||||
#[arg(short, long)]
|
|
||||||
out: PathBuf,
|
|
||||||
/// The file format to output.
|
|
||||||
#[arg(value_enum, short, long)]
|
|
||||||
format: PackFormat,
|
|
||||||
},
|
|
||||||
/// Get the raw GLSL output of a preprocessed shader.
|
|
||||||
Preprocess {
|
|
||||||
/// The path to the slang shader.
|
|
||||||
#[arg(short, long)]
|
|
||||||
shader: PathBuf,
|
|
||||||
/// The item to output.
|
|
||||||
///
|
|
||||||
/// `json` will print a JSON representation of the preprocessed shader.
|
|
||||||
#[arg(value_enum, short, long)]
|
|
||||||
output: PreprocessOutput,
|
|
||||||
},
|
|
||||||
/// Transpile a shader to the given format.
|
|
||||||
Transpile {
|
|
||||||
/// The path to the slang shader.
|
|
||||||
#[arg(short, long)]
|
|
||||||
shader: PathBuf,
|
|
||||||
|
|
||||||
/// The shader stage to output
|
|
||||||
#[arg(value_enum, short = 'o', long)]
|
|
||||||
stage: TranspileStage,
|
|
||||||
|
|
||||||
/// The output format.
|
|
||||||
#[arg(value_enum, short, long)]
|
|
||||||
format: TranspileFormat,
|
|
||||||
|
|
||||||
/// The version of the output format to parse as, if applicable
|
|
||||||
///
|
|
||||||
/// For GLSL, this should be an string corresponding to a GLSL version (e.g. '330', or '300es', or '300 es').
|
|
||||||
///
|
|
||||||
/// For HLSL, this is a shader model version as an integer (50), or a version in the format MAJ_MIN (5_0), or MAJ.MIN (5.0).
|
|
||||||
///
|
|
||||||
/// For MSL, this is the shader language version as an integer in format
|
|
||||||
/// <MMmmpp>(30100), or a version in the format MAJ_MIN (3_1), or MAJ.MIN (3.1).
|
|
||||||
///
|
|
||||||
/// For SPIR-V, if this is the string "raw-id", then shows raw ID values instead of friendly names.
|
|
||||||
#[arg(short, long)]
|
|
||||||
version: Option<String>,
|
|
||||||
},
|
|
||||||
/// Reflect the shader relative to a preset, giving information about semantics used in a slang shader.
|
|
||||||
Reflect {
|
|
||||||
#[clap(flatten)]
|
|
||||||
preset: PresetArgs,
|
|
||||||
|
|
||||||
/// The pass index to use.
|
|
||||||
#[arg(short, long)]
|
|
||||||
index: usize,
|
|
||||||
|
|
||||||
#[arg(value_enum, short, long, default_value = "cross")]
|
|
||||||
backend: ReflectionBackend,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
enum PreprocessOutput {
|
|
||||||
#[clap(name = "fragment")]
|
|
||||||
Fragment,
|
|
||||||
#[clap(name = "vertex")]
|
|
||||||
Vertex,
|
|
||||||
#[clap(name = "params")]
|
|
||||||
Params,
|
|
||||||
#[clap(name = "passformat")]
|
|
||||||
Format,
|
|
||||||
#[clap(name = "json")]
|
|
||||||
Json,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
enum TranspileStage {
|
|
||||||
#[clap(name = "fragment")]
|
|
||||||
Fragment,
|
|
||||||
#[clap(name = "vertex")]
|
|
||||||
Vertex,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
enum TranspileFormat {
|
|
||||||
#[clap(name = "glsl")]
|
|
||||||
GLSL,
|
|
||||||
#[clap(name = "hlsl")]
|
|
||||||
HLSL,
|
|
||||||
#[clap(name = "wgsl")]
|
|
||||||
WGSL,
|
|
||||||
#[clap(name = "msl")]
|
|
||||||
MSL,
|
|
||||||
#[clap(name = "spirv")]
|
|
||||||
SPIRV,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
enum PackFormat {
|
|
||||||
#[clap(name = "json")]
|
|
||||||
JSON,
|
|
||||||
#[clap(name = "msgpack")]
|
|
||||||
MsgPack,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
enum Runtime {
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
#[clap(name = "opengl3")]
|
|
||||||
OpenGL3,
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
#[clap(name = "opengl4")]
|
|
||||||
OpenGL4,
|
|
||||||
#[cfg(feature = "vulkan")]
|
|
||||||
#[clap(name = "vulkan")]
|
|
||||||
Vulkan,
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
#[clap(name = "wgpu")]
|
|
||||||
Wgpu,
|
|
||||||
#[cfg(all(windows, feature = "d3d9"))]
|
|
||||||
#[clap(name = "d3d9")]
|
|
||||||
Direct3D9,
|
|
||||||
#[cfg(all(windows, feature = "d3d11"))]
|
|
||||||
#[clap(name = "d3d11")]
|
|
||||||
Direct3D11,
|
|
||||||
#[cfg(all(windows, feature = "d3d12"))]
|
|
||||||
#[clap(name = "d3d12")]
|
|
||||||
Direct3D12,
|
|
||||||
#[cfg(all(target_vendor = "apple", feature = "metal"))]
|
|
||||||
#[clap(name = "metal")]
|
|
||||||
Metal,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
enum ReflectionBackend {
|
|
||||||
#[clap(name = "cross")]
|
|
||||||
SpirvCross,
|
|
||||||
#[clap(name = "naga")]
|
|
||||||
Naga,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! get_runtime {
|
|
||||||
($rt:ident, $image:ident) => {
|
|
||||||
match $rt {
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
Runtime::OpenGL3 => &mut librashader_test::render::gl::OpenGl3::new($image.as_path())?,
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
Runtime::OpenGL4 => &mut librashader_test::render::gl::OpenGl4::new($image.as_path())?,
|
|
||||||
#[cfg(feature = "vulkan")]
|
|
||||||
Runtime::Vulkan => &mut librashader_test::render::vk::Vulkan::new($image.as_path())?,
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
Runtime::Wgpu => &mut librashader_test::render::wgpu::Wgpu::new($image.as_path())?,
|
|
||||||
#[cfg(all(windows, feature = "d3d9"))]
|
|
||||||
Runtime::Direct3D9 => {
|
|
||||||
&mut librashader_test::render::d3d9::Direct3D9::new($image.as_path())?
|
|
||||||
}
|
|
||||||
#[cfg(all(windows, feature = "d3d11"))]
|
|
||||||
Runtime::Direct3D11 => {
|
|
||||||
&mut librashader_test::render::d3d11::Direct3D11::new($image.as_path())?
|
|
||||||
}
|
|
||||||
#[cfg(all(windows, feature = "d3d12"))]
|
|
||||||
Runtime::Direct3D12 => {
|
|
||||||
&mut librashader_test::render::d3d12::Direct3D12::new($image.as_path())?
|
|
||||||
}
|
|
||||||
#[cfg(all(target_vendor = "apple", feature = "metal"))]
|
|
||||||
Runtime::Metal => &mut librashader_test::render::mtl::Metal::new($image.as_path())?,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub fn main() -> Result<(), anyhow::Error> {
|
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
match args.command {
|
|
||||||
Commands::Render {
|
|
||||||
preset,
|
|
||||||
render,
|
|
||||||
out,
|
|
||||||
runtime,
|
|
||||||
} => {
|
|
||||||
let PresetArgs { preset, wildcards } = preset;
|
|
||||||
let RenderArgs {
|
|
||||||
frame,
|
|
||||||
dimensions,
|
|
||||||
params,
|
|
||||||
passes_enabled,
|
|
||||||
image,
|
|
||||||
options,
|
|
||||||
} = render;
|
|
||||||
|
|
||||||
let test: &mut dyn RenderTest = get_runtime!(runtime, image);
|
|
||||||
let dimensions = parse_dimension(dimensions, test.image_size())?;
|
|
||||||
let preset = get_shader_preset(preset, wildcards)?;
|
|
||||||
let params = parse_params(params)?;
|
|
||||||
|
|
||||||
let image = test.render_with_preset_and_params(
|
|
||||||
preset,
|
|
||||||
frame,
|
|
||||||
Some(dimensions),
|
|
||||||
Some(&|rp| set_params(rp, ¶ms, passes_enabled)),
|
|
||||||
options.map(CommonFrameOptions::from),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if out.as_path() == Path::new("-") {
|
|
||||||
let out = std::io::stdout();
|
|
||||||
image.write_with_encoder(PngEncoder::new(out))?;
|
|
||||||
} else {
|
|
||||||
image.save(out)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Compare {
|
|
||||||
preset,
|
|
||||||
render,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
out,
|
|
||||||
} => {
|
|
||||||
let PresetArgs { preset, wildcards } = preset;
|
|
||||||
let RenderArgs {
|
|
||||||
frame,
|
|
||||||
dimensions,
|
|
||||||
params,
|
|
||||||
passes_enabled,
|
|
||||||
image,
|
|
||||||
options,
|
|
||||||
} = render;
|
|
||||||
|
|
||||||
let left: &mut dyn RenderTest = get_runtime!(left, image);
|
|
||||||
let right: &mut dyn RenderTest = get_runtime!(right, image);
|
|
||||||
|
|
||||||
let dimensions = parse_dimension(dimensions, left.image_size())?;
|
|
||||||
let params = parse_params(params)?;
|
|
||||||
|
|
||||||
let left_preset = get_shader_preset(preset.clone(), wildcards.clone())?;
|
|
||||||
let left_image = left.render_with_preset_and_params(
|
|
||||||
left_preset,
|
|
||||||
frame,
|
|
||||||
Some(dimensions),
|
|
||||||
Some(&|rp| set_params(rp, ¶ms, passes_enabled)),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let right_preset = get_shader_preset(preset.clone(), wildcards.clone())?;
|
|
||||||
let right_image = right.render_with_preset_and_params(
|
|
||||||
right_preset,
|
|
||||||
frame,
|
|
||||||
Some(dimensions),
|
|
||||||
Some(&|rp| set_params(rp, ¶ms, passes_enabled)),
|
|
||||||
options.map(CommonFrameOptions::from),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let similarity = image_compare::rgba_hybrid_compare(&left_image, &right_image)?;
|
|
||||||
print!("{}", similarity.score);
|
|
||||||
|
|
||||||
if let Some(out) = out {
|
|
||||||
let image = similarity.image.to_color_map();
|
|
||||||
if out.as_path() == Path::new("-") {
|
|
||||||
let out = std::io::stdout();
|
|
||||||
image.write_with_encoder(PngEncoder::new(out))?;
|
|
||||||
} else {
|
|
||||||
image.save(out)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Parse { preset } => {
|
|
||||||
let PresetArgs { preset, wildcards } = preset;
|
|
||||||
|
|
||||||
let preset = get_shader_preset(preset, wildcards)?;
|
|
||||||
let out = serde_json::to_string_pretty(&preset)?;
|
|
||||||
print!("{out:}");
|
|
||||||
}
|
|
||||||
Commands::Preprocess { shader, output } => {
|
|
||||||
let source = librashader::preprocess::ShaderSource::load(&StorageType::Path(shader))?;
|
|
||||||
match output {
|
|
||||||
PreprocessOutput::Fragment => print!("{}", source.fragment),
|
|
||||||
PreprocessOutput::Vertex => print!("{}", source.vertex),
|
|
||||||
PreprocessOutput::Params => {
|
|
||||||
print!("{}", serde_json::to_string_pretty(&source.parameters)?)
|
|
||||||
}
|
|
||||||
PreprocessOutput::Format => print!("{:?}", source.format),
|
|
||||||
PreprocessOutput::Json => print!("{}", serde_json::to_string_pretty(&source)?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Commands::Transpile {
|
|
||||||
shader,
|
|
||||||
stage,
|
|
||||||
format,
|
|
||||||
version,
|
|
||||||
} => {
|
|
||||||
let source = librashader::preprocess::ShaderSource::load(&StorageType::Path(shader))?;
|
|
||||||
let compilation = SpirvCompilation::try_from(&source)?;
|
|
||||||
let output = match format {
|
|
||||||
TranspileFormat::GLSL => {
|
|
||||||
let mut compilation =
|
|
||||||
librashader::reflect::targets::GLSL::from_compilation(compilation)?;
|
|
||||||
compilation.validate()?;
|
|
||||||
|
|
||||||
let version = version
|
|
||||||
.map(|s| parse_glsl_version(&s))
|
|
||||||
.unwrap_or(Ok(GlslVersion::Glsl330))?;
|
|
||||||
|
|
||||||
let output = compilation.compile(version)?;
|
|
||||||
TranspileOutput {
|
|
||||||
vertex: output.vertex,
|
|
||||||
fragment: output.fragment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TranspileFormat::HLSL => {
|
|
||||||
let mut compilation =
|
|
||||||
librashader::reflect::targets::HLSL::from_compilation(compilation)?;
|
|
||||||
compilation.validate()?;
|
|
||||||
|
|
||||||
let shader_model = version
|
|
||||||
.map(|s| parse_hlsl_version(&s))
|
|
||||||
.unwrap_or(Ok(HlslShaderModel::ShaderModel5_0))?;
|
|
||||||
|
|
||||||
let output = compilation.compile(Some(shader_model))?;
|
|
||||||
TranspileOutput {
|
|
||||||
vertex: output.vertex,
|
|
||||||
fragment: output.fragment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TranspileFormat::WGSL => {
|
|
||||||
let mut compilation =
|
|
||||||
librashader::reflect::targets::WGSL::from_compilation(compilation)?;
|
|
||||||
compilation.validate()?;
|
|
||||||
let output = compilation.compile(NagaLoweringOptions {
|
|
||||||
write_pcb_as_ubo: true,
|
|
||||||
sampler_bind_group: 1,
|
|
||||||
})?;
|
|
||||||
TranspileOutput {
|
|
||||||
vertex: output.vertex,
|
|
||||||
fragment: output.fragment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TranspileFormat::MSL => {
|
|
||||||
let mut compilation =
|
|
||||||
<librashader::reflect::targets::MSL as FromCompilation<
|
|
||||||
SpirvCompilation,
|
|
||||||
SpirvCross,
|
|
||||||
>>::from_compilation(compilation)?;
|
|
||||||
compilation.validate()?;
|
|
||||||
|
|
||||||
let version = version
|
|
||||||
.map(|s| parse_msl_version(&s))
|
|
||||||
.unwrap_or(Ok(MslVersion::new(1, 2, 0)))?;
|
|
||||||
|
|
||||||
let output = compilation.compile(Some(version))?;
|
|
||||||
|
|
||||||
TranspileOutput {
|
|
||||||
vertex: output.vertex,
|
|
||||||
fragment: output.fragment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TranspileFormat::SPIRV => {
|
|
||||||
let mut compilation =
|
|
||||||
<librashader::reflect::targets::SPIRV as FromCompilation<
|
|
||||||
SpirvCompilation,
|
|
||||||
SpirvCross,
|
|
||||||
>>::from_compilation(compilation)?;
|
|
||||||
compilation.validate()?;
|
|
||||||
let output = compilation.compile(None)?;
|
|
||||||
|
|
||||||
let raw = version.is_some_and(|s| s == "raw-id");
|
|
||||||
TranspileOutput {
|
|
||||||
vertex: spirv_to_dis(output.vertex, raw)?,
|
|
||||||
fragment: spirv_to_dis(output.fragment, raw)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let print = match stage {
|
|
||||||
TranspileStage::Fragment => output.fragment,
|
|
||||||
TranspileStage::Vertex => output.vertex,
|
|
||||||
};
|
|
||||||
|
|
||||||
print!("{print}")
|
|
||||||
}
|
|
||||||
Commands::Reflect {
|
|
||||||
preset,
|
|
||||||
index,
|
|
||||||
backend,
|
|
||||||
} => {
|
|
||||||
let PresetArgs { preset, wildcards } = preset;
|
|
||||||
|
|
||||||
let preset = get_shader_preset(preset, wildcards)?;
|
|
||||||
let Some(shader) = preset.passes.get(index) else {
|
|
||||||
return Err(anyhow!("Invalid pass index for the preset"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let source = librashader::preprocess::ShaderSource::load(&shader.storage)?;
|
|
||||||
let compilation = SpirvCompilation::try_from(&source)?;
|
|
||||||
|
|
||||||
let semantics =
|
|
||||||
ShaderSemantics::create_pass_semantics::<anyhow::Error>(&preset, index)?;
|
|
||||||
|
|
||||||
let reflection = match backend {
|
|
||||||
ReflectionBackend::SpirvCross => {
|
|
||||||
let mut compilation =
|
|
||||||
<librashader::reflect::targets::SPIRV as FromCompilation<
|
|
||||||
SpirvCompilation,
|
|
||||||
SpirvCross,
|
|
||||||
>>::from_compilation(compilation)?;
|
|
||||||
compilation.reflect(index, &semantics)?
|
|
||||||
}
|
|
||||||
ReflectionBackend::Naga => {
|
|
||||||
let mut compilation =
|
|
||||||
<librashader::reflect::targets::SPIRV as FromCompilation<
|
|
||||||
SpirvCompilation,
|
|
||||||
Naga,
|
|
||||||
>>::from_compilation(compilation)?;
|
|
||||||
compilation.reflect(index, &semantics)?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
print!("{}", serde_json::to_string_pretty(&reflection)?);
|
|
||||||
}
|
|
||||||
Commands::Pack {
|
|
||||||
preset,
|
|
||||||
out,
|
|
||||||
format,
|
|
||||||
} => {
|
|
||||||
let PresetArgs { preset, wildcards } = preset;
|
|
||||||
let preset = get_shader_preset(preset, wildcards)?;
|
|
||||||
let preset = ShaderPresetPack::load_from_preset::<anyhow::Error>(preset)?;
|
|
||||||
let output_bytes = match format {
|
|
||||||
PackFormat::JSON => serde_json::to_vec_pretty(&preset)?,
|
|
||||||
PackFormat::MsgPack => rmp_serde::to_vec(&preset)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
if out.as_path() == Path::new("-") {
|
|
||||||
let mut out = std::io::stdout();
|
|
||||||
out.write_all(output_bytes.as_slice())?;
|
|
||||||
} else {
|
|
||||||
let mut file = File::create(out.as_path())?;
|
|
||||||
file.write_all(output_bytes.as_slice())?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TranspileOutput {
|
|
||||||
vertex: String,
|
|
||||||
fragment: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_shader_preset(
|
|
||||||
preset: PathBuf,
|
|
||||||
wildcards: Option<Vec<String>>,
|
|
||||||
) -> anyhow::Result<ShaderPreset> {
|
|
||||||
let mut context = WildcardContext::new();
|
|
||||||
context.add_path_defaults(preset.as_path());
|
|
||||||
if let Some(wildcards) = wildcards {
|
|
||||||
for string in wildcards {
|
|
||||||
let Some((left, right)) = string.split_once("=") else {
|
|
||||||
return Err(anyhow!("Encountered invalid context string {string}"));
|
|
||||||
};
|
|
||||||
|
|
||||||
context.append_item(ContextItem::ExternContext(
|
|
||||||
left.to_string(),
|
|
||||||
right.to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let preset = ShaderPreset::try_parse_with_context(preset, context)?;
|
|
||||||
Ok(preset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_params(
|
|
||||||
assignments: Option<Vec<String>>,
|
|
||||||
) -> anyhow::Result<Option<FastHashMap<ShortString, f32>>> {
|
|
||||||
let Some(assignments) = assignments else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut map = FastHashMap::default();
|
|
||||||
for string in assignments {
|
|
||||||
let Some((left, right)) = string.split_once("=") else {
|
|
||||||
return Err(anyhow!("Encountered invalid parameter string {string}"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = right
|
|
||||||
.parse::<f32>()
|
|
||||||
.map_err(|_| anyhow!("Encountered invalid parameter value: {right}"))?;
|
|
||||||
|
|
||||||
map.insert(ShortString::from(left), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(map))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_params(
|
|
||||||
params: &RuntimeParameters,
|
|
||||||
assignments: &Option<FastHashMap<ShortString, f32>>,
|
|
||||||
passes_enabled: Option<usize>,
|
|
||||||
) {
|
|
||||||
if let Some(passes_enabled) = passes_enabled {
|
|
||||||
params.set_passes_enabled(passes_enabled)
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(assignments) = assignments else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
params.update_parameters(|params| {
|
|
||||||
for (key, param) in assignments {
|
|
||||||
params.insert(key.clone(), *param);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spirv_to_dis(spirv: Vec<u32>, raw: bool) -> anyhow::Result<String> {
|
|
||||||
let binary = spq_spvasm::SpirvBinary::from(spirv);
|
|
||||||
spq_spvasm::Disassembler::new()
|
|
||||||
.print_header(true)
|
|
||||||
.name_ids(!raw)
|
|
||||||
.name_type_ids(!raw)
|
|
||||||
.name_const_ids(!raw)
|
|
||||||
.indent(true)
|
|
||||||
.disassemble(&binary)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_glsl_version(version_str: &str) -> anyhow::Result<GlslVersion> {
|
|
||||||
if version_str.contains("es") {
|
|
||||||
let Some(version) = version_str.strip_suffix("es").map(|s| s.trim()) else {
|
|
||||||
return Err(anyhow!("Unknown GLSL version"));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(match version {
|
|
||||||
"100" => GlslVersion::Glsl100Es,
|
|
||||||
"300" => GlslVersion::Glsl300Es,
|
|
||||||
"310" => GlslVersion::Glsl310Es,
|
|
||||||
"320" => GlslVersion::Glsl320Es,
|
|
||||||
_ => return Err(anyhow!("Unknown GLSL version")),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(match version_str {
|
|
||||||
"100" => GlslVersion::Glsl100Es,
|
|
||||||
"110" => GlslVersion::Glsl110,
|
|
||||||
"120" => GlslVersion::Glsl120,
|
|
||||||
"130" => GlslVersion::Glsl130,
|
|
||||||
"140" => GlslVersion::Glsl140,
|
|
||||||
"150" => GlslVersion::Glsl150,
|
|
||||||
"300" => GlslVersion::Glsl300Es,
|
|
||||||
"330" => GlslVersion::Glsl330,
|
|
||||||
"310" => GlslVersion::Glsl310Es,
|
|
||||||
"320" => GlslVersion::Glsl320Es,
|
|
||||||
"400" => GlslVersion::Glsl400,
|
|
||||||
"410" => GlslVersion::Glsl410,
|
|
||||||
"420" => GlslVersion::Glsl420,
|
|
||||||
"430" => GlslVersion::Glsl430,
|
|
||||||
"440" => GlslVersion::Glsl440,
|
|
||||||
"450" => GlslVersion::Glsl450,
|
|
||||||
"460" => GlslVersion::Glsl460,
|
|
||||||
_ => return Err(anyhow!("Unknown GLSL version")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn version_to_usize(version_str: &str) -> anyhow::Result<usize> {
|
|
||||||
let version: &str = if version_str.contains("_") {
|
|
||||||
&version_str.replace("_", "")
|
|
||||||
} else if version_str.contains(".") {
|
|
||||||
&version_str.replace(".", "")
|
|
||||||
} else {
|
|
||||||
version_str
|
|
||||||
};
|
|
||||||
|
|
||||||
let version = version
|
|
||||||
.parse::<usize>()
|
|
||||||
.map_err(|_| anyhow!("Invalid version string"))?;
|
|
||||||
Ok(version)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_hlsl_version(version_str: &str) -> anyhow::Result<HlslShaderModel> {
|
|
||||||
let version = version_to_usize(version_str)?;
|
|
||||||
Ok(match version {
|
|
||||||
30 => HlslShaderModel::ShaderModel3_0,
|
|
||||||
40 => HlslShaderModel::ShaderModel4_0,
|
|
||||||
50 => HlslShaderModel::ShaderModel5_0,
|
|
||||||
51 => HlslShaderModel::ShaderModel5_1,
|
|
||||||
60 => HlslShaderModel::ShaderModel6_0,
|
|
||||||
61 => HlslShaderModel::ShaderModel6_1,
|
|
||||||
62 => HlslShaderModel::ShaderModel6_2,
|
|
||||||
63 => HlslShaderModel::ShaderModel6_3,
|
|
||||||
64 => HlslShaderModel::ShaderModel6_4,
|
|
||||||
65 => HlslShaderModel::ShaderModel6_5,
|
|
||||||
66 => HlslShaderModel::ShaderModel6_6,
|
|
||||||
67 => HlslShaderModel::ShaderModel6_7,
|
|
||||||
68 => HlslShaderModel::ShaderModel6_8,
|
|
||||||
_ => return Err(anyhow!("Unknown Shader Model")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_msl_version(version_str: &str) -> anyhow::Result<MslVersion> {
|
|
||||||
let version = version_to_usize(version_str)?;
|
|
||||||
Ok(match version {
|
|
||||||
10 => MslVersion::new(1, 0, 0),
|
|
||||||
11 => MslVersion::new(1, 1, 0),
|
|
||||||
12 => MslVersion::new(1, 2, 0),
|
|
||||||
20 => MslVersion::new(2, 0, 0),
|
|
||||||
21 => MslVersion::new(2, 1, 0),
|
|
||||||
22 => MslVersion::new(2, 2, 0),
|
|
||||||
23 => MslVersion::new(2, 3, 0),
|
|
||||||
24 => MslVersion::new(2, 4, 0),
|
|
||||||
30 => MslVersion::new(3, 0, 0),
|
|
||||||
31 => MslVersion::new(3, 1, 0),
|
|
||||||
32 => MslVersion::new(3, 2, 0),
|
|
||||||
n if n >= 10000 => {
|
|
||||||
let major = n / 10000;
|
|
||||||
let minor = (n - (major * 10000)) / 100;
|
|
||||||
let patch = n - ((major * 10000) + (minor * 100));
|
|
||||||
MslVersion::new(major as u32, minor as u32, patch as u32)
|
|
||||||
}
|
|
||||||
_ => return Err(anyhow!("Unknown MSL version")),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_dimension(dimstr: Option<String>, image_dim: Size<u32>) -> anyhow::Result<Size<u32>> {
|
|
||||||
let Some(dimstr) = dimstr else {
|
|
||||||
return Ok(image_dim);
|
|
||||||
};
|
|
||||||
|
|
||||||
if dimstr.contains("x") {
|
|
||||||
if let Some((Ok(width), Ok(height))) = dimstr
|
|
||||||
.split_once("x")
|
|
||||||
.map(|(width, height)| (width.parse::<u32>(), height.parse::<u32>()))
|
|
||||||
{
|
|
||||||
if width < 1 || height < 1 {
|
|
||||||
return Err(anyhow!("Dimensions must be larger than 1x1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if width > 16384 || height > 16384 {
|
|
||||||
return Err(anyhow!("Dimensions must not be larger than 16384x16384"));
|
|
||||||
}
|
|
||||||
return Ok(Size::new(width, height));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if dimstr.ends_with("%") && dimstr.len() > 1 {
|
|
||||||
if let Ok(percent) = dimstr.trim_end_matches("%").parse::<u32>() {
|
|
||||||
let percent = percent as f32 / 100f32;
|
|
||||||
let width = (image_dim.width as f32 * percent) as u32;
|
|
||||||
let height = (image_dim.height as f32 * percent) as u32;
|
|
||||||
|
|
||||||
if width < 1 || height < 1 {
|
|
||||||
return Err(anyhow!("Dimensions must be larger than 1x1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if width > 16384 || height > 16384 {
|
|
||||||
return Err(anyhow!("Dimensions must not be larger than 16384x16384"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Size { width, height });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(anyhow!(
|
|
||||||
"Invalid dimension syntax, must either in form WIDTHxHEIGHT or SCALE%"
|
|
||||||
))
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
/// Render tests
|
|
||||||
pub mod render;
|
|
|
@ -1,258 +0,0 @@
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::runtime::d3d11::*;
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use std::io::{Cursor, Write};
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
impl RenderTest for Direct3D11 {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Direct3D11::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.image_bytes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
let output_size = output_size.unwrap_or(self.image_bytes.size);
|
|
||||||
let (renderbuffer, rtv) = self.create_renderbuffer(output_size)?;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut filter_chain = FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&self.device,
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
disable_cache: false,
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(&rtv, None)?;
|
|
||||||
let options = frame_options.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
});
|
|
||||||
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
filter_chain.frame(None, &self.image_srv, &viewport, frame, options.as_ref())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut renderbuffer_desc = Default::default();
|
|
||||||
self.immediate_context.Flush();
|
|
||||||
renderbuffer.GetDesc(&mut renderbuffer_desc);
|
|
||||||
|
|
||||||
eprintln!("{:?}", renderbuffer_desc);
|
|
||||||
let mut staging = None;
|
|
||||||
self.device.CreateTexture2D(
|
|
||||||
&D3D11_TEXTURE2D_DESC {
|
|
||||||
MipLevels: 1,
|
|
||||||
BindFlags: 0,
|
|
||||||
MiscFlags: 0,
|
|
||||||
Usage: D3D11_USAGE_STAGING,
|
|
||||||
CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
|
|
||||||
..renderbuffer_desc
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
Some(&mut staging),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let staging = staging.ok_or(anyhow!("Unable to create staging texture"))?;
|
|
||||||
|
|
||||||
self.immediate_context.CopyResource(&staging, &renderbuffer);
|
|
||||||
|
|
||||||
let mut pixels: Vec<u8> = Vec::new();
|
|
||||||
|
|
||||||
let mut map_info = Default::default();
|
|
||||||
self.immediate_context
|
|
||||||
.Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut map_info))?;
|
|
||||||
|
|
||||||
let slice = std::slice::from_raw_parts(
|
|
||||||
map_info.pData as *const u8,
|
|
||||||
(renderbuffer_desc.Height * map_info.RowPitch) as usize,
|
|
||||||
);
|
|
||||||
pixels.resize(
|
|
||||||
(renderbuffer_desc.Height * renderbuffer_desc.Width * 4) as usize,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut cursor = Cursor::new(pixels.deref_mut());
|
|
||||||
for chunk in slice.chunks(map_info.RowPitch as usize) {
|
|
||||||
cursor.write_all(&chunk[..(renderbuffer_desc.Width * 4) as usize])?
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = RgbaImage::from_raw(output_size.width, output_size.height, pixels)
|
|
||||||
.ok_or(anyhow!("Unable to create image from data"))?;
|
|
||||||
self.immediate_context.Unmap(&staging, 0);
|
|
||||||
|
|
||||||
Ok(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader_runtime::image::{Image, UVDirection};
|
|
||||||
use windows::{
|
|
||||||
Win32::Foundation::*, Win32::Graphics::Direct3D::*, Win32::Graphics::Direct3D11::*,
|
|
||||||
Win32::Graphics::Dxgi::Common::*, Win32::Graphics::Dxgi::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Direct3D11 {
|
|
||||||
device: ID3D11Device,
|
|
||||||
immediate_context: ID3D11DeviceContext,
|
|
||||||
_image_tex: ID3D11Texture2D,
|
|
||||||
image_srv: ID3D11ShaderResourceView,
|
|
||||||
image_bytes: Image,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direct3D11 {
|
|
||||||
fn create_device() -> anyhow::Result<(IDXGIFactory4, ID3D11Device, ID3D11DeviceContext)> {
|
|
||||||
let dxgi_factory: IDXGIFactory4 = unsafe { CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG) }?;
|
|
||||||
let feature_levels = vec![D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1];
|
|
||||||
|
|
||||||
let mut out_device = None;
|
|
||||||
let mut out_context = None;
|
|
||||||
let mut _out_feature_level = D3D_FEATURE_LEVEL_11_0;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
D3D11CreateDevice(
|
|
||||||
None,
|
|
||||||
D3D_DRIVER_TYPE_HARDWARE,
|
|
||||||
HMODULE::default(),
|
|
||||||
D3D11_CREATE_DEVICE_DEBUG,
|
|
||||||
Some(&feature_levels),
|
|
||||||
D3D11_SDK_VERSION,
|
|
||||||
Some(&mut out_device),
|
|
||||||
Some(&mut _out_feature_level),
|
|
||||||
Some(&mut out_context),
|
|
||||||
)
|
|
||||||
}?;
|
|
||||||
Ok((dxgi_factory, out_device.unwrap(), out_context.unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
|
|
||||||
let (_factory, device, imm_context) = Self::create_device()?;
|
|
||||||
|
|
||||||
let (image, image_tex, srv) = Self::load_image(&device, image_path)?;
|
|
||||||
Ok(Self {
|
|
||||||
device,
|
|
||||||
immediate_context: imm_context,
|
|
||||||
image_bytes: image,
|
|
||||||
_image_tex: image_tex,
|
|
||||||
image_srv: srv,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_image(
|
|
||||||
device: &ID3D11Device,
|
|
||||||
image_path: &Path,
|
|
||||||
) -> anyhow::Result<(Image, ID3D11Texture2D, ID3D11ShaderResourceView)> {
|
|
||||||
let image = Image::load(image_path, UVDirection::TopLeft)?;
|
|
||||||
let desc = D3D11_TEXTURE2D_DESC {
|
|
||||||
Width: image.size.width,
|
|
||||||
Height: image.size.height,
|
|
||||||
// todo: set this to 0
|
|
||||||
MipLevels: 1,
|
|
||||||
ArraySize: 1,
|
|
||||||
SampleDesc: DXGI_SAMPLE_DESC {
|
|
||||||
Count: 1,
|
|
||||||
Quality: 0,
|
|
||||||
},
|
|
||||||
CPUAccessFlags: 0,
|
|
||||||
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
||||||
Usage: D3D11_USAGE_DEFAULT,
|
|
||||||
BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut resource = None;
|
|
||||||
device.CreateTexture2D(
|
|
||||||
&desc,
|
|
||||||
Some(&D3D11_SUBRESOURCE_DATA {
|
|
||||||
pSysMem: image.bytes.as_ptr().cast(),
|
|
||||||
SysMemPitch: image.pitch as u32,
|
|
||||||
SysMemSlicePitch: 0,
|
|
||||||
}),
|
|
||||||
Some(&mut resource),
|
|
||||||
)?;
|
|
||||||
let resource = resource.ok_or(anyhow!("Failed to create texture"))?;
|
|
||||||
|
|
||||||
let mut srv = None;
|
|
||||||
device.CreateShaderResourceView(
|
|
||||||
&resource,
|
|
||||||
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),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let srv = srv.ok_or(anyhow!("Failed to create SRV"))?;
|
|
||||||
|
|
||||||
Ok((image, resource, srv))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_renderbuffer(
|
|
||||||
&self,
|
|
||||||
size: Size<u32>,
|
|
||||||
) -> anyhow::Result<(ID3D11Texture2D, ID3D11RenderTargetView)> {
|
|
||||||
let desc = D3D11_TEXTURE2D_DESC {
|
|
||||||
Width: size.width,
|
|
||||||
Height: size.height,
|
|
||||||
// todo: set this to 0
|
|
||||||
MipLevels: 1,
|
|
||||||
ArraySize: 1,
|
|
||||||
SampleDesc: DXGI_SAMPLE_DESC {
|
|
||||||
Count: 1,
|
|
||||||
Quality: 0,
|
|
||||||
},
|
|
||||||
CPUAccessFlags: 0,
|
|
||||||
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
||||||
Usage: D3D11_USAGE_DEFAULT,
|
|
||||||
BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut resource = None;
|
|
||||||
self.device
|
|
||||||
.CreateTexture2D(&desc, None, Some(&mut resource))?;
|
|
||||||
let resource = resource.ok_or(anyhow!("Failed to create texture"))?;
|
|
||||||
|
|
||||||
let mut rtv = None;
|
|
||||||
self.device
|
|
||||||
.CreateRenderTargetView(&resource, None, Some(&mut rtv))?;
|
|
||||||
|
|
||||||
let rtv = rtv.ok_or(anyhow!("Failed to create RTV"))?;
|
|
||||||
|
|
||||||
Ok((resource, rtv))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
use d3d12_descriptor_heap::D3D12DescriptorHeapType;
|
|
||||||
use windows::Win32::Graphics::Direct3D12::{
|
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC, D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
|
|
||||||
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct CpuStagingHeap;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RenderTargetHeap;
|
|
||||||
|
|
||||||
impl D3D12DescriptorHeapType for RenderTargetHeap {
|
|
||||||
// Lut texture heaps are CPU only and get bound to the descriptor heap of the shader.
|
|
||||||
fn create_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC {
|
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC {
|
|
||||||
Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
|
|
||||||
NumDescriptors: size as u32,
|
|
||||||
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
|
|
||||||
NodeMask: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl D3D12DescriptorHeapType for CpuStagingHeap {
|
|
||||||
// Lut texture heaps are CPU only and get bound to the descriptor heap of the shader.
|
|
||||||
fn create_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC {
|
|
||||||
D3D12_DESCRIPTOR_HEAP_DESC {
|
|
||||||
Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
|
|
||||||
NumDescriptors: size as u32,
|
|
||||||
Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
|
|
||||||
NodeMask: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,426 +0,0 @@
|
||||||
mod descriptor_heap;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
use crate::render::d3d12::descriptor_heap::{CpuStagingHeap, RenderTargetHeap};
|
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot};
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::d3d12::{D3D12OutputView, FilterChain, FilterChainOptions, FrameOptions};
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8};
|
|
||||||
use std::path::Path;
|
|
||||||
use windows::core::Interface;
|
|
||||||
use windows::Win32::Foundation::CloseHandle;
|
|
||||||
use windows::Win32::Graphics::Direct3D::{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_12_1};
|
|
||||||
use windows::Win32::Graphics::Direct3D12::{
|
|
||||||
D3D12CreateDevice, ID3D12CommandAllocator, ID3D12CommandQueue, ID3D12Device, ID3D12Fence,
|
|
||||||
ID3D12GraphicsCommandList, ID3D12Resource, D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
D3D12_COMMAND_QUEUE_DESC, D3D12_COMMAND_QUEUE_FLAG_NONE, D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
|
||||||
D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
|
|
||||||
D3D12_FENCE_FLAG_NONE, D3D12_HEAP_FLAG_NONE, D3D12_HEAP_PROPERTIES, D3D12_HEAP_TYPE_CUSTOM,
|
|
||||||
D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_TYPE_UPLOAD, D3D12_MEMORY_POOL_L0,
|
|
||||||
D3D12_MEMORY_POOL_UNKNOWN, D3D12_PLACED_SUBRESOURCE_FOOTPRINT, D3D12_RESOURCE_DESC,
|
|
||||||
D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
|
||||||
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_COMMON,
|
|
||||||
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ,
|
|
||||||
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
||||||
D3D12_SHADER_RESOURCE_VIEW_DESC, D3D12_SHADER_RESOURCE_VIEW_DESC_0,
|
|
||||||
D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_SUBRESOURCE_DATA, D3D12_TEX2D_SRV,
|
|
||||||
D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
|
|
||||||
};
|
|
||||||
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC};
|
|
||||||
use windows::Win32::Graphics::Dxgi::{
|
|
||||||
CreateDXGIFactory2, IDXGIAdapter1, IDXGIFactory4, DXGI_ADAPTER_FLAG_NONE,
|
|
||||||
DXGI_ADAPTER_FLAG_SOFTWARE, DXGI_CREATE_FACTORY_DEBUG,
|
|
||||||
};
|
|
||||||
use windows::Win32::System::Threading::{CreateEventA, WaitForSingleObject, INFINITE};
|
|
||||||
|
|
||||||
pub struct Direct3D12 {
|
|
||||||
device: ID3D12Device,
|
|
||||||
_cpu_heap: D3D12DescriptorHeap<CpuStagingHeap>,
|
|
||||||
rtv_heap: D3D12DescriptorHeap<RenderTargetHeap>,
|
|
||||||
|
|
||||||
texture: ID3D12Resource,
|
|
||||||
_heap_slot: D3D12DescriptorHeapSlot<CpuStagingHeap>,
|
|
||||||
command_pool: ID3D12CommandAllocator,
|
|
||||||
queue: ID3D12CommandQueue,
|
|
||||||
image: Image<BGRA8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTest for Direct3D12 {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Direct3D12::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.image.size
|
|
||||||
}
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
unsafe {
|
|
||||||
let descriptor = self.rtv_heap.allocate_descriptor()?;
|
|
||||||
|
|
||||||
let cmd: ID3D12GraphicsCommandList = self.device.CreateCommandList(
|
|
||||||
0,
|
|
||||||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
&self.command_pool,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let fence_event = CreateEventA(None, false, false, None)?;
|
|
||||||
let fence: ID3D12Fence = self.device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?;
|
|
||||||
|
|
||||||
let mut filter_chain = FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&self.device,
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
force_hlsl_pipeline: false,
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
disable_cache: false,
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_size = output_size.unwrap_or(self.image.size);
|
|
||||||
let mut output_texture = None;
|
|
||||||
let desc = D3D12_RESOURCE_DESC {
|
|
||||||
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
|
||||||
Alignment: 0,
|
|
||||||
Width: output_size.width as u64,
|
|
||||||
Height: output_size.height,
|
|
||||||
DepthOrArraySize: 1,
|
|
||||||
MipLevels: 1,
|
|
||||||
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
|
|
||||||
SampleDesc: DXGI_SAMPLE_DESC {
|
|
||||||
Count: 1,
|
|
||||||
Quality: 0,
|
|
||||||
},
|
|
||||||
Layout: Default::default(),
|
|
||||||
Flags: D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We only need to draw one frame, so don't worry about the performance impact
|
|
||||||
// of this heap.
|
|
||||||
self.device.CreateCommittedResource(
|
|
||||||
&D3D12_HEAP_PROPERTIES {
|
|
||||||
Type: D3D12_HEAP_TYPE_CUSTOM,
|
|
||||||
CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
|
|
||||||
MemoryPoolPreference: D3D12_MEMORY_POOL_L0,
|
|
||||||
CreationNodeMask: 1,
|
|
||||||
VisibleNodeMask: 1,
|
|
||||||
},
|
|
||||||
D3D12_HEAP_FLAG_NONE,
|
|
||||||
&desc,
|
|
||||||
D3D12_RESOURCE_STATE_COMMON | D3D12_RESOURCE_STATE_RENDER_TARGET,
|
|
||||||
None,
|
|
||||||
&mut output_texture,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let output_texture: ID3D12Resource =
|
|
||||||
output_texture.ok_or_else(|| anyhow!("Failed to allocate resource"))?;
|
|
||||||
|
|
||||||
self.device
|
|
||||||
.CreateRenderTargetView(&output_texture, None, *descriptor.as_ref());
|
|
||||||
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(
|
|
||||||
D3D12OutputView::new_from_raw(
|
|
||||||
*descriptor.as_ref(),
|
|
||||||
output_size,
|
|
||||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let options = frame_options.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
});
|
|
||||||
|
|
||||||
let image = self.texture.to_ref();
|
|
||||||
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
filter_chain.frame(&cmd, image.into(), &viewport, frame, options.as_ref())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Close()?;
|
|
||||||
self.queue.ExecuteCommandLists(&[Some(cmd.cast()?)]);
|
|
||||||
self.queue.Signal(&fence, 1)?;
|
|
||||||
|
|
||||||
if fence.GetCompletedValue() < 1 {
|
|
||||||
fence.SetEventOnCompletion(1, fence_event)?;
|
|
||||||
WaitForSingleObject(fence_event, INFINITE);
|
|
||||||
CloseHandle(fence_event)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buffer = vec![0u8; (output_size.height * output_size.width) as usize * 4];
|
|
||||||
|
|
||||||
output_texture.ReadFromSubresource(
|
|
||||||
buffer.as_mut_ptr().cast(),
|
|
||||||
4 * output_size.width,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
BGRA8::convert(&mut buffer);
|
|
||||||
|
|
||||||
let image =
|
|
||||||
RgbaImage::from_raw(output_size.width, output_size.height, Vec::from(buffer))
|
|
||||||
.ok_or(anyhow!("Unable to create image from data"))?;
|
|
||||||
|
|
||||||
Ok(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direct3D12 {
|
|
||||||
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
|
|
||||||
let device = Self::create_device()?;
|
|
||||||
let mut heap = unsafe { D3D12DescriptorHeap::new(&device, 8)? };
|
|
||||||
let rtv_heap = unsafe { D3D12DescriptorHeap::new(&device, 16)? };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let command_pool: ID3D12CommandAllocator =
|
|
||||||
device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)?;
|
|
||||||
|
|
||||||
let queue: ID3D12CommandQueue =
|
|
||||||
device.CreateCommandQueue(&D3D12_COMMAND_QUEUE_DESC {
|
|
||||||
Type: D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
Priority: 0,
|
|
||||||
Flags: D3D12_COMMAND_QUEUE_FLAG_NONE,
|
|
||||||
NodeMask: 0,
|
|
||||||
})?;
|
|
||||||
let (image, texture, heap_slot) =
|
|
||||||
Self::load_image(&device, &command_pool, &queue, &mut heap, image_path)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
device,
|
|
||||||
_cpu_heap: heap,
|
|
||||||
rtv_heap,
|
|
||||||
texture,
|
|
||||||
_heap_slot: heap_slot,
|
|
||||||
command_pool,
|
|
||||||
image,
|
|
||||||
queue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_device() -> anyhow::Result<ID3D12Device> {
|
|
||||||
let dxgi_factory_flags = DXGI_CREATE_FACTORY_DEBUG;
|
|
||||||
let dxgi_factory: IDXGIFactory4 = unsafe { CreateDXGIFactory2(dxgi_factory_flags) }?;
|
|
||||||
|
|
||||||
let adapter = Self::get_hardware_adapter(&dxgi_factory)?;
|
|
||||||
|
|
||||||
let mut device: Option<ID3D12Device> = None;
|
|
||||||
unsafe { D3D12CreateDevice(&adapter, D3D_FEATURE_LEVEL_12_1, &mut device) }?;
|
|
||||||
let device = device.ok_or(anyhow!("Failed to initialize D3D12 device"))?;
|
|
||||||
|
|
||||||
Ok(device)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_image(
|
|
||||||
device: &ID3D12Device,
|
|
||||||
command_pool: &ID3D12CommandAllocator,
|
|
||||||
queue: &ID3D12CommandQueue,
|
|
||||||
heap: &mut D3D12DescriptorHeap<CpuStagingHeap>,
|
|
||||||
path: &Path,
|
|
||||||
) -> anyhow::Result<(
|
|
||||||
Image<BGRA8>,
|
|
||||||
ID3D12Resource,
|
|
||||||
D3D12DescriptorHeapSlot<CpuStagingHeap>,
|
|
||||||
)> {
|
|
||||||
// 1 time queue infrastructure for lut uploads
|
|
||||||
let image: Image<BGRA8> = Image::load(path, UVDirection::TopLeft)?;
|
|
||||||
|
|
||||||
let desc = D3D12_RESOURCE_DESC {
|
|
||||||
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
|
|
||||||
Alignment: 0,
|
|
||||||
Width: image.size.width as u64,
|
|
||||||
Height: image.size.height,
|
|
||||||
DepthOrArraySize: 1,
|
|
||||||
MipLevels: 1,
|
|
||||||
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
|
|
||||||
SampleDesc: DXGI_SAMPLE_DESC {
|
|
||||||
Count: 1,
|
|
||||||
Quality: 0,
|
|
||||||
},
|
|
||||||
Layout: Default::default(),
|
|
||||||
Flags: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let descriptor = heap.allocate_descriptor()?;
|
|
||||||
|
|
||||||
// create handles on GPU
|
|
||||||
let mut resource: Option<ID3D12Resource> = None;
|
|
||||||
unsafe {
|
|
||||||
let cmd: ID3D12GraphicsCommandList = device.CreateCommandList(
|
|
||||||
0,
|
|
||||||
D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
&command_pool.clone(),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
let fence_event = CreateEventA(None, false, false, None)?;
|
|
||||||
let fence: ID3D12Fence = device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?;
|
|
||||||
|
|
||||||
device.CreateCommittedResource(
|
|
||||||
&D3D12_HEAP_PROPERTIES {
|
|
||||||
Type: D3D12_HEAP_TYPE_DEFAULT,
|
|
||||||
CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
|
||||||
MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
|
|
||||||
CreationNodeMask: 1,
|
|
||||||
VisibleNodeMask: 1,
|
|
||||||
},
|
|
||||||
D3D12_HEAP_FLAG_NONE,
|
|
||||||
&desc,
|
|
||||||
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
||||||
None,
|
|
||||||
&mut resource,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let resource = resource.ok_or_else(|| anyhow!("Failed to allocate resource"))?;
|
|
||||||
let srv_desc = D3D12_SHADER_RESOURCE_VIEW_DESC {
|
|
||||||
Format: desc.Format,
|
|
||||||
ViewDimension: D3D12_SRV_DIMENSION_TEXTURE2D,
|
|
||||||
Shader4ComponentMapping: D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
|
|
||||||
Anonymous: D3D12_SHADER_RESOURCE_VIEW_DESC_0 {
|
|
||||||
Texture2D: D3D12_TEX2D_SRV {
|
|
||||||
MipLevels: u32::MAX,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
device.CreateShaderResourceView(&resource, Some(&srv_desc), *descriptor.as_ref());
|
|
||||||
|
|
||||||
let mut buffer_desc = D3D12_RESOURCE_DESC {
|
|
||||||
Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut layout = D3D12_PLACED_SUBRESOURCE_FOOTPRINT::default();
|
|
||||||
let mut total = 0;
|
|
||||||
// texture upload
|
|
||||||
device.GetCopyableFootprints(
|
|
||||||
&desc,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
Some(&mut layout),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(&mut total),
|
|
||||||
);
|
|
||||||
|
|
||||||
buffer_desc.Width = total;
|
|
||||||
buffer_desc.Height = 1;
|
|
||||||
buffer_desc.DepthOrArraySize = 1;
|
|
||||||
buffer_desc.MipLevels = 1;
|
|
||||||
buffer_desc.SampleDesc.Count = 1;
|
|
||||||
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
|
||||||
|
|
||||||
let mut upload: Option<ID3D12Resource> = None;
|
|
||||||
|
|
||||||
device.CreateCommittedResource(
|
|
||||||
&D3D12_HEAP_PROPERTIES {
|
|
||||||
Type: D3D12_HEAP_TYPE_UPLOAD,
|
|
||||||
CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
|
||||||
MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
|
|
||||||
CreationNodeMask: 1,
|
|
||||||
VisibleNodeMask: 1,
|
|
||||||
},
|
|
||||||
D3D12_HEAP_FLAG_NONE,
|
|
||||||
&buffer_desc,
|
|
||||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
|
||||||
None,
|
|
||||||
&mut upload,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let upload = upload.ok_or_else(|| anyhow!("Failed to allocate copy texture"))?;
|
|
||||||
|
|
||||||
let subresource = [D3D12_SUBRESOURCE_DATA {
|
|
||||||
pData: image.bytes.as_ptr().cast(),
|
|
||||||
RowPitch: 4 * image.size.width as isize,
|
|
||||||
SlicePitch: (4 * image.size.width * image.size.height) as isize,
|
|
||||||
}];
|
|
||||||
|
|
||||||
util::d3d12_resource_transition(
|
|
||||||
&cmd,
|
|
||||||
&resource,
|
|
||||||
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
||||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
||||||
);
|
|
||||||
|
|
||||||
util::d3d12_update_subresources(&cmd, &resource, &upload, 0, 0, 1, &subresource)?;
|
|
||||||
|
|
||||||
util::d3d12_resource_transition(
|
|
||||||
&cmd,
|
|
||||||
&resource,
|
|
||||||
D3D12_RESOURCE_STATE_COPY_DEST,
|
|
||||||
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
|
||||||
);
|
|
||||||
|
|
||||||
cmd.Close()?;
|
|
||||||
queue.ExecuteCommandLists(&[Some(cmd.cast()?)]);
|
|
||||||
queue.Signal(&fence, 1)?;
|
|
||||||
|
|
||||||
if fence.GetCompletedValue() < 1 {
|
|
||||||
fence.SetEventOnCompletion(1, fence_event)?;
|
|
||||||
WaitForSingleObject(fence_event, INFINITE);
|
|
||||||
CloseHandle(fence_event)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((image, resource, descriptor))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hardware_adapter(factory: &IDXGIFactory4) -> windows::core::Result<IDXGIAdapter1> {
|
|
||||||
for i in 0.. {
|
|
||||||
let adapter = unsafe { factory.EnumAdapters1(i)? };
|
|
||||||
|
|
||||||
let desc = unsafe { adapter.GetDesc1()? };
|
|
||||||
|
|
||||||
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != DXGI_ADAPTER_FLAG_NONE.0 as u32
|
|
||||||
{
|
|
||||||
// Don't select the Basic Render Driver adapter. If you want a
|
|
||||||
// software adapter, pass in "/warp" on the command line.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see whether the adapter supports Direct3D 12, but don't
|
|
||||||
// create the actual device yet.
|
|
||||||
if unsafe {
|
|
||||||
D3D12CreateDevice(
|
|
||||||
&adapter,
|
|
||||||
D3D_FEATURE_LEVEL_11_0,
|
|
||||||
std::ptr::null_mut::<Option<ID3D12Device>>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
return Ok(adapter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to warp
|
|
||||||
unsafe { factory.EnumWarpAdapter() }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
use anyhow::anyhow;
|
|
||||||
use std::mem::ManuallyDrop;
|
|
||||||
use windows::Win32::Graphics::Direct3D12::{
|
|
||||||
ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_MEMCPY_DEST,
|
|
||||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_0,
|
|
||||||
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
|
||||||
D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_STATES,
|
|
||||||
D3D12_RESOURCE_TRANSITION_BARRIER, D3D12_SUBRESOURCE_DATA, D3D12_TEXTURE_COPY_LOCATION,
|
|
||||||
D3D12_TEXTURE_COPY_LOCATION_0, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
|
|
||||||
D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn d3d12_resource_transition(
|
|
||||||
cmd: &ID3D12GraphicsCommandList,
|
|
||||||
resource: &ID3D12Resource,
|
|
||||||
before: D3D12_RESOURCE_STATES,
|
|
||||||
after: D3D12_RESOURCE_STATES,
|
|
||||||
) {
|
|
||||||
d3d12_resource_transition_subresource(
|
|
||||||
cmd,
|
|
||||||
resource,
|
|
||||||
before,
|
|
||||||
after,
|
|
||||||
D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn d3d12_get_resource_transition_subresource(
|
|
||||||
resource: &ID3D12Resource,
|
|
||||||
before: D3D12_RESOURCE_STATES,
|
|
||||||
after: D3D12_RESOURCE_STATES,
|
|
||||||
subresource: u32,
|
|
||||||
) -> D3D12_RESOURCE_BARRIER {
|
|
||||||
D3D12_RESOURCE_BARRIER {
|
|
||||||
Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
|
||||||
Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
|
||||||
Anonymous: D3D12_RESOURCE_BARRIER_0 {
|
|
||||||
Transition: ManuallyDrop::new(D3D12_RESOURCE_TRANSITION_BARRIER {
|
|
||||||
pResource: ManuallyDrop::new(Some(resource.clone())),
|
|
||||||
Subresource: subresource,
|
|
||||||
StateBefore: before,
|
|
||||||
StateAfter: after,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn d3d12_resource_transition_subresource(
|
|
||||||
cmd: &ID3D12GraphicsCommandList,
|
|
||||||
resource: &ID3D12Resource,
|
|
||||||
before: D3D12_RESOURCE_STATES,
|
|
||||||
after: D3D12_RESOURCE_STATES,
|
|
||||||
subresource: u32,
|
|
||||||
) {
|
|
||||||
let barrier = [d3d12_get_resource_transition_subresource(
|
|
||||||
resource,
|
|
||||||
before,
|
|
||||||
after,
|
|
||||||
subresource,
|
|
||||||
)];
|
|
||||||
unsafe { cmd.ResourceBarrier(&barrier) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn d3d12_update_subresources(
|
|
||||||
cmd: &ID3D12GraphicsCommandList,
|
|
||||||
destination_resource: &ID3D12Resource,
|
|
||||||
intermediate_resource: &ID3D12Resource,
|
|
||||||
intermediate_offset: u64,
|
|
||||||
first_subresouce: u32,
|
|
||||||
num_subresources: u32,
|
|
||||||
source: &[D3D12_SUBRESOURCE_DATA],
|
|
||||||
) -> anyhow::Result<u64> {
|
|
||||||
// let allocation_size = std::mem::size_of::<D3D12_PLACED_SUBRESOURCE_FOOTPRINT>()
|
|
||||||
// + std::mem::size_of::<u32>()
|
|
||||||
// + std::mem::size_of::<u64>() * num_subresources;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let destination_desc = destination_resource.GetDesc();
|
|
||||||
let mut device: Option<ID3D12Device> = None;
|
|
||||||
destination_resource.GetDevice(&mut device)?;
|
|
||||||
let device = device.ok_or_else(|| anyhow!("Unable to get device"))?;
|
|
||||||
|
|
||||||
let mut layouts =
|
|
||||||
vec![D3D12_PLACED_SUBRESOURCE_FOOTPRINT::default(); num_subresources as usize];
|
|
||||||
let mut num_rows = vec![0; num_subresources as usize];
|
|
||||||
let mut row_sizes_in_bytes = vec![0; num_subresources as usize];
|
|
||||||
let mut required_size = 0;
|
|
||||||
|
|
||||||
// texture upload
|
|
||||||
device.GetCopyableFootprints(
|
|
||||||
&destination_desc,
|
|
||||||
first_subresouce,
|
|
||||||
num_subresources,
|
|
||||||
intermediate_offset,
|
|
||||||
Some(layouts.as_mut_ptr()),
|
|
||||||
Some(num_rows.as_mut_ptr()),
|
|
||||||
Some(row_sizes_in_bytes.as_mut_ptr()),
|
|
||||||
Some(&mut required_size),
|
|
||||||
);
|
|
||||||
|
|
||||||
update_subresources(
|
|
||||||
cmd,
|
|
||||||
destination_resource,
|
|
||||||
intermediate_resource,
|
|
||||||
first_subresouce,
|
|
||||||
num_subresources,
|
|
||||||
required_size,
|
|
||||||
&layouts,
|
|
||||||
&num_rows,
|
|
||||||
&row_sizes_in_bytes,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn update_subresources(
|
|
||||||
cmd: &ID3D12GraphicsCommandList,
|
|
||||||
destination_resource: &ID3D12Resource,
|
|
||||||
intermediate_resource: &ID3D12Resource,
|
|
||||||
first_subresouce: u32,
|
|
||||||
num_subresources: u32,
|
|
||||||
required_size: u64,
|
|
||||||
layouts: &[D3D12_PLACED_SUBRESOURCE_FOOTPRINT],
|
|
||||||
num_rows: &[u32],
|
|
||||||
row_sizes_in_bytes: &[u64],
|
|
||||||
source_data: &[D3D12_SUBRESOURCE_DATA],
|
|
||||||
) -> anyhow::Result<u64> {
|
|
||||||
// ToDo: implement validation as in the original function
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut data = std::ptr::null_mut();
|
|
||||||
intermediate_resource.Map(0, None, Some(&mut data))?;
|
|
||||||
|
|
||||||
for i in 0..num_subresources as usize {
|
|
||||||
let dest_data = D3D12_MEMCPY_DEST {
|
|
||||||
pData: data.offset(layouts[i].Offset as isize) as *mut std::ffi::c_void,
|
|
||||||
RowPitch: layouts[i].Footprint.RowPitch as usize,
|
|
||||||
SlicePitch: ((layouts[i].Footprint.RowPitch) * num_rows[i]) as usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
memcpy_subresource(
|
|
||||||
&dest_data,
|
|
||||||
&source_data[i],
|
|
||||||
row_sizes_in_bytes[i],
|
|
||||||
num_rows[i],
|
|
||||||
layouts[i].Footprint.Depth,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
intermediate_resource.Unmap(0, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let destination_desc = destination_resource.GetDesc();
|
|
||||||
if destination_desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER {
|
|
||||||
cmd.CopyBufferRegion(
|
|
||||||
destination_resource,
|
|
||||||
0,
|
|
||||||
intermediate_resource,
|
|
||||||
layouts[0].Offset,
|
|
||||||
layouts[0].Footprint.Width as u64,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
for i in 0..num_subresources as usize {
|
|
||||||
let dest_location = D3D12_TEXTURE_COPY_LOCATION {
|
|
||||||
pResource: ManuallyDrop::new(Some(destination_resource.clone())),
|
|
||||||
Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
|
|
||||||
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
|
|
||||||
SubresourceIndex: i as u32 + first_subresouce,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let source_location = D3D12_TEXTURE_COPY_LOCATION {
|
|
||||||
pResource: ManuallyDrop::new(Some(intermediate_resource.clone())),
|
|
||||||
Type: D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
|
|
||||||
Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 {
|
|
||||||
PlacedFootprint: layouts[i],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
cmd.CopyTextureRegion(&dest_location, 0, 0, 0, &source_location, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(required_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function should not leak to the public API, so
|
|
||||||
// there is no point in using struct wrappers
|
|
||||||
unsafe fn memcpy_subresource(
|
|
||||||
dest: &D3D12_MEMCPY_DEST,
|
|
||||||
src: &D3D12_SUBRESOURCE_DATA,
|
|
||||||
row_sizes_in_bytes: u64,
|
|
||||||
num_rows: u32,
|
|
||||||
num_slices: u32,
|
|
||||||
) {
|
|
||||||
unsafe {
|
|
||||||
for z in 0..num_slices as usize {
|
|
||||||
let dest_slice = dest.pData.add(dest.SlicePitch * z);
|
|
||||||
let src_slice = src.pData.offset(src.SlicePitch * z as isize);
|
|
||||||
|
|
||||||
for y in 0..num_rows as usize {
|
|
||||||
std::ptr::copy_nonoverlapping(
|
|
||||||
src_slice.offset(src.RowPitch * y as isize),
|
|
||||||
dest_slice.add(dest.RowPitch * y),
|
|
||||||
row_sizes_in_bytes as usize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,193 +0,0 @@
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::d3d9::{FilterChain, FilterChainOptions, FrameOptions};
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8};
|
|
||||||
use std::path::Path;
|
|
||||||
use windows::Win32::Foundation::{HWND, TRUE};
|
|
||||||
use windows::Win32::Graphics::Direct3D9::{
|
|
||||||
Direct3DCreate9, IDirect3D9, IDirect3DDevice9, IDirect3DTexture9, D3DADAPTER_DEFAULT,
|
|
||||||
D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DLOCKED_RECT,
|
|
||||||
D3DPOOL_DEFAULT, D3DPOOL_MANAGED, D3DPOOL_SYSTEMMEM, D3DPRESENT_INTERVAL_IMMEDIATE,
|
|
||||||
D3DPRESENT_PARAMETERS, D3DSURFACE_DESC, D3DSWAPEFFECT_DISCARD, D3DUSAGE_RENDERTARGET,
|
|
||||||
D3D_SDK_VERSION,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Direct3D9 {
|
|
||||||
pub texture: IDirect3DTexture9,
|
|
||||||
pub image: Image<BGRA8>,
|
|
||||||
pub direct3d: IDirect3D9,
|
|
||||||
pub device: IDirect3DDevice9,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTest for Direct3D9 {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Direct3D9::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.image.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
unsafe {
|
|
||||||
let mut filter_chain = FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&self.device,
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
disable_cache: false,
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut render_texture = None;
|
|
||||||
|
|
||||||
let output_size = output_size.unwrap_or(self.image.size);
|
|
||||||
self.device.CreateTexture(
|
|
||||||
output_size.width,
|
|
||||||
output_size.height,
|
|
||||||
1,
|
|
||||||
D3DUSAGE_RENDERTARGET as u32,
|
|
||||||
D3DFMT_A8R8G8B8,
|
|
||||||
D3DPOOL_DEFAULT,
|
|
||||||
&mut render_texture,
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let render_texture = render_texture
|
|
||||||
.ok_or_else(|| anyhow!("Unable to create Direct3D 9 render texture"))?;
|
|
||||||
|
|
||||||
let mut copy_texture = None;
|
|
||||||
|
|
||||||
self.device.CreateOffscreenPlainSurface(
|
|
||||||
output_size.width,
|
|
||||||
output_size.height,
|
|
||||||
D3DFMT_A8R8G8B8,
|
|
||||||
D3DPOOL_SYSTEMMEM,
|
|
||||||
&mut copy_texture,
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let copy_texture =
|
|
||||||
copy_texture.ok_or_else(|| anyhow!("Unable to create Direct3D 9 copy texture"))?;
|
|
||||||
|
|
||||||
let surface = render_texture.GetSurfaceLevel(0)?;
|
|
||||||
|
|
||||||
let options = frame_options.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
});
|
|
||||||
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(&surface, None)?;
|
|
||||||
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
filter_chain.frame(&self.texture, &viewport, frame, options.as_ref())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.device.GetRenderTargetData(&surface, ©_texture)?;
|
|
||||||
|
|
||||||
let mut desc = D3DSURFACE_DESC::default();
|
|
||||||
surface.GetDesc(&mut desc)?;
|
|
||||||
|
|
||||||
let mut lock = D3DLOCKED_RECT::default();
|
|
||||||
copy_texture.LockRect(&mut lock, std::ptr::null_mut(), 0)?;
|
|
||||||
let mut buffer = vec![0u8; desc.Height as usize * lock.Pitch as usize];
|
|
||||||
|
|
||||||
std::ptr::copy_nonoverlapping(lock.pBits.cast(), buffer.as_mut_ptr(), buffer.len());
|
|
||||||
copy_texture.UnlockRect()?;
|
|
||||||
|
|
||||||
BGRA8::convert(&mut buffer);
|
|
||||||
|
|
||||||
let image = RgbaImage::from_raw(output_size.width, output_size.height, buffer)
|
|
||||||
.ok_or(anyhow!("Unable to create image from data"))?;
|
|
||||||
|
|
||||||
Ok(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Direct3D9 {
|
|
||||||
pub fn new(image_path: impl AsRef<Path>) -> anyhow::Result<Self> {
|
|
||||||
let direct3d = unsafe {
|
|
||||||
Direct3DCreate9(D3D_SDK_VERSION)
|
|
||||||
.ok_or_else(|| anyhow!("Unable to create Direct3D 9 device"))?
|
|
||||||
};
|
|
||||||
|
|
||||||
let image = Image::<BGRA8>::load(image_path, UVDirection::TopLeft)?;
|
|
||||||
|
|
||||||
let mut present_params: D3DPRESENT_PARAMETERS = Default::default();
|
|
||||||
present_params.BackBufferWidth = image.size.width;
|
|
||||||
present_params.BackBufferHeight = image.size.height;
|
|
||||||
present_params.Windowed = TRUE;
|
|
||||||
present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
|
||||||
present_params.BackBufferFormat = D3DFMT_A8R8G8B8;
|
|
||||||
present_params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE as u32;
|
|
||||||
|
|
||||||
let device = unsafe {
|
|
||||||
let mut device = None;
|
|
||||||
direct3d.CreateDevice(
|
|
||||||
D3DADAPTER_DEFAULT,
|
|
||||||
D3DDEVTYPE_HAL,
|
|
||||||
HWND(std::ptr::null_mut()),
|
|
||||||
D3DCREATE_HARDWARE_VERTEXPROCESSING as u32,
|
|
||||||
&mut present_params,
|
|
||||||
&mut device,
|
|
||||||
)?;
|
|
||||||
device.ok_or_else(|| anyhow!("Unable to create Direct3D 9 device"))?
|
|
||||||
};
|
|
||||||
|
|
||||||
let texture = unsafe {
|
|
||||||
let mut texture = None;
|
|
||||||
device.CreateTexture(
|
|
||||||
image.size.width,
|
|
||||||
image.size.height,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
D3DFMT_A8R8G8B8,
|
|
||||||
D3DPOOL_MANAGED,
|
|
||||||
&mut texture,
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
texture.ok_or_else(|| anyhow!("Unable to create Direct3D 9 texture"))?
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut lock = D3DLOCKED_RECT::default();
|
|
||||||
texture.LockRect(0, &mut lock, std::ptr::null_mut(), 0)?;
|
|
||||||
std::ptr::copy_nonoverlapping(
|
|
||||||
image.bytes.as_ptr(),
|
|
||||||
lock.pBits.cast(),
|
|
||||||
image.bytes.len(),
|
|
||||||
);
|
|
||||||
texture.UnlockRect(0)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
texture,
|
|
||||||
image,
|
|
||||||
direct3d,
|
|
||||||
device,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
// Copyright (c) 2023 Christian Vallentin
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use glfw::{fail_on_errors, Context, Glfw, OpenGlProfileHint, PWindow, WindowHint, WindowMode};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct GlfwContext {
|
|
||||||
_wnd: PWindow,
|
|
||||||
_glfw: Glfw,
|
|
||||||
pub gl: Arc<glow::Context>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlfwContext {
|
|
||||||
pub fn new(version: GLVersion, width: u32, height: u32) -> Result<Self, anyhow::Error> {
|
|
||||||
let mut glfw = glfw::init(fail_on_errors!())?;
|
|
||||||
|
|
||||||
let GLVersion(major, minor) = version;
|
|
||||||
glfw.window_hint(WindowHint::ContextVersion(major, minor));
|
|
||||||
glfw.window_hint(WindowHint::OpenGlProfile(OpenGlProfileHint::Core));
|
|
||||||
glfw.window_hint(WindowHint::OpenGlForwardCompat(true));
|
|
||||||
glfw.window_hint(WindowHint::Visible(false));
|
|
||||||
glfw.window_hint(WindowHint::OpenGlDebugContext(true));
|
|
||||||
|
|
||||||
let (mut wnd, _events) = glfw
|
|
||||||
.create_window(width, height, env!("CARGO_PKG_NAME"), WindowMode::Windowed)
|
|
||||||
.ok_or_else(|| anyhow!("No window"))?;
|
|
||||||
|
|
||||||
wnd.make_current();
|
|
||||||
|
|
||||||
let gl =
|
|
||||||
unsafe { glow::Context::from_loader_function(|proc| wnd.get_proc_address(proc) as _) };
|
|
||||||
|
|
||||||
// unsafe {
|
|
||||||
// gl.enable(glow::DEBUG_OUTPUT);
|
|
||||||
// gl.enable(glow::DEBUG_OUTPUT_SYNCHRONOUS);
|
|
||||||
// gl.debug_message_callback(debug_callback);
|
|
||||||
// gl.debug_message_control(glow::DONT_CARE, glow::DONT_CARE, glow::DONT_CARE, &[], true);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
_wnd: wnd,
|
|
||||||
_glfw: glfw,
|
|
||||||
gl: Arc::new(gl),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GLVersion(pub u32, pub u32);
|
|
|
@ -1,259 +0,0 @@
|
||||||
mod context;
|
|
||||||
|
|
||||||
use crate::render::gl::context::{GLVersion, GlfwContext};
|
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use glow::{HasContext, PixelPackData, PixelUnpackData};
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::gl::{FilterChain, FilterChainOptions, FrameOptions, GLImage};
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use librashader_runtime::image::{Image, UVDirection, RGBA8};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
struct OpenGl {
|
|
||||||
context: GlfwContext,
|
|
||||||
texture: GLImage,
|
|
||||||
image_bytes: Image<RGBA8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OpenGl3(OpenGl);
|
|
||||||
pub struct OpenGl4(OpenGl);
|
|
||||||
|
|
||||||
impl RenderTest for OpenGl3 {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
OpenGl3::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.0.image_bytes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
let mut filter_chain = unsafe {
|
|
||||||
FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
Arc::clone(&self.0.context.gl),
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
glsl_version: 330,
|
|
||||||
use_dsa: false,
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
disable_cache: false,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self.0.render(
|
|
||||||
&mut filter_chain,
|
|
||||||
frame_count,
|
|
||||||
output_size,
|
|
||||||
frame_options
|
|
||||||
.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
})
|
|
||||||
.as_ref(),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTest for OpenGl4 {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
OpenGl4::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.0.image_bytes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
let mut filter_chain = unsafe {
|
|
||||||
FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
Arc::clone(&self.0.context.gl),
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
glsl_version: 460,
|
|
||||||
use_dsa: true,
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
disable_cache: true,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self.0.render(
|
|
||||||
&mut filter_chain,
|
|
||||||
frame_count,
|
|
||||||
output_size,
|
|
||||||
frame_options
|
|
||||||
.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
})
|
|
||||||
.as_ref(),
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenGl3 {
|
|
||||||
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
|
|
||||||
Ok(Self(OpenGl::new(image_path, false)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenGl4 {
|
|
||||||
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
|
|
||||||
Ok(Self(OpenGl::new(image_path, true)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenGl {
|
|
||||||
pub fn new(image_path: &Path, use_dsa: bool) -> anyhow::Result<Self> {
|
|
||||||
let image: Image<RGBA8> = Image::load(image_path, UVDirection::TopLeft)?;
|
|
||||||
let height = image.size.height;
|
|
||||||
let width = image.size.width;
|
|
||||||
let version = if use_dsa {
|
|
||||||
GLVersion(4, 6)
|
|
||||||
} else {
|
|
||||||
GLVersion(3, 3)
|
|
||||||
};
|
|
||||||
|
|
||||||
let context = GlfwContext::new(version, width, height)?;
|
|
||||||
|
|
||||||
let texture = unsafe {
|
|
||||||
let tex = context.gl.create_texture().map_err(|s| anyhow!("{}", s))?;
|
|
||||||
context.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
|
|
||||||
context.gl.tex_storage_2d(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
1,
|
|
||||||
glow::RGBA8,
|
|
||||||
image.size.width as i32,
|
|
||||||
image.size.height as i32,
|
|
||||||
);
|
|
||||||
|
|
||||||
context.gl.pixel_store_i32(glow::UNPACK_ROW_LENGTH, 0);
|
|
||||||
context.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 4);
|
|
||||||
context.gl.bind_buffer(glow::PIXEL_UNPACK_BUFFER, None);
|
|
||||||
|
|
||||||
context.gl.tex_sub_image_2d(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
image.size.width as i32,
|
|
||||||
image.size.height as i32,
|
|
||||||
glow::RGBA,
|
|
||||||
glow::UNSIGNED_BYTE,
|
|
||||||
PixelUnpackData::Slice(&image.bytes),
|
|
||||||
);
|
|
||||||
|
|
||||||
context.gl.bind_texture(glow::TEXTURE_2D, None);
|
|
||||||
tex
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
context,
|
|
||||||
texture: GLImage {
|
|
||||||
handle: Some(texture),
|
|
||||||
format: glow::RGBA8,
|
|
||||||
size: image.size,
|
|
||||||
},
|
|
||||||
image_bytes: image,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(
|
|
||||||
&self,
|
|
||||||
chain: &mut FilterChain,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
options: Option<&FrameOptions>,
|
|
||||||
) -> Result<RgbaImage, anyhow::Error> {
|
|
||||||
let output_size = output_size.unwrap_or(self.image_bytes.size);
|
|
||||||
|
|
||||||
let render_texture = unsafe {
|
|
||||||
let tex = self
|
|
||||||
.context
|
|
||||||
.gl
|
|
||||||
.create_texture()
|
|
||||||
.map_err(|s| anyhow!("{}", s))?;
|
|
||||||
self.context.gl.bind_texture(glow::TEXTURE_2D, Some(tex));
|
|
||||||
self.context.gl.tex_storage_2d(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
1,
|
|
||||||
glow::RGBA8,
|
|
||||||
output_size.width as i32,
|
|
||||||
output_size.height as i32,
|
|
||||||
);
|
|
||||||
self.context.gl.bind_texture(glow::TEXTURE_2D, None);
|
|
||||||
tex
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = GLImage {
|
|
||||||
handle: Some(render_texture),
|
|
||||||
format: glow::RGBA8,
|
|
||||||
size: output_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(&output, None)?;
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
unsafe {
|
|
||||||
chain.frame(&self.texture, &viewport, frame, options)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut data = vec![0u8; output_size.width as usize * output_size.height as usize * 4];
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.context
|
|
||||||
.gl
|
|
||||||
.bind_texture(glow::TEXTURE_2D, output.handle);
|
|
||||||
self.context.gl.get_tex_image(
|
|
||||||
glow::TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
glow::RGBA,
|
|
||||||
glow::UNSIGNED_BYTE,
|
|
||||||
PixelPackData::Slice(&mut data),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(
|
|
||||||
RgbaImage::from_raw(output_size.width, output_size.height, data)
|
|
||||||
.ok_or(anyhow!("failed to create image from slice"))?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
#[cfg(all(windows, feature = "d3d11"))]
|
|
||||||
pub mod d3d11;
|
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "d3d12"))]
|
|
||||||
pub mod d3d12;
|
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "d3d9"))]
|
|
||||||
pub mod d3d9;
|
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
pub mod gl;
|
|
||||||
|
|
||||||
#[cfg(feature = "vulkan")]
|
|
||||||
pub mod vk;
|
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
pub mod wgpu;
|
|
||||||
|
|
||||||
#[cfg(all(target_vendor = "apple", feature = "metal"))]
|
|
||||||
pub mod mtl;
|
|
||||||
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::Size;
|
|
||||||
use librashader_runtime::impl_default_frame_options;
|
|
||||||
use librashader_runtime::parameters::RuntimeParameters;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
/// Test harness to set up a device, render a triangle, and apply a shader
|
|
||||||
pub trait RenderTest {
|
|
||||||
/// Create a new instance of the test harness.
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// Get the size of the image loaded.
|
|
||||||
fn image_size(&self) -> Size<u32>;
|
|
||||||
|
|
||||||
/// Render a shader onto an image buffer, applying the provided shader.
|
|
||||||
///
|
|
||||||
/// The test should render in linear colour space for proper comparison against
|
|
||||||
/// backends.
|
|
||||||
///
|
|
||||||
/// For testing purposes, it is often that a single image will be reused with multiple
|
|
||||||
/// shader presets, so the actual image that a shader will be applied to
|
|
||||||
/// will often be part of the test harness object.
|
|
||||||
fn render(
|
|
||||||
&mut self,
|
|
||||||
path: &Path,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
let preset = ShaderPreset::try_parse(path)?;
|
|
||||||
self.render_with_preset(preset, frame_count, output_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a shader onto an image buffer, applying the provided shader.
|
|
||||||
///
|
|
||||||
/// The test should render in linear colour space for proper comparison against
|
|
||||||
/// backends.
|
|
||||||
///
|
|
||||||
/// For testing purposes, it is often that a single image will be reused with multiple
|
|
||||||
/// shader presets, so the actual image that a shader will be applied to
|
|
||||||
/// will often be part of the test harness object.
|
|
||||||
fn render_with_preset(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
self.render_with_preset_and_params(preset, frame_count, output_size, None, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a shader onto an image buffer, applying the provided shader.
|
|
||||||
///
|
|
||||||
/// The test should render in linear colour space for proper comparison against
|
|
||||||
/// backends.
|
|
||||||
///
|
|
||||||
/// For testing purposes, it is often that a single image will be reused with multiple
|
|
||||||
/// shader presets, so the actual image that a shader will be applied to
|
|
||||||
/// will often be part of the test harness object.
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_default_frame_options!(CommonFrameOptions);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
|
|
||||||
use crate::render::RenderTest;
|
|
||||||
use image::codecs::png::PngEncoder;
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
const IMAGE_PATH: &str = "../triangle.png";
|
|
||||||
const FILTER_PATH: &str = "../test/shaders_slang/crt/crt-royale.slangp";
|
|
||||||
|
|
||||||
// const FILTER_PATH: &str =
|
|
||||||
// "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp";
|
|
||||||
|
|
||||||
fn do_test<T: RenderTest>() -> anyhow::Result<()> {
|
|
||||||
let mut test = T::new(IMAGE_PATH.as_ref())?;
|
|
||||||
let image = test.render(FILTER_PATH.as_ref(), 100)?;
|
|
||||||
|
|
||||||
let out = File::create("out.png")?;
|
|
||||||
image.write_with_encoder(PngEncoder::new(out))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(all(windows, feature = "d3d11"))]
|
|
||||||
pub fn test_d3d11() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::d3d11::Direct3D11>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
pub fn test_wgpu() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::wgpu::Wgpu>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "vulkan")]
|
|
||||||
pub fn test_vk() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::vk::Vulkan>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
pub fn test_gl3() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::gl::OpenGl3>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
pub fn test_gl4() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::gl::OpenGl4>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(all(target_vendor = "apple", feature = "metal"))]
|
|
||||||
pub fn test_metal() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::mtl::Metal>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(all(windows, feature = "d3d9"))]
|
|
||||||
pub fn test_d3d9() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::d3d9::Direct3D9>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(all(windows, feature = "d3d12"))]
|
|
||||||
pub fn test_d3d12() -> anyhow::Result<()> {
|
|
||||||
do_test::<crate::render::d3d12::Direct3D12>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compare<A: RenderTest, B: RenderTest>() -> anyhow::Result<()> {
|
|
||||||
let mut a = A::new(IMAGE_PATH.as_ref())?;
|
|
||||||
let mut b = B::new(IMAGE_PATH.as_ref())?;
|
|
||||||
|
|
||||||
let a_image = a.render(FILTER_PATH.as_ref(), 100)?;
|
|
||||||
let b_image = b.render(FILTER_PATH.as_ref(), 100)?;
|
|
||||||
|
|
||||||
let similarity = image_compare::rgba_hybrid_compare(&a_image, &b_image)?;
|
|
||||||
assert!(similarity.score > 0.95);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,208 +0,0 @@
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::mtl::{FilterChain, FilterChainOptions, FrameOptions};
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8};
|
|
||||||
use objc2::ffi::NSUInteger;
|
|
||||||
use objc2::rc::Retained;
|
|
||||||
use objc2::runtime::ProtocolObject;
|
|
||||||
use objc2_metal::{
|
|
||||||
MTLCommandBuffer, MTLCommandQueue, MTLCreateSystemDefaultDevice, MTLDevice, MTLOrigin,
|
|
||||||
MTLPixelFormat, MTLRegion, MTLSize, MTLStorageMode, MTLTexture, MTLTextureDescriptor,
|
|
||||||
MTLTextureUsage,
|
|
||||||
};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
|
|
||||||
pub struct Metal {
|
|
||||||
device: Retained<ProtocolObject<dyn MTLDevice>>,
|
|
||||||
texture: Retained<ProtocolObject<dyn MTLTexture>>,
|
|
||||||
image_bytes: Image<BGRA8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTest for Metal {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Metal::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.image_bytes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
let queue = self
|
|
||||||
.device
|
|
||||||
.newCommandQueue()
|
|
||||||
.ok_or_else(|| anyhow!("Unable to create command queue"))?;
|
|
||||||
|
|
||||||
let cmd = queue
|
|
||||||
.commandBuffer()
|
|
||||||
.ok_or_else(|| anyhow!("Unable to create command buffer"))?;
|
|
||||||
|
|
||||||
let mut filter_chain = FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&queue,
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_size = output_size.unwrap_or(self.image_bytes.size);
|
|
||||||
|
|
||||||
let render_texture = unsafe {
|
|
||||||
let texture_descriptor =
|
|
||||||
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
|
||||||
MTLPixelFormat::BGRA8Unorm,
|
|
||||||
output_size.width as NSUInteger,
|
|
||||||
output_size.height as NSUInteger,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
texture_descriptor.setSampleCount(1);
|
|
||||||
texture_descriptor.setStorageMode(
|
|
||||||
if cfg!(all(target_arch = "aarch64", target_vendor = "apple")) {
|
|
||||||
MTLStorageMode::Shared
|
|
||||||
} else {
|
|
||||||
MTLStorageMode::Managed
|
|
||||||
},
|
|
||||||
);
|
|
||||||
texture_descriptor.setUsage(MTLTextureUsage::ShaderWrite);
|
|
||||||
|
|
||||||
let texture = self
|
|
||||||
.device
|
|
||||||
.newTextureWithDescriptor(&texture_descriptor)
|
|
||||||
.ok_or_else(|| anyhow!("Failed to create texture"))?;
|
|
||||||
|
|
||||||
texture
|
|
||||||
};
|
|
||||||
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(render_texture.as_ref(), None)?;
|
|
||||||
let options = frame_options.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
});
|
|
||||||
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
filter_chain.frame(
|
|
||||||
&self.texture,
|
|
||||||
&viewport,
|
|
||||||
cmd.as_ref(),
|
|
||||||
frame,
|
|
||||||
options.as_ref(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.commit();
|
|
||||||
unsafe {
|
|
||||||
cmd.waitUntilCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
let region = MTLRegion {
|
|
||||||
origin: MTLOrigin { x: 0, y: 0, z: 0 },
|
|
||||||
size: MTLSize {
|
|
||||||
width: output_size.width as usize,
|
|
||||||
height: output_size.height as usize,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// should be the same size
|
|
||||||
let mut buffer =
|
|
||||||
vec![0u8; output_size.width as usize * output_size.height as usize * 4];
|
|
||||||
render_texture.getBytes_bytesPerRow_fromRegion_mipmapLevel(
|
|
||||||
NonNull::new(buffer.as_mut_ptr().cast()).unwrap(),
|
|
||||||
4 * output_size.width as usize,
|
|
||||||
region,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// swap the BGRA back to RGBA.
|
|
||||||
BGRA8::convert(&mut buffer);
|
|
||||||
|
|
||||||
let image = RgbaImage::from_raw(
|
|
||||||
render_texture.width() as u32,
|
|
||||||
render_texture.height() as u32,
|
|
||||||
Vec::from(buffer),
|
|
||||||
)
|
|
||||||
.ok_or(anyhow!("Unable to create image from data"))?;
|
|
||||||
|
|
||||||
Ok(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Metal {
|
|
||||||
pub fn new(image_path: impl AsRef<Path>) -> anyhow::Result<Self> {
|
|
||||||
let image: Image<BGRA8> = Image::load(image_path, UVDirection::TopLeft)?;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let device = Retained::from_raw(MTLCreateSystemDefaultDevice())
|
|
||||||
.ok_or_else(|| anyhow!("Unable to create default Metal device"))?;
|
|
||||||
|
|
||||||
let texture_descriptor =
|
|
||||||
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(
|
|
||||||
MTLPixelFormat::BGRA8Unorm,
|
|
||||||
image.size.width as NSUInteger,
|
|
||||||
image.size.height as NSUInteger,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
texture_descriptor.setSampleCount(1);
|
|
||||||
texture_descriptor.setStorageMode(
|
|
||||||
if cfg!(all(target_arch = "aarch64", target_vendor = "apple")) {
|
|
||||||
MTLStorageMode::Shared
|
|
||||||
} else {
|
|
||||||
MTLStorageMode::Managed
|
|
||||||
},
|
|
||||||
);
|
|
||||||
texture_descriptor.setUsage(MTLTextureUsage::ShaderRead);
|
|
||||||
|
|
||||||
let texture = device
|
|
||||||
.newTextureWithDescriptor(&texture_descriptor)
|
|
||||||
.ok_or_else(|| anyhow!("Failed to create texture"))?;
|
|
||||||
|
|
||||||
let region = MTLRegion {
|
|
||||||
origin: MTLOrigin { x: 0, y: 0, z: 0 },
|
|
||||||
size: MTLSize {
|
|
||||||
width: image.size.width as usize,
|
|
||||||
height: image.size.height as usize,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
texture.replaceRegion_mipmapLevel_withBytes_bytesPerRow(
|
|
||||||
region,
|
|
||||||
0,
|
|
||||||
// SAFETY: replaceRegion withBytes is const.
|
|
||||||
NonNull::new_unchecked(image.bytes.as_slice().as_ptr() as *mut _),
|
|
||||||
4 * image.size.width as usize,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
device,
|
|
||||||
texture,
|
|
||||||
image_bytes: image,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
use ash::vk;
|
|
||||||
use gpu_allocator::vulkan::Allocator;
|
|
||||||
use librashader::runtime::vk::VulkanObjects;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct VulkanBase {
|
|
||||||
device: Arc<ash::Device>,
|
|
||||||
graphics_queue: vk::Queue,
|
|
||||||
allocator: Arc<Mutex<Allocator>>,
|
|
||||||
cmd_buffer: vk::CommandBuffer,
|
|
||||||
pool: vk::CommandPool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&VulkanBase> for VulkanObjects {
|
|
||||||
fn from(value: &VulkanBase) -> Self {
|
|
||||||
VulkanObjects {
|
|
||||||
device: Arc::clone(&value.device),
|
|
||||||
alloc: Arc::clone(&value.allocator),
|
|
||||||
queue: value.graphics_queue.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const KHRONOS_VALIDATION: &[u8] = b"VK_LAYER_KHRONOS_validation\0";
|
|
||||||
const LIBRASHADER_VULKAN: &[u8] = b"librashader Vulkan\0";
|
|
||||||
|
|
||||||
impl VulkanBase {
|
|
||||||
pub fn new() -> anyhow::Result<Self> {
|
|
||||||
let entry = unsafe { ash::Entry::load() }?;
|
|
||||||
let layers = [KHRONOS_VALIDATION.as_ptr().cast()];
|
|
||||||
let app_info = vk::ApplicationInfo::default()
|
|
||||||
.application_name(unsafe { CStr::from_bytes_with_nul_unchecked(LIBRASHADER_VULKAN) })
|
|
||||||
.engine_name(unsafe { CStr::from_bytes_with_nul_unchecked(LIBRASHADER_VULKAN) })
|
|
||||||
.engine_version(0)
|
|
||||||
.application_version(0)
|
|
||||||
.api_version(vk::make_api_version(0, 1, 3, 0));
|
|
||||||
|
|
||||||
let create_info = vk::InstanceCreateInfo::default()
|
|
||||||
.application_info(&app_info)
|
|
||||||
.enabled_layer_names(&layers)
|
|
||||||
.enabled_extension_names(&[]);
|
|
||||||
|
|
||||||
let instance = unsafe { entry.create_instance(&create_info, None) }?;
|
|
||||||
|
|
||||||
let physical_device = super::physical_device::pick_physical_device(&instance)?;
|
|
||||||
|
|
||||||
let (device, queue, cmd_pool) = Self::create_device(&instance, &physical_device)?;
|
|
||||||
|
|
||||||
let alloc = super::memory::create_allocator(
|
|
||||||
device.clone(),
|
|
||||||
instance.clone(),
|
|
||||||
physical_device.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let buffer_info = vk::CommandBufferAllocateInfo::default()
|
|
||||||
.command_pool(cmd_pool)
|
|
||||||
.level(vk::CommandBufferLevel::PRIMARY)
|
|
||||||
.command_buffer_count(1);
|
|
||||||
|
|
||||||
let buffers = unsafe { device.allocate_command_buffers(&buffer_info) }?
|
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
device: Arc::new(device),
|
|
||||||
graphics_queue: queue,
|
|
||||||
// debug,
|
|
||||||
allocator: alloc,
|
|
||||||
pool: cmd_pool,
|
|
||||||
cmd_buffer: buffers,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn device(&self) -> &Arc<ash::Device> {
|
|
||||||
&self.device
|
|
||||||
}
|
|
||||||
pub(crate) fn allocator(&self) -> &Arc<Mutex<Allocator>> {
|
|
||||||
&self.allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_device(
|
|
||||||
instance: &ash::Instance,
|
|
||||||
physical_device: &vk::PhysicalDevice,
|
|
||||||
) -> anyhow::Result<(ash::Device, vk::Queue, vk::CommandPool)> {
|
|
||||||
let _debug = [unsafe { CStr::from_bytes_with_nul_unchecked(KHRONOS_VALIDATION).as_ptr() }];
|
|
||||||
let indices = super::physical_device::find_queue_family(&instance, *physical_device);
|
|
||||||
|
|
||||||
let queue_info = [vk::DeviceQueueCreateInfo::default()
|
|
||||||
.queue_family_index(indices.graphics_family()?)
|
|
||||||
.queue_priorities(&[1.0f32])];
|
|
||||||
|
|
||||||
let mut physical_device_features =
|
|
||||||
vk::PhysicalDeviceVulkan13Features::default().dynamic_rendering(true);
|
|
||||||
|
|
||||||
let extensions = [ash::khr::dynamic_rendering::NAME.as_ptr()];
|
|
||||||
|
|
||||||
let device_create_info = vk::DeviceCreateInfo::default()
|
|
||||||
.queue_create_infos(&queue_info)
|
|
||||||
.enabled_extension_names(&extensions)
|
|
||||||
.push_next(&mut physical_device_features);
|
|
||||||
|
|
||||||
let device =
|
|
||||||
unsafe { instance.create_device(*physical_device, &device_create_info, None)? };
|
|
||||||
|
|
||||||
let queue = unsafe { device.get_device_queue(indices.graphics_family()?, 0) };
|
|
||||||
|
|
||||||
let create_info = vk::CommandPoolCreateInfo::default()
|
|
||||||
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
|
|
||||||
.queue_family_index(indices.graphics_family()?);
|
|
||||||
let pool = unsafe { device.create_command_pool(&create_info, None) }?;
|
|
||||||
|
|
||||||
Ok((device, queue, pool))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Simple helper function to synchronously queue work on the graphics queue
|
|
||||||
pub fn queue_work<T>(&self, f: impl FnOnce(vk::CommandBuffer) -> T) -> anyhow::Result<T> {
|
|
||||||
unsafe {
|
|
||||||
self.device.begin_command_buffer(
|
|
||||||
self.cmd_buffer,
|
|
||||||
&vk::CommandBufferBeginInfo::default()
|
|
||||||
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = f(self.cmd_buffer);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.device.end_command_buffer(self.cmd_buffer)?;
|
|
||||||
|
|
||||||
self.device.queue_submit(
|
|
||||||
self.graphics_queue,
|
|
||||||
&[vk::SubmitInfo::default().command_buffers(&[self.cmd_buffer])],
|
|
||||||
vk::Fence::null(),
|
|
||||||
)?;
|
|
||||||
self.device.queue_wait_idle(self.graphics_queue)?;
|
|
||||||
self.device
|
|
||||||
.reset_command_buffer(self.cmd_buffer, vk::CommandBufferResetFlags::empty())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for VulkanBase {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
self.device.destroy_command_pool(self.pool, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
use ash::vk;
|
|
||||||
use gpu_allocator::vulkan::{
|
|
||||||
Allocation, AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc,
|
|
||||||
};
|
|
||||||
use gpu_allocator::{AllocationSizes, MemoryLocation};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use std::mem::ManuallyDrop;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct VulkanImageMemory {
|
|
||||||
pub(crate) allocation: ManuallyDrop<Allocation>,
|
|
||||||
allocator: Arc<Mutex<Allocator>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VulkanImageMemory {
|
|
||||||
pub fn new(
|
|
||||||
device: &Arc<ash::Device>,
|
|
||||||
allocator: &Arc<Mutex<Allocator>>,
|
|
||||||
requirements: vk::MemoryRequirements,
|
|
||||||
image: &vk::Image,
|
|
||||||
location: MemoryLocation,
|
|
||||||
) -> anyhow::Result<VulkanImageMemory> {
|
|
||||||
let allocation = allocator.lock().allocate(&AllocationCreateDesc {
|
|
||||||
name: "imagemem",
|
|
||||||
requirements,
|
|
||||||
location,
|
|
||||||
linear: false,
|
|
||||||
allocation_scheme: AllocationScheme::DedicatedImage(*image),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
device.bind_image_memory(*image, allocation.memory(), 0)?;
|
|
||||||
Ok(VulkanImageMemory {
|
|
||||||
allocation: ManuallyDrop::new(allocation),
|
|
||||||
allocator: Arc::clone(allocator),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for VulkanImageMemory {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let allocation = unsafe { ManuallyDrop::take(&mut self.allocation) };
|
|
||||||
if let Err(e) = self.allocator.lock().free(allocation) {
|
|
||||||
println!("librashader-runtime-vk: [warn] failed to deallocate image buffer {e}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VulkanBuffer {
|
|
||||||
pub handle: vk::Buffer,
|
|
||||||
device: Arc<ash::Device>,
|
|
||||||
allocation: ManuallyDrop<Allocation>,
|
|
||||||
allocator: Arc<Mutex<Allocator>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VulkanBuffer {
|
|
||||||
pub fn new(
|
|
||||||
device: &Arc<ash::Device>,
|
|
||||||
allocator: &Arc<Mutex<Allocator>>,
|
|
||||||
usage: vk::BufferUsageFlags,
|
|
||||||
size: usize,
|
|
||||||
) -> anyhow::Result<VulkanBuffer> {
|
|
||||||
unsafe {
|
|
||||||
let buffer_info = vk::BufferCreateInfo::default()
|
|
||||||
.size(size as vk::DeviceSize)
|
|
||||||
.usage(usage)
|
|
||||||
.sharing_mode(vk::SharingMode::EXCLUSIVE);
|
|
||||||
let buffer = device.create_buffer(&buffer_info, None)?;
|
|
||||||
|
|
||||||
let memory_reqs = device.get_buffer_memory_requirements(buffer);
|
|
||||||
|
|
||||||
let alloc = allocator.lock().allocate(&AllocationCreateDesc {
|
|
||||||
name: "buffer",
|
|
||||||
requirements: memory_reqs,
|
|
||||||
location: MemoryLocation::CpuToGpu,
|
|
||||||
linear: true,
|
|
||||||
allocation_scheme: AllocationScheme::DedicatedBuffer(buffer),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// let alloc = device.allocate_memory(&alloc_info, None)?;
|
|
||||||
device.bind_buffer_memory(buffer, alloc.memory(), 0)?;
|
|
||||||
|
|
||||||
Ok(VulkanBuffer {
|
|
||||||
handle: buffer,
|
|
||||||
allocation: ManuallyDrop::new(alloc),
|
|
||||||
allocator: Arc::clone(allocator),
|
|
||||||
device: device.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_mut_slice(&mut self) -> anyhow::Result<&mut [u8]> {
|
|
||||||
let Some(allocation) = self.allocation.mapped_slice_mut() else {
|
|
||||||
return Err(anyhow!("Allocation is not host visible"));
|
|
||||||
};
|
|
||||||
Ok(allocation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for VulkanBuffer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: things can not be double dropped.
|
|
||||||
let allocation = ManuallyDrop::take(&mut self.allocation);
|
|
||||||
if let Err(e) = self.allocator.lock().free(allocation) {
|
|
||||||
println!("librashader-test-vk: [warn] failed to deallocate buffer memory {e}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.handle != vk::Buffer::null() {
|
|
||||||
self.device.destroy_buffer(self.handle, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_allocator(
|
|
||||||
device: ash::Device,
|
|
||||||
instance: ash::Instance,
|
|
||||||
physical_device: vk::PhysicalDevice,
|
|
||||||
) -> Arc<Mutex<Allocator>> {
|
|
||||||
let alloc = Allocator::new(&AllocatorCreateDesc {
|
|
||||||
instance,
|
|
||||||
device,
|
|
||||||
physical_device,
|
|
||||||
debug_settings: Default::default(),
|
|
||||||
buffer_device_address: false,
|
|
||||||
allocation_sizes: AllocationSizes::default(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
Arc::new(Mutex::new(alloc))
|
|
||||||
}
|
|
|
@ -1,391 +0,0 @@
|
||||||
use crate::render::vk::base::VulkanBase;
|
|
||||||
use crate::render::vk::memory::{VulkanBuffer, VulkanImageMemory};
|
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use ash::vk;
|
|
||||||
use gpu_allocator::MemoryLocation;
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::vk::{FilterChain, FilterChainOptions, FrameOptions, VulkanImage};
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use librashader_runtime::image::{Image, UVDirection, BGRA8};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
mod base;
|
|
||||||
mod memory;
|
|
||||||
mod physical_device;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
pub struct Vulkan {
|
|
||||||
vk: VulkanBase,
|
|
||||||
image_bytes: Image<BGRA8>,
|
|
||||||
image: vk::Image,
|
|
||||||
_image_alloc: VulkanImageMemory,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTest for Vulkan {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Vulkan::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.image_bytes.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
unsafe {
|
|
||||||
let mut filter_chain = FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
&self.vk,
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
frames_in_flight: 3,
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
use_dynamic_rendering: false,
|
|
||||||
disable_cache: false,
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(filter_chain.parameters());
|
|
||||||
}
|
|
||||||
|
|
||||||
let image_info = vk::ImageCreateInfo::default()
|
|
||||||
.image_type(vk::ImageType::TYPE_2D)
|
|
||||||
.format(vk::Format::B8G8R8A8_UNORM)
|
|
||||||
.extent(output_size.map_or(self.image_bytes.size.into(), |size| size.into()))
|
|
||||||
.mip_levels(1)
|
|
||||||
.array_layers(1)
|
|
||||||
.samples(vk::SampleCountFlags::TYPE_1)
|
|
||||||
.tiling(vk::ImageTiling::OPTIMAL)
|
|
||||||
.usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_SRC)
|
|
||||||
.initial_layout(vk::ImageLayout::UNDEFINED);
|
|
||||||
|
|
||||||
let render_texture = { self.vk.device().create_image(&image_info, None)? };
|
|
||||||
|
|
||||||
// This just needs to stay alive until the read.
|
|
||||||
let _memory = {
|
|
||||||
let mem_reqs = self
|
|
||||||
.vk
|
|
||||||
.device()
|
|
||||||
.get_image_memory_requirements(render_texture);
|
|
||||||
VulkanImageMemory::new(
|
|
||||||
self.vk.device(),
|
|
||||||
&self.vk.allocator(),
|
|
||||||
mem_reqs,
|
|
||||||
&render_texture,
|
|
||||||
MemoryLocation::GpuOnly,
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let transfer_texture = self.vk.device().create_image(
|
|
||||||
&vk::ImageCreateInfo::default()
|
|
||||||
.image_type(vk::ImageType::TYPE_2D)
|
|
||||||
.format(vk::Format::R8G8B8A8_UNORM)
|
|
||||||
.extent(self.image_bytes.size.into())
|
|
||||||
.mip_levels(1)
|
|
||||||
.array_layers(1)
|
|
||||||
.samples(vk::SampleCountFlags::TYPE_1)
|
|
||||||
.tiling(vk::ImageTiling::LINEAR)
|
|
||||||
.usage(vk::ImageUsageFlags::TRANSFER_DST)
|
|
||||||
.initial_layout(vk::ImageLayout::UNDEFINED),
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut transfer_memory = {
|
|
||||||
let mem_reqs = self
|
|
||||||
.vk
|
|
||||||
.device()
|
|
||||||
.get_image_memory_requirements(transfer_texture);
|
|
||||||
VulkanImageMemory::new(
|
|
||||||
self.vk.device(),
|
|
||||||
&self.vk.allocator(),
|
|
||||||
mem_reqs,
|
|
||||||
&transfer_texture,
|
|
||||||
MemoryLocation::GpuToCpu,
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
|
|
||||||
self.vk.queue_work(|cmd| {
|
|
||||||
util::vulkan_image_layout_transition_levels(
|
|
||||||
&self.vk.device(),
|
|
||||||
cmd,
|
|
||||||
render_texture,
|
|
||||||
vk::REMAINING_MIP_LEVELS,
|
|
||||||
vk::ImageLayout::UNDEFINED,
|
|
||||||
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
|
|
||||||
vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
|
|
||||||
vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
|
|
||||||
vk::PipelineStageFlags::ALL_GRAPHICS,
|
|
||||||
vk::PipelineStageFlags::ALL_GRAPHICS,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
);
|
|
||||||
|
|
||||||
util::vulkan_image_layout_transition_levels(
|
|
||||||
&self.vk.device(),
|
|
||||||
cmd,
|
|
||||||
transfer_texture,
|
|
||||||
vk::REMAINING_MIP_LEVELS,
|
|
||||||
vk::ImageLayout::UNDEFINED,
|
|
||||||
vk::ImageLayout::GENERAL,
|
|
||||||
vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
|
|
||||||
vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
|
|
||||||
vk::PipelineStageFlags::ALL_GRAPHICS,
|
|
||||||
vk::PipelineStageFlags::TRANSFER,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
);
|
|
||||||
|
|
||||||
let options = frame_options.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
});
|
|
||||||
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(
|
|
||||||
VulkanImage {
|
|
||||||
image: render_texture,
|
|
||||||
size: self.image_bytes.size.into(),
|
|
||||||
format: vk::Format::B8G8R8A8_UNORM,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
filter_chain.frame(
|
|
||||||
&VulkanImage {
|
|
||||||
image: self.image,
|
|
||||||
size: self.image_bytes.size,
|
|
||||||
format: vk::Format::B8G8R8A8_UNORM,
|
|
||||||
},
|
|
||||||
&viewport,
|
|
||||||
cmd,
|
|
||||||
frame,
|
|
||||||
options.as_ref(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
util::vulkan_image_layout_transition_levels(
|
|
||||||
&self.vk.device(),
|
|
||||||
cmd,
|
|
||||||
render_texture,
|
|
||||||
vk::REMAINING_MIP_LEVELS,
|
|
||||||
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
|
|
||||||
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
|
|
||||||
vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
|
|
||||||
vk::AccessFlags::TRANSFER_READ,
|
|
||||||
vk::PipelineStageFlags::ALL_GRAPHICS,
|
|
||||||
vk::PipelineStageFlags::TRANSFER,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let offsets = [
|
|
||||||
vk::Offset3D { x: 0, y: 0, z: 0 },
|
|
||||||
vk::Offset3D {
|
|
||||||
x: self.image_bytes.size.width as i32,
|
|
||||||
y: self.image_bytes.size.height as i32,
|
|
||||||
z: 1,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let subresource = vk::ImageSubresourceLayers::default()
|
|
||||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
|
||||||
.base_array_layer(0)
|
|
||||||
.layer_count(1);
|
|
||||||
|
|
||||||
let image_blit = vk::ImageBlit::default()
|
|
||||||
.src_subresource(subresource.clone())
|
|
||||||
.src_offsets(offsets.clone())
|
|
||||||
.dst_subresource(subresource)
|
|
||||||
.dst_offsets(offsets);
|
|
||||||
|
|
||||||
self.vk.device().cmd_blit_image(
|
|
||||||
cmd,
|
|
||||||
render_texture,
|
|
||||||
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
|
|
||||||
transfer_texture,
|
|
||||||
vk::ImageLayout::GENERAL,
|
|
||||||
&[image_blit],
|
|
||||||
vk::Filter::NEAREST,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok::<_, anyhow::Error>(())
|
|
||||||
})??;
|
|
||||||
|
|
||||||
// should have read now.
|
|
||||||
let mut memory = transfer_memory
|
|
||||||
.allocation
|
|
||||||
.mapped_slice_mut()
|
|
||||||
.ok_or(anyhow!("readback buffer was not mapped"))?;
|
|
||||||
|
|
||||||
let layout = self.vk.device().get_image_subresource_layout(
|
|
||||||
transfer_texture,
|
|
||||||
vk::ImageSubresource::default()
|
|
||||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
|
||||||
.array_layer(0),
|
|
||||||
);
|
|
||||||
memory = &mut memory[layout.offset as usize..];
|
|
||||||
|
|
||||||
// let mut pixels = Vec::with_capacity(layout.size as usize);
|
|
||||||
|
|
||||||
let image = RgbaImage::from_raw(
|
|
||||||
self.image_bytes.size.width,
|
|
||||||
self.image_bytes.size.height,
|
|
||||||
Vec::from(memory),
|
|
||||||
)
|
|
||||||
.ok_or(anyhow!("failed to create image from slice"));
|
|
||||||
|
|
||||||
self.vk.device().destroy_image(transfer_texture, None);
|
|
||||||
self.vk.device().destroy_image(render_texture, None);
|
|
||||||
|
|
||||||
Ok(image?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vulkan {
|
|
||||||
pub fn new(image_path: &Path) -> anyhow::Result<Self> {
|
|
||||||
let vk = VulkanBase::new()?;
|
|
||||||
|
|
||||||
let (image_bytes, image_alloc, image, _view) = Self::load_image(&vk, image_path)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
vk,
|
|
||||||
image,
|
|
||||||
image_bytes,
|
|
||||||
_image_alloc: image_alloc,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_image(
|
|
||||||
vk: &VulkanBase,
|
|
||||||
image_path: &Path,
|
|
||||||
) -> anyhow::Result<(Image<BGRA8>, VulkanImageMemory, vk::Image, vk::ImageView)> {
|
|
||||||
let image: Image<BGRA8> = Image::load(image_path, UVDirection::TopLeft)?;
|
|
||||||
|
|
||||||
let image_info = vk::ImageCreateInfo::default()
|
|
||||||
.image_type(vk::ImageType::TYPE_2D)
|
|
||||||
.format(vk::Format::B8G8R8A8_UNORM)
|
|
||||||
.extent(image.size.into())
|
|
||||||
.mip_levels(1)
|
|
||||||
.array_layers(1)
|
|
||||||
.samples(vk::SampleCountFlags::TYPE_1)
|
|
||||||
.tiling(vk::ImageTiling::OPTIMAL)
|
|
||||||
.usage(
|
|
||||||
vk::ImageUsageFlags::SAMPLED
|
|
||||||
| vk::ImageUsageFlags::TRANSFER_SRC
|
|
||||||
| vk::ImageUsageFlags::TRANSFER_DST,
|
|
||||||
)
|
|
||||||
.initial_layout(vk::ImageLayout::UNDEFINED);
|
|
||||||
|
|
||||||
let texture = unsafe { vk.device().create_image(&image_info, None)? };
|
|
||||||
|
|
||||||
let memory = unsafe {
|
|
||||||
let mem_reqs = vk.device().get_image_memory_requirements(texture);
|
|
||||||
VulkanImageMemory::new(
|
|
||||||
vk.device(),
|
|
||||||
&vk.allocator(),
|
|
||||||
mem_reqs,
|
|
||||||
&texture,
|
|
||||||
MemoryLocation::GpuOnly,
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let image_subresource = vk::ImageSubresourceRange::default()
|
|
||||||
.level_count(image_info.mip_levels)
|
|
||||||
.layer_count(1)
|
|
||||||
.aspect_mask(vk::ImageAspectFlags::COLOR);
|
|
||||||
|
|
||||||
let swizzle_components = vk::ComponentMapping::default()
|
|
||||||
.r(vk::ComponentSwizzle::R)
|
|
||||||
.g(vk::ComponentSwizzle::G)
|
|
||||||
.b(vk::ComponentSwizzle::B)
|
|
||||||
.a(vk::ComponentSwizzle::A);
|
|
||||||
|
|
||||||
let view_info = vk::ImageViewCreateInfo::default()
|
|
||||||
.view_type(vk::ImageViewType::TYPE_2D)
|
|
||||||
.format(vk::Format::B8G8R8A8_UNORM)
|
|
||||||
.image(texture)
|
|
||||||
.subresource_range(image_subresource)
|
|
||||||
.components(swizzle_components);
|
|
||||||
|
|
||||||
let texture_view = unsafe { vk.device().create_image_view(&view_info, None)? };
|
|
||||||
|
|
||||||
let mut staging = VulkanBuffer::new(
|
|
||||||
&vk.device(),
|
|
||||||
&vk.allocator(),
|
|
||||||
vk::BufferUsageFlags::TRANSFER_SRC,
|
|
||||||
image.bytes.len(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
staging.as_mut_slice()?.copy_from_slice(&image.bytes);
|
|
||||||
|
|
||||||
vk.queue_work(|cmd| unsafe {
|
|
||||||
util::vulkan_image_layout_transition_levels(
|
|
||||||
&vk.device(),
|
|
||||||
cmd,
|
|
||||||
texture,
|
|
||||||
vk::REMAINING_MIP_LEVELS,
|
|
||||||
vk::ImageLayout::UNDEFINED,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
vk::AccessFlags::empty(),
|
|
||||||
vk::AccessFlags::TRANSFER_WRITE,
|
|
||||||
vk::PipelineStageFlags::TOP_OF_PIPE,
|
|
||||||
vk::PipelineStageFlags::TRANSFER,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
);
|
|
||||||
|
|
||||||
let builder = vk::BufferImageCopy::default()
|
|
||||||
.image_subresource(
|
|
||||||
vk::ImageSubresourceLayers::default()
|
|
||||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
|
||||||
.mip_level(0)
|
|
||||||
.base_array_layer(0)
|
|
||||||
.layer_count(1),
|
|
||||||
)
|
|
||||||
.image_extent(image.size.into());
|
|
||||||
|
|
||||||
vk.device().cmd_copy_buffer_to_image(
|
|
||||||
cmd,
|
|
||||||
staging.handle,
|
|
||||||
texture,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
&[builder],
|
|
||||||
);
|
|
||||||
|
|
||||||
util::vulkan_image_layout_transition_levels(
|
|
||||||
&vk.device(),
|
|
||||||
cmd,
|
|
||||||
texture,
|
|
||||||
vk::REMAINING_MIP_LEVELS,
|
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
||||||
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
|
|
||||||
vk::AccessFlags::TRANSFER_WRITE,
|
|
||||||
vk::AccessFlags::SHADER_READ,
|
|
||||||
vk::PipelineStageFlags::TRANSFER,
|
|
||||||
vk::PipelineStageFlags::ALL_GRAPHICS,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
vk::QUEUE_FAMILY_IGNORED,
|
|
||||||
);
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok((image, memory, texture, texture_view))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
use anyhow::anyhow;
|
|
||||||
use ash::vk;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
|
|
||||||
pub struct QueueFamilyIndices {
|
|
||||||
graphics_family: Option<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QueueFamilyIndices {
|
|
||||||
pub fn is_complete(&self) -> bool {
|
|
||||||
self.graphics_family.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn graphics_family(&self) -> anyhow::Result<u32> {
|
|
||||||
self.graphics_family
|
|
||||||
.ok_or(anyhow!("The queue family is not complete"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn pick_physical_device(instance: &ash::Instance) -> anyhow::Result<vk::PhysicalDevice> {
|
|
||||||
let physical_devices = unsafe { instance.enumerate_physical_devices()? };
|
|
||||||
|
|
||||||
let mut result = None;
|
|
||||||
for &physical_device in physical_devices.iter() {
|
|
||||||
if is_physical_device_suitable(instance, physical_device) && result.is_none() {
|
|
||||||
result = Some(physical_device)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.ok_or(anyhow!("Failed to find a suitable GPU"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_physical_device_suitable(
|
|
||||||
instance: &ash::Instance,
|
|
||||||
physical_device: vk::PhysicalDevice,
|
|
||||||
) -> bool {
|
|
||||||
let device_properties = unsafe { instance.get_physical_device_properties(physical_device) };
|
|
||||||
let device_features = unsafe { instance.get_physical_device_features(physical_device) };
|
|
||||||
let device_queue_families =
|
|
||||||
unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
|
|
||||||
|
|
||||||
if cfg!(feature = "vulkan-debug") {
|
|
||||||
let device_type = match device_properties.device_type {
|
|
||||||
vk::PhysicalDeviceType::CPU => "Cpu",
|
|
||||||
vk::PhysicalDeviceType::INTEGRATED_GPU => "Integrated GPU",
|
|
||||||
vk::PhysicalDeviceType::DISCRETE_GPU => "Discrete GPU",
|
|
||||||
vk::PhysicalDeviceType::VIRTUAL_GPU => "Virtual GPU",
|
|
||||||
vk::PhysicalDeviceType::OTHER => "Unknown",
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let device_name = unsafe { CStr::from_ptr(device_properties.device_name.as_ptr()) };
|
|
||||||
|
|
||||||
let device_name = device_name.to_string_lossy();
|
|
||||||
println!(
|
|
||||||
"\tDevice Name: {}, id: {}, type: {}",
|
|
||||||
device_name, device_properties.device_id, device_type
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("\tAPI Version: {}", device_properties.api_version);
|
|
||||||
|
|
||||||
println!("\tSupport Queue Family: {}", device_queue_families.len());
|
|
||||||
println!("\t\tQueue Count | Graphics, Compute, Transfer, Sparse Binding");
|
|
||||||
for queue_family in device_queue_families.iter() {
|
|
||||||
let is_graphics_support = if queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS)
|
|
||||||
{
|
|
||||||
"support"
|
|
||||||
} else {
|
|
||||||
"unsupport"
|
|
||||||
};
|
|
||||||
let is_compute_support = if queue_family.queue_flags.contains(vk::QueueFlags::COMPUTE) {
|
|
||||||
"support"
|
|
||||||
} else {
|
|
||||||
"unsupport"
|
|
||||||
};
|
|
||||||
let is_transfer_support = if queue_family.queue_flags.contains(vk::QueueFlags::TRANSFER)
|
|
||||||
{
|
|
||||||
"support"
|
|
||||||
} else {
|
|
||||||
"unsupport"
|
|
||||||
};
|
|
||||||
let is_sparse_support = if queue_family
|
|
||||||
.queue_flags
|
|
||||||
.contains(vk::QueueFlags::SPARSE_BINDING)
|
|
||||||
{
|
|
||||||
"support"
|
|
||||||
} else {
|
|
||||||
"unsupport"
|
|
||||||
};
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"\t\t{}\t | {}, {}, {}, {}",
|
|
||||||
queue_family.queue_count,
|
|
||||||
is_graphics_support,
|
|
||||||
is_compute_support,
|
|
||||||
is_transfer_support,
|
|
||||||
is_sparse_support
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are plenty of features
|
|
||||||
println!(
|
|
||||||
"\tGeometry Shader support: {}",
|
|
||||||
if device_features.geometry_shader == 1 {
|
|
||||||
"support"
|
|
||||||
} else {
|
|
||||||
"unsupported"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let indices = find_queue_family(instance, physical_device);
|
|
||||||
|
|
||||||
indices.is_complete()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_queue_family(
|
|
||||||
instance: &ash::Instance,
|
|
||||||
physical_device: vk::PhysicalDevice,
|
|
||||||
) -> QueueFamilyIndices {
|
|
||||||
let queue_families =
|
|
||||||
unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
|
|
||||||
|
|
||||||
let mut queue_family_indices = QueueFamilyIndices {
|
|
||||||
graphics_family: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut index = 0;
|
|
||||||
for queue_family in queue_families.iter() {
|
|
||||||
if queue_family.queue_count > 0
|
|
||||||
&& queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS)
|
|
||||||
{
|
|
||||||
queue_family_indices.graphics_family = Some(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
if queue_family_indices.is_complete() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_family_indices
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
use ash::vk;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn vulkan_image_layout_transition_levels(
|
|
||||||
device: &ash::Device,
|
|
||||||
cmd: vk::CommandBuffer,
|
|
||||||
image: vk::Image,
|
|
||||||
levels: u32,
|
|
||||||
old_layout: vk::ImageLayout,
|
|
||||||
new_layout: vk::ImageLayout,
|
|
||||||
src_access: vk::AccessFlags,
|
|
||||||
dst_access: vk::AccessFlags,
|
|
||||||
src_stage: vk::PipelineStageFlags,
|
|
||||||
dst_stage: vk::PipelineStageFlags,
|
|
||||||
|
|
||||||
src_queue_family_index: u32,
|
|
||||||
dst_queue_family_index: u32,
|
|
||||||
) {
|
|
||||||
let mut barrier = vk::ImageMemoryBarrier::default();
|
|
||||||
barrier.s_type = vk::StructureType::IMAGE_MEMORY_BARRIER;
|
|
||||||
barrier.p_next = std::ptr::null();
|
|
||||||
barrier.src_access_mask = src_access;
|
|
||||||
barrier.dst_access_mask = dst_access;
|
|
||||||
barrier.old_layout = old_layout;
|
|
||||||
barrier.new_layout = new_layout;
|
|
||||||
barrier.src_queue_family_index = src_queue_family_index;
|
|
||||||
barrier.dst_queue_family_index = dst_queue_family_index;
|
|
||||||
barrier.image = image;
|
|
||||||
barrier.subresource_range.aspect_mask = vk::ImageAspectFlags::COLOR;
|
|
||||||
barrier.subresource_range.base_array_layer = 0;
|
|
||||||
barrier.subresource_range.level_count = levels;
|
|
||||||
barrier.subresource_range.layer_count = vk::REMAINING_ARRAY_LAYERS;
|
|
||||||
device.cmd_pipeline_barrier(
|
|
||||||
cmd,
|
|
||||||
src_stage,
|
|
||||||
dst_stage,
|
|
||||||
vk::DependencyFlags::empty(),
|
|
||||||
&[],
|
|
||||||
&[],
|
|
||||||
&[barrier],
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,268 +0,0 @@
|
||||||
use crate::render::{CommonFrameOptions, RenderTest};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use image::RgbaImage;
|
|
||||||
use librashader::runtime::wgpu::*;
|
|
||||||
use librashader::runtime::{Size, Viewport};
|
|
||||||
use librashader_runtime::image::{Image, UVDirection};
|
|
||||||
use std::io::{Cursor, Write};
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use wgpu::{Adapter, Device, Instance, Queue, Texture};
|
|
||||||
use wgpu_types::{
|
|
||||||
BufferAddress, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ImageCopyBuffer,
|
|
||||||
ImageDataLayout, Maintain, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
|
|
||||||
};
|
|
||||||
|
|
||||||
use librashader::presets::ShaderPreset;
|
|
||||||
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
pub struct Wgpu {
|
|
||||||
_instance: Instance,
|
|
||||||
_adapter: Adapter,
|
|
||||||
device: Arc<Device>,
|
|
||||||
queue: Arc<Queue>,
|
|
||||||
image: Image,
|
|
||||||
texture: Arc<Texture>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BufferDimensions {
|
|
||||||
height: usize,
|
|
||||||
unpadded_bytes_per_row: usize,
|
|
||||||
padded_bytes_per_row: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferDimensions {
|
|
||||||
fn new(width: usize, height: usize) -> Self {
|
|
||||||
let bytes_per_pixel = std::mem::size_of::<u32>();
|
|
||||||
let unpadded_bytes_per_row = width * bytes_per_pixel;
|
|
||||||
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
|
|
||||||
let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
|
|
||||||
let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
|
|
||||||
Self {
|
|
||||||
height,
|
|
||||||
unpadded_bytes_per_row,
|
|
||||||
padded_bytes_per_row,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTest for Wgpu {
|
|
||||||
fn new(path: &Path) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
Wgpu::new(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_size(&self) -> Size<u32> {
|
|
||||||
self.image.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_with_preset_and_params(
|
|
||||||
&mut self,
|
|
||||||
preset: ShaderPreset,
|
|
||||||
frame_count: usize,
|
|
||||||
output_size: Option<Size<u32>>,
|
|
||||||
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
|
|
||||||
frame_options: Option<CommonFrameOptions>,
|
|
||||||
) -> anyhow::Result<image::RgbaImage> {
|
|
||||||
let mut chain = FilterChain::load_from_preset(
|
|
||||||
preset,
|
|
||||||
Arc::clone(&self.device),
|
|
||||||
Arc::clone(&self.queue),
|
|
||||||
Some(&FilterChainOptions {
|
|
||||||
force_no_mipmaps: false,
|
|
||||||
enable_cache: true,
|
|
||||||
adapter_info: None,
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
if let Some(setter) = param_setter {
|
|
||||||
setter(&chain.parameters());
|
|
||||||
}
|
|
||||||
let mut cmd = self
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&CommandEncoderDescriptor { label: None });
|
|
||||||
|
|
||||||
let output_tex = self.device.create_texture(&TextureDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: output_size.map_or(self.texture.size(), |size| size.into()),
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: TextureDimension::D2,
|
|
||||||
format: TextureFormat::Rgba8Unorm,
|
|
||||||
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
|
|
||||||
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
|
|
||||||
});
|
|
||||||
|
|
||||||
let buffer_dimensions =
|
|
||||||
BufferDimensions::new(output_tex.width() as usize, output_tex.height() as usize);
|
|
||||||
let output_buf = Arc::new(self.device.create_buffer(&BufferDescriptor {
|
|
||||||
label: None,
|
|
||||||
size: (buffer_dimensions.padded_bytes_per_row * buffer_dimensions.height)
|
|
||||||
as BufferAddress, // 4bpp
|
|
||||||
usage: BufferUsages::COPY_DST | BufferUsages::MAP_READ,
|
|
||||||
mapped_at_creation: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
let view = output_tex.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let output = WgpuOutputView::new_from_raw(
|
|
||||||
&view,
|
|
||||||
output_tex.size().into(),
|
|
||||||
TextureFormat::Rgba8Unorm,
|
|
||||||
);
|
|
||||||
|
|
||||||
let viewport = Viewport::new_render_target_sized_origin(output, None)?;
|
|
||||||
let options = frame_options.map(|options| FrameOptions {
|
|
||||||
clear_history: options.clear_history,
|
|
||||||
frame_direction: options.frame_direction,
|
|
||||||
rotation: options.rotation,
|
|
||||||
total_subframes: options.total_subframes,
|
|
||||||
current_subframe: options.current_subframe,
|
|
||||||
});
|
|
||||||
|
|
||||||
for frame in 0..=frame_count {
|
|
||||||
chain.frame(
|
|
||||||
Arc::clone(&self.texture),
|
|
||||||
&viewport,
|
|
||||||
&mut cmd,
|
|
||||||
frame,
|
|
||||||
options.as_ref(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.copy_texture_to_buffer(
|
|
||||||
output_tex.as_image_copy(),
|
|
||||||
ImageCopyBuffer {
|
|
||||||
buffer: &output_buf,
|
|
||||||
layout: ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(buffer_dimensions.padded_bytes_per_row as u32),
|
|
||||||
rows_per_image: None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output_tex.size(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let si = self.queue.submit([cmd.finish()]);
|
|
||||||
self.device.poll(Maintain::WaitForSubmissionIndex(si));
|
|
||||||
|
|
||||||
let capturable = Arc::clone(&output_buf);
|
|
||||||
|
|
||||||
let pixels = Arc::new(Mutex::new(Vec::new()));
|
|
||||||
|
|
||||||
let pixels_async = Arc::clone(&pixels);
|
|
||||||
output_buf
|
|
||||||
.slice(..)
|
|
||||||
.map_async(wgpu::MapMode::Read, move |r| {
|
|
||||||
if r.is_ok() {
|
|
||||||
let buffer = capturable.slice(..).get_mapped_range();
|
|
||||||
let mut pixels = pixels_async.lock();
|
|
||||||
pixels.resize(buffer.len(), 0);
|
|
||||||
|
|
||||||
let mut cursor = Cursor::new(pixels.deref_mut());
|
|
||||||
for chunk in buffer.chunks(buffer_dimensions.padded_bytes_per_row) {
|
|
||||||
cursor
|
|
||||||
.write_all(&chunk[..buffer_dimensions.unpadded_bytes_per_row])
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor.into_inner();
|
|
||||||
}
|
|
||||||
capturable.unmap();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.device.poll(Maintain::Wait);
|
|
||||||
|
|
||||||
if pixels.lock().len() == 0 {
|
|
||||||
return Err(anyhow!("failed to copy pixels from buffer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = RgbaImage::from_raw(
|
|
||||||
output_tex.width(),
|
|
||||||
output_tex.height(),
|
|
||||||
pixels.lock().to_vec(),
|
|
||||||
)
|
|
||||||
.ok_or(anyhow!("Unable to create image from data"))?;
|
|
||||||
|
|
||||||
Ok(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wgpu {
|
|
||||||
pub fn new(image: &Path) -> anyhow::Result<Self> {
|
|
||||||
pollster::block_on(async {
|
|
||||||
let instance = wgpu::Instance::default();
|
|
||||||
let adapter = instance
|
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: wgpu::PowerPreference::default(),
|
|
||||||
compatible_surface: None,
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.ok_or(anyhow!("Couldn't request WGPU adapter"))?;
|
|
||||||
|
|
||||||
let (device, queue) = adapter
|
|
||||||
.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
|
|
||||||
| wgpu::Features::PIPELINE_CACHE
|
|
||||||
| wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
|
|
||||||
| wgpu::Features::FLOAT32_FILTERABLE,
|
|
||||||
required_limits: wgpu::Limits::default(),
|
|
||||||
label: None,
|
|
||||||
memory_hints: Default::default(),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let (image, texture) = Self::load_image(&device, &queue, image)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
_instance: instance,
|
|
||||||
_adapter: adapter,
|
|
||||||
device: Arc::new(device),
|
|
||||||
queue: Arc::new(queue),
|
|
||||||
image: image,
|
|
||||||
texture: Arc::new(texture),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_image(device: &Device, queue: &Queue, path: &Path) -> anyhow::Result<(Image, Texture)> {
|
|
||||||
let image = Image::load(path, UVDirection::TopLeft)?;
|
|
||||||
let texture = device.create_texture(&TextureDescriptor {
|
|
||||||
size: image.size.into(),
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: TextureDimension::D2,
|
|
||||||
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
|
||||||
format: wgpu::TextureFormat::Rgba8Unorm,
|
|
||||||
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
|
|
||||||
label: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
queue.write_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
texture: &texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: wgpu::Origin3d::ZERO,
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
},
|
|
||||||
&image.bytes,
|
|
||||||
wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(4 * image.size.width),
|
|
||||||
rows_per_image: None,
|
|
||||||
},
|
|
||||||
image.size.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let si = queue.submit([]);
|
|
||||||
|
|
||||||
device.poll(Maintain::WaitForSubmissionIndex(si));
|
|
||||||
|
|
||||||
Ok((image, texture))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ name = "librashader-common"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
license = "MPL-2.0 OR GPL-3.0-only"
|
||||||
version = "0.5.1"
|
version = "0.1.3"
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
repository = "https://github.com/SnowflakePowered/librashader"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
@ -13,47 +13,25 @@ description = "RetroArch shaders for all."
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
opengl = ["glow"]
|
opengl = ["gl"]
|
||||||
d3d9 = ["windows"]
|
|
||||||
d3d11 = ["windows", "dxgi"]
|
d3d11 = ["windows", "dxgi"]
|
||||||
d3d12 = ["windows", "dxgi"]
|
d3d12 = ["windows", "dxgi"]
|
||||||
dxgi = ["windows"]
|
dxgi = ["windows"]
|
||||||
vulkan = ["ash"]
|
vulkan = ["ash"]
|
||||||
wgpu = ["wgpu-types"]
|
|
||||||
metal = ["objc2", "objc2-metal"]
|
|
||||||
serde = ["dep:serde", "serde/derive", "smartstring/serde", "halfbrown/serde"]
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
gl = { version = "0.14.0", optional = true }
|
||||||
|
ash = { version = "0.37.1+1.3.235", optional = true }
|
||||||
|
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.15"
|
||||||
rustc-hash = "2.0.0"
|
|
||||||
halfbrown = "0.2.4"
|
|
||||||
smartstring = "1.0"
|
|
||||||
|
|
||||||
glow = { workspace = true, optional = true }
|
|
||||||
ash = { workspace = true, optional = true }
|
|
||||||
wgpu-types = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
serde = { version = "1.0", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
optional = true
|
optional = true
|
||||||
workspace = true
|
version = "0.44.0"
|
||||||
features = [
|
features = [
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Graphics_Dxgi_Common",
|
"Win32_Graphics_Dxgi_Common",
|
||||||
"Win32_Graphics_Direct3D",
|
"Win32_Graphics_Direct3D",
|
||||||
"Win32_Graphics_Direct3D9",
|
|
||||||
"Win32_Graphics_Direct3D11",
|
"Win32_Graphics_Direct3D11",
|
||||||
"Win32_Graphics_Direct3D12",
|
"Win32_Graphics_Direct3D12",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_vendor="apple")'.dependencies.objc2]
|
|
||||||
optional = true
|
|
||||||
workspace = true
|
|
||||||
features = ["apple"]
|
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_vendor="apple")'.dependencies.objc2-metal]
|
|
||||||
optional = true
|
|
||||||
workspace = true
|
|
||||||
features = ["MTLPixelFormat", "MTLRenderCommandEncoder", "MTLSampler"]
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use crate::{FilterMode, GetSize, Size, WrapMode};
|
use crate::{FilterMode, WrapMode};
|
||||||
use windows::Win32::Foundation::E_NOINTERFACE;
|
|
||||||
use windows::Win32::Graphics::Direct3D11;
|
use windows::Win32::Graphics::Direct3D11;
|
||||||
use windows::Win32::Graphics::Direct3D11::{ID3D11Texture2D, D3D11_RESOURCE_DIMENSION_TEXTURE2D};
|
|
||||||
|
|
||||||
impl From<WrapMode> for Direct3D11::D3D11_TEXTURE_ADDRESS_MODE {
|
impl From<WrapMode> for Direct3D11::D3D11_TEXTURE_ADDRESS_MODE {
|
||||||
fn from(value: WrapMode) -> Self {
|
fn from(value: WrapMode) -> Self {
|
||||||
|
@ -22,82 +20,3 @@ impl From<FilterMode> for Direct3D11::D3D11_FILTER {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetSize<u32> for &Direct3D11::ID3D11RenderTargetView {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let parent: ID3D11Texture2D = unsafe {
|
|
||||||
let resource = self.GetResource()?;
|
|
||||||
if resource.GetType() != D3D11_RESOURCE_DIMENSION_TEXTURE2D {
|
|
||||||
return Err(windows::core::Error::new(
|
|
||||||
E_NOINTERFACE,
|
|
||||||
"expected ID3D11Texture2D as the resource for the view.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// SAFETY: We know tha the resource is an `ID3D11Texture2D`.
|
|
||||||
// This downcast is safe because ID3D11Texture2D has ID3D11Resource in its
|
|
||||||
// inheritance chain.
|
|
||||||
//
|
|
||||||
// This check + transmute is cheaper than doing `.cast` (i.e. `QueryInterface`).
|
|
||||||
std::mem::transmute(resource)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut desc = Default::default();
|
|
||||||
unsafe {
|
|
||||||
parent.GetDesc(&mut desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Size {
|
|
||||||
height: desc.Height,
|
|
||||||
width: desc.Width,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for Direct3D11::ID3D11RenderTargetView {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
<&Self as GetSize<u32>>::size(&self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for &Direct3D11::ID3D11ShaderResourceView {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let parent: ID3D11Texture2D = unsafe {
|
|
||||||
let resource = self.GetResource()?;
|
|
||||||
if resource.GetType() != D3D11_RESOURCE_DIMENSION_TEXTURE2D {
|
|
||||||
return Err(windows::core::Error::new(
|
|
||||||
E_NOINTERFACE,
|
|
||||||
"expected ID3D11Texture2D as the resource for the view.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// SAFETY: We know tha the resource is an `ID3D11Texture2D`.
|
|
||||||
// This downcast is safe because ID3D11Texture2D has ID3D11Resource in its
|
|
||||||
// inheritance chain.
|
|
||||||
//
|
|
||||||
// This check + transmute is cheaper than doing `.cast` (i.e. `QueryInterface`).
|
|
||||||
std::mem::transmute(resource)
|
|
||||||
};
|
|
||||||
let mut desc = Default::default();
|
|
||||||
unsafe {
|
|
||||||
parent.GetDesc(&mut desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Size {
|
|
||||||
height: desc.Height,
|
|
||||||
width: desc.Width,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for Direct3D11::ID3D11ShaderResourceView {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
<&Self as GetSize<u32>>::size(&self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{FilterMode, GetSize, Size, WrapMode};
|
use crate::{FilterMode, WrapMode};
|
||||||
use windows::Win32::Graphics::Direct3D12;
|
use windows::Win32::Graphics::Direct3D12;
|
||||||
|
|
||||||
impl From<WrapMode> for Direct3D12::D3D12_TEXTURE_ADDRESS_MODE {
|
impl From<WrapMode> for Direct3D12::D3D12_TEXTURE_ADDRESS_MODE {
|
||||||
|
@ -20,12 +20,3 @@ impl From<FilterMode> for Direct3D12::D3D12_FILTER {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetSize<u32> for Direct3D12::ID3D12Resource {
|
|
||||||
type Error = std::convert::Infallible;
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let desc = unsafe { self.GetDesc() };
|
|
||||||
|
|
||||||
Ok(Size::new(desc.Width as u32, desc.Height))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
use crate::{FilterMode, GetSize, ImageFormat, Size, 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 GetSize<u32> for &Direct3D9::IDirect3DSurface9 {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let mut desc = Default::default();
|
|
||||||
unsafe {
|
|
||||||
self.GetDesc(&mut desc)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Size {
|
|
||||||
height: desc.Height,
|
|
||||||
width: desc.Width,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for Direct3D9::IDirect3DSurface9 {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
<&Self as GetSize<u32>>::size(&self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for &Direct3D9::IDirect3DTexture9 {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let mut desc = Default::default();
|
|
||||||
unsafe {
|
|
||||||
self.GetLevelDesc(0, &mut desc)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Size {
|
|
||||||
height: desc.Height,
|
|
||||||
width: desc.Width,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for Direct3D9::IDirect3DTexture9 {
|
|
||||||
type Error = windows::core::Error;
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
<&Self as GetSize<u32>>::size(&self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -1,71 +1,71 @@
|
||||||
use crate::{FilterMode, ImageFormat, WrapMode};
|
use crate::{FilterMode, ImageFormat, WrapMode};
|
||||||
|
|
||||||
impl From<ImageFormat> for u32 {
|
impl From<ImageFormat> for gl::types::GLenum {
|
||||||
fn from(format: ImageFormat) -> Self {
|
fn from(format: ImageFormat) -> Self {
|
||||||
match format {
|
match format {
|
||||||
ImageFormat::Unknown => 0,
|
ImageFormat::Unknown => 0 as gl::types::GLenum,
|
||||||
ImageFormat::R8Unorm => glow::R8,
|
ImageFormat::R8Unorm => gl::R8,
|
||||||
ImageFormat::R8Uint => glow::R8UI,
|
ImageFormat::R8Uint => gl::R8UI,
|
||||||
ImageFormat::R8Sint => glow::R8I,
|
ImageFormat::R8Sint => gl::R8I,
|
||||||
ImageFormat::R8G8Unorm => glow::RG8,
|
ImageFormat::R8G8Unorm => gl::RG8,
|
||||||
ImageFormat::R8G8Uint => glow::RG8UI,
|
ImageFormat::R8G8Uint => gl::RG8UI,
|
||||||
ImageFormat::R8G8Sint => glow::RG8I,
|
ImageFormat::R8G8Sint => gl::RG8I,
|
||||||
ImageFormat::R8G8B8A8Unorm => glow::RGBA8,
|
ImageFormat::R8G8B8A8Unorm => gl::RGBA8,
|
||||||
ImageFormat::R8G8B8A8Uint => glow::RGBA8UI,
|
ImageFormat::R8G8B8A8Uint => gl::RGBA8UI,
|
||||||
ImageFormat::R8G8B8A8Sint => glow::RGBA8I,
|
ImageFormat::R8G8B8A8Sint => gl::RGBA8I,
|
||||||
ImageFormat::R8G8B8A8Srgb => glow::SRGB8_ALPHA8,
|
ImageFormat::R8G8B8A8Srgb => gl::SRGB8_ALPHA8,
|
||||||
ImageFormat::A2B10G10R10UnormPack32 => glow::RGB10_A2,
|
ImageFormat::A2B10G10R10UnormPack32 => gl::RGB10_A2,
|
||||||
ImageFormat::A2B10G10R10UintPack32 => glow::RGB10_A2UI,
|
ImageFormat::A2B10G10R10UintPack32 => gl::RGB10_A2UI,
|
||||||
ImageFormat::R16Uint => glow::R16UI,
|
ImageFormat::R16Uint => gl::R16UI,
|
||||||
ImageFormat::R16Sint => glow::R16I,
|
ImageFormat::R16Sint => gl::R16I,
|
||||||
ImageFormat::R16Sfloat => glow::R16F,
|
ImageFormat::R16Sfloat => gl::R16F,
|
||||||
ImageFormat::R16G16Uint => glow::RG16UI,
|
ImageFormat::R16G16Uint => gl::RG16UI,
|
||||||
ImageFormat::R16G16Sint => glow::RG16I,
|
ImageFormat::R16G16Sint => gl::RG16I,
|
||||||
ImageFormat::R16G16Sfloat => glow::RG16F,
|
ImageFormat::R16G16Sfloat => gl::RG16F,
|
||||||
ImageFormat::R16G16B16A16Uint => glow::RGBA16UI,
|
ImageFormat::R16G16B16A16Uint => gl::RGBA16UI,
|
||||||
ImageFormat::R16G16B16A16Sint => glow::RGBA16I,
|
ImageFormat::R16G16B16A16Sint => gl::RGBA16I,
|
||||||
ImageFormat::R16G16B16A16Sfloat => glow::RGBA16F,
|
ImageFormat::R16G16B16A16Sfloat => gl::RGBA16F,
|
||||||
ImageFormat::R32Uint => glow::R32UI,
|
ImageFormat::R32Uint => gl::R32UI,
|
||||||
ImageFormat::R32Sint => glow::R32I,
|
ImageFormat::R32Sint => gl::R32I,
|
||||||
ImageFormat::R32Sfloat => glow::R32F,
|
ImageFormat::R32Sfloat => gl::R32F,
|
||||||
ImageFormat::R32G32Uint => glow::RG32UI,
|
ImageFormat::R32G32Uint => gl::RG32UI,
|
||||||
ImageFormat::R32G32Sint => glow::RG32I,
|
ImageFormat::R32G32Sint => gl::RG32I,
|
||||||
ImageFormat::R32G32Sfloat => glow::RG32F,
|
ImageFormat::R32G32Sfloat => gl::RG32F,
|
||||||
ImageFormat::R32G32B32A32Uint => glow::RGBA32UI,
|
ImageFormat::R32G32B32A32Uint => gl::RGBA32UI,
|
||||||
ImageFormat::R32G32B32A32Sint => glow::RGBA32I,
|
ImageFormat::R32G32B32A32Sint => gl::RGBA32I,
|
||||||
ImageFormat::R32G32B32A32Sfloat => glow::RGBA32F,
|
ImageFormat::R32G32B32A32Sfloat => gl::RGBA32F,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WrapMode> for i32 {
|
impl From<WrapMode> for gl::types::GLenum {
|
||||||
fn from(value: WrapMode) -> Self {
|
fn from(value: WrapMode) -> Self {
|
||||||
match value {
|
match value {
|
||||||
WrapMode::ClampToBorder => glow::CLAMP_TO_BORDER as i32,
|
WrapMode::ClampToBorder => gl::CLAMP_TO_BORDER,
|
||||||
WrapMode::ClampToEdge => glow::CLAMP_TO_EDGE as i32,
|
WrapMode::ClampToEdge => gl::CLAMP_TO_EDGE,
|
||||||
WrapMode::Repeat => glow::REPEAT as i32,
|
WrapMode::Repeat => gl::REPEAT,
|
||||||
WrapMode::MirroredRepeat => glow::MIRRORED_REPEAT as i32,
|
WrapMode::MirroredRepeat => gl::MIRRORED_REPEAT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FilterMode> for i32 {
|
impl From<FilterMode> for gl::types::GLenum {
|
||||||
fn from(value: FilterMode) -> Self {
|
fn from(value: FilterMode) -> Self {
|
||||||
match value {
|
match value {
|
||||||
FilterMode::Linear => glow::LINEAR as i32,
|
FilterMode::Linear => gl::LINEAR,
|
||||||
_ => glow::NEAREST as i32,
|
_ => gl::NEAREST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterMode {
|
impl FilterMode {
|
||||||
/// Get the mipmap filtering mode for the given combination.
|
/// Get the mipmap filtering mode for the given combination.
|
||||||
pub fn gl_mip(&self, mip: FilterMode) -> u32 {
|
pub fn gl_mip(&self, mip: FilterMode) -> gl::types::GLenum {
|
||||||
match (self, mip) {
|
match (self, mip) {
|
||||||
(FilterMode::Linear, FilterMode::Linear) => glow::LINEAR_MIPMAP_LINEAR,
|
(FilterMode::Linear, FilterMode::Linear) => gl::LINEAR_MIPMAP_LINEAR,
|
||||||
(FilterMode::Linear, FilterMode::Nearest) => glow::LINEAR_MIPMAP_NEAREST,
|
(FilterMode::Linear, FilterMode::Nearest) => gl::LINEAR_MIPMAP_NEAREST,
|
||||||
(FilterMode::Nearest, FilterMode::Linear) => glow::NEAREST_MIPMAP_LINEAR,
|
(FilterMode::Nearest, FilterMode::Linear) => gl::NEAREST_MIPMAP_LINEAR,
|
||||||
_ => glow::NEAREST_MIPMAP_NEAREST,
|
_ => gl::NEAREST_MIPMAP_NEAREST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,10 @@ pub mod gl;
|
||||||
#[cfg(feature = "vulkan")]
|
#[cfg(feature = "vulkan")]
|
||||||
pub mod vk;
|
pub mod vk;
|
||||||
|
|
||||||
/// WGPU common conversions.
|
|
||||||
#[cfg(feature = "wgpu")]
|
|
||||||
pub mod wgpu;
|
|
||||||
|
|
||||||
/// DXGI common conversions.
|
/// DXGI common conversions.
|
||||||
#[cfg(all(target_os = "windows", feature = "dxgi"))]
|
#[cfg(all(target_os = "windows", feature = "dxgi"))]
|
||||||
pub mod dxgi;
|
pub mod dxgi;
|
||||||
|
|
||||||
/// Direct3D 9 common conversions.
|
|
||||||
#[cfg(all(target_os = "windows", feature = "d3d9"))]
|
|
||||||
pub mod d3d9;
|
|
||||||
|
|
||||||
/// Direct3D 11 common conversions.
|
/// Direct3D 11 common conversions.
|
||||||
#[cfg(all(target_os = "windows", feature = "d3d11"))]
|
#[cfg(all(target_os = "windows", feature = "d3d11"))]
|
||||||
pub mod d3d11;
|
pub mod d3d11;
|
||||||
|
@ -28,32 +20,15 @@ pub mod d3d11;
|
||||||
#[cfg(all(target_os = "windows", feature = "d3d12"))]
|
#[cfg(all(target_os = "windows", feature = "d3d12"))]
|
||||||
pub mod d3d12;
|
pub mod d3d12;
|
||||||
|
|
||||||
#[cfg(all(target_vendor = "apple", feature = "metal"))]
|
|
||||||
pub mod metal;
|
|
||||||
|
|
||||||
mod viewport;
|
mod viewport;
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub mod map;
|
|
||||||
|
|
||||||
pub use viewport::Viewport;
|
pub use viewport::Viewport;
|
||||||
|
|
||||||
use num_traits::{AsPrimitive, Num};
|
use num_traits::AsPrimitive;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::ops::{Add, Sub};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub enum StorageType {
|
|
||||||
/// The fully qualified path to the resource, often a shader source file or a texture.
|
|
||||||
Path(std::path::PathBuf),
|
|
||||||
String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
/// Supported image formats for textures.
|
/// Supported image formats for textures.
|
||||||
pub enum ImageFormat {
|
pub enum ImageFormat {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -100,7 +75,6 @@ pub enum ImageFormat {
|
||||||
|
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
/// The filtering mode for a texture sampler.
|
/// The filtering mode for a texture sampler.
|
||||||
pub enum FilterMode {
|
pub enum FilterMode {
|
||||||
/// Linear filtering.
|
/// Linear filtering.
|
||||||
|
@ -139,7 +113,6 @@ impl FromStr for FilterMode {
|
||||||
|
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
/// The wrapping (address) mode for a texture sampler.
|
/// The wrapping (address) mode for a texture sampler.
|
||||||
pub enum WrapMode {
|
pub enum WrapMode {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -200,7 +173,6 @@ impl FromStr for ImageFormat {
|
||||||
|
|
||||||
/// A size with a width and height.
|
/// A size with a width and height.
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct Size<T> {
|
pub struct Size<T> {
|
||||||
pub width: T,
|
pub width: T,
|
||||||
pub height: T,
|
pub height: T,
|
||||||
|
@ -213,50 +185,6 @@ impl<T> Size<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sub<Output = T>> Sub for Size<T> {
|
|
||||||
type Output = Size<T>;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
width: self.width - rhs.width,
|
|
||||||
height: self.height - rhs.height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Sub<T, Output = T> + Copy> Sub<T> for Size<T> {
|
|
||||||
type Output = Size<T>;
|
|
||||||
|
|
||||||
fn sub(self, rhs: T) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
width: self.width - rhs,
|
|
||||||
height: self.height - rhs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Add<Output = T>> Add for Size<T> {
|
|
||||||
type Output = Size<T>;
|
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
width: self.width + rhs.width,
|
|
||||||
height: self.height + rhs.height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Add<T, Output = T> + Copy> Add<T> for Size<T> {
|
|
||||||
type Output = Size<T>;
|
|
||||||
|
|
||||||
fn add(self, rhs: T) -> Self::Output {
|
|
||||||
Self {
|
|
||||||
width: self.width + rhs,
|
|
||||||
height: self.height + rhs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> From<Size<T>> for [f32; 4]
|
impl<T> From<Size<T>> for [f32; 4]
|
||||||
where
|
where
|
||||||
T: Copy + AsPrimitive<f32>,
|
T: Copy + AsPrimitive<f32>,
|
||||||
|
@ -271,22 +199,3 @@ where
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for surface or texture objects that can fetch size.
|
|
||||||
pub trait GetSize<C: Num> {
|
|
||||||
type Error;
|
|
||||||
/// Fetch the size of the object
|
|
||||||
fn size(&self) -> Result<Size<C>, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: GetSize<u32>> GetSize<f32> for T {
|
|
||||||
type Error = T::Error;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<f32>, Self::Error> {
|
|
||||||
let size = <T as GetSize<u32>>::size(self)?;
|
|
||||||
Ok(Size {
|
|
||||||
width: size.width as f32,
|
|
||||||
height: size.height as f32,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
/// A hashmap optimized for small sets of size less than 32 with a fast hash implementation.
|
|
||||||
///
|
|
||||||
/// Used widely for shader reflection.
|
|
||||||
pub type FastHashMap<K, V> =
|
|
||||||
halfbrown::SizedHashMap<K, V, core::hash::BuildHasherDefault<rustc_hash::FxHasher>, 32>;
|
|
||||||
|
|
||||||
/// A string with small string optimizations up to 23 bytes.
|
|
||||||
pub type ShortString = smartstring::SmartString<smartstring::LazyCompact>;
|
|
|
@ -1,122 +0,0 @@
|
||||||
use crate::{FilterMode, GetSize, ImageFormat, Size, WrapMode};
|
|
||||||
use objc2::runtime::ProtocolObject;
|
|
||||||
use objc2_metal::{
|
|
||||||
MTLPixelFormat, MTLSamplerAddressMode, MTLSamplerMinMagFilter, MTLSamplerMipFilter, MTLTexture,
|
|
||||||
MTLViewport,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl From<ImageFormat> for MTLPixelFormat {
|
|
||||||
fn from(format: ImageFormat) -> Self {
|
|
||||||
match format {
|
|
||||||
ImageFormat::Unknown => MTLPixelFormat(0),
|
|
||||||
ImageFormat::R8Unorm => MTLPixelFormat::R8Unorm,
|
|
||||||
ImageFormat::R8Uint => MTLPixelFormat::R8Uint,
|
|
||||||
ImageFormat::R8Sint => MTLPixelFormat::R8Sint,
|
|
||||||
ImageFormat::R8G8Unorm => MTLPixelFormat::RG8Unorm,
|
|
||||||
ImageFormat::R8G8Uint => MTLPixelFormat::RG8Uint,
|
|
||||||
ImageFormat::R8G8Sint => MTLPixelFormat::RG8Sint,
|
|
||||||
ImageFormat::R8G8B8A8Unorm => MTLPixelFormat::RGBA8Unorm,
|
|
||||||
ImageFormat::R8G8B8A8Uint => MTLPixelFormat::RGBA8Uint,
|
|
||||||
ImageFormat::R8G8B8A8Sint => MTLPixelFormat::RGBA8Sint,
|
|
||||||
ImageFormat::R8G8B8A8Srgb => MTLPixelFormat::RGBA8Unorm_sRGB,
|
|
||||||
ImageFormat::A2B10G10R10UnormPack32 => MTLPixelFormat::RGB10A2Unorm,
|
|
||||||
ImageFormat::A2B10G10R10UintPack32 => MTLPixelFormat::RGB10A2Uint,
|
|
||||||
ImageFormat::R16Uint => MTLPixelFormat::R16Uint,
|
|
||||||
ImageFormat::R16Sint => MTLPixelFormat::R16Sint,
|
|
||||||
ImageFormat::R16Sfloat => MTLPixelFormat::R16Float,
|
|
||||||
ImageFormat::R16G16Uint => MTLPixelFormat::RG16Uint,
|
|
||||||
ImageFormat::R16G16Sint => MTLPixelFormat::RG16Sint,
|
|
||||||
ImageFormat::R16G16Sfloat => MTLPixelFormat::RG16Float,
|
|
||||||
ImageFormat::R16G16B16A16Uint => MTLPixelFormat::RGBA16Uint,
|
|
||||||
ImageFormat::R16G16B16A16Sint => MTLPixelFormat::RGBA16Sint,
|
|
||||||
ImageFormat::R16G16B16A16Sfloat => MTLPixelFormat::RGBA16Float,
|
|
||||||
ImageFormat::R32Uint => MTLPixelFormat::R32Uint,
|
|
||||||
ImageFormat::R32Sint => MTLPixelFormat::R32Sint,
|
|
||||||
ImageFormat::R32Sfloat => MTLPixelFormat::R32Float,
|
|
||||||
ImageFormat::R32G32Uint => MTLPixelFormat::RG32Uint,
|
|
||||||
ImageFormat::R32G32Sint => MTLPixelFormat::RG32Sint,
|
|
||||||
ImageFormat::R32G32Sfloat => MTLPixelFormat::RG32Float,
|
|
||||||
ImageFormat::R32G32B32A32Uint => MTLPixelFormat::RGBA32Uint,
|
|
||||||
ImageFormat::R32G32B32A32Sint => MTLPixelFormat::RGBA32Sint,
|
|
||||||
ImageFormat::R32G32B32A32Sfloat => MTLPixelFormat::RGBA32Float,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MTLViewport> for Size<u32> {
|
|
||||||
fn from(value: MTLViewport) -> Self {
|
|
||||||
Size {
|
|
||||||
width: value.width as u32,
|
|
||||||
height: value.height as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Size<u32>> for MTLViewport {
|
|
||||||
fn from(value: Size<u32>) -> Self {
|
|
||||||
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 MTLSamplerAddressMode {
|
|
||||||
fn from(value: WrapMode) -> Self {
|
|
||||||
match value {
|
|
||||||
WrapMode::ClampToBorder => MTLSamplerAddressMode::ClampToBorderColor,
|
|
||||||
WrapMode::ClampToEdge => MTLSamplerAddressMode::ClampToEdge,
|
|
||||||
WrapMode::Repeat => MTLSamplerAddressMode::Repeat,
|
|
||||||
WrapMode::MirroredRepeat => MTLSamplerAddressMode::MirrorRepeat,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FilterMode> for MTLSamplerMinMagFilter {
|
|
||||||
fn from(value: FilterMode) -> Self {
|
|
||||||
match value {
|
|
||||||
FilterMode::Linear => MTLSamplerMinMagFilter::Linear,
|
|
||||||
_ => MTLSamplerMinMagFilter::Nearest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FilterMode {
|
|
||||||
/// Get the mipmap filtering mode for the given combination.
|
|
||||||
pub fn mtl_mip(&self, _mip: FilterMode) -> MTLSamplerMipFilter {
|
|
||||||
match self {
|
|
||||||
FilterMode::Linear => MTLSamplerMipFilter::Linear,
|
|
||||||
FilterMode::Nearest => MTLSamplerMipFilter::Nearest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for ProtocolObject<dyn MTLTexture> {
|
|
||||||
type Error = std::convert::Infallible;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let height = self.height();
|
|
||||||
let width = self.width();
|
|
||||||
Ok(Size {
|
|
||||||
height: height as u32,
|
|
||||||
width: width as u32,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GetSize<u32> for &ProtocolObject<dyn MTLTexture> {
|
|
||||||
type Error = std::convert::Infallible;
|
|
||||||
|
|
||||||
fn size(&self) -> Result<Size<u32>, Self::Error> {
|
|
||||||
let height = self.height();
|
|
||||||
let width = self.width();
|
|
||||||
Ok(Size {
|
|
||||||
height: height as u32,
|
|
||||||
width: width as u32,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,6 @@
|
||||||
use crate::{GetSize, Size};
|
|
||||||
|
|
||||||
/// The rendering output of a filter chain.
|
/// The rendering output of a filter chain.
|
||||||
///
|
|
||||||
/// Viewport coordinates are relative to the coordinate system of the
|
|
||||||
/// target runtime. For correct results, `x` and `y` should almost always be
|
|
||||||
/// 0, and `size` should be the same as the size of the output texture.
|
|
||||||
///
|
|
||||||
/// Size uniforms will always be passed the full size of the output texture,
|
|
||||||
/// regardless of the user-specified viewport size.
|
|
||||||
pub struct Viewport<'a, T> {
|
pub struct Viewport<'a, T> {
|
||||||
/// The x offset to start rendering from. For correct results, this should almost
|
/// The x offset to start rendering from.
|
||||||
/// always be 0 to indicate the origin.
|
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
/// The y offset to begin rendering from.
|
/// The y offset to begin rendering from.
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
|
@ -19,28 +9,4 @@ pub struct Viewport<'a, T> {
|
||||||
pub mvp: Option<&'a [f32; 16]>,
|
pub mvp: Option<&'a [f32; 16]>,
|
||||||
/// The output handle to render the final image to.
|
/// The output handle to render the final image to.
|
||||||
pub output: T,
|
pub output: T,
|
||||||
/// The extent of the viewport size starting from the origin defined
|
|
||||||
/// by x and y.
|
|
||||||
pub size: Size<u32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: GetSize<u32>> Viewport<'a, T> {
|
|
||||||
/// Create a new viewport from an output that can be sized.
|
|
||||||
///
|
|
||||||
/// This will create a viewport that spans the entire output texture,
|
|
||||||
/// which will give correct results in the general case.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn new_render_target_sized_origin(
|
|
||||||
output: T,
|
|
||||||
mvp: Option<&'a [f32; 16]>,
|
|
||||||
) -> Result<Viewport<'a, T>, T::Error> {
|
|
||||||
let size = output.size()?;
|
|
||||||
Ok(Self {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
mvp,
|
|
||||||
output,
|
|
||||||
size,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
use crate::{FilterMode, ImageFormat, Size, WrapMode};
|
|
||||||
|
|
||||||
impl From<ImageFormat> for Option<wgpu_types::TextureFormat> {
|
|
||||||
fn from(format: ImageFormat) -> Self {
|
|
||||||
match format {
|
|
||||||
ImageFormat::Unknown => None,
|
|
||||||
ImageFormat::R8Unorm => Some(wgpu_types::TextureFormat::R8Unorm),
|
|
||||||
ImageFormat::R8Uint => Some(wgpu_types::TextureFormat::R8Uint),
|
|
||||||
ImageFormat::R8Sint => Some(wgpu_types::TextureFormat::R8Sint),
|
|
||||||
ImageFormat::R8G8Unorm => Some(wgpu_types::TextureFormat::Rg8Unorm),
|
|
||||||
ImageFormat::R8G8Uint => Some(wgpu_types::TextureFormat::Rg8Uint),
|
|
||||||
ImageFormat::R8G8Sint => Some(wgpu_types::TextureFormat::Rg8Sint),
|
|
||||||
ImageFormat::R8G8B8A8Unorm => Some(wgpu_types::TextureFormat::Rgba8Unorm),
|
|
||||||
ImageFormat::R8G8B8A8Uint => Some(wgpu_types::TextureFormat::Rgba8Uint),
|
|
||||||
ImageFormat::R8G8B8A8Sint => Some(wgpu_types::TextureFormat::Rgba8Sint),
|
|
||||||
ImageFormat::R8G8B8A8Srgb => Some(wgpu_types::TextureFormat::Rgba8UnormSrgb),
|
|
||||||
ImageFormat::A2B10G10R10UnormPack32 => Some(wgpu_types::TextureFormat::Rgb10a2Unorm),
|
|
||||||
ImageFormat::A2B10G10R10UintPack32 => Some(wgpu_types::TextureFormat::Rgb10a2Uint),
|
|
||||||
ImageFormat::R16Uint => Some(wgpu_types::TextureFormat::R16Uint),
|
|
||||||
ImageFormat::R16Sint => Some(wgpu_types::TextureFormat::R16Sint),
|
|
||||||
ImageFormat::R16Sfloat => Some(wgpu_types::TextureFormat::R16Float),
|
|
||||||
ImageFormat::R16G16Uint => Some(wgpu_types::TextureFormat::Rg16Uint),
|
|
||||||
ImageFormat::R16G16Sint => Some(wgpu_types::TextureFormat::Rg16Sint),
|
|
||||||
ImageFormat::R16G16Sfloat => Some(wgpu_types::TextureFormat::Rg16Float),
|
|
||||||
ImageFormat::R16G16B16A16Uint => Some(wgpu_types::TextureFormat::Rgba16Uint),
|
|
||||||
ImageFormat::R16G16B16A16Sint => Some(wgpu_types::TextureFormat::Rgba16Sint),
|
|
||||||
ImageFormat::R16G16B16A16Sfloat => Some(wgpu_types::TextureFormat::Rgba16Float),
|
|
||||||
ImageFormat::R32Uint => Some(wgpu_types::TextureFormat::R32Uint),
|
|
||||||
ImageFormat::R32Sint => Some(wgpu_types::TextureFormat::R32Sint),
|
|
||||||
ImageFormat::R32Sfloat => Some(wgpu_types::TextureFormat::R32Float),
|
|
||||||
ImageFormat::R32G32Uint => Some(wgpu_types::TextureFormat::Rg32Uint),
|
|
||||||
ImageFormat::R32G32Sint => Some(wgpu_types::TextureFormat::Rg32Sint),
|
|
||||||
ImageFormat::R32G32Sfloat => Some(wgpu_types::TextureFormat::Rg32Float),
|
|
||||||
ImageFormat::R32G32B32A32Uint => Some(wgpu_types::TextureFormat::Rgba32Uint),
|
|
||||||
ImageFormat::R32G32B32A32Sint => Some(wgpu_types::TextureFormat::Rgba32Sint),
|
|
||||||
ImageFormat::R32G32B32A32Sfloat => Some(wgpu_types::TextureFormat::Rgba32Float),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<wgpu_types::TextureFormat> for ImageFormat {
|
|
||||||
fn from(format: wgpu_types::TextureFormat) -> Self {
|
|
||||||
match format {
|
|
||||||
wgpu_types::TextureFormat::R8Unorm => ImageFormat::R8Unorm,
|
|
||||||
wgpu_types::TextureFormat::R8Uint => ImageFormat::R8Uint,
|
|
||||||
wgpu_types::TextureFormat::R8Sint => ImageFormat::R8Sint,
|
|
||||||
wgpu_types::TextureFormat::Rg8Unorm => ImageFormat::R8G8Unorm,
|
|
||||||
wgpu_types::TextureFormat::Rg8Uint => ImageFormat::R8G8Uint,
|
|
||||||
wgpu_types::TextureFormat::Rg8Sint => ImageFormat::R8G8Sint,
|
|
||||||
wgpu_types::TextureFormat::Rgba8Unorm => ImageFormat::R8G8B8A8Unorm,
|
|
||||||
wgpu_types::TextureFormat::Rgba8Uint => ImageFormat::R8G8B8A8Uint,
|
|
||||||
wgpu_types::TextureFormat::Rgba8Sint => ImageFormat::R8G8B8A8Sint,
|
|
||||||
wgpu_types::TextureFormat::Rgba8UnormSrgb => ImageFormat::R8G8B8A8Srgb,
|
|
||||||
wgpu_types::TextureFormat::Rgb10a2Unorm => ImageFormat::A2B10G10R10UnormPack32,
|
|
||||||
wgpu_types::TextureFormat::Rgb10a2Uint => ImageFormat::A2B10G10R10UintPack32,
|
|
||||||
wgpu_types::TextureFormat::R16Uint => ImageFormat::R16Uint,
|
|
||||||
wgpu_types::TextureFormat::R16Sint => ImageFormat::R16Sint,
|
|
||||||
wgpu_types::TextureFormat::R16Float => ImageFormat::R16Sfloat,
|
|
||||||
wgpu_types::TextureFormat::Rg16Uint => ImageFormat::R16G16Uint,
|
|
||||||
wgpu_types::TextureFormat::Rg16Sint => ImageFormat::R16G16Sint,
|
|
||||||
wgpu_types::TextureFormat::Rg16Float => ImageFormat::R16G16Sfloat,
|
|
||||||
wgpu_types::TextureFormat::Rgba16Uint => ImageFormat::R16G16B16A16Uint,
|
|
||||||
wgpu_types::TextureFormat::Rgba16Sint => ImageFormat::R16G16B16A16Sint,
|
|
||||||
wgpu_types::TextureFormat::Rgba16Float => ImageFormat::R16G16B16A16Sfloat,
|
|
||||||
wgpu_types::TextureFormat::R32Uint => ImageFormat::R32Uint,
|
|
||||||
wgpu_types::TextureFormat::R32Sint => ImageFormat::R32Sint,
|
|
||||||
wgpu_types::TextureFormat::R32Float => ImageFormat::R32Sfloat,
|
|
||||||
wgpu_types::TextureFormat::Rg32Uint => ImageFormat::R32G32Uint,
|
|
||||||
wgpu_types::TextureFormat::Rg32Sint => ImageFormat::R32G32Sint,
|
|
||||||
wgpu_types::TextureFormat::Rg32Float => ImageFormat::R32G32Sfloat,
|
|
||||||
wgpu_types::TextureFormat::Rgba32Uint => ImageFormat::R32G32B32A32Uint,
|
|
||||||
wgpu_types::TextureFormat::Rgba32Sint => ImageFormat::R32G32B32A32Sint,
|
|
||||||
wgpu_types::TextureFormat::Rgba32Float => ImageFormat::R32G32B32A32Sfloat,
|
|
||||||
_ => ImageFormat::Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Option<wgpu_types::TextureFormat>> for ImageFormat {
|
|
||||||
fn from(format: Option<wgpu_types::TextureFormat>) -> Self {
|
|
||||||
let Some(format) = format else {
|
|
||||||
return ImageFormat::Unknown;
|
|
||||||
};
|
|
||||||
ImageFormat::from(format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<wgpu_types::Extent3d> for Size<u32> {
|
|
||||||
fn from(value: wgpu_types::Extent3d) -> Self {
|
|
||||||
Size {
|
|
||||||
width: value.width,
|
|
||||||
height: value.height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FilterMode> for wgpu_types::FilterMode {
|
|
||||||
fn from(value: FilterMode) -> Self {
|
|
||||||
match value {
|
|
||||||
FilterMode::Linear => wgpu_types::FilterMode::Linear,
|
|
||||||
FilterMode::Nearest => wgpu_types::FilterMode::Nearest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WrapMode> for wgpu_types::AddressMode {
|
|
||||||
fn from(value: WrapMode) -> Self {
|
|
||||||
match value {
|
|
||||||
WrapMode::ClampToBorder => wgpu_types::AddressMode::ClampToBorder,
|
|
||||||
WrapMode::ClampToEdge => wgpu_types::AddressMode::ClampToEdge,
|
|
||||||
WrapMode::Repeat => wgpu_types::AddressMode::Repeat,
|
|
||||||
WrapMode::MirroredRepeat => wgpu_types::AddressMode::MirrorRepeat,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Size<u32>> for wgpu_types::Extent3d {
|
|
||||||
fn from(value: Size<u32>) -> Self {
|
|
||||||
wgpu_types::Extent3d {
|
|
||||||
width: value.width,
|
|
||||||
height: value.height,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "librashader-pack"
|
|
||||||
version = "0.5.1"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
|
||||||
readme = "../README.md"
|
|
||||||
categories = ["emulators", "compilers", "graphics"]
|
|
||||||
keywords = ["shader", "retroarch", "SPIR-V"]
|
|
||||||
description = "RetroArch shaders for all."
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
librashader-presets = { path = "../librashader-presets", version = "0.5.1", features = [
|
|
||||||
"serde",
|
|
||||||
] }
|
|
||||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.5.1", features = [
|
|
||||||
"serde",
|
|
||||||
] }
|
|
||||||
librashader-common = { path = "../librashader-common", version = "0.5.1", features = [
|
|
||||||
"serde",
|
|
||||||
] }
|
|
||||||
|
|
||||||
thiserror = "1.0.64"
|
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
|
||||||
rayon = { workspace = true }
|
|
||||||
image = { workspace = true }
|
|
||||||
base64 = { version = "0.22.1", optional = true }
|
|
||||||
serde_bytes = { version = "0.11.15", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
parse_legacy_glsl = ["librashader-presets/parse_legacy_glsl"]
|
|
||||||
serde = ["dep:serde", "dep:base64", "dep:serde_bytes"]
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch="wasm32"))'.dependencies]
|
|
||||||
rayon = { workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
anyhow = "1.0.89"
|
|
||||||
serde_json = "1.0.128"
|
|
||||||
rmp-serde = "1.3.0"
|
|
|
@ -1,254 +0,0 @@
|
||||||
//! Shader preset resource handling for librashader.
|
|
||||||
//!
|
|
||||||
//! This crate contains facilities to load shader preset resources from a [`ShaderPreset`].
|
|
||||||
//!
|
|
||||||
//! Also defines abstractly the `.slangpack` shader format implemented via serde derives on [`ShaderPresetPack`].
|
|
||||||
//!
|
|
||||||
use image::{ImageError, RgbaImage};
|
|
||||||
use librashader_common::StorageType;
|
|
||||||
use librashader_preprocess::{PreprocessError, ShaderSource};
|
|
||||||
use librashader_presets::{ParameterMeta, PassMeta, ShaderPreset, TextureMeta};
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use rayon::prelude::*;
|
|
||||||
|
|
||||||
/// A buffer holding RGBA image bytes.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct TextureBuffer {
|
|
||||||
#[cfg_attr(feature = "serde", serde(with = "serde_base64_or_bytes"))]
|
|
||||||
image: Vec<u8>,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TextureBuffer> for Option<RgbaImage> {
|
|
||||||
fn from(value: TextureBuffer) -> Self {
|
|
||||||
RgbaImage::from_raw(value.width, value.height, value.image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for TextureBuffer {
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
self.image.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RgbaImage> for TextureBuffer {
|
|
||||||
fn from(value: RgbaImage) -> Self {
|
|
||||||
let width = value.width();
|
|
||||||
let height = value.height();
|
|
||||||
TextureBuffer {
|
|
||||||
image: value.into_raw(),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A resource for a shader preset, fully loaded into memory.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct LoadedResource<M: LoadableResource> {
|
|
||||||
/// The fully qualified path to the texture.
|
|
||||||
pub data: M::ResourceType,
|
|
||||||
/// Meta information about the texture.
|
|
||||||
pub meta: M,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for a resource that is loadable from disk.
|
|
||||||
pub trait LoadableResource {
|
|
||||||
/// The type of the resource.
|
|
||||||
type ResourceType;
|
|
||||||
/// The error type when loading the resource.
|
|
||||||
type Error;
|
|
||||||
/// Load the resource from the path.
|
|
||||||
fn load(storage: &StorageType) -> Result<Self::ResourceType, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoadableResource for PassMeta {
|
|
||||||
type ResourceType = ShaderSource;
|
|
||||||
type Error = PreprocessError;
|
|
||||||
|
|
||||||
fn load(storage: &StorageType) -> Result<Self::ResourceType, Self::Error> {
|
|
||||||
ShaderSource::load(storage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LoadableResource for TextureMeta {
|
|
||||||
type ResourceType = TextureBuffer;
|
|
||||||
type Error = ImageError;
|
|
||||||
|
|
||||||
fn load(storage: &StorageType) -> Result<Self::ResourceType, Self::Error> {
|
|
||||||
let image = match storage {
|
|
||||||
StorageType::Path(path_buf) => image::open(path_buf),
|
|
||||||
StorageType::String(_) => todo!(),
|
|
||||||
};
|
|
||||||
image.map(|img| TextureBuffer::from(img.to_rgba8()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The loaded resource information for the source code of a shader pass.
|
|
||||||
pub type PassResource = LoadedResource<PassMeta>;
|
|
||||||
|
|
||||||
/// The loaded texture resource for a shader preset.
|
|
||||||
pub type TextureResource = LoadedResource<TextureMeta>;
|
|
||||||
|
|
||||||
/// A fully loaded-in-memory shader preset, with all paths resolved to data.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct ShaderPresetPack {
|
|
||||||
/// Used in legacy GLSL shader semantics. If < 0, no feedback pass is used.
|
|
||||||
/// Otherwise, the FBO after pass #N is passed a texture to next frame
|
|
||||||
#[cfg(feature = "parse_legacy_glsl")]
|
|
||||||
pub feedback_pass: i32,
|
|
||||||
|
|
||||||
/// The number of shaders enabled in the filter chain.
|
|
||||||
pub pass_count: i32,
|
|
||||||
// Everything is in Vecs because the expect number of values is well below 64.
|
|
||||||
/// Preset information for each shader.
|
|
||||||
pub passes: Vec<PassResource>,
|
|
||||||
|
|
||||||
/// Preset information for each texture.
|
|
||||||
pub textures: Vec<TextureResource>,
|
|
||||||
|
|
||||||
/// Preset information for each user parameter.
|
|
||||||
pub parameters: Vec<ParameterMeta>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShaderPresetPack {
|
|
||||||
/// Load a `ShaderPack` from a [`ShaderPreset`].
|
|
||||||
pub fn load_from_preset<E>(preset: ShaderPreset) -> Result<ShaderPresetPack, E>
|
|
||||||
where
|
|
||||||
E: From<PreprocessError>,
|
|
||||||
E: From<ImageError>,
|
|
||||||
E: Send,
|
|
||||||
{
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let shaders_iter = preset.passes.into_par_iter();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let shaders_iter = preset.shaders.into_iter();
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let textures_iter = preset.textures.into_par_iter();
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let textures_iter = preset.textures.into_iter();
|
|
||||||
|
|
||||||
Ok(ShaderPresetPack {
|
|
||||||
#[cfg(feature = "parse_legacy_glsl")]
|
|
||||||
feedback_pass: preset.feedback_pass,
|
|
||||||
|
|
||||||
pass_count: preset.pass_count,
|
|
||||||
passes: shaders_iter
|
|
||||||
.map(|v| {
|
|
||||||
Ok::<_, E>(PassResource {
|
|
||||||
data: PassMeta::load(&v.storage)?,
|
|
||||||
meta: v.meta,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
|
||||||
textures: textures_iter
|
|
||||||
.into_par_iter()
|
|
||||||
.map(|t| {
|
|
||||||
Ok::<_, E>(TextureResource {
|
|
||||||
data: TextureMeta::load(&t.storage)?,
|
|
||||||
meta: t.meta,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
|
||||||
parameters: preset.parameters,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
mod serde_base64_or_bytes {
|
|
||||||
use base64::display::Base64Display;
|
|
||||||
use base64::engine::general_purpose::STANDARD;
|
|
||||||
use base64::Engine;
|
|
||||||
use serde::{Deserializer, Serializer};
|
|
||||||
|
|
||||||
#[allow(clippy::ptr_arg)]
|
|
||||||
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
|
|
||||||
if s.is_human_readable() {
|
|
||||||
s.collect_str(&Base64Display::new(v, &STANDARD))
|
|
||||||
} else {
|
|
||||||
serde_bytes::serialize(v, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
|
|
||||||
if d.is_human_readable() {
|
|
||||||
struct Base64Visitor;
|
|
||||||
impl<'de> serde::de::Visitor<'de> for Base64Visitor {
|
|
||||||
type Value = Vec<u8>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("a base64 encoded string")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_bytes(v.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_bytes(v.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
STANDARD.decode(v).map_err(serde::de::Error::custom)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
self.visit_bytes(v.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.deserialize_str(Base64Visitor)
|
|
||||||
} else {
|
|
||||||
serde_bytes::deserialize(d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::ShaderPresetPack;
|
|
||||||
use librashader_presets::ShaderPreset;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
let preset =
|
|
||||||
ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap();
|
|
||||||
let resolved = ShaderPresetPack::load_from_preset::<anyhow::Error>(preset).unwrap();
|
|
||||||
let mut file = File::create("crt-royale.slangpack.json").unwrap();
|
|
||||||
file.write_all(serde_json::to_vec_pretty(&resolved).unwrap().as_ref())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rmp() {
|
|
||||||
let preset =
|
|
||||||
ShaderPreset::try_parse("../test/shaders_slang/crt/crt-royale.slangp").unwrap();
|
|
||||||
let resolved = ShaderPresetPack::load_from_preset::<anyhow::Error>(preset).unwrap();
|
|
||||||
let mut file = File::create("crt-royale.slangpack").unwrap();
|
|
||||||
file.write_all(rmp_serde::to_vec(&resolved).unwrap().as_ref())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ name = "librashader-preprocess"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
license = "MPL-2.0 OR GPL-3.0-only"
|
||||||
version = "0.5.1"
|
version = "0.1.3"
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
repository = "https://github.com/SnowflakePowered/librashader"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
@ -14,15 +14,15 @@ description = "RetroArch shaders for all."
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
nom = "7.1.1"
|
nom = "7.1.1"
|
||||||
librashader-common = { path = "../librashader-common", version = "0.5.1" }
|
librashader-common = { path = "../librashader-common", version = "0.1.3" }
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
encoding_rs = "0.8.31"
|
encoding_rs = "0.8.31"
|
||||||
serde = { version = "1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "line_directives" ]
|
default = [ "line_directives" ]
|
||||||
line_directives = []
|
line_directives = []
|
||||||
serde = ["dep:serde", "serde/derive", "librashader-common/serde"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
librashader-presets = "0.1.0-rc.3"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
rayon = "1.6.1"
|
rayon = "1.6.1"
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use librashader_common::map::ShortString;
|
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -28,7 +27,7 @@ pub enum PreprocessError {
|
||||||
PragmaParseError(String),
|
PragmaParseError(String),
|
||||||
/// The given pragma was declared multiple times with differing values.
|
/// The given pragma was declared multiple times with differing values.
|
||||||
#[error("duplicate pragma found")]
|
#[error("duplicate pragma found")]
|
||||||
DuplicatePragmaError(ShortString),
|
DuplicatePragmaError(String),
|
||||||
/// The image format requested by the shader was unknown or not supported.
|
/// The image format requested by the shader was unknown or not supported.
|
||||||
#[error("shader format is unknown or not found")]
|
#[error("shader format is unknown or not found")]
|
||||||
UnknownImageFormat,
|
UnknownImageFormat,
|
||||||
|
|
|
@ -25,7 +25,7 @@ fn read_file(path: impl AsRef<Path>) -> Result<String, PreprocessError> {
|
||||||
let buf = e.into_bytes();
|
let buf = e.into_bytes();
|
||||||
let decoder = WINDOWS_1252.new_decoder();
|
let decoder = WINDOWS_1252.new_decoder();
|
||||||
let Some(len) = decoder.max_utf8_buffer_length_without_replacement(buf.len()) else {
|
let Some(len) = decoder.max_utf8_buffer_length_without_replacement(buf.len()) else {
|
||||||
return Err(PreprocessError::EncodingError(path.to_path_buf()));
|
return Err(PreprocessError::EncodingError(path.to_path_buf()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut latin1_string = String::with_capacity(len);
|
let mut latin1_string = String::with_capacity(len);
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
//!
|
//!
|
||||||
//! This crate contains facilities and types for resolving `#include` directives in `.slang`
|
//! This crate contains facilities and types for resolving `#include` directives in `.slang`
|
||||||
//! into a single compilation unit. `#pragma` directives are also parsed and resolved as
|
//! into a single compilation unit. `#pragma` directives are also parsed and resolved as
|
||||||
//! [`ShaderParameter`] structs.
|
//! [`ShaderParameter`](crate::ShaderParameter) structs.
|
||||||
//!
|
//!
|
||||||
//! The resulting [`ShaderSource`]can then be passed into a
|
//! The resulting [`ShaderSource`](crate::ShaderSource) can then be passed into a
|
||||||
//! reflection target for reflection and compilation into the target shader format.
|
//! reflection target for reflection and compilation into the target shader format.
|
||||||
//!
|
//!
|
||||||
//! Re-exported as [`librashader::preprocess`](https://docs.rs/librashader/latest/librashader/preprocess/index.html).
|
//! Re-exported as [`librashader::preprocess`](https://docs.rs/librashader/latest/librashader/preprocess/index.html).
|
||||||
|
@ -15,13 +15,12 @@ mod stage;
|
||||||
|
|
||||||
use crate::include::read_source;
|
use crate::include::read_source;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
use librashader_common::map::{FastHashMap, ShortString};
|
use librashader_common::ImageFormat;
|
||||||
use librashader_common::{ImageFormat, StorageType};
|
use rustc_hash::FxHashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// The source file for a single shader pass.
|
/// The source file for a single shader pass.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct ShaderSource {
|
pub struct ShaderSource {
|
||||||
/// The source contents for the vertex shader.
|
/// The source contents for the vertex shader.
|
||||||
pub vertex: String,
|
pub vertex: String,
|
||||||
|
@ -30,10 +29,10 @@ pub struct ShaderSource {
|
||||||
pub fragment: String,
|
pub fragment: String,
|
||||||
|
|
||||||
/// The alias of the shader if available.
|
/// The alias of the shader if available.
|
||||||
pub name: Option<ShortString>,
|
pub name: Option<String>,
|
||||||
|
|
||||||
/// The list of shader parameters found in the shader source.
|
/// The list of shader parameters found in the shader source.
|
||||||
pub parameters: FastHashMap<ShortString, ShaderParameter>,
|
pub parameters: FxHashMap<String, ShaderParameter>,
|
||||||
|
|
||||||
/// The image format the shader expects.
|
/// The image format the shader expects.
|
||||||
pub format: ImageFormat,
|
pub format: ImageFormat,
|
||||||
|
@ -41,10 +40,9 @@ pub struct ShaderSource {
|
||||||
|
|
||||||
/// A user tweakable parameter for the shader as declared in source.
|
/// A user tweakable parameter for the shader as declared in source.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct ShaderParameter {
|
pub struct ShaderParameter {
|
||||||
/// The name of the parameter.
|
/// The name of the parameter.
|
||||||
pub id: ShortString,
|
pub id: String,
|
||||||
/// The description of the parameter.
|
/// The description of the parameter.
|
||||||
pub description: String,
|
pub description: String,
|
||||||
/// The initial value the parameter is set to.
|
/// The initial value the parameter is set to.
|
||||||
|
@ -60,11 +58,8 @@ pub struct ShaderParameter {
|
||||||
impl ShaderSource {
|
impl ShaderSource {
|
||||||
/// Load the source file at the given path, resolving includes relative to the location of the
|
/// Load the source file at the given path, resolving includes relative to the location of the
|
||||||
/// source file.
|
/// source file.
|
||||||
pub fn load(storage: &StorageType) -> Result<ShaderSource, PreprocessError> {
|
pub fn load(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> {
|
||||||
match storage {
|
load_shader_source(path)
|
||||||
StorageType::Path(path_buf) => load_shader_source(path_buf),
|
|
||||||
StorageType::String(source) => parse_shader_source(source.clone()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,14 +80,9 @@ impl SourceOutput for String {
|
||||||
|
|
||||||
pub(crate) fn load_shader_source(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> {
|
pub(crate) fn load_shader_source(path: impl AsRef<Path>) -> Result<ShaderSource, PreprocessError> {
|
||||||
let source = read_source(path)?;
|
let source = read_source(path)?;
|
||||||
parse_shader_source(source)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_shader_source(source: String) -> Result<ShaderSource, PreprocessError> {
|
|
||||||
let meta = pragma::parse_pragma_meta(&source)?;
|
let meta = pragma::parse_pragma_meta(&source)?;
|
||||||
|
|
||||||
let text = stage::process_stages(&source)?;
|
let text = stage::process_stages(&source)?;
|
||||||
let parameters = FastHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p)));
|
let parameters = FxHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p)));
|
||||||
|
|
||||||
Ok(ShaderSource {
|
Ok(ShaderSource {
|
||||||
vertex: text.vertex,
|
vertex: text.vertex,
|
||||||
|
|
|
@ -2,9 +2,7 @@ use crate::{PreprocessError, ShaderParameter};
|
||||||
use librashader_common::ImageFormat;
|
use librashader_common::ImageFormat;
|
||||||
use nom::bytes::complete::{is_not, tag, take_while};
|
use nom::bytes::complete::{is_not, tag, take_while};
|
||||||
|
|
||||||
use librashader_common::map::ShortString;
|
use nom::character::complete::multispace1;
|
||||||
use nom::character::complete::{multispace0, multispace1};
|
|
||||||
use nom::combinator::opt;
|
|
||||||
use nom::number::complete::float;
|
use nom::number::complete::float;
|
||||||
use nom::sequence::delimited;
|
use nom::sequence::delimited;
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
|
@ -14,7 +12,7 @@ use std::str::FromStr;
|
||||||
pub(crate) struct ShaderMeta {
|
pub(crate) struct ShaderMeta {
|
||||||
pub(crate) format: ImageFormat,
|
pub(crate) format: ImageFormat,
|
||||||
pub(crate) parameters: Vec<ShaderParameter>,
|
pub(crate) parameters: Vec<ShaderParameter>,
|
||||||
pub(crate) name: Option<ShortString>,
|
pub(crate) name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessError> {
|
fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessError> {
|
||||||
|
@ -37,25 +35,17 @@ fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessErro
|
||||||
let (input, minimum) = float(input)?;
|
let (input, minimum) = float(input)?;
|
||||||
let (input, _) = multispace1(input)?;
|
let (input, _) = multispace1(input)?;
|
||||||
let (input, maximum) = float(input)?;
|
let (input, maximum) = float(input)?;
|
||||||
|
let (input, _) = multispace1(input)?;
|
||||||
// Step is actually optional and defaults to 0.02
|
let (input, step) = float(input)?;
|
||||||
// This behaviour can be seen in shaders like
|
|
||||||
// crt/crt-slangtest-cubic.slangp
|
|
||||||
// which doesn't have a step argument
|
|
||||||
// #pragma parameter OUT_GAMMA "Monitor Output Gamma" 2.2 1.8 2.4
|
|
||||||
|
|
||||||
// https://github.com/libretro/slang-shaders/blob/0e2939787076e4a8a83be89175557fde23abe837/crt/shaders/crt-slangtest/parameters.inc#L1
|
|
||||||
let (input, _) = multispace0(input)?;
|
|
||||||
let (input, step) = opt(float)(input)?;
|
|
||||||
Ok((
|
Ok((
|
||||||
input,
|
input,
|
||||||
ShaderParameter {
|
ShaderParameter {
|
||||||
id: name.into(),
|
id: name.to_string(),
|
||||||
description: description.to_string(),
|
description: description.to_string(),
|
||||||
initial,
|
initial,
|
||||||
minimum,
|
minimum,
|
||||||
maximum,
|
maximum,
|
||||||
step: step.unwrap_or(0.02),
|
step,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -70,7 +60,7 @@ fn parse_parameter_string(input: &str) -> Result<ShaderParameter, PreprocessErro
|
||||||
Ok(param)
|
Ok(param)
|
||||||
} else {
|
} else {
|
||||||
Ok(ShaderParameter {
|
Ok(ShaderParameter {
|
||||||
id: name.into(),
|
id: name.to_string(),
|
||||||
description: description.to_string(),
|
description: description.to_string(),
|
||||||
initial: 0f32,
|
initial: 0f32,
|
||||||
minimum: 0f32,
|
minimum: 0f32,
|
||||||
|
@ -99,7 +89,7 @@ pub(crate) fn parse_pragma_meta(source: impl AsRef<str>) -> Result<ShaderMeta, P
|
||||||
|
|
||||||
if let Some(format_string) = line.strip_prefix("#pragma format ") {
|
if let Some(format_string) = line.strip_prefix("#pragma format ") {
|
||||||
if format != ImageFormat::Unknown {
|
if format != ImageFormat::Unknown {
|
||||||
return Err(PreprocessError::DuplicatePragmaError(line.into()));
|
return Err(PreprocessError::DuplicatePragmaError(line.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let format_string = format_string.trim();
|
let format_string = format_string.trim();
|
||||||
|
@ -110,12 +100,12 @@ pub(crate) fn parse_pragma_meta(source: impl AsRef<str>) -> Result<ShaderMeta, P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pragma_name) = line.strip_prefix("#pragma name ") {
|
if line.starts_with("#pragma name ") {
|
||||||
if name.is_some() {
|
if name.is_some() {
|
||||||
return Err(PreprocessError::DuplicatePragmaError(line.into()));
|
return Err(PreprocessError::DuplicatePragmaError(line.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
name = Some(ShortString::from(pragma_name.trim()))
|
name = Some(line.trim().to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +124,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_parameter_pragma() {
|
fn parses_parameter_pragma() {
|
||||||
assert_eq!(ShaderParameter {
|
assert_eq!(ShaderParameter {
|
||||||
id: "exc".into(),
|
id: "exc".to_string(),
|
||||||
description: "orizontal correction hack (games where players stay at center)".to_string(),
|
description: "orizontal correction hack (games where players stay at center)".to_string(),
|
||||||
initial: 0.0,
|
initial: 0.0,
|
||||||
minimum: -10.0,
|
minimum: -10.0,
|
||||||
|
@ -142,34 +132,4 @@ mod test {
|
||||||
step: 0.25
|
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())
|
}, 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".into(),
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parses_parameter_pragma_with_no_step() {
|
|
||||||
assert_eq!(
|
|
||||||
ShaderParameter {
|
|
||||||
id: "OUT_GAMMA".into(),
|
|
||||||
description: "Monitor Output Gamma".to_string(),
|
|
||||||
initial: 2.2,
|
|
||||||
minimum: 1.8,
|
|
||||||
maximum: 2.4,
|
|
||||||
step: 0.02
|
|
||||||
},
|
|
||||||
parse_parameter_string(
|
|
||||||
r#"#pragma parameter OUT_GAMMA "Monitor Output Gamma" 2.2 1.8 2.4"#
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,7 @@ pub(crate) fn process_stages(source: &str) -> Result<ShaderOutput, PreprocessErr
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.starts_with("#pragma name ")
|
if line.starts_with("#pragma name ") || line.starts_with("#pragma format ") {
|
||||||
|| line.starts_with("#pragma format ")
|
|
||||||
|| line.starts_with("#pragma parameter ")
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
librashader-preprocess/tests/parse_all.rs
Normal file
24
librashader-preprocess/tests/parse_all.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
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"
|
edition = "2021"
|
||||||
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
license = "MPL-2.0 OR GPL-3.0-only"
|
||||||
version = "0.5.1"
|
version = "0.1.3"
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
repository = "https://github.com/SnowflakePowered/librashader"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
@ -15,18 +15,11 @@ description = "RetroArch shaders for all."
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
nom = "7.1.1"
|
nom = "7.1.1"
|
||||||
nom_locate = "4.0.0"
|
nom_locate = "4.0.0"
|
||||||
librashader-common = { path = "../librashader-common", version = "0.5.1" }
|
librashader-common = { path = "../librashader-common", version = "0.1.3" }
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
once_cell = "1"
|
|
||||||
# we don't need unicode
|
|
||||||
regex = { version = "1", default-features = false, features = ["perf"] }
|
|
||||||
vec_extract_if_polyfill = "0.1.0"
|
|
||||||
|
|
||||||
serde = { version = "1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
parse_legacy_glsl = []
|
parse_legacy_glsl = []
|
||||||
serde = ["dep:serde", "serde/derive", "librashader-common/serde"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
|
|
|
@ -1,413 +0,0 @@
|
||||||
// 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The wildcard key associated with the context item.
|
|
||||||
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()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the context into a string hashmap.
|
|
||||||
///
|
|
||||||
/// This is a one way conversion, and will normalize rotation context items
|
|
||||||
/// into `VID-FINAL-ROT`.
|
|
||||||
pub fn into_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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,7 +25,7 @@ pub enum ParsePresetError {
|
||||||
#[error("shader presets must be resolved against an absolute path")]
|
#[error("shader presets must be resolved against an absolute path")]
|
||||||
RootPathWasNotAbsolute,
|
RootPathWasNotAbsolute,
|
||||||
/// An IO error occurred when reading the shader preset.
|
/// An IO error occurred when reading the shader preset.
|
||||||
#[error("io error on file {0:?}: {1}")]
|
#[error("the file was not found during resolution")]
|
||||||
IOError(PathBuf, std::io::Error),
|
IOError(PathBuf, std::io::Error),
|
||||||
/// The shader preset did not contain valid UTF-8 bytes.
|
/// The shader preset did not contain valid UTF-8 bytes.
|
||||||
#[error("expected utf8 bytes but got invalid utf8")]
|
#[error("expected utf8 bytes but got invalid utf8")]
|
||||||
|
|
|
@ -3,16 +3,14 @@
|
||||||
//! This crate contains facilities and types for parsing `.slangp` shader presets files.
|
//! This crate contains facilities and types for parsing `.slangp` shader presets files.
|
||||||
//!
|
//!
|
||||||
//! Shader presets contain shader and texture parameters, and the order in which to apply a set of
|
//! Shader presets contain shader and texture parameters, and the order in which to apply a set of
|
||||||
//! shaders in a filter chain. A librashader runtime takes a resulting [`ShaderPreset`]
|
//! shaders in a filter chain. A librashader runtime takes a resulting [`ShaderPreset`](crate::ShaderPreset)
|
||||||
//! as input to create a filter chain.
|
//! as input to create a filter chain.
|
||||||
//!
|
//!
|
||||||
//! Re-exported as [`librashader::presets`](https://docs.rs/librashader/latest/librashader/presets/index.html).
|
//! Re-exported as [`librashader::presets`](https://docs.rs/librashader/latest/librashader/presets/index.html).
|
||||||
|
#![feature(drain_filter)]
|
||||||
|
|
||||||
pub mod context;
|
|
||||||
mod error;
|
mod error;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod preset;
|
mod preset;
|
||||||
|
|
||||||
pub use context::WildcardContext;
|
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
pub use preset::*;
|
pub use preset::*;
|
||||||
|
|
|
@ -10,7 +10,6 @@ mod value;
|
||||||
pub(crate) type Span<'a> = LocatedSpan<&'a str>;
|
pub(crate) type Span<'a> = LocatedSpan<&'a str>;
|
||||||
pub(crate) use token::Token;
|
pub(crate) use token::Token;
|
||||||
|
|
||||||
use crate::context::{VideoDriver, WildcardContext};
|
|
||||||
use crate::error::ParsePresetError;
|
use crate::error::ParsePresetError;
|
||||||
use crate::parse::preset::resolve_values;
|
use crate::parse::preset::resolve_values;
|
||||||
use crate::parse::value::parse_preset;
|
use crate::parse::value::parse_preset;
|
||||||
|
@ -22,38 +21,8 @@ pub(crate) fn remove_if<T>(values: &mut Vec<T>, f: impl FnMut(&T) -> bool) -> Op
|
||||||
|
|
||||||
impl ShaderPreset {
|
impl ShaderPreset {
|
||||||
/// Try to parse the shader preset at the given path.
|
/// 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> {
|
pub fn try_parse(path: impl AsRef<Path>) -> Result<ShaderPreset, ParsePresetError> {
|
||||||
let mut context = WildcardContext::new();
|
let values = parse_preset(path)?;
|
||||||
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))
|
Ok(resolve_values(values))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,41 @@
|
||||||
use crate::parse::remove_if;
|
use crate::parse::remove_if;
|
||||||
use crate::parse::value::Value;
|
use crate::parse::value::Value;
|
||||||
use crate::{
|
use crate::{ParameterConfig, Scale2D, Scaling, ShaderPassConfig, ShaderPreset, TextureConfig};
|
||||||
ParameterMeta, PassConfig, PassMeta, Scale2D, Scaling, ShaderPreset, TextureConfig, TextureMeta,
|
|
||||||
};
|
|
||||||
use vec_extract_if_polyfill::MakeExtractIf;
|
|
||||||
|
|
||||||
pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
|
pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
|
||||||
let textures: Vec<TextureConfig> =
|
let textures: Vec<TextureConfig> = values
|
||||||
MakeExtractIf::extract_if(&mut values, |f| matches!(*f, Value::Texture { .. }))
|
.drain_filter(|f| matches!(*f, Value::Texture { .. }))
|
||||||
.map(|value| {
|
.map(|value| {
|
||||||
if let Value::Texture {
|
if let Value::Texture {
|
||||||
|
name,
|
||||||
|
filter_mode,
|
||||||
|
wrap_mode,
|
||||||
|
mipmap,
|
||||||
|
path,
|
||||||
|
} = value
|
||||||
|
{
|
||||||
|
TextureConfig {
|
||||||
name,
|
name,
|
||||||
filter_mode,
|
|
||||||
wrap_mode,
|
|
||||||
mipmap,
|
|
||||||
path,
|
path,
|
||||||
} = value
|
wrap_mode,
|
||||||
{
|
filter_mode,
|
||||||
TextureConfig {
|
mipmap,
|
||||||
storage: librashader_common::StorageType::Path(path),
|
|
||||||
meta: TextureMeta {
|
|
||||||
name,
|
|
||||||
wrap_mode,
|
|
||||||
filter_mode,
|
|
||||||
mipmap,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!("values should all be of type Texture")
|
|
||||||
}
|
}
|
||||||
})
|
} else {
|
||||||
.collect();
|
unreachable!("values should all be of type Texture")
|
||||||
let parameters: Vec<ParameterMeta> =
|
}
|
||||||
MakeExtractIf::extract_if(&mut values, |f| matches!(*f, Value::Parameter { .. }))
|
})
|
||||||
.map(|value| {
|
.collect();
|
||||||
if let Value::Parameter(name, value) = value {
|
let parameters: Vec<ParameterConfig> = values
|
||||||
ParameterMeta { name, value }
|
.drain_filter(|f| matches!(*f, Value::Parameter { .. }))
|
||||||
} else {
|
.map(|value| {
|
||||||
unreachable!("values should be all of type parameters")
|
if let Value::Parameter(name, value) = value {
|
||||||
}
|
ParameterConfig { name, value }
|
||||||
})
|
} else {
|
||||||
.collect();
|
unreachable!("values should be all of type parameters")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut shaders = Vec::new();
|
let mut shaders = Vec::new();
|
||||||
let shader_count =
|
let shader_count =
|
||||||
|
@ -68,9 +63,9 @@ pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
|
||||||
&mut values,
|
&mut values,
|
||||||
|v| matches!(*v, Value::Shader(shader_index, _) if shader_index == shader),
|
|v| matches!(*v, Value::Shader(shader_index, _) if shader_index == shader),
|
||||||
) {
|
) {
|
||||||
let shader_values: Vec<Value> =
|
let shader_values: Vec<Value> = values
|
||||||
MakeExtractIf::extract_if(&mut values, |v| v.shader_index() == Some(shader))
|
.drain_filter(|v| v.shader_index() == Some(shader))
|
||||||
.collect();
|
.collect();
|
||||||
let scale_type = shader_values.iter().find_map(|f| match f {
|
let scale_type = shader_values.iter().find_map(|f| match f {
|
||||||
Value::ScaleType(_, value) => Some(*value),
|
Value::ScaleType(_, value) => Some(*value),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -117,66 +112,64 @@ pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
|
||||||
scale_y = scale;
|
scale_y = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
let shader = PassConfig {
|
let shader = ShaderPassConfig {
|
||||||
storage: librashader_common::StorageType::Path(name),
|
id,
|
||||||
meta: PassMeta {
|
name,
|
||||||
id,
|
alias: shader_values.iter().find_map(|f| match f {
|
||||||
alias: shader_values.iter().find_map(|f| match f {
|
Value::Alias(_, value) => Some(value.to_string()),
|
||||||
Value::Alias(_, value) => Some(value.clone()),
|
_ => None,
|
||||||
|
}),
|
||||||
|
filter: shader_values
|
||||||
|
.iter()
|
||||||
|
.find_map(|f| match f {
|
||||||
|
Value::FilterMode(_, value) => Some(*value),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
})
|
||||||
filter: shader_values
|
.unwrap_or_default(),
|
||||||
.iter()
|
wrap_mode: shader_values
|
||||||
.find_map(|f| match f {
|
.iter()
|
||||||
Value::FilterMode(_, value) => Some(*value),
|
.find_map(|f| match f {
|
||||||
_ => None,
|
Value::WrapMode(_, value) => Some(*value),
|
||||||
})
|
_ => None,
|
||||||
.unwrap_or_default(),
|
})
|
||||||
wrap_mode: shader_values
|
.unwrap_or_default(),
|
||||||
.iter()
|
frame_count_mod: shader_values
|
||||||
.find_map(|f| match f {
|
.iter()
|
||||||
Value::WrapMode(_, value) => Some(*value),
|
.find_map(|f| match f {
|
||||||
_ => None,
|
Value::FrameCountMod(_, value) => Some(*value),
|
||||||
})
|
_ => None,
|
||||||
.unwrap_or_default(),
|
})
|
||||||
frame_count_mod: shader_values
|
.unwrap_or(0),
|
||||||
.iter()
|
srgb_framebuffer: shader_values
|
||||||
.find_map(|f| match f {
|
.iter()
|
||||||
Value::FrameCountMod(_, value) => Some(*value),
|
.find_map(|f| match f {
|
||||||
_ => None,
|
Value::SrgbFramebuffer(_, value) => Some(*value),
|
||||||
})
|
_ => None,
|
||||||
.unwrap_or(0),
|
})
|
||||||
srgb_framebuffer: shader_values
|
.unwrap_or(false),
|
||||||
.iter()
|
float_framebuffer: shader_values
|
||||||
.find_map(|f| match f {
|
.iter()
|
||||||
Value::SrgbFramebuffer(_, value) => Some(*value),
|
.find_map(|f| match f {
|
||||||
_ => None,
|
Value::FloatFramebuffer(_, value) => Some(*value),
|
||||||
})
|
_ => None,
|
||||||
.unwrap_or(false),
|
})
|
||||||
float_framebuffer: shader_values
|
.unwrap_or(false),
|
||||||
.iter()
|
mipmap_input: shader_values
|
||||||
.find_map(|f| match f {
|
.iter()
|
||||||
Value::FloatFramebuffer(_, value) => Some(*value),
|
.find_map(|f| match f {
|
||||||
_ => None,
|
Value::MipmapInput(_, value) => Some(*value),
|
||||||
})
|
_ => None,
|
||||||
.unwrap_or(false),
|
})
|
||||||
mipmap_input: shader_values
|
.unwrap_or(false),
|
||||||
.iter()
|
scaling: Scale2D {
|
||||||
.find_map(|f| match f {
|
valid: scale_valid,
|
||||||
Value::MipmapInput(_, value) => Some(*value),
|
x: Scaling {
|
||||||
_ => None,
|
scale_type: scale_type_x.unwrap_or_default(),
|
||||||
})
|
factor: scale_x.unwrap_or_default(),
|
||||||
.unwrap_or(false),
|
},
|
||||||
scaling: Scale2D {
|
y: Scaling {
|
||||||
valid: scale_valid,
|
scale_type: scale_type_y.unwrap_or_default(),
|
||||||
x: Scaling {
|
factor: scale_y.unwrap_or_default(),
|
||||||
scale_type: scale_type_x.unwrap_or_default(),
|
|
||||||
factor: scale_x.unwrap_or_default(),
|
|
||||||
},
|
|
||||||
y: Scaling {
|
|
||||||
scale_type: scale_type_y.unwrap_or_default(),
|
|
||||||
factor: scale_y.unwrap_or_default(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -188,8 +181,8 @@ pub fn resolve_values(mut values: Vec<Value>) -> ShaderPreset {
|
||||||
ShaderPreset {
|
ShaderPreset {
|
||||||
#[cfg(feature = "parse_legacy_glsl")]
|
#[cfg(feature = "parse_legacy_glsl")]
|
||||||
feedback_pass,
|
feedback_pass,
|
||||||
pass_count: shader_count,
|
shader_count,
|
||||||
passes: shaders,
|
shaders,
|
||||||
textures,
|
textures,
|
||||||
parameters,
|
parameters,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
|
use std::ops::RangeFrom;
|
||||||
use crate::error::ParsePresetError;
|
use crate::error::ParsePresetError;
|
||||||
use crate::parse::Span;
|
use crate::parse::Span;
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::{is_not, take_until};
|
use nom::bytes::complete::{is_not, take_until};
|
||||||
use nom::character::complete::{char, line_ending, multispace1, not_line_ending};
|
use nom::character::complete::{char, line_ending, multispace1, not_line_ending};
|
||||||
use std::ops::RangeFrom;
|
|
||||||
|
|
||||||
use nom::combinator::{eof, map_res, value};
|
use nom::combinator::{eof, map_res, value};
|
||||||
use nom::error::{ErrorKind, ParseError};
|
use nom::error::{ErrorKind, ParseError};
|
||||||
|
|
||||||
use nom::sequence::delimited;
|
use nom::sequence::delimited;
|
||||||
use nom::{
|
use nom::{bytes::complete::tag, character::complete::multispace0, IResult, InputIter, InputLength, InputTake, Parser, Slice, AsChar};
|
||||||
bytes::complete::tag, character::complete::multispace0, AsChar, IResult, InputIter,
|
|
||||||
InputLength, InputTake, Slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Token<'a> {
|
pub struct Token<'a> {
|
||||||
|
@ -54,10 +51,9 @@ fn parse_assignment(input: Span) -> IResult<Span, ()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbalanced_quote<I>(input: I) -> IResult<I, ()>
|
fn unbalanced_quote<I>(input: I) -> IResult<I, ()>
|
||||||
where
|
where I: Slice<RangeFrom<usize>> + InputIter + InputLength,
|
||||||
I: Slice<RangeFrom<usize>> + InputIter + InputLength,
|
<I as InputIter>::Item: AsChar,
|
||||||
<I as InputIter>::Item: AsChar,
|
I: Copy
|
||||||
I: Copy,
|
|
||||||
{
|
{
|
||||||
if let Ok((input, _)) = eof::<_, ()>(input) {
|
if let Ok((input, _)) = eof::<_, ()>(input) {
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
|
@ -171,7 +167,7 @@ pub fn do_lex(input: &str) -> Result<Vec<Token>, ParsePresetError> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::parse::token::single_comment;
|
use crate::parse::token::{do_lex, single_comment};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_single_line_comment() {
|
fn parses_single_line_comment() {
|
||||||
|
|
|
@ -10,20 +10,16 @@ use nom::IResult;
|
||||||
use num_traits::cast::ToPrimitive;
|
use num_traits::cast::ToPrimitive;
|
||||||
|
|
||||||
use crate::parse::token::do_lex;
|
use crate::parse::token::do_lex;
|
||||||
use librashader_common::map::{FastHashMap, ShortString};
|
|
||||||
use librashader_common::{FilterMode, WrapMode};
|
use librashader_common::{FilterMode, WrapMode};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::context::{apply_context, WildcardContext};
|
|
||||||
use vec_extract_if_polyfill::MakeExtractIf;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
ShaderCount(i32),
|
ShaderCount(i32),
|
||||||
FeedbackPass(#[allow(unused)] i32),
|
FeedbackPass(i32),
|
||||||
Shader(i32, PathBuf),
|
Shader(i32, PathBuf),
|
||||||
ScaleX(i32, ScaleFactor),
|
ScaleX(i32, ScaleFactor),
|
||||||
ScaleY(i32, ScaleFactor),
|
ScaleY(i32, ScaleFactor),
|
||||||
|
@ -37,10 +33,10 @@ pub enum Value {
|
||||||
FloatFramebuffer(i32, bool),
|
FloatFramebuffer(i32, bool),
|
||||||
SrgbFramebuffer(i32, bool),
|
SrgbFramebuffer(i32, bool),
|
||||||
MipmapInput(i32, bool),
|
MipmapInput(i32, bool),
|
||||||
Alias(i32, ShortString),
|
Alias(i32, String),
|
||||||
Parameter(ShortString, f32),
|
Parameter(String, f32),
|
||||||
Texture {
|
Texture {
|
||||||
name: ShortString,
|
name: String,
|
||||||
filter_mode: FilterMode,
|
filter_mode: FilterMode,
|
||||||
wrap_mode: WrapMode,
|
wrap_mode: WrapMode,
|
||||||
mipmap: bool,
|
mipmap: bool,
|
||||||
|
@ -152,11 +148,9 @@ fn parse_indexed_key<'a>(key: &'static str, input: Span<'a>) -> IResult<Span<'a>
|
||||||
|
|
||||||
pub const SHADER_MAX_REFERENCE_DEPTH: usize = 16;
|
pub const SHADER_MAX_REFERENCE_DEPTH: usize = 16;
|
||||||
|
|
||||||
// prereq: root_path must be contextualized
|
|
||||||
fn load_child_reference_strings(
|
fn load_child_reference_strings(
|
||||||
root_references: Vec<PathBuf>,
|
root_references: Vec<PathBuf>,
|
||||||
root_path: impl AsRef<Path>,
|
root_path: impl AsRef<Path>,
|
||||||
context: &FastHashMap<String, String>,
|
|
||||||
) -> Result<Vec<(PathBuf, String)>, ParsePresetError> {
|
) -> Result<Vec<(PathBuf, String)>, ParsePresetError> {
|
||||||
let root_path = root_path.as_ref();
|
let root_path = root_path.as_ref();
|
||||||
|
|
||||||
|
@ -165,14 +159,13 @@ fn load_child_reference_strings(
|
||||||
let root_references = vec![(root_path.to_path_buf(), root_references)];
|
let root_references = vec![(root_path.to_path_buf(), root_references)];
|
||||||
let mut root_references = VecDeque::from(root_references);
|
let mut root_references = VecDeque::from(root_references);
|
||||||
// search needs to be depth first to allow for overrides.
|
// search needs to be depth first to allow for overrides.
|
||||||
while let Some((mut reference_root, referenced_paths)) = root_references.pop_front() {
|
while let Some((reference_root, referenced_paths)) = root_references.pop_front() {
|
||||||
if reference_depth > SHADER_MAX_REFERENCE_DEPTH {
|
if reference_depth > SHADER_MAX_REFERENCE_DEPTH {
|
||||||
return Err(ParsePresetError::ExceededReferenceDepth);
|
return Err(ParsePresetError::ExceededReferenceDepth);
|
||||||
}
|
}
|
||||||
// enter the current root
|
// enter the current root
|
||||||
reference_depth += 1;
|
reference_depth += 1;
|
||||||
// canonicalize current root
|
// canonicalize current root
|
||||||
apply_context(&mut reference_root, context);
|
|
||||||
let reference_root = reference_root
|
let reference_root = reference_root
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.map_err(|e| ParsePresetError::IOError(reference_root.to_path_buf(), e))?;
|
.map_err(|e| ParsePresetError::IOError(reference_root.to_path_buf(), e))?;
|
||||||
|
@ -181,10 +174,8 @@ fn load_child_reference_strings(
|
||||||
// println!("Resolving {referenced_paths:?} against {reference_root:?}.");
|
// println!("Resolving {referenced_paths:?} against {reference_root:?}.");
|
||||||
|
|
||||||
for path in referenced_paths {
|
for path in referenced_paths {
|
||||||
let mut path = reference_root.join(path.clone());
|
let mut path = reference_root
|
||||||
apply_context(&mut path, context);
|
.join(path.clone())
|
||||||
|
|
||||||
let mut path = path
|
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.map_err(|e| ParsePresetError::IOError(path.clone(), e))?;
|
.map_err(|e| ParsePresetError::IOError(path.clone(), e))?;
|
||||||
// println!("Opening {:?}", path);
|
// println!("Opening {:?}", path);
|
||||||
|
@ -195,10 +186,8 @@ fn load_child_reference_strings(
|
||||||
.map_err(|e| ParsePresetError::IOError(path.clone(), e))?;
|
.map_err(|e| ParsePresetError::IOError(path.clone(), e))?;
|
||||||
|
|
||||||
let mut new_tokens = do_lex(&reference_contents)?;
|
let mut new_tokens = do_lex(&reference_contents)?;
|
||||||
let new_references: Vec<PathBuf> =
|
let new_references: Vec<PathBuf> = new_tokens
|
||||||
MakeExtractIf::extract_if(&mut new_tokens, |token| {
|
.drain_filter(|token| *token.key.fragment() == "#reference")
|
||||||
*token.key.fragment() == "#reference"
|
|
||||||
})
|
|
||||||
.map(|value| PathBuf::from(*value.value.fragment()))
|
.map(|value| PathBuf::from(*value.value.fragment()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -213,16 +202,8 @@ fn load_child_reference_strings(
|
||||||
Ok(reference_strings.into())
|
Ok(reference_strings.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_preset(
|
pub fn parse_preset(path: impl AsRef<Path>) -> Result<Vec<Value>, ParsePresetError> {
|
||||||
path: impl AsRef<Path>,
|
|
||||||
context: WildcardContext,
|
|
||||||
) -> Result<Vec<Value>, ParsePresetError> {
|
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let mut path = path.to_path_buf();
|
|
||||||
let context = context.into_hashmap();
|
|
||||||
|
|
||||||
apply_context(&mut path, &context);
|
|
||||||
|
|
||||||
let path = path
|
let path = path
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?;
|
.map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?;
|
||||||
|
@ -233,14 +214,12 @@ pub(crate) fn parse_preset(
|
||||||
.map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?;
|
.map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?;
|
||||||
|
|
||||||
let tokens = super::token::do_lex(&contents)?;
|
let tokens = super::token::do_lex(&contents)?;
|
||||||
parse_values(tokens, path, context)
|
parse_values(tokens, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prereq: root_path must be contextualized
|
|
||||||
pub fn parse_values(
|
pub fn parse_values(
|
||||||
mut tokens: Vec<Token>,
|
mut tokens: Vec<Token>,
|
||||||
root_path: impl AsRef<Path>,
|
root_path: impl AsRef<Path>,
|
||||||
context: FastHashMap<String, String>,
|
|
||||||
) -> Result<Vec<Value>, ParsePresetError> {
|
) -> Result<Vec<Value>, ParsePresetError> {
|
||||||
let mut root_path = root_path.as_ref().to_path_buf();
|
let mut root_path = root_path.as_ref().to_path_buf();
|
||||||
if root_path.is_relative() {
|
if root_path.is_relative() {
|
||||||
|
@ -252,15 +231,13 @@ pub fn parse_values(
|
||||||
root_path.pop();
|
root_path.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let references: Vec<PathBuf> =
|
let references: Vec<PathBuf> = tokens
|
||||||
MakeExtractIf::extract_if(&mut tokens, |token| *token.key.fragment() == "#reference")
|
.drain_filter(|token| *token.key.fragment() == "#reference")
|
||||||
.map(|value| PathBuf::from(*value.value.fragment()))
|
.map(|value| PathBuf::from(*value.value.fragment()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// unfortunately we need to lex twice because there's no way to know the references ahead of time.
|
// unfortunately we need to lex twice because there's no way to know the references ahead of time.
|
||||||
// the returned references should have context applied
|
let child_strings = load_child_reference_strings(references, &root_path)?;
|
||||||
|
|
||||||
let child_strings = load_child_reference_strings(references, &root_path, &context)?;
|
|
||||||
let mut all_tokens: Vec<(&Path, Vec<Token>)> = Vec::new();
|
let mut all_tokens: Vec<(&Path, Vec<Token>)> = Vec::new();
|
||||||
|
|
||||||
for (path, string) in child_strings.iter() {
|
for (path, string) in child_strings.iter() {
|
||||||
|
@ -277,12 +254,10 @@ pub fn parse_values(
|
||||||
// collect all possible parameter names.
|
// collect all possible parameter names.
|
||||||
let mut parameter_names: Vec<&str> = Vec::new();
|
let mut parameter_names: Vec<&str> = Vec::new();
|
||||||
for (_, tokens) in all_tokens.iter_mut() {
|
for (_, tokens) in all_tokens.iter_mut() {
|
||||||
for token in
|
for token in tokens.drain_filter(|token| *token.key.fragment() == "parameters") {
|
||||||
MakeExtractIf::extract_if(tokens, |token| *token.key.fragment() == "parameters")
|
|
||||||
{
|
|
||||||
let parameter_name_string: &str = token.value.fragment();
|
let parameter_name_string: &str = token.value.fragment();
|
||||||
for parameter_name in parameter_name_string.split(';') {
|
for parameter_name in parameter_name_string.split(';') {
|
||||||
parameter_names.push(parameter_name.trim());
|
parameter_names.push(parameter_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,11 +265,10 @@ pub fn parse_values(
|
||||||
// collect all possible texture names.
|
// collect all possible texture names.
|
||||||
let mut texture_names: Vec<&str> = Vec::new();
|
let mut texture_names: Vec<&str> = Vec::new();
|
||||||
for (_, tokens) in all_tokens.iter_mut() {
|
for (_, tokens) in all_tokens.iter_mut() {
|
||||||
for token in MakeExtractIf::extract_if(tokens, |token| *token.key.fragment() == "textures")
|
for token in tokens.drain_filter(|token| *token.key.fragment() == "textures") {
|
||||||
{
|
|
||||||
let texture_name_string: &str = token.value.fragment();
|
let texture_name_string: &str = token.value.fragment();
|
||||||
for texture_name in texture_name_string.split(';') {
|
for texture_name in texture_name_string.split(';') {
|
||||||
texture_names.push(texture_name.trim());
|
texture_names.push(texture_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,9 +276,7 @@ pub fn parse_values(
|
||||||
let mut values = Vec::new();
|
let mut values = Vec::new();
|
||||||
// resolve shader paths.
|
// resolve shader paths.
|
||||||
for (path, tokens) in all_tokens.iter_mut() {
|
for (path, tokens) in all_tokens.iter_mut() {
|
||||||
for token in MakeExtractIf::extract_if(tokens, |token| {
|
for token in tokens.drain_filter(|token| parse_indexed_key("shader", token.key).is_ok()) {
|
||||||
parse_indexed_key("shader", token.key).is_ok()
|
|
||||||
}) {
|
|
||||||
let (_, index) = parse_indexed_key("shader", token.key).map_err(|e| match e {
|
let (_, index) = parse_indexed_key("shader", token.key).map_err(|e| match e {
|
||||||
nom::Err::Error(e) | nom::Err::Failure(e) => {
|
nom::Err::Error(e) | nom::Err::Failure(e) => {
|
||||||
let input: Span = e.input;
|
let input: Span = e.input;
|
||||||
|
@ -335,11 +307,8 @@ pub fn parse_values(
|
||||||
// resolve texture paths
|
// resolve texture paths
|
||||||
let mut textures = Vec::new();
|
let mut textures = Vec::new();
|
||||||
for (path, tokens) in all_tokens.iter_mut() {
|
for (path, tokens) in all_tokens.iter_mut() {
|
||||||
for token in
|
for token in tokens.drain_filter(|token| texture_names.contains(token.key.fragment())) {
|
||||||
MakeExtractIf::extract_if(tokens, |token| texture_names.contains(token.key.fragment()))
|
|
||||||
{
|
|
||||||
let mut relative_path = path.to_path_buf();
|
let mut relative_path = path.to_path_buf();
|
||||||
// Don't trim paths
|
|
||||||
relative_path.push(*token.value.fragment());
|
relative_path.push(*token.value.fragment());
|
||||||
relative_path
|
relative_path
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
|
@ -390,7 +359,7 @@ pub fn parse_values(
|
||||||
.map_or(None, |(_, v)| Some(FilterMode::from_str(&v.value).unwrap()));
|
.map_or(None, |(_, v)| Some(FilterMode::from_str(&v.value).unwrap()));
|
||||||
|
|
||||||
values.push(Value::Texture {
|
values.push(Value::Texture {
|
||||||
name: ShortString::from(*texture.fragment()),
|
name: texture.to_string(),
|
||||||
filter_mode: filter.unwrap_or(if linear {
|
filter_mode: filter.unwrap_or(if linear {
|
||||||
FilterMode::Linear
|
FilterMode::Linear
|
||||||
} else {
|
} else {
|
||||||
|
@ -405,7 +374,7 @@ pub fn parse_values(
|
||||||
let mut rest_tokens = Vec::new();
|
let mut rest_tokens = Vec::new();
|
||||||
// hopefully no more textures left in the token tree
|
// hopefully no more textures left in the token tree
|
||||||
for (p, token) in tokens {
|
for (p, token) in tokens {
|
||||||
if parameter_names.contains(&token.key.fragment().trim()) {
|
if parameter_names.contains(token.key.fragment()) {
|
||||||
let param_val = from_float(token.value)
|
let param_val = from_float(token.value)
|
||||||
// This is literally just to work around BEAM_PROFILE in crt-hyllian-sinc-glow.slangp
|
// This is literally just to work around BEAM_PROFILE in crt-hyllian-sinc-glow.slangp
|
||||||
// which has ""0'.000000". This somehow works in RA because it defaults to 0, probably.
|
// which has ""0'.000000". This somehow works in RA because it defaults to 0, probably.
|
||||||
|
@ -413,7 +382,7 @@ pub fn parse_values(
|
||||||
// params (god help me), it would be pretty bad because we lose texture path fallback.
|
// params (god help me), it would be pretty bad because we lose texture path fallback.
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
values.push(Value::Parameter(
|
values.push(Value::Parameter(
|
||||||
ShortString::from(token.key.fragment().trim()),
|
token.key.fragment().to_string(),
|
||||||
param_val,
|
param_val,
|
||||||
));
|
));
|
||||||
continue;
|
continue;
|
||||||
|
@ -494,10 +463,7 @@ pub fn parse_values(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok((_, idx)) = parse_indexed_key("alias", token.key) {
|
if let Ok((_, idx)) = parse_indexed_key("alias", token.key) {
|
||||||
values.push(Value::Alias(
|
values.push(Value::Alias(idx, token.value.to_string()));
|
||||||
idx,
|
|
||||||
ShortString::from(token.value.fragment().trim()),
|
|
||||||
));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Ok((_, idx)) = parse_indexed_key("scale_type", token.key) {
|
if let Ok((_, idx)) = parse_indexed_key("scale_type", token.key) {
|
||||||
|
@ -560,10 +526,11 @@ pub fn parse_values(
|
||||||
// handle undeclared parameters after parsing everything else as a last resort.
|
// handle undeclared parameters after parsing everything else as a last resort.
|
||||||
if let Ok(param_val) = from_float(token.value) {
|
if let Ok(param_val) = from_float(token.value) {
|
||||||
values.push(Value::Parameter(
|
values.push(Value::Parameter(
|
||||||
ShortString::from(token.key.fragment().trim()),
|
token.key.fragment().to_string(),
|
||||||
param_val,
|
param_val,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// very last resort, assume undeclared texture (must have extension)
|
// very last resort, assume undeclared texture (must have extension)
|
||||||
else if Path::new(token.value.fragment()).extension().is_some()
|
else if Path::new(token.value.fragment()).extension().is_some()
|
||||||
&& ["_mipmap", "_linear", "_wrap_mode", "_repeat_mode"]
|
&& ["_mipmap", "_linear", "_wrap_mode", "_repeat_mode"]
|
||||||
|
@ -571,7 +538,6 @@ pub fn parse_values(
|
||||||
.all(|k| !token.key.ends_with(k))
|
.all(|k| !token.key.ends_with(k))
|
||||||
{
|
{
|
||||||
let mut relative_path = path.to_path_buf();
|
let mut relative_path = path.to_path_buf();
|
||||||
// Don't trim paths.
|
|
||||||
relative_path.push(*token.value.fragment());
|
relative_path.push(*token.value.fragment());
|
||||||
relative_path
|
relative_path
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
|
@ -610,7 +576,7 @@ pub fn parse_values(
|
||||||
});
|
});
|
||||||
|
|
||||||
values.push(Value::Texture {
|
values.push(Value::Texture {
|
||||||
name: ShortString::from(*texture.fragment()),
|
name: texture.to_string(),
|
||||||
filter_mode: if linear {
|
filter_mode: if linear {
|
||||||
FilterMode::Linear
|
FilterMode::Linear
|
||||||
} else {
|
} else {
|
||||||
|
@ -629,14 +595,13 @@ pub fn parse_values(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::parse::value::parse_preset;
|
use crate::parse::value::parse_preset;
|
||||||
use crate::WildcardContext;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn parse_basic() {
|
pub fn parse_basic() {
|
||||||
let root =
|
let root =
|
||||||
PathBuf::from("../test/shaders_slang/bezel/Mega_Bezel/Presets/Base_CRT_Presets/MBZ__3__STD__MEGATRON-NTSC.slangp");
|
PathBuf::from("../test/slang-shaders/bezel/Mega_Bezel/Presets/Base_CRT_Presets/MBZ__3__STD__MEGATRON-NTSC.slangp");
|
||||||
let basic = parse_preset(root, WildcardContext::new());
|
let basic = parse_preset(root);
|
||||||
eprintln!("{basic:?}");
|
eprintln!("{basic:?}");
|
||||||
assert!(basic.is_ok());
|
assert!(basic.is_ok());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,18 @@
|
||||||
use crate::error::ParsePresetError;
|
use crate::error::ParsePresetError;
|
||||||
use librashader_common::map::ShortString;
|
|
||||||
use librashader_common::{FilterMode, ImageFormat, WrapMode};
|
use librashader_common::{FilterMode, ImageFormat, WrapMode};
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// The configuration for a single shader pass.
|
/// The configuration for a single shader pass.
|
||||||
pub type PassConfig = PathReference<PassMeta>;
|
|
||||||
|
|
||||||
/// Configuration options for a lookup texture used in the shader.
|
|
||||||
pub type TextureConfig = PathReference<TextureMeta>;
|
|
||||||
|
|
||||||
/// A reference to a resource on disk.
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
pub struct ShaderPassConfig {
|
||||||
pub struct PathReference<M> {
|
|
||||||
pub storage: librashader_common::StorageType,
|
|
||||||
/// Meta information about the resource.
|
|
||||||
pub meta: M,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Meta information about a shader pass.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct PassMeta {
|
|
||||||
/// The index of the shader pass relative to its parent preset.
|
/// The index of the shader pass relative to its parent preset.
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
/// The fully qualified path to the shader pass source file.
|
||||||
|
pub name: PathBuf,
|
||||||
/// The alias of the shader pass if available.
|
/// The alias of the shader pass if available.
|
||||||
pub alias: Option<ShortString>,
|
pub alias: Option<String>,
|
||||||
/// The filtering mode that this shader pass should expect.
|
/// The filtering mode that this shader pass should expect.
|
||||||
pub filter: FilterMode,
|
pub filter: FilterMode,
|
||||||
/// The texture addressing (wrap) mode that this shader pass expects.
|
/// The texture addressing (wrap) mode that this shader pass expects.
|
||||||
|
@ -43,7 +29,7 @@ pub struct PassMeta {
|
||||||
pub scaling: Scale2D,
|
pub scaling: Scale2D,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PassMeta {
|
impl ShaderPassConfig {
|
||||||
/// If the framebuffer expects a different format than what was defined in the
|
/// If the framebuffer expects a different format than what was defined in the
|
||||||
/// shader source, returns such format.
|
/// shader source, returns such format.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -68,7 +54,6 @@ impl PassMeta {
|
||||||
|
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
#[derive(Default, Copy, Clone, Debug)]
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
/// The scaling type for the shader pass.
|
/// The scaling type for the shader pass.
|
||||||
pub enum ScaleType {
|
pub enum ScaleType {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -78,13 +63,10 @@ pub enum ScaleType {
|
||||||
Absolute,
|
Absolute,
|
||||||
/// Scale by the size of the viewport.
|
/// Scale by the size of the viewport.
|
||||||
Viewport,
|
Viewport,
|
||||||
/// Scale by the size of the original input quad.
|
|
||||||
Original,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The scaling factor for framebuffer scaling.
|
/// The scaling factor for framebuffer scaling.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub enum ScaleFactor {
|
pub enum ScaleFactor {
|
||||||
/// Scale by a fractional float factor.
|
/// Scale by a fractional float factor.
|
||||||
Float(f32),
|
Float(f32),
|
||||||
|
@ -137,7 +119,6 @@ impl FromStr for ScaleType {
|
||||||
"source" => Ok(ScaleType::Input),
|
"source" => Ok(ScaleType::Input),
|
||||||
"viewport" => Ok(ScaleType::Viewport),
|
"viewport" => Ok(ScaleType::Viewport),
|
||||||
"absolute" => Ok(ScaleType::Absolute),
|
"absolute" => Ok(ScaleType::Absolute),
|
||||||
"original" => Ok(ScaleType::Original),
|
|
||||||
_ => Err(ParsePresetError::InvalidScaleType(s.to_string())),
|
_ => Err(ParsePresetError::InvalidScaleType(s.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +126,6 @@ impl FromStr for ScaleType {
|
||||||
|
|
||||||
/// Framebuffer scaling parameters.
|
/// Framebuffer scaling parameters.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct Scaling {
|
pub struct Scaling {
|
||||||
/// The method to scale the framebuffer with.
|
/// The method to scale the framebuffer with.
|
||||||
pub scale_type: ScaleType,
|
pub scale_type: ScaleType,
|
||||||
|
@ -155,7 +135,6 @@ pub struct Scaling {
|
||||||
|
|
||||||
/// 2D quad scaling parameters.
|
/// 2D quad scaling parameters.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct Scale2D {
|
pub struct Scale2D {
|
||||||
/// Whether or not this combination of scaling factors is valid.
|
/// Whether or not this combination of scaling factors is valid.
|
||||||
pub valid: bool,
|
pub valid: bool,
|
||||||
|
@ -167,24 +146,24 @@ pub struct Scale2D {
|
||||||
|
|
||||||
/// Configuration options for a lookup texture used in the shader.
|
/// Configuration options for a lookup texture used in the shader.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
pub struct TextureConfig {
|
||||||
pub struct TextureMeta {
|
|
||||||
/// The name of the texture.
|
/// The name of the texture.
|
||||||
pub name: ShortString,
|
pub name: String,
|
||||||
|
/// The fully qualified path to the texture.
|
||||||
|
pub path: PathBuf,
|
||||||
/// The wrap (addressing) mode to use when sampling the texture.
|
/// The wrap (addressing) mode to use when sampling the texture.
|
||||||
pub wrap_mode: WrapMode,
|
pub wrap_mode: WrapMode,
|
||||||
/// The filter mode to use when sampling the texture.
|
/// The filter mode to use when sampling the texture.
|
||||||
pub filter_mode: FilterMode,
|
pub filter_mode: FilterMode,
|
||||||
/// Whether to generate mipmaps for this texture.
|
/// Whether or not to generate mipmaps for this texture.
|
||||||
pub mipmap: bool,
|
pub mipmap: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration options for a shader parameter.
|
/// Configuration options for a shader parameter.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
pub struct ParameterConfig {
|
||||||
pub struct ParameterMeta {
|
|
||||||
/// The name of the parameter.
|
/// The name of the parameter.
|
||||||
pub name: ShortString,
|
pub name: String,
|
||||||
/// The value it is set to in the preset.
|
/// The value it is set to in the preset.
|
||||||
pub value: f32,
|
pub value: f32,
|
||||||
}
|
}
|
||||||
|
@ -194,7 +173,6 @@ pub struct ParameterMeta {
|
||||||
/// A shader preset can be used to create a filter chain runtime instance, or reflected to get
|
/// A shader preset can be used to create a filter chain runtime instance, or reflected to get
|
||||||
/// parameter metadata.
|
/// parameter metadata.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct ShaderPreset {
|
pub struct ShaderPreset {
|
||||||
/// Used in legacy GLSL shader semantics. If < 0, no feedback pass is used.
|
/// Used in legacy GLSL shader semantics. If < 0, no feedback pass is used.
|
||||||
/// Otherwise, the FBO after pass #N is passed a texture to next frame
|
/// Otherwise, the FBO after pass #N is passed a texture to next frame
|
||||||
|
@ -202,14 +180,14 @@ pub struct ShaderPreset {
|
||||||
pub feedback_pass: i32,
|
pub feedback_pass: i32,
|
||||||
|
|
||||||
/// The number of shaders enabled in the filter chain.
|
/// The number of shaders enabled in the filter chain.
|
||||||
pub pass_count: i32,
|
pub shader_count: i32,
|
||||||
// Everything is in Vecs because the expect number of values is well below 64.
|
// Everything is in Vecs because the expect number of values is well below 64.
|
||||||
/// Preset information for each shader.
|
/// Preset information for each shader.
|
||||||
pub passes: Vec<PassConfig>,
|
pub shaders: Vec<ShaderPassConfig>,
|
||||||
|
|
||||||
/// Preset information for each texture.
|
/// Preset information for each texture.
|
||||||
pub textures: Vec<TextureConfig>,
|
pub textures: Vec<TextureConfig>,
|
||||||
|
|
||||||
/// Preset information for each user parameter.
|
/// Preset information for each user parameter.
|
||||||
pub parameters: Vec<ParameterMeta>,
|
pub parameters: Vec<ParameterConfig>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
use librashader_presets::context::{ContextItem, VideoDriver, WildcardContext};
|
|
||||||
use librashader_presets::ShaderPreset;
|
use librashader_presets::ShaderPreset;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -16,19 +15,7 @@ fn parses_all_slang_presets() {
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_problematic() {
|
fn parses_problematic() {
|
||||||
let path = "../test/Mega_Bezel_Packs/Duimon-Mega-Bezel/Presets/Advanced/Nintendo_NDS_DREZ/NDS-[DREZ]-[Native]-[ADV]-[Guest]-[Night].slangp";
|
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));
|
ShaderPreset::try_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));
|
.expect(&format!("Failed to parse {}", path));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "librashader-reflect"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
license = "MPL-2.0 OR GPL-3.0-only"
|
license = "MPL-2.0 OR GPL-3.0-only"
|
||||||
version = "0.5.1"
|
version = "0.1.3"
|
||||||
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
authors = ["Ronny Chan <ronny@ronnychan.ca>"]
|
||||||
repository = "https://github.com/SnowflakePowered/librashader"
|
repository = "https://github.com/SnowflakePowered/librashader"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
@ -12,39 +12,28 @@ keywords = ["shader", "retroarch", "SPIR-V"]
|
||||||
description = "RetroArch shaders for all."
|
description = "RetroArch shaders for all."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glslang = "0.6.0"
|
shaderc = { version = "0.8.2", features = [] }
|
||||||
bytemuck = "1.13.0"
|
bytemuck = "1.13.0"
|
||||||
|
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
bitflags = "2.4.2"
|
bitflags = "1.3.2"
|
||||||
|
rustc-hash = "1.1.0"
|
||||||
|
|
||||||
librashader-common = { path = "../librashader-common", version = "0.5.1" }
|
librashader-common = { path = "../librashader-common", version = "0.1.3" }
|
||||||
librashader-preprocess = { path = "../librashader-preprocess", version = "0.5.1" }
|
librashader-preprocess = { path = "../librashader-preprocess", version = "0.1.3" }
|
||||||
librashader-presets = { path = "../librashader-presets", version = "0.5.1" }
|
librashader-presets = { path = "../librashader-presets", version = "0.1.3" }
|
||||||
librashader-pack = { path = "../librashader-pack", version = "0.5.1" }
|
|
||||||
|
|
||||||
spirv-cross2 = { workspace = true, optional = true }
|
spirv_cross = { package = "librashader-spirv-cross", version = "0.23", optional = true }
|
||||||
|
spirv-to-dxil = { version = "0.3", optional = true }
|
||||||
|
naga = { version = "0.11.0", features = ["glsl-in", "spv-in", "spv-out", "glsl-out", "wgsl-out"], optional = true }
|
||||||
|
|
||||||
naga = { version = "22", optional = true }
|
rspirv = { version = "0.11.0+1.5.4", optional = true }
|
||||||
rspirv = { version = "0.12.0", optional = true }
|
|
||||||
spirv = { version = "0.3.0", optional = true}
|
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||||
rustc-hash = "2.0.0"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.spirv-to-dxil]
|
|
||||||
version = "0.4.7"
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["cross", "naga", "wgsl", "msl"]
|
default = ["cross", "serialize"]
|
||||||
dxil = [ "spirv-cross2?/hlsl", "dep:spirv-to-dxil" ]
|
unstable-naga = [ "naga", "rspirv" ]
|
||||||
wgsl = [ "cross", "naga", "naga/wgsl-out", "dep:spirv", "dep:rspirv"]
|
standalone = ["shaderc/build-from-source"]
|
||||||
cross = [ "spirv-cross2", "spirv-cross2/glsl", "spirv-cross2/hlsl", "spirv-cross2/msl" ]
|
dxil = ["cross", "spirv-to-dxil"]
|
||||||
naga = [ "dep:rspirv", "dep:spirv", "dep:naga", "naga/spv-in", "naga/spv-out", "naga/wgsl-out", "naga/msl-out" ]
|
cross = [ "spirv_cross", "spirv_cross/glsl", "spirv_cross/hlsl" ]
|
||||||
serde = ["dep:serde", "serde/derive", "librashader-common/serde", "bitflags/serde"]
|
serialize = [ "serde" ]
|
||||||
msl = [ "cross", "spirv-cross2/msl", "naga?/msl-out" ]
|
|
||||||
|
|
||||||
stable = []
|
|
||||||
|
|
||||||
unstable-naga-in = ["naga/glsl-in"]
|
|
||||||
|
|
Binary file not shown.
58
librashader-reflect/src/back/cross.rs
Normal file
58
librashader-reflect/src/back/cross.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
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,66 +1,45 @@
|
||||||
use crate::back::spirv::WriteSpirV;
|
use crate::back::spirv::WriteSpirV;
|
||||||
use crate::back::targets::{OutputTarget, DXIL};
|
use crate::back::{CompileShader, CompilerBackend, FromCompilation, ShaderCompilerOutput};
|
||||||
use crate::back::{
|
|
||||||
CompileReflectShader, CompileShader, CompilerBackend, FromCompilation, ShaderCompilerOutput,
|
|
||||||
};
|
|
||||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
|
||||||
use crate::front::SpirvCompilation;
|
|
||||||
use crate::reflect::cross::glsl::GlslReflect;
|
|
||||||
use crate::reflect::cross::SpirvCross;
|
|
||||||
pub use spirv_to_dxil::DxilObject;
|
pub use spirv_to_dxil::DxilObject;
|
||||||
pub use spirv_to_dxil::ShaderModel;
|
pub use spirv_to_dxil::ShaderModel;
|
||||||
use spirv_to_dxil::{
|
use spirv_to_dxil::{
|
||||||
PushConstantBufferConfig, RuntimeConfig, RuntimeDataBufferConfig, ShaderStage, ValidatorVersion,
|
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 {
|
impl OutputTarget for DXIL {
|
||||||
type Output = DxilObject;
|
type Output = DxilObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "stable"))]
|
impl FromCompilation<GlslangCompilation> for DXIL {
|
||||||
impl FromCompilation<SpirvCompilation, SpirvCross> for DXIL {
|
|
||||||
type Target = DXIL;
|
type Target = DXIL;
|
||||||
type Options = Option<ShaderModel>;
|
type Options = Option<ShaderModel>;
|
||||||
type Context = ();
|
type Context = ();
|
||||||
type Output = impl CompileReflectShader<Self::Target, SpirvCompilation, SpirvCross>;
|
type Output = impl CompileShader<Self::Target, Options = Self::Options, Context = Self::Context>
|
||||||
|
+ ReflectShader;
|
||||||
|
|
||||||
fn from_compilation(
|
fn from_compilation(
|
||||||
compile: SpirvCompilation,
|
compile: GlslangCompilation,
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
||||||
let reflect = GlslReflect::try_from(&compile)?;
|
let reflect = GlslReflect::try_from(&compile)?;
|
||||||
|
let vertex = compile.vertex;
|
||||||
|
let fragment = compile.fragment;
|
||||||
Ok(CompilerBackend {
|
Ok(CompilerBackend {
|
||||||
// we can just reuse WriteSpirV as the backend.
|
// we can just reuse WriteSpirV as the backend.
|
||||||
backend: WriteSpirV {
|
backend: WriteSpirV {
|
||||||
reflect,
|
reflect,
|
||||||
vertex: compile.vertex,
|
vertex,
|
||||||
fragment: compile.fragment,
|
fragment,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "stable")]
|
|
||||||
impl FromCompilation<SpirvCompilation, SpirvCross> for DXIL {
|
|
||||||
type Target = DXIL;
|
|
||||||
type Options = Option<ShaderModel>;
|
|
||||||
type Context = ();
|
|
||||||
type Output = Box<dyn CompileReflectShader<Self::Target, SpirvCompilation, SpirvCross> + Send>;
|
|
||||||
|
|
||||||
fn from_compilation(
|
|
||||||
compile: SpirvCompilation,
|
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
|
||||||
let reflect = GlslReflect::try_from(&compile)?;
|
|
||||||
Ok(CompilerBackend {
|
|
||||||
// we can just reuse WriteSpirV as the backend.
|
|
||||||
backend: Box::new(WriteSpirV {
|
|
||||||
reflect,
|
|
||||||
vertex: compile.vertex,
|
|
||||||
fragment: compile.fragment,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompileShader<DXIL> for WriteSpirV {
|
impl CompileShader<DXIL> for WriteSpirV {
|
||||||
type Options = Option<ShaderModel>;
|
type Options = Option<ShaderModel>;
|
||||||
type Context = ();
|
type Context = ();
|
||||||
|
@ -80,7 +59,6 @@ impl CompileShader<DXIL> for WriteSpirV {
|
||||||
register_space: 0,
|
register_space: 0,
|
||||||
base_shader_register: 1,
|
base_shader_register: 1,
|
||||||
},
|
},
|
||||||
shader_model_max: sm,
|
|
||||||
..RuntimeConfig::default()
|
..RuntimeConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,6 +68,7 @@ impl CompileShader<DXIL> for WriteSpirV {
|
||||||
None,
|
None,
|
||||||
"main",
|
"main",
|
||||||
ShaderStage::Vertex,
|
ShaderStage::Vertex,
|
||||||
|
sm,
|
||||||
ValidatorVersion::None,
|
ValidatorVersion::None,
|
||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
|
@ -100,6 +79,7 @@ impl CompileShader<DXIL> for WriteSpirV {
|
||||||
None,
|
None,
|
||||||
"main",
|
"main",
|
||||||
ShaderStage::Fragment,
|
ShaderStage::Fragment,
|
||||||
|
sm,
|
||||||
ValidatorVersion::None,
|
ValidatorVersion::None,
|
||||||
&config,
|
&config,
|
||||||
)
|
)
|
||||||
|
@ -111,11 +91,4 @@ impl CompileShader<DXIL> for WriteSpirV {
|
||||||
context: (),
|
context: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_boxed(
|
|
||||||
self: Box<Self>,
|
|
||||||
options: Self::Options,
|
|
||||||
) -> Result<ShaderCompilerOutput<DxilObject, Self::Context>, ShaderCompileError> {
|
|
||||||
<WriteSpirV as CompileShader<DXIL>>::compile(*self, options)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
use crate::back::targets::GLSL;
|
|
||||||
use crate::back::{CompileReflectShader, CompilerBackend, FromCompilation};
|
|
||||||
use crate::error::ShaderReflectError;
|
|
||||||
use crate::front::SpirvCompilation;
|
|
||||||
use crate::reflect::cross::{CompiledProgram, SpirvCross};
|
|
||||||
|
|
||||||
/// The GLSL version to target.
|
|
||||||
pub use spirv_cross2::compile::glsl::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_cross2::targets::Glsl>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "stable"))]
|
|
||||||
impl FromCompilation<SpirvCompilation, SpirvCross> for GLSL {
|
|
||||||
type Target = GLSL;
|
|
||||||
type Options = GlslVersion;
|
|
||||||
type Context = CrossGlslContext;
|
|
||||||
type Output = impl CompileReflectShader<Self::Target, SpirvCompilation, SpirvCross>;
|
|
||||||
|
|
||||||
fn from_compilation(
|
|
||||||
compile: SpirvCompilation,
|
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
|
||||||
Ok(CompilerBackend {
|
|
||||||
backend: GlslReflect::try_from(&compile)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "stable")]
|
|
||||||
impl FromCompilation<SpirvCompilation, SpirvCross> for GLSL {
|
|
||||||
type Target = GLSL;
|
|
||||||
type Options = GlslVersion;
|
|
||||||
type Context = CrossGlslContext;
|
|
||||||
type Output = Box<dyn CompileReflectShader<Self::Target, SpirvCompilation, SpirvCross> + Send>;
|
|
||||||
|
|
||||||
fn from_compilation(
|
|
||||||
compile: SpirvCompilation,
|
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
|
||||||
Ok(CompilerBackend {
|
|
||||||
backend: Box::new(GlslReflect::try_from(&compile)?),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
use crate::back::targets::HLSL;
|
|
||||||
use crate::back::{CompileReflectShader, CompilerBackend, FromCompilation};
|
|
||||||
use crate::error::ShaderReflectError;
|
|
||||||
use crate::front::SpirvCompilation;
|
|
||||||
use crate::reflect::cross::hlsl::HlslReflect;
|
|
||||||
use crate::reflect::cross::{CompiledProgram, SpirvCross};
|
|
||||||
|
|
||||||
/// The HLSL shader model version to target.
|
|
||||||
pub use spirv_cross2::compile::hlsl::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;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sometimes SPIRV-cross will assign variables to "global"
|
|
||||||
if Self::find_mangled_name("global", uniform_name, mangled_name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The context for a HLSL compilation via spirv-cross.
|
|
||||||
pub struct CrossHlslContext {
|
|
||||||
/// The compiled HLSL program.
|
|
||||||
pub artifact: CompiledProgram<spirv_cross2::targets::Hlsl>,
|
|
||||||
pub vertex_buffers: HlslBufferAssignments,
|
|
||||||
pub fragment_buffers: HlslBufferAssignments,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "stable"))]
|
|
||||||
impl FromCompilation<SpirvCompilation, SpirvCross> for HLSL {
|
|
||||||
type Target = HLSL;
|
|
||||||
type Options = Option<HlslShaderModel>;
|
|
||||||
type Context = CrossHlslContext;
|
|
||||||
type Output = impl CompileReflectShader<Self::Target, SpirvCompilation, SpirvCross>;
|
|
||||||
|
|
||||||
fn from_compilation(
|
|
||||||
compile: SpirvCompilation,
|
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
|
||||||
Ok(CompilerBackend {
|
|
||||||
backend: HlslReflect::try_from(&compile)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "stable")]
|
|
||||||
impl FromCompilation<SpirvCompilation, SpirvCross> for HLSL {
|
|
||||||
type Target = HLSL;
|
|
||||||
type Options = Option<HlslShaderModel>;
|
|
||||||
type Context = CrossHlslContext;
|
|
||||||
type Output = Box<dyn CompileReflectShader<Self::Target, SpirvCompilation, SpirvCross> + Send>;
|
|
||||||
|
|
||||||
fn from_compilation(
|
|
||||||
compile: SpirvCompilation,
|
|
||||||
) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
|
|
||||||
Ok(CompilerBackend {
|
|
||||||
backend: Box::new(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,11 +1,8 @@
|
||||||
#[cfg(all(target_os = "windows", feature = "dxil"))]
|
pub mod cross;
|
||||||
|
#[cfg(feature = "dxil")]
|
||||||
pub mod dxil;
|
pub mod dxil;
|
||||||
pub mod glsl;
|
mod spirv;
|
||||||
pub mod hlsl;
|
|
||||||
pub mod msl;
|
|
||||||
pub mod spirv;
|
|
||||||
pub mod targets;
|
pub mod targets;
|
||||||
pub mod wgsl;
|
|
||||||
|
|
||||||
use crate::back::targets::OutputTarget;
|
use crate::back::targets::OutputTarget;
|
||||||
use crate::error::{ShaderCompileError, ShaderReflectError};
|
use crate::error::{ShaderCompileError, ShaderReflectError};
|
||||||
|
@ -32,50 +29,37 @@ pub trait CompileShader<T: OutputTarget> {
|
||||||
type Context;
|
type Context;
|
||||||
|
|
||||||
/// Consume the object and return the compiled output of the shader.
|
/// Consume the object and return the compiled output of the shader.
|
||||||
///
|
|
||||||
/// The shader should either be reflected or validated as
|
|
||||||
/// [ReflectShader] before being compiled, or the results may not be valid.
|
|
||||||
fn compile(
|
fn compile(
|
||||||
self,
|
self,
|
||||||
options: Self::Options,
|
options: Self::Options,
|
||||||
) -> Result<ShaderCompilerOutput<T::Output, Self::Context>, ShaderCompileError>;
|
) -> Result<ShaderCompilerOutput<T::Output, Self::Context>, ShaderCompileError>;
|
||||||
|
|
||||||
/// Consume the object and return the compiled output of the shader.
|
|
||||||
///
|
|
||||||
/// This is an internal implementation detail for stable building without TAIT,
|
|
||||||
/// to allow delegation when Self is unsized (i.e. dyn CompileReflectShader).
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn compile_boxed(
|
|
||||||
self: Box<Self>,
|
|
||||||
options: Self::Options,
|
|
||||||
) -> Result<ShaderCompilerOutput<T::Output, Self::Context>, ShaderCompileError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marker trait for combinations of targets and compilations that can be reflected and compiled
|
/// Marker trait for combinations of targets and compilations that can be reflected and compiled
|
||||||
/// successfully.
|
/// successfully.
|
||||||
///
|
///
|
||||||
/// This trait is automatically implemented for reflected outputs that have [`FromCompilation`] implement
|
/// This trait is automatically implemented for reflected outputs that have [`FromCompilation`](crate::back::FromCompilation) implement
|
||||||
/// for a given target that also implement [`CompileShader`] for that target.
|
/// for a given target that also implement [`CompileShader`](crate::back::CompileShader) for that target.
|
||||||
pub trait CompileReflectShader<T: OutputTarget, C, S>:
|
pub trait CompileReflectShader<T: OutputTarget, C>:
|
||||||
CompileShader<
|
CompileShader<
|
||||||
T,
|
T,
|
||||||
Options = <T as FromCompilation<C, S>>::Options,
|
Options = <T as FromCompilation<C>>::Options,
|
||||||
Context = <T as FromCompilation<C, S>>::Context,
|
Context = <T as FromCompilation<C>>::Context,
|
||||||
> + ReflectShader
|
> + ReflectShader
|
||||||
where
|
where
|
||||||
T: FromCompilation<C, S>,
|
T: FromCompilation<C>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, C, O, S> CompileReflectShader<T, C, S> for O
|
impl<T, C, O> CompileReflectShader<T, C> for O
|
||||||
where
|
where
|
||||||
T: OutputTarget,
|
T: OutputTarget,
|
||||||
T: FromCompilation<C, S>,
|
T: FromCompilation<C>,
|
||||||
O: ReflectShader,
|
O: ReflectShader,
|
||||||
O: CompileShader<
|
O: CompileShader<
|
||||||
T,
|
T,
|
||||||
Options = <T as FromCompilation<C, S>>::Options,
|
Options = <T as FromCompilation<C>>::Options,
|
||||||
Context = <T as FromCompilation<C, S>>::Context,
|
Context = <T as FromCompilation<C>>::Context,
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -94,23 +78,10 @@ where
|
||||||
) -> Result<ShaderCompilerOutput<E::Output, Self::Context>, ShaderCompileError> {
|
) -> Result<ShaderCompilerOutput<E::Output, Self::Context>, ShaderCompileError> {
|
||||||
self.backend.compile(options)
|
self.backend.compile(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_boxed(
|
|
||||||
self: Box<Self>,
|
|
||||||
options: Self::Options,
|
|
||||||
) -> Result<ShaderCompilerOutput<E::Output, Self::Context>, ShaderCompileError> {
|
|
||||||
self.backend.compile(options)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for reflectable compilations that can be transformed
|
/// A trait for reflectable compilations that can be transformed into an object ready for reflection or compilation.
|
||||||
/// into an object ready for reflection or compilation.
|
pub trait FromCompilation<T> {
|
||||||
///
|
|
||||||
/// `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.
|
/// The target that the transformed object is expected to compile for.
|
||||||
type Target: OutputTarget;
|
type Target: OutputTarget;
|
||||||
/// Options provided to the compiler.
|
/// Options provided to the compiler.
|
||||||
|
@ -142,56 +113,4 @@ where
|
||||||
) -> Result<ShaderReflection, ShaderReflectError> {
|
) -> Result<ShaderReflection, ShaderReflectError> {
|
||||||
self.backend.reflect(pass_number, semantics)
|
self.backend.reflect(pass_number, semantics)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&mut self) -> Result<(), ShaderReflectError> {
|
|
||||||
self.backend.validate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ReflectShader + ?Sized> ReflectShader for Box<T> {
|
|
||||||
fn reflect(
|
|
||||||
&mut self,
|
|
||||||
pass_number: usize,
|
|
||||||
semantics: &ShaderSemantics,
|
|
||||||
) -> Result<ShaderReflection, ShaderReflectError> {
|
|
||||||
(**self).reflect(pass_number, semantics)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate(&mut self) -> Result<(), ShaderReflectError> {
|
|
||||||
(**self).validate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O, T> CompileShader<T> for Box<O>
|
|
||||||
where
|
|
||||||
O: CompileShader<T> + ?Sized,
|
|
||||||
T: OutputTarget,
|
|
||||||
{
|
|
||||||
type Options = O::Options;
|
|
||||||
type Context = O::Context;
|
|
||||||
|
|
||||||
fn compile(
|
|
||||||
self,
|
|
||||||
options: Self::Options,
|
|
||||||
) -> Result<ShaderCompilerOutput<T::Output, Self::Context>, ShaderCompileError> {
|
|
||||||
O::compile_boxed(self, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_boxed(
|
|
||||||
self: Box<Self>,
|
|
||||||
options: Self::Options,
|
|
||||||
) -> Result<ShaderCompilerOutput<T::Output, Self::Context>, ShaderCompileError> {
|
|
||||||
self.compile(options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::front::{Glslang, ShaderInputCompiler};
|
|
||||||
use librashader_preprocess::ShaderSource;
|
|
||||||
|
|
||||||
pub fn test() {
|
|
||||||
let result = ShaderSource::load("../test/basic.slang").unwrap();
|
|
||||||
let _cross = Glslang::compile(&result).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue