├── .github └── workflows │ └── main.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── LICENSE-MPL ├── README.md ├── android-example ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── org │ │ │ └── mozilla │ │ │ └── surfmanthreadsexample │ │ │ └── SurfmanInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── blit.fs.glsl │ │ │ ├── check.fs.glsl │ │ │ ├── grid.fs.glsl │ │ │ ├── quad.vs.glsl │ │ │ ├── tri.fs.glsl │ │ │ └── tri.vs.glsl │ │ ├── java │ │ │ └── org │ │ │ │ └── mozilla │ │ │ │ └── surfmanthreadsexample │ │ │ │ ├── MainActivity.java │ │ │ │ ├── SurfmanThreadsExampleRenderer.java │ │ │ │ └── SurfmanThreadsExampleResourceLoader.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── org │ │ └── mozilla │ │ └── surfmanthreadsexample │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── rust │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── settings.gradle ├── build.rs ├── doc └── surfman-logo.svg ├── examples ├── chaos_game.rs ├── common │ └── mod.rs ├── offscreen.rs └── threads.rs ├── resources └── examples │ ├── blit.fs.glsl │ ├── check.fs.glsl │ ├── grid.fs.glsl │ ├── quad.vs.glsl │ ├── tri.fs.glsl │ └── tri.vs.glsl ├── rust-toolchain.toml └── src ├── chains.rs ├── connection.rs ├── context.rs ├── device.rs ├── error.rs ├── gl_utils.rs ├── info.rs ├── lib.rs ├── macros.rs ├── platform ├── egl │ ├── android_ffi.rs │ ├── connection.rs │ ├── context.rs │ ├── device.rs │ ├── mod.rs │ ├── ohos_ffi.rs │ └── surface │ │ ├── android_surface.rs │ │ ├── mod.rs │ │ └── ohos_surface.rs ├── generic │ ├── egl │ │ ├── context.rs │ │ ├── device.rs │ │ ├── error.rs │ │ ├── ffi.rs │ │ ├── mod.rs │ │ └── surface.rs │ ├── mod.rs │ └── multi │ │ ├── connection.rs │ │ ├── context.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ └── surface.rs ├── macos │ ├── cgl │ │ ├── connection.rs │ │ ├── context.rs │ │ ├── device.rs │ │ ├── error.rs │ │ ├── ffi.rs │ │ ├── mod.rs │ │ └── surface.rs │ ├── mod.rs │ └── system │ │ ├── connection.rs │ │ ├── device.rs │ │ ├── ffi.rs │ │ ├── mod.rs │ │ └── surface.rs ├── mod.rs ├── unix │ ├── default.rs │ ├── generic │ │ ├── connection.rs │ │ ├── context.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ └── surface.rs │ ├── mod.rs │ ├── wayland │ │ ├── connection.rs │ │ ├── context.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ └── surface.rs │ └── x11 │ │ ├── connection.rs │ │ ├── context.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ └── surface.rs └── windows │ ├── angle │ ├── connection.rs │ ├── context.rs │ ├── device.rs │ ├── mod.rs │ └── surface.rs │ ├── mod.rs │ └── wgl │ ├── connection.rs │ ├── context.rs │ ├── device.rs │ ├── mod.rs │ └── surface.rs ├── renderbuffers.rs ├── surface.rs └── tests.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: ["**"] 8 | merge_group: 9 | types: [checks_requested] 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | RUST_BACKTRACE: full 14 | 15 | jobs: 16 | Build: 17 | name: ${{ format('{0} {1}', matrix.platform.target, matrix.features)}} 18 | runs-on: ${{ matrix.platform.os }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | platform: 23 | - { target: aarch64-apple-darwin, os: macos-14, test: true } 24 | - { target: x86_64-apple-darwin, os: macos-13, test: true } 25 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-24.04, test: true } 26 | - { target: x86_64-pc-windows-msvc, os: windows-latest, test: true } 27 | - { target: i686-pc-windows-msvc, os: windows-latest, test: false } 28 | - { target: aarch64-pc-windows-msvc, os: windows-latest, test: false } 29 | - { 30 | target: aarch64-unknown-linux-ohos, 31 | os: ubuntu-24.04, 32 | test: false, 33 | } 34 | - { target: arm-linux-androideabi, os: ubuntu-24.04, test: false } 35 | features: 36 | [ 37 | "", 38 | "chains sm-raw-window-handle-06", 39 | "chains sm-raw-window-handle-05", 40 | ] 41 | include: 42 | - features: "sm-x11 sm-wayland-default" 43 | platform: 44 | { target: x86_64-unknown-linux-gnu, os: ubuntu-24.04, test: true } 45 | - features: "chains sm-angle-builtin" 46 | platform: 47 | { target: x86_64-pc-windows-msvc, os: windows-latest, test: true } 48 | - features: "chains sm-no-wgl sm-angle-builtin" 49 | platform: 50 | { target: x86_64-pc-windows-msvc, os: windows-latest, test: true } 51 | steps: 52 | - uses: actions/checkout@v4 53 | - name: Install dependencies (Linux) 54 | if: startsWith(matrix.platform.os, 'ubuntu') 55 | run: | 56 | sudo apt update 57 | sudo apt install gcc libxxf86vm-dev libosmesa6-dev libgles2-mesa-dev xvfb weston -y 58 | - name: Install rust 59 | id: toolchain 60 | uses: dtolnay/rust-toolchain@1.81.0 61 | with: 62 | targets: ${{ matrix.platform.target }} 63 | - name: Build 64 | run: | 65 | cargo build --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} 66 | - name: Test 67 | if: ${{ matrix.platform.test && startsWith(matrix.platform.os, 'ubuntu') }} 68 | run: | 69 | weston --no-config --socket=wl-test-env --backend=headless & 70 | WAYLAND_DISPLAY=wl-test-env xvfb-run cargo test --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} -- --test-threads 1 71 | - name: Test 72 | if: ${{ matrix.platform.test && !startsWith(matrix.platform.os, 'ubuntu') }} 73 | run: | 74 | cargo test --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} 75 | 76 | android-test: 77 | name: ${{ format('x86_64-linux-android {0}', matrix.features)}} 78 | runs-on: ubuntu-24.04 79 | strategy: 80 | fail-fast: false 81 | matrix: 82 | features: 83 | [ 84 | "", 85 | "chains sm-raw-window-handle-06", 86 | "chains sm-raw-window-handle-05", 87 | ] 88 | steps: 89 | - uses: actions/checkout@v4 90 | - name: Install rust 91 | id: toolchain 92 | uses: dtolnay/rust-toolchain@1.81.0 93 | with: 94 | targets: x86_64-linux-android 95 | 96 | - uses: taiki-e/install-action@v2 97 | with: 98 | tool: cargo-dinghy 99 | 100 | - name: Enable KVM 101 | run: | 102 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules 103 | sudo udevadm control --reload-rules 104 | sudo udevadm trigger --name-match=kvm 105 | 106 | - name: Run tests in android emulator 107 | uses: reactivecircus/android-emulator-runner@v2 108 | env: 109 | RANLIB: "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib" 110 | with: 111 | api-level: 30 112 | arch: x86_64 113 | emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none 114 | disable-animations: true 115 | script: | 116 | cargo dinghy all-platforms 117 | cargo dinghy all-devices 118 | cargo dinghy -p auto-android-x86_64-api30 --env RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} test --features "${{ matrix.features }}" -- --test-threads 1 --nocapture 119 | 120 | Format: 121 | name: Run `rustfmt` 122 | runs-on: ubuntu-24.04 123 | steps: 124 | - uses: actions/checkout@v4 125 | - uses: mkroening/rust-toolchain-toml@main 126 | - run: cargo fmt --check 127 | build_result: 128 | name: Result 129 | runs-on: ubuntu-24.04 130 | needs: ["Build", "android-test", "Format"] 131 | if: always() 132 | steps: 133 | - name: Mark the job as successful 134 | if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} 135 | run: exit 0 136 | - name: Mark the job as unsuccessful 137 | if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') 138 | run: exit 1 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.lock 3 | *~ 4 | *.iml 5 | /android-example/local.properties 6 | /android-example/target 7 | gc.properties 8 | *.bin 9 | /android-example/app/.classpath 10 | /android-example/app/build 11 | /android-example/.gradle 12 | /android-example/.idea 13 | /android-example/**/.project 14 | /android-example/**/.settings 15 | SDL2 16 | *.code-workspace 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "surfman" 3 | license = "MIT OR Apache-2.0 OR MPL-2.0" 4 | edition = "2021" 5 | version = "0.9.8" 6 | authors = [ 7 | "Patrick Walton ", 8 | "Emilio Cobos Álvarez ", 9 | "The Servo Project Developers", 10 | ] 11 | description = "A cross-platform, low-level toolkit for GPU surface management" 12 | repository = "https://github.com/servo/surfman" 13 | build = "build.rs" 14 | readme = "README.md" 15 | 16 | [build-dependencies] 17 | gl_generator = "0.14" 18 | cfg_aliases = "0.2.1" 19 | 20 | [features] 21 | chains = ["fnv"] 22 | default = ["sm-raw-window-handle-06"] 23 | sm-angle = [] 24 | sm-angle-builtin = ["mozangle"] 25 | sm-angle-default = ["sm-angle"] 26 | sm-no-wgl = ["sm-angle-default"] 27 | sm-test = [] 28 | sm-wayland-default = [] 29 | sm-x11 = ["x11-dl"] 30 | sm-raw-window-handle-generic = [] 31 | sm-raw-window-handle-05 = ["dep:rwh_05"] 32 | sm-raw-window-handle-06 = ["dep:rwh_06"] 33 | 34 | [dependencies] 35 | bitflags = "2.6" 36 | euclid = "0.22" 37 | fnv = { version = "1.0", optional = true } 38 | libc = "0.2" 39 | log = "0.4" 40 | glow = "0.16" 41 | osmesa-sys = { version = "0.1", optional = true } 42 | rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = [ 43 | "std", 44 | ], optional = true } 45 | rwh_06 = { package = "raw-window-handle", version = "0.6.2", features = [ 46 | "std", 47 | ], optional = true } 48 | 49 | [dev-dependencies] 50 | clap = "2" 51 | gl = "0.14" 52 | png = "0.17" 53 | rand = "0.8" 54 | winit = { version = "0.29.10", features = ["android-native-activity"] } 55 | serial_test = "3.1.0" 56 | 57 | [target.'cfg(target_os = "macos")'.dependencies] 58 | cgl = "0.3.2" 59 | mach2 = "0.4" 60 | objc2 = "0.6.1" 61 | objc2-app-kit = { version = "0.3", default-features = false, features = [ 62 | "std", 63 | "objc2-quartz-core", 64 | "objc2-core-foundation", 65 | "NSResponder", 66 | "NSScreen", 67 | "NSView", 68 | "NSGraphics", 69 | "NSWindow", 70 | ] } 71 | objc2-core-foundation = { version = "0.3.1", default-features = false, features = [ 72 | "std", 73 | "CFBase", 74 | "CFBundle", 75 | "CFCGTypes", 76 | "CFDictionary", 77 | "CFNumber", 78 | "CFString", 79 | ] } 80 | objc2-core-video = { version = "0.3.1", default-features = false, features = [ 81 | "std", 82 | "objc2-core-graphics", 83 | "CVBase", 84 | "CVDisplayLink", 85 | "CVPixelBuffer", 86 | "CVReturn", 87 | ] } 88 | objc2-foundation = { version = "0.3.1", default-features = false, features = [ 89 | "std", 90 | "objc2-core-foundation", 91 | "NSEnumerator", 92 | "NSGeometry", 93 | "NSString", 94 | "NSValue", 95 | ] } 96 | objc2-io-surface = { version = "0.3.1", default-features = false, features = [ 97 | "std", 98 | "libc", 99 | "objc2", 100 | "objc2-core-foundation", 101 | "IOSurfaceRef", 102 | "IOSurfaceTypes", 103 | ] } 104 | objc2-metal = { version = "0.3.1", default-features = false, features = [ 105 | "std", 106 | "MTLDevice", 107 | ] } 108 | objc2-quartz-core = { version = "0.3.1", default-features = false, features = [ 109 | "std", 110 | "objc2-core-foundation", 111 | "CALayer", 112 | "CATransaction", 113 | "CATransform3D", 114 | ] } 115 | 116 | [target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android", target_env = "ohos"))))'.dependencies.wayland-sys] 117 | version = "0.31" 118 | features = ["client", "dlopen", "egl"] 119 | 120 | [target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android", target_env = "ohos"))))'.dependencies.x11-dl] 121 | version = "2.3.0" 122 | optional = true 123 | 124 | # Ensure that we have a static libEGL.lib present for linking with EGL bindings. 125 | [target.'cfg(target_os = "windows")'.dependencies.mozangle] 126 | version = "0.5.1" 127 | features = ["egl", "build_dlls"] 128 | optional = true 129 | 130 | [target.'cfg(target_os = "windows")'.dependencies] 131 | wio = "0.2" 132 | winapi = { version = "0.3", features = [ 133 | "d3d11", 134 | "libloaderapi", 135 | "winbase", 136 | "winerror", 137 | "wingdi", 138 | "winuser", 139 | ] } 140 | 141 | [target.'cfg(target_os = "android")'.dependencies] 142 | rwh_06 = { package = "raw-window-handle", version = "0.6" } 143 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # surfman [![Build Status](https://github.com/servo/surfman/workflows/Rust/badge.svg)](https://github.com/servo/surfman/actions) 2 | 3 | ![surfman logo](https://i.imgur.com/t0xcJ6D.png) 4 | 5 | `surfman` is a low-level, cross-platform Rust library for managing *surfaces*, blocks of image data 6 | in GPU memory. Using this library, you can: 7 | 8 | * Draw to a window (perhaps created with `winit`) on the CPU. 9 | 10 | * Render to a window (created via `winit` or otherwise) with OpenGL. 11 | 12 | * Render to an off-screen surface with OpenGL. 13 | 14 | * Use a surface created on one thread as an OpenGL texture on another thread. 15 | 16 | * Draw to a surface with a platform-specific GPU API like Metal. 17 | 18 | `surfman` forms the low-level graphics infrastructure of the 19 | [Servo](https://github.com/servo/servo/) project, where it allows for easy porting of the 20 | browser's WebGL and WebXR code to a variety of platforms. 21 | 22 | ## What `surfman` is not 23 | 24 | `surfman` is not a full-featured GPU rendering API. It doesn't attempt to abstract over rendering 25 | libraries like OpenGL, Metal, and Direct3D. For that, try [gfx-rs](https://github.com/gfx-rs/gfx). 26 | 27 | `surfman` is also not a windowing solution. It can only render to a window that is already open 28 | and needs to be paired with a crate like [winit](https://github.com/rust-windowing/winit) to 29 | actually open the window. 30 | 31 | Likewise, `surfman` is not a UI toolkit. For that, see GTK+ and many other libraries. It's possible 32 | to use `surfman` alongside any of these UI toolkits to efficiently integrate GPU rendering into an 33 | application, however. 34 | 35 | ## Why `surfman`? 36 | 37 | Most of this functionality can be achieved with other libraries, such as `glutin` and SDL. However, 38 | for several use cases you can achieve better performance and/or correctness with `surfman`. For 39 | example: 40 | 41 | * On multi-GPU systems, games typically want to use the discrete GPU instead of the integrated GPU 42 | for better performance, while UI applications want the reverse for better energy consumption. 43 | However, most game-oriented OpenGL windowing libraries end up using the discrete GPU on Linux 44 | and macOS and the integrated GPU on Windows. On such systems, `surfman` explicitly allows you to 45 | choose which GPU you would like to render with. 46 | 47 | * OpenGL's *share context* or *share lists* feature allows you to share textures across contexts. 48 | However, this often exposes driver bugs, and, even if it works, it causes most operations to 49 | take mutex locks. Efficient texture sharing requires the use of platform-specific APIs, which 50 | `surfman` abstracts over. 51 | 52 | * The ANGLE implementation of OpenGL on Windows is not generally thread-safe, so attempts to render 53 | on background threads will generally segfault. `surfman` carefully works around all the safety 54 | issues so that the library is safe to use from any thread. 55 | 56 | * Applications such as emulators and video players that draw to the CPU want to avoid copying 57 | pixels as much as possible. Classic APIs for transferring image data like `glTexImage2D()` and 58 | `XPutImage()` often cause the data to be copied several times. In contrast, `surfman` allows you 59 | to render to the screen with as few copies as feasible—sometimes even zero, depending on the 60 | platform. 61 | 62 | ## Platform support 63 | 64 | The library supports the following platforms: 65 | 66 | * Windows, with OpenGL via the native WGL framework. 67 | 68 | * Windows, with OpenGL via Google's ANGLE library. 69 | 70 | * macOS, with OpenGL via the native CGL framework. 71 | 72 | * macOS, with Metal. 73 | 74 | * Linux/other Unix, with OpenGL on Wayland. 75 | 76 | * Linux/other Unix, with OpenGL on X11 via GLX. 77 | 78 | * Android P and up, with OpenGL. 79 | 80 | * Generic CPU rendering of OpenGL via the OSMesa framework. 81 | 82 | ## Future work 83 | 84 | The following features may be added later: 85 | 86 | * Support for Android Marshmallow, Nougat, and Oreo. 87 | 88 | * Partial presentation, to allow the OS to composite only the region of the window that has 89 | changed. 90 | 91 | * CPU rendering support on more platforms. (Right now, the CPU rendering features only work on 92 | macOS.) 93 | 94 | * Vulkan support. 95 | 96 | * Direct3D 11 support on Windows. 97 | 98 | * YUV surfaces, for software video codecs. 99 | 100 | * Support for running in a browser with WebAssembly. 101 | 102 | ## License 103 | 104 | `surfman` is licensed under the same terms as Rust itself. 105 | 106 | `surfman` abides by the same code of conduct as Rust itself. 107 | -------------------------------------------------------------------------------- /android-example/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /android-example/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-example/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "org.mozilla.surfmanthreadsexample" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | implementation 'com.android.support:design:28.0.0' 29 | } 30 | -------------------------------------------------------------------------------- /android-example/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android-example/app/src/androidTest/java/org/mozilla/surfmanthreadsexample/SurfmanInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package org.mozilla.surfmanthreadsexample; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class SurfmanInstrumentedTest { 19 | private static native void testContextCreation(); 20 | private static native void testCrossDeviceSurfaceTextureBlitFramebuffer(); 21 | private static native void testCrossThreadSurfaceTextureBlitFramebuffer(); 22 | private static native void testDeviceAccessors(); 23 | private static native void testDeviceCreation(); 24 | private static native void testGenericSurfaceCreation(); 25 | private static native void testGL(); 26 | private static native void testNewlyCreatedContextsAreCurrent(); 27 | private static native void testSurfaceTextureBlitFramebuffer(); 28 | private static native void testSurfaceTextureRightSideUp(); 29 | 30 | static { 31 | System.loadLibrary("surfman_android_threads"); 32 | } 33 | 34 | @Test 35 | public void useAppContext() { 36 | // Context of the app under test. 37 | Context appContext = InstrumentationRegistry.getTargetContext(); 38 | 39 | assertEquals("org.mozilla.surfmanthreadsexample", appContext.getPackageName()); 40 | } 41 | 42 | @Test 43 | public void contextCreation() { 44 | testContextCreation(); 45 | } 46 | 47 | @Test 48 | public void crossDeviceSurfaceTextureBlitFramebuffer() { 49 | testCrossDeviceSurfaceTextureBlitFramebuffer(); 50 | } 51 | 52 | @Test 53 | public void crossThreadSurfaceTextureBlitFramebuffer() { 54 | testCrossThreadSurfaceTextureBlitFramebuffer(); 55 | } 56 | 57 | @Test 58 | public void deviceAccessors() { 59 | testDeviceAccessors(); 60 | } 61 | 62 | @Test 63 | public void deviceCreation() { 64 | testDeviceCreation(); 65 | } 66 | 67 | @Test 68 | public void genericSurfaceCreation() { 69 | testGenericSurfaceCreation(); 70 | } 71 | 72 | @Test 73 | public void gl() { 74 | testGL(); 75 | } 76 | 77 | @Test 78 | public void newlyCreatedContextsAreCurrent() { 79 | testNewlyCreatedContextsAreCurrent(); 80 | } 81 | 82 | @Test 83 | public void surfaceTextureBlitFramebuffer() { 84 | testSurfaceTextureBlitFramebuffer(); 85 | } 86 | 87 | @Test 88 | public void surfaceTextureRightSideUp() { 89 | testSurfaceTextureRightSideUp(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /android-example/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /android-example/app/src/main/assets/blit.fs.glsl: -------------------------------------------------------------------------------- 1 | ../../../../../resources/examples/blit.fs.glsl -------------------------------------------------------------------------------- /android-example/app/src/main/assets/check.fs.glsl: -------------------------------------------------------------------------------- 1 | ../../../../../resources/examples/check.fs.glsl -------------------------------------------------------------------------------- /android-example/app/src/main/assets/grid.fs.glsl: -------------------------------------------------------------------------------- 1 | ../../../../../resources/examples/grid.fs.glsl -------------------------------------------------------------------------------- /android-example/app/src/main/assets/quad.vs.glsl: -------------------------------------------------------------------------------- 1 | ../../../../../resources/examples/quad.vs.glsl -------------------------------------------------------------------------------- /android-example/app/src/main/assets/tri.fs.glsl: -------------------------------------------------------------------------------- 1 | ../../../../../resources/examples/tri.fs.glsl -------------------------------------------------------------------------------- /android-example/app/src/main/assets/tri.vs.glsl: -------------------------------------------------------------------------------- 1 | ../../../../../resources/examples/tri.vs.glsl -------------------------------------------------------------------------------- /android-example/app/src/main/java/org/mozilla/surfmanthreadsexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.mozilla.surfmanthreadsexample; 2 | 3 | import android.opengl.GLSurfaceView; 4 | import android.support.design.widget.FloatingActionButton; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Toast; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | this.setContentView(R.layout.activity_main); 16 | 17 | final SurfmanThreadsExampleRenderer renderer = new SurfmanThreadsExampleRenderer(this); 18 | 19 | GLSurfaceView surfaceView = this.findViewById(R.id.surface_view); 20 | surfaceView.setEGLContextClientVersion(3); 21 | surfaceView.setRenderer(renderer); 22 | 23 | FloatingActionButton runTestsButton = this.findViewById(R.id.run_tests_button); 24 | runTestsButton.setOnClickListener(new View.OnClickListener() { 25 | @Override 26 | public void onClick(View view) { 27 | renderer.runTests(); 28 | 29 | Toast toast = Toast.makeText(getApplicationContext(), 30 | "Tests passed.", 31 | Toast.LENGTH_SHORT); 32 | toast.show(); 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android-example/app/src/main/java/org/mozilla/surfmanthreadsexample/SurfmanThreadsExampleRenderer.java: -------------------------------------------------------------------------------- 1 | package org.mozilla.surfmanthreadsexample; 2 | 3 | import android.opengl.GLSurfaceView.Renderer; 4 | import javax.microedition.khronos.egl.EGLConfig; 5 | import javax.microedition.khronos.opengles.GL10; 6 | 7 | public class SurfmanThreadsExampleRenderer implements Renderer { 8 | private static native void init(SurfmanThreadsExampleResourceLoader resourceLoader, 9 | int width, 10 | int height); 11 | private static native void tick(); 12 | static native void runTests(); 13 | 14 | private final MainActivity mActivity; 15 | 16 | static { 17 | System.loadLibrary("surfman_android_threads"); 18 | } 19 | 20 | SurfmanThreadsExampleRenderer(MainActivity activity) { 21 | mActivity = activity; 22 | } 23 | 24 | @Override 25 | public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { 26 | } 27 | 28 | @Override 29 | public void onSurfaceChanged(GL10 gl10, int width, int height) { 30 | init(new SurfmanThreadsExampleResourceLoader(mActivity.getAssets()), width, height); 31 | } 32 | 33 | @Override 34 | public void onDrawFrame(GL10 gl10) { 35 | tick(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android-example/app/src/main/java/org/mozilla/surfmanthreadsexample/SurfmanThreadsExampleResourceLoader.java: -------------------------------------------------------------------------------- 1 | package org.mozilla.surfmanthreadsexample; 2 | 3 | import android.content.res.AssetManager; 4 | import android.util.Log; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.ByteBuffer; 10 | 11 | public class SurfmanThreadsExampleResourceLoader { 12 | private AssetManager m_assetManager; 13 | 14 | SurfmanThreadsExampleResourceLoader(AssetManager assetManager) { 15 | m_assetManager = assetManager; 16 | } 17 | 18 | ByteBuffer slurp(String path) { 19 | try { 20 | InputStream inputStream = m_assetManager.open(path); 21 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 22 | 23 | byte[] buffer = new byte[4096]; 24 | while (true) { 25 | int nRead = inputStream.read(buffer, 0, buffer.length); 26 | if (nRead == -1) 27 | break; 28 | outputStream.write(buffer, 0, nRead); 29 | } 30 | 31 | byte[] outputBytes = outputStream.toByteArray(); 32 | ByteBuffer resultBuffer = ByteBuffer.allocateDirect(outputStream.size()); 33 | resultBuffer.put(outputBytes); 34 | return resultBuffer; 35 | } catch (IOException exception) { 36 | Log.e("SurfmanThreadsExample", "Resource not found: " + path); 37 | return null; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 31 | 32 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-example/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SurfmanThreadsExample 3 | 4 | -------------------------------------------------------------------------------- /android-example/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android-example/app/src/test/java/org/mozilla/surfmanthreadsexample/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.mozilla.surfmanthreadsexample; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android-example/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.2' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /android-example/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /android-example/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/servo/surfman/8e8095549d721685055f99399032c6be3b1bc9f6/android-example/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android-example/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Oct 04 20:27:47 PDT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /android-example/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /android-example/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /android-example/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "surfman_android_threads" 3 | version = "0.1.0" 4 | authors = ["Patrick Walton "] 5 | edition = "2021" 6 | 7 | [lib] 8 | name = "main" 9 | crate-type = ["cdylib"] 10 | 11 | [profile.dev] 12 | panic = "abort" 13 | 14 | [profile.release] 15 | panic = "abort" 16 | 17 | [dependencies] 18 | android_logger = "0.13" 19 | euclid = { version = "0.22" } 20 | gl = "0.14" 21 | jni = "0.21" 22 | log = "0.4" 23 | surfman = { version = "0.9.4", features = [ "sm-test" ] } 24 | winit = { version = "0.29.10", features = [ "android-native-activity", "rwh_06" ] } 25 | rwh_06 = { package = "raw-window-handle", version = "0.6" } 26 | 27 | [patch.crates-io] 28 | surfman = { path = "../../" } 29 | -------------------------------------------------------------------------------- /android-example/rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | // surfman/android-example/rust/src/lib.rs 2 | 3 | #[macro_use] 4 | extern crate log; 5 | 6 | use crate::threads::common::ResourceLoader; 7 | use crate::threads::App; 8 | 9 | use android_logger::Config; 10 | use euclid::default::Size2D; 11 | use jni::objects::{GlobalRef, JByteBuffer, JClass, JObject, JValue, JValueGen}; 12 | use jni::{JNIEnv, JavaVM}; 13 | use log::Level; 14 | use std::cell::{Cell, RefCell}; 15 | use std::mem; 16 | use std::thread::{self, JoinHandle}; 17 | use surfman::platform::egl::tests; 18 | use surfman::{Connection, NativeContext, NativeDevice}; 19 | 20 | #[path = "../../../examples/threads.rs"] 21 | mod threads; 22 | 23 | thread_local! { 24 | static APP: RefCell> = RefCell::new(None); 25 | static ATTACHED_TO_JNI: Cell = Cell::new(false); 26 | } 27 | 28 | #[no_mangle] 29 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanThreadsExampleRenderer_init( 30 | env: JNIEnv, 31 | _class: JClass, 32 | loader: JObject, 33 | width: i32, 34 | height: i32, 35 | ) { 36 | ATTACHED_TO_JNI.with(|attached_to_jni| attached_to_jni.set(true)); 37 | 38 | android_logger::init_once(Config::default()); 39 | 40 | let window_size = Size2D::new(width, height); 41 | 42 | let connection = Connection::new().unwrap(); 43 | let device = connection 44 | .create_device_from_native_device(NativeDevice::current()) 45 | .unwrap(); 46 | let context = device 47 | .create_context_from_native_context(NativeContext::current().unwrap()) 48 | .unwrap(); 49 | let adapter = device.adapter(); 50 | 51 | APP.with(|app| { 52 | let resource_loader = Box::new(JavaResourceLoader::new(env, loader)); 53 | *app.borrow_mut() = Some(App::new( 54 | connection, 55 | adapter, 56 | device, 57 | context, 58 | resource_loader, 59 | window_size, 60 | )) 61 | }); 62 | } 63 | 64 | #[no_mangle] 65 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanThreadsExampleRenderer_tick( 66 | _env: JNIEnv, 67 | _class: JClass, 68 | ) { 69 | APP.with(|app| app.borrow_mut().as_mut().unwrap().tick(false)); 70 | } 71 | 72 | // NB: New tests should be added here. 73 | 74 | #[no_mangle] 75 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testContextCreation( 76 | _env: JNIEnv, 77 | _class: JClass, 78 | ) { 79 | tests::test_context_creation(); 80 | } 81 | 82 | #[no_mangle] 83 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testCrossDeviceSurfaceTextureBlitFramebuffer( 84 | _env: JNIEnv, 85 | _class: JClass, 86 | ) { 87 | tests::test_cross_device_surface_texture_blit_framebuffer(); 88 | } 89 | 90 | #[no_mangle] 91 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testCrossThreadSurfaceTextureBlitFramebuffer( 92 | _env: JNIEnv, 93 | _class: JClass, 94 | ) { 95 | tests::test_cross_thread_surface_texture_blit_framebuffer(); 96 | } 97 | 98 | #[no_mangle] 99 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testDeviceAccessors( 100 | _env: JNIEnv, 101 | _class: JClass, 102 | ) { 103 | tests::test_device_accessors(); 104 | } 105 | 106 | #[no_mangle] 107 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testDeviceCreation( 108 | _env: JNIEnv, 109 | _class: JClass, 110 | ) { 111 | tests::test_device_creation(); 112 | } 113 | 114 | #[no_mangle] 115 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testGenericSurfaceCreation( 116 | _env: JNIEnv, 117 | _class: JClass, 118 | ) { 119 | tests::test_generic_surface_creation(); 120 | } 121 | 122 | #[no_mangle] 123 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testGL( 124 | _env: JNIEnv, 125 | _class: JClass, 126 | ) { 127 | tests::test_gl(); 128 | } 129 | 130 | #[no_mangle] 131 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testNewlyCreatedContextsAreCurrent( 132 | _env: JNIEnv, 133 | _class: JClass, 134 | ) { 135 | tests::test_newly_created_contexts_are_current(); 136 | } 137 | 138 | #[no_mangle] 139 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testSurfaceTextureBlitFramebuffer( 140 | _env: JNIEnv, 141 | _class: JClass, 142 | ) { 143 | tests::test_surface_texture_blit_framebuffer(); 144 | } 145 | 146 | #[no_mangle] 147 | pub unsafe extern "system" fn Java_org_mozilla_surfmanthreadsexample_SurfmanInstrumentedTest_testSurfaceTextureRightSideUp( 148 | _env: JNIEnv, 149 | _class: JClass, 150 | ) { 151 | tests::test_surface_texture_right_side_up(); 152 | } 153 | 154 | struct JavaResourceLoader { 155 | loader: GlobalRef, 156 | vm: JavaVM, 157 | } 158 | 159 | impl ResourceLoader for JavaResourceLoader { 160 | fn slurp(&self, dest: &mut Vec, filename: &str) { 161 | ATTACHED_TO_JNI.with(|attached_to_jni| { 162 | if !attached_to_jni.get() { 163 | mem::forget(self.vm.attach_current_thread().unwrap()); 164 | attached_to_jni.set(true); 165 | } 166 | }); 167 | 168 | let loader = self.loader.as_obj(); 169 | let mut env = self.vm.get_env().unwrap(); 170 | match env.call_method( 171 | loader, 172 | "slurp", 173 | "(Ljava/lang/String;)Ljava/nio/ByteBuffer;", 174 | &[JValue::Object(&env.new_string(filename).unwrap())], 175 | ) { 176 | Ok(JValueGen::Object(object)) => { 177 | let byte_buffer = JByteBuffer::from(object); 178 | unsafe { 179 | let slice = std::slice::from_raw_parts( 180 | env.get_direct_buffer_address(&byte_buffer).unwrap(), 181 | env.get_direct_buffer_capacity(&byte_buffer).unwrap(), 182 | ); 183 | dest.extend_from_slice(slice); 184 | } 185 | } 186 | _ => panic!("Unexpected return value!"), 187 | } 188 | } 189 | } 190 | 191 | impl JavaResourceLoader { 192 | fn new(env: JNIEnv, loader: JObject) -> JavaResourceLoader { 193 | JavaResourceLoader { 194 | loader: env.new_global_ref(loader).unwrap(), 195 | vm: env.get_java_vm().unwrap(), 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /android-example/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/build.rs 2 | // 3 | //! The `surfman` build script. 4 | 5 | use cfg_aliases::cfg_aliases; 6 | use gl_generator::{Api, Fallbacks, Profile, Registry, StructGenerator}; 7 | use std::env; 8 | use std::fs::File; 9 | use std::path::PathBuf; 10 | 11 | fn main() { 12 | // Setup aliases for #[cfg] checks 13 | cfg_aliases! { 14 | // Platforms 15 | android_platform: { target_os = "android" }, 16 | ohos_platform: { target_env = "ohos" }, 17 | web_platform: { all(target_family = "wasm", target_os = "unknown") }, 18 | macos_platform: { target_os = "macos" }, 19 | ios_platform: { target_os = "ios" }, 20 | windows_platform: { target_os = "windows" }, 21 | apple: { any(target_os = "ios", target_os = "macos") }, 22 | free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten"), not(ohos_platform)) }, 23 | 24 | // Native displays. 25 | x11_platform: { all(free_unix, feature = "sm-x11") }, 26 | wayland_platform: { all(free_unix) }, 27 | 28 | // Features: 29 | // Here we collect the features that are only valid on certain platforms and 30 | // we add aliases that include checks for the correct platform. 31 | angle: { all(windows, feature = "sm-angle") }, 32 | angle_builtin: { all(windows_platform, feature = "sm-angle-builtin") }, 33 | angle_default: { all(windows_platform, feature = "sm-angle-default") }, 34 | no_wgl: { all(windows_platform, feature = "sm-no-wgl") }, 35 | wayland_default: { all(wayland_platform, any(not(x11_platform), feature = "sm-wayland-default")) }, 36 | } 37 | 38 | let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); 39 | let target_family = env::var("CARGO_CFG_TARGET_FAMILY").ok(); 40 | let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); 41 | let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); 42 | 43 | // Generate EGL bindings. 44 | if target_os == "android" 45 | || (target_os == "windows" && cfg!(feature = "sm-angle")) 46 | || target_env == "ohos" 47 | || target_family.as_ref().map_or(false, |f| f == "unix") 48 | { 49 | let mut file = File::create(dest.join("egl_bindings.rs")).unwrap(); 50 | let registry = Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, []); 51 | registry.write_bindings(StructGenerator, &mut file).unwrap(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /doc/surfman-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/chaos_game.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/examples/chaos_game.rs 2 | // 3 | //! Demonstrates how to use `surfman` to draw to a window surface via the CPU. 4 | 5 | use euclid::default::{Point2D, Size2D}; 6 | use rand::{self, Rng}; 7 | use surfman::{SurfaceAccess, SurfaceType}; 8 | use winit::dpi::PhysicalSize; 9 | use winit::event::WindowEvent::KeyboardInput; 10 | use winit::event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent}; 11 | use winit::event_loop::{ControlFlow, EventLoop}; 12 | use winit::keyboard::{Key, NamedKey, PhysicalKey}; 13 | use winit::raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle, HasWindowHandle}; 14 | use winit::window::WindowBuilder; 15 | 16 | #[cfg(target_os = "macos")] 17 | use surfman::SystemConnection; 18 | 19 | const WINDOW_WIDTH: i32 = 800; 20 | const WINDOW_HEIGHT: i32 = 600; 21 | 22 | const BYTES_PER_PIXEL: usize = 4; 23 | 24 | const FOREGROUND_COLOR: u32 = !0; 25 | 26 | const ITERATIONS_PER_FRAME: usize = 20; 27 | 28 | static TRIANGLE_POINTS: [(f32, f32); 3] = [ 29 | (400.0, 300.0 + 75.0 + 150.0), 30 | (400.0 + 259.81, 300.0 + 75.0 - 300.0), 31 | (400.0 - 259.81, 300.0 + 75.0 - 300.0), 32 | ]; 33 | 34 | #[cfg(not(all(target_os = "macos", feature = "sm-raw-window-handle-06")))] 35 | fn main() { 36 | println!("The `chaos_game` demo is not yet supported on this platform."); 37 | } 38 | 39 | #[cfg(all(target_os = "macos", feature = "sm-raw-window-handle-06"))] 40 | fn main() { 41 | let connection = SystemConnection::new().unwrap(); 42 | let adapter = connection.create_adapter().unwrap(); 43 | let mut device = connection.create_device(&adapter).unwrap(); 44 | 45 | let event_loop = EventLoop::new().unwrap(); 46 | let physical_size = PhysicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT); 47 | let window = WindowBuilder::new() 48 | .with_title("Chaos game example") 49 | .with_inner_size(physical_size) 50 | .build(&event_loop) 51 | .unwrap(); 52 | 53 | window.set_visible(true); 54 | 55 | let window_size = window.inner_size(); 56 | let window_size = Size2D::new(window_size.width as i32, window_size.height as i32); 57 | let handle = window.window_handle().unwrap(); 58 | let native_widget = connection 59 | .create_native_widget_from_window_handle(handle, window_size) 60 | .unwrap(); 61 | 62 | let surface_type = SurfaceType::Widget { native_widget }; 63 | let mut surface = device 64 | .create_surface(SurfaceAccess::GPUCPU, surface_type) 65 | .unwrap(); 66 | 67 | let mut rng = rand::thread_rng(); 68 | let mut point = Point2D::new(WINDOW_WIDTH as f32 * 0.5, WINDOW_HEIGHT as f32 * 0.5); 69 | let mut data = vec![0; WINDOW_WIDTH as usize * WINDOW_HEIGHT as usize * 4]; 70 | 71 | event_loop.run(move |event, event_loop| { 72 | match event { 73 | Event::WindowEvent { 74 | event: WindowEvent::CloseRequested, 75 | .. 76 | } 77 | | Event::WindowEvent { 78 | event: 79 | WindowEvent::KeyboardInput { 80 | event: 81 | KeyEvent { 82 | state: ElementState::Pressed, 83 | logical_key: Key::Named(NamedKey::Escape), 84 | .. 85 | }, 86 | .. 87 | }, 88 | .. 89 | } => event_loop.exit(), 90 | _ => { 91 | for _ in 0..ITERATIONS_PER_FRAME { 92 | let (dest_x, dest_y) = TRIANGLE_POINTS[rng.gen_range(0..3)]; 93 | point = point.lerp(Point2D::new(dest_x, dest_y), 0.5); 94 | put_pixel(&mut data, &point, FOREGROUND_COLOR); 95 | } 96 | 97 | device 98 | .lock_surface_data(&mut surface) 99 | .unwrap() 100 | .data() 101 | .copy_from_slice(&data); 102 | device.present_surface(&mut surface).unwrap(); 103 | } 104 | }; 105 | }); 106 | } 107 | 108 | fn put_pixel(data: &mut [u8], point: &Point2D, color: u32) { 109 | let (x, y) = (f32::round(point.x) as usize, f32::round(point.y) as usize); 110 | let start = (y * WINDOW_WIDTH as usize + x) * BYTES_PER_PIXEL; 111 | for index in 0..BYTES_PER_PIXEL { 112 | data[index + start] = (color >> (index * 8)) as u8; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /examples/common/mod.rs: -------------------------------------------------------------------------------- 1 | // examples/common/mod.rs 2 | // 3 | // OpenGL convenience wrappers used in the examples. 4 | 5 | use gl; 6 | use gl::types::{GLenum, GLint, GLuint}; 7 | use std::fs::File; 8 | use std::io::Read; 9 | use std::ptr; 10 | use surfman::GLApi; 11 | 12 | pub struct Program { 13 | pub object: GLuint, 14 | #[allow(dead_code)] 15 | vertex_shader: Shader, 16 | #[allow(dead_code)] 17 | fragment_shader: Shader, 18 | } 19 | 20 | impl Program { 21 | pub fn new(vertex_shader: Shader, fragment_shader: Shader) -> Program { 22 | unsafe { 23 | let program = gl::CreateProgram(); 24 | ck(); 25 | gl::AttachShader(program, vertex_shader.object); 26 | ck(); 27 | gl::AttachShader(program, fragment_shader.object); 28 | ck(); 29 | gl::LinkProgram(program); 30 | ck(); 31 | Program { 32 | object: program, 33 | vertex_shader, 34 | fragment_shader, 35 | } 36 | } 37 | } 38 | } 39 | 40 | pub struct Shader { 41 | object: GLuint, 42 | } 43 | 44 | impl Shader { 45 | pub fn new( 46 | name: &str, 47 | kind: ShaderKind, 48 | gl_api: GLApi, 49 | gl_texture_target: GLenum, 50 | resource_loader: &dyn ResourceLoader, 51 | ) -> Shader { 52 | let mut source = vec![]; 53 | match gl_api { 54 | GLApi::GL => source.extend_from_slice(b"#version 330\n"), 55 | GLApi::GLES => source.extend_from_slice(b"#version 300 es\n"), 56 | } 57 | match gl_texture_target { 58 | gl::TEXTURE_2D => {} 59 | gl::TEXTURE_RECTANGLE => source.extend_from_slice(b"#define SAMPLER_RECT\n"), 60 | _ => {} 61 | } 62 | resource_loader.slurp(&mut source, &format!("{}.{}.glsl", name, kind.extension())); 63 | 64 | unsafe { 65 | let shader = gl::CreateShader(kind.to_gl()); 66 | ck(); 67 | gl::ShaderSource( 68 | shader, 69 | 1, 70 | &(source.as_ptr().cast()), 71 | &(source.len() as GLint), 72 | ); 73 | ck(); 74 | gl::CompileShader(shader); 75 | ck(); 76 | 77 | let mut compile_status = 0; 78 | gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut compile_status); 79 | ck(); 80 | if compile_status != gl::TRUE as GLint { 81 | let mut info_log_length = 0; 82 | gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut info_log_length); 83 | let mut info_log = vec![0; info_log_length as usize + 1]; 84 | gl::GetShaderInfoLog( 85 | shader, 86 | info_log_length, 87 | ptr::null_mut(), 88 | info_log.as_mut_ptr() as *mut _, 89 | ); 90 | eprintln!( 91 | "Failed to compile shader:\n{}", 92 | String::from_utf8_lossy(&info_log) 93 | ); 94 | panic!("Shader compilation failed!"); 95 | } 96 | debug_assert_eq!(compile_status, gl::TRUE as GLint); 97 | 98 | Shader { object: shader } 99 | } 100 | } 101 | } 102 | 103 | pub struct Buffer { 104 | pub object: GLuint, 105 | } 106 | 107 | impl Buffer { 108 | pub fn from_data(data: &[u8]) -> Buffer { 109 | unsafe { 110 | let mut buffer = 0; 111 | gl::GenBuffers(1, &mut buffer); 112 | ck(); 113 | gl::BindBuffer(gl::ARRAY_BUFFER, buffer); 114 | ck(); 115 | gl::BufferData( 116 | gl::ARRAY_BUFFER, 117 | data.len() as isize, 118 | data.as_ptr().cast(), 119 | gl::STATIC_DRAW, 120 | ); 121 | ck(); 122 | Buffer { object: buffer } 123 | } 124 | } 125 | } 126 | 127 | #[derive(Clone, Copy, PartialEq, Debug)] 128 | pub enum ShaderKind { 129 | Vertex, 130 | Fragment, 131 | } 132 | 133 | impl ShaderKind { 134 | fn extension(self) -> &'static str { 135 | match self { 136 | ShaderKind::Vertex => "vs", 137 | ShaderKind::Fragment => "fs", 138 | } 139 | } 140 | 141 | fn to_gl(self) -> GLenum { 142 | match self { 143 | ShaderKind::Vertex => gl::VERTEX_SHADER, 144 | ShaderKind::Fragment => gl::FRAGMENT_SHADER, 145 | } 146 | } 147 | } 148 | 149 | pub trait ResourceLoader { 150 | fn slurp(&self, dest: &mut Vec, filename: &str); 151 | } 152 | 153 | #[allow(dead_code)] 154 | pub struct FilesystemResourceLoader; 155 | 156 | impl ResourceLoader for FilesystemResourceLoader { 157 | fn slurp(&self, dest: &mut Vec, filename: &str) { 158 | let path = format!("resources/examples/{}", filename); 159 | File::open(&path) 160 | .expect("Failed to open file!") 161 | .read_to_end(dest) 162 | .unwrap(); 163 | } 164 | } 165 | 166 | pub fn ck() { 167 | unsafe { 168 | debug_assert_eq!(gl::GetError(), gl::NO_ERROR); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /examples/offscreen.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/examples/offscreen.rs 2 | // 3 | //! This example demonstrates how to create an off-screen context and render into it using 4 | //! `surfman` alone, without any other windowing libraries. 5 | 6 | use crate::common::{ck, Buffer, FilesystemResourceLoader, Program, Shader, ShaderKind}; 7 | 8 | use clap::{App, Arg}; 9 | use euclid::default::Size2D; 10 | use gl; 11 | use gl::types::{GLchar, GLenum, GLint, GLuint, GLvoid}; 12 | use png::{BitDepth, ColorType, Encoder}; 13 | use std::fs::File; 14 | use std::mem; 15 | use std::path::Path; 16 | use std::slice; 17 | use surfman::{Connection, ContextAttributeFlags, ContextAttributes, GLApi, GLVersion}; 18 | use surfman::{SurfaceAccess, SurfaceType}; 19 | 20 | mod common; 21 | 22 | const FRAMEBUFFER_WIDTH: i32 = 640; 23 | const FRAMEBUFFER_HEIGHT: i32 = 480; 24 | 25 | #[derive(Clone, Copy)] 26 | #[repr(C)] 27 | struct Vertex { 28 | x: f32, 29 | y: f32, 30 | r: u8, 31 | g: u8, 32 | b: u8, 33 | a: u8, 34 | } 35 | 36 | static TRI_VERTICES: [Vertex; 3] = [ 37 | Vertex { 38 | x: 0.0, 39 | y: 0.5, 40 | r: 255, 41 | g: 0, 42 | b: 0, 43 | a: 255, 44 | }, 45 | Vertex { 46 | x: 0.5, 47 | y: -0.5, 48 | r: 0, 49 | g: 255, 50 | b: 0, 51 | a: 255, 52 | }, 53 | Vertex { 54 | x: -0.5, 55 | y: -0.5, 56 | r: 0, 57 | g: 0, 58 | b: 255, 59 | a: 255, 60 | }, 61 | ]; 62 | 63 | static APP_NAME: &'static str = "surfman offscreen example"; 64 | 65 | fn main() { 66 | let matches = App::new(APP_NAME) 67 | .arg( 68 | Arg::with_name("hardware") 69 | .short("H") 70 | .long("hardware") 71 | .help("Use hardware rendering"), 72 | ) 73 | .arg( 74 | Arg::with_name("software") 75 | .short("s") 76 | .long("software") 77 | .conflicts_with("hardware") 78 | .help("Use software rendering"), 79 | ) 80 | .arg( 81 | Arg::with_name("OUTPUT") 82 | .required(true) 83 | .index(1) 84 | .help("Output PNG file"), 85 | ) 86 | .get_matches(); 87 | 88 | let connection = Connection::new().unwrap(); 89 | 90 | let adapter = if matches.is_present("software") { 91 | connection.create_software_adapter().unwrap() 92 | } else if matches.is_present("hardware") { 93 | connection.create_hardware_adapter().unwrap() 94 | } else { 95 | connection.create_adapter().unwrap() 96 | }; 97 | 98 | let output_path = Path::new(matches.value_of("OUTPUT").unwrap()).to_owned(); 99 | let output_file = File::create(output_path).unwrap(); 100 | 101 | let mut device = connection.create_device(&adapter).unwrap(); 102 | 103 | let context_attributes = ContextAttributes { 104 | version: GLVersion::new(3, 3), 105 | flags: ContextAttributeFlags::empty(), 106 | }; 107 | let context_descriptor = device 108 | .create_context_descriptor(&context_attributes) 109 | .unwrap(); 110 | let mut context = device.create_context(&context_descriptor, None).unwrap(); 111 | let surface = device 112 | .create_surface( 113 | &context, 114 | SurfaceAccess::GPUOnly, 115 | SurfaceType::Generic { 116 | size: Size2D::new(FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT), 117 | }, 118 | ) 119 | .unwrap(); 120 | device 121 | .bind_surface_to_context(&mut context, surface) 122 | .unwrap(); 123 | 124 | device.make_context_current(&context).unwrap(); 125 | gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name)); 126 | 127 | let mut pixels: Vec = vec![0; FRAMEBUFFER_WIDTH as usize * FRAMEBUFFER_HEIGHT as usize * 4]; 128 | let tri_vertex_array = TriVertexArray::new(device.gl_api(), device.surface_gl_texture_target()); 129 | 130 | unsafe { 131 | let surface_info = device.context_surface_info(&context).unwrap().unwrap(); 132 | gl::BindFramebuffer( 133 | gl::FRAMEBUFFER, 134 | surface_info.framebuffer_object.map_or(0, |fbo| fbo.0.get()), 135 | ); 136 | gl::Viewport(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); 137 | ck(); 138 | gl::ClearColor(0.0, 0.0, 0.0, 1.0); 139 | ck(); 140 | gl::Clear(gl::COLOR_BUFFER_BIT); 141 | ck(); 142 | gl::BindVertexArray(tri_vertex_array.object); 143 | ck(); 144 | gl::UseProgram(tri_vertex_array.tri_program.program.object); 145 | ck(); 146 | gl::DrawArrays(gl::TRIANGLES, 0, 3); 147 | ck(); 148 | gl::Flush(); 149 | ck(); 150 | 151 | gl::ReadPixels( 152 | 0, 153 | 0, 154 | FRAMEBUFFER_WIDTH, 155 | FRAMEBUFFER_HEIGHT, 156 | gl::RGBA, 157 | gl::UNSIGNED_BYTE, 158 | pixels.as_mut_ptr() as *mut GLvoid, 159 | ); 160 | ck(); 161 | } 162 | 163 | device.destroy_context(&mut context).unwrap(); 164 | 165 | let mut encoder = Encoder::new( 166 | output_file, 167 | FRAMEBUFFER_WIDTH as u32, 168 | FRAMEBUFFER_HEIGHT as u32, 169 | ); 170 | encoder.set_color(ColorType::Rgba); 171 | encoder.set_depth(BitDepth::Eight); 172 | let mut image_writer = encoder.write_header().unwrap(); 173 | image_writer.write_image_data(&pixels).unwrap(); 174 | } 175 | 176 | struct TriVertexArray { 177 | object: GLuint, 178 | tri_program: TriProgram, 179 | #[allow(dead_code)] 180 | vertex_buffer: Buffer, 181 | } 182 | 183 | impl TriVertexArray { 184 | fn new(gl_api: GLApi, gl_texture_target: GLenum) -> TriVertexArray { 185 | let tri_program = TriProgram::new(gl_api, gl_texture_target); 186 | unsafe { 187 | let mut vertex_array = 0; 188 | gl::GenVertexArrays(1, &mut vertex_array); 189 | ck(); 190 | gl::BindVertexArray(vertex_array); 191 | ck(); 192 | 193 | let vertex_buffer = Buffer::from_data(slice::from_raw_parts( 194 | TRI_VERTICES.as_ptr().cast(), 195 | mem::size_of::() * 3, 196 | )); 197 | 198 | gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer.object); 199 | ck(); 200 | gl::VertexAttribPointer( 201 | tri_program.position_attribute as GLuint, 202 | 2, 203 | gl::FLOAT, 204 | gl::FALSE, 205 | 12, 206 | 0 as _, 207 | ); 208 | ck(); 209 | gl::VertexAttribPointer( 210 | tri_program.color_attribute as GLuint, 211 | 4, 212 | gl::UNSIGNED_BYTE, 213 | gl::TRUE, 214 | 12, 215 | 8 as _, 216 | ); 217 | ck(); 218 | gl::EnableVertexAttribArray(tri_program.position_attribute as GLuint); 219 | ck(); 220 | gl::EnableVertexAttribArray(tri_program.color_attribute as GLuint); 221 | ck(); 222 | 223 | TriVertexArray { 224 | object: vertex_array, 225 | tri_program, 226 | vertex_buffer, 227 | } 228 | } 229 | } 230 | } 231 | 232 | struct TriProgram { 233 | program: Program, 234 | position_attribute: GLint, 235 | color_attribute: GLint, 236 | } 237 | 238 | impl TriProgram { 239 | fn new(gl_api: GLApi, gl_texture_target: GLenum) -> TriProgram { 240 | let vertex_shader = Shader::new( 241 | "tri", 242 | ShaderKind::Vertex, 243 | gl_api, 244 | gl_texture_target, 245 | &FilesystemResourceLoader, 246 | ); 247 | let fragment_shader = Shader::new( 248 | "tri", 249 | ShaderKind::Fragment, 250 | gl_api, 251 | gl_texture_target, 252 | &FilesystemResourceLoader, 253 | ); 254 | let program = Program::new(vertex_shader, fragment_shader); 255 | unsafe { 256 | let position_attribute = 257 | gl::GetAttribLocation(program.object, c"aPosition".as_ptr().cast()); 258 | ck(); 259 | let color_attribute = gl::GetAttribLocation(program.object, c"aColor".as_ptr().cast()); 260 | ck(); 261 | TriProgram { 262 | program, 263 | position_attribute, 264 | color_attribute, 265 | } 266 | } 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /resources/examples/blit.fs.glsl: -------------------------------------------------------------------------------- 1 | // surfman/resources/examples/blit.fs.glsl 2 | 3 | precision highp float; 4 | 5 | #ifdef SAMPLER_RECT 6 | uniform sampler2DRect uSource; 7 | #else 8 | uniform sampler2D uSource; 9 | #endif 10 | 11 | in vec2 vTexCoord; 12 | 13 | out vec4 oFragColor; 14 | 15 | void main() { 16 | vec2 texCoord = vTexCoord; 17 | #ifdef SAMPLER_RECT 18 | texCoord /= vec2(dFdx(vTexCoord.x), dFdy(vTexCoord.y)); 19 | #endif 20 | oFragColor = texture(uSource, texCoord); 21 | } 22 | -------------------------------------------------------------------------------- /resources/examples/check.fs.glsl: -------------------------------------------------------------------------------- 1 | // surfman/resources/examples/check.fs.glsl 2 | 3 | precision highp float; 4 | 5 | uniform vec2 uViewportOrigin; 6 | uniform vec2 uSpherePosition; 7 | uniform vec3 uRotation; 8 | uniform vec4 uColorA; 9 | uniform vec4 uColorB; 10 | uniform vec3 uCameraPosition; 11 | uniform vec3 uLightPosition; 12 | uniform float uRadius; 13 | 14 | in vec2 vTexCoord; 15 | 16 | out vec4 oFragColor; 17 | 18 | const float PI = 3.14159; 19 | 20 | const float LIGHT_AMBIENT = 1.0; 21 | const float LIGHT_DIFFUSE = 1.0; 22 | const float LIGHT_SPECULAR = 1.0; 23 | const float MATERIAL_AMBIENT = 0.2; 24 | const float MATERIAL_DIFFUSE = 0.7; 25 | const float MATERIAL_SPECULAR = 0.1; 26 | 27 | const vec2 CHECKER_COUNTS = vec2(16.0, 8.0); 28 | 29 | // Hardcoded albedo of 16.0. Works around precision issues. 30 | float pow16(float n) { 31 | float n2 = n * n; 32 | float n4 = n2 * n2; 33 | float n8 = n4 * n4; 34 | return n8 * n8; 35 | } 36 | 37 | mat3 rotateZXY(vec3 theta) { 38 | vec3 tCos = cos(theta), tSin = sin(theta); 39 | vec4 zx = vec4(tSin.zz, tCos.zz) * vec4(tCos.x, tSin.xx, tCos.x); 40 | return mat3( tCos.y * tCos.z, -tCos.y * tSin.z, tSin.y, 41 | tSin.y * zx.z, -tSin.y * zx.y, -tCos.y * tSin.x, 42 | -tSin.y * zx.w, tSin.y * zx.x, tCos.y * tCos.x) + 43 | mat3(0.0, 0.0, 0.0, 44 | zx.x, zx.w, 0.0, 45 | zx.y, zx.z, 0.0); 46 | } 47 | 48 | bool raytraceSphere(vec3 rayOrigin, 49 | vec3 rayDirection, 50 | vec3 center, 51 | float radius, 52 | out vec3 outHitPosition, 53 | out vec3 outHitNormal) { 54 | vec3 originToCenter = center - rayOrigin; 55 | float tCA = dot(originToCenter, rayDirection); 56 | if (tCA < 0.0) 57 | return false; 58 | 59 | float d2 = dot(originToCenter, originToCenter) - tCA * tCA; 60 | float radiusSq = radius * radius; 61 | if (d2 > radiusSq) 62 | return false; 63 | 64 | float tHC = sqrt(radiusSq - d2); 65 | vec2 ts = vec2(tCA) + vec2(-tHC, tHC); 66 | ts = vec2(min(ts.x, ts.y), max(ts.x, ts.y)); 67 | 68 | float t = ts.x >= 0.0 ? ts.x : ts.y; 69 | if (t < 0.0) 70 | return false; 71 | 72 | vec3 hitPosition = rayOrigin + rayDirection * vec3(t); 73 | outHitPosition = hitPosition; 74 | outHitNormal = normalize(hitPosition - center); 75 | return true; 76 | } 77 | 78 | void main() { 79 | vec3 rayOrigin = uCameraPosition; 80 | vec3 rayDirection = normalize(vec3(gl_FragCoord.xy + uViewportOrigin, 0.0) - rayOrigin); 81 | vec3 center = vec3(uSpherePosition, 0.0); 82 | 83 | vec3 hitPosition, normal; 84 | bool hit = raytraceSphere(rayOrigin, rayDirection, center, uRadius, hitPosition, normal); 85 | if (!hit) { 86 | oFragColor = vec4(0.0); 87 | return; 88 | } 89 | 90 | // Hack: Just rotate the texture instead of rotating the sphere. 91 | vec3 texNormal = rotateZXY(uRotation) * normal; 92 | vec2 uv = vec2((1.0 + atan(texNormal.z, texNormal.x) / PI) * 0.5, 93 | acos(texNormal.y) / PI) * CHECKER_COUNTS; 94 | 95 | ivec2 on = ivec2(greaterThanEqual(mod(uv, vec2(2.0)), vec2(1.0))); 96 | vec4 diffuse = ((on.x ^ on.y) > 0) ? uColorA : uColorB; 97 | 98 | vec3 lightDirection = normalize(uLightPosition - hitPosition); 99 | vec3 reflection = -reflect(lightDirection, normal); 100 | vec3 viewer = normalize(uCameraPosition - hitPosition); 101 | 102 | float intensity = LIGHT_AMBIENT * MATERIAL_AMBIENT + 103 | MATERIAL_DIFFUSE * dot(lightDirection, normal) * LIGHT_DIFFUSE + 104 | MATERIAL_SPECULAR * pow16(dot(reflection, viewer)) * LIGHT_SPECULAR; 105 | 106 | oFragColor = vec4(intensity * diffuse.rgb, diffuse.a); 107 | } 108 | -------------------------------------------------------------------------------- /resources/examples/grid.fs.glsl: -------------------------------------------------------------------------------- 1 | // surfman/resources/examples/grid.fs.glsl 2 | 3 | precision highp float; 4 | 5 | uniform vec4 uGridlineColor; 6 | uniform vec4 uBGColor; 7 | uniform float uRadius; 8 | uniform vec2 uSpherePosition; 9 | uniform vec3 uCameraPosition; 10 | uniform vec3 uLightPosition; 11 | 12 | in vec2 vTexCoord; 13 | 14 | out vec4 oFragColor; 15 | 16 | const float DEPTH = 200.0; 17 | const int GRID_SPACING = 50; 18 | const float SHADOW_ATTENUATION = 0.7; 19 | 20 | // FIXME(pcwalton): Move to an include file. 21 | bool raytraceSphere(vec3 rayOrigin, 22 | vec3 rayDirection, 23 | vec3 center, 24 | float radius, 25 | out vec3 outHitPosition, 26 | out vec3 outHitNormal) { 27 | vec3 originToCenter = center - rayOrigin; 28 | float tCA = dot(originToCenter, rayDirection); 29 | if (tCA < 0.0) 30 | return false; 31 | 32 | float d2 = dot(originToCenter, originToCenter) - tCA * tCA; 33 | float radiusSq = radius * radius; 34 | if (d2 > radiusSq) 35 | return false; 36 | 37 | float tHC = sqrt(radiusSq - d2); 38 | vec2 ts = vec2(tCA) + vec2(-tHC, tHC); 39 | ts = vec2(min(ts.x, ts.y), max(ts.x, ts.y)); 40 | 41 | float t = ts.x >= 0.0 ? ts.x : ts.y; 42 | if (t < 0.0) 43 | return false; 44 | 45 | vec3 hitPosition = rayOrigin + rayDirection * vec3(t); 46 | outHitPosition = hitPosition; 47 | outHitNormal = normalize(hitPosition - center); 48 | return true; 49 | } 50 | 51 | void main() { 52 | vec3 rayOrigin = vec3(gl_FragCoord.xy, DEPTH); 53 | vec3 rayDirection = normalize(uLightPosition - rayOrigin); 54 | vec3 origin = vec3(uSpherePosition, 0.0); 55 | 56 | vec3 hitPosition, hitNormal; 57 | bool hit = raytraceSphere(rayOrigin, rayDirection, origin, uRadius, hitPosition, hitNormal); 58 | 59 | bool onGrid = any(equal(ivec2(gl_FragCoord.xy) % ivec2(GRID_SPACING), ivec2(0))); 60 | 61 | vec4 color = onGrid ? uGridlineColor : uBGColor; 62 | if (hit) 63 | color.rgb *= vec3(SHADOW_ATTENUATION); 64 | 65 | oFragColor = color; 66 | } 67 | -------------------------------------------------------------------------------- /resources/examples/quad.vs.glsl: -------------------------------------------------------------------------------- 1 | // surfman/resources/examples/quad.vs.glsl 2 | 3 | precision highp float; 4 | 5 | uniform mat2 uTransform; 6 | uniform vec2 uTranslation; 7 | uniform mat2 uTexTransform; 8 | uniform vec2 uTexTranslation; 9 | 10 | in vec2 aPosition; 11 | 12 | out vec2 vTexCoord; 13 | 14 | void main() { 15 | vTexCoord = uTexTransform * aPosition + uTexTranslation; 16 | vec2 position = uTransform * aPosition + uTranslation; 17 | gl_Position = vec4(position, 0.0, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /resources/examples/tri.fs.glsl: -------------------------------------------------------------------------------- 1 | // surfman/resources/examples/tri.fs.glsl 2 | 3 | precision highp float; 4 | 5 | in vec4 vColor; 6 | 7 | out vec4 oFragColor; 8 | 9 | void main() { 10 | oFragColor = vColor; 11 | } 12 | -------------------------------------------------------------------------------- /resources/examples/tri.vs.glsl: -------------------------------------------------------------------------------- 1 | // surfman/resources/examples/tri.vs.glsl 2 | 3 | precision highp float; 4 | 5 | in vec2 aPosition; 6 | in vec4 aColor; 7 | 8 | out vec4 vColor; 9 | 10 | void main() { 11 | vColor = aColor; 12 | gl_Position = vec4(aPosition, 0.0, 1.0); 13 | } 14 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.81.0" 3 | components = ["rustfmt"] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /src/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/connection.rs 2 | // 3 | //! The abstract interface that all connections conform to. 4 | 5 | use crate::Error; 6 | use crate::GLApi; 7 | 8 | use euclid::default::Size2D; 9 | 10 | use std::os::raw::c_void; 11 | 12 | /// Methods relating to display server connections. 13 | pub trait Connection: Sized { 14 | /// The adapter type associated with this connection. 15 | type Adapter; 16 | /// The device type associated with this connection. 17 | type Device; 18 | /// The native type associated with this connection. 19 | type NativeConnection; 20 | /// The native device type associated with this connection. 21 | type NativeDevice; 22 | /// The native widget type associated with this connection. 23 | type NativeWidget; 24 | 25 | /// Connects to the default display. 26 | fn new() -> Result; 27 | 28 | /// Returns the native connection corresponding to this connection. 29 | fn native_connection(&self) -> Self::NativeConnection; 30 | 31 | /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES). 32 | fn gl_api(&self) -> GLApi; 33 | 34 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 35 | /// 36 | /// This is an alias for `Connection::create_hardware_adapter()`. 37 | fn create_adapter(&self) -> Result; 38 | 39 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 40 | fn create_hardware_adapter(&self) -> Result; 41 | 42 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 43 | fn create_low_power_adapter(&self) -> Result; 44 | 45 | /// Returns the "best" adapter on this system, preferring software adapters. 46 | fn create_software_adapter(&self) -> Result; 47 | 48 | /// Opens a device. 49 | fn create_device(&self, adapter: &Self::Adapter) -> Result; 50 | 51 | /// Wraps an existing native device type in a device. 52 | unsafe fn create_device_from_native_device( 53 | &self, 54 | native_device: Self::NativeDevice, 55 | ) -> Result; 56 | 57 | /// Opens the display connection corresponding to the given `RawDisplayHandle`. 58 | #[cfg(feature = "sm-raw-window-handle-05")] 59 | fn from_raw_display_handle(raw_handle: rwh_05::RawDisplayHandle) -> Result; 60 | 61 | /// Opens the display connection corresponding to the given `DisplayHandle`. 62 | #[cfg(feature = "sm-raw-window-handle-06")] 63 | fn from_display_handle(handle: rwh_06::DisplayHandle) -> Result; 64 | 65 | /// Creates a native widget from a raw pointer 66 | unsafe fn create_native_widget_from_ptr( 67 | &self, 68 | raw: *mut c_void, 69 | size: Size2D, 70 | ) -> Self::NativeWidget; 71 | 72 | /// Create a native widget type from the given `RawWindowHandle`. 73 | #[cfg(feature = "sm-raw-window-handle-05")] 74 | fn create_native_widget_from_raw_window_handle( 75 | &self, 76 | window: rwh_05::RawWindowHandle, 77 | size: Size2D, 78 | ) -> Result; 79 | 80 | /// Create a native widget type from the given `WindowHandle`. 81 | #[cfg(feature = "sm-raw-window-handle-06")] 82 | fn create_native_widget_from_window_handle( 83 | &self, 84 | window: rwh_06::WindowHandle, 85 | size: Size2D, 86 | ) -> Result; 87 | } 88 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/context.rs 2 | // 3 | //! Declarations common to all platform contexts. 4 | 5 | #![allow(unused_imports)] 6 | 7 | use crate::gl; 8 | use crate::info::GLVersion; 9 | use crate::Gl; 10 | 11 | use std::ffi::CStr; 12 | use std::os::raw::c_char; 13 | use std::sync::Mutex; 14 | 15 | /// A unique ID among all currently-allocated contexts. 16 | /// 17 | /// If you destroy a context, subsequently-allocated contexts might reuse the same ID. 18 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 19 | pub struct ContextID(pub u64); 20 | 21 | #[doc(hidden)] 22 | pub static CREATE_CONTEXT_MUTEX: Mutex = Mutex::new(ContextID(0)); 23 | 24 | bitflags! { 25 | /// Various flags that control attributes of the context and/or surfaces created from that 26 | /// context. 27 | /// 28 | /// These roughly correspond to: 29 | /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#WEBGLCONTEXTATTRIBUTES 30 | /// 31 | /// There are some extra `surfman`-specific flags as well. 32 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 33 | pub struct ContextAttributeFlags: u8 { 34 | /// Surfaces created for this context will have an alpha channel (RGBA or BGRA; i.e. 4 35 | /// channels, 32 bits per pixel, 8 bits per channel). If this is not present, surfaces will 36 | /// be RGBX or BGRX (i.e. 3 channels, 32 bits per pixel, 8 bits per channel). 37 | const ALPHA = 0x01; 38 | /// Surfaces created for this context will have a 24-bit depth buffer. 39 | const DEPTH = 0x02; 40 | /// Surfaces created for this context will have an 8-bit stencil buffer, possibly using 41 | /// packed depth/stencil if the GL implementation supports it. 42 | const STENCIL = 0x04; 43 | /// The OpenGL compatibility profile will be used. If this is not present, the core profile 44 | /// is used. 45 | const COMPATIBILITY_PROFILE = 0x08; 46 | } 47 | } 48 | 49 | /// Attributes that control aspects of a context and/or surfaces created from that context. 50 | /// 51 | /// Similar to: 52 | #[derive(Clone, Copy, PartialEq, Debug)] 53 | pub struct ContextAttributes { 54 | /// The OpenGL or OpenGL ES version that this context supports. 55 | /// 56 | /// Keep in mind that OpenGL and OpenGL ES have different version numbering schemes. Before 57 | /// filling in this field, check the result of `Device::gl_api()`. 58 | pub version: GLVersion, 59 | /// Various flags. 60 | pub flags: ContextAttributeFlags, 61 | } 62 | 63 | impl ContextAttributes { 64 | #[allow(dead_code)] 65 | pub(crate) fn zeroed() -> ContextAttributes { 66 | ContextAttributes { 67 | version: GLVersion::new(0, 0), 68 | flags: ContextAttributeFlags::empty(), 69 | } 70 | } 71 | } 72 | 73 | #[cfg(any(target_os = "android", target_env = "ohos"))] 74 | pub(crate) fn current_context_uses_compatibility_profile(_gl: &Gl) -> bool { 75 | false 76 | } 77 | 78 | #[cfg(not(any(target_os = "android", target_env = "ohos")))] 79 | #[allow(dead_code)] 80 | pub(crate) fn current_context_uses_compatibility_profile(gl: &Gl) -> bool { 81 | use glow::HasContext; 82 | 83 | unsafe { 84 | // First, try `GL_CONTEXT_PROFILE_MASK`. 85 | let context_profile_mask = gl.get_parameter_i32(gl::CONTEXT_PROFILE_MASK); 86 | if gl.get_error() == gl::NO_ERROR 87 | && (context_profile_mask & gl::CONTEXT_COMPATIBILITY_PROFILE_BIT as i32) != 0 88 | { 89 | return true; 90 | } 91 | 92 | // Second, look for the `GL_ARB_compatibility` extension. 93 | gl.supported_extensions().contains("GL_ARB_compatibility") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/error.rs 2 | // 3 | //! Various errors that methods can produce. 4 | 5 | /// Various errors that methods can produce. 6 | #[derive(Debug)] 7 | pub enum Error { 8 | /// The method failed for a miscellaneous reason. 9 | Failed, 10 | /// The platform doesn't support this method. 11 | UnsupportedOnThisPlatform, 12 | /// The platform supports this method in theory, but the functionality isn't implemented yet. 13 | Unimplemented, 14 | /// The system doesn't support the requested OpenGL API type (OpenGL or OpenGL ES). 15 | UnsupportedGLType, 16 | /// The system doesn't support the requested OpenGL compatibility profile for the supplied 17 | /// OpenGL version. 18 | /// 19 | /// On some systems, like macOS, the compatibility profile is only supported on some GL 20 | /// versions. 21 | UnsupportedGLProfile, 22 | /// The system doesn't support the requested OpenGL API version. 23 | UnsupportedGLVersion, 24 | /// Choosing an OpenGL pixel format failed. 25 | PixelFormatSelectionFailed(WindowingApiError), 26 | /// The system couldn't choose an OpenGL pixel format. 27 | NoPixelFormatFound, 28 | /// The system couldn't create an OpenGL context. 29 | ContextCreationFailed(WindowingApiError), 30 | /// The system couldn't destroy the OpenGL context. 31 | ContextDestructionFailed(WindowingApiError), 32 | /// The system couldn't make the OpenGL context current or not current. 33 | MakeCurrentFailed(WindowingApiError), 34 | /// The system OpenGL library couldn't be located. 35 | NoGLLibraryFound, 36 | /// An extension necessary for this library to function isn't supported. 37 | RequiredExtensionUnavailable, 38 | /// Looking up an OpenGL function address failed. 39 | GLFunctionNotFound, 40 | /// This context renders to an externally-managed render target. 41 | ExternalRenderTarget, 42 | /// A surface was already attached to this context. 43 | SurfaceAlreadyBound, 44 | /// No suitable adapter could be found. 45 | NoAdapterFound, 46 | /// The device couldn't be opened. 47 | DeviceOpenFailed, 48 | /// The system couldn't create a surface. 49 | SurfaceCreationFailed(WindowingApiError), 50 | /// The system couldn't import a surface from another thread. 51 | SurfaceImportFailed(WindowingApiError), 52 | /// The system couldn't create a surface texture from a surface. 53 | SurfaceTextureCreationFailed(WindowingApiError), 54 | /// The system couldn't present a widget surface. 55 | PresentFailed(WindowingApiError), 56 | /// A context couldn't be created because there is no current context. 57 | NoCurrentContext, 58 | /// The current connection couldn't be fetched because there is no current connection. 59 | NoCurrentConnection, 60 | /// The surface was not created from this context. 61 | IncompatibleSurface, 62 | /// The context descriptor is from a hardware device, but this is a software device, or vice 63 | /// versa. 64 | IncompatibleContextDescriptor, 65 | /// The context is from a hardware device, but this is a software device, or vice versa. 66 | IncompatibleContext, 67 | /// The shared context is not compatible for sharing. 68 | IncompatibleSharedContext, 69 | /// The surface texture is from a hardware device, but this is a software device, or vice 70 | /// versa. 71 | IncompatibleSurfaceTexture, 72 | /// The surface has no window attachment. 73 | NoWidgetAttached, 74 | /// The surface has a window attachment. 75 | WidgetAttached, 76 | /// The native widget is invalid. 77 | InvalidNativeWidget, 78 | /// The surface was not created with the `CPU_READ_WRITE` flag, so it cannot be accessed from 79 | /// the CPU. 80 | SurfaceDataInaccessible, 81 | /// The surface could not be locked for CPU reading due to an OS error. 82 | SurfaceLockFailed, 83 | /// A connection to the display server could not be opened. 84 | ConnectionFailed, 85 | /// A connection to the window server is required to open a hardware device. 86 | ConnectionRequired, 87 | /// The adapter type does not match the supplied connection. 88 | IncompatibleAdapter, 89 | /// The native widget type does not match the supplied device. 90 | IncompatibleNativeWidget, 91 | /// The `raw display handle` is incompatible with this backend. 92 | IncompatibleRawDisplayHandle, 93 | /// The native context does not match the supplied device. 94 | IncompatibleNativeContext, 95 | /// The native device does not match the supplied connection. 96 | IncompatibleNativeDevice, 97 | } 98 | 99 | /// Abstraction of the errors that EGL, CGL, GLX, CGL, etc. return. 100 | /// 101 | /// They all tend to follow similar patterns. 102 | #[derive(Clone, Copy, Debug)] 103 | pub enum WindowingApiError { 104 | /// Miscellaneous error. 105 | Failed, 106 | /// CGL: Invalid pixel format attribute. 107 | /// EGL: An unrecognized attribute or attribute value was passed in the attribute list. 108 | /// X11: Attribute to get is bad. 109 | BadAttribute, 110 | /// CGL: Invalid renderer property. 111 | BadProperty, 112 | /// CGL: Invalid pixel format object. 113 | /// X11: Invalid framebuffer configuration, including an unsupported OpenGL version. 114 | BadPixelFormat, 115 | /// CGL: Invalid renderer information object. 116 | BadRendererInfo, 117 | /// CGL: Invalid context object. 118 | /// EGL: An EGLContext argument does not name a valid EGL rendering context. 119 | /// X11: The context is invalid. 120 | BadContext, 121 | /// Invalid drawable. 122 | BadDrawable, 123 | /// CGL: Invalid display. 124 | /// EGL: An EGLDisplay argument does not name a valid EGL display connection. 125 | BadDisplay, 126 | /// CGL: Invalid context state. 127 | BadState, 128 | /// CGL: Invalid numerical value. 129 | /// X11: Invalid value. 130 | /// GL: Given when a value parameter is not a legal value for that function. 131 | BadValue, 132 | /// CGL: Invalid share context. 133 | /// EGL: Arguments are inconsistent (for example, a valid context requires 134 | /// buffers not supplied by a valid surface). 135 | BadMatch, 136 | /// CGL: Invalid enumerant (constant). 137 | /// X11: Invalid enum value. 138 | /// GL: Given when an enumeration parameter is not a legal enumeration for that function. 139 | BadEnumeration, 140 | /// CGL: Invalid off-screen drawable. 141 | BadOffScreen, 142 | /// CGL: Invalid full-screen drawable. 143 | BadFullScreen, 144 | /// CGL: Invalid window. 145 | BadWindow, 146 | /// CGL: Invalid address; e.g. null pointer passed to function requiring 147 | /// a non-null pointer argument. 148 | BadAddress, 149 | /// CGL: Invalid code module. 150 | BadCodeModule, 151 | /// CGL: Invalid memory allocation; i.e. CGL couldn't allocate memory. 152 | /// EGL: EGL failed to allocate resources for the requested operation. 153 | BadAlloc, 154 | /// CGL: Invalid Core Graphics connection. 155 | BadConnection, 156 | /// EGL: EGL is not initialized, or could not be initialized, for the 157 | /// specified EGL display connection. 158 | NotInitialized, 159 | /// EGL: EGL cannot access a requested resource (for example a context is 160 | /// bound in another thread). 161 | BadAccess, 162 | /// EGL: The current surface of the calling thread is a window, pixel 163 | /// buffer or pixmap that is no longer valid. 164 | BadCurrentSurface, 165 | /// EGL: An EGLSurface argument does not name a valid surface (window, 166 | /// pixel buffer or pixmap) configured for GL rendering. 167 | BadSurface, 168 | /// EGL: One or more argument values are invalid. 169 | BadParameter, 170 | /// EGL: A NativePixmapType argument does not refer to a valid native 171 | /// pixmap. 172 | BadNativePixmap, 173 | /// EGL: A NativeWindowType argument does not refer to a valid native 174 | /// window. 175 | BadNativeWindow, 176 | /// EGL: A power management event has occurred. The application must 177 | /// destroy all contexts and reinitialise OpenGL ES state and objects to 178 | /// continue rendering. 179 | ContextLost, 180 | /// X11: Screen number is bad. 181 | BadScreen, 182 | /// X11: The GLX extension is unavailable on the server. 183 | NoExtension, 184 | /// X11: Visual number not known by GLX. 185 | BadVisual, 186 | /// GL: Given when the set of state for a command is not legal for the parameters given to that 187 | /// command. 188 | BadOperation, 189 | /// EGL: The EGL configuration is unsupported. 190 | BadConfig, 191 | } 192 | -------------------------------------------------------------------------------- /src/gl_utils.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/gl_utils.rs 2 | // 3 | //! Various OpenGL utilities used by the different backends. 4 | 5 | use glow::{HasContext, NativeFramebuffer}; 6 | 7 | use crate::gl; 8 | use crate::Gl; 9 | 10 | #[allow(dead_code)] 11 | pub(crate) fn create_and_bind_framebuffer( 12 | gl: &Gl, 13 | texture_target: u32, 14 | texture_object: Option, 15 | ) -> NativeFramebuffer { 16 | unsafe { 17 | let framebuffer_object = gl.create_framebuffer().unwrap(); 18 | gl.bind_framebuffer(gl::FRAMEBUFFER, Some(framebuffer_object)); 19 | gl.framebuffer_texture_2d( 20 | gl::FRAMEBUFFER, 21 | gl::COLOR_ATTACHMENT0, 22 | texture_target, 23 | texture_object, 24 | 0, 25 | ); 26 | framebuffer_object 27 | } 28 | } 29 | 30 | pub(crate) fn unbind_framebuffer_if_necessary(gl: &Gl, framebuffer_object: NativeFramebuffer) { 31 | unsafe { 32 | // Unbind the framebuffer if it's bound. 33 | let current_draw_framebuffer = gl.get_parameter_framebuffer(gl::DRAW_FRAMEBUFFER_BINDING); 34 | let current_read_framebuffer = gl.get_parameter_framebuffer(gl::READ_FRAMEBUFFER_BINDING); 35 | if current_draw_framebuffer == Some(framebuffer_object) { 36 | gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, None); 37 | } 38 | if current_read_framebuffer == Some(framebuffer_object) { 39 | gl.bind_framebuffer(gl::READ_FRAMEBUFFER, None); 40 | } 41 | } 42 | } 43 | 44 | #[allow(dead_code)] 45 | pub(crate) fn destroy_framebuffer(gl: &Gl, framebuffer_object: NativeFramebuffer) { 46 | unbind_framebuffer_if_necessary(gl, framebuffer_object); 47 | unsafe { 48 | gl.delete_framebuffer(framebuffer_object); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/info.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/info.rs 2 | // 3 | //! OpenGL information. 4 | 5 | use crate::Gl; 6 | use glow::HasContext; 7 | 8 | /// The API (OpenGL or OpenGL ES). 9 | #[derive(Clone, Copy, Debug, PartialEq)] 10 | pub enum GLApi { 11 | /// OpenGL (full or desktop OpenGL). 12 | GL, 13 | /// OpenGL ES (embedded OpenGL). 14 | GLES, 15 | } 16 | 17 | /// Describes the OpenGL version that is requested when a context is created. 18 | /// 19 | /// Since OpenGL and OpenGL ES have different version numbering schemes, the valid values here 20 | /// depend on the value of `Device::gl_api()`. 21 | #[derive(Clone, Copy, Debug, PartialEq)] 22 | pub struct GLVersion { 23 | /// The major OpenGL version (e.g. 4 in 4.2). 24 | pub major: u8, 25 | /// The minor OpenGL version (e.g. 2 in 4.2). 26 | pub minor: u8, 27 | } 28 | 29 | impl GLVersion { 30 | /// Creates a GL version structure with the given major and minor version numbers. 31 | #[inline] 32 | pub fn new(major: u8, minor: u8) -> GLVersion { 33 | GLVersion { major, minor } 34 | } 35 | 36 | #[allow(dead_code)] 37 | pub(crate) fn current(gl: &Gl) -> GLVersion { 38 | let version = gl.version(); 39 | Self { 40 | major: version.major as u8, 41 | minor: version.minor as u8, 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/lib.rs 2 | // 3 | //! Cross-platform GPU device and surface management. 4 | //! 5 | //! You can use this crate to multithread a graphics application so that rendering happens on 6 | //! multiple threads, sharing textures among them in the most efficient manner. It may also be 7 | //! useful as a lightweight framework for *just* initializing rendering in native applications. 8 | //! This is in contrast to crates like SDL, GLFW, winit, and Glutin, all of which have a broader 9 | //! focus in that they manage windowing and the event loop as well. 10 | 11 | #![warn(missing_docs)] 12 | 13 | #[macro_use] 14 | extern crate bitflags; 15 | #[allow(unused_imports)] 16 | #[macro_use] 17 | extern crate log; 18 | 19 | pub mod platform; 20 | pub use platform::default::connection::{Connection, NativeConnection}; 21 | pub use platform::default::context::{Context, ContextDescriptor, NativeContext}; 22 | pub use platform::default::device::{Adapter, Device, NativeDevice}; 23 | pub use platform::default::surface::{NativeWidget, Surface, SurfaceTexture}; 24 | 25 | // TODO(pcwalton): Fill this in with other OS's. 26 | #[cfg(target_os = "macos")] 27 | pub use platform::system::connection::Connection as SystemConnection; 28 | #[cfg(target_os = "macos")] 29 | pub use platform::system::device::{Adapter as SystemAdapter, Device as SystemDevice}; 30 | #[cfg(target_os = "macos")] 31 | pub use platform::system::surface::Surface as SystemSurface; 32 | 33 | #[cfg(feature = "chains")] 34 | pub mod chains; 35 | pub mod connection; 36 | pub mod device; 37 | 38 | pub mod error; 39 | pub use crate::error::{Error, WindowingApiError}; 40 | 41 | mod context; 42 | pub use crate::context::{ContextAttributeFlags, ContextAttributes, ContextID}; 43 | 44 | mod info; 45 | pub use crate::info::{GLApi, GLVersion}; 46 | 47 | mod surface; 48 | pub use crate::surface::{SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType, SystemSurfaceInfo}; 49 | 50 | pub mod macros; 51 | pub(crate) use macros::implement_interfaces; 52 | 53 | pub(crate) use glow::{self as gl, Context as Gl}; 54 | 55 | mod gl_utils; 56 | mod renderbuffers; 57 | 58 | #[cfg(any( 59 | target_os = "android", 60 | target_env = "ohos", 61 | all(target_os = "windows", feature = "sm-angle"), 62 | unix 63 | ))] 64 | #[allow(non_camel_case_types)] 65 | #[allow(clippy::all)] 66 | mod egl { 67 | use std::os::raw::{c_long, c_void}; 68 | pub type khronos_utime_nanoseconds_t = khronos_uint64_t; 69 | pub type khronos_uint64_t = u64; 70 | pub type khronos_ssize_t = c_long; 71 | pub type EGLint = i32; 72 | pub type EGLNativeDisplayType = *const c_void; 73 | pub type EGLNativePixmapType = *const c_void; 74 | pub type EGLNativeWindowType = *const c_void; 75 | pub type NativeDisplayType = EGLNativeDisplayType; 76 | pub type NativePixmapType = EGLNativePixmapType; 77 | pub type NativeWindowType = EGLNativeWindowType; 78 | include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); 79 | } 80 | -------------------------------------------------------------------------------- /src/platform/egl/android_ffi.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/egl/android_ffi.rs 2 | 3 | use std::os::raw::c_int; 4 | 5 | pub(crate) const AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: u32 = 1; 6 | 7 | pub(crate) const AHARDWAREBUFFER_USAGE_CPU_READ_NEVER: u64 = 0; 8 | pub(crate) const AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER: u64 = 0 << 4; 9 | pub(crate) const AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE: u64 = 1 << 8; 10 | pub(crate) const AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER: u64 = 1 << 9; 11 | 12 | #[repr(C)] 13 | pub struct AHardwareBuffer { 14 | opaque: i32, 15 | } 16 | 17 | #[repr(C)] 18 | pub(crate) struct AHardwareBuffer_Desc { 19 | pub(crate) width: u32, 20 | pub(crate) height: u32, 21 | pub(crate) layers: u32, 22 | pub(crate) format: u32, 23 | pub(crate) usage: u64, 24 | pub(crate) stride: u32, 25 | pub(crate) rfu0: u32, 26 | pub(crate) rfu1: u64, 27 | } 28 | 29 | #[repr(C)] 30 | pub struct ANativeWindow { 31 | opaque: i32, 32 | } 33 | 34 | #[link(name = "android")] 35 | extern "C" { 36 | pub(crate) fn AHardwareBuffer_allocate( 37 | desc: *const AHardwareBuffer_Desc, 38 | outBuffer: *mut *mut AHardwareBuffer, 39 | ) -> c_int; 40 | pub(crate) fn AHardwareBuffer_release(buffer: *mut AHardwareBuffer); 41 | 42 | pub(crate) fn ANativeWindow_getWidth(window: *mut ANativeWindow) -> i32; 43 | pub(crate) fn ANativeWindow_getHeight(window: *mut ANativeWindow) -> i32; 44 | } 45 | -------------------------------------------------------------------------------- /src/platform/egl/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/egl/connection.rs 2 | // 3 | //! A no-op connection for Android. 4 | //! 5 | //! FIXME(pcwalton): Should this instead wrap `EGLDisplay`? Is that thread-safe on Android? 6 | 7 | use super::device::{Adapter, Device, NativeDevice}; 8 | use super::surface::NativeWidget; 9 | use crate::Error; 10 | use crate::GLApi; 11 | 12 | #[cfg(android_platform)] 13 | use super::android_ffi::ANativeWindow; 14 | #[cfg(ohos_platform)] 15 | use super::ohos_ffi::OHNativeWindow; 16 | 17 | use euclid::default::Size2D; 18 | 19 | use std::os::raw::c_void; 20 | 21 | /// A connection to the display server. 22 | #[derive(Clone)] 23 | pub struct Connection; 24 | 25 | /// An empty placeholder for native connections. 26 | #[derive(Clone)] 27 | pub struct NativeConnection; 28 | 29 | impl Connection { 30 | /// Connects to the default display. 31 | #[inline] 32 | pub fn new() -> Result { 33 | Ok(Connection) 34 | } 35 | 36 | /// An alias for `Connection::new()`, present for consistency with other backends. 37 | #[inline] 38 | pub unsafe fn from_native_connection(_: NativeConnection) -> Result { 39 | Connection::new() 40 | } 41 | 42 | /// Returns the underlying native connection. 43 | #[inline] 44 | pub fn native_connection(&self) -> NativeConnection { 45 | NativeConnection 46 | } 47 | 48 | /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES). 49 | #[inline] 50 | pub fn gl_api(&self) -> GLApi { 51 | GLApi::GLES 52 | } 53 | 54 | /// Returns the "best" adapter on this system. 55 | /// 56 | /// This is an alias for `Connection::create_hardware_adapter()`. 57 | #[inline] 58 | pub fn create_adapter(&self) -> Result { 59 | self.create_hardware_adapter() 60 | } 61 | 62 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 63 | #[inline] 64 | pub fn create_hardware_adapter(&self) -> Result { 65 | Ok(Adapter) 66 | } 67 | 68 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 69 | #[inline] 70 | pub fn create_low_power_adapter(&self) -> Result { 71 | Ok(Adapter) 72 | } 73 | 74 | /// Returns the "best" adapter on this system, preferring software adapters. 75 | #[inline] 76 | pub fn create_software_adapter(&self) -> Result { 77 | Ok(Adapter) 78 | } 79 | 80 | /// Opens the hardware device corresponding to the given adapter. 81 | /// 82 | /// Device handles are local to a single thread. 83 | #[inline] 84 | pub fn create_device(&self, _: &Adapter) -> Result { 85 | Device::new() 86 | } 87 | 88 | /// Wraps an Android `EGLDisplay` in a device and returns it. 89 | /// 90 | /// The underlying `EGLDisplay` is not retained, as there is no way to do this in the EGL API. 91 | /// Therefore, it is the caller's responsibility to keep it alive as long as this `Device` 92 | /// remains alive. 93 | #[inline] 94 | pub unsafe fn create_device_from_native_device( 95 | &self, 96 | native_device: NativeDevice, 97 | ) -> Result { 98 | Ok(Device { 99 | egl_display: native_device.0, 100 | display_is_owned: false, 101 | }) 102 | } 103 | 104 | /// Opens the display connection corresponding to the given raw display handle. 105 | #[cfg(feature = "sm-raw-window-handle-05")] 106 | pub fn from_raw_display_handle(_: rwh_05::RawDisplayHandle) -> Result { 107 | Ok(Connection) 108 | } 109 | 110 | /// Opens the display connection corresponding to the given `DisplayHandle`. 111 | #[cfg(feature = "sm-raw-window-handle-06")] 112 | pub fn from_display_handle(_: rwh_06::DisplayHandle) -> Result { 113 | Ok(Connection) 114 | } 115 | 116 | #[cfg(android_platform)] 117 | fn create_native_widget_from_ptr_impl(raw: *mut c_void) -> NativeWidget { 118 | NativeWidget { 119 | native_window: raw as *mut ANativeWindow, 120 | } 121 | } 122 | 123 | #[cfg(ohos_platform)] 124 | fn create_native_widget_from_ptr_impl(raw: *mut c_void) -> NativeWidget { 125 | NativeWidget { 126 | native_window: raw as *mut OHNativeWindow, 127 | } 128 | } 129 | 130 | /// Create a native widget from a raw pointer 131 | pub unsafe fn create_native_widget_from_ptr( 132 | &self, 133 | raw: *mut c_void, 134 | _size: Size2D, 135 | ) -> NativeWidget { 136 | debug_assert!(!raw.is_null()); 137 | Self::create_native_widget_from_ptr_impl(raw) 138 | } 139 | 140 | #[cfg(all(feature = "sm-raw-window-handle-05", android_platform))] 141 | #[inline] 142 | fn create_native_widget_from_rwh_05_handle( 143 | raw_handle: rwh_05::RawWindowHandle, 144 | ) -> Result { 145 | use rwh_05::RawWindowHandle::AndroidNdk; 146 | 147 | match raw_handle { 148 | AndroidNdk(handle) => Ok(NativeWidget { 149 | native_window: handle.a_native_window as *mut _, 150 | }), 151 | _ => Err(Error::IncompatibleNativeWidget), 152 | } 153 | } 154 | 155 | #[cfg(all(feature = "sm-raw-window-handle-05", ohos_platform))] 156 | #[inline] 157 | fn create_native_widget_from_rwh_05_handle( 158 | _raw_handle: rwh_05::RawWindowHandle, 159 | ) -> Result { 160 | Err(Error::IncompatibleNativeWidget) 161 | } 162 | 163 | /// Create a native widget type from the given `RawWindowHandle`. 164 | #[cfg(feature = "sm-raw-window-handle-05")] 165 | #[inline] 166 | pub fn create_native_widget_from_raw_window_handle( 167 | &self, 168 | raw_handle: rwh_05::RawWindowHandle, 169 | _size: Size2D, 170 | ) -> Result { 171 | Self::create_native_widget_from_rwh_05_handle(raw_handle) 172 | } 173 | 174 | #[cfg(all(feature = "sm-raw-window-handle-06", android_platform))] 175 | #[inline] 176 | fn create_native_widget_from_rwh_06_handle( 177 | handle: rwh_06::WindowHandle, 178 | ) -> Result { 179 | use rwh_06::RawWindowHandle::AndroidNdk; 180 | 181 | match handle.as_raw() { 182 | AndroidNdk(handle) => Ok(NativeWidget { 183 | native_window: handle.a_native_window.as_ptr() as *mut _, 184 | }), 185 | _ => Err(Error::IncompatibleNativeWidget), 186 | } 187 | } 188 | 189 | #[cfg(all(feature = "sm-raw-window-handle-06", ohos_platform))] 190 | #[inline] 191 | fn create_native_widget_from_rwh_06_handle( 192 | handle: rwh_06::WindowHandle, 193 | ) -> Result { 194 | use rwh_06::RawWindowHandle::OhosNdk; 195 | 196 | match handle.as_raw() { 197 | OhosNdk(handle) => Ok(NativeWidget { 198 | native_window: handle.native_window.as_ptr().cast(), 199 | }), 200 | _ => Err(Error::IncompatibleNativeWidget), 201 | } 202 | } 203 | 204 | /// Create a native widget type from the given `WindowHandle`. 205 | #[cfg(feature = "sm-raw-window-handle-06")] 206 | #[inline] 207 | pub fn create_native_widget_from_window_handle( 208 | &self, 209 | handle: rwh_06::WindowHandle, 210 | _size: Size2D, 211 | ) -> Result { 212 | Self::create_native_widget_from_rwh_06_handle(handle) 213 | } 214 | } 215 | 216 | impl NativeConnection { 217 | /// Creates a native connection. 218 | /// 219 | /// This is a no-op method present for consistency with other backends. 220 | #[inline] 221 | pub fn current() -> Result { 222 | Ok(NativeConnection) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/platform/egl/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/egl/device.rs 2 | // 3 | //! A thread-local handle to the device. 4 | 5 | use super::connection::Connection; 6 | use crate::egl; 7 | use crate::egl::types::EGLDisplay; 8 | use crate::platform::generic::egl::device::EGL_FUNCTIONS; 9 | use crate::{Error, GLApi}; 10 | 11 | /// Represents a hardware display adapter that can be used for rendering (including the CPU). 12 | /// 13 | /// Adapters can be sent between threads. To render with an adapter, open a thread-local `Device`. 14 | #[derive(Clone, Debug)] 15 | pub struct Adapter; 16 | 17 | /// A thread-local handle to a device. 18 | /// 19 | /// Devices contain most of the relevant surface management methods. 20 | pub struct Device { 21 | pub(crate) egl_display: EGLDisplay, 22 | pub(crate) display_is_owned: bool, 23 | } 24 | 25 | /// Wrapper for an `EGLDisplay`. 26 | #[derive(Clone, Copy)] 27 | pub struct NativeDevice(pub EGLDisplay); 28 | 29 | impl Drop for Device { 30 | fn drop(&mut self) { 31 | EGL_FUNCTIONS.with(|egl| unsafe { 32 | if !self.display_is_owned { 33 | return; 34 | } 35 | let result = egl.Terminate(self.egl_display); 36 | assert_ne!(result, egl::FALSE); 37 | self.egl_display = egl::NO_DISPLAY; 38 | }) 39 | } 40 | } 41 | 42 | impl NativeDevice { 43 | /// Returns the current EGL display. 44 | /// 45 | /// If there is no current EGL display, `egl::NO_DISPLAY` is returned. 46 | pub fn current() -> NativeDevice { 47 | EGL_FUNCTIONS.with(|egl| unsafe { NativeDevice(egl.GetCurrentDisplay()) }) 48 | } 49 | } 50 | 51 | impl Device { 52 | #[inline] 53 | pub(crate) fn new() -> Result { 54 | EGL_FUNCTIONS.with(|egl| { 55 | unsafe { 56 | let egl_display = egl.GetDisplay(egl::DEFAULT_DISPLAY); 57 | assert_ne!(egl_display, egl::NO_DISPLAY); 58 | 59 | // I don't think this should ever fail. 60 | let (mut major_version, mut minor_version) = (0, 0); 61 | let result = egl.Initialize(egl_display, &mut major_version, &mut minor_version); 62 | assert_ne!(result, egl::FALSE); 63 | 64 | Ok(Device { 65 | egl_display, 66 | display_is_owned: true, 67 | }) 68 | } 69 | }) 70 | } 71 | 72 | /// Returns the EGL display corresponding to this device. 73 | #[inline] 74 | pub fn native_device(&self) -> NativeDevice { 75 | NativeDevice(self.egl_display) 76 | } 77 | 78 | /// Returns the display server connection that this device was created with. 79 | #[inline] 80 | pub fn connection(&self) -> Connection { 81 | Connection 82 | } 83 | 84 | /// Returns the adapter that this device was created with. 85 | #[inline] 86 | pub fn adapter(&self) -> Adapter { 87 | Adapter 88 | } 89 | 90 | /// Returns the OpenGL API flavor that this device supports (OpenGL or OpenGL ES). 91 | #[inline] 92 | pub fn gl_api(&self) -> GLApi { 93 | GLApi::GLES 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/platform/egl/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/egl/mod.rs 2 | // 3 | //! Bindings to EGL on Android. 4 | 5 | pub mod connection; 6 | pub mod context; 7 | pub mod device; 8 | pub mod surface; 9 | 10 | #[cfg(android_platform)] 11 | mod android_ffi; 12 | 13 | #[cfg(ohos_platform)] 14 | mod ohos_ffi; 15 | 16 | crate::implement_interfaces!(); 17 | 18 | #[cfg(feature = "sm-test")] 19 | #[path = "../../tests.rs"] 20 | pub mod tests; 21 | 22 | #[cfg(all(not(feature = "sm-test"), test))] 23 | #[path = "../../tests.rs"] 24 | pub mod tests; 25 | -------------------------------------------------------------------------------- /src/platform/egl/ohos_ffi.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(non_upper_case_globals)] 3 | #![allow(non_snake_case)] 4 | #![allow(unused)] 5 | 6 | use std::ffi::c_void; 7 | 8 | /// From `eglext.h` on OpenHarmony. 9 | pub(crate) const EGL_NATIVE_BUFFER_OHOS: u32 = 0x34E1; 10 | 11 | use crate::egl::types::EGLClientBuffer; 12 | 13 | #[repr(C)] 14 | pub struct NativeWindow { 15 | _unused: [u8; 0], 16 | } 17 | 18 | pub type OHNativeWindow = NativeWindow; 19 | 20 | #[repr(transparent)] 21 | pub(crate) struct NativeWindowOperation(core::ffi::c_int); 22 | 23 | impl NativeWindowOperation { 24 | pub const GET_BUFFER_GEOMETRY: Self = Self(1); 25 | } 26 | 27 | /// According to the [native window guidelines], users need to link against 28 | /// both the NDK and `native_window`. 29 | /// [native window guidelines]: 30 | #[link(name = "native_window")] 31 | #[link(name = "ace_ndk.z")] 32 | extern "C" { 33 | /// Sets or obtains the attributes of a native window 34 | /// 35 | /// Can be used to query information like height and width. 36 | /// See the official [Documentation] for detailed usage information. 37 | /// 38 | /// # Safety 39 | /// 40 | /// - The `window` handle must be valid. 41 | /// - The variable arguments which must be passed to this function vary depending on the 42 | /// value of `code`. 43 | /// - For `NativeWindowOperation::GET_BUFFER_GEOMETRY` the function takes two output 44 | /// i32 pointers, `height: *mut i32` and `width: *mut i32` which are passed as variadic 45 | /// arguments. 46 | /// 47 | /// 48 | /// [Documentation]: 49 | pub(crate) fn OH_NativeWindow_NativeWindowHandleOpt( 50 | window: *mut OHNativeWindow, 51 | code: NativeWindowOperation, 52 | ... 53 | ) -> i32; 54 | } 55 | 56 | #[link(name = "EGL")] 57 | extern "C" { 58 | /// Get the native Client buffer 59 | /// 60 | /// The extension function `eglGetNativeClientBufferANDROID` is available starting with OpenHarmony 5.0. 61 | /// Its availability is documented here: https://docs.openharmony.cn/pages/v5.0/en/application-dev/reference/native-lib/egl-symbol.md 62 | /// However it is not available in `EGL_EXTENSION_FUNCTIONS`, since `eglGetProcAddress()` does not find 63 | /// the function and returns NULL. 64 | pub(crate) fn eglGetNativeClientBufferANDROID(buffer: *const c_void) -> EGLClientBuffer; 65 | } 66 | 67 | // Bindings to `native_buffer` components we use. Official Documentation: 68 | // https://docs.openharmony.cn/pages/v5.0/en/application-dev/graphics/native-buffer-guidelines.md 69 | 70 | #[repr(C)] 71 | pub struct OH_NativeBuffer { 72 | _opaque: [u8; 0], 73 | } 74 | 75 | #[repr(C)] 76 | #[derive(Debug, Clone)] 77 | pub struct OH_NativeBuffer_Config { 78 | /// Width in pixels 79 | pub width: i32, 80 | /// Height in pixels 81 | pub height: i32, 82 | /// One of PixelFormat 83 | pub format: OH_NativeBuffer_Format, 84 | /// Combination of buffer usage 85 | pub usage: OH_NativeBuffer_Usage, 86 | /// the stride of memory 87 | pub stride: i32, 88 | } 89 | 90 | #[repr(transparent)] 91 | #[derive(Debug, Copy, Clone)] 92 | pub struct OH_NativeBuffer_Format(core::ffi::c_int); 93 | 94 | impl OH_NativeBuffer_Format { 95 | /// RGBA8888 format 96 | pub const RGBA_8888: OH_NativeBuffer_Format = OH_NativeBuffer_Format(12); 97 | } 98 | 99 | bitflags! { 100 | #[repr(transparent)] 101 | #[derive(Debug, Copy, Clone)] 102 | pub struct OH_NativeBuffer_Usage: core::ffi::c_int { 103 | /// CPU read buffer 104 | const CPU_READ = 1; 105 | /// CPU write memory 106 | const CPU_WRITE = 1 << 1; 107 | /// Direct memory access (DMA) buffer 108 | const MEM_DMA = 1 << 3; 109 | /// For GPU write case 110 | const HW_RENDER = 1 << 8; 111 | /// For GPU read case 112 | const HW_TEXTURE = 1 << 9; 113 | /// Often be mapped for direct CPU reads 114 | const CPU_READ_OFTEN = 1 << 16; 115 | /// 512 bytes alignment 116 | const ALIGNMENT_512 = 1 << 18; 117 | } 118 | } 119 | 120 | #[link(name = "native_buffer")] 121 | extern "C" { 122 | /// Allocate an `OH_NativeBuffer`` that matches the passed config. 123 | /// 124 | /// A new `OH_NativeBuffer` instance is created each time this function is called. 125 | /// NULL is returned if allocation fails. 126 | pub fn OH_NativeBuffer_Alloc(config: *const OH_NativeBuffer_Config) -> *mut OH_NativeBuffer; 127 | /// Decreases the reference count of a OH_NativeBuffer and, when the reference count reaches 0, 128 | /// destroys this OH_NativeBuffer. 129 | /// 130 | /// Since API-9 131 | pub fn OH_NativeBuffer_Unreference(buffer: *mut OH_NativeBuffer) -> i32; 132 | } 133 | -------------------------------------------------------------------------------- /src/platform/egl/surface/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/egl/surface/mod.rs 2 | // 3 | //! Surface management for Android and OpenHarmony using the `GraphicBuffer` class and EGL. 4 | 5 | use crate::context::ContextID; 6 | use crate::platform::generic::egl::ffi::EGLImageKHR; 7 | 8 | use euclid::default::Size2D; 9 | use glow::Texture; 10 | use std::fmt::{self, Debug, Formatter}; 11 | use std::marker::PhantomData; 12 | use std::thread; 13 | 14 | pub use crate::platform::generic::egl::context::ContextDescriptor; 15 | 16 | #[cfg(android_platform)] 17 | mod android_surface; 18 | 19 | #[cfg(android_platform)] 20 | pub use android_surface::*; 21 | 22 | #[cfg(ohos_platform)] 23 | mod ohos_surface; 24 | 25 | #[cfg(ohos_platform)] 26 | pub use ohos_surface::*; 27 | 28 | /// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either 29 | /// displayed in a native widget or bound to a texture for reading. 30 | /// 31 | /// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a 32 | /// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, 33 | /// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a 34 | /// widget but not bound to a texture. 35 | /// 36 | /// Surfaces are specific to a given context and cannot be rendered to from any context other than 37 | /// the one they were created with. However, they can be *read* from any context on any thread (as 38 | /// long as that context shares the same adapter and connection), by wrapping them in a 39 | /// `SurfaceTexture`. 40 | /// 41 | /// Depending on the platform, each surface may be internally double-buffered. 42 | /// 43 | /// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. 44 | pub struct Surface { 45 | pub(crate) context_id: ContextID, 46 | pub(crate) size: Size2D, 47 | pub(crate) objects: SurfaceObjects, 48 | pub(crate) destroyed: bool, 49 | } 50 | 51 | /// Represents an OpenGL texture that wraps a surface. 52 | /// 53 | /// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to 54 | /// write to such a texture (e.g. by binding it to a framebuffer and rendering to that 55 | /// framebuffer). 56 | /// 57 | /// Surface textures are local to a context, but that context does not have to be the same context 58 | /// as that associated with the underlying surface. The texture must be destroyed with the 59 | /// `destroy_surface_texture()` method, or a panic will occur. 60 | pub struct SurfaceTexture { 61 | pub(crate) surface: Surface, 62 | pub(crate) local_egl_image: EGLImageKHR, 63 | pub(crate) texture_object: Option, 64 | pub(crate) phantom: PhantomData<*const ()>, 65 | } 66 | 67 | unsafe impl Send for Surface {} 68 | 69 | impl Debug for Surface { 70 | fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { 71 | write!(formatter, "Surface({:x})", self.id().0) 72 | } 73 | } 74 | 75 | impl Drop for Surface { 76 | fn drop(&mut self) { 77 | if !self.destroyed && !thread::panicking() { 78 | panic!("Should have destroyed the surface first with `destroy_surface()`!") 79 | } 80 | } 81 | } 82 | 83 | impl Debug for SurfaceTexture { 84 | fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 85 | write!(f, "SurfaceTexture({:?})", self.surface) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/platform/generic/egl/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/generic/egl/device.rs 2 | // 3 | //! Functionality common to backends using EGL displays. 4 | 5 | use crate::egl::Egl; 6 | 7 | #[cfg(not(target_os = "windows"))] 8 | use libc::{dlopen, dlsym, RTLD_LAZY}; 9 | use std::ffi::{CStr, CString}; 10 | use std::mem; 11 | use std::os::raw::c_void; 12 | use std::sync::LazyLock; 13 | #[cfg(target_os = "windows")] 14 | use winapi::shared::minwindef::HMODULE; 15 | #[cfg(target_os = "windows")] 16 | use winapi::um::libloaderapi; 17 | 18 | thread_local! { 19 | pub static EGL_FUNCTIONS: Egl = Egl::load_with(get_proc_address); 20 | } 21 | 22 | #[cfg(target_os = "windows")] 23 | static EGL_LIBRARY: LazyLock = LazyLock::new(|| unsafe { 24 | let module = libloaderapi::LoadLibraryA(c"libEGL.dll".as_ptr()); 25 | EGLLibraryWrapper(module) 26 | }); 27 | 28 | #[cfg(target_env = "ohos")] 29 | static EGL_POTENTIAL_SO_NAMES: [&CStr; 1] = [c"libEGL.so"]; 30 | 31 | #[cfg(not(any(target_os = "windows", target_os = "macos", target_env = "ohos")))] 32 | static EGL_POTENTIAL_SO_NAMES: [&CStr; 2] = [c"libEGL.so.1", c"libEGL.so"]; 33 | 34 | #[cfg(not(any(target_os = "windows", target_os = "macos")))] 35 | static EGL_LIBRARY: LazyLock = LazyLock::new(|| { 36 | for soname in EGL_POTENTIAL_SO_NAMES { 37 | unsafe { 38 | let handle = dlopen(soname.as_ptr(), RTLD_LAZY); 39 | if !handle.is_null() { 40 | return EGLLibraryWrapper(handle); 41 | } 42 | } 43 | } 44 | panic!("Unable to load the libEGL shared object"); 45 | }); 46 | 47 | #[cfg(target_os = "windows")] 48 | struct EGLLibraryWrapper(HMODULE); 49 | #[cfg(not(target_os = "windows"))] 50 | struct EGLLibraryWrapper(*mut c_void); 51 | 52 | unsafe impl Send for EGLLibraryWrapper {} 53 | unsafe impl Sync for EGLLibraryWrapper {} 54 | 55 | #[cfg(target_os = "windows")] 56 | fn get_proc_address(symbol_name: &str) -> *const c_void { 57 | unsafe { 58 | let symbol_name: CString = CString::new(symbol_name).unwrap(); 59 | let symbol_ptr = symbol_name.as_ptr(); 60 | libloaderapi::GetProcAddress(EGL_LIBRARY.0, symbol_ptr).cast() 61 | } 62 | } 63 | 64 | #[cfg(not(target_os = "windows"))] 65 | fn get_proc_address(symbol_name: &str) -> *const c_void { 66 | unsafe { 67 | let symbol_name: CString = CString::new(symbol_name).unwrap(); 68 | let symbol_ptr = symbol_name.as_ptr(); 69 | dlsym(EGL_LIBRARY.0, symbol_ptr).cast_const() 70 | } 71 | } 72 | 73 | pub(crate) unsafe fn lookup_egl_extension(name: &CStr) -> *mut c_void { 74 | EGL_FUNCTIONS.with(|egl| mem::transmute(egl.GetProcAddress(name.as_ptr()))) 75 | } 76 | -------------------------------------------------------------------------------- /src/platform/generic/egl/error.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/generic/egl/error.rs 2 | 3 | //! Translation of errors from the EGL API to `surfman` errors. 4 | 5 | use crate::egl; 6 | use crate::egl::types::{EGLenum, EGLint}; 7 | use crate::WindowingApiError; 8 | 9 | pub(crate) trait ToWindowingApiError { 10 | fn to_windowing_api_error(self) -> WindowingApiError; 11 | } 12 | 13 | impl ToWindowingApiError for EGLint { 14 | fn to_windowing_api_error(self) -> WindowingApiError { 15 | match self as EGLenum { 16 | egl::NOT_INITIALIZED => WindowingApiError::NotInitialized, 17 | egl::BAD_ACCESS => WindowingApiError::BadAccess, 18 | egl::BAD_ALLOC => WindowingApiError::BadAlloc, 19 | egl::BAD_ATTRIBUTE => WindowingApiError::BadAttribute, 20 | egl::BAD_CONFIG => WindowingApiError::BadConfig, 21 | egl::BAD_CONTEXT => WindowingApiError::BadContext, 22 | egl::BAD_CURRENT_SURFACE => WindowingApiError::BadCurrentSurface, 23 | egl::BAD_DISPLAY => WindowingApiError::BadDisplay, 24 | egl::BAD_SURFACE => WindowingApiError::BadSurface, 25 | egl::BAD_MATCH => WindowingApiError::BadMatch, 26 | egl::BAD_PARAMETER => WindowingApiError::BadParameter, 27 | egl::BAD_NATIVE_PIXMAP => WindowingApiError::BadNativePixmap, 28 | egl::BAD_NATIVE_WINDOW => WindowingApiError::BadNativeWindow, 29 | egl::CONTEXT_LOST => WindowingApiError::ContextLost, 30 | _ => WindowingApiError::Failed, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/platform/generic/egl/ffi.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/generic/egl/ffi.rs 2 | // 3 | //! FFI-related functionality common to the various EGL backends. 4 | 5 | #![allow(dead_code)] 6 | 7 | use crate::egl::types::{EGLAttrib, EGLBoolean, EGLContext, EGLDeviceEXT, EGLDisplay, EGLSurface}; 8 | use crate::egl::types::{EGLenum, EGLint}; 9 | 10 | use std::os::raw::c_void; 11 | use std::sync::LazyLock; 12 | 13 | pub enum EGLClientBufferOpaque {} 14 | pub type EGLClientBuffer = *mut EGLClientBufferOpaque; 15 | 16 | pub enum EGLImageKHROpaque {} 17 | pub type EGLImageKHR = *mut EGLImageKHROpaque; 18 | 19 | pub const EGL_GL_TEXTURE_2D_KHR: EGLenum = 0x30b1; 20 | pub const EGL_IMAGE_PRESERVED_KHR: EGLenum = 0x30d2; 21 | pub const EGL_CONTEXT_MINOR_VERSION_KHR: EGLenum = 0x30fb; 22 | pub const EGL_CONTEXT_OPENGL_PROFILE_MASK: EGLenum = 0x30fd; 23 | pub const EGL_PLATFORM_DEVICE_EXT: EGLenum = 0x313f; 24 | pub const EGL_NATIVE_BUFFER_ANDROID: EGLenum = 0x3140; 25 | pub const EGL_PLATFORM_X11_KHR: EGLenum = 0x31d5; 26 | pub const EGL_PLATFORM_WAYLAND_KHR: EGLenum = 0x31d8; 27 | pub const EGL_PLATFORM_SURFACELESS_MESA: EGLenum = 0x31dd; 28 | pub const EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE: EGLenum = 0x3200; 29 | pub const EGL_BAD_DEVICE_EXT: EGLenum = 0x322b; 30 | pub const EGL_DEVICE_EXT: EGLenum = 0x322c; 31 | pub const EGL_D3D11_DEVICE_ANGLE: EGLenum = 0x33a1; 32 | pub const EGL_DXGI_KEYED_MUTEX_ANGLE: EGLenum = 0x33a2; 33 | pub const EGL_D3D_TEXTURE_ANGLE: EGLenum = 0x33a3; 34 | 35 | pub const EGL_NO_DEVICE_EXT: EGLDeviceEXT = 0 as EGLDeviceEXT; 36 | pub const EGL_NO_IMAGE_KHR: EGLImageKHR = 0 as EGLImageKHR; 37 | 38 | pub const EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT: EGLint = 1; 39 | pub const EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT: EGLint = 2; 40 | 41 | #[allow(non_snake_case)] 42 | pub(crate) struct EGLExtensionFunctions { 43 | // Ubiquitous extensions assumed to be present 44 | pub(crate) CreateImageKHR: extern "C" fn( 45 | dpy: EGLDisplay, 46 | ctx: EGLContext, 47 | target: EGLenum, 48 | buffer: EGLClientBuffer, 49 | attrib_list: *const EGLint, 50 | ) -> EGLImageKHR, 51 | pub(crate) DestroyImageKHR: extern "C" fn(dpy: EGLDisplay, image: EGLImageKHR) -> EGLBoolean, 52 | pub(crate) ImageTargetTexture2DOES: extern "C" fn(target: EGLenum, image: EGLImageKHR), 53 | 54 | // Optional extensions 55 | pub(crate) CreateDeviceANGLE: Option< 56 | extern "C" fn( 57 | device_type: EGLint, 58 | native_device: *mut c_void, 59 | attrib_list: *const EGLAttrib, 60 | ) -> EGLDeviceEXT, 61 | >, 62 | pub(crate) GetNativeClientBufferANDROID: 63 | Option EGLClientBuffer>, 64 | pub(crate) QueryDeviceAttribEXT: Option< 65 | extern "C" fn(device: EGLDeviceEXT, attribute: EGLint, value: *mut EGLAttrib) -> EGLBoolean, 66 | >, 67 | pub(crate) QueryDisplayAttribEXT: Option< 68 | extern "C" fn(dpy: EGLDisplay, attribute: EGLint, value: *mut EGLAttrib) -> EGLBoolean, 69 | >, 70 | pub(crate) QuerySurfacePointerANGLE: Option< 71 | extern "C" fn( 72 | dpy: EGLDisplay, 73 | surface: EGLSurface, 74 | attribute: EGLint, 75 | value: *mut *mut c_void, 76 | ) -> EGLBoolean, 77 | >, 78 | } 79 | 80 | pub(crate) static EGL_EXTENSION_FUNCTIONS: LazyLock = LazyLock::new(|| { 81 | use crate::platform::generic::egl::device::lookup_egl_extension as get; 82 | use std::mem::transmute as cast; 83 | unsafe { 84 | EGLExtensionFunctions { 85 | CreateImageKHR: cast(get(c"eglCreateImageKHR")), 86 | DestroyImageKHR: cast(get(c"eglDestroyImageKHR")), 87 | ImageTargetTexture2DOES: cast(get(c"glEGLImageTargetTexture2DOES")), 88 | 89 | CreateDeviceANGLE: cast(get(c"eglCreateDeviceANGLE")), 90 | GetNativeClientBufferANDROID: cast(get(c"eglGetNativeClientBufferANDROID")), 91 | QueryDeviceAttribEXT: cast(get(c"eglQueryDeviceAttribEXT")), 92 | QueryDisplayAttribEXT: cast(get(c"eglQueryDisplayAttribEXT")), 93 | QuerySurfacePointerANGLE: cast(get(c"eglQuerySurfacePointerANGLE")), 94 | } 95 | } 96 | }); 97 | -------------------------------------------------------------------------------- /src/platform/generic/egl/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/common/egl/mod.rs 2 | // 3 | //! Functionality common to EGL-based backends. 4 | 5 | #![allow(dead_code)] 6 | 7 | pub(crate) mod context; 8 | pub(crate) mod device; 9 | pub(crate) mod error; 10 | pub(crate) mod ffi; 11 | pub(crate) mod surface; 12 | -------------------------------------------------------------------------------- /src/platform/generic/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/generic/mod.rs 2 | // 3 | //! Backends that are not specific to any operating system. 4 | 5 | #[cfg(any(android_platform, angle, free_unix, ohos_platform))] 6 | pub(crate) mod egl; 7 | 8 | pub mod multi; 9 | -------------------------------------------------------------------------------- /src/platform/generic/multi/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/generic/multi/mod.rs 2 | // 3 | //! An abstraction that allows the choice of backends dynamically. 4 | //! 5 | //! This is useful on Unix systems, because it allows for Wayland to be tried first, and, failing 6 | //! that, to use X11. 7 | //! 8 | //! Each type here has two type parameters: a "default" device and an "alternate" device. Opening a 9 | //! connection will first attempt to open the default connection and, if that fails, attempts to 10 | //! open the alternate connection. You can also create instances of these types manually (i.e. 11 | //! wrapping a default or alternate type directly) if you have platform-specific initialization 12 | //! code. 13 | //! 14 | //! You can "daisy chain" these types to switch between more than two backends. For example, you 15 | //! might use `multi::Device>` for a 16 | //! device that can dynamically switch between Wayland, X11, and OSMesa. 17 | 18 | pub mod connection; 19 | pub mod context; 20 | pub mod device; 21 | pub mod surface; 22 | -------------------------------------------------------------------------------- /src/platform/macos/cgl/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/cgl/connection.rs 2 | // 3 | //! Represents the connection to the Core Graphics window server. 4 | //! 5 | //! Connection types are zero-sized on macOS, because the system APIs automatically manage the 6 | //! global window server connection. 7 | 8 | use super::device::{Adapter, Device}; 9 | use crate::platform::macos::system::connection::Connection as SystemConnection; 10 | use crate::platform::macos::system::device::NativeDevice; 11 | use crate::platform::macos::system::surface::NativeWidget; 12 | use crate::Error; 13 | use crate::GLApi; 14 | 15 | use euclid::default::Size2D; 16 | 17 | use std::os::raw::c_void; 18 | 19 | pub use crate::platform::macos::system::connection::NativeConnection; 20 | 21 | /// A connection to the display server. 22 | #[derive(Clone)] 23 | pub struct Connection(pub SystemConnection); 24 | 25 | impl Connection { 26 | /// Connects to the default display. 27 | #[inline] 28 | pub fn new() -> Result { 29 | SystemConnection::new().map(Connection) 30 | } 31 | 32 | /// An alias for `Connection::new()`, present for consistency with other backends. 33 | #[inline] 34 | pub unsafe fn from_native_connection( 35 | native_connection: NativeConnection, 36 | ) -> Result { 37 | SystemConnection::from_native_connection(native_connection).map(Connection) 38 | } 39 | 40 | /// Returns the underlying native connection. 41 | #[inline] 42 | pub fn native_connection(&self) -> NativeConnection { 43 | self.0.native_connection() 44 | } 45 | 46 | /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES). 47 | #[inline] 48 | pub fn gl_api(&self) -> GLApi { 49 | GLApi::GL 50 | } 51 | 52 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 53 | /// 54 | /// This is an alias for `Connection::create_hardware_adapter()`. 55 | #[inline] 56 | pub fn create_adapter(&self) -> Result { 57 | self.0.create_adapter().map(Adapter) 58 | } 59 | 60 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 61 | #[inline] 62 | pub fn create_hardware_adapter(&self) -> Result { 63 | self.0.create_hardware_adapter().map(Adapter) 64 | } 65 | 66 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 67 | #[inline] 68 | pub fn create_low_power_adapter(&self) -> Result { 69 | self.0.create_low_power_adapter().map(Adapter) 70 | } 71 | 72 | /// Returns the "best" adapter on this system, preferring software adapters. 73 | #[inline] 74 | pub fn create_software_adapter(&self) -> Result { 75 | self.0.create_software_adapter().map(Adapter) 76 | } 77 | 78 | /// Opens the hardware device corresponding to the given adapter. 79 | /// 80 | /// Device handles are local to a single thread. 81 | #[inline] 82 | pub fn create_device(&self, adapter: &Adapter) -> Result { 83 | self.0.create_device(&adapter.0).map(Device) 84 | } 85 | 86 | /// An alias for `connection.create_device()` with the default adapter. 87 | #[inline] 88 | pub unsafe fn create_device_from_native_device( 89 | &self, 90 | native_device: NativeDevice, 91 | ) -> Result { 92 | self.0 93 | .create_device_from_native_device(native_device) 94 | .map(Device) 95 | } 96 | 97 | /// Opens the display connection corresponding to the given `RawDisplayHandle`. 98 | #[cfg(feature = "sm-raw-window-handle-05")] 99 | pub fn from_raw_display_handle( 100 | raw_handle: rwh_05::RawDisplayHandle, 101 | ) -> Result { 102 | SystemConnection::from_raw_display_handle(raw_handle).map(Connection) 103 | } 104 | 105 | /// Opens the display connection corresponding to the given `DisplayHandle`. 106 | #[cfg(feature = "sm-raw-window-handle-06")] 107 | pub fn from_display_handle(handle: rwh_06::DisplayHandle) -> Result { 108 | SystemConnection::from_display_handle(handle).map(Connection) 109 | } 110 | 111 | /// Creates a native widget from a raw pointer 112 | pub unsafe fn create_native_widget_from_ptr( 113 | &self, 114 | raw: *mut c_void, 115 | size: Size2D, 116 | ) -> NativeWidget { 117 | self.0.create_native_widget_from_ptr(raw, size) 118 | } 119 | 120 | /// Create a native widget type from the given `RawWindowHandle`. 121 | #[cfg(feature = "sm-raw-window-handle-05")] 122 | #[inline] 123 | pub fn create_native_widget_from_raw_window_handle( 124 | &self, 125 | raw_handle: rwh_05::RawWindowHandle, 126 | _size: Size2D, 127 | ) -> Result { 128 | use objc2::{MainThreadMarker, Message}; 129 | use objc2_app_kit::{NSView, NSWindow}; 130 | use rwh_05::RawWindowHandle::AppKit; 131 | 132 | match raw_handle { 133 | AppKit(handle) => { 134 | assert!( 135 | MainThreadMarker::new().is_some(), 136 | "NSView is only usable on the main thread" 137 | ); 138 | // SAFETY: The pointer is valid for as long as the handle is, 139 | // and we just checked that we're on the main thread. 140 | let ns_view = unsafe { handle.ns_view.cast::().as_ref().unwrap() }; 141 | let ns_window = unsafe { handle.ns_window.cast::().as_ref().unwrap() }; 142 | 143 | Ok(NativeWidget { 144 | view: ns_view.retain(), 145 | opaque: unsafe { ns_window.isOpaque() }, 146 | }) 147 | } 148 | _ => Err(Error::IncompatibleNativeWidget), 149 | } 150 | } 151 | 152 | /// Create a native widget type from the given `WindowHandle`. 153 | #[cfg(feature = "sm-raw-window-handle-06")] 154 | #[inline] 155 | pub fn create_native_widget_from_window_handle( 156 | &self, 157 | handle: rwh_06::WindowHandle, 158 | _size: Size2D, 159 | ) -> Result { 160 | use objc2::{MainThreadMarker, Message}; 161 | use objc2_app_kit::NSView; 162 | use rwh_06::RawWindowHandle::AppKit; 163 | 164 | match handle.as_raw() { 165 | AppKit(handle) => { 166 | assert!( 167 | MainThreadMarker::new().is_some(), 168 | "NSView is only usable on the main thread" 169 | ); 170 | // SAFETY: The pointer is valid for as long as the handle is, 171 | // and we just checked that we're on the main thread. 172 | let ns_view = unsafe { handle.ns_view.cast::().as_ref() }; 173 | let ns_window = ns_view 174 | .window() 175 | .expect("view must be installed in a window"); 176 | Ok(NativeWidget { 177 | // Extend the lifetime of the view. 178 | view: ns_view.retain(), 179 | opaque: unsafe { ns_window.isOpaque() }, 180 | }) 181 | } 182 | _ => Err(Error::IncompatibleNativeWidget), 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/platform/macos/cgl/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/cgl/device.rs 2 | // 3 | //! A handle to the device. (This is a no-op, because handles are implicit in Apple's Core OpenGL.) 4 | 5 | use super::connection::Connection; 6 | use crate::platform::macos::system::device::{Adapter as SystemAdapter, Device as SystemDevice}; 7 | use crate::GLApi; 8 | 9 | pub use crate::platform::macos::system::device::NativeDevice; 10 | 11 | /// Represents a hardware display adapter that can be used for rendering (including the CPU). 12 | /// 13 | /// Adapters can be sent between threads. To render with an adapter, open a thread-local `Device`. 14 | #[derive(Clone, Debug)] 15 | pub struct Adapter(pub(crate) SystemAdapter); 16 | 17 | /// A thread-local handle to a device. 18 | /// 19 | /// Devices contain most of the relevant surface management methods. 20 | #[derive(Clone)] 21 | pub struct Device(pub(crate) SystemDevice); 22 | 23 | impl Device { 24 | /// Returns the native device corresponding to this device. 25 | #[inline] 26 | pub fn native_device(&self) -> NativeDevice { 27 | self.0.native_device() 28 | } 29 | 30 | /// Returns the display server connection that this device was created with. 31 | #[inline] 32 | pub fn connection(&self) -> Connection { 33 | Connection(self.0.connection()) 34 | } 35 | 36 | /// Returns the adapter that this device was created with. 37 | #[inline] 38 | pub fn adapter(&self) -> Adapter { 39 | Adapter(self.0.adapter()) 40 | } 41 | 42 | /// Returns the OpenGL API flavor that this device supports (OpenGL or OpenGL ES). 43 | #[inline] 44 | pub fn gl_api(&self) -> GLApi { 45 | GLApi::GL 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/platform/macos/cgl/error.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/cgl/error.rs 2 | // 3 | //! Translation of errors from the CGL API to `surfman` errors. 4 | 5 | use crate::WindowingApiError; 6 | use cgl::CGLError; 7 | 8 | pub(crate) trait ToWindowingApiError { 9 | fn to_windowing_api_error(self) -> WindowingApiError; 10 | } 11 | 12 | impl ToWindowingApiError for CGLError { 13 | fn to_windowing_api_error(self) -> WindowingApiError { 14 | match self { 15 | 10000 => WindowingApiError::BadAttribute, 16 | 10001 => WindowingApiError::BadProperty, 17 | 10002 => WindowingApiError::BadPixelFormat, 18 | 10003 => WindowingApiError::BadRendererInfo, 19 | 10004 => WindowingApiError::BadContext, 20 | 10005 => WindowingApiError::BadDrawable, 21 | 10006 => WindowingApiError::BadDisplay, 22 | 10007 => WindowingApiError::BadState, 23 | 10008 => WindowingApiError::BadValue, 24 | 10009 => WindowingApiError::BadMatch, 25 | 10010 => WindowingApiError::BadEnumeration, 26 | 10011 => WindowingApiError::BadOffScreen, 27 | 10012 => WindowingApiError::BadFullScreen, 28 | 10013 => WindowingApiError::BadWindow, 29 | 10014 => WindowingApiError::BadAddress, 30 | 10015 => WindowingApiError::BadCodeModule, 31 | 10016 => WindowingApiError::BadAlloc, 32 | 10017 => WindowingApiError::BadConnection, 33 | _ => WindowingApiError::Failed, 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/platform/macos/cgl/ffi.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/cgl/ffi.rs 2 | // 3 | //! FFI declarations not provided by the upstream `cgl` crate. 4 | 5 | use cgl::CGLContextObj; 6 | 7 | #[link(name = "OpenGL", kind = "framework")] 8 | extern "C" { 9 | pub(crate) fn CGLRetainContext(ctx: CGLContextObj) -> CGLContextObj; 10 | pub(crate) fn CGLReleaseContext(ctx: CGLContextObj); 11 | } 12 | -------------------------------------------------------------------------------- /src/platform/macos/cgl/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/cgl/mod.rs 2 | // 3 | //! Bindings to Apple's OpenGL implementation on macOS. 4 | 5 | pub mod connection; 6 | pub mod context; 7 | pub mod device; 8 | pub mod surface; 9 | 10 | mod error; 11 | mod ffi; 12 | 13 | crate::implement_interfaces!(); 14 | 15 | #[cfg(test)] 16 | #[path = "../../../tests.rs"] 17 | mod tests; 18 | -------------------------------------------------------------------------------- /src/platform/macos/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/mod.rs 2 | // 3 | //! Bindings to Apple's OpenGL implementation on macOS. 4 | 5 | pub mod cgl; 6 | pub mod system; 7 | -------------------------------------------------------------------------------- /src/platform/macos/system/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/system/connection.rs 2 | // 3 | //! Represents the connection to the Core Graphics window server. 4 | //! 5 | //! Connection types are zero-sized on macOS, because the system APIs automatically manage the 6 | //! global window server connection. 7 | 8 | use super::device::{Adapter, Device, NativeDevice}; 9 | use super::surface::NativeWidget; 10 | use crate::Error; 11 | 12 | use objc2::rc::Retained; 13 | use objc2_app_kit::NSView; 14 | use objc2_core_foundation::{CFBoolean, CFBundle, CFMutableDictionary, CFRetained, CFString}; 15 | 16 | use euclid::default::Size2D; 17 | 18 | use std::os::raw::c_void; 19 | 20 | /// A no-op connection. 21 | /// 22 | /// Connections to the CGS window server are implicit on macOS, so this is a zero-sized type. 23 | #[derive(Clone)] 24 | pub struct Connection; 25 | 26 | /// An empty placeholder for native connections. 27 | /// 28 | /// Connections to the CGS window server are implicit on macOS, so this is a zero-sized type. 29 | #[derive(Clone)] 30 | pub struct NativeConnection; 31 | 32 | impl Connection { 33 | /// Connects to the default display. 34 | #[inline] 35 | pub fn new() -> Result { 36 | unsafe { 37 | // Adjust the `NSSupportsAutomaticGraphicsSwitching` key in our `Info.plist` so that we 38 | // can opt into the integrated GPU if available. 39 | let main_bundle = CFBundle::main_bundle().unwrap(); 40 | let bundle_info_dictionary = main_bundle.info_dictionary().unwrap(); 41 | 42 | // This is a total hack, as there's no guarantee `Info.plist` dictionaries are mutable. 43 | let bundle_info_dictionary = 44 | CFRetained::cast_unchecked::(bundle_info_dictionary); 45 | 46 | let supports_automatic_graphics_switching_key = 47 | CFString::from_str("NSSupportsAutomaticGraphicsSwitching"); 48 | let supports_automatic_graphics_switching_value = CFBoolean::new(true); 49 | CFMutableDictionary::set_value( 50 | Some(&bundle_info_dictionary), 51 | &*supports_automatic_graphics_switching_key as *const _ as *const c_void, 52 | &*supports_automatic_graphics_switching_value as *const _ as *const c_void, 53 | ); 54 | } 55 | 56 | Ok(Connection) 57 | } 58 | 59 | /// An alias for `Connection::new()`, present for consistency with other backends. 60 | #[inline] 61 | pub unsafe fn from_native_connection(_: NativeConnection) -> Result { 62 | Connection::new() 63 | } 64 | 65 | /// Returns the underlying native connection. 66 | #[inline] 67 | pub fn native_connection(&self) -> NativeConnection { 68 | NativeConnection 69 | } 70 | 71 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 72 | /// 73 | /// This is an alias for `Connection::create_hardware_adapter()`. 74 | #[inline] 75 | pub fn create_adapter(&self) -> Result { 76 | self.create_hardware_adapter() 77 | } 78 | 79 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 80 | #[inline] 81 | pub fn create_hardware_adapter(&self) -> Result { 82 | Ok(Adapter { 83 | is_low_power: false, 84 | }) 85 | } 86 | 87 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 88 | #[inline] 89 | pub fn create_low_power_adapter(&self) -> Result { 90 | Ok(Adapter { is_low_power: true }) 91 | } 92 | 93 | /// Returns the "best" adapter on this system, preferring software adapters. 94 | #[inline] 95 | pub fn create_software_adapter(&self) -> Result { 96 | self.create_low_power_adapter() 97 | } 98 | 99 | /// Opens the hardware device corresponding to the given adapter. 100 | /// 101 | /// Device handles are local to a single thread. 102 | #[inline] 103 | pub fn create_device(&self, adapter: &Adapter) -> Result { 104 | Device::new((*adapter).clone()) 105 | } 106 | 107 | /// An alias for `connection.create_device()` with the default adapter. 108 | #[inline] 109 | pub unsafe fn create_device_from_native_device( 110 | &self, 111 | _: NativeDevice, 112 | ) -> Result { 113 | self.create_device(&self.create_adapter()?) 114 | } 115 | 116 | /// Opens the display connection corresponding to the given `RawDisplayHandle`. 117 | #[cfg(feature = "sm-raw-window-handle-05")] 118 | pub fn from_raw_display_handle(_: rwh_05::RawDisplayHandle) -> Result { 119 | Connection::new() 120 | } 121 | 122 | /// Opens the display connection corresponding to the given `DisplayHandle`. 123 | #[cfg(feature = "sm-raw-window-handle-06")] 124 | pub fn from_display_handle(_: rwh_06::DisplayHandle) -> Result { 125 | Connection::new() 126 | } 127 | 128 | /// Create a native widget from a raw pointer 129 | pub unsafe fn create_native_widget_from_ptr( 130 | &self, 131 | raw: *mut c_void, 132 | _size: Size2D, 133 | ) -> NativeWidget { 134 | let view_ptr: *mut NSView = raw.cast(); 135 | NativeWidget { 136 | // SAFETY: Validity of the NSView is upheld by caller. 137 | // TODO(madsmtm): We should probably `retain` here, rather than 138 | // take ownership of the pointer. 139 | view: unsafe { Retained::from_raw(view_ptr).unwrap() }, 140 | opaque: true, 141 | } 142 | } 143 | 144 | /// Create a native widget type from the given `RawWindowHandle`. 145 | #[cfg(feature = "sm-raw-window-handle-05")] 146 | #[inline] 147 | pub fn create_native_widget_from_raw_window_handle( 148 | &self, 149 | raw_handle: rwh_05::RawWindowHandle, 150 | _size: Size2D, 151 | ) -> Result { 152 | use objc2::{MainThreadMarker, Message}; 153 | use objc2_app_kit::NSWindow; 154 | use rwh_05::RawWindowHandle::AppKit; 155 | 156 | match raw_handle { 157 | AppKit(handle) => { 158 | assert!( 159 | MainThreadMarker::new().is_some(), 160 | "NSView is only usable on the main thread" 161 | ); 162 | // SAFETY: The pointer is valid for as long as the handle is, 163 | // and we just checked that we're on the main thread. 164 | let ns_view = unsafe { handle.ns_view.cast::().as_ref().unwrap() }; 165 | let ns_window = unsafe { handle.ns_window.cast::().as_ref().unwrap() }; 166 | 167 | Ok(NativeWidget { 168 | view: ns_view.retain(), 169 | opaque: unsafe { ns_window.isOpaque() }, 170 | }) 171 | } 172 | _ => Err(Error::IncompatibleNativeWidget), 173 | } 174 | } 175 | 176 | /// Create a native widget type from the given `WindowHandle`. 177 | #[cfg(feature = "sm-raw-window-handle-06")] 178 | #[inline] 179 | pub fn create_native_widget_from_window_handle( 180 | &self, 181 | handle: rwh_06::WindowHandle, 182 | _size: Size2D, 183 | ) -> Result { 184 | use objc2::{MainThreadMarker, Message}; 185 | use rwh_06::RawWindowHandle::AppKit; 186 | 187 | match handle.as_raw() { 188 | AppKit(handle) => { 189 | assert!( 190 | MainThreadMarker::new().is_some(), 191 | "NSView is only usable on the main thread" 192 | ); 193 | // SAFETY: The pointer is valid for as long as the handle is, 194 | // and we just checked that we're on the main thread. 195 | let ns_view = unsafe { handle.ns_view.cast::().as_ref() }; 196 | let ns_window = ns_view 197 | .window() 198 | .expect("view must be installed in a window"); 199 | Ok(NativeWidget { 200 | // Extend the lifetime of the view. 201 | view: ns_view.retain(), 202 | opaque: unsafe { ns_window.isOpaque() }, 203 | }) 204 | } 205 | _ => Err(Error::IncompatibleNativeWidget), 206 | } 207 | } 208 | } 209 | 210 | impl NativeConnection { 211 | /// Returns the current native connection. 212 | /// 213 | /// This is a no-op on macOS, because Core Graphics window server connections are implicit in 214 | /// the platform APIs. 215 | #[inline] 216 | pub fn current() -> Result { 217 | Ok(NativeConnection) 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/platform/macos/system/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/system/device.rs 2 | // 3 | //! A handle to the device. (This is a no-op, because handles are implicit in `IOSurface`.) 4 | 5 | use super::connection::Connection; 6 | use crate::Error; 7 | 8 | use objc2::{rc::Retained, runtime::ProtocolObject}; 9 | use objc2_metal::{MTLCopyAllDevices, MTLDevice}; 10 | use std::marker::PhantomData; 11 | 12 | /// Represents a hardware display adapter that can be used for rendering (including the CPU). 13 | /// 14 | /// Adapters can be sent between threads. To render with an adapter, open a thread-local `Device`. 15 | #[derive(Clone, Debug)] 16 | pub struct Adapter { 17 | pub(crate) is_low_power: bool, 18 | } 19 | 20 | /// A thread-local handle to a device. 21 | /// 22 | /// Devices contain most of the relevant surface management methods. 23 | #[derive(Clone)] 24 | pub struct Device { 25 | adapter: Adapter, 26 | phantom: PhantomData<*mut ()>, 27 | } 28 | 29 | /// The Metal device corresponding to this device. 30 | #[derive(Clone)] 31 | pub struct NativeDevice(pub Retained>); 32 | 33 | impl Device { 34 | #[inline] 35 | pub(crate) fn new(adapter: Adapter) -> Result { 36 | Ok(Device { 37 | adapter, 38 | phantom: PhantomData, 39 | }) 40 | } 41 | 42 | /// Returns the native device corresponding to this device. 43 | pub fn native_device(&self) -> NativeDevice { 44 | NativeDevice( 45 | MTLCopyAllDevices() 46 | .into_iter() 47 | .find(|device| device.isLowPower() == self.adapter.is_low_power) 48 | .expect("No Metal device found!"), 49 | ) 50 | } 51 | 52 | /// Returns the display server connection that this device was created with. 53 | #[inline] 54 | pub fn connection(&self) -> Connection { 55 | Connection 56 | } 57 | 58 | /// Returns the adapter that this device was created with. 59 | #[inline] 60 | pub fn adapter(&self) -> Adapter { 61 | self.adapter.clone() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/platform/macos/system/ffi.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/system/ffi.rs 2 | // 3 | //! FFI definitions for the macOS backend. 4 | 5 | #![allow(non_upper_case_globals)] 6 | 7 | pub(crate) const kIODefaultCache: i32 = 0; 8 | pub(crate) const kIOWriteCombineCache: i32 = 4; 9 | pub(crate) const kIOMapCacheShift: i32 = 8; 10 | pub(crate) const kIOMapDefaultCache: i32 = kIODefaultCache << kIOMapCacheShift; 11 | pub(crate) const kIOMapWriteCombineCache: i32 = kIOWriteCombineCache << kIOMapCacheShift; 12 | -------------------------------------------------------------------------------- /src/platform/macos/system/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/macos/system/mod.rs 2 | // 3 | //! Bindings to Apple's OpenGL implementation on macOS. 4 | 5 | pub mod connection; 6 | pub mod device; 7 | pub mod surface; 8 | 9 | mod ffi; 10 | -------------------------------------------------------------------------------- /src/platform/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/mod.rs 2 | // 3 | //! Platform-specific backends. 4 | 5 | pub mod generic; 6 | 7 | #[cfg(any(android_platform, ohos_platform))] 8 | pub mod egl; 9 | #[cfg(any(android_platform, ohos_platform))] 10 | pub use egl as default; 11 | 12 | #[cfg(macos_platform)] 13 | pub mod macos; 14 | #[cfg(macos_platform)] 15 | pub use macos::cgl as default; 16 | #[cfg(macos_platform)] 17 | pub use macos::system; 18 | 19 | #[cfg(free_unix)] 20 | pub mod unix; 21 | #[cfg(free_unix)] 22 | pub use unix::default; 23 | 24 | #[cfg(windows_platform)] 25 | pub mod windows; 26 | #[cfg(angle_default)] 27 | pub use windows::angle as default; 28 | #[cfg(all(windows_platform, not(angle_default)))] 29 | pub use windows::wgl as default; 30 | -------------------------------------------------------------------------------- /src/platform/unix/default.rs: -------------------------------------------------------------------------------- 1 | // surfman/src/platform/unix/default.rs 2 | // 3 | //! The default backend for Unix, which dynamically switches between Wayland, X11 and surfaceless. 4 | 5 | /// Wayland or X11 display server connections. 6 | pub mod connection { 7 | use crate::platform::generic::multi::connection::Connection as MultiConnection; 8 | use crate::platform::generic::multi::connection::NativeConnection as MultiNativeConnection; 9 | use crate::platform::generic::multi::device::Device as MultiDevice; 10 | use crate::platform::unix::generic::device::Device as SWDevice; 11 | use crate::platform::unix::wayland::device::Device as WaylandDevice; 12 | use crate::platform::unix::x11::device::Device as X11Device; 13 | type HWDevice = MultiDevice; 14 | 15 | /// Either a Wayland or an X11 display server connection. 16 | pub type Connection = MultiConnection; 17 | 18 | /// Either a Wayland or an X11 native connection 19 | pub type NativeConnection = MultiNativeConnection; 20 | } 21 | 22 | /// OpenGL rendering contexts. 23 | pub mod context { 24 | use crate::platform::generic::multi::context::Context as MultiContext; 25 | use crate::platform::generic::multi::context::ContextDescriptor as MultiContextDescriptor; 26 | use crate::platform::generic::multi::context::NativeContext as MultiNativeContext; 27 | use crate::platform::generic::multi::device::Device as MultiDevice; 28 | use crate::platform::unix::generic::device::Device as SWDevice; 29 | use crate::platform::unix::wayland::device::Device as WaylandDevice; 30 | use crate::platform::unix::x11::device::Device as X11Device; 31 | type HWDevice = MultiDevice; 32 | 33 | /// Represents an OpenGL rendering context. 34 | /// 35 | /// A context allows you to issue rendering commands to a surface. When initially created, a 36 | /// context has no attached surface, so rendering commands will fail or be ignored. Typically, 37 | /// you attach a surface to the context before rendering. 38 | /// 39 | /// Contexts take ownership of the surfaces attached to them. In order to mutate a surface in 40 | /// any way other than rendering to it (e.g. presenting it to a window, which causes a buffer 41 | /// swap), it must first be detached from its context. Each surface is associated with a single 42 | /// context upon creation and may not be rendered to from any other context. However, you can 43 | /// wrap a surface in a surface texture, which allows the surface to be read from another 44 | /// context. 45 | /// 46 | /// OpenGL objects may not be shared across contexts directly, but surface textures effectively 47 | /// allow for sharing of texture data. Contexts are local to a single thread and device. 48 | /// 49 | /// A context must be explicitly destroyed with `destroy_context()`, or a panic will occur. 50 | pub type Context = MultiContext; 51 | 52 | /// Information needed to create a context. Some APIs call this a "config" or a "pixel format". 53 | /// 54 | /// These are local to a device. 55 | pub type ContextDescriptor = MultiContextDescriptor; 56 | 57 | /// Either a Wayland or an X11 native context 58 | pub type NativeContext = MultiNativeContext; 59 | } 60 | 61 | /// Thread-local handles to devices. 62 | pub mod device { 63 | use crate::platform::generic::multi::device::Adapter as MultiAdapter; 64 | use crate::platform::generic::multi::device::NativeDevice as MultiNativeDevice; 65 | use crate::platform::unix::generic::device::Device as SWDevice; 66 | use crate::platform::unix::wayland::device::Device as WaylandDevice; 67 | use crate::platform::unix::x11::device::Device as X11Device; 68 | 69 | use crate::platform::generic::multi::device::Device as MultiDevice; 70 | type HWDevice = MultiDevice; 71 | 72 | /// Represents a hardware display adapter that can be used for rendering (including the CPU). 73 | /// 74 | /// Adapters can be sent between threads. To render with an adapter, open a thread-local 75 | /// `Device`. 76 | pub type Adapter = MultiAdapter; 77 | 78 | /// A thread-local handle to a device. 79 | /// 80 | /// Devices contain most of the relevant surface management methods. 81 | pub type Device = MultiDevice; 82 | 83 | /// Either a Wayland or an X11 native device 84 | pub type NativeDevice = MultiNativeDevice; 85 | } 86 | 87 | /// Hardware buffers of pixels. 88 | pub mod surface { 89 | use crate::platform::generic::multi::device::Device as MultiDevice; 90 | use crate::platform::generic::multi::surface::NativeWidget as MultiNativeWidget; 91 | use crate::platform::generic::multi::surface::Surface as MultiSurface; 92 | use crate::platform::generic::multi::surface::SurfaceTexture as MultiSurfaceTexture; 93 | use crate::platform::unix::generic::device::Device as SWDevice; 94 | use crate::platform::unix::wayland::device::Device as WaylandDevice; 95 | use crate::platform::unix::x11::device::Device as X11Device; 96 | type HWDevice = MultiDevice; 97 | 98 | /// A wrapper for a Wayland surface or an X11 `Window`, as appropriate. 99 | pub type NativeWidget = MultiNativeWidget; 100 | 101 | /// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and 102 | /// either displayed in a native widget or bound to a texture for reading. 103 | /// 104 | /// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound 105 | /// to a texture but cannot be displayed in a widget (without using other APIs such as Core 106 | /// Animation, DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be 107 | /// displayed in a widget but not bound to a texture. 108 | /// 109 | /// Surfaces are specific to a given context and cannot be rendered to from any context other 110 | /// than the one they were created with. However, they can be *read* from any context on any 111 | /// thread (as long as that context shares the same adapter and connection), by wrapping them 112 | /// in a `SurfaceTexture`. 113 | /// 114 | /// Depending on the platform, each surface may be internally double-buffered. 115 | /// 116 | /// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. 117 | pub type Surface = MultiSurface; 118 | 119 | /// Represents an OpenGL texture that wraps a surface. 120 | /// 121 | /// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior 122 | /// to write to such a texture (e.g. by binding it to a framebuffer and rendering to that 123 | /// framebuffer). 124 | /// 125 | /// Surface textures are local to a context, but that context does not have to be the same 126 | /// context as that associated with the underlying surface. The texture must be destroyed with 127 | /// the `destroy_surface_texture()` method, or a panic will occur. 128 | pub type SurfaceTexture = MultiSurfaceTexture; 129 | 130 | // FIXME(pcwalton): Revamp how this works. 131 | #[doc(hidden)] 132 | pub struct SurfaceDataGuard {} 133 | } 134 | -------------------------------------------------------------------------------- /src/platform/unix/generic/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/generic/connection.rs 2 | // 3 | //! Represents a connection to a display server. 4 | 5 | use super::device::{Adapter, Device, NativeDevice}; 6 | use super::surface::NativeWidget; 7 | use crate::egl; 8 | use crate::egl::types::{EGLAttrib, EGLDisplay}; 9 | use crate::info::GLApi; 10 | use crate::platform::generic::egl::device::EGL_FUNCTIONS; 11 | use crate::platform::generic::egl::ffi::EGL_PLATFORM_SURFACELESS_MESA; 12 | use crate::Error; 13 | 14 | use euclid::default::Size2D; 15 | 16 | use std::os::raw::c_void; 17 | use std::sync::Arc; 18 | 19 | /// A no-op connection. 20 | #[derive(Clone)] 21 | pub struct Connection { 22 | pub(crate) native_connection: Arc, 23 | } 24 | 25 | /// Native connections. 26 | #[derive(Clone)] 27 | pub struct NativeConnection(Arc); 28 | 29 | /// Native connections. 30 | pub struct NativeConnectionWrapper { 31 | pub(crate) egl_display: EGLDisplay, 32 | } 33 | 34 | unsafe impl Send for NativeConnectionWrapper {} 35 | unsafe impl Sync for NativeConnectionWrapper {} 36 | 37 | impl Connection { 38 | /// Opens a surfaceless Mesa display. 39 | #[inline] 40 | pub fn new() -> Result { 41 | unsafe { 42 | EGL_FUNCTIONS.with(|egl| { 43 | let egl_display_attributes = [egl::NONE as EGLAttrib]; 44 | let egl_display = egl.GetPlatformDisplay( 45 | EGL_PLATFORM_SURFACELESS_MESA, 46 | egl::DEFAULT_DISPLAY as *mut c_void, 47 | egl_display_attributes.as_ptr(), 48 | ); 49 | if egl_display == egl::NO_DISPLAY { 50 | return Err(Error::ConnectionFailed); 51 | } 52 | 53 | let (mut egl_major_version, mut egl_minor_version) = (0, 0); 54 | let ok = 55 | egl.Initialize(egl_display, &mut egl_major_version, &mut egl_minor_version); 56 | if ok == egl::FALSE { 57 | return Err(Error::ConnectionFailed); 58 | } 59 | 60 | let native_connection = 61 | NativeConnection(Arc::new(NativeConnectionWrapper { egl_display })); 62 | 63 | Connection::from_native_connection(native_connection) 64 | }) 65 | } 66 | } 67 | 68 | /// An alias for `Connection::new()`, present for consistency with other backends. 69 | #[inline] 70 | pub unsafe fn from_native_connection( 71 | native_connection: NativeConnection, 72 | ) -> Result { 73 | Ok(Connection { 74 | native_connection: native_connection.0, 75 | }) 76 | } 77 | 78 | /// Returns the underlying native connection. 79 | #[inline] 80 | pub fn native_connection(&self) -> NativeConnection { 81 | NativeConnection(self.native_connection.clone()) 82 | } 83 | 84 | /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES). 85 | #[inline] 86 | pub fn gl_api(&self) -> GLApi { 87 | GLApi::GL 88 | } 89 | 90 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 91 | /// 92 | /// This is an alias for `Connection::create_hardware_adapter()`. 93 | #[inline] 94 | pub fn create_adapter(&self) -> Result { 95 | self.create_hardware_adapter() 96 | } 97 | 98 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 99 | /// 100 | /// On the OSMesa backend, this returns a software adapter. 101 | #[inline] 102 | pub fn create_hardware_adapter(&self) -> Result { 103 | Ok(Adapter::hardware()) 104 | } 105 | 106 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 107 | /// 108 | /// On the OSMesa backend, this returns a software adapter. 109 | #[inline] 110 | pub fn create_low_power_adapter(&self) -> Result { 111 | Ok(Adapter::low_power()) 112 | } 113 | 114 | /// Returns the "best" adapter on this system, preferring software adapters. 115 | #[inline] 116 | pub fn create_software_adapter(&self) -> Result { 117 | Ok(Adapter::software()) 118 | } 119 | 120 | /// Opens the hardware device corresponding to the given adapter. 121 | /// 122 | /// Device handles are local to a single thread. 123 | #[inline] 124 | pub fn create_device(&self, adapter: &Adapter) -> Result { 125 | Device::new(self, adapter) 126 | } 127 | 128 | /// An alias for `connection.create_device()` with the default adapter. 129 | #[inline] 130 | pub unsafe fn create_device_from_native_device( 131 | &self, 132 | _: NativeDevice, 133 | ) -> Result { 134 | Device::new(self, &self.create_adapter()?) 135 | } 136 | 137 | /// Opens the display connection corresponding to the given `RawDisplayHandle`. 138 | #[cfg(feature = "sm-raw-window-handle-05")] 139 | pub fn from_raw_display_handle(_: rwh_05::RawDisplayHandle) -> Result { 140 | Err(Error::IncompatibleNativeWidget) 141 | } 142 | 143 | /// Opens the display connection corresponding to the given `DisplayHandle`. 144 | #[cfg(feature = "sm-raw-window-handle-06")] 145 | pub fn from_display_handle(_: rwh_06::DisplayHandle) -> Result { 146 | Err(Error::IncompatibleNativeWidget) 147 | } 148 | 149 | /// Create a native widget from a raw pointer 150 | pub unsafe fn create_native_widget_from_ptr( 151 | &self, 152 | _raw: *mut c_void, 153 | _size: Size2D, 154 | ) -> NativeWidget { 155 | NativeWidget 156 | } 157 | 158 | /// Create a native widget type from the given `RawWindowHandle`. 159 | #[cfg(feature = "sm-raw-window-handle-05")] 160 | #[inline] 161 | pub fn create_native_widget_from_raw_window_handle( 162 | &self, 163 | _: rwh_05::RawWindowHandle, 164 | _size: Size2D, 165 | ) -> Result { 166 | Err(Error::IncompatibleNativeWidget) 167 | } 168 | 169 | /// Create a native widget type from the given `WindowHandle`. 170 | #[cfg(feature = "sm-raw-window-handle-06")] 171 | #[inline] 172 | pub fn create_native_widget_from_window_handle( 173 | &self, 174 | _: rwh_06::WindowHandle, 175 | _size: Size2D, 176 | ) -> Result { 177 | Err(Error::IncompatibleNativeWidget) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/platform/unix/generic/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/generic/device.rs 2 | // 3 | //! A wrapper around surfaceless Mesa `EGLDisplay`s. 4 | 5 | use super::connection::{Connection, NativeConnectionWrapper}; 6 | use crate::{Error, GLApi}; 7 | 8 | use std::env; 9 | use std::sync::Arc; 10 | 11 | static MESA_SOFTWARE_RENDERING_ENV_VAR: &str = "LIBGL_ALWAYS_SOFTWARE"; 12 | static MESA_DRI_PRIME_ENV_VAR: &str = "DRI_PRIME"; 13 | 14 | /// Represents a hardware display adapter that can be used for rendering (including the CPU). 15 | /// 16 | /// Adapters can be sent between threads. To render with an adapter, open a thread-local `Device`. 17 | #[derive(Clone, Debug)] 18 | pub enum Adapter { 19 | #[doc(hidden)] 20 | Hardware, 21 | #[doc(hidden)] 22 | HardwarePrime, 23 | #[doc(hidden)] 24 | Software, 25 | } 26 | 27 | impl Adapter { 28 | #[inline] 29 | pub(crate) fn hardware() -> Adapter { 30 | Adapter::HardwarePrime 31 | } 32 | 33 | #[inline] 34 | pub(crate) fn low_power() -> Adapter { 35 | Adapter::Hardware 36 | } 37 | 38 | #[inline] 39 | pub(crate) fn software() -> Adapter { 40 | Adapter::Software 41 | } 42 | 43 | pub(crate) fn set_environment_variables(&self) { 44 | match *self { 45 | Adapter::Hardware | Adapter::HardwarePrime => { 46 | env::remove_var(MESA_SOFTWARE_RENDERING_ENV_VAR); 47 | } 48 | Adapter::Software => { 49 | env::set_var(MESA_SOFTWARE_RENDERING_ENV_VAR, "1"); 50 | } 51 | } 52 | 53 | match *self { 54 | Adapter::Software => {} 55 | Adapter::Hardware => { 56 | env::remove_var(MESA_DRI_PRIME_ENV_VAR); 57 | } 58 | Adapter::HardwarePrime => { 59 | env::set_var(MESA_DRI_PRIME_ENV_VAR, "1"); 60 | } 61 | } 62 | } 63 | } 64 | 65 | /// A thread-local handle to a device. 66 | /// 67 | /// Devices contain most of the relevant surface management methods. 68 | pub struct Device { 69 | pub(crate) native_connection: Arc, 70 | pub(crate) adapter: Adapter, 71 | } 72 | 73 | /// Wraps an adapter. 74 | /// 75 | /// On Wayland, devices and adapters are essentially identical types. 76 | #[derive(Clone)] 77 | pub struct NativeDevice { 78 | /// The hardware adapter corresponding to this device. 79 | pub adapter: Adapter, 80 | } 81 | 82 | impl Device { 83 | #[inline] 84 | pub(crate) fn new(connection: &Connection, adapter: &Adapter) -> Result { 85 | Ok(Device { 86 | native_connection: connection.native_connection.clone(), 87 | adapter: (*adapter).clone(), 88 | }) 89 | } 90 | 91 | /// Returns the native device corresponding to this device. 92 | /// 93 | /// This method is essentially an alias for the `adapter()` method on Mesa, since there is 94 | /// no explicit concept of a device on this backend. 95 | #[inline] 96 | pub fn native_device(&self) -> NativeDevice { 97 | NativeDevice { 98 | adapter: self.adapter(), 99 | } 100 | } 101 | 102 | /// Returns the display server connection that this device was created with. 103 | #[inline] 104 | pub fn connection(&self) -> Connection { 105 | Connection { 106 | native_connection: self.native_connection.clone(), 107 | } 108 | } 109 | 110 | /// Returns the adapter that this device was created with. 111 | #[inline] 112 | pub fn adapter(&self) -> Adapter { 113 | self.adapter.clone() 114 | } 115 | 116 | /// Returns the OpenGL API flavor that this device supports (OpenGL or OpenGL ES). 117 | #[inline] 118 | pub fn gl_api(&self) -> GLApi { 119 | GLApi::GL 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/platform/unix/generic/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/generic/mod.rs 2 | // 3 | //! The Mesa "surfaceless" backend, which only supports off-screen surfaces and cannot directly 4 | //! display surfaces on a screen. 5 | 6 | pub mod connection; 7 | pub mod context; 8 | pub mod device; 9 | pub mod surface; 10 | 11 | crate::implement_interfaces!(); 12 | 13 | #[cfg(test)] 14 | #[path = "../../../tests.rs"] 15 | mod tests; 16 | -------------------------------------------------------------------------------- /src/platform/unix/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/mod.rs 2 | // 3 | //! Backends specific to Unix-like systems, particularly Linux. 4 | 5 | // The default when x11 is enabled, and wayland default is not explicitly selected. 6 | #[cfg(all(x11_platform, not(wayland_default)))] 7 | pub mod default; 8 | 9 | #[cfg(wayland_default)] 10 | pub use wayland as default; 11 | 12 | #[cfg(free_unix)] 13 | pub mod generic; 14 | 15 | #[cfg(wayland_platform)] 16 | pub mod wayland; 17 | #[cfg(x11_platform)] 18 | pub mod x11; 19 | -------------------------------------------------------------------------------- /src/platform/unix/wayland/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/wayland/device.rs 2 | // 3 | //! A wrapper around Wayland `EGLDisplay`s. 4 | 5 | use super::connection::{Connection, NativeConnectionWrapper}; 6 | use crate::{Error, GLApi}; 7 | 8 | use std::sync::Arc; 9 | 10 | pub use crate::platform::unix::generic::device::Adapter; 11 | 12 | /// A thread-local handle to a device. 13 | /// 14 | /// Devices contain most of the relevant surface management methods. 15 | pub struct Device { 16 | pub(crate) native_connection: Arc, 17 | pub(crate) adapter: Adapter, 18 | } 19 | 20 | /// Wraps an adapter. 21 | /// 22 | /// On Wayland, devices and adapters are essentially identical types. 23 | #[derive(Clone)] 24 | pub struct NativeDevice { 25 | /// The hardware adapter corresponding to this device. 26 | pub adapter: Adapter, 27 | } 28 | 29 | impl Device { 30 | #[inline] 31 | pub(crate) fn new(connection: &Connection, adapter: &Adapter) -> Result { 32 | Ok(Device { 33 | native_connection: connection.native_connection.clone(), 34 | adapter: (*adapter).clone(), 35 | }) 36 | } 37 | 38 | /// Returns the native device corresponding to this device. 39 | /// 40 | /// This method is essentially an alias for the `adapter()` method on Wayland, since there is 41 | /// no explicit concept of a device on this backend. 42 | #[inline] 43 | pub fn native_device(&self) -> NativeDevice { 44 | NativeDevice { 45 | adapter: self.adapter(), 46 | } 47 | } 48 | 49 | /// Returns the display server connection that this device was created with. 50 | #[inline] 51 | pub fn connection(&self) -> Connection { 52 | Connection { 53 | native_connection: self.native_connection.clone(), 54 | } 55 | } 56 | 57 | /// Returns the adapter that this device was created with. 58 | #[inline] 59 | pub fn adapter(&self) -> Adapter { 60 | self.adapter.clone() 61 | } 62 | 63 | /// Returns the OpenGL API flavor that this device supports (OpenGL or OpenGL ES). 64 | #[inline] 65 | pub fn gl_api(&self) -> GLApi { 66 | if std::env::var("SURFMAN_FORCE_GLES").is_ok() { 67 | GLApi::GLES 68 | } else { 69 | GLApi::GL 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/platform/unix/wayland/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/wayland/mod.rs 2 | // 3 | //! Bindings to Wayland via the Linux GBM interface. 4 | 5 | pub mod connection; 6 | pub mod context; 7 | pub mod device; 8 | pub mod surface; 9 | 10 | crate::implement_interfaces!(); 11 | 12 | #[cfg(test)] 13 | #[path = "../../../tests.rs"] 14 | mod tests; 15 | -------------------------------------------------------------------------------- /src/platform/unix/x11/device.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/x11/device.rs 2 | // 3 | //! A wrapper around X11 `EGLDisplay`s. 4 | 5 | use super::connection::{Connection, NativeConnectionWrapper}; 6 | use crate::{Error, GLApi}; 7 | 8 | use std::sync::Arc; 9 | 10 | pub use crate::platform::unix::generic::device::Adapter; 11 | 12 | /// A thread-local handle to a device. 13 | /// 14 | /// Devices contain most of the relevant surface management methods. 15 | pub struct Device { 16 | pub(crate) native_connection: Arc, 17 | pub(crate) adapter: Adapter, 18 | } 19 | 20 | /// Wraps an adapter. 21 | /// 22 | /// On X11, devices and adapters are essentially identical types. 23 | #[derive(Clone)] 24 | pub struct NativeDevice { 25 | /// The hardware adapter corresponding to this device. 26 | pub adapter: Adapter, 27 | } 28 | 29 | impl Device { 30 | #[inline] 31 | pub(crate) fn new(connection: &Connection, adapter: &Adapter) -> Result { 32 | Ok(Device { 33 | native_connection: connection.native_connection.clone(), 34 | adapter: (*adapter).clone(), 35 | }) 36 | } 37 | 38 | /// Returns the native device corresponding to this device. 39 | /// 40 | /// This method is essentially an alias for the `adapter()` method on Wayland, since there is 41 | /// no explicit concept of a device on this backend. 42 | #[inline] 43 | pub fn native_device(&self) -> NativeDevice { 44 | NativeDevice { 45 | adapter: self.adapter(), 46 | } 47 | } 48 | 49 | /// Returns the display server connection that this device was created with. 50 | #[inline] 51 | pub fn connection(&self) -> Connection { 52 | Connection { 53 | native_connection: self.native_connection.clone(), 54 | } 55 | } 56 | 57 | /// Returns the adapter that this device was created with. 58 | #[inline] 59 | pub fn adapter(&self) -> Adapter { 60 | self.adapter.clone() 61 | } 62 | 63 | /// Returns the OpenGL API flavor that this device supports (OpenGL or OpenGL ES). 64 | #[inline] 65 | pub fn gl_api(&self) -> GLApi { 66 | GLApi::GL 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/platform/unix/x11/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/unix/x11/mod.rs 2 | // 3 | //! Bindings to EGL via Xlib. 4 | 5 | pub mod connection; 6 | pub mod context; 7 | pub mod device; 8 | pub mod surface; 9 | 10 | crate::implement_interfaces!(); 11 | 12 | #[cfg(test)] 13 | #[path = "../../../tests.rs"] 14 | mod tests; 15 | -------------------------------------------------------------------------------- /src/platform/windows/angle/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/windows/angle/connection.rs 2 | // 3 | //! A connection to the window server. 4 | //! 5 | //! It might seem like this should wrap an `EGLDisplay`, but it doesn't. Unfortunately, in the 6 | //! ANGLE implementation `EGLDisplay` is not thread-safe, while `surfman` connections must be 7 | //! thread-safe. So we need to use the DXGI/Direct3D concept of a connection instead. These are 8 | //! implicit in the Win32 API, and as such this type is a no-op. 9 | 10 | use super::device::{Adapter, Device, NativeDevice, VendorPreference}; 11 | use super::surface::NativeWidget; 12 | use crate::egl::types::{EGLDisplay, EGLNativeWindowType}; 13 | use crate::Error; 14 | use crate::GLApi; 15 | 16 | use euclid::default::Size2D; 17 | 18 | use std::os::raw::c_void; 19 | 20 | use winapi::shared::minwindef::UINT; 21 | use winapi::um::d3dcommon::{D3D_DRIVER_TYPE_UNKNOWN, D3D_DRIVER_TYPE_WARP}; 22 | 23 | const INTEL_PCI_ID: UINT = 0x8086; 24 | 25 | /// A no-op connection. 26 | /// 27 | /// It might seem like this should wrap an `EGLDisplay`, but it doesn't. Unfortunately, in the 28 | /// ANGLE implementation `EGLDisplay` is not thread-safe, while `surfman` connections must be 29 | /// thread-safe. So we need to use the DXGI/Direct3D concept of a connection instead. These are 30 | /// implicit in the Win32 API, and as such this type is a no-op. 31 | #[derive(Clone)] 32 | pub struct Connection; 33 | 34 | /// An empty placeholder for native connections. 35 | /// 36 | /// It might seem like this should wrap an `EGLDisplay`, but it doesn't. Unfortunately, in the 37 | /// ANGLE implementation `EGLDisplay` is not thread-safe, while `surfman` connections must be 38 | /// thread-safe. So we need to use the DXGI/Direct3D concept of a connection instead. These are 39 | /// implicit in the Win32 API, and as such this type is a no-op. 40 | #[derive(Clone)] 41 | pub struct NativeConnection; 42 | 43 | impl Connection { 44 | /// Connects to the default display. 45 | #[inline] 46 | pub fn new() -> Result { 47 | Ok(Connection) 48 | } 49 | 50 | /// An alias for `Connection::new()`, present for consistency with other backends. 51 | #[inline] 52 | pub unsafe fn from_native_connection(_: NativeConnection) -> Result { 53 | Connection::new() 54 | } 55 | 56 | /// Returns the underlying native connection. 57 | #[inline] 58 | pub fn native_connection(&self) -> NativeConnection { 59 | NativeConnection 60 | } 61 | 62 | /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES). 63 | #[inline] 64 | pub fn gl_api(&self) -> GLApi { 65 | GLApi::GLES 66 | } 67 | 68 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 69 | /// 70 | /// This is an alias for `Connection::create_hardware_adapter()`. 71 | #[inline] 72 | pub fn create_adapter(&self) -> Result { 73 | self.create_hardware_adapter() 74 | } 75 | 76 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 77 | #[inline] 78 | pub fn create_hardware_adapter(&self) -> Result { 79 | Adapter::new( 80 | D3D_DRIVER_TYPE_UNKNOWN, 81 | VendorPreference::Avoid(INTEL_PCI_ID), 82 | ) 83 | } 84 | 85 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 86 | #[inline] 87 | pub fn create_low_power_adapter(&self) -> Result { 88 | Adapter::new( 89 | D3D_DRIVER_TYPE_UNKNOWN, 90 | VendorPreference::Prefer(INTEL_PCI_ID), 91 | ) 92 | } 93 | 94 | /// Returns the "best" adapter on this system, preferring software adapters. 95 | #[inline] 96 | pub fn create_software_adapter(&self) -> Result { 97 | Adapter::new(D3D_DRIVER_TYPE_WARP, VendorPreference::None) 98 | } 99 | 100 | /// Opens the hardware device corresponding to the given adapter. 101 | /// 102 | /// Device handles are local to a single thread. 103 | #[inline] 104 | pub fn create_device(&self, adapter: &Adapter) -> Result { 105 | Device::new(adapter) 106 | } 107 | 108 | /// Wraps a `NativeDevice` in a `Device` and returns it. 109 | #[inline] 110 | pub unsafe fn create_device_from_native_device( 111 | &self, 112 | native_device: NativeDevice, 113 | ) -> Result { 114 | Device::from_native_device(native_device) 115 | } 116 | 117 | /// Wraps an ANGLE `EGLDisplay`, along with the associated Direct3D device, in a `Device` and 118 | /// returns it. 119 | /// 120 | /// The underlying `EGLDisplay` is not retained, as there is no way to do this in the EGL API. 121 | /// Therefore, it is the caller's responsibility to keep it alive as long as this `Device` 122 | /// remains alive. This function does, however, call `AddRef` on the Direct3D device. 123 | #[inline] 124 | pub unsafe fn create_device_from_egl_display( 125 | &self, 126 | egl_display: EGLDisplay, 127 | ) -> Result { 128 | Device::from_egl_display(egl_display) 129 | } 130 | 131 | /// Opens the display connection corresponding to the given `RawDisplayHandle`. 132 | #[cfg(feature = "sm-raw-window-handle-05")] 133 | pub fn from_raw_display_handle(_: rwh_05::RawDisplayHandle) -> Result { 134 | Connection::new() 135 | } 136 | 137 | /// Opens the display connection corresponding to the given `DisplayHandle`. 138 | #[cfg(feature = "sm-raw-window-handle-06")] 139 | pub fn from_display_handle(_: rwh_06::DisplayHandle) -> Result { 140 | Connection::new() 141 | } 142 | 143 | /// Create a native widget from a raw pointer 144 | pub unsafe fn create_native_widget_from_ptr( 145 | &self, 146 | raw: *mut c_void, 147 | _size: Size2D, 148 | ) -> NativeWidget { 149 | NativeWidget { 150 | egl_native_window: raw as EGLNativeWindowType, 151 | } 152 | } 153 | 154 | /// Create a native widget type from the given `RawWindowHandle`. 155 | #[cfg(feature = "sm-raw-window-handle-05")] 156 | #[inline] 157 | pub fn create_native_widget_from_raw_window_handle( 158 | &self, 159 | handle: rwh_05::RawWindowHandle, 160 | _size: Size2D, 161 | ) -> Result { 162 | if let rwh_05::RawWindowHandle::Win32(handle) = handle { 163 | Ok(NativeWidget { 164 | egl_native_window: handle.hwnd as EGLNativeWindowType, 165 | }) 166 | } else { 167 | Err(Error::IncompatibleNativeWidget) 168 | } 169 | } 170 | 171 | /// Create a native widget type from the given `WindowHandle`. 172 | #[cfg(feature = "sm-raw-window-handle-06")] 173 | #[inline] 174 | pub fn create_native_widget_from_window_handle( 175 | &self, 176 | handle: rwh_06::WindowHandle, 177 | _size: Size2D, 178 | ) -> Result { 179 | if let rwh_06::RawWindowHandle::Win32(handle) = handle.as_raw() { 180 | Ok(NativeWidget { 181 | egl_native_window: handle.hwnd.get() as EGLNativeWindowType, 182 | }) 183 | } else { 184 | Err(Error::IncompatibleNativeWidget) 185 | } 186 | } 187 | } 188 | 189 | impl NativeConnection { 190 | /// Creates a native connection. 191 | /// 192 | /// This is a no-op method present for consistency with other backends. 193 | #[inline] 194 | pub fn new() -> NativeConnection { 195 | NativeConnection 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/platform/windows/angle/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/windows/angle/mod.rs 2 | // 3 | //! Bindings to Direct3D 11 via the ANGLE OpenGL-to-Direct3D translation layer on Windows. 4 | 5 | pub mod connection; 6 | pub mod context; 7 | pub mod device; 8 | pub mod surface; 9 | 10 | crate::implement_interfaces!(); 11 | 12 | #[cfg(test)] 13 | #[path = "../../../tests.rs"] 14 | mod tests; 15 | -------------------------------------------------------------------------------- /src/platform/windows/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/windows/mod.rs 2 | // 3 | //! Windows support, either via the native WGL interface or Google's ANGLE library. 4 | 5 | #[cfg(feature = "sm-angle")] 6 | pub mod angle; 7 | 8 | #[cfg(not(feature = "sm-no-wgl"))] 9 | pub mod wgl; 10 | -------------------------------------------------------------------------------- /src/platform/windows/wgl/connection.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/platform/windows/wgl/connection.rs 2 | // 3 | //! A connection to the window server. 4 | //! 5 | //! Window server connections are implicit in the Win32 API, so this is a zero-sized type. 6 | 7 | use super::device::{Adapter, Device, NativeDevice}; 8 | use super::surface::NativeWidget; 9 | use crate::Error; 10 | use crate::GLApi; 11 | 12 | use euclid::default::Size2D; 13 | 14 | use std::os::raw::c_void; 15 | 16 | use winapi::shared::windef::HWND; 17 | 18 | /// Represents a connection to the display server. 19 | /// 20 | /// Window server connections are implicit in the Win32 API, so this is a zero-sized type. 21 | #[derive(Clone)] 22 | pub struct Connection; 23 | 24 | /// An empty placeholder for native connections. 25 | /// 26 | /// Window server connections are implicit in the Win32 API, so this is a zero-sized type. 27 | #[derive(Clone)] 28 | pub struct NativeConnection; 29 | 30 | impl Connection { 31 | /// Connects to the default display. 32 | #[inline] 33 | pub fn new() -> Result { 34 | Ok(Connection) 35 | } 36 | 37 | /// An alias for `Connection::new()`, present for consistency with other backends. 38 | #[inline] 39 | pub unsafe fn from_native_connection(_: NativeConnection) -> Result { 40 | Connection::new() 41 | } 42 | 43 | /// Returns the underlying native connection. 44 | #[inline] 45 | pub fn native_connection(&self) -> NativeConnection { 46 | NativeConnection 47 | } 48 | 49 | /// Returns the OpenGL API flavor that this connection supports (OpenGL or OpenGL ES). 50 | #[inline] 51 | pub fn gl_api(&self) -> GLApi { 52 | GLApi::GL 53 | } 54 | 55 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 56 | /// 57 | /// This is an alias for `Connection::create_hardware_adapter()`. 58 | #[inline] 59 | pub fn create_adapter(&self) -> Result { 60 | self.create_hardware_adapter() 61 | } 62 | 63 | /// Returns the "best" adapter on this system, preferring high-performance hardware adapters. 64 | #[inline] 65 | pub fn create_hardware_adapter(&self) -> Result { 66 | Ok(Adapter::HighPerformance) 67 | } 68 | 69 | /// Returns the "best" adapter on this system, preferring low-power hardware adapters. 70 | #[inline] 71 | pub fn create_low_power_adapter(&self) -> Result { 72 | Ok(Adapter::LowPower) 73 | } 74 | 75 | /// Returns the "best" adapter on this system, preferring software adapters. 76 | #[inline] 77 | pub fn create_software_adapter(&self) -> Result { 78 | self.create_low_power_adapter() 79 | } 80 | 81 | /// Opens a device. 82 | #[inline] 83 | pub fn create_device(&self, adapter: &Adapter) -> Result { 84 | Device::new(adapter) 85 | } 86 | 87 | /// Creates a `Device` from a Direct3D 11 device and associated GL/DX interop handle. 88 | /// 89 | /// The handle can be created by calling `wglDXOpenDeviceNV` from the `WGL_NV_DX_interop` 90 | /// extension. 91 | /// 92 | /// This method increases the reference count on the Direct3D 11 device and takes ownership of 93 | /// the GL/DX interop handle. 94 | #[inline] 95 | pub unsafe fn create_device_from_native_device( 96 | &self, 97 | native_device: NativeDevice, 98 | ) -> Result { 99 | Device::from_native_device(native_device) 100 | } 101 | 102 | /// Opens the display connection corresponding to the given `RawDisplayHandle`. 103 | #[cfg(feature = "sm-raw-window-handle-05")] 104 | pub fn from_raw_display_handle(_: rwh_05::RawDisplayHandle) -> Result { 105 | Connection::new() 106 | } 107 | 108 | /// Opens the display connection corresponding to the given `DisplayHandle`. 109 | #[cfg(feature = "sm-raw-window-handle-06")] 110 | pub fn from_display_handle(_: rwh_06::DisplayHandle) -> Result { 111 | Connection::new() 112 | } 113 | 114 | /// Create a native widget from a raw pointer 115 | pub unsafe fn create_native_widget_from_ptr( 116 | &self, 117 | raw: *mut c_void, 118 | _size: Size2D, 119 | ) -> NativeWidget { 120 | NativeWidget { 121 | window_handle: raw as HWND, 122 | } 123 | } 124 | 125 | /// Create a native widget type from the given `RawWindowHandle`. 126 | #[cfg(feature = "sm-raw-window-handle-05")] 127 | pub fn create_native_widget_from_raw_window_handle( 128 | &self, 129 | raw_handle: rwh_05::RawWindowHandle, 130 | _size: Size2D, 131 | ) -> Result { 132 | use rwh_05::RawWindowHandle::Win32; 133 | 134 | match raw_handle { 135 | Win32(handle) => Ok(NativeWidget { 136 | window_handle: handle.hwnd as HWND, 137 | }), 138 | _ => Err(Error::IncompatibleNativeWidget), 139 | } 140 | } 141 | 142 | /// Create a native widget type from the given `WindowHandle`. 143 | #[cfg(feature = "sm-raw-window-handle-06")] 144 | pub fn create_native_widget_from_window_handle( 145 | &self, 146 | handle: rwh_06::WindowHandle, 147 | _size: Size2D, 148 | ) -> Result { 149 | use rwh_06::RawWindowHandle::Win32; 150 | 151 | match handle.as_raw() { 152 | Win32(handle) => Ok(NativeWidget { 153 | window_handle: handle.hwnd.get() as HWND, 154 | }), 155 | _ => Err(Error::IncompatibleNativeWidget), 156 | } 157 | } 158 | } 159 | 160 | impl NativeConnection { 161 | /// Creates a native connection. 162 | /// 163 | /// This is a no-op method present for consistency with other backends. 164 | #[inline] 165 | pub fn new() -> NativeConnection { 166 | NativeConnection 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/platform/windows/wgl/mod.rs: -------------------------------------------------------------------------------- 1 | // surfman/src/platform/windows/wgl/mod.rs 2 | 3 | //! A backend using the native Windows OpenGL WGL API. 4 | 5 | pub mod connection; 6 | pub mod context; 7 | pub mod device; 8 | pub mod surface; 9 | 10 | crate::implement_interfaces!(); 11 | 12 | #[cfg(test)] 13 | #[path = "../../../tests.rs"] 14 | mod tests; 15 | -------------------------------------------------------------------------------- /src/renderbuffers.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/renderbuffers.rs 2 | // 3 | //! A utility module for backends that wrap surfaces in OpenGL FBOs. 4 | 5 | use crate::context::{ContextAttributeFlags, ContextAttributes}; 6 | use crate::gl; 7 | use crate::Gl; 8 | use std::thread; 9 | 10 | use euclid::default::Size2D; 11 | use gl::Renderbuffer; 12 | use glow::HasContext; 13 | 14 | pub(crate) enum Renderbuffers { 15 | IndividualDepthStencil { 16 | depth: Option, 17 | stencil: Option, 18 | }, 19 | CombinedDepthStencil(Option), 20 | } 21 | 22 | impl Drop for Renderbuffers { 23 | fn drop(&mut self) { 24 | match *self { 25 | Renderbuffers::IndividualDepthStencil { 26 | depth: None, 27 | stencil: None, 28 | } 29 | | Renderbuffers::CombinedDepthStencil(None) => {} 30 | _ => { 31 | if !thread::panicking() { 32 | panic!("Should have destroyed the FBO renderbuffers with `destroy()`!") 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | impl Renderbuffers { 40 | pub(crate) fn new( 41 | gl: &Gl, 42 | size: &Size2D, 43 | attributes: &ContextAttributes, 44 | ) -> Renderbuffers { 45 | unsafe { 46 | if attributes 47 | .flags 48 | .contains(ContextAttributeFlags::DEPTH | ContextAttributeFlags::STENCIL) 49 | { 50 | let renderbuffer = gl.create_renderbuffer().unwrap(); 51 | gl.bind_renderbuffer(gl::RENDERBUFFER, Some(renderbuffer)); 52 | gl.renderbuffer_storage( 53 | gl::RENDERBUFFER, 54 | gl::DEPTH24_STENCIL8, 55 | size.width, 56 | size.height, 57 | ); 58 | gl.bind_renderbuffer(gl::RENDERBUFFER, None); 59 | return Renderbuffers::CombinedDepthStencil(Some(renderbuffer)); 60 | } 61 | 62 | let (mut depth_renderbuffer, mut stencil_renderbuffer) = (None, None); 63 | if attributes.flags.contains(ContextAttributeFlags::DEPTH) { 64 | depth_renderbuffer = Some(gl.create_renderbuffer().unwrap()); 65 | gl.bind_renderbuffer(gl::RENDERBUFFER, depth_renderbuffer); 66 | gl.renderbuffer_storage( 67 | gl::RENDERBUFFER, 68 | gl::DEPTH_COMPONENT24, 69 | size.width, 70 | size.height, 71 | ); 72 | } 73 | if attributes.flags.contains(ContextAttributeFlags::STENCIL) { 74 | stencil_renderbuffer = Some(gl.create_renderbuffer().unwrap()); 75 | gl.bind_renderbuffer(gl::RENDERBUFFER, stencil_renderbuffer); 76 | gl.renderbuffer_storage( 77 | gl::RENDERBUFFER, 78 | gl::STENCIL_INDEX8, 79 | size.width, 80 | size.height, 81 | ); 82 | } 83 | gl.bind_renderbuffer(gl::RENDERBUFFER, None); 84 | 85 | Renderbuffers::IndividualDepthStencil { 86 | depth: depth_renderbuffer, 87 | stencil: stencil_renderbuffer, 88 | } 89 | } 90 | } 91 | 92 | pub(crate) fn bind_to_current_framebuffer(&self, gl: &Gl) { 93 | unsafe { 94 | match *self { 95 | Renderbuffers::CombinedDepthStencil(renderbuffer) => { 96 | if renderbuffer.is_some() { 97 | gl.framebuffer_renderbuffer( 98 | gl::FRAMEBUFFER, 99 | gl::DEPTH_STENCIL_ATTACHMENT, 100 | gl::RENDERBUFFER, 101 | renderbuffer, 102 | ); 103 | } 104 | } 105 | Renderbuffers::IndividualDepthStencil { 106 | depth: depth_renderbuffer, 107 | stencil: stencil_renderbuffer, 108 | } => { 109 | if depth_renderbuffer.is_some() { 110 | gl.framebuffer_renderbuffer( 111 | gl::FRAMEBUFFER, 112 | gl::DEPTH_ATTACHMENT, 113 | gl::RENDERBUFFER, 114 | depth_renderbuffer, 115 | ); 116 | } 117 | if stencil_renderbuffer.is_some() { 118 | gl.framebuffer_renderbuffer( 119 | gl::FRAMEBUFFER, 120 | gl::STENCIL_ATTACHMENT, 121 | gl::RENDERBUFFER, 122 | stencil_renderbuffer, 123 | ); 124 | } 125 | } 126 | } 127 | } 128 | } 129 | 130 | pub(crate) fn destroy(&mut self, gl: &Gl) { 131 | unsafe { 132 | gl.bind_renderbuffer(gl::RENDERBUFFER, None); 133 | 134 | match *self { 135 | Renderbuffers::CombinedDepthStencil(ref mut renderbuffer) => { 136 | if let Some(renderbuffer) = renderbuffer.take() { 137 | gl.delete_renderbuffer(renderbuffer); 138 | } 139 | } 140 | Renderbuffers::IndividualDepthStencil { 141 | depth: ref mut depth_renderbuffer, 142 | stencil: ref mut stencil_renderbuffer, 143 | } => { 144 | if let Some(stencil_renderbuffer) = stencil_renderbuffer.take() { 145 | gl.delete_renderbuffer(stencil_renderbuffer); 146 | } 147 | if let Some(depth_renderbuffer) = depth_renderbuffer.take() { 148 | gl.delete_renderbuffer(depth_renderbuffer); 149 | } 150 | } 151 | } 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/surface.rs: -------------------------------------------------------------------------------- 1 | // surfman/surfman/src/surface.rs 2 | // 3 | //! Information related to hardware surfaces. 4 | 5 | use crate::context::ContextID; 6 | 7 | use euclid::default::Size2D; 8 | use std::fmt::{self, Display, Formatter}; 9 | 10 | /// Various data about the surface. 11 | pub struct SystemSurfaceInfo { 12 | /// The surface's size, in device pixels. 13 | pub size: Size2D, 14 | /// The ID of the surface. This should be globally unique for each currently-allocated surface. 15 | pub id: SurfaceID, 16 | } 17 | 18 | /// Various data about the surface. 19 | pub struct SurfaceInfo { 20 | /// The surface's size, in device pixels. 21 | pub size: Size2D, 22 | /// The ID of the surface. This should be globally unique for each currently-allocated surface. 23 | pub id: SurfaceID, 24 | /// The ID of the context that this surface belongs to. 25 | pub context_id: ContextID, 26 | /// The OpenGL framebuffer object that can be used to render to this surface. 27 | /// 28 | /// This is only valid when the surface is actually attached to a context. 29 | pub framebuffer_object: Option, 30 | } 31 | 32 | // The default framebuffer for a context. 33 | #[allow(dead_code)] 34 | pub(crate) enum Framebuffer { 35 | // No framebuffer has been attached to the context. 36 | None, 37 | // The context is externally-managed. 38 | External(E), 39 | // The context renders to a surface. 40 | Surface(S), 41 | } 42 | 43 | /// A unique ID per allocated surface. 44 | /// 45 | /// If you destroy a surface and then create a new one, the ID may be reused. 46 | #[derive(Clone, Copy, Debug, PartialEq)] 47 | pub struct SurfaceID(pub usize); 48 | 49 | impl Display for SurfaceID { 50 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 51 | write!(f, "{:?}", *self) 52 | } 53 | } 54 | 55 | /// Specifies how and if the CPU has direct access to the surface data. 56 | /// 57 | /// No matter what value you choose here, the CPU can always indirectly upload data to the surface 58 | /// by, for example, drawing a full-screen quad. This enumeration simply describes whether the CPU 59 | /// has *direct* memory access to the surface, via a slice of pixel data. 60 | /// 61 | /// You can achieve better performance by limiting surfaces to `GPUOnly` unless you need to access 62 | /// the data on the CPU. For surfaces marked as GPU-only, the GPU can use texture swizzling to 63 | /// improve memory locality. 64 | #[derive(Clone, Copy, PartialEq, Debug)] 65 | pub enum SurfaceAccess { 66 | /// The surface data is accessible by the GPU only. 67 | /// 68 | /// The `lock_surface_data()` method will return the `SurfaceDataInaccessible` error when 69 | /// called on this surface. 70 | /// 71 | /// This is typically the flag you will want to use. 72 | GPUOnly, 73 | 74 | /// The surface data is accessible by the GPU and CPU. 75 | GPUCPU, 76 | 77 | /// The surface data is accessible by the GPU and CPU, and the CPU will send surface data over 78 | /// the bus to the GPU using write-combining if available. 79 | /// 80 | /// Specifically, what this means is that data transfer will be optimized for the following 81 | /// patterns: 82 | /// 83 | /// 1. Writing, not reading. 84 | /// 85 | /// 2. Writing sequentially, filling every byte in a range. 86 | /// 87 | /// This flag has no effect on correctness (at least on x86), but not following the rules 88 | /// above may result in severe performance consequences. 89 | /// 90 | /// The driver is free to treat this as identical to `GPUCPU`. 91 | GPUCPUWriteCombined, 92 | } 93 | 94 | /// Information specific to the type of surface: generic or widget. 95 | #[derive(Clone)] 96 | pub enum SurfaceType { 97 | /// An off-screen surface that has a pixel size. Generic surfaces can sometimes be shown on 98 | /// screen using platform-specific APIs, but `surfman` itself provides no way to draw their 99 | /// contents on screen. Only generic surfaces can be bound to textures. 100 | Generic { 101 | /// The size of the surface. 102 | /// 103 | /// For HiDPI screens, this is a physical size, not a logical size. 104 | size: Size2D, 105 | }, 106 | /// A surface displayed inside a native widget (window or view). The size of a widget surface 107 | /// is automatically determined based on the size of the widget. (For example, if the widget is 108 | /// a window, the size of the surface will be the physical size of the window.) Widget surfaces 109 | /// cannot be bound to textures. 110 | Widget { 111 | /// A native widget type specific to the backend. 112 | /// 113 | /// For example, on Windows this wraps an `HWND`. 114 | native_widget: NativeWidget, 115 | }, 116 | } 117 | 118 | impl SurfaceAccess { 119 | #[allow(dead_code)] 120 | #[inline] 121 | pub(crate) fn cpu_access_allowed(self) -> bool { 122 | match self { 123 | SurfaceAccess::GPUOnly => false, 124 | SurfaceAccess::GPUCPU | SurfaceAccess::GPUCPUWriteCombined => true, 125 | } 126 | } 127 | } 128 | --------------------------------------------------------------------------------