Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:ila.embsys:branches:hardware:xr
gxr
gxr.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File gxr.obscpio of Package gxr
07070100000000000081A4000000000000000000000001636E6486000001DA000000000000000000000000000000000000001200000000gxr/.clang-formatBasedOnStyle: GNU ColumnLimit: 80 ContinuationIndentWidth: 2 Cpp11BracedListStyle: true SpaceAfterCStyleCast: true BreakBeforeBraces: GNU AlwaysBreakAfterReturnType: All IndentCaseLabels: true PenaltyBreakAssignment: 10000 PenaltyBreakString: 15000 PenaltyBreakBeforeFirstCallParameter: 20000 PenaltyBreakOpenParenthesis: 3000000 #PenaltyExcessCharacter: 10000 BinPackParameters: false # https://github.com/llvm/llvm-project/issues/51202 AlignConsecutiveDeclarations: true 07070100000001000081A4000000000000000000000001636E64860000001A000000000000000000000000000000000000000F00000000gxr/.gitignore.buildconfig build/ *.spv 07070100000002000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000F00000000gxr/.gitlab-ci07070100000003000081A4000000000000000000000001636E648600002D80000000000000000000000000000000000000001300000000gxr/.gitlab-ci.yml.templates_sha: &templates_sha e9a461877f8c7f9fed9fff5491067ec3c3472559 include: - project: 'freedesktop/ci-templates' ref: *templates_sha file: '/templates/arch.yml' - project: "freedesktop/ci-templates" ref: *templates_sha file: "/templates/ubuntu.yml" - project: "freedesktop/ci-templates" ref: *templates_sha file: "/templates/debian.yml" variables: FDO_UPSTREAM_REPO: xrdesktop/gxr GULKAN_COMMIT: "9e52e7ce0d50d86c4810af586dcdf4868b30e7f5" OPENXR_SDK_TAG: "release-1.0.24" # increment to easily rebuild all images GLOBAL_TAG_SUFFIX: 1 FDO_DISTRIBUTION_DATE: "2022-10-13" stages: - container_prep - build_and_test - package - reprepro - pages # "Base" job for installing Gulkan, OpenXR SDK # Variables are already substituted when running script, so pass as args .xrdesktop.base-job.build_deps: stage: container_prep variables: FDO_DISTRIBUTION_EXEC: | bash .gitlab-ci/dep_install/install_gulkan.sh $GULKAN_COMMIT && \ bash .gitlab-ci/dep_install/install_openxr.sh $OPENXR_SDK_TAG # "Base" job for a Meson build .xrdesktop.base-job.build-meson: stage: build_and_test script: - meson $MESON_ARGS --prefix /usr build - ninja -C build install - meson test -C build/ --no-suite gxr:xr # variables shared by container_prep and build jobs .xrdesktop.variables.arch:rolling: variables: FDO_DISTRIBUTION_TAG: "${FDO_DISTRIBUTION_DATE}.${GLOBAL_TAG_SUFFIX}.0" .xrdesktop.variables.ubuntu:focal: variables: FDO_DISTRIBUTION_VERSION: "20.04" FDO_DISTRIBUTION_TAG: "${FDO_DISTRIBUTION_DATE}.${GLOBAL_TAG_SUFFIX}.0" .xrdesktop.variables.ubuntu:jammy: variables: FDO_DISTRIBUTION_VERSION: "22.04" FDO_DISTRIBUTION_TAG: "${FDO_DISTRIBUTION_DATE}.${GLOBAL_TAG_SUFFIX}.0" .xrdesktop.variables.debian:bookworm: variables: FDO_DISTRIBUTION_VERSION: "bookworm" FDO_DISTRIBUTION_TAG: "${FDO_DISTRIBUTION_DATE}.${GLOBAL_TAG_SUFFIX}.0" .xrdesktop.variables.debian:bullseye: variables: FDO_DISTRIBUTION_VERSION: "bullseye" FDO_DISTRIBUTION_TAG: "${FDO_DISTRIBUTION_DATE}.${GLOBAL_TAG_SUFFIX}.0" .xrdesktop.variables.debian:sid: variables: FDO_DISTRIBUTION_VERSION: "sid" FDO_DISTRIBUTION_TAG: "${FDO_DISTRIBUTION_DATE}.${GLOBAL_TAG_SUFFIX}.0" .xrdesktop.variables.debian-based-packages: variables: CORE_REQUIRED_PACKAGES: "git gcc meson pkg-config libglib2.0-dev libgdk-pixbuf2.0-dev libvulkan-dev libgraphene-1.0-dev libcairo2-dev glslang-tools cmake libxxf86vm-dev libgl1-mesa-dev libx11-xcb-dev libxcb-dri2-0-dev libxcb-glx0-dev libxcb-icccm4-dev libxcb-keysyms1-dev libxcb-randr0-dev libxrandr-dev libxxf86vm-dev mesa-common-dev libgtk-3-dev libjson-glib-dev ca-certificates libdrm-dev apt-utils reprepro build-essential devscripts debhelper dput-ng gettext-base gtk-doc-tools libglew-dev libglfw3-dev" # === Archlinux === arch:container_prep: extends: - .xrdesktop.variables.arch:rolling - .fdo.container-build@arch # from ci-templates - .xrdesktop.base-job.build_deps stage: container_prep variables: FDO_DISTRIBUTION_PACKAGES: "pkgconf meson gdk-pixbuf2 vulkan-headers vulkan-icd-loader graphene cairo glslang glfw-x11 glew shaderc json-glib gcc clang git cmake gtk3" arch:meson:gcc: extends: - .xrdesktop.variables.arch:rolling - .fdo.distribution-image@arch # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: gcc CXX: g++ arch:meson:clang: extends: - .xrdesktop.variables.arch:rolling - .fdo.distribution-image@arch # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: clang CXX: clang++ # === Ubuntu Focal === ubuntu:focal:container_prep: stage: container_prep extends: - .xrdesktop.variables.ubuntu:focal - .fdo.container-build@ubuntu # from ci-templates - .xrdesktop.base-job.build_deps - .xrdesktop.variables.debian-based-packages variables: FDO_DISTRIBUTION_PACKAGES: "${CORE_REQUIRED_PACKAGES}" ubuntu:focal:gcc: extends: - .xrdesktop.variables.ubuntu:focal - .fdo.distribution-image@ubuntu # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: gcc CXX: g++ # === Ubuntu jammy === ubuntu:jammy:container_prep: stage: container_prep extends: - .xrdesktop.variables.ubuntu:jammy - .fdo.container-build@ubuntu # from ci-templates - .xrdesktop.base-job.build_deps - .xrdesktop.variables.debian-based-packages variables: FDO_DISTRIBUTION_PACKAGES: "${CORE_REQUIRED_PACKAGES}" ubuntu:jammy:gcc: extends: - .xrdesktop.variables.ubuntu:jammy - .fdo.distribution-image@ubuntu # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: gcc CXX: g++ # === Debian bookworm === debian:bookworm:container_prep: stage: container_prep extends: - .xrdesktop.variables.debian:bookworm - .fdo.container-build@debian # from ci-templates - .xrdesktop.base-job.build_deps - .xrdesktop.variables.debian-based-packages variables: FDO_DISTRIBUTION_PACKAGES: "${CORE_REQUIRED_PACKAGES}" debian:bookworm:gcc: extends: - .xrdesktop.variables.debian:bookworm - .fdo.distribution-image@debian # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: gcc CXX: g++ # === Debian bullseye === debian:bullseye:container_prep: stage: container_prep extends: - .xrdesktop.variables.debian:bullseye - .fdo.container-build@debian # from ci-templates - .xrdesktop.base-job.build_deps - .xrdesktop.variables.debian-based-packages variables: FDO_DISTRIBUTION_PACKAGES: "${CORE_REQUIRED_PACKAGES}" debian:bullseye:gcc: extends: - .xrdesktop.variables.debian:bullseye - .fdo.distribution-image@debian # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: gcc CXX: g++ # === Debian sid === debian:sid:container_prep: stage: container_prep extends: - .xrdesktop.variables.debian:sid - .fdo.container-build@debian # from ci-templates - .xrdesktop.base-job.build_deps - .xrdesktop.variables.debian-based-packages variables: FDO_DISTRIBUTION_PACKAGES: "${CORE_REQUIRED_PACKAGES}" debian:sid:gcc: extends: - .xrdesktop.variables.debian:sid - .fdo.distribution-image@debian # from ci-templates - .xrdesktop.base-job.build-meson variables: MESON_ARGS: -Dapi_doc=false CC: gcc CXX: g++ # Packaging .xrdesktop.packaging.conditions: rules: # Only the default branch of the "upstream" repo. - if: '$CI_PROJECT_PATH == $FDO_UPSTREAM_REPO && $CI_COMMIT_REF_NAME == "next"' when: on_success # Otherwise, don't build packages. - when: never # Packaging .xrdesktop.base-job.debuild: extends: - .xrdesktop.packaging.conditions stage: package before_script: # Configure git - needed despite not actually committing here. - git config --global user.email "christoph.haag@collabora.com" - git config --global user.name "xrdesktop CI" script: # Prep the source tree - git clean -dfx - git remote -v - git fetch --unshallow - git fetch origin - git fetch origin --tags - rm -rf debian - cp -ra ${PACKAGE_DIR} debian - DEBFULLNAME="xrdesktop CI <christoph.haag@collabora.com>" DEBEMAIL="christoph.haag@collabora.com" debian/extra/prepare-commit-package.sh ${CI_COMMIT_SHA} 1~${BACKPORT_SUFFIX}~ci$(date --utc "+%Y%m%d") # Build the package - debuild -uc -us -d -sa # Use dput-ng to move the package-related files into some artifacts. - export INCOMING=$(pwd)/incoming - mkdir -p $INCOMING - mkdir -p ~/.dput.d/profiles - cat .gitlab-ci/localhost.json | envsubst > ~/.dput.d/profiles/localhost.json - dpkg-parsechangelog --show-field version > incoming/${DISTRO}.distro - dput --force --debug localhost ../gxr_$(dpkg-parsechangelog --show-field version)_amd64.changes artifacts: paths: - "incoming/" expire_in: 2 days debian:bookworm:package: extends: - .xrdesktop.variables.debian:bookworm - .fdo.distribution-image@debian # from ci-templates - .xrdesktop.base-job.debuild variables: BACKPORT_SUFFIX: bpo8 PACKAGE_DIR: .gitlab-ci/debian_sid DISTRO: bookworm debian:bullseye:package: extends: - .xrdesktop.variables.debian:bullseye - .fdo.distribution-image@debian # from ci-templates - .xrdesktop.base-job.debuild variables: BACKPORT_SUFFIX: bpo9 PACKAGE_DIR: .gitlab-ci/debian_sid DISTRO: bullseye debian:sid:package: extends: - .xrdesktop.variables.debian:sid - .fdo.distribution-image@debian # from ci-templates - .xrdesktop.base-job.debuild variables: BACKPORT_SUFFIX: bpo10 PACKAGE_DIR: .gitlab-ci/debian_sid DISTRO: sid ubuntu:focal:package: extends: - .xrdesktop.variables.ubuntu:focal - .fdo.distribution-image@ubuntu # from ci-templates - .xrdesktop.base-job.debuild variables: BACKPORT_SUFFIX: ubuntu20.04 PACKAGE_DIR: .gitlab-ci/debian_sid DISTRO: focal ubuntu:jammy:package: extends: - .xrdesktop.variables.ubuntu:jammy - .fdo.distribution-image@ubuntu # from ci-templates - .xrdesktop.base-job.debuild variables: BACKPORT_SUFFIX: ubuntu22.04 PACKAGE_DIR: .gitlab-ci/debian_sid DISTRO: jammy reprepro:package: stage: reprepro extends: - .xrdesktop.variables.debian:sid - .xrdesktop.packaging.conditions - .fdo.distribution-image@debian # from ci-templates dependencies: - debian:bookworm:package - debian:bullseye:package - debian:sid:package - ubuntu:focal:package - ubuntu:jammy:package before_script: # Convince gnupg to work properly in CI - mkdir -p ~/.gnupg && chmod 700 ~/.gnupg - touch ~/.gnupg/gpg.conf - echo 'use-agent' > ~/.gnupg/gpg.conf - echo 'pinentry-mode loopback' >> ~/.gnupg/gpg.conf - touch ~/.gnupg/gpg-agent.conf - echo 'allow-loopback-pinentry' > ~/.gnupg/gpg-agent.conf - echo RELOADAGENT | gpg-connect-agent - gpg --batch --no-tty --yes --pinentry-mode loopback --passphrase ${XRDESKTOP_GPG_PASSPHRASE} --import ${XRDESKTOP_GPG_SECRET_KEY} script: # Use reprepro to create an apt repository in our artifacts - mkdir -p repo/conf # For each distro, sign the changes file and add it to the repo. - | for fn in incoming/*.distro; do # parse the distro name out export DISTRO=$(echo $fn | sed -e 's:incoming/::' -e 's:[.]distro::') echo "Processing $DISTRO" # add distro to repository config - blank line is mandatory! cat .gitlab-ci/distributions | envsubst >> repo/conf/distributions echo >> repo/conf/distributions echo "Signing package for $DISTRO" debsign -k ${XRDESKTOP_GPG_FINGERPRINT} -p "gpg --batch --no-tty --yes --pinentry-mode loopback --passphrase ${XRDESKTOP_GPG_PASSPHRASE}" incoming/gxr_$(cat $fn)_amd64.changes echo "Adding package for $DISTRO to the repository" reprepro -V --ignore=wrongdistribution -b repo include ${DISTRO} incoming/gxr_$(cat $fn)_amd64.changes done artifacts: paths: - "repo/" expire_in: 2 days ### # Pages ### pages: stage: pages extends: - .xrdesktop.packaging.conditions dependencies: - reprepro:package script: - mkdir -p public # - mv build/doc/html/* public/ - mv repo public/apt artifacts: paths: - public 07070100000004000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001A00000000gxr/.gitlab-ci/debian_sid07070100000005000081A4000000000000000000000001636E64860000094A000000000000000000000000000000000000002400000000gxr/.gitlab-ci/debian_sid/changeloggxr (0.15.1-2) unstable; urgency=medium * Source only upload. -- Andrew Lee (李健秋) <ajqlee@debian.org> Tue, 21 Jul 2020 14:58:40 +0800 gxr (0.15.1-1) experimental; urgency=medium * New upstream version 0.15.1 * Drop non-free libopenvr support. (Closes: #959608, #963882) * Bump Standard-Version to 4.5.0. * Rename shared library package for soname bump. * Bump build-deps on libgulkan 0.15. * Clean up rules for non-free libopenvr support. -- Andrew Lee (李健秋) <ajqlee@debian.org> Mon, 20 Jul 2020 13:46:49 +0800 gxr (0.14.0-4) unstable; urgency=medium * Source only upload. -- Andrew Lee (李健秋) <ajqlee@debian.org> Fri, 24 Apr 2020 12:12:25 +0800 gxr (0.14.0-3) unstable; urgency=medium * Update package descriptions. * Package both openvr and openxr backends. (Closes: #956732) * Use chrpath utility to remove the RPATH. -- Andrew Lee (李健秋) <ajqlee@debian.org> Tue, 21 Apr 2020 18:27:00 +0800 gxr (0.14.0-2) unstable; urgency=medium * Source only upload. -- Andrew Lee (李健秋) <ajqlee@debian.org> Tue, 14 Apr 2020 17:28:50 +0800 gxr (0.14.0-1) unstable; urgency=medium * New upstream release. * Drop patches already included in upstream. * Build against to openxr instead of non-free openvr. * Move to main as no depends on non-free software openvr now. * debian/control: update versioned build-deps on gulkan. * Rename package to match the sonames numbers. -- Andrew Lee (李健秋) <ajqlee@debian.org> Fri, 03 Apr 2020 14:00:55 +0800 gxr (0.13.2-2) unstable; urgency=medium * debian/control: update build-depends and depends for openvr transition. -- Andrew Lee (李健秋) <ajqlee@debian.org> Wed, 11 Dec 2019 16:13:40 +0800 gxr (0.13.2-1) unstable; urgency=medium * New upstream 0.13.2 release. * debian/copyright: update DFSG license for the cat image. * Cherry-pick upstream patches to support openvr version 0.8.19. * Use chrpath utility to remove the RPATH. -- Andrew Lee (李健秋) <ajqlee@debian.org> Mon, 25 Nov 2019 14:16:57 +0800 gxr (0.13.1-1) unstable; urgency=medium * New upstream 0.13.1 release. -- Andrew Lee (李健秋) <ajqlee@debian.org> Sat, 12 Oct 2019 19:55:22 +0800 gxr (0.12.1-1) unstable; urgency=medium * Initial Release. (Closes: #933568) -- Andrew Lee (李健秋) <ajqlee@debian.org> Sun, 04 Aug 2019 10:17:40 +0800 07070100000006000081A4000000000000000000000001636E648600000003000000000000000000000000000000000000002100000000gxr/.gitlab-ci/debian_sid/compat11 07070100000007000081A4000000000000000000000001636E64860000055B000000000000000000000000000000000000002200000000gxr/.gitlab-ci/debian_sid/controlSource: gxr Section: libs Priority: optional Maintainer: Andrew Lee (李健秋) <ajqlee@debian.org> Uploaders: Héctor Orón Martínez <zumbi@debian.org> Build-Depends: debhelper (>= 11), chrpath, glslang-tools, gobject-introspection, gtk-doc-tools, libgtk-3-dev (>= 3.22), libgulkan-dev (>= 0.15.0), libjson-glib-dev, libopenxr-dev, mesa-common-dev, meson (>= 0.45.1), pkg-config, Standards-Version: 4.5.0 Homepage: https://gitlab.freedesktop.org/xrdesktop/gxr Vcs-Browser: https://salsa.debian.org/xrdesktop-team/gxr Vcs-Git: https://salsa.debian.org/xrdesktop-team/gxr.git Package: libgxr-0.15-0 Section: libs Architecture: linux-any Depends: ${misc:Depends}, ${shlibs:Depends}, libsdl2-2.0-0, Description: glib wrapper for OpenXR API A glib wrapper and utilities for the OpenXR API. . This package contains the shared libraries. Package: libgxr-dev Section: libdevel Architecture: any Depends: ${misc:Depends}, libcogl-dev, libcogl-path-dev, libgdk-pixbuf2.0-dev, libglib2.0-dev, libgraphene-1.0-dev, libgtk-3-dev, libgulkan-dev, libopenxr-dev, libgxr-0.15-0 (= ${binary:Version}), libvulkan-dev, mesa-common-dev, Description: glib wrapper for the OpenXR API - development headers Development files for the libgxr library. . This package contains the header and development files which are needed for building OpenXR applications. 07070100000008000081A4000000000000000000000001636E648600004701000000000000000000000000000000000000002400000000gxr/.gitlab-ci/debian_sid/copyrightFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: gxr Source: https://gitlab.freedesktop.org/xrdesktop/gxr Files: * Copyright: 2018-2019 Collabora Ltd. License: Expat Files: res/cat.jpg Copyright: 2015 LisOk1984 License: CC-BY-SA-4.0 Files: res/hawk.jpg Copyright: 2015 Charles J Sharp License: CC-BY-SA-4.0 Files: examples/dmabuf_content.h Copyright: 2014 Rob Clark <robdclark@gmail.com> 2018 Collabora Ltd. License: Expat Files: debian/* Copyright: 2018-2019 Collabora Ltd. 2019 Héctor Orón Martínez <zumbi@debian.org> 2019 Andrew Lee (李健秋) <ajqlee@debian.org> License: Expat License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License: CC-BY-SA-4.0 Creative Commons Attribution-ShareAlike 4.0 International Public License . By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. . . Section 1 -- Definitions. . a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. . b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. . c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. . d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. . e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. . f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. . g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. . h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. . i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. . j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. . k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. . l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. . m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. . . Section 2 -- Scope. . a. License grant. . 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: . a. reproduce and Share the Licensed Material, in whole or in part; and . b. produce, reproduce, and Share Adapted Material. . 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. . 3. Term. The term of this Public License is specified in Section 6(a). . 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. . 5. Downstream recipients. . a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. . b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. . c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. . 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). . b. Other rights. . 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. . 2. Patent and trademark rights are not licensed under this Public License. . 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. . . Section 3 -- License Conditions. . Your exercise of the Licensed Rights is expressly made subject to the following conditions. . a. Attribution. . 1. If You Share the Licensed Material (including in modified form), You must: . a. retain the following if it is supplied by the Licensor with the Licensed Material: . i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); . ii. a copyright notice; . iii. a notice that refers to this Public License; . iv. a notice that refers to the disclaimer of warranties; . v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; . b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and . c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. . 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. . 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. . b. ShareAlike. . In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. . 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. . 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. . 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. . . Section 4 -- Sui Generis Database Rights. . Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: . a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; . b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, . including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. . For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. . . Section 5 -- Disclaimer of Warranties and Limitation of Liability. . a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. . b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. . c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. . . Section 6 -- Term and Termination. . a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. . b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: . 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or . 2. upon express reinstatement by the Licensor. . For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. . c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. . d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. . . Section 7 -- Other Terms and Conditions. . a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. . b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. . . Section 8 -- Interpretation. . a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. . b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. . c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. . d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 07070100000009000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000002000000000gxr/.gitlab-ci/debian_sid/extra0707010000000A000081ED000000000000000000000001636E6486000003CC000000000000000000000000000000000000003A00000000gxr/.gitlab-ci/debian_sid/extra/prepare-commit-package.sh#!/bin/sh # Copyright 2020, Ryan Pavlik <ryan@ryanpavlik.com> # SPDX-License-Identifier: CC0-1.0 # Packages produced this way are for automated use only and shouldn't be uploaded to the Debian archive. set -e ( cd "$(dirname $0)" cd ../.. export DEVSCRIPTS_CHECK_DIRNAME_LEVEL=0 if [ x"$1" != x ]; then COMMIT_TO_PACKAGE=$1 export COMMIT_TO_PACKAGE else COMMIT_TO_PACKAGE=master export COMMIT_TO_PACKAGE fi if [ x"$2" != x ]; then PKG_REVISION=$2 export PKG_REVISION else PKG_REVISION=1~ci$(date --utc "+%Y%m%d") export PKG_REVISION fi UPSTREAM_VER=$(git describe --abbrev=0 --tags $COMMIT_TO_PACKAGE | sed -E -e 's/^v//' -e 's/-([0-9]+)-g([0-9a-f])/+git\1.\2/') git archive -o "../gxr_${UPSTREAM_VER}.orig.tar.gz" ${COMMIT_TO_PACKAGE} dch -b --newversion "${UPSTREAM_VER}-${PKG_REVISION}" --preserve "Automated CI build of commit ${COMMIT_TO_PACKAGE}" ) 0707010000000B000081A4000000000000000000000001636E648600000063000000000000000000000000000000000000002300000000gxr/.gitlab-ci/debian_sid/gbp.conf[DEFAULT] debian-branch = debian/master pristine-tar = True upstream-branch = upstream/latest 0707010000000C000081A4000000000000000000000001636E64860000001B000000000000000000000000000000000000003000000000gxr/.gitlab-ci/debian_sid/libgxr-0.15-0.installusr/lib/*/libgxr-0.*.so.* 0707010000000D000081A4000000000000000000000001636E648600000037000000000000000000000000000000000000002D00000000gxr/.gitlab-ci/debian_sid/libgxr-dev.installusr/include/* usr/lib/*/lib*.so usr/lib/*/pkgconfig/* 0707010000000E000081ED000000000000000000000001636E6486000001E5000000000000000000000000000000000000002000000000gxr/.gitlab-ci/debian_sid/rules#!/usr/bin/make -f %: dh $@ override_dh_missing: dh_missing --list-missing test_env = \ -u DISPLAY \ HOME=$(CURDIR)/debian/tmp-home XDG_CACHE_HOME= override_dh_auto_test: install -d $(CURDIR)/debian/tmp-home/.cache/gxr # Run tests without VR runtime env $(test_env) meson build env $(test_env) ninja -C build env $(test_env) meson test -C build/ --no-suite gxr:xr --no-suite post-install override_dh_shlibdeps: dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info 0707010000000F000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000002100000000gxr/.gitlab-ci/debian_sid/source07070100000010000081A4000000000000000000000001636E64860000000C000000000000000000000000000000000000002800000000gxr/.gitlab-ci/debian_sid/source/format3.0 (quilt) 07070100000011000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001B00000000gxr/.gitlab-ci/dep_install07070100000012000081ED000000000000000000000001636E6486000000B6000000000000000000000000000000000000002D00000000gxr/.gitlab-ci/dep_install/install_gulkan.sh#!/bin/bash mkdir -p deps cd deps git clone https://gitlab.freedesktop.org/xrdesktop/gulkan.git cd gulkan git checkout $1 meson build --prefix /usr ninja -C build install cd ../.. 07070100000013000081ED000000000000000000000001636E648600000126000000000000000000000000000000000000002D00000000gxr/.gitlab-ci/dep_install/install_openxr.sh#!/bin/bash mkdir -p deps cd deps git clone --branch $1 https://github.com/KhronosGroup/OpenXR-SDK.git cd OpenXR-SDK cmake . \ -G Ninja \ -B build \ -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTS=OFF \ -DDYNAMIC_LOADER=ON ninja -C build install cd ../.. 07070100000014000081A4000000000000000000000001636E6486000000CA000000000000000000000000000000000000001D00000000gxr/.gitlab-ci/distributionsOrigin: xrdesktop.freedesktop.org Description: xrdesktop CI apt repository Codename: ${DISTRO} Architectures: amd64 i386 source Components: main Tracking: minimal SignWith: ${XRDESKTOP_GPG_FINGERPRINT} 07070100000015000081A4000000000000000000000001636E648600000041000000000000000000000000000000000000002500000000gxr/.gitlab-ci/distributions.licenseCopyright 2020, Collabora, Ltd. SPDX-License-Identifier: BSL-1.007070100000016000081A4000000000000000000000001636E6486000000F2000000000000000000000000000000000000001E00000000gxr/.gitlab-ci/localhost.json{ "+hooks": [ ], "-hooks": [ "gpg", "allowed-distribution", "suite-mismatch", "lintian" ], "incoming": "${INCOMING}", "meta": "debian", "method": "local", "run_lintian": false } 07070100000017000081A4000000000000000000000001636E648600000041000000000000000000000000000000000000002600000000gxr/.gitlab-ci/localhost.json.licenseCopyright 2020, Collabora, Ltd. SPDX-License-Identifier: BSL-1.007070100000018000081A4000000000000000000000001636E648600000C6F000000000000000000000000000000000000003E00000000gxr/.gitlab-ci/xrdesktop_ci_signing_key_0x7C668346_public.asc-----BEGIN PGP PUBLIC KEY BLOCK----- mQINBGC1diEBEADXENojlfGkPAHesXpYNBhxqtCDvEV1wXqmclYhs8xwXLuC/bgb 7OLtkVrnOY7tQR+zUpbb32KmAlpdD0Aq3xgdtijWXDBBMcOjAYev5622aH4lLrps oNGOQOfnM61fb8GmjoGi6oRrYwqBmC/2Z4GvCqgVhZRi5CbxDrrRKANVH8jWv8oW IFjU62mEbO4BmqLTgAdYNS2WqMI5Q1d8MtWiG7OYxtZYHatcyAVA/eNiHB/OOoKr TN8k3omfqbePcZk9BowOEOpizN033cA3+F4Es0/MJjPmqg2BLyJRxjFkrZZkm0jg HtszhvVjl0uAT9oPETYwySV71gpT1H6NR+MASgvOo7QL0HuIL1WMXZxyMebWuYo7 xjbuHeiQqzWlbv+exKjZpz/AJ43c4Wni+D5MqE5guCPco4/EZ9c0l+ccyKJROfWj AnLWQ8b/loMX1bLMwwQWlwaNCb5pnoTGxxBp9PekT7M2Pkm9/DHhG8JESBmfQTRr kHYHTGZzSxHcA7HHYJolI8cK0USSHPKyZday6xUa3vp/n7D3SMLO50pnNaZSfDNr f4ix/e6YrpDxwbgJUYQBx+DvwWOdj31DirSCJ6bK7ntnNvNgnjySS51P72+qH7IR J8vRYvWGS0xxHJxvK4Jg5s5XouZASPA4FKnSvN/ZDq80W0ySnJgb3b1aVwARAQAB tEhDaHJpc3RvcGggSGFhZyAoeHJkZXNrdG9wIGNpIHNpZ25pbmcga2V5KSA8Y2hy aXN0b3BoLmhhYWdAY29sbGFib3JhLmNvbT6JAk4EEwEIADgWIQRQEB1IppTDFn77 8X7AGgu0fGaDRgUCYLV2IQIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDA Ggu0fGaDRp17EACe7/LVS+6AJ9Tcj8j12B/1APbJQANdTKi6X+oLcm0kuwN+3u9a 9cfpCxcBHv+0Gauxh5jT6I500NF/uVHa2jkcxRHN3fReuSz65x7JVgRk40/5rHTu sC8+A4sNhgYYFt4KDTzJwuuyRWN8nOCi5QsMrUnDRxhLDmUxA8Eou6KvrnoHGv3L o0Mkv0cI6hCRvCmtt4WODfVT2Mi116RsSHPDYkujocdIel1iIt/Ofrss5rZT2DXW raVgDZz5WhPXrvHtSbCiEjgTtIOWWX/UOu2y9E2I6teIuHMkVlpfFH4KlOC5BKb/ kAxN7VgQVT1LI4MJXTHJom47PIvH3ciR01/nDoCawdrsWQiv6w/t46AscrJoC7rC 8lLmuqneqyeHbIdm4PfXrbJOpv6PHIEvlVyB+yxMmu+R5Ad6VcaVa8e2J/wubPvO P+XXqTT5/49DSXPB5L13UK6vyo/I7/aPkPMawuQ2EcEzOa7mQTH15sUIzON/Wo+Q L9DHfabXAtHGN6biqR1ZZvdK9WTvNqrAK8DfFWBVEvg8TKDO3KOD07y2Kni/KCpK 77RVRKmZDAIXCG/NZaDWZ2YyhgAq9zH72/gYc72xPNXbePy9qzaaUwt7gELVri8L 7d/7zmjQzCJ3/xwaChEmAFxab4sl4hNNYZKCr447nfW8ZWaoX0czSgmMwrkCDQRg tXYhARAAt0d7dZLNDlLmXaLqPPHVG8MgPbiEBjuNCm/vmh+fH4EjWT00bS49hH+M NNYoc31eAA2t13UrvwYuGIuK+P7yPd9MPW/FSMrbTRWEbeYP727ByZBsjk28pjGM tPI6cOtLuGyMZKugAwCQU19XTVlScySONZ7gu1giLMpYQTEKHG95V6v/kVH4f3pR /NpPgegQu/cVUqITcLXsIzuzP3iBY2etZ8NLcmNX5TqefiLUh2Sa4kT/6+G52Yye 6FXSiMbZfXtUAZU89Y5HjxtU64BLAB8fxKvIjm03Z42V2i2AsnYLIs58fuCfqlxL CF8qwiWACG9kV+O1sg6LFXWhJHaE0ZJlRPCcECmX+gdI+AGyWg3QnHBXIjxyU/7r DaTFe1wijv5EAUhBVjXrpYo2+38uEWMuAMTFGysS0rWaDbQhm/5GnYyo2UxA+46k NiKO+5iEqgwP8y7RCy9GtazHN/k4BYw19/VPefbelyj0W6tq4Z1dnf0NECREL8vl nhy+GQSlKpThM0EAcemI6w+B+ZyEYHOBlKcRQxjvIC2Wy8rOWaEb+5+CvdJWxbsd dUVi2Pi41ZeQDHNO6Yg78apFx73s2xIjDUSAuTZR0S6D2ytFS6/yryfM14KzF1Nc FUyMhQ722v+S9JgC1hqv50uXcMrMcSiPWcwSJUmvKXpEDf4XmSMAEQEAAYkCNgQY AQgAIBYhBFAQHUimlMMWfvvxfsAaC7R8ZoNGBQJgtXYhAhsMAAoJEMAaC7R8ZoNG QLUQAKvRHH//uzNn+8L8QwsuUSaPUc5TaLNSxgN8614te+nwXn6eGwEgW39xMDme x9K02MFv+R9ivZdQC7L2ziZ0E0wn6oPwn39sQlhS5Fl8VHRPf2eQyZ0GSD/Eiyy7 i/up73jVPlR5J3aDsx3av/rCPTTXYvljAFk94JUgyWJMWpe7D9uEcDEcLc7hTf+x rSdgGpLFK49QF6WwSZ020GgzUUoyRjw+8q3yH6Wydg3UDSnwbFBbdLZIzvT4LNrG KhtjF/D8Bl0JNK/J+V6cm/lzTJBXBu4iX2Hfe7R5aG69qaElTi2MbAXsQgVWYOOF mCYH3fgKYwhkGqtQ6V1UbOijN4/MnTRdRPvghwEg9QNe8z4lIjvovTEXJaihG13n hK7lTCGsWpKrIFXc01VjZ6rs8oIEfIRMyffsi4uH+hbby01ew1fVQTaMI7Bg2BWt Dq+lzCGmggi8VyCQ30MEF3lAvasOM/Fmta3rr9+qh6CCjL4S66uxL5QSsZ5u4PD/ Og2djYPXkChLKbuXCBKboISVm45siI5yB4YnvKOiexkeqaoO/8o9LcV8TVbBPInG avFd/2IGYBzfNIk8+LYxA4zhQSRddtawLyyl5oLpyvAMbn1QBfFPFdtTxuCi0HCR lTXDL/uHZRuV2jHDgaelEzp/139TbR8F/Gv/S9JKNh84SDDy =802W -----END PGP PUBLIC KEY BLOCK----- 07070100000019000081A4000000000000000000000001636E648600000435000000000000000000000000000000000000000C00000000gxr/LICENSEThe MIT License (MIT) Copyright 2018 Collabora Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 0707010000001A000081A4000000000000000000000001636E648600000508000000000000000000000000000000000000000E00000000gxr/README.mdgxr === A GLib OpenXR library. ## Build #### Configure the project ``` $ meson build ``` #### Compile the project ``` $ ninja -C build ``` #### Build the docs ``` meson build -Dapi_doc=true $ ninja -C build gxr-doc ``` ## Run #### Run the demo locally ``` $ ./build/examples/demo/gxr-demo ``` #### Run the tests Run all tests. ``` $ ninja -C build test ``` Don't run tests that require a running XR runtime. ``` meson test -C build/ --no-suite gxr:xr ``` Since meson `0.46` the project name can be omitted from the test suite: ``` meson test -C build/ --no-suite xr ``` ## Code of Conduct Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. We follow the standard freedesktop.org code of conduct, available at <https://www.freedesktop.org/wiki/CodeOfConduct/>, which is based on the [Contributor Covenant](https://www.contributor-covenant.org). Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting: * First-line project contacts: * Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * Christoph Haag <christoph.haag@collabora.com> * freedesktop.org contacts: see most recent list at <https://www.freedesktop.org/wiki/CodeOfConduct/> 0707010000001B000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000800000000gxr/doc0707010000001C000081A4000000000000000000000001636E648600000136000000000000000000000000000000000000001300000000gxr/doc/.gitignore/.libs /html /*.stamp /gxr-decl-list.txt /gxr-decl-list.txt.bak /gxr-decl.txt /gxr-decl.txt.bak /gxr-undeclared.txt /gxr-undocumented.txt /gxr-unused.txt /gxr.args /gxr.hierarchy /gxr.interfaces /gxr.prerequisites /gxr.signals /gxr.types /gtkdoc-check.log /gtkdoc-check.test /gtkdoc-check.trs /test-suite.log 0707010000001D000081A4000000000000000000000001636E648600000612000000000000000000000000000000000000001500000000gxr/doc/gxr-docs.xml<?xml version="1.0"?> <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [ <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> ]> <book id="index"> <bookinfo> <title>Gxr Reference Manual</title> <releaseinfo> <para>This document is the API reference for the gxr library.</para> <para> The latest version of gxr, as well as the latest version of this API reference, is <ulink role="online-location" url="http://gitlab.com/">available online</ulink>. </para> </releaseinfo> </bookinfo> <chapter> <title>gxr</title> <refsect2 id="overview"> <para> gxr is a glib wrapper for OpenVR and OpenXR APIs. </para> <para> gxr abstracts XR API calls. </para> </refsect2> <xi:include href="xml/gxr-action-set.xml"/> <xi:include href="xml/gxr-action.xml"/> <xi:include href="xml/gxr-context.xml"/> <xi:include href="xml/gxr-io.xml"/> <xi:include href="xml/gxr-manifest.xml"/> <xi:include href="xml/graphene-ext.xml"/> <xi:include href="xml/gxr-controller.xml"/> <xi:include href="xml/gxr-device-manager.xml"/> <xi:include href="xml/gxr-device.xml"/> </chapter> <index id="api-index"> <title>API Index</title> <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> </index> <!-- <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> --> </book> 0707010000001E000081A4000000000000000000000001636E648600000433000000000000000000000000000000000000001400000000gxr/doc/meson.buildsubdir('xml') private_headers = [ 'gxr-config.h', 'gxr.h', 'gxr-version.h', 'gxr-context-private.h', ] glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix') glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') docpath = join_paths(gxr_datadir, 'gtk-doc', 'html') gnome.gtkdoc('gxr', main_xml: 'gxr-docs.xml', src_dir: [ src_inc, ], dependencies: [gxr_dep], gobject_typesfile: 'gxr.types', scan_args: [ '--rebuild-types', '--rebuild-sections', '--ignore-headers=' + ' '.join(private_headers), ], mkdb_args: [ '--default-include=gxr.h', '--ignore-files=' + ' '.join(private_headers), ], fixxref_args: [ '--html-dir=@0@'.format(docpath), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'gobject')), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'graphene')), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'gulkan')), ], c_args: '-w', # html_assets: html_images, install: true, check: false, ) 0707010000001F000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000C00000000gxr/doc/xml07070100000020000081A4000000000000000000000001636E64860000015C000000000000000000000000000000000000002200000000gxr/doc/xml/gtkdocentities.ent.in<!ENTITY package "@PACKAGE@"> <!ENTITY package_bugreport "@PACKAGE_BUGREPORT@"> <!ENTITY package_name "@PACKAGE_NAME@"> <!ENTITY package_string "@PACKAGE_STRING@"> <!ENTITY package_tarname "@PACKAGE_TARNAME@"> <!ENTITY package_url "@PACKAGE_URL@"> <!ENTITY package_version "@PACKAGE_VERSION@"> <!ENTITY package_api_version "@PACKAGE_API_VERSION@"> 07070100000021000081A4000000000000000000000001636E648600000239000000000000000000000000000000000000001800000000gxr/doc/xml/meson.buildent_conf = configuration_data() ent_conf.set('PACKAGE', 'gxr') ent_conf.set('PACKAGE_BUGREPORT', 'https://gitlab.freedesktop.org/xrdesktop/gxr') ent_conf.set('PACKAGE_NAME', 'gxr') ent_conf.set('PACKAGE_STRING', 'gxr') ent_conf.set('PACKAGE_TARNAME', 'gxr-' + meson.project_version()) ent_conf.set('PACKAGE_URL', 'https://gitlab.freedesktop.org/xrdesktop/gxr') ent_conf.set('PACKAGE_VERSION', meson.project_version()) ent_conf.set('PACKAGE_API_VERSION', api_version) configure_file(input: 'gtkdocentities.ent.in', output: 'gtkdocentities.ent', configuration: ent_conf) 07070100000022000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000D00000000gxr/examples07070100000023000081A4000000000000000000000001636E648600000F8C000000000000000000000000000000000000001700000000gxr/examples/actions.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include <glib-unix.h> #include <gxr.h> enum { WM_ACTIONSET, SYNTH_ACTIONSET, LAST_ACTIONSET }; typedef struct Example { GMainLoop *loop; GxrAction *haptic; GxrContext *context; GxrManifest *manifest; /* array of action sets */ GxrActionSet *action_sets[LAST_ACTIONSET]; } Example; static gboolean _sigint_cb (gpointer _self) { Example *self = (Example *) _self; g_main_loop_quit (self->loop); return TRUE; } static gboolean _poll_events_cb (gpointer _self) { Example *self = (Example *) _self; if (!gxr_context_begin_frame (self->context)) return FALSE; gxr_context_end_frame (self->context); gxr_context_poll_events (self->context); if (!gxr_action_sets_poll (self->action_sets, 2)) { g_print ("Failed to poll events\n"); return FALSE; } return TRUE; } static void _digital_cb (GxrAction *action, GxrDigitalEvent *event, Example *self) { (void) action; (void) self; g_print ("DIGITAL (%lu): %d | %d\n", gxr_device_get_handle (GXR_DEVICE (event->controller)), event->active, event->state); if (event->changed) { gxr_action_trigger_haptic ( self->haptic, 0.0f, .2f, 160.f, 1.0f, gxr_device_get_handle (GXR_DEVICE (event->controller))); } } static void _hand_pose_cb (GxrAction *action, GxrPoseEvent *event, Example *self) { (void) action; (void) self; g_print ("POSE (%lu): %d | %f %f %f | %f %f %f\n", gxr_device_get_handle (GXR_DEVICE (event->controller)), event->active, graphene_vec3_get_x (&event->velocity), graphene_vec3_get_y (&event->velocity), graphene_vec3_get_z (&event->velocity), graphene_vec3_get_x (&event->angular_velocity), graphene_vec3_get_y (&event->angular_velocity), graphene_vec3_get_z (&event->angular_velocity)); graphene_matrix_print (&event->pose); } static void _cleanup (Example *self) { g_print ("bye\n"); g_main_loop_unref (self->loop); g_object_unref (self->context); for (int i = 0; i < 2; i++) g_object_unref (self->action_sets[i]); g_object_unref (self->haptic); } int main () { Example self = { .loop = g_main_loop_new (NULL, FALSE), .context = gxr_context_new ("Actions Example", 1), }; self.manifest = gxr_manifest_new ("/res/bindings", "actions.json"); if (!self.manifest) { g_print ("Failed to load action bindings!\n"); return 1; } self.action_sets[WM_ACTIONSET] = gxr_action_set_new_from_url (self.context, self.manifest, "/actions/wm"); self.action_sets[SYNTH_ACTIONSET] = gxr_action_set_new_from_url (self.context, self.manifest, "/actions/mouse_synth"); self.haptic = gxr_action_new_from_type_url (self.context, self.action_sets[WM_ACTIONSET], GXR_ACTION_HAPTIC, "/actions/wm/out/haptic"); gxr_action_set_append_action (self.action_sets[WM_ACTIONSET], self.haptic); gxr_action_set_connect (self.action_sets[WM_ACTIONSET], GXR_ACTION_POSE, "/actions/wm/in/hand_pose", (GCallback) _hand_pose_cb, &self); gxr_action_set_connect (self.action_sets[SYNTH_ACTIONSET], GXR_ACTION_DIGITAL, "/actions/mouse_synth/in/left_click", (GCallback) _digital_cb, &self); gxr_context_attach_action_sets (self.context, self.action_sets, 2); g_timeout_add (20, _poll_events_cb, &self); g_unix_signal_add (SIGINT, _sigint_cb, &self); g_main_loop_run (self.loop); _cleanup (&self); return 0; } 07070100000024000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001200000000gxr/examples/cube07070100000025000081A4000000000000000000000001636E64860000319D000000000000000000000000000000000000001900000000gxr/examples/cube/cube.c/* * gxr * Copyright 2021 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include <glib-unix.h> #include <glib.h> #include <gxr.h> #include <stdalign.h> typedef struct { alignas (32) float mvp[2][16]; } UniformBuffer; // clang-format off static const float positions[] = { -1.f, +1.f, +1.f, +1.f, +1.f, +1.f, -1.f, -1.f, +1.f, +1.f, -1.f, +1.f, +1.f, -1.f, -1.f, +1.f, +1.f, +1.f, +1.f, +1.f, -1.f, -1.f, +1.f, +1.f, -1.f, +1.f, -1.f, -1.f, -1.f, +1.f, -1.f, -1.f, -1.f, +1.f, -1.f, -1.f, -1.f, +1.f, -1.f, +1.f, +1.f, -1.f }; // clang-format on #define CUBE_TYPE_EXAMPLE cube_example_get_type () G_DECLARE_FINAL_TYPE (CubeExample, cube_example, CUBE, EXAMPLE, GulkanRenderer) struct _CubeExample { GulkanRenderer parent; GMainLoop *loop; GulkanPipeline *pipeline; GulkanRenderPass *render_pass; GulkanUniformBuffer *uniform_buffer; GulkanDescriptorSet *descriptor_set; GulkanDescriptorPool *descriptor_pool; GulkanVertexBuffer *vb; GulkanCmdBuffer **cmd_buffers; GxrContext *context; guint render_source; guint sigint_signal; gboolean render_background; UniformBuffer ub; }; G_DEFINE_TYPE (CubeExample, cube_example, GULKAN_TYPE_RENDERER) static void _build_cmd_buffers (CubeExample *self); static CubeExample * cube_example_new (void) { return (CubeExample *) g_object_new (CUBE_TYPE_EXAMPLE, 0); } static gboolean _sigint_cb (gpointer _self) { CubeExample *self = (CubeExample *) _self; g_main_loop_quit (self->loop); return TRUE; } static void cube_example_init (CubeExample *self) { self->context = NULL; self->descriptor_pool = NULL; self->loop = g_main_loop_new (NULL, FALSE); self->sigint_signal = g_unix_signal_add (SIGINT, _sigint_cb, self); self->render_background = TRUE; self->cmd_buffers = NULL; } static void cube_example_finalize (GObject *gobject) { CubeExample *self = CUBE_EXAMPLE (gobject); g_source_remove (self->render_source); g_source_remove (self->sigint_signal); GulkanContext *gc = gxr_context_get_gulkan (self->context); GulkanDevice *gd = gulkan_context_get_device (gc); VkDevice device = gulkan_context_get_device_handle (gc); GulkanQueue *queue = gulkan_device_get_graphics_queue (gd); vkDeviceWaitIdle (device); uint32_t swapchain_length = gxr_context_get_swapchain_length (self->context); for (uint32_t i = 0; i < swapchain_length; i++) { gulkan_queue_free_cmd_buffer (queue, self->cmd_buffers[i]); } g_free (self->cmd_buffers); g_object_unref (self->pipeline); g_object_unref (self->uniform_buffer); g_object_unref (self->descriptor_set); g_object_unref (self->descriptor_pool); g_clear_object (&self->vb); g_clear_object (&self->render_pass); g_clear_object (&self->context); g_main_loop_unref (self->loop); G_OBJECT_CLASS (cube_example_parent_class)->finalize (gobject); } static void cube_example_class_init (CubeExampleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cube_example_finalize; } static gboolean _init_pipeline (CubeExample *self) { VkVertexInputBindingDescription *binding_desc = gulkan_vertex_buffer_create_binding_desc (self->vb); VkVertexInputAttributeDescription *attrib_desc = gulkan_vertex_buffer_create_attrib_desc (self->vb); GulkanPipelineConfig config = { .extent = gulkan_renderer_get_extent (GULKAN_RENDERER (self)), .sample_count = VK_SAMPLE_COUNT_1_BIT, .vertex_shader_uri = "/shaders/minimal.vert.spv", .fragment_shader_uri = "/shaders/minimal.frag.spv", .topology = gulkan_vertex_buffer_get_topology (self->vb), .attribs = attrib_desc, .attrib_count = gulkan_vertex_buffer_get_attrib_count (self->vb), .bindings = binding_desc, .binding_count = gulkan_vertex_buffer_get_attrib_count (self->vb), .blend_attachments = (VkPipelineColorBlendAttachmentState[]){ { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }, }, .rasterization_state = &(VkPipelineRasterizationStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_CLOCKWISE, .lineWidth = 1.0f, }, .depth_stencil_state = &(VkPipelineDepthStencilStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, }, .dynamic_viewport = FALSE, .flip_y = TRUE, }; GulkanContext *gulkan = gxr_context_get_gulkan (self->context); self->pipeline = gulkan_pipeline_new (gulkan, self->descriptor_pool, self->render_pass, &config); g_free (binding_desc); g_free (attrib_desc); return TRUE; } static GxrContext * _create_gxr_context () { GSList *instance_ext_list = gulkan_context_get_external_memory_instance_extensions (); GSList *device_ext_list = gulkan_context_get_external_memory_device_extensions (); device_ext_list = g_slist_append (device_ext_list, g_strdup (VK_KHR_MAINTENANCE1_EXTENSION_NAME)); GxrContext *context = gxr_context_new_from_vulkan_extensions (instance_ext_list, device_ext_list, "GXR Cube", 1); g_slist_free_full (instance_ext_list, g_free); g_slist_free_full (device_ext_list, g_free); return context; } static gboolean _init_descriptor_pool (CubeExample *self) { GulkanContext *gulkan = gxr_context_get_gulkan (self->context); VkDescriptorSetLayoutBinding bindings[] = { { .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .pImmutableSamplers = NULL, }, }; self->descriptor_pool = GULKAN_DESCRIPTOR_POOL_NEW (gulkan, bindings, 1); if (!self->descriptor_pool) return FALSE; self->descriptor_set = gulkan_descriptor_pool_create_set (self->descriptor_pool); return TRUE; } static gboolean _init_vertex_buffer (CubeExample *self) { GulkanContext *gc = gxr_context_get_gulkan (self->context); GulkanDevice *device = gulkan_context_get_device (gc); self->vb = gulkan_vertex_buffer_new (device, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); gulkan_vertex_buffer_add_attribute (self->vb, 3, sizeof (positions), 0, (uint8_t *) positions); if (!gulkan_vertex_buffer_upload (self->vb)) return FALSE; if (!self->vb) return FALSE; return TRUE; } static gboolean _init_uniform_buffer (CubeExample *self) { GulkanContext *gc = gxr_context_get_gulkan (self->context); GulkanDevice *gulkan_device = gulkan_context_get_device (gc); /* Create uniform buffer to hold a matrix per eye */ self->uniform_buffer = gulkan_uniform_buffer_new (gulkan_device, sizeof (UniformBuffer)); if (!self->uniform_buffer) return FALSE; return TRUE; } static void _overlay_cb (GxrContext *context, GxrOverlayEvent *event, CubeExample *self) { (void) context; self->render_background = !event->main_session_visible; } static gboolean _init (CubeExample *self) { self->context = _create_gxr_context (); if (!self->context) return FALSE; self->cmd_buffers = g_malloc (sizeof (GulkanCmdBuffer *) * gxr_context_get_swapchain_length (self->context)); gulkan_renderer_set_context (GULKAN_RENDERER (self), gxr_context_get_gulkan (self->context)); VkExtent2D extent = gxr_context_get_swapchain_extent (self->context, 0); gulkan_renderer_set_extent (GULKAN_RENDERER (self), extent); if (!gxr_context_init_framebuffers (self->context, extent, VK_SAMPLE_COUNT_1_BIT, &self->render_pass)) return FALSE; if (!_init_vertex_buffer (self)) return FALSE; if (!_init_uniform_buffer (self)) return FALSE; if (!_init_descriptor_pool (self)) return FALSE; gulkan_descriptor_set_update_buffer (self->descriptor_set, 0, self->uniform_buffer); if (!_init_pipeline (self)) return FALSE; _build_cmd_buffers (self); g_signal_connect (self->context, "overlay-event", (GCallback) _overlay_cb, self); return TRUE; } static void _update_transformation (CubeExample *self) { graphene_matrix_t model; graphene_matrix_init_identity (&model); int64_t t = gulkan_renderer_get_msec_since_start (GULKAN_RENDERER (self)); t /= 5; graphene_matrix_rotate_x (&model, 45.0f + (0.25f * (float) t)); graphene_matrix_rotate_y (&model, 45.0f - (0.5f * (float) t)); graphene_matrix_rotate_z (&model, 10.0f + (0.15f * (float) t)); graphene_point3d_t pos = (graphene_point3d_t){0.0f, 0.0f, -6.0f}; graphene_matrix_translate (&model, &pos); for (uint32_t eye = 0; eye < 2; eye++) { graphene_matrix_t view; graphene_matrix_t projection; gxr_context_get_view (self->context, eye, &view); gxr_context_get_projection (self->context, eye, 0.05f, 100.0f, &projection); graphene_matrix_t model_view; graphene_matrix_multiply (&model, &view, &model_view); graphene_matrix_t model_view_projection; graphene_matrix_multiply (&model_view, &projection, &model_view_projection); graphene_matrix_to_float (&model_view_projection, self->ub.mvp[eye]); } gulkan_uniform_buffer_update (self->uniform_buffer, (gpointer) &self->ub); } static void _render_stereo (CubeExample *self, VkCommandBuffer cmd_buffer, uint32_t swapchain_image_id) { VkExtent2D extent = gulkan_renderer_get_extent (GULKAN_RENDERER (self)); VkClearColorValue clear_color = {.float32 = {0, 0, 0, 0}}; if (self->render_background) clear_color = (VkClearColorValue){ .float32 = {0.1f, 0.1f, 0.7f, 1.0f}, }; GulkanFrameBuffer *framebuffer = gxr_context_get_framebuffer_at (self->context, swapchain_image_id); gulkan_render_pass_begin (self->render_pass, extent, clear_color, framebuffer, cmd_buffer); gulkan_pipeline_bind (self->pipeline, cmd_buffer); VkPipelineLayout layout = gulkan_descriptor_pool_get_pipeline_layout (self->descriptor_pool); gulkan_descriptor_set_bind (self->descriptor_set, layout, cmd_buffer); gulkan_vertex_buffer_bind_with_offsets (self->vb, cmd_buffer); vkCmdDraw (cmd_buffer, 14, 1, 0, 0); vkCmdEndRenderPass (cmd_buffer); } static void _build_cmd_buffers (CubeExample *self) { GulkanContext *gc = gxr_context_get_gulkan (self->context); GulkanDevice *device = gulkan_context_get_device (gc); GulkanQueue *queue = gulkan_device_get_graphics_queue (device); uint32_t swapchain_length = gxr_context_get_swapchain_length (self->context); for (uint32_t i = 0; i < swapchain_length; i++) { self->cmd_buffers[i] = gulkan_queue_request_cmd_buffer (queue); gulkan_cmd_buffer_begin (self->cmd_buffers[i], 0); VkCommandBuffer cmd_handle = gulkan_cmd_buffer_get_handle (self ->cmd_buffers [i]); _render_stereo (self, cmd_handle, i); gulkan_cmd_buffer_end (self->cmd_buffers[i]); } } static gboolean _iterate_cb (gpointer _self) { CubeExample *self = (CubeExample *) _self; GulkanContext *gc = gxr_context_get_gulkan (self->context); GulkanDevice *device = gulkan_context_get_device (gc); GulkanQueue *queue = gulkan_device_get_graphics_queue (device); gxr_context_poll_events (self->context); if (!gxr_context_begin_frame (self->context)) return FALSE; _update_transformation (self); uint32_t i = gxr_context_get_buffer_index (self->context); gulkan_queue_submit (queue, self->cmd_buffers[i]); gxr_context_end_frame (self->context); return TRUE; } static void _run (CubeExample *self) { self->render_source = g_timeout_add (1, _iterate_cb, self); g_main_loop_run (self->loop); } int main (void) { CubeExample *self = cube_example_new (); if (!_init (self)) return 1; _run (self); g_object_unref (self); return 0; } 07070100000026000081A4000000000000000000000001636E6486000000D2000000000000000000000000000000000000001E00000000gxr/examples/cube/meson.buildsubdir('shaders') cube_sources = [ 'cube.c', cube_shader_resources, ] executable( 'cube', cube_sources, dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) 07070100000027000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001A00000000gxr/examples/cube/shaders07070100000028000081A4000000000000000000000001636E64860000027C000000000000000000000000000000000000002600000000gxr/examples/cube/shaders/meson.buildshaders = ['minimal.vert', 'minimal.frag'] glslc = find_program('glslc', required : false) if glslc.found() # Prefer shaderc cmd = [glslc] else # Use glslang as fallback glslang = find_program('glslangValidator') if glslang.found() cmd = [glslang, '-V'] endif endif foreach s : shaders r = run_command(cmd + ['-o', s + '.spv', s], check: true) if r.returncode() != 0 message('Could not compile shaders:') message(r.stderr().strip()) message(r.stdout().strip()) endif endforeach cube_shader_resources = gnome.compile_resources( 'cube_shader_resources', 'shaders.gresource.xml', source_dir : '.') 07070100000029000081A4000000000000000000000001636E648600000125000000000000000000000000000000000000002700000000gxr/examples/cube/shaders/minimal.frag/* * gulkan * Copyright 2020 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #version 460 core layout (location = 0) in vec4 in_color; layout (location = 0) out vec4 out_color; void main () { out_color = vec4 (in_color); } 0707010000002A000081A4000000000000000000000001636E6486000001C2000000000000000000000000000000000000002700000000gxr/examples/cube/shaders/minimal.vert/* * gulkan * Copyright 2020 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #version 460 core #extension GL_EXT_multiview : enable layout (std140, set = 0, binding = 0) uniform block { uniform mat4 mvp[2]; }; layout (location = 0) in vec4 in_position; layout (location = 0) out vec4 color; void main () { color = in_position; gl_Position = mvp[gl_ViewIndex] * in_position; } 0707010000002B000081A4000000000000000000000001636E6486000000B5000000000000000000000000000000000000003000000000gxr/examples/cube/shaders/shaders.gresource.xml<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/shaders"> <file>minimal.frag.spv</file> <file>minimal.vert.spv</file> </gresource> </gresources> 0707010000002C000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001200000000gxr/examples/demo0707010000002D000081A4000000000000000000000001636E648600004ABB000000000000000000000000000000000000001D00000000gxr/examples/demo/gxr-demo.c/* * xrdesktop * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include <glib-unix.h> #include <glib.h> #include <gxr.h> #include "scene-controller.h" #include "gxr-device-manager.h" #include "scene-background.h" #include "scene-cube.h" #include "scene-pointer.h" #if defined(RENDERDOC) #include "renderdoc_app.h" #include <dlfcn.h> static RENDERDOC_API_1_1_2 *rdoc_api = NULL; static void _init_renderdoc () { if (rdoc_api != NULL) return; void *mod = dlopen ("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD); if (mod) { pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI) dlsym (mod, "RENDERDOC_GetAPI"); int ret = RENDERDOC_GetAPI (eRENDERDOC_API_Version_1_1_2, (void **) &rdoc_api); if (ret != 1) g_debug ("Failed to init renderdoc"); } } #endif typedef struct { graphene_point3d_t position; graphene_point_t uv; } SceneVertex; #define GXR_TYPE_DEMO gxr_demo_get_type () G_DECLARE_FINAL_TYPE (GxrDemo, gxr_demo, GXR, DEMO, GulkanRenderer) struct _GxrDemo { GulkanRenderer parent; GMainLoop *loop; GxrController *cube_grabbed; graphene_matrix_t pointer_pose; GxrContext *context; guint render_source; bool shutdown; bool rendering; bool framecycle; GxrActionSet *actionset; guint poll_input_source_id; guint poll_runtime_event_source_id; gulong device_activate_signal; gulong device_deactivate_signal; guint sigint_signal; graphene_matrix_t mat_view[2]; graphene_matrix_t mat_projection[2]; float near; float far; SceneBackground *background; gboolean render_background; SceneCube *cube; // GxrController -> SceneController GHashTable *controller_map; VkSampleCountFlagBits sample_count; GulkanDescriptorPool *descriptor_pool; GulkanPipeline *line_pipeline; GxrManifest *manifest; GulkanRenderPass *render_pass; }; G_DEFINE_TYPE (GxrDemo, gxr_demo, GULKAN_TYPE_RENDERER) static GxrDemo * gxr_demo_new (void) { return (GxrDemo *) g_object_new (GXR_TYPE_DEMO, 0); } static gboolean _sigint_cb (gpointer _self) { GxrDemo *self = (GxrDemo *) _self; g_main_loop_quit (self->loop); return TRUE; } static void _finalize (GObject *gobject) { GxrDemo *self = GXR_DEMO (gobject); self->shutdown = true; if (self->render_source > 0) g_source_remove (self->render_source); self->render_source = 0; if (self->poll_runtime_event_source_id > 0) g_source_remove (self->poll_runtime_event_source_id); self->poll_runtime_event_source_id = 0; if (self->poll_input_source_id > 0) g_source_remove (self->poll_input_source_id); self->poll_input_source_id = 0; GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); g_signal_handler_disconnect (dm, self->device_activate_signal); g_signal_handler_disconnect (dm, self->device_deactivate_signal); g_source_remove (self->sigint_signal); g_clear_object (&self->actionset); g_clear_object (&self->manifest); g_clear_object (&self->background); g_clear_object (&self->cube); GulkanContext *gc = gxr_context_get_gulkan (self->context); VkDevice device = gulkan_context_get_device_handle (gc); if (device != VK_NULL_HANDLE) vkDeviceWaitIdle (device); g_object_unref (self->descriptor_pool); g_object_unref (self->line_pipeline); g_clear_object (&self->render_pass); g_clear_object (&self->context); g_hash_table_unref (self->controller_map); g_print ("Cleaned up!\n"); } static void gxr_demo_class_init (GxrDemoClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = _finalize; } static GxrContext * _create_gxr_context () { GSList *instance_ext_list = gulkan_context_get_external_memory_instance_extensions (); GSList *device_ext_list = gulkan_context_get_external_memory_device_extensions (); device_ext_list = g_slist_append (device_ext_list, g_strdup (VK_KHR_MAINTENANCE1_EXTENSION_NAME)); GxrContext *context = gxr_context_new_from_vulkan_extensions (instance_ext_list, device_ext_list, "gxr Demo", 1); g_slist_free_full (instance_ext_list, g_free); g_slist_free_full (device_ext_list, g_free); return context; } static void _render_pointers (GxrDemo *self, VkCommandBuffer cmd_buffer, VkPipelineLayout pipeline_layout, graphene_matrix_t *vp) { GList *scs = g_hash_table_get_values (self->controller_map); for (GList *l = scs; l; l = l->next) { SceneController *sc = SCENE_CONTROLLER (l->data); if (!gxr_controller_is_pointer_pose_valid ( scene_controller_get_controller (sc))) { /* g_print ("Pointer pose not valid: %lu\n", scene_controller_get_handle (controller)); */ continue; } ScenePointer *pointer = SCENE_POINTER (scene_controller_get_pointer (sc)); scene_pointer_render (pointer, self->line_pipeline, pipeline_layout, cmd_buffer, vp); } } static void _cube_set_position (GxrDemo *example) { if (!example->cube_grabbed) return; graphene_matrix_t pointer_pose; gxr_controller_get_pointer_pose (GXR_CONTROLLER (example->cube_grabbed), &pointer_pose); SceneController *sc = g_hash_table_lookup (example->controller_map, example->cube_grabbed); ScenePointer *pointer = scene_controller_get_pointer (sc); float distance = scene_pointer_get_length (pointer); graphene_point3d_t p = {.x = .0f, .y = .0f, .z = -distance}; graphene_matrix_transform_point3d (&pointer_pose, &p, &p); scene_cube_override_position (example->cube, &p); } static void _render (GxrDemo *self, VkCommandBuffer cmd_buffer) { graphene_matrix_t vp[2]; for (uint32_t eye = 0; eye < 2; eye++) graphene_matrix_multiply (&self->mat_view[eye], &self->mat_projection[eye], &vp[eye]); VkPipelineLayout layout = gulkan_descriptor_pool_get_pipeline_layout (self->descriptor_pool); if (self->render_background) scene_background_render (self->background, self->line_pipeline, layout, cmd_buffer, vp); _render_pointers (self, cmd_buffer, layout, vp); _cube_set_position (self); scene_cube_render (self->cube, cmd_buffer, self->mat_view, self->mat_projection); } static void _render_stereo (GxrDemo *self, VkCommandBuffer cmd_buffer) { VkExtent2D extent = gulkan_renderer_get_extent (GULKAN_RENDERER (self)); VkClearColorValue black = { .float32 = {0.0f, 0.0f, 0.0f, .0f}, }; GulkanFrameBuffer *framebuffer = gxr_context_get_acquired_framebuffer (self->context); if (!GULKAN_IS_FRAME_BUFFER (framebuffer)) { g_printerr ("framebuffer invalid\n"); } gulkan_render_pass_begin (self->render_pass, extent, black, framebuffer, cmd_buffer); _render (self, cmd_buffer); vkCmdEndRenderPass (cmd_buffer); } static void _draw (GxrDemo *self) { #if defined(RENDERDOC) if (rdoc_api) rdoc_api->StartFrameCapture (NULL, NULL); else _init_renderdoc (); #endif GulkanContext *gc = gxr_context_get_gulkan (self->context); GulkanDevice *device = gulkan_context_get_device (gc); GulkanQueue *queue = gulkan_device_get_graphics_queue (device); GulkanCmdBuffer *cmd_buffer = gulkan_queue_request_cmd_buffer (queue); gulkan_cmd_buffer_begin_one_time (cmd_buffer); VkCommandBuffer cmd_handle = gulkan_cmd_buffer_get_handle (cmd_buffer); _render_stereo (self, cmd_handle); gulkan_queue_end_submit (queue, cmd_buffer); gulkan_queue_free_cmd_buffer (queue, cmd_buffer); #if defined(RENDERDOC) if (rdoc_api) rdoc_api->EndFrameCapture (NULL, NULL); #endif } static gboolean _iterate_cb (gpointer _self) { GxrDemo *self = (GxrDemo *) _self; if (self->shutdown) return FALSE; if (!self->framecycle) return TRUE; if (!gxr_context_begin_frame (self->context)) return FALSE; for (uint32_t eye = 0; eye < 2; eye++) { gxr_context_get_view (self->context, eye, &self->mat_view[eye]); gxr_context_get_projection (self->context, eye, self->near, self->far, &self->mat_projection[eye]); } if (self->rendering) _draw (self); gxr_context_end_frame (self->context); return TRUE; } static void _device_activate_cb (GxrDeviceManager *dm, GxrController *controller, gpointer _self) { (void) dm; GxrDemo *self = _self; g_print ("Controller %lu activated.\n", gxr_device_get_handle (GXR_DEVICE (controller))); GulkanContext *client = gxr_context_get_gulkan (self->context); SceneController *sc = scene_controller_new (controller, self->context); GulkanDescriptorSet *descriptor_set = gulkan_descriptor_pool_create_set (self->descriptor_pool); ScenePointer *pointer = scene_pointer_new (client, descriptor_set); scene_controller_set_pointer (sc, pointer); g_hash_table_insert (self->controller_map, controller, sc); } static void _device_deactivate_cb (GxrDeviceManager *dm, GxrController *controller, gpointer _self) { (void) dm; GxrDemo *self = _self; g_print ("Controller deactivated..\n"); SceneController *sc = g_hash_table_lookup (self->controller_map, controller); g_hash_table_remove (self->controller_map, controller); g_object_unref (sc); } static void _action_quit_cb (GxrAction *action, GxrDigitalEvent *event, GxrDemo *self) { (void) action; if (event->active && event->changed && event->state) { g_print ("Quitting example\n"); g_main_loop_quit (self->loop); } } static void _action_grab_cb (GxrAction *action, GxrDigitalEvent *event, GxrDemo *self) { (void) action; if (event->active && event->changed && event->state) { if (self->cube_grabbed == NULL) { g_print ("Grabbing cube\n"); self->cube_grabbed = event->controller; } } else if (event->active && event->changed && !event->state) { g_print ("Ungrabbing cube\n"); self->cube_grabbed = NULL; } } static GxrActionSet * _create_wm_action_set (GxrDemo *self) { GxrActionSet *set = gxr_action_set_new_from_url (self->context, self->manifest, "/actions/wm"); gxr_action_set_connect_digital_from_float (set, "/actions/wm/in/grab_window", 0.25f, "/actions/wm/out/haptic", (GCallback) _action_grab_cb, self); gxr_action_set_connect (set, GXR_ACTION_DIGITAL, "/actions/wm/in/menu", (GCallback) _action_quit_cb, self); GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); gxr_device_manager_connect_pose_actions (dm, set, "/actions/wm/in/hand_pose", NULL); return set; } static gboolean _poll_runtime_events (GxrDemo *self) { if (!self->context) return FALSE; gxr_context_poll_events (self->context); return TRUE; } static gboolean _poll_input_events (GxrDemo *self) { if (!self->framecycle) return TRUE; if (!self->context) { g_printerr ("Error polling events: No Gxr Context\n"); self->poll_input_source_id = 0; return FALSE; } if (self->actionset == NULL) { g_printerr ("Error: Action Set not created!\n"); return TRUE; } if (!gxr_action_sets_poll (&self->actionset, 1)) { g_printerr ("Error polling actions\n"); self->poll_input_source_id = 0; return FALSE; } return TRUE; } static void _init_input_callbacks (GxrDemo *self) { self->manifest = gxr_manifest_new ("/res/bindings", "actions.json"); if (!self->manifest) { g_print ("Failed to load action bindings!\n"); return; } self->actionset = _create_wm_action_set (self); gxr_context_attach_action_sets (self->context, &self->actionset, 1); self->poll_input_source_id = g_timeout_add (3, (GSourceFunc) _poll_input_events, self); } static void _state_change_cb (GxrContext *context, GxrStateChangeEvent *event, GxrDemo *self) { (void) context; switch (event->state_change) { case GXR_STATE_SHUTDOWN: g_main_loop_quit (self->loop); break; case GXR_STATE_FRAMECYCLE_START: self->framecycle = TRUE; break; case GXR_STATE_FRAMECYCLE_STOP: self->framecycle = FALSE; break; case GXR_STATE_RENDERING_START: self->rendering = TRUE; break; case GXR_STATE_RENDERING_STOP: self->rendering = FALSE; break; } } static void _overlay_cb (GxrContext *context, GxrOverlayEvent *event, GxrDemo *self) { (void) context; self->render_background = !event->main_session_visible; } static gboolean _init_framebuffers (GxrDemo *self) { VkExtent2D extent = gxr_context_get_swapchain_extent (self->context, 0); gulkan_renderer_set_extent (GULKAN_RENDERER (self), extent); if (!gxr_context_init_framebuffers (self->context, extent, self->sample_count, &self->render_pass)) return FALSE; return TRUE; } /* Create a descriptor set layout compatible with all shaders. */ static gboolean _init_descriptor_pool (GxrDemo *self) { VkDescriptorSetLayoutBinding bindings[] = { // mvp buffer { .binding = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, }, }; GulkanContext *gulkan = gxr_context_get_gulkan (self->context); self->descriptor_pool = GULKAN_DESCRIPTOR_POOL_NEW (gulkan, bindings, 8); if (!self->descriptor_pool) return FALSE; return TRUE; } static gboolean _init_graphics_pipelines (GxrDemo *self) { GulkanPipelineConfig config = { .extent = gulkan_renderer_get_extent (GULKAN_RENDERER (self)), .sample_count = VK_SAMPLE_COUNT_1_BIT, .vertex_shader_uri = "/shaders/pointer.vert.spv", .fragment_shader_uri = "/shaders/pointer.frag.spv", .topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, .attribs = (VkVertexInputAttributeDescription []) { {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0}, {1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof (float) * 3}, }, .attrib_count = 2, .bindings = &(VkVertexInputBindingDescription) { .binding = 0, .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, .stride = sizeof (float) * 6, }, .binding_count = 1, .blend_attachments = &(VkPipelineColorBlendAttachmentState) { .blendEnable = VK_FALSE, .colorWriteMask = 0xf, }, .rasterization_state = &(VkPipelineRasterizationStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .polygonMode = VK_POLYGON_MODE_LINE, .cullMode = VK_CULL_MODE_BACK_BIT, .lineWidth = 2.0f, }, .depth_stencil_state = &(VkPipelineDepthStencilStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, }, .flip_y = TRUE, }; GulkanContext *gulkan = gulkan_renderer_get_context (GULKAN_RENDERER (self)); self->line_pipeline = gulkan_pipeline_new (gulkan, self->descriptor_pool, self->render_pass, &config); return TRUE; } static gboolean _init_vulkan (GxrDemo *self) { GulkanContext *gulkan = gxr_context_get_gulkan (self->context); gulkan_renderer_set_context (GULKAN_RENDERER (self), gulkan); self->sample_count = VK_SAMPLE_COUNT_1_BIT; if (!_init_framebuffers (self)) return FALSE; if (!_init_descriptor_pool (self)) return FALSE; if (!_init_graphics_pipelines (self)) return FALSE; self->cube = scene_cube_new (gulkan, GULKAN_RENDERER (self), self->render_pass, self->sample_count); GulkanDescriptorSet *descriptor_set = gulkan_descriptor_pool_create_set (self->descriptor_pool); self->background = scene_background_new (gulkan, descriptor_set); return TRUE; } static void gxr_demo_init (GxrDemo *self) { self->shutdown = false; self->poll_runtime_event_source_id = 0; self->poll_input_source_id = 0; self->actionset = NULL; self->context = NULL; self->near = 0.05f; self->far = 100.0f; self->background = NULL; self->cube_grabbed = NULL; self->device_activate_signal = 0; self->device_deactivate_signal = 0; self->manifest = NULL; graphene_matrix_init_identity (&self->pointer_pose); self->loop = g_main_loop_new (NULL, FALSE); self->render_source = g_timeout_add (1, _iterate_cb, self); self->sigint_signal = g_unix_signal_add (SIGINT, _sigint_cb, self); self->context = _create_gxr_context (); g_assert (self->context); self->controller_map = g_hash_table_new (g_direct_hash, g_direct_equal); GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); self->device_activate_signal = g_signal_connect (dm, "device-activate-event", (GCallback) _device_activate_cb, self); self->device_deactivate_signal = g_signal_connect (dm, "device-deactivate-event", (GCallback) _device_deactivate_cb, self); graphene_vec4_t position; graphene_vec4_init (&position, 0, 0, 0, 1); graphene_vec4_t color; graphene_vec4_init (&color, .078f, .471f, .675f, 1); self->render_background = TRUE; g_assert (_init_vulkan (self)); _init_input_callbacks (self); self->poll_runtime_event_source_id = g_timeout_add (20, (GSourceFunc) _poll_runtime_events, self); g_signal_connect (self->context, "state-change-event", (GCallback) _state_change_cb, self); g_signal_connect (self->context, "overlay-event", (GCallback) _overlay_cb, self); } int main (void) { GxrDemo *demo = gxr_demo_new (); g_main_loop_run (demo->loop); g_object_unref (demo); return 0; } 0707010000002E000081A4000000000000000000000001636E64860000022F000000000000000000000000000000000000001E00000000gxr/examples/demo/meson.buildsubdir('shaders') cube_sources = [ 'gxr-demo.c', 'scene-background.c', 'scene-object.c', 'scene-pointer.c', 'scene-cube.c', 'scene-controller.c', demo_shader_resources, test_resources ] deps = gxr_deps c_args = [] renderdoc = get_option('cube_renderdoc') if renderdoc c_args += '-DRENDERDOC' dl_dep = meson.get_compiler('c').find_library('dl', required : true) deps += dl_dep endif executable( 'gxr-demo', cube_sources, c_args: c_args, dependencies: deps, link_with: gxr_lib, include_directories: gxr_inc, install: true) 0707010000002F000081A4000000000000000000000001636E648600007112000000000000000000000000000000000000002200000000gxr/examples/demo/renderdoc_app.h// clang-format off /****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2020 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #pragma once ////////////////////////////////////////////////////////////////////////////////////////////////// // // Documentation for the API is available at https://renderdoc.org/docs/in_application_api.html // #if !defined(RENDERDOC_NO_STDINT) #include <stdint.h> #endif #if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) #define RENDERDOC_CC __cdecl #elif defined(__linux__) #define RENDERDOC_CC #elif defined(__APPLE__) #define RENDERDOC_CC #else #error "Unknown platform" #endif #ifdef __cplusplus extern "C" { #endif ////////////////////////////////////////////////////////////////////////////////////////////////// // Constants not used directly in below API // This is a GUID/magic value used for when applications pass a path where shader debug // information can be found to match up with a stripped shader. // the define can be used like so: const GUID RENDERDOC_ShaderDebugMagicValue = // RENDERDOC_ShaderDebugMagicValue_value #define RENDERDOC_ShaderDebugMagicValue_struct \ { \ 0xeab25520, 0x6670, 0x4865, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \ } // as an alternative when you want a byte array (assuming x86 endianness): #define RENDERDOC_ShaderDebugMagicValue_bytearray \ { \ 0x20, 0x55, 0xb2, 0xea, 0x70, 0x66, 0x65, 0x48, 0x84, 0x29, 0x6c, 0x8, 0x51, 0x54, 0x00, 0xff \ } // truncated version when only a uint64_t is available (e.g. Vulkan tags): #define RENDERDOC_ShaderDebugMagicValue_truncated 0x48656670eab25520ULL ////////////////////////////////////////////////////////////////////////////////////////////////// // RenderDoc capture options // typedef enum RENDERDOC_CaptureOption { // Allow the application to enable vsync // // Default - enabled // // 1 - The application can enable or disable vsync at will // 0 - vsync is force disabled eRENDERDOC_Option_AllowVSync = 0, // Allow the application to enable fullscreen // // Default - enabled // // 1 - The application can enable or disable fullscreen at will // 0 - fullscreen is force disabled eRENDERDOC_Option_AllowFullscreen = 1, // Record API debugging events and messages // // Default - disabled // // 1 - Enable built-in API debugging features and records the results into // the capture, which is matched up with events on replay // 0 - no API debugging is forcibly enabled eRENDERDOC_Option_APIValidation = 2, eRENDERDOC_Option_DebugDeviceMode = 2, // deprecated name of this enum // Capture CPU callstacks for API events // // Default - disabled // // 1 - Enables capturing of callstacks // 0 - no callstacks are captured eRENDERDOC_Option_CaptureCallstacks = 3, // When capturing CPU callstacks, only capture them from drawcalls. // This option does nothing without the above option being enabled // // Default - disabled // // 1 - Only captures callstacks for drawcall type API events. // Ignored if CaptureCallstacks is disabled // 0 - Callstacks, if enabled, are captured for every event. eRENDERDOC_Option_CaptureCallstacksOnlyDraws = 4, // Specify a delay in seconds to wait for a debugger to attach, after // creating or injecting into a process, before continuing to allow it to run. // // 0 indicates no delay, and the process will run immediately after injection // // Default - 0 seconds // eRENDERDOC_Option_DelayForDebugger = 5, // Verify buffer access. This includes checking the memory returned by a Map() call to // detect any out-of-bounds modification, as well as initialising buffers with undefined contents // to a marker value to catch use of uninitialised memory. // // NOTE: This option is only valid for OpenGL and D3D11. Explicit APIs such as D3D12 and Vulkan do // not do the same kind of interception & checking and undefined contents are really undefined. // // Default - disabled // // 1 - Verify buffer access // 0 - No verification is performed, and overwriting bounds may cause crashes or corruption in // RenderDoc. eRENDERDOC_Option_VerifyBufferAccess = 6, // The old name for eRENDERDOC_Option_VerifyBufferAccess was eRENDERDOC_Option_VerifyMapWrites. // This option now controls the filling of uninitialised buffers with 0xdddddddd which was // previously always enabled eRENDERDOC_Option_VerifyMapWrites = eRENDERDOC_Option_VerifyBufferAccess, // Hooks any system API calls that create child processes, and injects // RenderDoc into them recursively with the same options. // // Default - disabled // // 1 - Hooks into spawned child processes // 0 - Child processes are not hooked by RenderDoc eRENDERDOC_Option_HookIntoChildren = 7, // By default RenderDoc only includes resources in the final capture necessary // for that frame, this allows you to override that behaviour. // // Default - disabled // // 1 - all live resources at the time of capture are included in the capture // and available for inspection // 0 - only the resources referenced by the captured frame are included eRENDERDOC_Option_RefAllResources = 8, // **NOTE**: As of RenderDoc v1.1 this option has been deprecated. Setting or // getting it will be ignored, to allow compatibility with older versions. // In v1.1 the option acts as if it's always enabled. // // By default RenderDoc skips saving initial states for resources where the // previous contents don't appear to be used, assuming that writes before // reads indicate previous contents aren't used. // // Default - disabled // // 1 - initial contents at the start of each captured frame are saved, even if // they are later overwritten or cleared before being used. // 0 - unless a read is detected, initial contents will not be saved and will // appear as black or empty data. eRENDERDOC_Option_SaveAllInitials = 9, // In APIs that allow for the recording of command lists to be replayed later, // RenderDoc may choose to not capture command lists before a frame capture is // triggered, to reduce overheads. This means any command lists recorded once // and replayed many times will not be available and may cause a failure to // capture. // // NOTE: This is only true for APIs where multithreading is difficult or // discouraged. Newer APIs like Vulkan and D3D12 will ignore this option // and always capture all command lists since the API is heavily oriented // around it and the overheads have been reduced by API design. // // 1 - All command lists are captured from the start of the application // 0 - Command lists are only captured if their recording begins during // the period when a frame capture is in progress. eRENDERDOC_Option_CaptureAllCmdLists = 10, // Mute API debugging output when the API validation mode option is enabled // // Default - enabled // // 1 - Mute any API debug messages from being displayed or passed through // 0 - API debugging is displayed as normal eRENDERDOC_Option_DebugOutputMute = 11, // Option to allow vendor extensions to be used even when they may be // incompatible with RenderDoc and cause corrupted replays or crashes. // // Default - inactive // // No values are documented, this option should only be used when absolutely // necessary as directed by a RenderDoc developer. eRENDERDOC_Option_AllowUnsupportedVendorExtensions = 12, } RENDERDOC_CaptureOption; // Sets an option that controls how RenderDoc behaves on capture. // // Returns 1 if the option and value are valid // Returns 0 if either is invalid and the option is unchanged typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionU32)(RENDERDOC_CaptureOption opt, uint32_t val); typedef int(RENDERDOC_CC *pRENDERDOC_SetCaptureOptionF32)(RENDERDOC_CaptureOption opt, float val); // Gets the current value of an option as a uint32_t // // If the option is invalid, 0xffffffff is returned typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionU32)(RENDERDOC_CaptureOption opt); // Gets the current value of an option as a float // // If the option is invalid, -FLT_MAX is returned typedef float(RENDERDOC_CC *pRENDERDOC_GetCaptureOptionF32)(RENDERDOC_CaptureOption opt); typedef enum RENDERDOC_InputButton { // '0' - '9' matches ASCII values eRENDERDOC_Key_0 = 0x30, eRENDERDOC_Key_1 = 0x31, eRENDERDOC_Key_2 = 0x32, eRENDERDOC_Key_3 = 0x33, eRENDERDOC_Key_4 = 0x34, eRENDERDOC_Key_5 = 0x35, eRENDERDOC_Key_6 = 0x36, eRENDERDOC_Key_7 = 0x37, eRENDERDOC_Key_8 = 0x38, eRENDERDOC_Key_9 = 0x39, // 'A' - 'Z' matches ASCII values eRENDERDOC_Key_A = 0x41, eRENDERDOC_Key_B = 0x42, eRENDERDOC_Key_C = 0x43, eRENDERDOC_Key_D = 0x44, eRENDERDOC_Key_E = 0x45, eRENDERDOC_Key_F = 0x46, eRENDERDOC_Key_G = 0x47, eRENDERDOC_Key_H = 0x48, eRENDERDOC_Key_I = 0x49, eRENDERDOC_Key_J = 0x4A, eRENDERDOC_Key_K = 0x4B, eRENDERDOC_Key_L = 0x4C, eRENDERDOC_Key_M = 0x4D, eRENDERDOC_Key_N = 0x4E, eRENDERDOC_Key_O = 0x4F, eRENDERDOC_Key_P = 0x50, eRENDERDOC_Key_Q = 0x51, eRENDERDOC_Key_R = 0x52, eRENDERDOC_Key_S = 0x53, eRENDERDOC_Key_T = 0x54, eRENDERDOC_Key_U = 0x55, eRENDERDOC_Key_V = 0x56, eRENDERDOC_Key_W = 0x57, eRENDERDOC_Key_X = 0x58, eRENDERDOC_Key_Y = 0x59, eRENDERDOC_Key_Z = 0x5A, // leave the rest of the ASCII range free // in case we want to use it later eRENDERDOC_Key_NonPrintable = 0x100, eRENDERDOC_Key_Divide, eRENDERDOC_Key_Multiply, eRENDERDOC_Key_Subtract, eRENDERDOC_Key_Plus, eRENDERDOC_Key_F1, eRENDERDOC_Key_F2, eRENDERDOC_Key_F3, eRENDERDOC_Key_F4, eRENDERDOC_Key_F5, eRENDERDOC_Key_F6, eRENDERDOC_Key_F7, eRENDERDOC_Key_F8, eRENDERDOC_Key_F9, eRENDERDOC_Key_F10, eRENDERDOC_Key_F11, eRENDERDOC_Key_F12, eRENDERDOC_Key_Home, eRENDERDOC_Key_End, eRENDERDOC_Key_Insert, eRENDERDOC_Key_Delete, eRENDERDOC_Key_PageUp, eRENDERDOC_Key_PageDn, eRENDERDOC_Key_Backspace, eRENDERDOC_Key_Tab, eRENDERDOC_Key_PrtScrn, eRENDERDOC_Key_Pause, eRENDERDOC_Key_Max, } RENDERDOC_InputButton; // Sets which key or keys can be used to toggle focus between multiple windows // // If keys is NULL or num is 0, toggle keys will be disabled typedef void(RENDERDOC_CC *pRENDERDOC_SetFocusToggleKeys)(RENDERDOC_InputButton *keys, int num); // Sets which key or keys can be used to capture the next frame // // If keys is NULL or num is 0, captures keys will be disabled typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(RENDERDOC_InputButton *keys, int num); typedef enum RENDERDOC_OverlayBits { // This single bit controls whether the overlay is enabled or disabled globally eRENDERDOC_Overlay_Enabled = 0x1, // Show the average framerate over several seconds as well as min/max eRENDERDOC_Overlay_FrameRate = 0x2, // Show the current frame number eRENDERDOC_Overlay_FrameNumber = 0x4, // Show a list of recent captures, and how many captures have been made eRENDERDOC_Overlay_CaptureList = 0x8, // Default values for the overlay mask eRENDERDOC_Overlay_Default = (eRENDERDOC_Overlay_Enabled | eRENDERDOC_Overlay_FrameRate | eRENDERDOC_Overlay_FrameNumber | eRENDERDOC_Overlay_CaptureList), // Enable all bits eRENDERDOC_Overlay_All = ~0U, // Disable all bits eRENDERDOC_Overlay_None = 0, } RENDERDOC_OverlayBits; // returns the overlay bits that have been set typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetOverlayBits)(); // sets the overlay bits with an and & or mask typedef void(RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t And, uint32_t Or); // this function will attempt to remove RenderDoc's hooks in the application. // // Note: that this can only work correctly if done immediately after // the module is loaded, before any API work happens. RenderDoc will remove its // injected hooks and shut down. Behaviour is undefined if this is called // after any API functions have been called, and there is still no guarantee of // success. typedef void(RENDERDOC_CC *pRENDERDOC_RemoveHooks)(); // DEPRECATED: compatibility for code compiled against pre-1.4.1 headers. typedef pRENDERDOC_RemoveHooks pRENDERDOC_Shutdown; // This function will unload RenderDoc's crash handler. // // If you use your own crash handler and don't want RenderDoc's handler to // intercede, you can call this function to unload it and any unhandled // exceptions will pass to the next handler. typedef void(RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)(); // Sets the capture file path template // // pathtemplate is a UTF-8 string that gives a template for how captures will be named // and where they will be saved. // // Any extension is stripped off the path, and captures are saved in the directory // specified, and named with the filename and the frame number appended. If the // directory does not exist it will be created, including any parent directories. // // If pathtemplate is NULL, the template will remain unchanged // // Example: // // SetCaptureFilePathTemplate("my_captures/example"); // // Capture #1 -> my_captures/example_frame123.rdc // Capture #2 -> my_captures/example_frame456.rdc typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFilePathTemplate)(const char *pathtemplate); // returns the current capture path template, see SetCaptureFileTemplate above, as a UTF-8 string typedef const char *(RENDERDOC_CC *pRENDERDOC_GetCaptureFilePathTemplate)(); // DEPRECATED: compatibility for code compiled against pre-1.1.2 headers. typedef pRENDERDOC_SetCaptureFilePathTemplate pRENDERDOC_SetLogFilePathTemplate; typedef pRENDERDOC_GetCaptureFilePathTemplate pRENDERDOC_GetLogFilePathTemplate; // returns the number of captures that have been made typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetNumCaptures)(); // This function returns the details of a capture, by index. New captures are added // to the end of the list. // // filename will be filled with the absolute path to the capture file, as a UTF-8 string // pathlength will be written with the length in bytes of the filename string // timestamp will be written with the time of the capture, in seconds since the Unix epoch // // Any of the parameters can be NULL and they'll be skipped. // // The function will return 1 if the capture index is valid, or 0 if the index is invalid // If the index is invalid, the values will be unchanged // // Note: when captures are deleted in the UI they will remain in this list, so the // capture path may not exist anymore. typedef uint32_t(RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *filename, uint32_t *pathlength, uint64_t *timestamp); // Sets the comments associated with a capture file. These comments are displayed in the // UI program when opening. // // filePath should be a path to the capture file to add comments to. If set to NULL or "" // the most recent capture file created made will be used instead. // comments should be a NULL-terminated UTF-8 string to add as comments. // // Any existing comments will be overwritten. typedef void(RENDERDOC_CC *pRENDERDOC_SetCaptureFileComments)(const char *filePath, const char *comments); // returns 1 if the RenderDoc UI is connected to this application, 0 otherwise typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsTargetControlConnected)(); // DEPRECATED: compatibility for code compiled against pre-1.1.1 headers. // This was renamed to IsTargetControlConnected in API 1.1.1, the old typedef is kept here for // backwards compatibility with old code, it is castable either way since it's ABI compatible // as the same function pointer type. typedef pRENDERDOC_IsTargetControlConnected pRENDERDOC_IsRemoteAccessConnected; // This function will launch the Replay UI associated with the RenderDoc library injected // into the running application. // // if connectTargetControl is 1, the Replay UI will be launched with a command line parameter // to connect to this application // cmdline is the rest of the command line, as a UTF-8 string. E.g. a captures to open // if cmdline is NULL, the command line will be empty. // // returns the PID of the replay UI if successful, 0 if not successful. typedef uint32_t(RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectTargetControl, const char *cmdline); // RenderDoc can return a higher version than requested if it's backwards compatible, // this function returns the actual version returned. If a parameter is NULL, it will be // ignored and the others will be filled out. typedef void(RENDERDOC_CC *pRENDERDOC_GetAPIVersion)(int *major, int *minor, int *patch); ////////////////////////////////////////////////////////////////////////// // Capturing functions // // A device pointer is a pointer to the API's root handle. // // This would be an ID3D11Device, HGLRC/GLXContext, ID3D12Device, etc typedef void *RENDERDOC_DevicePointer; // A window handle is the OS's native window handle // // This would be an HWND, GLXDrawable, etc typedef void *RENDERDOC_WindowHandle; // A helper macro for Vulkan, where the device handle cannot be used directly. // // Passing the VkInstance to this macro will return the RENDERDOC_DevicePointer to use. // // Specifically, the value needed is the dispatch table pointer, which sits as the first // pointer-sized object in the memory pointed to by the VkInstance. Thus we cast to a void** and // indirect once. #define RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(inst) (*((void **)(inst))) // This sets the RenderDoc in-app overlay in the API/window pair as 'active' and it will // respond to keypresses. Neither parameter can be NULL typedef void(RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle); // capture the next frame on whichever window and API is currently considered active typedef void(RENDERDOC_CC *pRENDERDOC_TriggerCapture)(); // capture the next N frames on whichever window and API is currently considered active typedef void(RENDERDOC_CC *pRENDERDOC_TriggerMultiFrameCapture)(uint32_t numFrames); // When choosing either a device pointer or a window handle to capture, you can pass NULL. // Passing NULL specifies a 'wildcard' match against anything. This allows you to specify // any API rendering to a specific window, or a specific API instance rendering to any window, // or in the simplest case of one window and one API, you can just pass NULL for both. // // In either case, if there are two or more possible matching (device,window) pairs it // is undefined which one will be captured. // // Note: for headless rendering you can pass NULL for the window handle and either specify // a device pointer or leave it NULL as above. // Immediately starts capturing API calls on the specified device pointer and window handle. // // If there is no matching thing to capture (e.g. no supported API has been initialised), // this will do nothing. // // The results are undefined (including crashes) if two captures are started overlapping, // even on separate devices and/oror windows. typedef void(RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle); // Returns whether or not a frame capture is currently ongoing anywhere. // // This will return 1 if a capture is ongoing, and 0 if there is no capture running typedef uint32_t(RENDERDOC_CC *pRENDERDOC_IsFrameCapturing)(); // Ends capturing immediately. // // This will return 1 if the capture succeeded, and 0 if there was an error capturing. typedef uint32_t(RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle); // Ends capturing immediately and discard any data stored without saving to disk. // // This will return 1 if the capture was discarded, and 0 if there was an error or no capture // was in progress typedef uint32_t(RENDERDOC_CC *pRENDERDOC_DiscardFrameCapture)(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle); ////////////////////////////////////////////////////////////////////////////////////////////////// // RenderDoc API versions // // RenderDoc uses semantic versioning (http://semver.org/). // // MAJOR version is incremented when incompatible API changes happen. // MINOR version is incremented when functionality is added in a backwards-compatible manner. // PATCH version is incremented when backwards-compatible bug fixes happen. // // Note that this means the API returned can be higher than the one you might have requested. // e.g. if you are running against a newer RenderDoc that supports 1.0.1, it will be returned // instead of 1.0.0. You can check this with the GetAPIVersion entry point typedef enum RENDERDOC_Version { eRENDERDOC_API_Version_1_0_0 = 10000, // RENDERDOC_API_1_0_0 = 1 00 00 eRENDERDOC_API_Version_1_0_1 = 10001, // RENDERDOC_API_1_0_1 = 1 00 01 eRENDERDOC_API_Version_1_0_2 = 10002, // RENDERDOC_API_1_0_2 = 1 00 02 eRENDERDOC_API_Version_1_1_0 = 10100, // RENDERDOC_API_1_1_0 = 1 01 00 eRENDERDOC_API_Version_1_1_1 = 10101, // RENDERDOC_API_1_1_1 = 1 01 01 eRENDERDOC_API_Version_1_1_2 = 10102, // RENDERDOC_API_1_1_2 = 1 01 02 eRENDERDOC_API_Version_1_2_0 = 10200, // RENDERDOC_API_1_2_0 = 1 02 00 eRENDERDOC_API_Version_1_3_0 = 10300, // RENDERDOC_API_1_3_0 = 1 03 00 eRENDERDOC_API_Version_1_4_0 = 10400, // RENDERDOC_API_1_4_0 = 1 04 00 eRENDERDOC_API_Version_1_4_1 = 10401, // RENDERDOC_API_1_4_1 = 1 04 01 } RENDERDOC_Version; // API version changelog: // // 1.0.0 - initial release // 1.0.1 - Bugfix: IsFrameCapturing() was returning false for captures that were triggered // by keypress or TriggerCapture, instead of Start/EndFrameCapture. // 1.0.2 - Refactor: Renamed eRENDERDOC_Option_DebugDeviceMode to eRENDERDOC_Option_APIValidation // 1.1.0 - Add feature: TriggerMultiFrameCapture(). Backwards compatible with 1.0.x since the new // function pointer is added to the end of the struct, the original layout is identical // 1.1.1 - Refactor: Renamed remote access to target control (to better disambiguate from remote // replay/remote server concept in replay UI) // 1.1.2 - Refactor: Renamed "log file" in function names to just capture, to clarify that these // are captures and not debug logging files. This is the first API version in the v1.0 // branch. // 1.2.0 - Added feature: SetCaptureFileComments() to add comments to a capture file that will be // displayed in the UI program on load. // 1.3.0 - Added feature: New capture option eRENDERDOC_Option_AllowUnsupportedVendorExtensions // which allows users to opt-in to allowing unsupported vendor extensions to function. // Should be used at the user's own risk. // Refactor: Renamed eRENDERDOC_Option_VerifyMapWrites to // eRENDERDOC_Option_VerifyBufferAccess, which now also controls initialisation to // 0xdddddddd of uninitialised buffer contents. // 1.4.0 - Added feature: DiscardFrameCapture() to discard a frame capture in progress and stop // capturing without saving anything to disk. // 1.4.1 - Refactor: Renamed Shutdown to RemoveHooks to better clarify what is happening typedef struct RENDERDOC_API_1_4_1 { pRENDERDOC_GetAPIVersion GetAPIVersion; pRENDERDOC_SetCaptureOptionU32 SetCaptureOptionU32; pRENDERDOC_SetCaptureOptionF32 SetCaptureOptionF32; pRENDERDOC_GetCaptureOptionU32 GetCaptureOptionU32; pRENDERDOC_GetCaptureOptionF32 GetCaptureOptionF32; pRENDERDOC_SetFocusToggleKeys SetFocusToggleKeys; pRENDERDOC_SetCaptureKeys SetCaptureKeys; pRENDERDOC_GetOverlayBits GetOverlayBits; pRENDERDOC_MaskOverlayBits MaskOverlayBits; // Shutdown was renamed to RemoveHooks in 1.4.1. // These unions allow old code to continue compiling without changes union { pRENDERDOC_Shutdown Shutdown; pRENDERDOC_RemoveHooks RemoveHooks; }; pRENDERDOC_UnloadCrashHandler UnloadCrashHandler; // Get/SetLogFilePathTemplate was renamed to Get/SetCaptureFilePathTemplate in 1.1.2. // These unions allow old code to continue compiling without changes union { // deprecated name pRENDERDOC_SetLogFilePathTemplate SetLogFilePathTemplate; // current name pRENDERDOC_SetCaptureFilePathTemplate SetCaptureFilePathTemplate; }; union { // deprecated name pRENDERDOC_GetLogFilePathTemplate GetLogFilePathTemplate; // current name pRENDERDOC_GetCaptureFilePathTemplate GetCaptureFilePathTemplate; }; pRENDERDOC_GetNumCaptures GetNumCaptures; pRENDERDOC_GetCapture GetCapture; pRENDERDOC_TriggerCapture TriggerCapture; // IsRemoteAccessConnected was renamed to IsTargetControlConnected in 1.1.1. // This union allows old code to continue compiling without changes union { // deprecated name pRENDERDOC_IsRemoteAccessConnected IsRemoteAccessConnected; // current name pRENDERDOC_IsTargetControlConnected IsTargetControlConnected; }; pRENDERDOC_LaunchReplayUI LaunchReplayUI; pRENDERDOC_SetActiveWindow SetActiveWindow; pRENDERDOC_StartFrameCapture StartFrameCapture; pRENDERDOC_IsFrameCapturing IsFrameCapturing; pRENDERDOC_EndFrameCapture EndFrameCapture; // new function in 1.1.0 pRENDERDOC_TriggerMultiFrameCapture TriggerMultiFrameCapture; // new function in 1.2.0 pRENDERDOC_SetCaptureFileComments SetCaptureFileComments; // new function in 1.4.0 pRENDERDOC_DiscardFrameCapture DiscardFrameCapture; } RENDERDOC_API_1_4_1; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_0_0; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_0_1; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_0_2; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_1_0; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_1_1; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_1_2; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_2_0; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_3_0; typedef RENDERDOC_API_1_4_1 RENDERDOC_API_1_4_0; ////////////////////////////////////////////////////////////////////////////////////////////////// // RenderDoc API entry point // // This entry point can be obtained via GetProcAddress/dlsym if RenderDoc is available. // // The name is the same as the typedef - "RENDERDOC_GetAPI" // // This function is not thread safe, and should not be called on multiple threads at once. // Ideally, call this once as early as possible in your application's startup, before doing // any API work, since some configuration functionality etc has to be done also before // initialising any APIs. // // Parameters: // version is a single value from the RENDERDOC_Version above. // // outAPIPointers will be filled out with a pointer to the corresponding struct of function // pointers. // // Returns: // 1 - if the outAPIPointers has been filled with a pointer to the API struct requested // 0 - if the requested version is not supported or the arguments are invalid. // typedef int(RENDERDOC_CC *pRENDERDOC_GetAPI)(RENDERDOC_Version version, void **outAPIPointers); #ifdef __cplusplus } // extern "C" #endif 07070100000030000081A4000000000000000000000001636E64860000165D000000000000000000000000000000000000002500000000gxr/examples/demo/scene-background.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "scene-background.h" #include "graphene-ext.h" #include <gulkan.h> #include <stdalign.h> typedef struct { alignas (32) float mvp[2][16]; } SceneBackgroundUniformBuffer; struct _SceneBackground { SceneObject parent; GulkanVertexBuffer *vertex_buffer; }; G_DEFINE_TYPE (SceneBackground, scene_background, SCENE_TYPE_OBJECT) static void scene_background_finalize (GObject *gobject); static void scene_background_class_init (SceneBackgroundClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = scene_background_finalize; } static void scene_background_init (SceneBackground *self) { self->vertex_buffer = NULL; } static gboolean _initialize (SceneBackground *self, GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set); SceneBackground * scene_background_new (GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set) { SceneBackground *self = (SceneBackground *) g_object_new (SCENE_TYPE_BACKGROUND, 0); _initialize (self, gulkan, descriptor_set); return self; } static void scene_background_finalize (GObject *gobject) { SceneBackground *self = SCENE_BACKGROUND (gobject); g_clear_object (&self->vertex_buffer); G_OBJECT_CLASS (scene_background_parent_class)->finalize (gobject); } static void _append_star (GulkanVertexBuffer *self, float radius, float y, uint32_t sections, graphene_vec3_t *color) { graphene_vec4_t *points = g_malloc (sizeof (graphene_vec4_t) * sections); graphene_vec4_init (&points[0], radius, y, 0, 1); graphene_vec4_init (&points[1], -radius, y, 0, 1); graphene_matrix_t rotation; graphene_matrix_init_identity (&rotation); graphene_matrix_rotate_y (&rotation, 360.0f / (float) sections); for (uint32_t i = 0; i < sections / 2 - 1; i++) { uint32_t j = i * 2; graphene_matrix_transform_vec4 (&rotation, &points[j], &points[j + 2]); graphene_matrix_transform_vec4 (&rotation, &points[j + 1], &points[j + 3]); } for (uint32_t i = 0; i < sections; i++) gulkan_vertex_buffer_append_with_color (self, &points[i], color); g_free (points); } static void _append_circle (GulkanVertexBuffer *self, float radius, float y, uint32_t edges, graphene_vec3_t *color) { graphene_vec4_t *points = g_malloc (sizeof (graphene_vec4_t) * edges * 2); graphene_vec4_init (&points[0], radius, y, 0, 1); graphene_matrix_t rotation; graphene_matrix_init_identity (&rotation); graphene_matrix_rotate_y (&rotation, 360.0f / (float) edges); for (uint32_t i = 0; i < edges; i++) { uint32_t j = i * 2; if (i != 0) graphene_vec4_init_from_vec4 (&points[j], &points[j - 1]); graphene_matrix_transform_vec4 (&rotation, &points[j], &points[j + 1]); } for (uint32_t i = 0; i < edges * 2; i++) gulkan_vertex_buffer_append_with_color (self, &points[i], color); g_free (points); } static void _append_floor (GulkanVertexBuffer *self, uint32_t radius, float y, graphene_vec3_t *color) { _append_star (self, (float) radius, y, 8, color); for (uint32_t i = 1; i <= radius; i++) _append_circle (self, (float) i, y, 128, color); } static gboolean _initialize (SceneBackground *self, GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set) { GulkanDevice *device = gulkan_context_get_device (gulkan); self->vertex_buffer = gulkan_vertex_buffer_new (device, VK_PRIMITIVE_TOPOLOGY_LINE_LIST); gulkan_vertex_buffer_reset (self->vertex_buffer); graphene_vec3_t color; graphene_vec3_init (&color, .6f, .6f, .6f); _append_floor (self->vertex_buffer, 20, 0.0f, &color); _append_floor (self->vertex_buffer, 20, 4.0f, &color); if (!gulkan_vertex_buffer_alloc_empty (self->vertex_buffer, GXR_DEVICE_INDEX_MAX)) return FALSE; gulkan_vertex_buffer_map_array (self->vertex_buffer); SceneObject *obj = SCENE_OBJECT (self); VkDeviceSize ub_size = sizeof (SceneBackgroundUniformBuffer); if (!scene_object_initialize (obj, gulkan, ub_size, descriptor_set)) return FALSE; scene_object_update_descriptors (obj); return TRUE; } void scene_background_render (SceneBackground *self, GulkanPipeline *pipeline, VkPipelineLayout pipeline_layout, VkCommandBuffer cmd_buffer, graphene_matrix_t *vp) { if (!gulkan_vertex_buffer_is_initialized (self->vertex_buffer)) return; SceneObject *obj = SCENE_OBJECT (self); gulkan_pipeline_bind (pipeline, cmd_buffer); SceneBackgroundUniformBuffer ub = {0}; graphene_matrix_t m_matrix; scene_object_get_transformation (SCENE_OBJECT (self), &m_matrix); for (uint32_t eye = 0; eye < 2; eye++) { graphene_matrix_t mvp_matrix; graphene_matrix_multiply (&m_matrix, &vp[eye], &mvp_matrix); float mvp[16]; graphene_matrix_to_float (&mvp_matrix, mvp); for (int i = 0; i < 16; i++) ub.mvp[eye][i] = mvp[i]; } scene_object_update_ubo (SCENE_OBJECT (self), &ub); scene_object_bind (obj, cmd_buffer, pipeline_layout); gulkan_vertex_buffer_draw (self->vertex_buffer, cmd_buffer); } 07070100000031000081A4000000000000000000000001636E6486000003C3000000000000000000000000000000000000002500000000gxr/examples/demo/scene-background.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef SCENE_BACKGROUND_H_ #define SCENE_BACKGROUND_H_ #include <glib-object.h> #include <gulkan.h> #include <gxr.h> #include "scene-object.h" #define SCENE_TYPE_BACKGROUND scene_background_get_type () G_DECLARE_FINAL_TYPE (SceneBackground, scene_background, SCENE, BACKGROUND, SceneObject) SceneBackground * scene_background_new (GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set); void scene_background_render (SceneBackground *self, GulkanPipeline *pipeline, VkPipelineLayout pipeline_layout, VkCommandBuffer cmd_buffer, graphene_matrix_t *vp); #endif /* SCENE_BACKGROUND_H_ */ 07070100000032000081A4000000000000000000000001636E648600000B70000000000000000000000000000000000000002500000000gxr/examples/demo/scene-controller.c/* * xrdesktop * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #include "scene-controller.h" #include "graphene-ext.h" struct _SceneController { GObject parent; ScenePointer *pointer_ray; GxrContext *context; GxrController *controller; gulong controller_move_signal; }; G_DEFINE_TYPE (SceneController, scene_controller, G_TYPE_OBJECT) static void scene_controller_finalize (GObject *gobject); static void scene_controller_class_init (SceneControllerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = scene_controller_finalize; } static void scene_controller_init (SceneController *self) { self->pointer_ray = NULL; self->context = NULL; self->controller = NULL; self->controller_move_signal = 0; } static void _controller_move_cb (GxrController *controller, GxrStateChangeEvent *event, SceneController *self); SceneController * scene_controller_new (GxrController *controller, GxrContext *context) { SceneController *self = (SceneController *) g_object_new (SCENE_TYPE_CONTROLLER, 0); self->context = g_object_ref (context); self->controller = g_object_ref (controller); self->controller_move_signal = g_signal_connect (self->controller, "move", (GCallback) _controller_move_cb, self); return self; } static void scene_controller_finalize (GObject *gobject) { SceneController *self = SCENE_CONTROLLER (gobject); if (self->pointer_ray) g_object_unref (self->pointer_ray); g_signal_handler_disconnect (self->controller, self->controller_move_signal); g_object_unref (self->controller); g_object_unref (self->context); g_debug ("Destroyed pointer ray, pointer tip, controller\n"); G_OBJECT_CLASS (scene_controller_parent_class)->finalize (gobject); } ScenePointer * scene_controller_get_pointer (SceneController *self) { return self->pointer_ray; } void scene_controller_set_pointer (SceneController *self, ScenePointer *pointer) { self->pointer_ray = pointer; } static void _controller_move_cb (GxrController *controller, GxrStateChangeEvent *event, SceneController *self) { (void) event; (void) self; graphene_matrix_t pose; gxr_controller_get_pointer_pose (controller, &pose); /* The pointer's pose is always set by pose update. When hovering, only the * length is set by gxr_controller_update_hovered_object(). */ scene_object_set_transformation_direct (SCENE_OBJECT (self->pointer_ray), &pose); } GxrController * scene_controller_get_controller (SceneController *self) { return self->controller; } 07070100000033000081A4000000000000000000000001636E648600000749000000000000000000000000000000000000002500000000gxr/examples/demo/scene-controller.h/* * xrdesktop * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef SCENE_CONTROLLER_H_ #define SCENE_CONTROLLER_H_ #include <glib-object.h> #include <gxr.h> #include "scene-pointer.h" #include <graphene.h> #define SCENE_TYPE_CONTROLLER scene_controller_get_type () G_DECLARE_FINAL_TYPE (SceneController, scene_controller, SCENE, CONTROLLER, GObject) /** * GxrTransformLock: * @GXR_TRANSFORM_LOCK_NONE: The grab action does not currently have a *transformation it is locked to. * @GXR_TRANSFORM_LOCK_PUSH_PULL: Only push pull transformation can be *performed. * @GXR_TRANSFORM_LOCK_SCALE: Only a scale transformation can be performed. * * The type of transformation the grab action is currently locked to. * This will be detected at the begginging of a grab transformation * and reset after the transformation is done. * **/ typedef enum { GXR_TRANSFORM_LOCK_NONE, GXR_TRANSFORM_LOCK_PUSH_PULL, GXR_TRANSFORM_LOCK_SCALE } GxrTransformLock; typedef struct { gpointer hovered_object; float distance; graphene_point_t intersection_2d; } GxrHoverState; typedef struct { gpointer grabbed_object; graphene_quaternion_t object_rotation; graphene_quaternion_t inverse_controller_rotation; graphene_point_t grab_offset; GxrTransformLock transform_lock; } GxrGrabState; SceneController * scene_controller_new (GxrController *controller, GxrContext *context); ScenePointer * scene_controller_get_pointer (SceneController *self); void scene_controller_set_pointer (SceneController *self, ScenePointer *pointer); GxrController * scene_controller_get_controller (SceneController *self); #endif /* SCENE_CONTROLLER_H_ */ 07070100000034000081A4000000000000000000000001636E648600002B74000000000000000000000000000000000000001F00000000gxr/examples/demo/scene-cube.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "scene-cube.h" #include "graphene-ext.h" #include <gulkan.h> typedef struct __attribute__ ((__packed__)) { float mv_matrix[2][16]; float mvp_matrix[2][16]; float normal_matrix[2][12]; } SceneCubeUniformBuffer; typedef struct { const gchar *vert; const gchar *frag; } ShaderResources; static const float positions[] = { // front -1.0f, +1.0f, +1.0f, // point blue +1.0f, +1.0f, +1.0f, // point magenta -1.0f, -1.0f, +1.0f, // point cyan +1.0f, -1.0f, +1.0f, // point white // back +1.0f, +1.0f, -1.0f, // point red -1.0f, +1.0f, -1.0f, // point black +1.0f, -1.0f, -1.0f, // point yellow -1.0f, -1.0f, -1.0f, // point green // right +1.0f, +1.0f, +1.0f, // point magenta +1.0f, +1.0f, -1.0f, // point red +1.0f, -1.0f, +1.0f, // point white +1.0f, -1.0f, -1.0f, // point yellow // left -1.0f, +1.0f, -1.0f, // point black -1.0f, +1.0f, +1.0f, // point blue -1.0f, -1.0f, -1.0f, // point green -1.0f, -1.0f, +1.0f, // point cyan // bottom -1.0f, -1.0f, +1.0f, // point cyan +1.0f, -1.0f, +1.0f, // point white -1.0f, -1.0f, -1.0f, // point green +1.0f, -1.0f, -1.0f, // point yellow // top -1.0f, +1.0f, -1.0f, // point black +1.0f, +1.0f, -1.0f, // point red -1.0f, +1.0f, +1.0f, // point blue +1.0f, +1.0f, +1.0f // point magenta }; static const float colors[] = { // front 0.0f, 0.0f, 1.0f, // blue 1.0f, 0.0f, 1.0f, // magenta 0.0f, 1.0f, 1.0f, // cyan 1.0f, 1.0f, 1.0f, // white // back 1.0f, 0.0f, 0.0f, // red 0.0f, 0.0f, 0.0f, // black 1.0f, 1.0f, 0.0f, // yellow 0.0f, 1.0f, 0.0f, // green // right 1.0f, 0.0f, 1.0f, // magenta 1.0f, 0.0f, 0.0f, // red 1.0f, 1.0f, 1.0f, // white 1.0f, 1.0f, 0.0f, // yellow // left 0.0f, 0.0f, 0.0f, // black 0.0f, 0.0f, 1.0f, // blue 0.0f, 1.0f, 0.0f, // green 0.0f, 1.0f, 1.0f, // cyan // bottom 0.0f, 1.0f, 1.0f, // cyan 1.0f, 1.0f, 1.0f, // white 0.0f, 1.0f, 0.0f, // green 1.0f, 1.0f, 0.0f, // yellow // top 0.0f, 0.0f, 0.0f, // black 1.0f, 0.0f, 0.0f, // red 0.0f, 0.0f, 1.0f, // blue 1.0f, 0.0f, 1.0f // magenta }; static const float normals[] = { // front +0.0f, +0.0f, +1.0f, // forward +0.0f, +0.0f, +1.0f, // forward +0.0f, +0.0f, +1.0f, // forward +0.0f, +0.0f, +1.0f, // forward // back +0.0f, +0.0f, -1.0f, // backbard +0.0f, +0.0f, -1.0f, // backbard +0.0f, +0.0f, -1.0f, // backbard +0.0f, +0.0f, -1.0f, // backbard // right +1.0f, +0.0f, +0.0f, // right +1.0f, +0.0f, +0.0f, // right +1.0f, +0.0f, +0.0f, // right +1.0f, +0.0f, +0.0f, // right // left -1.0f, +0.0f, +0.0f, // left -1.0f, +0.0f, +0.0f, // left -1.0f, +0.0f, +0.0f, // left -1.0f, +0.0f, +0.0f, // left // bottom +0.0f, -1.0f, +0.0f, // up +0.0f, -1.0f, +0.0f, // up +0.0f, -1.0f, +0.0f, // up +0.0f, -1.0f, +0.0f, // up // top +0.0f, +1.0f, +0.0f, // down +0.0f, +1.0f, +0.0f, // down +0.0f, +1.0f, +0.0f, // down +0.0f, +1.0f, +0.0f // down }; struct _SceneCube { SceneObject parent; GulkanVertexBuffer *vb; GulkanContext *gulkan; GulkanRenderer *renderer; VkSampleCountFlagBits sample_count; GulkanDescriptorPool *descriptor_pool; GulkanDescriptorSet *descriptor_set; GulkanPipeline *pipeline; graphene_point3d_t pos; }; G_DEFINE_TYPE (SceneCube, scene_cube, SCENE_TYPE_OBJECT) static void scene_cube_finalize (GObject *gobject); static void scene_cube_class_init (SceneCubeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = scene_cube_finalize; } static gboolean _init_pipeline (SceneCube *self, GulkanRenderPass *render_pass) { VkVertexInputBindingDescription *binding_desc = gulkan_vertex_buffer_create_binding_desc (self->vb); VkVertexInputAttributeDescription *attrib_desc = gulkan_vertex_buffer_create_attrib_desc (self->vb); GulkanPipelineConfig config = { .extent = gulkan_renderer_get_extent (self->renderer), .sample_count = VK_SAMPLE_COUNT_1_BIT, .vertex_shader_uri = "/shaders/cube.vert.spv", .fragment_shader_uri = "/shaders/cube.frag.spv", .topology = gulkan_vertex_buffer_get_topology (self->vb), .attribs = attrib_desc, .attrib_count = gulkan_vertex_buffer_get_attrib_count (self->vb), .bindings = binding_desc, .binding_count = gulkan_vertex_buffer_get_attrib_count (self->vb), .blend_attachments = (VkPipelineColorBlendAttachmentState[]){ { .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }, }, .rasterization_state = &(VkPipelineRasterizationStateCreateInfo){ .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_CLOCKWISE, .lineWidth = 1.0f, }, .depth_stencil_state = &(VkPipelineDepthStencilStateCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = VK_TRUE, .depthWriteEnable = VK_TRUE, .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, }, .dynamic_viewport = FALSE, .flip_y = TRUE, }; self->pipeline = gulkan_pipeline_new (self->gulkan, self->descriptor_pool, render_pass, &config); g_free (binding_desc); g_free (attrib_desc); return TRUE; } static gboolean _init_descriptor_set_pool (SceneCube *self) { VkDescriptorSetLayoutBinding bindings[] = { { .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .pImmutableSamplers = NULL, }, }; self->descriptor_pool = GULKAN_DESCRIPTOR_POOL_NEW (self->gulkan, bindings, 1); if (!self->descriptor_pool) return FALSE; return TRUE; } static gboolean _initialize (SceneCube *self, GulkanContext *gulkan, GulkanRenderer *renderer, GulkanRenderPass *render_pass, VkSampleCountFlagBits sample_count) { self->gulkan = g_object_ref (gulkan); self->renderer = g_object_ref (renderer); self->sample_count = sample_count; GulkanDevice *device = gulkan_context_get_device (gulkan); self->vb = gulkan_vertex_buffer_new (device, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); gulkan_vertex_buffer_add_attribute (self->vb, 3, sizeof (positions), 0, (uint8_t *) positions); gulkan_vertex_buffer_add_attribute (self->vb, 3, sizeof (colors), 0, (uint8_t *) colors); gulkan_vertex_buffer_add_attribute (self->vb, 3, sizeof (normals), 0, (uint8_t *) normals); if (!gulkan_vertex_buffer_upload (self->vb)) return FALSE; if (!self->vb) return FALSE; SceneObject *obj = SCENE_OBJECT (self); if (!_init_descriptor_set_pool (self)) return FALSE; if (!_init_pipeline (self, render_pass)) return FALSE; self->descriptor_set = gulkan_descriptor_pool_create_set (self->descriptor_pool); VkDeviceSize ubo_size = sizeof (SceneCubeUniformBuffer); if (!scene_object_initialize (obj, gulkan, ubo_size, self->descriptor_set)) return FALSE; scene_object_update_descriptors (obj); self->pos = (graphene_point3d_t){0.0f, 0.0f, -6.0f}; return TRUE; } static void scene_cube_init (SceneCube *self) { (void) self; } SceneCube * scene_cube_new (GulkanContext *gulkan, GulkanRenderer *renderer, GulkanRenderPass *render_pass, VkSampleCountFlagBits sample_count) { SceneCube *self = (SceneCube *) g_object_new (SCENE_TYPE_CUBE, 0); _initialize (self, gulkan, renderer, render_pass, sample_count); return self; } static void scene_cube_finalize (GObject *gobject) { SceneCube *self = SCENE_CUBE (gobject); g_object_unref (self->descriptor_set); g_object_unref (self->descriptor_pool); g_object_unref (self->pipeline); g_clear_object (&self->vb); g_clear_object (&self->renderer); g_clear_object (&self->gulkan); G_OBJECT_CLASS (scene_cube_parent_class)->finalize (gobject); } static void _set_transformation (SceneCube *self) { graphene_matrix_t m_matrix; graphene_matrix_init_identity (&m_matrix); int64_t t = gulkan_renderer_get_msec_since_start (self->renderer); t /= 5; graphene_matrix_rotate_x (&m_matrix, 45.0f + (0.25f * (float) t)); graphene_matrix_rotate_y (&m_matrix, 45.0f - (0.5f * (float) t)); graphene_matrix_rotate_z (&m_matrix, 10.0f + (0.15f * (float) t)); graphene_matrix_translate (&m_matrix, &self->pos); scene_object_set_transformation (SCENE_OBJECT (self), &m_matrix); } void scene_cube_render (SceneCube *self, VkCommandBuffer cmd_buffer, graphene_matrix_t *view, graphene_matrix_t *projection) { if (!gulkan_vertex_buffer_is_initialized (self->vb)) { g_printerr ("Cube vb not initialized\n"); return; } _set_transformation (self); gulkan_pipeline_bind (self->pipeline, cmd_buffer); /* TODO: would be nice if update_mvp matrix would fully update our ubo * scene_object_update_mvp_matrix (obj, eye, vp); */ SceneCubeUniformBuffer ub; graphene_matrix_t m_matrix; scene_object_get_transformation (SCENE_OBJECT (self), &m_matrix); for (uint32_t eye = 0; eye < 2; eye++) { graphene_matrix_t mv_matrix; graphene_matrix_multiply (&m_matrix, &view[eye], &mv_matrix); graphene_matrix_t mvp_matrix; graphene_matrix_multiply (&mv_matrix, &projection[eye], &mvp_matrix); float mv[16]; graphene_matrix_to_float (&mv_matrix, mv); for (int i = 0; i < 16; i++) ub.mv_matrix[eye][i] = mv[i]; float mvp[16]; graphene_matrix_to_float (&mvp_matrix, mvp); for (int i = 0; i < 16; i++) ub.mvp_matrix[eye][i] = mvp[i]; /* The mat3 normalMatrix is laid out as 3 vec4s. */ memcpy (ub.normal_matrix[eye], ub.mv_matrix[eye], sizeof ub.normal_matrix[eye]); } scene_object_update_ubo (SCENE_OBJECT (self), &ub); VkPipelineLayout layout = gulkan_descriptor_pool_get_pipeline_layout (self->descriptor_pool); gulkan_descriptor_set_bind (self->descriptor_set, layout, cmd_buffer); gulkan_vertex_buffer_bind_with_offsets (self->vb, cmd_buffer); vkCmdDraw (cmd_buffer, 4, 1, 0, 0); vkCmdDraw (cmd_buffer, 4, 1, 4, 0); vkCmdDraw (cmd_buffer, 4, 1, 8, 0); vkCmdDraw (cmd_buffer, 4, 1, 12, 0); vkCmdDraw (cmd_buffer, 4, 1, 16, 0); vkCmdDraw (cmd_buffer, 4, 1, 20, 0); } void scene_cube_override_position (SceneCube *self, graphene_point3d_t *position) { self->pos = *position; } 07070100000035000081A4000000000000000000000001636E6486000003B7000000000000000000000000000000000000001F00000000gxr/examples/demo/scene-cube.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef SCENE_CUBE_H_ #define SCENE_CUBE_H_ #include <glib-object.h> #include <gulkan.h> #include "scene-object.h" #define SCENE_TYPE_CUBE scene_cube_get_type () G_DECLARE_FINAL_TYPE (SceneCube, scene_cube, SCENE, CUBE, SceneObject) SceneCube * scene_cube_new (GulkanContext *gulkan, GulkanRenderer *renderer, GulkanRenderPass *render_pass, VkSampleCountFlagBits sample_count); void scene_cube_render (SceneCube *self, VkCommandBuffer cmd_buffer, graphene_matrix_t *view, graphene_matrix_t *projection); void scene_cube_override_position (SceneCube *self, graphene_point3d_t *position); #endif /* SCENE_CUBE_H_ */ 07070100000036000081A4000000000000000000000001636E64860000114B000000000000000000000000000000000000002100000000gxr/examples/demo/scene-object.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #include "scene-object.h" #include <gulkan.h> #include "graphene-ext.h" typedef struct _SceneObjectPrivate { GObject parent; GulkanContext *gulkan; GulkanUniformBuffer *uniform_buffer; GulkanDescriptorSet *descriptor_set; graphene_matrix_t model_matrix; graphene_point3d_t position; float scale; graphene_quaternion_t orientation; } SceneObjectPrivate; G_DEFINE_TYPE_WITH_PRIVATE (SceneObject, scene_object, G_TYPE_OBJECT) static void scene_object_finalize (GObject *gobject); static void scene_object_class_init (SceneObjectClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = scene_object_finalize; } static void scene_object_init (SceneObject *self) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); graphene_matrix_init_identity (&priv->model_matrix); priv->scale = 1.0f; priv->gulkan = NULL; } static void scene_object_finalize (GObject *gobject) { SceneObject *self = SCENE_OBJECT (gobject); SceneObjectPrivate *priv = scene_object_get_instance_private (self); g_object_unref (priv->uniform_buffer); g_clear_object (&priv->gulkan); G_OBJECT_CLASS (scene_object_parent_class)->finalize (gobject); } static void _update_model_matrix (SceneObject *self) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); graphene_matrix_init_scale (&priv->model_matrix, priv->scale, priv->scale, priv->scale); graphene_matrix_rotate_quaternion (&priv->model_matrix, &priv->orientation); graphene_matrix_translate (&priv->model_matrix, &priv->position); } void scene_object_bind (SceneObject *self, VkCommandBuffer cmd_buffer, VkPipelineLayout layout) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); gulkan_descriptor_set_bind (priv->descriptor_set, layout, cmd_buffer); } gboolean scene_object_initialize (SceneObject *self, GulkanContext *gulkan, VkDeviceSize uniform_buffer_size, GulkanDescriptorSet *descriptor_set) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); priv->gulkan = g_object_ref (gulkan); GulkanDevice *device = gulkan_context_get_device (gulkan); /* Create uniform buffer to hold a matrix per eye */ priv->uniform_buffer = gulkan_uniform_buffer_new (device, uniform_buffer_size); if (!priv->uniform_buffer) return FALSE; priv->descriptor_set = descriptor_set; return TRUE; } void scene_object_update_descriptors (SceneObject *self) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); gulkan_descriptor_set_update_buffer (priv->descriptor_set, 0, priv->uniform_buffer); } void scene_object_set_transformation (SceneObject *self, graphene_matrix_t *mat) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); graphene_point3d_t unused_scale; graphene_ext_matrix_get_rotation_quaternion (mat, &unused_scale, &priv->orientation); graphene_ext_matrix_get_translation_point3d (mat, &priv->position); _update_model_matrix (self); } /* * Set transformation without matrix decomposition and ability to rebuild * This will include scale as well. */ void scene_object_set_transformation_direct (SceneObject *self, graphene_matrix_t *mat) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); graphene_matrix_init_from_matrix (&priv->model_matrix, mat); } void scene_object_get_transformation (SceneObject *self, graphene_matrix_t *transformation) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); graphene_matrix_init_from_matrix (transformation, &priv->model_matrix); } void scene_object_update_ubo (SceneObject *self, gpointer uniform_buffer) { SceneObjectPrivate *priv = scene_object_get_instance_private (self); gulkan_uniform_buffer_update (priv->uniform_buffer, uniform_buffer); } 07070100000037000081A4000000000000000000000001636E6486000006DB000000000000000000000000000000000000002100000000gxr/examples/demo/scene-object.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef SCENE_OBJECT_H_ #define SCENE_OBJECT_H_ #include "glib.h" #include <graphene.h> #include <gulkan.h> #include <gxr.h> #define SCENE_TYPE_OBJECT scene_object_get_type () G_DECLARE_DERIVABLE_TYPE (SceneObject, scene_object, SCENE, OBJECT, GObject) /** * SceneObjectClass: * @parent: The object class structure needs to be the first * element in the widget class structure in order for the class mechanism * to work correctly. This allows a SceneObjectClass pointer to be cast to * a GObjectClass pointer. */ struct _SceneObjectClass { GObjectClass parent; }; void scene_object_get_transformation (SceneObject *self, graphene_matrix_t *transformation); void scene_object_bind (SceneObject *self, VkCommandBuffer cmd_buffer, VkPipelineLayout pipeline_layout); gboolean scene_object_initialize (SceneObject *self, GulkanContext *gulkan, VkDeviceSize uniform_buffer_size, GulkanDescriptorSet *descriptor_set); void scene_object_update_descriptors (SceneObject *self); void scene_object_set_transformation (SceneObject *self, graphene_matrix_t *mat); void scene_object_set_transformation_direct (SceneObject *self, graphene_matrix_t *mat); GulkanUniformBuffer * scene_object_get_ubo (SceneObject *self); void scene_object_update_ubo (SceneObject *self, gpointer uniform_buffer); #endif /* SCENE_OBJECT_H_ */ 07070100000038000081A4000000000000000000000001636E648600000E23000000000000000000000000000000000000002200000000gxr/examples/demo/scene-pointer.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #include "scene-pointer.h" #include <gulkan.h> #include "graphene-ext.h" #include <stdalign.h> typedef struct { alignas (32) float mvp[2][16]; } ScenePointerUniformBuffer; struct _ScenePointer { SceneObject parent; GulkanVertexBuffer *vertex_buffer; float start_offset; float length; }; G_DEFINE_TYPE (ScenePointer, scene_pointer, SCENE_TYPE_OBJECT) static void scene_pointer_finalize (GObject *gobject); static void scene_pointer_class_init (ScenePointerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = scene_pointer_finalize; } static void scene_pointer_init (ScenePointer *self) { self->vertex_buffer = NULL; self->start_offset = -0.02f; self->length = 5.0f; } static gboolean _initialize (ScenePointer *self, GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set) { GulkanDevice *device = gulkan_context_get_device (gulkan); self->vertex_buffer = gulkan_vertex_buffer_new (device, VK_PRIMITIVE_TOPOLOGY_LINE_LIST); gulkan_vertex_buffer_reset (self->vertex_buffer); graphene_vec4_t start; graphene_vec4_init (&start, 0, 0, self->start_offset, 1); graphene_matrix_t identity; graphene_matrix_init_identity (&identity); gulkan_geometry_append_ray (self->vertex_buffer, &start, self->length, &identity); if (!gulkan_vertex_buffer_alloc_empty (self->vertex_buffer, GXR_DEVICE_INDEX_MAX)) return FALSE; gulkan_vertex_buffer_map_array (self->vertex_buffer); SceneObject *obj = SCENE_OBJECT (self); VkDeviceSize ubo_size = sizeof (ScenePointerUniformBuffer); if (!scene_object_initialize (obj, gulkan, ubo_size, descriptor_set)) return FALSE; scene_object_update_descriptors (obj); return TRUE; } ScenePointer * scene_pointer_new (GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set) { ScenePointer *self = (ScenePointer *) g_object_new (SCENE_TYPE_POINTER, 0); _initialize (self, gulkan, descriptor_set); return self; } static void scene_pointer_finalize (GObject *gobject) { ScenePointer *self = SCENE_POINTER (gobject); g_clear_object (&self->vertex_buffer); G_OBJECT_CLASS (scene_pointer_parent_class)->finalize (gobject); } void scene_pointer_render (ScenePointer *self, GulkanPipeline *pipeline, VkPipelineLayout pipeline_layout, VkCommandBuffer cmd_buffer, graphene_matrix_t *vp) { if (!gulkan_vertex_buffer_is_initialized (self->vertex_buffer)) return; SceneObject *obj = SCENE_OBJECT (self); ScenePointerUniformBuffer ub = {0}; graphene_matrix_t m_matrix; scene_object_get_transformation (SCENE_OBJECT (self), &m_matrix); for (uint32_t eye = 0; eye < 2; eye++) { graphene_matrix_t mvp_matrix; graphene_matrix_multiply (&m_matrix, &vp[eye], &mvp_matrix); float mvp[16]; graphene_matrix_to_float (&mvp_matrix, mvp); for (int i = 0; i < 16; i++) ub.mvp[eye][i] = mvp[i]; } scene_object_update_ubo (SCENE_OBJECT (self), &ub); gulkan_pipeline_bind (pipeline, cmd_buffer); scene_object_bind (obj, cmd_buffer, pipeline_layout); gulkan_vertex_buffer_draw (self->vertex_buffer, cmd_buffer); } float scene_pointer_get_length (ScenePointer *self) { return self->length; } 07070100000039000081A4000000000000000000000001636E648600000391000000000000000000000000000000000000002200000000gxr/examples/demo/scene-pointer.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef SCENE_POINTER_H_ #define SCENE_POINTER_H_ #include <glib-object.h> #include <gulkan.h> #include <gxr.h> #include "scene-object.h" #define SCENE_TYPE_POINTER scene_pointer_get_type () G_DECLARE_FINAL_TYPE (ScenePointer, scene_pointer, SCENE, POINTER, SceneObject) ScenePointer * scene_pointer_new (GulkanContext *gulkan, GulkanDescriptorSet *descriptor_set); void scene_pointer_render (ScenePointer *self, GulkanPipeline *pipeline, VkPipelineLayout pipeline_layout, VkCommandBuffer cmd_buffer, graphene_matrix_t *vp); float scene_pointer_get_length (ScenePointer *self); #endif /* SCENE_POINTER_H_ */ 0707010000003A000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001A00000000gxr/examples/demo/shaders0707010000003B000081A4000000000000000000000001636E648600000484000000000000000000000000000000000000002400000000gxr/examples/demo/shaders/cube.frag/* * gulkan * Copyright 2020 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #version 460 core layout (location = 0) in vec3 positon_view; layout (location = 1) in vec3 normal_view; layout (location = 2) in vec4 diffuse_color; layout (location = 0) out vec4 out_color; const vec3 position_light = vec3 (1.0, 1.0, 1.0); const vec3 specular_color = vec3 (1.0, 1.0, 1.0); const float shininess = 16.0; void main () { vec3 direction_light = normalize (position_light - positon_view); vec3 normal = normalize (normal_view); float lambertian = max (dot (direction_light, normal), 0.0); float specular = 0.0; if (lambertian > 0.0) { vec3 direction_reflection = reflect (-direction_light, normal); vec3 direction_view = normalize (-positon_view); float specular_angle = max (dot (direction_reflection, direction_view), 0.0); specular = pow (specular_angle, shininess); } out_color = vec4 (lambertian * diffuse_color.xyz + lambertian * specular * specular_color, 1.0); } 0707010000003C000081A4000000000000000000000001636E648600000382000000000000000000000000000000000000002400000000gxr/examples/demo/shaders/cube.vert/* * gulkan * Copyright 2020 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #version 460 core #extension GL_EXT_multiview : enable layout (std140, set = 0, binding = 0) uniform block { uniform mat4 mv_matrix[2]; uniform mat4 mvp_matrix[2]; uniform mat3 normal_matrix[2]; }; layout (location = 0) in vec4 in_position; layout (location = 1) in vec4 in_color; layout (location = 2) in vec3 in_normal; layout (location = 0) out vec3 positon_view; layout (location = 1) out vec3 normal_view; layout (location = 2) out vec4 diffuse_color; void main () { normal_view = normal_matrix[gl_ViewIndex] * in_normal; vec4 positon_view_vec4 = mv_matrix[gl_ViewIndex] * in_position; positon_view = positon_view_vec4.xyz / positon_view_vec4.w; diffuse_color = in_color; gl_Position = mvp_matrix[gl_ViewIndex] * in_position; } 0707010000003D000081A4000000000000000000000001636E6486000002A2000000000000000000000000000000000000002600000000gxr/examples/demo/shaders/meson.buildshaders = ['pointer.vert', 'pointer.frag', 'cube.frag', 'cube.vert' ] glslc = find_program('glslc', required : false) if glslc.found() # Prefer shaderc cmd = [glslc] else # Use glslang as fallback glslang = find_program('glslangValidator') if glslang.found() cmd = [glslang, '-V'] endif endif foreach s : shaders r = run_command(cmd + ['-o', s + '.spv', s], check: true) if r.returncode() != 0 message('Could not compile shaders:') message(r.stderr().strip()) message(r.stdout().strip()) endif endforeach demo_shader_resources = gnome.compile_resources( 'demo_shader_resources', 'shaders.gresource.xml', source_dir : '.') 0707010000003E000081A4000000000000000000000001636E648600000146000000000000000000000000000000000000002700000000gxr/examples/demo/shaders/pointer.frag/* * gulkan * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #version 460 #extension GL_ARB_separate_shader_objects : enable layout (location = 0) in vec4 color; layout (location = 0) out vec4 out_color; void main () { out_color = color; } 0707010000003F000081A4000000000000000000000001636E64860000024B000000000000000000000000000000000000002700000000gxr/examples/demo/shaders/pointer.vert/* * gulkan * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #version 460 #extension GL_ARB_separate_shader_objects : enable #extension GL_EXT_multiview : enable layout (binding = 0) uniform Transformation { mat4 mvp[2]; } ubo; layout (location = 0) in vec3 position; layout (location = 1) in vec3 color; layout (location = 0) out vec4 out_color; out gl_PerVertex { vec4 gl_Position; }; void main () { gl_Position = ubo.mvp[gl_ViewIndex] * vec4 (position, 1.0); out_color = vec4 (color, 1.0); } 07070100000040000081A4000000000000000000000001636E6486000000F3000000000000000000000000000000000000003000000000gxr/examples/demo/shaders/shaders.gresource.xml<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/shaders"> <file>pointer.vert.spv</file> <file>pointer.frag.spv</file> <file>cube.frag.spv</file> <file>cube.vert.spv</file> </gresource> </gresources> 07070100000041000081A4000000000000000000000001636E648600000171000000000000000000000000000000000000001900000000gxr/examples/meson.build# gxr samples executable( 'actions', ['actions.c', test_resources], dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) executable( 'parse_manifest', ['parse_manifest.c', test_resources], dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) subdir('demo') subdir('cube') 07070100000042000081A4000000000000000000000001636E64860000012D000000000000000000000000000000000000001E00000000gxr/examples/parse_manifest.c/* * gxr * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #include <gxr.h> int main () { GxrManifest *manifest = gxr_manifest_new ("/res/bindings", "actions.json"); g_assert (manifest); g_object_unref (manifest); } 07070100000043000081A4000000000000000000000001636E648600000959000000000000000000000000000000000000001000000000gxr/meson.buildproject('gxr', 'c', version: '0.16.0', meson_version: '>= 0.52.0', default_options : [ 'c_std=c11', 'warning_level=3', ], ) gnome = import('gnome') config_h = configuration_data() project_args = ['-I' + meson.build_root(), '-Wno-overlength-strings' ] compiler = meson.get_compiler('c') compiler_id = compiler.get_id() if compiler_id == 'clang' project_args += [ '-Weverything', '-Wno-float-equal', '-Wno-reserved-id-macro', '-Wno-documentation', '-Wno-documentation-unknown-command', '-Wno-padded', '-Wno-disabled-macro-expansion', '-Wno-atomic-implicit-seq-cst', '-Wno-gnu-empty-initializer', '-Wno-covered-switch-default', '-Wno-switch-enum', '-Wno-used-but-marked-unused', '-Wno-double-promotion', '-Wno-format-nonliteral', # variadic %f '-Wno-gnu-folding-constant', '-Wno-cast-qual', # G_DEFINE_TYPE produces this '-Wno-declaration-after-statement', '-Wno-reserved-identifier', '-Wno-unused-macros', # GLib ] endif add_project_arguments([project_args], language: ['c']) # Paths gxr_prefix = get_option('prefix') gxr_libdir = join_paths(gxr_prefix, get_option('libdir')) gxr_includedir = join_paths(gxr_prefix, get_option('includedir')) gxr_datadir = join_paths(gxr_prefix, get_option('datadir')) src_inc = include_directories('src') ### Dependencies c = meson.get_compiler('c') ## Required m_dep = c.find_library('m') gulkan_dep_major_minor_ver = '0.16' gulkan_dep_patch_ver = '.0' gulkan_proj = subproject('gulkan', default_options: ['api_doc=false', 'tests=false', 'examples=false'], required: false, version: '>= ' + gulkan_dep_major_minor_ver + gulkan_dep_patch_ver, ) if gulkan_proj.found() gulkan_dep = gulkan_proj.get_variable('gulkan_dep') else gulkan_dep = dependency('gulkan-' + gulkan_dep_major_minor_ver, version: '>= ' + gulkan_dep_major_minor_ver + gulkan_dep_patch_ver, include_type: 'system') endif gdk_dep = dependency('gdk-3.0', version: '>= 3.22') gmodule_dep = dependency('gmodule-2.0') json_glib_dep = dependency('json-glib-1.0') openxr_dep = dependency('openxr') config_h.set('GXR_DEFAULT_API', 'GXR_API_OPENXR') subdir('src') subdir('res') if get_option('tests') subdir('tests') endif if get_option('examples') subdir('examples') endif if get_option('api_doc') subdir('doc') endif 07070100000044000081A4000000000000000000000001636E6486000001EA000000000000000000000000000000000000001600000000gxr/meson_options.txtoption('api_doc', type: 'boolean', value: false, description: 'Build API documentation using GTK-Doc' ) option('cube_renderdoc', type: 'boolean', value: false, description: 'Enable debugging the cube example with renderdoc' ) option('introspection', type : 'boolean', value : false) option('examples', type: 'boolean', value: true, description: 'Build the examples' ) option('tests', type: 'boolean', value: true, description: 'Build the tests' ) 07070100000045000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000800000000gxr/res07070100000046000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001100000000gxr/res/bindings07070100000047000081A4000000000000000000000001636E6486000008D2000000000000000000000000000000000000001E00000000gxr/res/bindings/actions.json{ "default_bindings":[ { "controller_type":"khronos_simple", "binding_url":"bindings_khronos_simple_controller.json" }, { "controller_type": "index", "binding_url": "bindings_valve_index_controller.json" }, { "controller_type": "htcvive", "binding_url": "bindings_htc_vive_controller.json" } ], "actions":[ { "name":"/actions/wm/in/grab_window", "type":"vector1" }, { "name":"/actions/wm/in/reset_orientation", "type":"boolean" }, { "name":"/actions/wm/in/show_keyboard", "type":"boolean" }, { "name":"/actions/wm/in/push_pull", "type": "vector2" }, { "name":"/actions/wm/in/push_pull_scale", "type": "vector2" }, { "name": "/actions/wm/in/hand_pose", "type": "pose" }, { "name": "/actions/wm/in/hand_pose_hand_grip", "type": "pose" }, { "name": "/actions/wm/out/haptic", "type": "vibration" }, { "name":"/actions/wm/in/menu", "type":"boolean" }, { "name":"/actions/mouse_synth/in/left_click", "type":"boolean" }, { "name":"/actions/mouse_synth/in/right_click", "type":"boolean" }, { "name":"/actions/mouse_synth/in/middle_click", "type":"boolean" }, { "name":"/actions/mouse_synth/in/scroll", "type": "vector2" } ], "action_sets": [ { "name": "/actions/wm" }, { "name": "/actions/mouse_synth" } ], "localization":[ { "language_tag":"en_US", "/actions/wm/in/grab_window":"Grab Window", "/actions/wm/in/reset_orientation":"Reset Window Orientation", "/actions/wm/in/show_keyboard":"Show On Screen Keyboard", "/actions/wm/in/push_pull":"Move window along Z axis", "/actions/wm/in/push_pull_scale":"Scale Window", "/actions/wm/in/hand_pose" : "Controller Ray Pose", "/actions/wm/out/haptic" : "Haptic Feedback", "/actions/wm/in/menu" : "Window Manager Menu", "/actions/mouse_synth/in/left_click" : "Left Click", "/actions/mouse_synth/in/right_click" : "Right Click", "/actions/mouse_synth/in/scroll" : "Scroll" } ] } 07070100000048000081A4000000000000000000000001636E6486000014E7000000000000000000000000000000000000003300000000gxr/res/bindings/bindings_htc_vive_controller.json{ "interaction_profile": "/interaction_profiles/htc/vive_controller", "bindings" : { "/actions/mouse_synth": { "sources": [ { "path": "/user/hand/right/input/trackpad", "inputs": { "position": { "output": "/actions/mouse_synth/in/scroll" } } }, { "path": "/user/hand/right/input/menu", "inputs": { "click": { "output": "/actions/mouse_synth/in/right_click" } } }, { "path": "/user/hand/right/input/trackpad", "inputs": { "click": { "output": "/actions/mouse_synth/in/left_click" } } }, { "path": "/user/hand/left/input/trackpad", "inputs": { "position": { "output": "/actions/mouse_synth/in/scroll" } } }, { "path": "/user/hand/left/input/trackpad", "inputs": { "click": { "output": "/actions/mouse_synth/in/left_click" } } }, { "path": "/user/hand/left/input/menu", "inputs": { "click": { "output": "/actions/mouse_synth/in/right_click" } } } ] }, "/actions/wm": { "poses": [ { "output": "/actions/wm/in/hand_pose", "path": "/user/hand/right/input/aim/pose" }, { "output": "/actions/wm/in/hand_pose", "path": "/user/hand/left/input/aim/pose" }, { "output": "/actions/wm/in/hand_pose_hand_grip", "path": "/user/hand/right/input/grip/pose" }, { "output": "/actions/wm/in/hand_pose_hand_grip", "path": "/user/hand/left/input/grip/pose" } ], "haptics": [ { "output": "/actions/wm/out/haptic", "path": "/user/hand/right/output/haptic" }, { "output": "/actions/wm/out/haptic", "path": "/user/hand/left/output/haptic" } ], "sources": [ { "path": "/user/hand/right/input/trigger", "mode": "button", "inputs": { "pull": { "output": "/actions/wm/in/grab_window" } } }, { "path": "/user/hand/right/input/trigger", "mode": "button", "inputs": { "click": { "output": "/actions/wm/in/reset_orientation" } } }, { "path": "/user/hand/right/input/trackpad", "mode": "trackpad", "inputs": { "position": { "output": "/actions/wm/in/push_pull_scale" } } }, { "path": "/user/hand/right/input/menu", "mode": "button", "inputs": { "click": { "output": "/actions/wm/in/menu" } } }, { "path": "/user/hand/left/input/squeeze", "mode": "button", "inputs": { "click": { "output": "/actions/wm/in/show_keyboard" } } }, { "path": "/user/hand/left/input/trigger", "mode": "button", "inputs": { "pull": { "output": "/actions/wm/in/grab_window" } } }, { "path": "/user/hand/left/input/trigger", "mode": "button", "inputs": { "click": { "output": "/actions/wm/in/reset_orientation" } } }, { "path": "/user/hand/left/input/trackpad", "mode": "trackpad", "inputs": { "position": { "output": "/actions/wm/in/push_pull_scale" } } }, { "path": "/user/hand/left/input/menu", "mode": "button", "inputs": { "click": { "output": "/actions/wm/in/menu" } } }, { "path": "/user/hand/right/input/squeeze", "mode": "button", "inputs": { "click": { "output": "/actions/wm/in/show_keyboard" } } } ] } } } 07070100000049000081A4000000000000000000000001636E648600000538000000000000000000000000000000000000003900000000gxr/res/bindings/bindings_khronos_simple_controller.json{ "interaction_profile": "/interaction_profiles/khr/simple_controller", "bindings": { "/actions/wm/": { "sources": [ { "path": "/user/hand/left/input/select", "inputs": { "click": { "output": "/actions/wm/in/grab_window" } } }, { "path": "/user/hand/right/input/select", "inputs": { "click": { "output": "/actions/wm/in/grab_window" } } } ], "poses": [ { "path": "/user/hand/left/input/aim/pose", "output": "/actions/wm/in/hand_pose" }, { "path": "/user/hand/left/input/grip/pose", "output": "/actions/wm/in/hand_pose_hand_grip" }, { "path": "/user/hand/right/input/aim/pose", "output": "/actions/wm/in/hand_pose" }, { "path": "/user/hand/right/input/grip/pose", "output": "/actions/wm/in/hand_pose_hand_grip" } ], "haptics": [ { "path": "/user/hand/left/output/haptic", "output": "/actions/wm/out/haptic" }, { "path": "/user/hand/right/output/haptic", "output": "/actions/wm/out/haptic" } ] } } } 0707010000004A000081A4000000000000000000000001636E64860000138F000000000000000000000000000000000000003600000000gxr/res/bindings/bindings_valve_index_controller.json{ "interaction_profile": "/interaction_profiles/valve/index_controller", "bindings": { "/actions/wm/": { "sources": [ { "path": "/user/hand/left/input/trigger", "inputs": { "pull": { "output": "/actions/wm/in/grab_window" } } }, { "path": "/user/hand/left/input/trigger", "inputs": { "click": { "output": "/actions/wm/in/reset_orientation" } } }, { "path": "/user/hand/left/input/thumbstick", "inputs": { "click": { "output": "/actions/wm/in/show_keyboard" } } }, { "path": "/user/hand/left/input/trackpad", "inputs": { "position": { "output": "/actions/wm/in/push_pull" } } }, { "path": "/user/hand/left/input/thumbstick", "inputs": { "position": { "output": "/actions/wm/in/push_pull_scale" } } }, { "path": "/user/hand/left/input/b", "inputs": { "click": { "output": "/actions/wm/in/menu" } } }, { "path": "/user/hand/right/input/trigger", "inputs": { "pull": { "output": "/actions/wm/in/grab_window" } } }, { "path": "/user/hand/right/input/trigger", "inputs": { "click": { "output": "/actions/wm/in/reset_orientation" } } }, { "path": "/user/hand/right/input/thumbstick", "inputs": { "click": { "output": "/actions/wm/in/show_keyboard" } } }, { "path": "/user/hand/right/input/trackpad", "inputs": { "position": { "output": "/actions/wm/in/push_pull" } } }, { "path": "/user/hand/right/input/thumbstick", "inputs": { "position": { "output": "/actions/wm/in/push_pull_scale" } } }, { "path": "/user/hand/right/input/b", "inputs": { "click": { "output": "/actions/wm/in/menu" } } } ], "poses": [ { "path": "/user/hand/left/input/aim/pose", "output": "/actions/wm/in/hand_pose" }, { "path": "/user/hand/left/input/grip/pose", "output": "/actions/wm/in/hand_pose_hand_grip" }, { "path": "/user/hand/right/input/aim/pose", "output": "/actions/wm/in/hand_pose" }, { "path": "/user/hand/right/input/grip/pose", "output": "/actions/wm/in/hand_pose_hand_grip" } ], "haptics": [ { "path": "/user/hand/left/output/haptic", "output": "/actions/wm/out/haptic" }, { "path": "/user/hand/right/output/haptic", "output": "/actions/wm/out/haptic" } ] }, "/actions/mouse_synth": { "sources": [ { "path": "/user/hand/left/input/a", "inputs": { "click": { "output": "/actions/mouse_synth/in/left_click" } } }, { "path": "/user/hand/left/input/b", "inputs": { "click": { "output": "/actions/mouse_synth/in/right_click" } } }, { "path": "/user/hand/left/input/trackpad", "inputs": { "position": { "output": "/actions/mouse_synth/in/scroll" } } }, { "path": "/user/hand/left/input/trackpad", "inputs": { "touch": { "output": "/actions/mouse_synth/in/middle_click" } } }, { "path": "/user/hand/right/input/a", "inputs": { "click": { "output": "/actions/mouse_synth/in/left_click" } } }, { "path": "/user/hand/right/input/b", "inputs": { "click": { "output": "/actions/mouse_synth/in/right_click" } } }, { "path": "/user/hand/right/input/trackpad", "inputs": { "position": { "output": "/actions/mouse_synth/in/scroll" } } }, { "path": "/user/hand/right/input/trackpad", "inputs": { "touch": { "output": "/actions/mouse_synth/in/middle_click" } } } ] } } } 0707010000004B000081A4000000000000000000000001636E64860000007E000000000000000000000000000000000000001400000000gxr/res/meson.build# generate gresources test_resources = gnome.compile_resources( 'test_resources', 'tests.gresource.xml', source_dir : '.')0707010000004C000081A4000000000000000000000001636E648600000151000000000000000000000000000000000000001C00000000gxr/res/tests.gresource.xml<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/res"> <file>bindings/actions.json</file> <file>bindings/bindings_khronos_simple_controller.json</file> <file>bindings/bindings_htc_vive_controller.json</file> <file>bindings/bindings_valve_index_controller.json</file> </gresource> </gresources> 0707010000004D000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000C00000000gxr/scripts0707010000004E000081ED000000000000000000000001636E64860000003C000000000000000000000000000000000000001600000000gxr/scripts/format.shclang-format `git ls-files "*.h" "*.c" "*.frag" "*.vert"` -i0707010000004F000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000800000000gxr/src07070100000050000081A4000000000000000000000001636E64860000278F000000000000000000000000000000000000001700000000gxr/src/graphene-ext.c/* * Graphene Extensions * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "graphene-ext.h" #include <inttypes.h> // TODO: Move to upstream void graphene_ext_quaternion_to_float (const graphene_quaternion_t *q, float *dest) { graphene_vec4_t v; graphene_quaternion_to_vec4 (q, &v); graphene_vec4_to_float (&v, dest); } void graphene_ext_quaternion_print (const graphene_quaternion_t *q) { float f[4]; graphene_ext_quaternion_to_float (q, f); g_print ("| %f %f %f %f |\n", (double) f[0], (double) f[1], (double) f[2], (double) f[3]); } void graphene_ext_matrix_get_translation_vec3 (const graphene_matrix_t *m, graphene_vec3_t *res) { float f[16]; graphene_matrix_to_float (m, f); graphene_vec3_init (res, f[12], f[13], f[14]); } void graphene_ext_matrix_get_translation_point3d (const graphene_matrix_t *m, graphene_point3d_t *res) { float f[16]; graphene_matrix_to_float (m, f); graphene_point3d_init (res, f[12], f[13], f[14]); } void graphene_ext_matrix_set_translation_vec3 (graphene_matrix_t *m, const graphene_vec3_t *t) { float f[16]; graphene_matrix_to_float (m, f); f[12] = graphene_vec3_get_x (t); f[13] = graphene_vec3_get_y (t); f[14] = graphene_vec3_get_z (t); graphene_matrix_init_from_float (m, f); } void graphene_ext_matrix_set_translation_point3d (graphene_matrix_t *m, const graphene_point3d_t *t) { float f[16]; graphene_matrix_to_float (m, f); f[12] = t->x; f[13] = t->y; f[14] = t->z; graphene_matrix_init_from_float (m, f); } void graphene_ext_matrix_get_scale (const graphene_matrix_t *m, graphene_point3d_t *res) { float f[16]; graphene_matrix_to_float (m, f); graphene_vec3_t sx, sy, sz; graphene_vec3_init (&sx, f[0], f[1], f[2]); graphene_vec3_init (&sy, f[4], f[5], f[6]); graphene_vec3_init (&sz, f[8], f[9], f[10]); graphene_point3d_init (res, graphene_vec3_length (&sx), graphene_vec3_length (&sy), graphene_vec3_length (&sz)); } void graphene_ext_matrix_get_rotation_matrix (const graphene_matrix_t *m, graphene_point3d_t *scale, graphene_matrix_t *rotation) { float f[16]; graphene_matrix_to_float (m, f); graphene_ext_matrix_get_scale (m, scale); // clang-format off float r[16] = { f[0] / scale->x, f[1] / scale->x, f[2] / scale->x, 0, f[4] / scale->y, f[5] / scale->y, f[6] / scale->y, 0, f[8] / scale->z, f[9] / scale->z, f[10] / scale->z, 0, 0 , 0 , 0 , 1 }; // clang-format on graphene_matrix_init_from_float (rotation, r); } void graphene_ext_matrix_get_rotation_quaternion (const graphene_matrix_t *m, graphene_point3d_t *scale, graphene_quaternion_t *res) { graphene_matrix_t rot_m; graphene_ext_matrix_get_rotation_matrix (m, scale, &rot_m); graphene_quaternion_init_from_matrix (res, &rot_m); } void graphene_ext_matrix_get_rotation_angles (const graphene_matrix_t *m, float *deg_x, float *deg_y, float *deg_z) { graphene_quaternion_t q; graphene_point3d_t unused_scale; graphene_ext_matrix_get_rotation_quaternion (m, &unused_scale, &q); graphene_quaternion_to_angles (&q, deg_x, deg_y, deg_z); } /** * graphene_point_scale: * @p: a #graphene_point_t * @factor: the scaling factor * @res: (out caller-allocates): return location for the scaled point * * Scales the coordinates of the given #graphene_point_t by * the given @factor. */ void graphene_ext_point_scale (const graphene_point_t *p, float factor, graphene_point_t *res) { graphene_point_init (res, p->x * factor, p->y * factor); } void graphene_ext_ray_get_origin_vec4 (const graphene_ray_t *r, float w, graphene_vec4_t *res) { graphene_vec3_t o; graphene_ext_ray_get_origin_vec3 (r, &o); graphene_vec4_init_from_vec3 (res, &o, w); } void graphene_ext_ray_get_origin_vec3 (const graphene_ray_t *r, graphene_vec3_t *res) { graphene_point3d_t o; graphene_ray_get_origin (r, &o); graphene_point3d_to_vec3 (&o, res); } void graphene_ext_ray_get_direction_vec4 (const graphene_ray_t *r, float w, graphene_vec4_t *res) { graphene_vec3_t d; graphene_ray_get_direction (r, &d); graphene_vec4_init_from_vec3 (res, &d, w); } void graphene_ext_vec4_print (const graphene_vec4_t *v) { float f[4]; graphene_vec4_to_float (v, f); g_print ("| %f %f %f %f |\n", (double) f[0], (double) f[1], (double) f[2], (double) f[3]); } void graphene_ext_vec3_print (const graphene_vec3_t *v) { float f[3]; graphene_vec3_to_float (v, f); g_print ("| %f %f %f |\n", (double) f[0], (double) f[1], (double) f[2]); } void graphene_ext_matrix_interpolate_simple (const graphene_matrix_t *from, const graphene_matrix_t *to, float factor, graphene_matrix_t *result) { float from_f[16]; float to_f[16]; float interpolated_f[16]; graphene_matrix_to_float (from, from_f); graphene_matrix_to_float (to, to_f); for (uint32_t i = 0; i < 16; i++) interpolated_f[i] = from_f[i] * (1.0f - factor) + to_f[i] * factor; graphene_matrix_init_from_float (result, interpolated_f); } gboolean graphene_ext_matrix_decompose (const graphene_matrix_t *m, graphene_point3d_t *scale, graphene_quaternion_t *rotation, graphene_point3d_t *translation) { graphene_ext_matrix_get_translation_point3d (m, translation); graphene_ext_matrix_get_rotation_quaternion (m, scale, rotation); return true; } gboolean graphene_ext_matrix_validate (const graphene_matrix_t *m) { float f[16]; graphene_matrix_to_float (m, f); for (uint32_t i = 0; i < 16; i++) { if (isnan (f[i])) { g_assert ("Matrix component is NaN" && FALSE); return FALSE; } if (isinf (f[i])) { g_assert ("Matrix component is inf" && FALSE); return FALSE; } } return TRUE; } gboolean graphene_ext_quaternion_validate (const graphene_quaternion_t *q) { float f[4]; graphene_ext_quaternion_to_float (q, f); gboolean all_zero = TRUE; for (uint32_t i = 0; i < 4; i++) { if (isnan (f[i])) { g_assert ("Quaternion component is NaN" && FALSE); return FALSE; } if (isinf (f[i])) { g_assert ("Quaternion component is inf" && FALSE); return FALSE; } if (f[i] != 0) { all_zero = FALSE; } } if (all_zero) { g_assert ("Quaternion is all zero" && FALSE); return FALSE; } return TRUE; } gboolean graphene_ext_point3d_validate (const graphene_point3d_t *p) { float f[3] = {p->x, p->y, p->z}; for (uint32_t i = 0; i < 3; i++) { if (isnan (f[i])) { g_assert ("Point component is NaN" && FALSE); return FALSE; } if (isinf (f[i])) { g_assert ("Point component is inf" && FALSE); return FALSE; } } return TRUE; } gboolean graphene_ext_point3d_validate_all_nonzero (const graphene_point3d_t *p) { float f[3] = {p->x, p->y, p->z}; for (uint32_t i = 0; i < 3; i++) { if (isnan (f[i])) { g_assert ("Point component is NaN" && FALSE); return FALSE; } if (isinf (f[i])) { g_assert ("Point component is inf" && FALSE); return FALSE; } if (f[i] == 0) { g_assert ("Point component is zero" && FALSE); return FALSE; } } return TRUE; } gboolean graphene_ext_vec3_validate (const graphene_vec3_t *v) { float f[3]; graphene_vec3_to_float (v, f); for (uint32_t i = 0; i < 3; i++) { if (isnan (f[i])) { g_assert ("Vec3 component is NaN" && FALSE); return FALSE; } if (isinf (f[i])) { g_assert ("Vec3 component is inf" && FALSE); return FALSE; } } return TRUE; } bool graphene_ext_point3d_near (const graphene_point3d_t *a, const graphene_point3d_t *b, float epsilon) { if (fabsf (a->x - b->x) > epsilon) { return false; } if (fabsf (a->y - b->y) > epsilon) { return false; } if (fabsf (a->z - b->z) > epsilon) { return false; } return true; } bool graphene_ext_quaternion_near (const graphene_quaternion_t *a, const graphene_quaternion_t *b, float epsilon) { float af[4]; graphene_ext_quaternion_to_float (a, af); float bf[4]; graphene_ext_quaternion_to_float (b, bf); for (uint8_t i = 0; i < 4; i++) { // TODO: To pass recomposition tests we need to drop the signs. // Something in our recomposition is flipping them. See unit tests. af[i] = fabsf (af[i]); bf[i] = fabsf (bf[i]); if (fabsf (af[i] - bf[i]) > epsilon) { return false; } } return true; } 07070100000051000081A4000000000000000000000001636E648600000FC1000000000000000000000000000000000000001700000000gxr/src/graphene-ext.h/* * Graphene Extensions * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef XRD_GRAPHENE_EXT_H_ #define XRD_GRAPHENE_EXT_H_ #include <glib.h> #include <graphene.h> void graphene_ext_quaternion_to_float (const graphene_quaternion_t *q, float *dest); void graphene_ext_quaternion_print (const graphene_quaternion_t *q); void graphene_ext_matrix_get_translation_vec3 (const graphene_matrix_t *m, graphene_vec3_t *res); void graphene_ext_matrix_get_translation_point3d (const graphene_matrix_t *m, graphene_point3d_t *res); void graphene_ext_matrix_set_translation_vec3 (graphene_matrix_t *m, const graphene_vec3_t *t); void graphene_ext_matrix_set_translation_point3d (graphene_matrix_t *m, const graphene_point3d_t *t); void graphene_ext_matrix_get_scale (const graphene_matrix_t *m, graphene_point3d_t *res); void graphene_ext_matrix_get_rotation_matrix (const graphene_matrix_t *m, graphene_point3d_t *scale, graphene_matrix_t *rotation); void graphene_ext_matrix_get_rotation_quaternion (const graphene_matrix_t *m, graphene_point3d_t *scale, graphene_quaternion_t *res); void graphene_ext_matrix_get_rotation_angles (const graphene_matrix_t *m, float *deg_x, float *deg_y, float *deg_z); void graphene_ext_point_scale (const graphene_point_t *p, float factor, graphene_point_t *res); void graphene_ext_ray_get_origin_vec4 (const graphene_ray_t *r, float w, graphene_vec4_t *res); void graphene_ext_ray_get_origin_vec3 (const graphene_ray_t *r, graphene_vec3_t *res); void graphene_ext_ray_get_direction_vec4 (const graphene_ray_t *r, float w, graphene_vec4_t *res); void graphene_ext_vec4_print (const graphene_vec4_t *v); void graphene_ext_vec3_print (const graphene_vec3_t *v); void graphene_ext_matrix_interpolate_simple (const graphene_matrix_t *from, const graphene_matrix_t *to, float factor, graphene_matrix_t *result); gboolean graphene_ext_matrix_decompose (const graphene_matrix_t *m, graphene_point3d_t *scale, graphene_quaternion_t *rotation, graphene_point3d_t *translation); gboolean graphene_ext_matrix_validate (const graphene_matrix_t *m); gboolean graphene_ext_quaternion_validate (const graphene_quaternion_t *q); gboolean graphene_ext_point3d_validate (const graphene_point3d_t *p); gboolean graphene_ext_point3d_validate_all_nonzero (const graphene_point3d_t *p); gboolean graphene_ext_vec3_validate (const graphene_vec3_t *v); bool graphene_ext_quaternion_near (const graphene_quaternion_t *a, const graphene_quaternion_t *b, float epsilon); bool graphene_ext_point3d_near (const graphene_point3d_t *a, const graphene_point3d_t *b, float epsilon); #endif /* XRD_GRAPHENE_QUATERNION_H_ */ 07070100000052000081A4000000000000000000000001636E648600001D30000000000000000000000000000000000000001900000000gxr/src/gxr-action-set.c/* * gxr * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-action-set.h" #include "gxr-action.h" #include "gxr-context-private.h" #include "gxr-manifest.h" struct _GxrActionSet { GObject parent; GSList *actions; GxrContext *context; char *url; GxrManifest *manifest; XrActionSet handle; }; G_DEFINE_TYPE (GxrActionSet, gxr_action_set, G_TYPE_OBJECT) static void gxr_action_set_finalize (GObject *gobject); static void gxr_action_set_class_init (GxrActionSetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_action_set_finalize; } static void gxr_action_set_init (GxrActionSet *self) { self->actions = NULL; self->handle = XR_NULL_HANDLE; self->manifest = NULL; } static gboolean _url_to_name (char *url, char *name) { char *basename = g_path_get_basename (url); if (g_strcmp0 (basename, ".") == 0) { g_free (basename); return FALSE; } strncpy (name, basename, XR_MAX_ACTION_NAME_SIZE - 1); g_free (basename); return TRUE; } static void _printerr_xr_result (XrInstance instance, XrResult result) { char buffer[XR_MAX_RESULT_STRING_SIZE]; xrResultToString (instance, result, buffer); g_printerr ("%s\n", buffer); } GxrActionSet * gxr_action_set_new_from_url (GxrContext *context, GxrManifest *manifest, gchar *url) { GxrActionSet *self = (GxrActionSet *) g_object_new (GXR_TYPE_ACTION_SET, 0); self->context = context; self->manifest = g_object_ref (manifest); self->url = g_strdup (url); XrActionSetCreateInfo set_info = { .type = XR_TYPE_ACTION_SET_CREATE_INFO, .next = NULL, .priority = 0, }; /* TODO: proper names, localized name */ char name[XR_MAX_ACTION_NAME_SIZE]; _url_to_name (self->url, name); strcpy (set_info.actionSetName, name); strcpy (set_info.localizedActionSetName, name); XrInstance instance = gxr_context_get_openxr_instance (context); XrResult result = xrCreateActionSet (instance, &set_info, &self->handle); if (result != XR_SUCCESS) { g_printerr ("Failed to create action set: "); _printerr_xr_result (instance, result); g_clear_object (&self); } return self; } static void gxr_action_set_finalize (GObject *gobject) { GxrActionSet *self = GXR_ACTION_SET (gobject); g_slist_free_full (self->actions, g_object_unref); g_free (self->url); g_clear_object (&self->manifest); G_OBJECT_CLASS (gxr_action_set_parent_class)->finalize (gobject); } static gboolean _update (GxrActionSet **sets, uint32_t count) { if (count == 0) return TRUE; /* All actionsets must be attached to the same session */ XrInstance instance = gxr_context_get_openxr_instance (sets[0]->context); XrSession session = gxr_context_get_openxr_session (sets[0]->context); XrSessionState state = gxr_context_get_session_state (sets[0]->context); /* just pretend no input happens when we're not focused */ if (state != XR_SESSION_STATE_FOCUSED) return TRUE; XrActiveActionSet *active_action_sets = g_malloc (sizeof (XrActiveActionSet) * count); for (uint32_t i = 0; i < count; i++) { GxrActionSet *self = sets[i]; active_action_sets[i].actionSet = self->handle; active_action_sets[i].subactionPath = XR_NULL_PATH; } XrActionsSyncInfo syncInfo = { .type = XR_TYPE_ACTIONS_SYNC_INFO, .countActiveActionSets = count, .activeActionSets = active_action_sets, }; XrResult result = xrSyncActions (session, &syncInfo); g_free (active_action_sets); if (result == XR_SESSION_NOT_FOCUSED) { /* xrSyncActions can be called before reading the session state change */ g_debug ("SyncActions while session not focused"); return TRUE; } if (result != XR_SUCCESS) { g_printerr ("ERROR: SyncActions: "); _printerr_xr_result (instance, result); return FALSE; } return TRUE; } gboolean gxr_action_sets_poll (GxrActionSet **sets, uint32_t count) { if (!_update (sets, count)) return FALSE; for (uint32_t i = 0; i < count; i++) { for (GSList *l = sets[i]->actions; l != NULL; l = l->next) { GxrAction *action = (GxrAction *) l->data; /* haptic has no inputs, can't be polled */ if (gxr_action_get_action_type (action) == GXR_ACTION_HAPTIC) continue; if (!gxr_action_poll (action)) return FALSE; } } return TRUE; } GxrAction * gxr_action_set_connect_digital_from_float (GxrActionSet *self, gchar *url, float threshold, char *haptic_url, GCallback callback, gpointer data) { GxrAction *action = gxr_action_new_from_type_url (self->context, self, GXR_ACTION_DIGITAL_FROM_FLOAT, url); if (action != NULL) self->actions = g_slist_append (self->actions, action); GxrAction *haptic_action = NULL; if (haptic_url) { haptic_action = gxr_action_new_from_type_url (self->context, self, GXR_ACTION_HAPTIC, haptic_url); if (haptic_action != NULL) { self->actions = g_slist_append (self->actions, haptic_action); gxr_action_set_digital_from_float_haptic (action, haptic_action); } } gxr_action_set_digital_from_float_threshold (action, threshold); g_signal_connect (action, "digital-event", callback, data); return action; } gboolean gxr_action_set_connect (GxrActionSet *self, GxrActionType type, gchar *url, GCallback callback, gpointer data) { GxrAction *action = gxr_action_new_from_type_url (self->context, self, type, url); if (action != NULL) self->actions = g_slist_append (self->actions, action); else { g_printerr ("Failed to create/connect action %s\n", url); return FALSE; } switch (type) { case GXR_ACTION_DIGITAL: g_signal_connect (action, "digital-event", callback, data); break; case GXR_ACTION_FLOAT: case GXR_ACTION_VEC2F: g_signal_connect (action, "analog-event", callback, data); break; case GXR_ACTION_POSE: g_signal_connect (action, "pose-event", callback, data); break; case GXR_ACTION_HAPTIC: /* no input, only output */ break; default: g_printerr ("Unknown action type %d\n", type); return FALSE; } return TRUE; } void gxr_action_set_append_action (GxrActionSet *self, GxrAction *action) { self->actions = g_slist_append (self->actions, g_object_ref (action)); } GSList * gxr_action_set_get_actions (GxrActionSet *self) { return self->actions; } XrActionSet gxr_action_set_get_handle (GxrActionSet *self) { return self->handle; } GxrManifest * gxr_action_set_get_manifest (GxrActionSet *self) { return self->manifest; } 07070100000053000081A4000000000000000000000001636E648600000734000000000000000000000000000000000000001900000000gxr/src/gxr-action-set.h/* * gxr * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_ACTION_SET_H_ #define GXR_ACTION_SET_H_ #if !defined(GXR_INSIDE) && !defined(GXR_COMPILATION) #error "Only <gxr.h> can be included directly." #endif #include <glib-object.h> #include <stdint.h> #include "gxr-action.h" #include "gxr-manifest.h" G_BEGIN_DECLS #define GXR_TYPE_ACTION_SET gxr_action_set_get_type () G_DECLARE_FINAL_TYPE (GxrActionSet, gxr_action_set, GXR, ACTION_SET, GObject) /** * GxrActionSetClass: * @parent: The parent class */ struct _GxrActionSetClass { GObjectClass parent; }; GxrActionSet * gxr_action_set_new_from_url (GxrContext *context, GxrManifest *manifest, gchar *url); gboolean gxr_action_sets_poll (GxrActionSet **sets, uint32_t count); gboolean gxr_action_set_connect (GxrActionSet *self, GxrActionType type, gchar *url, GCallback callback, gpointer data); GxrAction * gxr_action_set_connect_digital_from_float (GxrActionSet *self, gchar *url, float threshold, char *haptic_url, GCallback callback, gpointer data); GSList * gxr_action_set_get_actions (GxrActionSet *self); XrActionSet gxr_action_set_get_handle (GxrActionSet *self); GxrManifest * gxr_action_set_get_manifest (GxrActionSet *self); void gxr_action_set_append_action (GxrActionSet *self, GxrAction *action); G_END_DECLS #endif /* GXR_ACTION_SET_H_ */ 07070100000054000081A4000000000000000000000001636E648600005495000000000000000000000000000000000000001500000000gxr/src/gxr-action.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-action.h" #include <gdk/gdk.h> #include "gxr-action-set.h" #include "gxr-context-private.h" #include "gxr-controller.h" // TODO: Do not hardcode this #define NUM_HANDS 2 struct _GxrAction { GObject parent; GxrActionSet *action_set; gchar *url; GxrActionType type; GxrContext *context; XrInstance instance; XrSession session; XrPath hand_paths[NUM_HANDS]; /* only used when this action is a pose action*/ XrSpace hand_spaces[NUM_HANDS]; XrSpace tracked_space; XrAction handle; // gxr API has delta from last values, but OpenXR does not graphene_vec3_t last_vec[NUM_HANDS]; /* Only used for DIGITAL_FROM_FLOAT */ float threshold; float last_float[NUM_HANDS]; gboolean last_bool[NUM_HANDS]; GxrAction *haptic_action; }; G_DEFINE_TYPE (GxrAction, gxr_action, G_TYPE_OBJECT) enum { DIGITAL_EVENT, ANALOG_EVENT, POSE_EVENT, LAST_SIGNAL }; static guint action_signals[LAST_SIGNAL] = {0}; static void gxr_action_finalize (GObject *gobject); gboolean gxr_action_load_handle (GxrAction *self, char *url); static void gxr_action_class_init (GxrActionClass *klass) { action_signals[DIGITAL_EVENT] = g_signal_new ("digital-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); action_signals[ANALOG_EVENT] = g_signal_new ("analog-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); action_signals[POSE_EVENT] = g_signal_new ("pose-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_action_finalize; } static void gxr_action_init (GxrAction *self) { self->action_set = NULL; self->url = NULL; self->handle = XR_NULL_HANDLE; for (int i = 0; i < NUM_HANDS; i++) { self->hand_spaces[i] = XR_NULL_HANDLE; self->last_float[i] = 0.0f; self->last_bool[i] = FALSE; graphene_vec3_init (&self->last_vec[i], 0, 0, 0); } self->threshold = 0.0f; self->haptic_action = NULL; } GxrAction * gxr_action_new (GxrContext *context) { GxrAction *self = (GxrAction *) g_object_new (GXR_TYPE_ACTION, 0); /* TODO: Handle this more nicely */ self->context = context; self->instance = gxr_context_get_openxr_instance (context); self->session = gxr_context_get_openxr_session (context); self->tracked_space = gxr_context_get_tracked_space (context); xrStringToPath (self->instance, "/user/hand/left", &self->hand_paths[0]); xrStringToPath (self->instance, "/user/hand/right", &self->hand_paths[1]); return self; } static gboolean _url_to_name (char *url, char *name) { char *basename = g_path_get_basename (url); if (g_strcmp0 (basename, ".") == 0) { g_free (basename); return FALSE; } strncpy (name, basename, XR_MAX_ACTION_NAME_SIZE - 1); g_free (basename); return TRUE; } GxrAction * gxr_action_new_from_type_url (GxrContext *context, GxrActionSet *action_set, GxrActionType type, char *url) { GxrAction *self = gxr_action_new (context); gxr_action_set_action_type (GXR_ACTION (self), type); gxr_action_set_url (GXR_ACTION (self), g_strdup (url)); gxr_action_set_action_set (GXR_ACTION (self), action_set); XrActionType action_type; switch (type) { case GXR_ACTION_DIGITAL_FROM_FLOAT: case GXR_ACTION_FLOAT: action_type = XR_ACTION_TYPE_FLOAT_INPUT; break; case GXR_ACTION_VEC2F: action_type = XR_ACTION_TYPE_VECTOR2F_INPUT; break; case GXR_ACTION_DIGITAL: action_type = XR_ACTION_TYPE_BOOLEAN_INPUT; break; case GXR_ACTION_POSE: action_type = XR_ACTION_TYPE_POSE_INPUT; break; case GXR_ACTION_HAPTIC: action_type = XR_ACTION_TYPE_VIBRATION_OUTPUT; break; default: g_printerr ("Unknown action type %d\n", type); action_type = XR_ACTION_TYPE_BOOLEAN_INPUT; } XrActionCreateInfo action_info = { .type = XR_TYPE_ACTION_CREATE_INFO, .next = NULL, .actionType = action_type, .countSubactionPaths = NUM_HANDS, .subactionPaths = self->hand_paths, }; /* TODO: proper names, localized name */ /* TODO: proper names, localized name */ char name[XR_MAX_ACTION_NAME_SIZE]; _url_to_name (self->url, name); strcpy (action_info.actionName, name); strcpy (action_info.localizedActionName, name); XrActionSet set = gxr_action_set_get_handle (action_set); XrResult result = xrCreateAction (set, &action_info, &self->handle); if (result != XR_SUCCESS) { char buffer[XR_MAX_RESULT_STRING_SIZE]; xrResultToString (self->instance, result, buffer); g_printerr ("Failed to create action %s: %s\n", url, buffer); g_object_unref (self); self = NULL; } if (action_type == XR_ACTION_TYPE_POSE_INPUT) { for (int i = 0; i < NUM_HANDS; i++) { XrActionSpaceCreateInfo action_space_info = { .type = XR_TYPE_ACTION_SPACE_CREATE_INFO, .next = NULL, .action = self->handle, .poseInActionSpace.orientation.w = 1.f, .subactionPath = self->hand_paths[i], }; result = xrCreateActionSpace (self->session, &action_space_info, &self->hand_spaces[i]); if (result != XR_SUCCESS) { char buffer[XR_MAX_RESULT_STRING_SIZE]; xrResultToString (self->instance, result, buffer); g_printerr ("Failed to create action space %s: %s\n", url, buffer); g_object_unref (self); self = NULL; } } } return self; } static XrPath _handle_to_subaction (GxrAction *self, guint64 handle) { return self->hand_paths[handle]; } /* equivalent to openvr: "Time relative to now when this event happened" */ static float _get_time_diff (XrTime xr_time) { (void) xr_time; /* TODO: for now pretend everything happens right now */ return 0; } static gboolean _action_poll_digital (GxrAction *self) { GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); GSList *controllers = gxr_device_manager_get_controllers (dm); for (GSList *l = controllers; l; l = l->next) { GxrController *controller = GXR_CONTROLLER (l->data); guint64 controller_handle = gxr_device_get_handle (GXR_DEVICE (controller)); XrPath subaction_path = _handle_to_subaction (self, controller_handle); XrActionStateGetInfo get_info = { .type = XR_TYPE_ACTION_STATE_GET_INFO, .next = NULL, .action = self->handle, .subactionPath = subaction_path, }; XrActionStateBoolean value = { .type = XR_TYPE_ACTION_STATE_BOOLEAN, }; XrResult result = xrGetActionStateBoolean (self->session, &get_info, &value); if (result != XR_SUCCESS) { g_debug ("Failed to poll digital action"); continue; } if (!controller) { g_print ("Digital without controller\n"); return TRUE; } GxrDigitalEvent event = { .controller = controller, .active = (gboolean) value.isActive, .state = (gboolean) value.currentState, .changed = (gboolean) value.changedSinceLastSync, .time = _get_time_diff (value.lastChangeTime), }; gxr_action_emit_digital (GXR_ACTION (self), &event); } return TRUE; } static gboolean _threshold_passed (float threshold, float last, float current) { return (last < threshold && current >= threshold) || (last >= threshold && current <= threshold); } static gboolean _action_poll_digital_from_float (GxrAction *self) { GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); GSList *controllers = gxr_device_manager_get_controllers (dm); for (GSList *l = controllers; l; l = l->next) { GxrController *controller = GXR_CONTROLLER (l->data); guint64 controller_handle = gxr_device_get_handle (GXR_DEVICE (controller)); XrPath subaction_path = _handle_to_subaction (self, controller_handle); XrActionStateGetInfo get_info = { .type = XR_TYPE_ACTION_STATE_GET_INFO, .next = NULL, .action = self->handle, .subactionPath = subaction_path, }; XrActionStateFloat value = { .type = XR_TYPE_ACTION_STATE_FLOAT, }; XrResult result = xrGetActionStateFloat (self->session, &get_info, &value); if (result != XR_SUCCESS) { g_debug ("Failed to poll float action"); continue; } if (self->haptic_action && _threshold_passed (self->threshold, self->last_float[controller_handle], value.currentState)) { g_debug ("Threshold %f passed, triggering haptic", self->threshold); gxr_action_trigger_haptic (GXR_ACTION (self->haptic_action), 0.f, 0.03f, 50.f, 0.4f, controller_handle); } gboolean currentState = value.currentState >= self->threshold; GxrDigitalEvent event = { .controller = controller, .active = (gboolean) value.isActive, .state = (gboolean) currentState, .changed = (gboolean) (value.changedSinceLastSync && currentState != self->last_bool[controller_handle]), .time = _get_time_diff (value.lastChangeTime), }; gxr_action_emit_digital (GXR_ACTION (self), &event); self->last_float[controller_handle] = value.currentState; self->last_bool[controller_handle] = currentState; } return TRUE; } static gboolean _action_poll_analog (GxrAction *self) { GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); GSList *controllers = gxr_device_manager_get_controllers (dm); for (GSList *l = controllers; l; l = l->next) { GxrController *controller = GXR_CONTROLLER (l->data); guint64 controller_handle = gxr_device_get_handle (GXR_DEVICE (controller)); XrPath subaction_path = _handle_to_subaction (self, controller_handle); XrActionStateGetInfo get_info = { .type = XR_TYPE_ACTION_STATE_GET_INFO, .next = NULL, .action = self->handle, .subactionPath = subaction_path, }; XrActionStateFloat value = { .type = XR_TYPE_ACTION_STATE_FLOAT, }; XrResult result = xrGetActionStateFloat (self->session, &get_info, &value); if (result != XR_SUCCESS) { g_debug ("Failed to poll float action"); continue; } GxrAnalogEvent event = { .controller = controller, .active = (gboolean) value.isActive, .time = _get_time_diff (value.lastChangeTime), }; graphene_vec3_init (&event.state, value.currentState, 0, 0); graphene_vec3_subtract (&event.state, &self->last_vec[controller_handle], &event.delta); gxr_action_emit_analog (GXR_ACTION (self), &event); graphene_vec3_init_from_vec3 (&self->last_vec[controller_handle], &event.state); } return TRUE; } static gboolean _action_poll_vec2f (GxrAction *self) { GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); GSList *controllers = gxr_device_manager_get_controllers (dm); for (GSList *l = controllers; l; l = l->next) { GxrController *controller = GXR_CONTROLLER (l->data); guint64 controller_handle = gxr_device_get_handle (GXR_DEVICE (controller)); XrPath subaction_path = _handle_to_subaction (self, controller_handle); XrActionStateGetInfo get_info = { .type = XR_TYPE_ACTION_STATE_GET_INFO, .next = NULL, .action = self->handle, .subactionPath = subaction_path, }; XrActionStateVector2f value = { .type = XR_TYPE_ACTION_STATE_VECTOR2F, }; XrResult result = xrGetActionStateVector2f (self->session, &get_info, &value); if (result != XR_SUCCESS) { g_debug ("Failed to poll vec2f action"); continue; } GxrAnalogEvent event = { .controller = controller, .active = (gboolean) value.isActive, .time = _get_time_diff (value.lastChangeTime), }; graphene_vec3_init (&event.state, value.currentState.x, value.currentState.y, 0); graphene_vec3_subtract (&event.state, &self->last_vec[controller_handle], &event.delta); gxr_action_emit_analog (GXR_ACTION (self), &event); graphene_vec3_init_from_vec3 (&self->last_vec[controller_handle], &event.state); } return TRUE; } static gboolean _space_location_valid (XrSpaceLocation *sl) { return /* (sl->locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && */ (sl->locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0; } static void _get_model_matrix_from_pose (XrPosef *pose, graphene_matrix_t *mat) { graphene_quaternion_t q; graphene_quaternion_init (&q, pose->orientation.x, pose->orientation.y, pose->orientation.z, pose->orientation.w); graphene_matrix_init_identity (mat); graphene_matrix_rotate_quaternion (mat, &q); graphene_point3d_t translation = { pose->position.x, pose->position.y, pose->position.z, }; graphene_matrix_translate (mat, &translation); } static gboolean _action_poll_pose_secs_from_now (GxrAction *self, float secs) { (void) secs; GxrDeviceManager *dm = gxr_context_get_device_manager (self->context); GSList *controllers = gxr_device_manager_get_controllers (dm); for (GSList *l = controllers; l; l = l->next) { GxrController *controller = GXR_CONTROLLER (l->data); guint64 controller_handle = gxr_device_get_handle (GXR_DEVICE (controller)); XrPath subaction_path = _handle_to_subaction (self, controller_handle); XrActionStateGetInfo get_info = { .type = XR_TYPE_ACTION_STATE_GET_INFO, .action = self->handle, .subactionPath = subaction_path, }; XrActionStatePose value = { .type = XR_TYPE_ACTION_STATE_POSE, }; XrResult result = xrGetActionStatePose (self->session, &get_info, &value); if (result != XR_SUCCESS) { g_debug ("Failed to poll analog action"); continue; } gboolean spaceLocationValid; XrSpaceLocation space_location = { .type = XR_TYPE_SPACE_LOCATION, }; /* TODO: secs from now ignored, API not appropriate for OpenXR */ XrTime time = gxr_context_get_predicted_display_time (self->context); XrSpace hand_space = self->hand_spaces[controller_handle]; result = xrLocateSpace (hand_space, self->tracked_space, time, &space_location); if (result != XR_SUCCESS) { g_debug ("Failed to poll hand space location"); continue; } spaceLocationValid = _space_location_valid (&space_location); /* g_print("Polled space %s active %d valid %d, %f %f %f\n", self->url, value.isActive, spaceLocationValid, space_location.pose.position.x, space_location.pose.position.y, space_location.pose.position.z ); */ GxrPoseEvent event = { .active = value.isActive == XR_TRUE, .controller = controller, .valid = spaceLocationValid, .device_connected = value.isActive == XR_TRUE, }; _get_model_matrix_from_pose (&space_location.pose, &event.pose); graphene_vec3_init (&event.velocity, 0, 0, 0); graphene_vec3_init (&event.angular_velocity, 0, 0, 0); gxr_action_emit_pose (GXR_ACTION (self), &event); } return TRUE; } gboolean gxr_action_poll (GxrAction *self) { GxrActionType type = gxr_action_get_action_type (self); switch (type) { case GXR_ACTION_DIGITAL: return _action_poll_digital (self); case GXR_ACTION_DIGITAL_FROM_FLOAT: return _action_poll_digital_from_float (self); case GXR_ACTION_FLOAT: return _action_poll_analog (self); case GXR_ACTION_VEC2F: return _action_poll_vec2f (self); case GXR_ACTION_POSE: return _action_poll_pose_secs_from_now (self, 0); default: g_printerr ("Unknown action type %d\n", type); return FALSE; } } gboolean gxr_action_trigger_haptic (GxrAction *self, float start_seconds_from_now, float duration_seconds, float frequency, float amplitude, guint64 controller_handle) { (void) start_seconds_from_now; XrTime duration = (XrTime) ((double) duration_seconds * 1000. * 1000. * 1000.); // g_debug ("Haptic %f %f Hz, %lu ns", amplitude, frequency, duration); XrHapticVibration vibration = { .type = XR_TYPE_HAPTIC_VIBRATION, .next = NULL, .amplitude = amplitude, .duration = duration, .frequency = frequency, }; XrHapticActionInfo hapticActionInfo = { .type = XR_TYPE_HAPTIC_ACTION_INFO, .next = NULL, .action = self->handle, .subactionPath = self->hand_paths[controller_handle], }; XrResult result = xrApplyHapticFeedback (self->session, &hapticActionInfo, (const XrHapticBaseHeader *) &vibration); return result == XR_SUCCESS; } static void gxr_action_finalize (GObject *gobject) { GxrAction *self = GXR_ACTION (gobject); if (self->haptic_action) g_clear_object (&self->haptic_action); g_free (self->url); } GxrActionType gxr_action_get_action_type (GxrAction *self) { return self->type; } GxrActionSet * gxr_action_get_action_set (GxrAction *self) { return self->action_set; } gchar * gxr_action_get_url (GxrAction *self) { return self->url; } void gxr_action_set_action_type (GxrAction *self, GxrActionType type) { self->type = type; } void gxr_action_set_action_set (GxrAction *self, GxrActionSet *action_set) { self->action_set = action_set; } void gxr_action_set_url (GxrAction *self, gchar *url) { self->url = url; } void gxr_action_emit_digital (GxrAction *self, GxrDigitalEvent *event) { g_signal_emit (self, action_signals[DIGITAL_EVENT], 0, event); } void gxr_action_emit_analog (GxrAction *self, GxrAnalogEvent *event) { g_signal_emit (self, action_signals[ANALOG_EVENT], 0, event); } void gxr_action_emit_pose (GxrAction *self, GxrPoseEvent *event) { g_signal_emit (self, action_signals[POSE_EVENT], 0, event); } void gxr_action_set_digital_from_float_threshold (GxrAction *self, float threshold) { self->threshold = threshold; } void gxr_action_set_digital_from_float_haptic (GxrAction *self, GxrAction *haptic_action) { if (self->haptic_action) g_clear_object (&self->haptic_action); self->haptic_action = g_object_ref (haptic_action); } /* creates controllers with handles 0 for left hand and 1 for right hand. * TODO: create controllers dynamically for the inputs bound to this action */ void gxr_action_update_controllers (GxrAction *self) { GxrContext *context = GXR_CONTEXT (self->context); GxrDeviceManager *dm = gxr_context_get_device_manager (context); for (guint64 i = 0; i < NUM_HANDS; i++) { guint64 controller_handle = i; if (!gxr_device_manager_get (dm, controller_handle)) { gxr_device_manager_add (dm, controller_handle, TRUE); g_debug ("Added controller %lu from action %s", controller_handle, self->url); } } } XrAction gxr_action_get_handle (GxrAction *self) { return self->handle; } GxrAction * gxr_action_get_haptic_action (GxrAction *self) { return self->haptic_action; } 07070100000055000081A4000000000000000000000001636E648600000BFA000000000000000000000000000000000000001500000000gxr/src/gxr-action.h/* * gxr * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_ACTION_H_ #define GXR_ACTION_H_ #if !defined(GXR_INSIDE) && !defined(GXR_COMPILATION) #error "Only <gxr.h> can be included directly." #endif #include <glib-object.h> #include <openxr/openxr.h> #include "gxr-controller.h" G_BEGIN_DECLS #define GXR_TYPE_ACTION gxr_action_get_type () G_DECLARE_FINAL_TYPE (GxrAction, gxr_action, GXR, ACTION, GObject) #ifndef __GTK_DOC_IGNORE__ typedef struct _GxrContext GxrContext; typedef struct _GxrActionSet GxrActionSet; #endif /** * GxrActionClass: * @parent: The parent class */ struct _GxrActionClass { GObjectClass parent; }; /** * GxrActionType: * @GXR_ACTION_DIGITAL: A digital action. * @GXR_ACTION_DIGITAL_FROM_FLOAT: A digital action constructed from float *thresholds. * @GXR_ACTION_VEC2F: An analog action with floats x,y. * @GXR_ACTION_FLOAT: An analog action. * @GXR_ACTION_POSE: A pose action. * @GXR_ACTION_HAPTIC: A haptic action. * * The type of the GxrAction. * **/ typedef enum { GXR_ACTION_DIGITAL, GXR_ACTION_DIGITAL_FROM_FLOAT, GXR_ACTION_VEC2F, GXR_ACTION_FLOAT, GXR_ACTION_POSE, GXR_ACTION_HAPTIC } GxrActionType; GxrAction * gxr_action_new (GxrContext *context); GxrAction * gxr_action_new_from_type_url (GxrContext *context, GxrActionSet *action_set, GxrActionType type, char *url); gboolean gxr_action_poll (GxrAction *self); gboolean gxr_action_trigger_haptic (GxrAction *self, float start_seconds_from_now, float duration_seconds, float frequency, float amplitude, guint64 controller_handle); GxrActionType gxr_action_get_action_type (GxrAction *self); GxrActionSet * gxr_action_get_action_set (GxrAction *self); gchar * gxr_action_get_url (GxrAction *self); void gxr_action_set_action_type (GxrAction *self, GxrActionType type); void gxr_action_set_action_set (GxrAction *self, GxrActionSet *action_set); void gxr_action_set_url (GxrAction *self, gchar *url); void gxr_action_emit_digital (GxrAction *self, GxrDigitalEvent *event); void gxr_action_emit_analog (GxrAction *self, GxrAnalogEvent *event); void gxr_action_emit_pose (GxrAction *self, GxrPoseEvent *event); void gxr_action_set_digital_from_float_threshold (GxrAction *self, float threshold); void gxr_action_set_digital_from_float_haptic (GxrAction *self, GxrAction *haptic_action); void gxr_action_update_controllers (GxrAction *self); uint32_t gxr_action_get_num_bindings (GxrAction *self); void gxr_action_set_bindings (GxrAction *self, XrActionSuggestedBinding *bindings); XrAction gxr_action_get_handle (GxrAction *self); GxrAction * gxr_action_get_haptic_action (GxrAction *self); G_END_DECLS #endif /* GXR_ACTION_H_ */ 07070100000056000081A4000000000000000000000001636E648600000261000000000000000000000000000000000000001E00000000gxr/src/gxr-context-private.h/* * gxr * Copyright 2020 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_CONTEXT_PRIVATE_H_ #define GXR_CONTEXT_PRIVATE_H_ #include "gxr-context.h" #include "gxr-manifest.h" XrInstance gxr_context_get_openxr_instance (GxrContext *self); XrSession gxr_context_get_openxr_session (GxrContext *self); XrSpace gxr_context_get_tracked_space (GxrContext *self); XrTime gxr_context_get_predicted_display_time (GxrContext *self); XrSessionState gxr_context_get_session_state (GxrContext *self); #endif /* GXR_CONTEXT_PRIVATE_H_ */ 07070100000057000081A4000000000000000000000001636E64860001211B000000000000000000000000000000000000001600000000gxr/src/gxr-context.c/* * gxr * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-context-private.h" #define XR_USE_PLATFORM_XLIB 1 #define XR_USE_GRAPHICS_API_VULKAN 1 #include <openxr/openxr.h> #include <openxr/openxr_platform.h> #include <openxr/openxr_reflection.h> #include "gxr-controller.h" #include "gxr-version.h" // TODO: Do not hardcode this #define NUM_CONTROLLERS 2 struct _GxrContext { GObject parent; GulkanContext *gc; struct { gboolean vulkan_enable2; gboolean overlay; } extensions; XrEnvironmentBlendMode blend_mode; GxrDeviceManager *device_manager; XrInstance instance; XrSession session; XrReferenceSpaceType play_space_type; XrSpace play_space; XrSpace view_space; XrSystemId system_id; XrViewConfigurationType view_config_type; /* One array per eye */ XrSwapchain swapchain; XrSwapchainImageVulkanKHR *images; /* last acquired swapchain image index per swapchain */ uint32_t buffer_index; /* for each view */ uint32_t swapchain_length; /* 1 framebuffer for each swapchain image, for each swapchain (1 per view) */ GulkanFrameBuffer **framebuffers; VkExtent2D framebuffer_extent; VkSampleCountFlagBits framebuffer_sample_count; XrCompositionLayerProjectionView *projection_views; XrViewConfigurationView *configuration_views; XrGraphicsBindingVulkanKHR graphics_binding; uint32_t view_count; XrSessionState session_state; gboolean should_render; gboolean have_valid_pose; // to avoid beginning an already running session gboolean session_running; // run begin/end frame cycle only when we are in certain states gboolean should_submit_frames; XrCompositionLayerProjection projection_layer; volatile XrTime predicted_display_time; volatile XrDuration predicted_display_period; XrView *views; int64_t swapchain_format; XrVersion desired_vk_version; GMutex wait_frame_mutex; }; G_DEFINE_TYPE (GxrContext, gxr_context, G_TYPE_OBJECT) enum { STATE_CHANGE_EVENT, OVERLAY_EVENT, LAST_SIGNAL }; static guint context_signals[LAST_SIGNAL] = {0}; static void gxr_context_finalize (GObject *gobject); static void gxr_context_class_init (GxrContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_context_finalize; context_signals[STATE_CHANGE_EVENT] = g_signal_new ("state-change-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE); context_signals[OVERLAY_EVENT] = g_signal_new ("overlay-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE); } static const char *viewport_config_name = "/viewport_configuration/vr"; static const char * _check_xr_result_to_string (XrResult result) { switch (result) { #define MAKE_CASE(VAL, _) \ case VAL: \ return #VAL; XR_LIST_ENUM_XrResult (MAKE_CASE) default : return "UNKNOWN"; } } #define BUF_LEN 1024 static gboolean _check_xr_result (XrResult result, const char *format, ...) { if (XR_SUCCEEDED (result)) return TRUE; const char *result_str = _check_xr_result_to_string (result); char msg[BUF_LEN] = {0}; g_snprintf (msg, BUF_LEN, "[%s] ", result_str); gulong result_written_len = (gulong) strlen (msg); va_list args; va_start (args, format); g_vsnprintf (msg + result_written_len, BUF_LEN - result_written_len, format, args); va_end (args); g_warning ("%s", msg); return FALSE; } static gboolean _is_extension_supported (char *name, XrExtensionProperties *props, uint32_t count) { for (uint32_t i = 0; i < count; i++) if (!strcmp (name, props[i].extensionName)) return TRUE; return FALSE; } static gboolean _check_extensions (GxrContext *self) { XrResult result; uint32_t instanceExtensionCount = 0; result = xrEnumerateInstanceExtensionProperties (NULL, 0, &instanceExtensionCount, NULL); if (!_check_xr_result (result, "Failed to enumerate number of instance " "extension properties")) return FALSE; XrExtensionProperties *instanceExtensionProperties = g_malloc (sizeof (XrExtensionProperties) * instanceExtensionCount); for (uint16_t i = 0; i < instanceExtensionCount; i++) instanceExtensionProperties[i] = (XrExtensionProperties){ .type = XR_TYPE_EXTENSION_PROPERTIES, }; result = xrEnumerateInstanceExtensionProperties (NULL, instanceExtensionCount, &instanceExtensionCount, instanceExtensionProperties); if (!_check_xr_result (result, "Failed to enumerate extension properties")) return FALSE; self->extensions.vulkan_enable2 = _is_extension_supported (XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, instanceExtensionProperties, instanceExtensionCount); self->extensions.overlay = _is_extension_supported (XR_EXTX_OVERLAY_EXTENSION_NAME, instanceExtensionProperties, instanceExtensionCount); g_debug ("%s extension supported: %d", XR_EXTX_OVERLAY_EXTENSION_NAME, self->extensions.overlay); g_free (instanceExtensionProperties); if (!self->extensions.vulkan_enable2) { g_printerr ("Runtime does not support instance extension %s\n", XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME); return FALSE; } return TRUE; } static gboolean _check_blend_mode (GxrContext *self) { XrResult result; uint32_t blend_modes_count = 0; result = xrEnumerateEnvironmentBlendModes (self->instance, self->system_id, self->view_config_type, 0, &blend_modes_count, NULL); if (!_check_xr_result (result, "Failed to get blend modes count")) return FALSE; XrEnvironmentBlendMode *blend_modes = g_malloc (sizeof (XrEnvironmentBlendMode) * blend_modes_count); result = xrEnumerateEnvironmentBlendModes (self->instance, self->system_id, self->view_config_type, blend_modes_count, &blend_modes_count, blend_modes); if (!_check_xr_result (result, "Failed to get blend modes")) { g_free (blend_modes); return FALSE; } for (uint32_t i = 0; i < blend_modes_count; i++) { if (blend_modes[i] == XR_ENVIRONMENT_BLEND_MODE_OPAQUE) { self->blend_mode = blend_modes[i]; break; } } if (self->blend_mode == 0) { g_warning ("XR_ENVIRONMENT_BLEND_MODE_OPAQUE not supported, fallback: %d", blend_modes[0]); self->blend_mode = blend_modes[0]; } g_free (blend_modes); return TRUE; } static gboolean _create_instance (GxrContext *self, char *app_name, uint32_t app_version) { // vulkan_enable2 is required. overlay is optional. // list will need to be dynamic when more optional extensions are used. const char *const enabled_extensions[] = { XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME, XR_EXTX_OVERLAY_EXTENSION_NAME, }; XrInstanceCreateInfo instanceCreateInfo = { .type = XR_TYPE_INSTANCE_CREATE_INFO, .createFlags = 0, .enabledExtensionCount = self->extensions.overlay ? 2 : 1, .enabledExtensionNames = enabled_extensions, .enabledApiLayerCount = 0, .applicationInfo = { .applicationVersion = app_version, .engineName = "gxr", .engineVersion = GXR_VERSION_HEX, .apiVersion = XR_CURRENT_API_VERSION, }, }; strncpy (instanceCreateInfo.applicationInfo.applicationName, app_name, XR_MAX_APPLICATION_NAME_SIZE); XrResult result; result = xrCreateInstance (&instanceCreateInfo, &self->instance); if (!_check_xr_result (result, "Failed to create XR instance.")) return FALSE; return TRUE; } static gboolean _create_system (GxrContext *self) { XrPath vrConfigName; XrResult result; result = xrStringToPath (self->instance, viewport_config_name, &vrConfigName); _check_xr_result (result, "failed to get viewport configuration name"); g_debug ("Got vrconfig %lu", vrConfigName); XrSystemGetInfo systemGetInfo = { .type = XR_TYPE_SYSTEM_GET_INFO, .formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY, }; result = xrGetSystem (self->instance, &systemGetInfo, &self->system_id); if (!_check_xr_result (result, "Failed to get system for %s viewport configuration.", viewport_config_name)) return FALSE; XrSystemProperties systemProperties = { .type = XR_TYPE_SYSTEM_PROPERTIES, .graphicsProperties = {0}, .trackingProperties = {0}, }; result = xrGetSystemProperties (self->instance, self->system_id, &systemProperties); if (!_check_xr_result (result, "Failed to get System properties")) return FALSE; return TRUE; } static gboolean _set_up_views (GxrContext *self) { uint32_t viewConfigurationCount; XrResult result; result = xrEnumerateViewConfigurations (self->instance, self->system_id, 0, &viewConfigurationCount, NULL); if (!_check_xr_result (result, "Failed to get view configuration count")) return FALSE; XrViewConfigurationType *viewConfigurations = g_malloc (sizeof (XrViewConfigurationType) * viewConfigurationCount); result = xrEnumerateViewConfigurations (self->instance, self->system_id, viewConfigurationCount, &viewConfigurationCount, viewConfigurations); if (!_check_xr_result (result, "Failed to enumerate view configurations!")) return FALSE; self->view_config_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; /* if struct (more specifically .type) is still 0 after searching, then we have not found the config. This way we don't need to set a bool found to TRUE. */ XrViewConfigurationProperties requiredViewConfigProperties = {0}; for (uint32_t i = 0; i < viewConfigurationCount; ++i) { XrViewConfigurationProperties properties = { .type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES, }; result = xrGetViewConfigurationProperties (self->instance, self->system_id, viewConfigurations[i], &properties); if (!_check_xr_result (result, "Failed to get view configuration info %d!", i)) return FALSE; if (viewConfigurations[i] == self->view_config_type && properties.viewConfigurationType == self->view_config_type) requiredViewConfigProperties = properties; } g_free (viewConfigurations); if (requiredViewConfigProperties.type != XR_TYPE_VIEW_CONFIGURATION_PROPERTIES) { g_print ("Couldn't get required VR View Configuration %s from Runtime!\n", viewport_config_name); return FALSE; } result = xrEnumerateViewConfigurationViews (self->instance, self->system_id, self->view_config_type, 0, &self->view_count, NULL); self->views = g_malloc (sizeof (XrView) * self->view_count); for (uint32_t i = 0; i < self->view_count; i++) { self->views[i].type = XR_TYPE_VIEW; self->views[i].next = NULL; } if (!_check_xr_result (result, "Failed to get view configuration view count!")) return FALSE; self->configuration_views = g_malloc (sizeof (XrViewConfigurationView) * self->view_count); for (uint32_t i = 0; i < self->view_count; i++) { self->configuration_views[i].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; self->configuration_views[i].next = NULL; } result = xrEnumerateViewConfigurationViews (self->instance, self->system_id, self->view_config_type, self->view_count, &self->view_count, self->configuration_views); if (!_check_xr_result (result, "Failed to enumerate view configuration views!")) return FALSE; return TRUE; } static gboolean _check_graphics_api_support (GxrContext *self) { // same aliased struct and type for vulkan_enable and vulkan_enable2 XrGraphicsRequirementsVulkanKHR vk_reqs = { .type = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR, }; PFN_xrGetVulkanGraphicsRequirementsKHR GetVulkanGraphicsRequirements2 = NULL; XrResult res; res = xrGetInstanceProcAddr (self->instance, "xrGetVulkanGraphicsRequirements2KHR", (PFN_xrVoidFunction *) (&GetVulkanGraphicsRequirements2)); if (!_check_xr_result (res, "Failed to retrieve " "xrGetVulkanGraphicsRequirements2KHR pointer!")) return FALSE; res = GetVulkanGraphicsRequirements2 (self->instance, self->system_id, &vk_reqs); if (!_check_xr_result (res, "Failed to get Vulkan graphics requirements!")) return FALSE; if (self->desired_vk_version > vk_reqs.maxApiVersionSupported || self->desired_vk_version < vk_reqs.minApiVersionSupported) { g_warning ("Runtime does not support requested Vulkan version %lu", self->desired_vk_version); g_warning ("We will use maxApiVersionSupported %lu", vk_reqs.maxApiVersionSupported); self->desired_vk_version = vk_reqs.maxApiVersionSupported; } return TRUE; } static gboolean _init_runtime (GxrContext *self, char *app_name, uint32_t app_version) { if (!_check_extensions (self)) return FALSE; if (!_create_instance (self, app_name, app_version)) return FALSE; if (!_create_system (self)) return FALSE; if (!_set_up_views (self)) return FALSE; if (!_check_graphics_api_support (self)) return FALSE; if (!_check_blend_mode (self)) return FALSE; return TRUE; } static gboolean _create_session (GxrContext *self) { // TODO: session layer placement should be configurable XrSessionCreateInfoOverlayEXTX overlay_info = { .type = XR_TYPE_SESSION_CREATE_INFO_OVERLAY_EXTX, .next = &self->graphics_binding, .sessionLayersPlacement = 1, }; XrSessionCreateInfo session_create_info = { .type = XR_TYPE_SESSION_CREATE_INFO, .next = self->extensions.overlay ? (void *) &overlay_info : (void *) &self->graphics_binding, .systemId = self->system_id, }; XrResult result = xrCreateSession (self->instance, &session_create_info, &self->session); if (!_check_xr_result (result, "Failed to create session")) return FALSE; return TRUE; } static gboolean _is_space_supported (XrReferenceSpaceType *spaces, uint32_t count, XrReferenceSpaceType type) { for (uint32_t i = 0; i < count; i++) if (spaces[i] == type) return TRUE; return FALSE; } static gboolean _check_supported_spaces (GxrContext *self) { uint32_t count; XrResult result = xrEnumerateReferenceSpaces (self->session, 0, &count, NULL); if (!_check_xr_result (result, "Getting number of reference spaces failed!")) return FALSE; XrReferenceSpaceType *spaces = g_malloc (sizeof (XrReferenceSpaceType) * count); result = xrEnumerateReferenceSpaces (self->session, count, &count, spaces); if (!_check_xr_result (result, "Enumerating reference spaces failed!")) return FALSE; XrReferenceSpaceType space_type = XR_REFERENCE_SPACE_TYPE_STAGE; const gchar *gxr_space = g_getenv ("GXR_SPACE"); if (gxr_space) { if (g_strcmp0 (gxr_space, "LOCAL") == 0) space_type = XR_REFERENCE_SPACE_TYPE_LOCAL; } if (_is_space_supported (spaces, count, space_type)) { self->play_space_type = space_type; g_debug ("Requested play space supported."); } else { self->play_space_type = spaces[0]; g_debug ("Requested play space not supported, fall back to %d!", spaces[0]); } if (!_is_space_supported (spaces, count, XR_REFERENCE_SPACE_TYPE_VIEW)) { g_print ("XR_REFERENCE_SPACE_TYPE_VIEW unsupported.\n"); return FALSE; } g_free (spaces); XrPosef space_pose = { .orientation = {.x = 0, .y = 0, .z = 0, .w = 1.0}, .position = {.x = 0, .y = 0, .z = 0}, }; // TODO: monado doesn't handle this well #if 0 if (self->play_space_type == XR_REFERENCE_SPACE_TYPE_LOCAL) { space_pose.position.y = -1.6f; g_debug ("Using local space with %f y offset", space_pose.position.y); } #endif XrReferenceSpaceCreateInfo info = { .type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO, .referenceSpaceType = self->play_space_type, .poseInReferenceSpace = space_pose, }; result = xrCreateReferenceSpace (self->session, &info, &self->play_space); if (!_check_xr_result (result, "Failed to create local space.")) return FALSE; info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; result = xrCreateReferenceSpace (self->session, &info, &self->view_space); if (!_check_xr_result (result, "Failed to create view space.")) return FALSE; return TRUE; } static gboolean _begin_session (GxrContext *self) { XrSessionBeginInfo sessionBeginInfo = { .type = XR_TYPE_SESSION_BEGIN_INFO, .primaryViewConfigurationType = self->view_config_type, }; XrResult result = xrBeginSession (self->session, &sessionBeginInfo); if (!_check_xr_result (result, "Failed to begin session!")) return FALSE; self->session_running = TRUE; return TRUE; } static gboolean _end_session (GxrContext *self) { XrResult result = xrEndSession (self->session); if (!_check_xr_result (result, "Failed to end session!")) return FALSE; self->session_running = FALSE; return TRUE; } static gboolean _destroy_session (GxrContext *self) { XrResult result = xrDestroySession (self->session); if (!_check_xr_result (result, "Failed to destroy session!")) return FALSE; self->session_running = FALSE; self->session = NULL; return TRUE; } static gboolean _create_swapchains (GxrContext *self) { XrResult result; uint32_t swapchainFormatCount; result = xrEnumerateSwapchainFormats (self->session, 0, &swapchainFormatCount, NULL); if (!_check_xr_result (result, "Failed to get number of supported swapchain formats")) return FALSE; int64_t *swapchainFormats = g_malloc (sizeof (int64_t) * swapchainFormatCount); result = xrEnumerateSwapchainFormats (self->session, swapchainFormatCount, &swapchainFormatCount, swapchainFormats); if (!_check_xr_result (result, "Failed to enumerate swapchain formats")) { g_free (swapchainFormats); return FALSE; } g_debug ("Supported swapchain formats:"); for (uint32_t i = 0; i < swapchainFormatCount; i++) g_debug ("%s", vk_format_string ((VkFormat) swapchainFormats[i])); self->swapchain_format = VK_FORMAT_R8G8B8A8_SRGB; gboolean format_found = FALSE; for (uint32_t i = 0; i < swapchainFormatCount; i++) if (swapchainFormats[i] == self->swapchain_format) { format_found = TRUE; break; } if (!format_found) { g_warning ("Requested %s, but runtime doesn't support it.", vk_format_string ((VkFormat) self->swapchain_format)); g_warning ("Using %s instead.", vk_format_string ((VkFormat) swapchainFormats[0])); self->swapchain_format = swapchainFormats[0]; } /* make sure we don't clean up uninitialized pointer on failure */ self->images = NULL; XrSwapchainCreateInfo swapchainCreateInfo = { .type = XR_TYPE_SWAPCHAIN_CREATE_INFO, .usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, .createFlags = 0, // just use the first enumerated format .format = self->swapchain_format, .sampleCount = 1, .width = self->configuration_views[0].recommendedImageRectWidth, .height = self->configuration_views[0].recommendedImageRectHeight, .faceCount = 1, .arraySize = 2, .mipCount = 1, }; g_debug ("Swapchain %d dimensions: %dx%d", 0, self->configuration_views[0].recommendedImageRectWidth, self->configuration_views[0].recommendedImageRectHeight); result = xrCreateSwapchain (self->session, &swapchainCreateInfo, &self->swapchain); if (!_check_xr_result (result, "Failed to create swapchain %d!", 0)) { g_free (swapchainFormats); return FALSE; } result = xrEnumerateSwapchainImages (self->swapchain, 0, &self->swapchain_length, NULL); if (!_check_xr_result (result, "Failed to enumerate swapchains")) { g_free (swapchainFormats); return FALSE; } self->images = g_malloc (sizeof (XrSwapchainImageVulkanKHR) * self->swapchain_length); for (uint32_t j = 0; j < self->swapchain_length; j++) { // ...IMAGE_VULKAN2_KHR = ...IMAGE_VULKAN_KHR self->images[j].type = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR; self->images[j].next = NULL; } result = xrEnumerateSwapchainImages (self->swapchain, self->swapchain_length, &self->swapchain_length, (XrSwapchainImageBaseHeader *) self->images); if (!_check_xr_result (result, "Failed to enumerate swapchain images")) { g_free (swapchainFormats); return FALSE; } g_free (swapchainFormats); return TRUE; } static void _create_projection_views (GxrContext *self) { self->projection_views = g_malloc (sizeof (XrCompositionLayerProjectionView) * self->view_count); for (uint32_t i = 0; i < self->view_count; i++) self->projection_views[i] = (XrCompositionLayerProjectionView) { .type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW, .subImage = { .swapchain = self->swapchain, .imageRect = { .extent = { .width = (int32_t) self->configuration_views[i].recommendedImageRectWidth, .height = (int32_t) self->configuration_views[i].recommendedImageRectHeight, }, }, .imageArrayIndex = i, }, }; } static gboolean _init_session (GxrContext *self) { GulkanContext *gc = gxr_context_get_gulkan (self); GulkanDevice *gd = gulkan_context_get_device (gc); GulkanQueue *queue = gulkan_device_get_graphics_queue (gd); uint32_t family_index = gulkan_queue_get_family_index (queue); self->graphics_binding = (XrGraphicsBindingVulkanKHR){ .type = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR, .instance = gulkan_context_get_instance_handle (gc), .physicalDevice = gulkan_device_get_physical_handle (gd), .device = gulkan_device_get_handle (gd), .queueFamilyIndex = family_index, .queueIndex = 0, }; if (!_create_session (self)) return FALSE; if (!_check_supported_spaces (self)) return FALSE; if (!_begin_session (self)) return FALSE; if (!_create_swapchains (self)) return FALSE; g_print ("Created swapchains.\n"); _create_projection_views (self); self->buffer_index = 0; self->session_state = XR_SESSION_STATE_UNKNOWN; self->should_render = FALSE; self->have_valid_pose = FALSE; self->projection_layer = (XrCompositionLayerProjection){ .type = XR_TYPE_COMPOSITION_LAYER_PROJECTION, .layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, .space = self->play_space, .viewCount = self->view_count, .views = self->projection_views, }; return TRUE; } static void _remove_unsupported_exts (GSList **target, uint32_t supported_count, VkExtensionProperties *supported_props) { for (GSList *l = *target; l; l = l->next) { gboolean is_supported = FALSE; for (uint32_t i = 0; i < supported_count; i++) { gchar *ext = l->data; if (strcmp (supported_props[i].extensionName, ext) == 0) { is_supported = TRUE; break; } } if (!is_supported) { g_print ("Disabling unsupported ext %s\n", (gchar *) l->data); *target = g_slist_delete_link (*target, l); } } } static void _remove_unsupported_instance_exts (GSList **target) { uint32_t supported_count; vkEnumerateInstanceExtensionProperties (NULL, &supported_count, NULL); VkExtensionProperties *supported_props = g_malloc (sizeof (VkExtensionProperties) * supported_count); vkEnumerateInstanceExtensionProperties (NULL, &supported_count, supported_props); _remove_unsupported_exts (target, supported_count, supported_props); g_free (supported_props); } static void _remove_unsupported_device_exts (VkPhysicalDevice vk_physical_device, GSList **target) { uint32_t supported_count; vkEnumerateDeviceExtensionProperties (vk_physical_device, NULL, &supported_count, NULL); VkExtensionProperties *supported_props = g_malloc (sizeof (VkExtensionProperties) * supported_count); vkEnumerateDeviceExtensionProperties (vk_physical_device, NULL, &supported_count, supported_props); _remove_unsupported_exts (target, supported_count, supported_props); g_free (supported_props); } static gpointer _copy_str (gconstpointer src, gpointer data) { (void) data; return g_strdup (src); } static uint32_t _xr_to_vk_version (XrVersion version) { #if VK_HEADER_VERSION >= 175 return VK_MAKE_API_VERSION (0, XR_VERSION_MAJOR (version), XR_VERSION_MINOR (version), XR_VERSION_PATCH (version)); #else return VK_MAKE_VERSION (XR_VERSION_MAJOR (version), XR_VERSION_MINOR (version), XR_VERSION_PATCH (version)); #endif } static gboolean _create_vk_instance2 (GxrContext *self, GSList *instance_ext_list, VkInstance *instance) { VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "gxr", .pEngineName = "gxr", .apiVersion = _xr_to_vk_version (self->desired_vk_version), }; GSList *instance_ext_list_reduced = g_slist_copy_deep (instance_ext_list, _copy_str, NULL); _remove_unsupported_instance_exts (&instance_ext_list_reduced); uint32_t num_extensions = g_slist_length (instance_ext_list_reduced); const char **extension_names = g_malloc (sizeof (char *) * num_extensions); uint32_t i = 0; for (GSList *l = instance_ext_list_reduced; l; l = l->next) extension_names[i++] = l->data; // runtime will add extensions it requires VkInstanceCreateInfo instance_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, .enabledExtensionCount = num_extensions, .ppEnabledExtensionNames = extension_names, }; XrVulkanInstanceCreateInfoKHR xr_vk_instance_info = { .type = XR_TYPE_VULKAN_INSTANCE_CREATE_INFO_KHR, .next = NULL, .createFlags = 0, .pfnGetInstanceProcAddr = vkGetInstanceProcAddr, .systemId = self->system_id, .vulkanCreateInfo = &instance_info, .vulkanAllocator = NULL, }; PFN_xrCreateVulkanInstanceKHR CreateVulkanInstanceKHR = NULL; XrResult res; res = xrGetInstanceProcAddr (self->instance, "xrCreateVulkanInstanceKHR", (PFN_xrVoidFunction *) &CreateVulkanInstanceKHR); if (!_check_xr_result (res, "Failed to load xrCreateVulkanInstanceKHR.")) { g_free (extension_names); g_slist_free_full (instance_ext_list_reduced, g_free); return FALSE; } VkResult vk_result; res = CreateVulkanInstanceKHR (self->instance, &xr_vk_instance_info, instance, &vk_result); g_free (extension_names); g_slist_free_full (instance_ext_list_reduced, g_free); if (!_check_xr_result (res, "Failed to create Vulkan instance!")) return FALSE; if (vk_result != VK_SUCCESS) { g_printerr ("Runtime failed to create Vulkan instance: %d\n", vk_result); return FALSE; } return TRUE; } static gboolean _get_vk_physical_device2 (GxrContext *self, VkInstance vk_instance, VkPhysicalDevice *physical_device) { PFN_xrGetVulkanGraphicsDevice2KHR fun = NULL; XrResult res; res = xrGetInstanceProcAddr (self->instance, "xrGetVulkanGraphicsDevice2KHR", (PFN_xrVoidFunction *) &fun); if (!_check_xr_result (res, "Failed to load xrGetVulkanGraphicsDevice2KHR.")) return FALSE; XrVulkanGraphicsDeviceGetInfoKHR info = { .type = XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR, .next = NULL, .systemId = self->system_id, .vulkanInstance = vk_instance, }; res = fun (self->instance, &info, physical_device); if (!_check_xr_result (res, "Failed to get Vulkan graphics device.")) return FALSE; return TRUE; } static gboolean find_queue_index_for_flags (VkQueueFlagBits flags, VkQueueFlagBits exclude_flags, uint32_t num_queues, VkQueueFamilyProperties *queue_family_props, uint32_t *out_index) { uint32_t i = 0; for (i = 0; i < num_queues; i++) if (queue_family_props[i].queueFlags & flags && !(queue_family_props[i].queueFlags & exclude_flags)) break; if (i >= num_queues) return FALSE; *out_index = i; return TRUE; } static gboolean _find_queue_families (VkPhysicalDevice vk_physical_device, uint32_t *graphics_queue_index, uint32_t *transfer_queue_index) { /* Find the first graphics queue */ uint32_t num_queues = 0; vkGetPhysicalDeviceQueueFamilyProperties (vk_physical_device, &num_queues, 0); VkQueueFamilyProperties *queue_family_props = g_malloc (sizeof (VkQueueFamilyProperties) * num_queues); vkGetPhysicalDeviceQueueFamilyProperties (vk_physical_device, &num_queues, queue_family_props); if (num_queues == 0) { g_printerr ("Failed to get queue properties.\n"); return FALSE; } *graphics_queue_index = UINT32_MAX; if (!find_queue_index_for_flags (VK_QUEUE_GRAPHICS_BIT, (VkQueueFlagBits) 0, num_queues, queue_family_props, graphics_queue_index)) { g_printerr ("No graphics queue found\n"); return FALSE; } *transfer_queue_index = UINT32_MAX; if (find_queue_index_for_flags (VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT, num_queues, queue_family_props, transfer_queue_index)) { g_debug ("Got pure transfer queue"); } else { g_debug ("No pure transfer queue found, trying all queues"); if (find_queue_index_for_flags (VK_QUEUE_TRANSFER_BIT, (VkQueueFlagBits) 0, num_queues, queue_family_props, transfer_queue_index)) { g_debug ("Got a transfer queue"); } else { g_printerr ("No transfer queue found\n"); return FALSE; } } g_free (queue_family_props); return TRUE; } static gboolean _create_vk_device2 (GxrContext *self, VkPhysicalDevice physical_device, GSList *device_ext_list, VkDevice *vk_device, uint32_t *graphics_queue_index, uint32_t *transfer_queue_index) { PFN_xrCreateVulkanDeviceKHR CreateVulkanDeviceKHR = NULL; XrResult res; res = xrGetInstanceProcAddr (self->instance, "xrCreateVulkanDeviceKHR", (PFN_xrVoidFunction *) &CreateVulkanDeviceKHR); if (!_check_xr_result (res, "Failed to load xrCreateVulkanDeviceKHR.")) return FALSE; if (!_find_queue_families (physical_device, graphics_queue_index, transfer_queue_index)) return FALSE; VkPhysicalDeviceFeatures physical_device_features; vkGetPhysicalDeviceFeatures (physical_device, &physical_device_features); GSList *device_ext_list_reduced = g_slist_copy_deep (device_ext_list, _copy_str, NULL); _remove_unsupported_device_exts (physical_device, &device_ext_list_reduced); uint32_t num_extensions = g_slist_length (device_ext_list_reduced); const char **extension_names = g_malloc (sizeof (char *) * num_extensions); uint32_t i = 0; for (GSList *l = device_ext_list_reduced; l; l = l->next) extension_names[i++] = l->data; // runtime will add extensions it requires VkDeviceCreateInfo device_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueCreateInfoCount = 2, .pQueueCreateInfos = (VkDeviceQueueCreateInfo[]) { { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = *graphics_queue_index, .queueCount = 1, .pQueuePriorities = (const float[]) { 1.0f }, }, { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = *transfer_queue_index, .queueCount = 1, .pQueuePriorities = (const float[]) { 0.8f }, }, }, .pEnabledFeatures = &physical_device_features, .enabledExtensionCount = num_extensions, .ppEnabledExtensionNames = extension_names, }; #ifdef VK_API_VERSION_1_2 VkPhysicalDeviceVulkan11Features enabled_vulkan11_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, .multiview = VK_TRUE, }; if (self->desired_vk_version >= XR_MAKE_VERSION (1, 2, 0)) device_info.pNext = &enabled_vulkan11_features; #endif XrVulkanDeviceCreateInfoKHR info = { .type = XR_TYPE_VULKAN_DEVICE_CREATE_INFO_KHR, .next = NULL, .systemId = self->system_id, .createFlags = 0, .pfnGetInstanceProcAddr = vkGetInstanceProcAddr, .vulkanPhysicalDevice = physical_device, .vulkanCreateInfo = &device_info, .vulkanAllocator = NULL, }; VkResult vk_result; res = CreateVulkanDeviceKHR (self->instance, &info, vk_device, &vk_result); g_free (extension_names); g_slist_free_full (device_ext_list_reduced, g_free); if (!_check_xr_result (res, "Failed to create Vulkan graphics device.")) return FALSE; if (vk_result != VK_SUCCESS) { g_printerr ("Runtime failed to create Vulkan device: %d\n", vk_result); return FALSE; } return TRUE; } static gboolean init_vulkan_enable2 (GxrContext *self, GSList *instance_ext_list, GSList *device_ext_list) { if (!_check_graphics_api_support (self)) return FALSE; VkInstance vk_instance; if (!_create_vk_instance2 (self, instance_ext_list, &vk_instance)) return FALSE; VkPhysicalDevice physical_device; if (!_get_vk_physical_device2 (self, vk_instance, &physical_device)) return TRUE; VkDevice vk_device; uint32_t graphics_queue_index; uint32_t transfer_queue_index; if (!_create_vk_device2 (self, physical_device, device_ext_list, &vk_device, &graphics_queue_index, &transfer_queue_index)) return TRUE; self->gc = gulkan_context_new_from_vk (vk_instance, physical_device, vk_device, graphics_queue_index, transfer_queue_index); if (!self->gc) return FALSE; return TRUE; } static GxrContext * _new (GSList *instance_ext_list, GSList *device_ext_list, char *app_name, uint32_t app_version) { GxrContext *self = (GxrContext *) g_object_new (GXR_TYPE_CONTEXT, 0); if (!self) { g_printerr ("Could not init gxr context.\n"); return NULL; } if (!_init_runtime (self, app_name, app_version)) { g_object_unref (self); g_printerr ("Could not init runtime.\n"); return NULL; } // Always enable multiview extension. device_ext_list = g_slist_append (device_ext_list, g_strdup (VK_KHR_MULTIVIEW_EXTENSION_NAME)); if (!init_vulkan_enable2 (self, instance_ext_list, device_ext_list)) { g_free (self); return NULL; } if (!_init_session (self)) { g_object_unref (self); g_printerr ("Could not init VR session.\n"); return NULL; } g_mutex_init (&self->wait_frame_mutex); return self; } GxrContext * gxr_context_new (char *app_name, uint32_t app_version) { return gxr_context_new_from_vulkan_extensions (NULL, NULL, app_name, app_version); } GxrContext * gxr_context_new_from_vulkan_extensions (GSList *instance_ext_list, GSList *device_ext_list, char *app_name, uint32_t app_version) { return _new (instance_ext_list, device_ext_list, app_name, app_version); } static void gxr_context_init (GxrContext *self) { self->gc = NULL; self->device_manager = gxr_device_manager_new (); self->view_count = 0; self->views = NULL; self->predicted_display_time = 0; self->predicted_display_period = 0; self->framebuffers = NULL; self->images = NULL; self->desired_vk_version = XR_MAKE_VERSION (1, 2, 0); } static void _cleanup (GxrContext *self) { if (self->swapchain != XR_NULL_HANDLE) xrDestroySwapchain (self->swapchain); if (self->play_space) xrDestroySpace (self->play_space); if (self->session) xrDestroySession (self->session); if (self->instance) xrDestroyInstance (self->instance); g_free (self->configuration_views); g_free (self->views); g_free (self->projection_views); if (self->framebuffers) { for (uint32_t i = 0; i < self->swapchain_length; i++) { if (GULKAN_IS_FRAME_BUFFER (self->framebuffers[i])) g_object_unref (self->framebuffers[i]); else g_printerr ("Failed to release framebuffer %d\n", i); } g_free (self->framebuffers); } if (self->images) g_free (self->images); } static void gxr_context_finalize (GObject *gobject) { GxrContext *self = GXR_CONTEXT (gobject); _cleanup (self); GulkanContext *gulkan = gxr_context_get_gulkan (GXR_CONTEXT (gobject)); if (gulkan) g_object_unref (gulkan); if (self->device_manager != NULL) g_clear_object (&self->device_manager); /* child classes MUST destroy gulkan after this destructor finishes */ g_debug ("destroyed up gxr context, bye"); G_OBJECT_CLASS (gxr_context_parent_class)->finalize (gobject); } GulkanContext * gxr_context_get_gulkan (GxrContext *self) { return self->gc; } static gboolean _space_location_valid (XrSpaceLocation *sl) { return (sl->locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && (sl->locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0; } static void _get_model_matrix_from_pose (XrPosef *pose, graphene_matrix_t *mat) { graphene_quaternion_t q; graphene_quaternion_init (&q, pose->orientation.x, pose->orientation.y, pose->orientation.z, pose->orientation.w); graphene_matrix_t rotation; graphene_matrix_init_identity (&rotation); graphene_matrix_rotate_quaternion (&rotation, &q); graphene_point3d_t position = { pose->position.x, pose->position.y, pose->position.z, }; graphene_matrix_t translation; graphene_matrix_init_translate (&translation, &position); graphene_matrix_multiply (&rotation, &translation, mat); } gboolean gxr_context_get_head_pose (GxrContext *self, graphene_matrix_t *pose) { XrSpaceLocation space_location = { .type = XR_TYPE_SPACE_LOCATION, }; XrResult result = xrLocateSpace (self->view_space, self->play_space, self->predicted_display_time, &space_location); _check_xr_result (result, "Failed to locate head space."); gboolean valid = _space_location_valid (&space_location); if (!valid) { g_printerr ("Could not get valid head pose.\n"); graphene_matrix_init_identity (pose); return FALSE; } _get_model_matrix_from_pose (&space_location.pose, pose); return TRUE; } #define PI 3.1415926535f #define RAD_TO_DEG(x) ((x) *360.0f / (2.0f * PI)) void gxr_context_get_frustum_angles (GxrContext *self, GxrEye eye, float *left, float *right, float *top, float *bottom) { *left = RAD_TO_DEG (self->views[eye].fov.angleLeft); *right = RAD_TO_DEG (self->views[eye].fov.angleRight); *top = RAD_TO_DEG (self->views[eye].fov.angleUp); *bottom = RAD_TO_DEG (self->views[eye].fov.angleDown); // g_debug ("Fov angles L %f R %f T %f B %f", *left, *right, *top, *bottom); } static void _handle_visibility_changed (GxrContext *self, XrEventDataBuffer *runtimeEvent) { XrEventDataMainSessionVisibilityChangedEXTX *event = (XrEventDataMainSessionVisibilityChangedEXTX *) runtimeEvent; g_debug ("Event: main session visibility now: %d", event->visible); GxrOverlayEvent overlay_event = { .main_session_visible = event->visible, }; g_signal_emit (self, context_signals[OVERLAY_EVENT], 0, &overlay_event); } static void _handle_state_changed (GxrContext *self, XrEventDataBuffer *runtimeEvent) { XrEventDataSessionStateChanged *event = (XrEventDataSessionStateChanged *) runtimeEvent; self->session_state = event->state; g_debug ("EVENT: session state changed to %d", event->state); /* * react to session state changes, see OpenXR spec 9.3 diagram. What we need * to react to: * * * READY -> xrBeginSession STOPPING -> xrEndSession (note that the same * session can be restarted) * * EXITING -> xrDestroySession (EXITING only happens after we went through * STOPPING and called xrEndSession) * * After exiting it is still possible to create a new session but we don't do * that here. * * * IDLE -> don't run render loop, but keep polling for events * * SYNCHRONIZED, VISIBLE, FOCUSED -> run render loop */ gboolean should_submit_frames = FALSE; switch (event->state) { // skip render loop, keep polling case XR_SESSION_STATE_MAX_ENUM: // must be a bug case XR_SESSION_STATE_IDLE: case XR_SESSION_STATE_UNKNOWN: break; // state handling switch // do nothing, run render loop normally case XR_SESSION_STATE_FOCUSED: case XR_SESSION_STATE_SYNCHRONIZED: case XR_SESSION_STATE_VISIBLE: should_submit_frames = TRUE; break; // state handling switch // begin session and then run render loop case XR_SESSION_STATE_READY: { // start session only if it is not running, i.e. not when we already // called xrBeginSession but the runtime did not switch to the next // state yet if (!self->session_running) { if (!_begin_session (self)) g_printerr ("Failed to begin session\n"); } should_submit_frames = TRUE; break; // state handling switch } // end session, skip render loop, keep polling for next state change case XR_SESSION_STATE_STOPPING: { // end session only if it is running, i.e. not when we already called // xrEndSession but the runtime did not switch to the next state yet if (self->session_running) { if (!_end_session (self)) g_printerr ("Failed to end session\n"); } break; // state handling switch } // destroy session, skip render loop, exit render loop and quit case XR_SESSION_STATE_LOSS_PENDING: case XR_SESSION_STATE_EXITING: if (self->session) { if (!_destroy_session (self)) g_printerr ("Failed to destroy session\n"); GxrStateChangeEvent state_change_event = { .state_change = GXR_STATE_SHUTDOWN, }; g_debug ("Event: state is EXITING, sending VR_QUIT_SHUTDOWN"); g_signal_emit (self, context_signals[STATE_CHANGE_EVENT], 0, &state_change_event); } break; // state handling switch } if (!self->should_submit_frames && should_submit_frames) { GxrStateChangeEvent state_change_event = { .state_change = GXR_STATE_FRAMECYCLE_START, }; g_debug ("Event: start frame cycle"); g_signal_emit (self, context_signals[STATE_CHANGE_EVENT], 0, &state_change_event); } else if (self->should_submit_frames && !should_submit_frames) { GxrStateChangeEvent state_change_event = { .state_change = GXR_STATE_FRAMECYCLE_STOP, }; g_debug ("Event: stop frame cycle"); g_signal_emit (self, context_signals[STATE_CHANGE_EVENT], 0, &state_change_event); } self->should_submit_frames = should_submit_frames; } static void _handle_instance_loss (GxrContext *self) { GxrStateChangeEvent event = { .state_change = GXR_STATE_SHUTDOWN, }; g_debug ("Event: instance loss pending, sending VR_QUIT_SHUTDOWN"); g_signal_emit (self, context_signals[STATE_CHANGE_EVENT], 0, &event); } static void _handle_interaction_profile_changed (GxrContext *self) { g_print ("Event: STUB: interaction profile changed\n"); XrInteractionProfileState state = { .type = XR_TYPE_INTERACTION_PROFILE_STATE, }; char *hand_str[2] = {"/user/hand/left", "/user/hand/right"}; XrPath hand_paths[2]; xrStringToPath (self->instance, hand_str[0], &hand_paths[0]); xrStringToPath (self->instance, hand_str[1], &hand_paths[1]); for (int i = 0; i < NUM_CONTROLLERS; i++) { XrResult res = xrGetCurrentInteractionProfile (self->session, hand_paths[i], &state); if (!_check_xr_result (res, "Failed to get interaction profile for %s", hand_str[i])) continue; XrPath prof = state.interactionProfile; if (prof == XR_NULL_PATH) { // perhaps no controller is present g_debug ("Event: Interaction profile on %s: [none]", hand_str[i]); continue; } uint32_t strl; char profile_str[XR_MAX_PATH_LENGTH]; res = xrPathToString (self->instance, prof, XR_MAX_PATH_LENGTH, &strl, profile_str); if (!_check_xr_result (res, "Failed to get interaction profile path str for " "%s", hand_str[i])) continue; g_debug ("Event: Interaction profile on %s: %s", hand_str[i], profile_str); } } void gxr_context_poll_events (GxrContext *self) { XrEventDataBuffer runtimeEvent = { .type = XR_TYPE_EVENT_DATA_BUFFER, }; XrResult pollResult; while (TRUE) { runtimeEvent.type = XR_TYPE_EVENT_DATA_BUFFER; pollResult = xrPollEvent (self->instance, &runtimeEvent); if (pollResult != XR_SUCCESS) break; switch (runtimeEvent.type) { case XR_TYPE_EVENT_DATA_EVENTS_LOST: /* We didnt poll enough */ g_printerr ("Event: Events lost\n"); break; case XR_TYPE_EVENT_DATA_MAIN_SESSION_VISIBILITY_CHANGED_EXTX: _handle_visibility_changed (self, &runtimeEvent); break; case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: g_debug ("Event: STUB: perf settings"); break; case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR: g_debug ("Event: STUB: visibility mask changed"); break; case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: _handle_state_changed (self, &runtimeEvent); break; case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: _handle_instance_loss (self); break; case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: _handle_interaction_profile_changed (self); break; case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: g_debug ("Event: STUB: reference space change pending\n"); break; default: { char buffer[XR_MAX_STRUCTURE_NAME_SIZE]; xrStructureTypeToString (self->instance, runtimeEvent.type, buffer); g_print ("Event: Unhandled event type %s (%d)\n", buffer, runtimeEvent.type); break; } } } if (pollResult != XR_EVENT_UNAVAILABLE) { g_printerr ("Failed to poll events!\n"); } } gboolean gxr_context_init_framebuffers (GxrContext *self, VkExtent2D extent, VkSampleCountFlagBits sample_count, GulkanRenderPass **render_pass) { GulkanContext *gc = gxr_context_get_gulkan (self); GulkanDevice *device = gulkan_context_get_device (gc); self->framebuffer_extent = extent; self->framebuffer_sample_count = sample_count; VkFormat format = (VkFormat) self->swapchain_format; *render_pass = gulkan_render_pass_new_multiview (device, sample_count, format, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, TRUE); if (!*render_pass) { g_printerr ("Could not init render pass.\n"); return FALSE; } g_debug ("Creating %d framebuffers", self->swapchain_length); self->framebuffers = g_malloc (sizeof (GulkanFrameBuffer *) * self->swapchain_length); for (uint32_t i = 0; i < self->swapchain_length; i++) { self->framebuffers[i] = gulkan_frame_buffer_new_from_image_with_depth (device, *render_pass, self->images[i].image, extent, sample_count, format, self->view_count); if (!GULKAN_IS_FRAME_BUFFER (self->framebuffers[i])) { g_printerr ("Could not initialize frambuffer."); return FALSE; } } return TRUE; } static void _get_projection_matrix_from_fov (const XrFovf fov, const float near_z, const float far_z, graphene_matrix_t *mat) { const float tan_left = tanf (fov.angleLeft); const float tan_right = tanf (fov.angleRight); const float tan_down = tanf (fov.angleDown); const float tan_up = tanf (fov.angleUp); const float tan_width = tan_right - tan_left; const float tan_height = tan_up - tan_down; const float a11 = 2 / tan_width; const float a22 = 2 / tan_height; const float a31 = (tan_right + tan_left) / tan_width; const float a32 = (tan_up + tan_down) / tan_height; const float a33 = -far_z / (far_z - near_z); const float a43 = -(far_z * near_z) / (far_z - near_z); const float m[16] = { a11, 0, 0, 0, 0, a22, 0, 0, a31, a32, a33, -1, 0, 0, a43, 0, }; graphene_matrix_init_from_float (mat, m); } void gxr_context_get_projection (GxrContext *self, GxrEye eye, float near, float far, graphene_matrix_t *mat) { if (self->views == NULL) { g_warning ("get_projection needs to be called " "between begin and end frame.\n"); graphene_matrix_init_identity (mat); return; } _get_projection_matrix_from_fov (self->views[eye].fov, near, far, mat); } static void _get_view_matrix_from_pose (XrPosef *pose, graphene_matrix_t *mat) { graphene_quaternion_t q; graphene_quaternion_init (&q, pose->orientation.x, pose->orientation.y, pose->orientation.z, pose->orientation.w); graphene_matrix_t rotation; graphene_matrix_init_identity (&rotation); graphene_matrix_rotate_quaternion (&rotation, &q); graphene_point3d_t position = { pose->position.x, pose->position.y, pose->position.z, }; graphene_matrix_t translation; graphene_matrix_init_translate (&translation, &position); graphene_matrix_t view; graphene_matrix_multiply (&rotation, &translation, &view); graphene_matrix_inverse (&view, mat); } void gxr_context_get_view (GxrContext *self, GxrEye eye, graphene_matrix_t *mat) { if (self->views == NULL) { g_warning ("get_view needs to be called between begin and end frame.\n"); graphene_matrix_init_identity (mat); return; } _get_view_matrix_from_pose (&self->views[eye].pose, mat); } void gxr_context_get_eye_position (GxrContext *self, GxrEye eye, graphene_vec3_t *v) { graphene_vec3_init (v, self->views[eye].pose.position.x, self->views[eye].pose.position.y, self->views[eye].pose.position.z); } gboolean gxr_context_wait_frame (GxrContext *self) { XrResult result; XrFrameState frame_state = { .type = XR_TYPE_FRAME_STATE, }; XrFrameWaitInfo frameWaitInfo = { .type = XR_TYPE_FRAME_WAIT_INFO, }; result = xrWaitFrame (self->session, &frameWaitInfo, &frame_state); if (!_check_xr_result (result, "xrWaitFrame() was not successful, exiting...")) return FALSE; uint32_t buffer_index = 0; XrSwapchainImageAcquireInfo swapchainImageAcquireInfo = { .type = XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, }; result = xrAcquireSwapchainImage (self->swapchain, &swapchainImageAcquireInfo, &buffer_index); if (!_check_xr_result (result, "failed to acquire swapchain image!")) return FALSE; XrSwapchainImageWaitInfo swapchainImageWaitInfo = { .type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, .timeout = INT64_MAX, }; result = xrWaitSwapchainImage (self->swapchain, &swapchainImageWaitInfo); if (!_check_xr_result (result, "failed to wait for swapchain image!")) return FALSE; g_mutex_lock (&self->wait_frame_mutex); self->buffer_index = buffer_index; if (!self->should_render && frame_state.shouldRender) { GxrStateChangeEvent state_change_event = { .state_change = GXR_STATE_RENDERING_START, }; g_debug ("Event: start rendering"); g_signal_emit (self, context_signals[STATE_CHANGE_EVENT], 0, &state_change_event); } else if (self->should_render && !frame_state.shouldRender) { GxrStateChangeEvent state_change_event = { .state_change = GXR_STATE_RENDERING_STOP, }; g_debug ("Event: stop rendering"); g_signal_emit (self, context_signals[STATE_CHANGE_EVENT], 0, &state_change_event); } self->should_render = frame_state.shouldRender == XR_TRUE; self->predicted_display_time = frame_state.predictedDisplayTime; self->predicted_display_period = frame_state.predictedDisplayPeriod; g_mutex_unlock (&self->wait_frame_mutex); return TRUE; } static gboolean _begin_frame (GxrContext *self) { if (!self->session_running) { g_printerr ("Can't begin frame with no session running\n"); return FALSE; } if (self->session_state == XR_SESSION_STATE_EXITING || self->session_state == XR_SESSION_STATE_LOSS_PENDING || self->session_state == XR_SESSION_STATE_STOPPING) return FALSE; XrResult result; g_mutex_lock (&self->wait_frame_mutex); XrTime predicted_display_time = self->predicted_display_time; g_mutex_unlock (&self->wait_frame_mutex); // --- Create projection matrices and view matrices for each eye XrViewLocateInfo viewLocateInfo = { .type = XR_TYPE_VIEW_LOCATE_INFO, .displayTime = predicted_display_time, .space = self->play_space, .viewConfigurationType = self->view_config_type, }; XrViewState viewState = { .type = XR_TYPE_VIEW_STATE, }; uint32_t viewCountOutput; result = xrLocateViews (self->session, &viewLocateInfo, &viewState, self->view_count, &viewCountOutput, self->views); if (!_check_xr_result (result, "Could not locate views")) return FALSE; // --- Begin frame XrFrameBeginInfo frameBeginInfo = { .type = XR_TYPE_FRAME_BEGIN_INFO, }; result = xrBeginFrame (self->session, &frameBeginInfo); if (!_check_xr_result (result, "failed to begin frame!")) return FALSE; self->have_valid_pose = (viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) != 0 && (viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) != 0; return TRUE; } gboolean gxr_context_begin_frame (GxrContext *self) { if (!_begin_frame (self)) { g_printerr ("Could not begin XR frame.\n"); return FALSE; } for (uint32_t i = 0; i < 2; i++) { self->projection_views[i].pose = self->views[i].pose; self->projection_views[i].fov = self->views[i].fov; self->projection_views[i].subImage.imageArrayIndex = i; } /* TODO: update poses, so GxrContext can update them for DeviceManager. * device poses are used for device model rendering, which is not in the * OpenXR spec yet. * Interaction is done using aim and grip pose actions instead. */ /* TODO: This can be probably removed */ GxrPose poses[GXR_DEVICE_INDEX_MAX]; for (int i = 0; i < GXR_DEVICE_INDEX_MAX; i++) { poses[i].is_valid = FALSE; } GxrDeviceManager *dm = gxr_context_get_device_manager (self); gxr_device_manager_update_poses (dm, poses); return TRUE; } static gboolean _end_frame (GxrContext *self) { if (!self->session_running) { g_printerr ("Can't end frame with no session running\n"); return FALSE; } XrResult result; const XrCompositionLayerBaseHeader *layers[1]; uint32_t layer_count = 0; g_mutex_lock (&self->wait_frame_mutex); // if we end up here but shouldn't render, the app probably hasn't rendered if (self->should_render) { // for submiting projection layers we need a valid pose from xrLocateViews if (self->have_valid_pose) { layers[layer_count++] = (const XrCompositionLayerBaseHeader *) &self ->projection_layer; } } XrFrameEndInfo frame_end_info = { .type = XR_TYPE_FRAME_END_INFO, .displayTime = self->predicted_display_time, .layerCount = layer_count, .layers = layers, .environmentBlendMode = self->blend_mode, }; result = xrEndFrame (self->session, &frame_end_info); if (!_check_xr_result (result, "failed to end frame!")) return FALSE; g_mutex_unlock (&self->wait_frame_mutex); return TRUE; } static gboolean _release_swapchain (GxrContext *self) { XrSwapchainImageReleaseInfo swapchainImageReleaseInfo = { .type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, }; XrResult result = xrReleaseSwapchainImage (self->swapchain, &swapchainImageReleaseInfo); if (!_check_xr_result (result, "failed to release swapchain image!")) return FALSE; return TRUE; } gboolean gxr_context_end_frame (GxrContext *self) { if (!_release_swapchain (self)) { g_printerr ("Could not release xr swapchain\n"); return FALSE; } if (!_end_frame (self)) { g_printerr ("Could not end xr frame\n"); } return TRUE; } void gxr_context_request_quit (GxrContext *self) { /* _poll_event will send quit event once session is in STOPPING state */ xrRequestExitSession (self->session); } gboolean gxr_context_get_runtime_instance_extensions (GxrContext *self, GSList **out_list) { PFN_xrGetVulkanInstanceExtensionsKHR GetVulkanInstanceExtensionsKHR = NULL; XrResult res; res = xrGetInstanceProcAddr (self->instance, "xrGetVulkanInstanceExtensionsKHR", (PFN_xrVoidFunction *) (&GetVulkanInstanceExtensionsKHR)); if (!_check_xr_result (res, "Failed to retrieve " "xrGetVulkanGraphicsRequirements2KHR pointer!")) return FALSE; uint32_t ext_str_len = 0; res = GetVulkanInstanceExtensionsKHR (self->instance, self->system_id, 0, &ext_str_len, NULL); if (!_check_xr_result (res, "Failed to get Vulkan instance extension string " "size!")) return FALSE; gchar *ext_str = g_malloc (sizeof (gchar) * ext_str_len); res = GetVulkanInstanceExtensionsKHR (self->instance, self->system_id, ext_str_len, &ext_str_len, ext_str); if (!_check_xr_result (res, "Failed to get Vulkan instance extension string!")) { g_free (ext_str); return FALSE; } g_debug ("runtime instance ext str: %s", ext_str); gchar **ext_str_split = g_strsplit (ext_str, " ", 0); for (gchar **token = ext_str_split; *token; token++) *out_list = g_slist_append (*out_list, g_strdup (*token)); g_strfreev (ext_str_split); g_free (ext_str); return TRUE; } gboolean gxr_context_get_runtime_device_extensions (GxrContext *self, GSList **out_list) { PFN_xrGetVulkanDeviceExtensionsKHR GetVulkanDeviceExtensionsKHR = NULL; XrResult res; res = xrGetInstanceProcAddr (self->instance, "xrGetVulkanDeviceExtensionsKHR", (PFN_xrVoidFunction *) (&GetVulkanDeviceExtensionsKHR)); if (!_check_xr_result (res, "Failed to retrieve " "xrGetVulkanGraphicsRequirements2KHR pointer!")) return FALSE; uint32_t ext_str_len = 0; res = GetVulkanDeviceExtensionsKHR (self->instance, self->system_id, 0, &ext_str_len, NULL); if (!_check_xr_result (res, "Failed to get Vulkan device extension string size!")) return FALSE; gchar *ext_str = g_malloc (sizeof (gchar) * ext_str_len); res = GetVulkanDeviceExtensionsKHR (self->instance, self->system_id, ext_str_len, &ext_str_len, ext_str); if (!_check_xr_result (res, "Failed to get Vulkan device extension string!")) { g_free (ext_str); return FALSE; } g_debug ("runtime device ext str: %s", ext_str); gchar **ext_str_split = g_strsplit (ext_str, " ", 0); for (gchar **token = ext_str_split; *token; token++) *out_list = g_slist_append (*out_list, g_strdup (*token)); g_strfreev (ext_str_split); g_free (ext_str); return TRUE; return TRUE; } GxrDeviceManager * gxr_context_get_device_manager (GxrContext *self) { return self->device_manager; } uint32_t gxr_context_get_swapchain_length (GxrContext *self) { return self->swapchain_length; } GulkanFrameBuffer * gxr_context_get_acquired_framebuffer (GxrContext *self) { g_mutex_lock (&self->wait_frame_mutex); GulkanFrameBuffer *fb = GULKAN_FRAME_BUFFER (self->framebuffers [self->buffer_index]); g_mutex_unlock (&self->wait_frame_mutex); return fb; } GulkanFrameBuffer * gxr_context_get_framebuffer_at (GxrContext *self, uint32_t i) { GulkanFrameBuffer *fb = GULKAN_FRAME_BUFFER (self->framebuffers[i]); return fb; } uint32_t gxr_context_get_buffer_index (GxrContext *self) { g_mutex_lock (&self->wait_frame_mutex); uint32_t buffer_index = self->buffer_index; g_mutex_unlock (&self->wait_frame_mutex); return buffer_index; } XrSessionState gxr_context_get_session_state (GxrContext *self) { return self->session_state; } XrTime gxr_context_get_predicted_display_time (GxrContext *self) { g_mutex_lock (&self->wait_frame_mutex); XrTime time = self->predicted_display_time; g_mutex_unlock (&self->wait_frame_mutex); return time; } XrInstance gxr_context_get_openxr_instance (GxrContext *self) { return self->instance; } XrSession gxr_context_get_openxr_session (GxrContext *self) { return self->session; } XrSpace gxr_context_get_tracked_space (GxrContext *self) { return self->play_space; } VkExtent2D gxr_context_get_swapchain_extent (GxrContext *self, uint32_t view_index) { VkExtent2D extent = { .width = self->configuration_views[view_index].recommendedImageRectWidth, .height = self->configuration_views[view_index].recommendedImageRectHeight, }; return extent; } static GxrAction * _find_openxr_action_from_url (GxrActionSet **sets, uint32_t count, gchar *url) { for (uint32_t i = 0; i < count; i++) { GSList *actions = gxr_action_set_get_actions (GXR_ACTION_SET (sets[i])); for (GSList *l = actions; l != NULL; l = l->next) { GxrAction *action = GXR_ACTION (l->data); gchar *action_url = gxr_action_get_url (action); if (g_strcmp0 (action_url, url) == 0) return action; } } g_debug ("Skipping action %s not connected by application", url); return NULL; } static uint32_t _count_input_paths (GxrBindingManifest *binding_manifest) { uint32_t num = 0; for (GSList *l = binding_manifest->gxr_bindings; l; l = l->next) { GxrBinding *binding = l->data; num += g_slist_length (binding->input_paths); } return num; } static void _update_controllers (GxrActionSet *self) { GSList *actions = gxr_action_set_get_actions (GXR_ACTION_SET (self)); for (GSList *l = actions; l != NULL; l = l->next) { GxrAction *action = GXR_ACTION (l->data); gxr_action_update_controllers (action); } } static gchar * _component_to_str (GxrBindingComponent c) { switch (c) { case GXR_BINDING_COMPONENT_CLICK: return "click"; case GXR_BINDING_COMPONENT_PULL: return "value"; case GXR_BINDING_COMPONENT_TOUCH: return "touch"; case GXR_BINDING_COMPONENT_FORCE: return "force"; case GXR_BINDING_COMPONENT_POSITION: /* use .../trackpad instead of .../trackpad/x etc. */ return NULL; default: return NULL; } } static gboolean _suggest_for_interaction_profile (GxrActionSet **sets, uint32_t count, XrInstance instance, GxrBindingManifest *binding_manifest) { uint32_t num_bindings = _count_input_paths (binding_manifest); XrActionSuggestedBinding *suggested_bindings = g_malloc (sizeof (XrActionSuggestedBinding) * (unsigned long) num_bindings); uint32_t num_suggestion = 0; for (GSList *l = binding_manifest->gxr_bindings; l != NULL; l = l->next) { GxrBinding *binding = l->data; gchar *action_url = binding->action->name; GxrAction *action = _find_openxr_action_from_url (sets, count, action_url); if (!action) continue; XrAction handle = gxr_action_get_handle (action); char *url = gxr_action_get_url (action); g_debug ("Action %s has %d inputs", url, g_slist_length (binding->input_paths)); for (GSList *m = binding->input_paths; m; m = m->next) { GxrBindingPath *input_path = m->data; gchar *component_str = _component_to_str (input_path->component); GString *full_path = g_string_new (""); if (component_str) g_string_printf (full_path, "%s/%s", input_path->path, component_str); else g_string_append (full_path, input_path->path); g_debug ("\t%s ", full_path->str); XrPath component_path; xrStringToPath (instance, full_path->str, &component_path); suggested_bindings[num_suggestion].action = handle; suggested_bindings[num_suggestion].binding = component_path; num_suggestion++; g_string_free (full_path, TRUE); } } g_debug ("Suggested %d/%d bindings!", num_suggestion, num_bindings); XrPath profile_path; xrStringToPath (instance, binding_manifest->interaction_profile, &profile_path); const XrInteractionProfileSuggestedBinding suggestion_info = { .type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING, .next = NULL, .interactionProfile = profile_path, .countSuggestedBindings = num_suggestion, .suggestedBindings = suggested_bindings, }; XrResult result = xrSuggestInteractionProfileBindings (instance, &suggestion_info); g_free (suggested_bindings); if (result != XR_SUCCESS) { char buffer[XR_MAX_RESULT_STRING_SIZE]; xrResultToString (instance, result, buffer); g_printerr ("ERROR: Suggesting actions for profile %s: %s\n", binding_manifest->interaction_profile, buffer); return FALSE; } return TRUE; } gboolean gxr_context_attach_action_sets (GxrContext *self, GxrActionSet **sets, uint32_t count) { for (uint32_t i = 0; i < count; i++) { uint32_t j; for (j = 0; j < i; j++) { GxrManifest *manifest_i = gxr_action_set_get_manifest (sets[i]); GxrManifest *manifest_j = gxr_action_set_get_manifest (sets[i]); if (manifest_i == manifest_j) break; } if (i == j) { // do for every unique manifest GxrManifest *manifest = gxr_action_set_get_manifest (sets[i]); GSList *binding_manifests = gxr_manifest_get_binding_manifests (manifest); for (GSList *l = binding_manifests; l; l = l->next) { GxrBindingManifest *binding_manifest = l->data; g_debug ("==="); g_debug ("Suggesting for profile %s", binding_manifest->interaction_profile); _suggest_for_interaction_profile (sets, count, self->instance, binding_manifest); } } } XrActionSet *handles = g_malloc (sizeof (XrActionSet) * count); for (uint32_t i = 0; i < count; i++) { handles[i] = gxr_action_set_get_handle (sets[i]); } XrSessionActionSetsAttachInfo attachInfo = { .type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO, .next = NULL, .countActionSets = count, .actionSets = handles, }; XrResult result = xrAttachSessionActionSets (self->session, &attachInfo); g_free (handles); if (result != XR_SUCCESS) { char buffer[XR_MAX_RESULT_STRING_SIZE]; xrResultToString (self->instance, result, buffer); g_printerr ("ERROR: attaching action set: %s\n", buffer); return FALSE; } g_debug ("Attached %d action sets", count); for (uint32_t i = 0; i < count; i++) { _update_controllers (sets[i]); } g_debug ("Updating controllers based on actions"); return TRUE; } 07070100000058000081A4000000000000000000000001636E648600001239000000000000000000000000000000000000001600000000gxr/src/gxr-context.h/* * gxr * Copyright 2019 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_CONTEXT_H_ #define GXR_CONTEXT_H_ #if !defined(GXR_INSIDE) && !defined(GXR_COMPILATION) #error "Only <gxr.h> can be included directly." #endif #include <glib-object.h> #include <graphene.h> #include <gulkan.h> #include <stdint.h> #include "gxr-device-manager.h" G_BEGIN_DECLS #define GXR_DEVICE_INDEX_MAX 64 #define GXR_TYPE_CONTEXT gxr_context_get_type () G_DECLARE_FINAL_TYPE (GxrContext, gxr_context, GXR, CONTEXT, GObject) /** * GxrContextClass: * @parent: The parent class */ struct _GxrContextClass { GObjectClass parent; }; /** * GxrEye: * @GXR_EYE_LEFT: Left eye. * @GXR_EYE_RIGHT: Right eye. * * Type of Gxr viewport. * **/ typedef enum { GXR_EYE_LEFT = 0, GXR_EYE_RIGHT = 1 } GxrEye; /** * GxrStateChange: * @GXR_STATE_FRAMECYCLE_START: Ready to call gxr_context_begin_frame / * gxr_context_end_frame. * @GXR_STATE_FRAMECYCLE_STOP: Not ready to call gxr_context_begin_frame / * gxr_context_end_frame. * @GXR_STATE_RENDERING_START: The frame content will be shown in XR. * @GXR_STATE_RENDERING_STOP: The frame content will not be visible, expensive * rendering work can be skipped, but gxr_context_begin_frame / * gxr_context_end_frame should be called. * @GXR_STATE_SHUTDOWN: XR Runtime is shutting down. * **/ typedef enum { GXR_STATE_FRAMECYCLE_START, GXR_STATE_FRAMECYCLE_STOP, GXR_STATE_RENDERING_START, GXR_STATE_RENDERING_STOP, GXR_STATE_SHUTDOWN, } GxrStateChange; /** * GxrStateChangeEvent: * @state_change: The #GxrStateChange. * * Event that is emitted when the application needs to quit. **/ typedef struct { GxrStateChange state_change; } GxrStateChangeEvent; /** * GxrOverlayEvent: * @main_session_visible: If a Main session is visible after this event. * * Event that is emitted when running in OpenXR overlay mode. **/ typedef struct { bool main_session_visible; } GxrOverlayEvent; GxrContext * gxr_context_new (char *app_name, uint32_t app_version); GxrContext * gxr_context_new_from_vulkan_extensions (GSList *instance_ext_list, GSList *device_ext_list, char *app_name, uint32_t app_version); gboolean gxr_context_get_head_pose (GxrContext *self, graphene_matrix_t *pose); void gxr_context_get_frustum_angles (GxrContext *self, GxrEye eye, float *left, float *right, float *top, float *bottom); gboolean gxr_context_init_framebuffers (GxrContext *self, VkExtent2D extent, VkSampleCountFlagBits sample_count, GulkanRenderPass **render_pass); void gxr_context_poll_events (GxrContext *self); void gxr_context_get_projection (GxrContext *self, GxrEye eye, float near, float far, graphene_matrix_t *mat); void gxr_context_get_view (GxrContext *self, GxrEye eye, graphene_matrix_t *mat); void gxr_context_get_eye_position (GxrContext *self, GxrEye eye, graphene_vec3_t *v); gboolean gxr_context_wait_frame (GxrContext *self); gboolean gxr_context_begin_frame (GxrContext *self); gboolean gxr_context_end_frame (GxrContext *self); void gxr_context_request_quit (GxrContext *self); GulkanContext * gxr_context_get_gulkan (GxrContext *self); gboolean gxr_context_get_runtime_instance_extensions (GxrContext *self, GSList **out_list); gboolean gxr_context_get_runtime_device_extensions (GxrContext *self, GSList **out_list); GxrDeviceManager * gxr_context_get_device_manager (GxrContext *self); uint32_t gxr_context_get_swapchain_length (GxrContext *self); GulkanFrameBuffer * gxr_context_get_acquired_framebuffer (GxrContext *self); GulkanFrameBuffer * gxr_context_get_framebuffer_at (GxrContext *self, uint32_t i); VkExtent2D gxr_context_get_swapchain_extent (GxrContext *self, uint32_t view_index); uint32_t gxr_context_get_buffer_index (GxrContext *self); gboolean gxr_context_attach_action_sets (GxrContext *self, GxrActionSet **sets, uint32_t count); G_END_DECLS #endif /* GXR_CONTEXT_H_ */ 07070100000059000081A4000000000000000000000001636E648600000AA9000000000000000000000000000000000000001900000000gxr/src/gxr-controller.c/* * xrdesktop * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-controller.h" struct _GxrController { GxrDevice parent; graphene_matrix_t pointer_pose; gboolean pointer_pose_valid; graphene_matrix_t hand_grip_pose; gboolean hand_grip_pose_valid; }; G_DEFINE_TYPE (GxrController, gxr_controller, GXR_TYPE_DEVICE) enum { MOVE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = {0}; static void gxr_controller_finalize (GObject *gobject); static void gxr_controller_class_init (GxrControllerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_controller_finalize; signals[MOVE] = g_signal_new ("move", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE); } static void gxr_controller_init (GxrController *self) { graphene_matrix_init_identity (&self->pointer_pose); graphene_matrix_init_identity (&self->hand_grip_pose); self->pointer_pose_valid = FALSE; self->hand_grip_pose_valid = FALSE; } GxrController * gxr_controller_new (guint64 controller_handle) { GxrController *controller = (GxrController *) g_object_new (GXR_TYPE_CONTROLLER, 0); GxrDevice *device = GXR_DEVICE (controller); gxr_device_set_handle (device, controller_handle); return controller; } static void gxr_controller_finalize (GObject *gobject) { G_OBJECT_CLASS (gxr_controller_parent_class)->finalize (gobject); } void gxr_controller_get_hand_grip_pose (GxrController *self, graphene_matrix_t *pose) { graphene_matrix_init_from_matrix (pose, &self->hand_grip_pose); } void gxr_controller_update_pointer_pose (GxrController *self, GxrPoseEvent *event) { graphene_matrix_init_from_matrix (&self->pointer_pose, &event->pose); gboolean valid = event->device_connected && event->active && event->valid; self->pointer_pose_valid = valid; g_signal_emit (self, signals[MOVE], 0, event); } void gxr_controller_update_hand_grip_pose (GxrController *self, GxrPoseEvent *event) { graphene_matrix_init_from_matrix (&self->hand_grip_pose, &event->pose); gboolean valid = event->device_connected && event->active && event->valid; self->hand_grip_pose_valid = valid; } gboolean gxr_controller_is_pointer_pose_valid (GxrController *self) { return self->pointer_pose_valid; } gboolean gxr_controller_get_pointer_pose (GxrController *self, graphene_matrix_t *pose) { graphene_matrix_init_from_matrix (pose, &self->pointer_pose); return self->pointer_pose_valid; } 0707010000005A000081A4000000000000000000000001636E648600000B3D000000000000000000000000000000000000001900000000gxr/src/gxr-controller.h/* * xrdesktop * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_CONTROLLER_H_ #define GXR_CONTROLLER_H_ #include <glib-object.h> #include <graphene.h> #include "gxr-device.h" G_BEGIN_DECLS #define GXR_TYPE_CONTROLLER gxr_controller_get_type () G_DECLARE_FINAL_TYPE (GxrController, gxr_controller, GXR, CONTROLLER, GxrDevice) /** * GxrDigitalEvent: * @active: Whether or not this action is currently available to be bound in the *active action set. * @state: Pressed or released. * @changed: Whether the state has changed since last event. * @controller: The controller identifier. * @time: The time of the event. * * Digital event. **/ // clang-format off // https://gitlab.gnome.org/GNOME/gtk-doc/-/issues/91 typedef struct { gboolean active; gboolean state; gboolean changed; GxrController *controller; gfloat time; } GxrDigitalEvent; // clang-format on /** * GxrAnalogEvent: * @active: Whether or not this action is currently available to be bound in the *active action set. * @state: A #graphene_vec3_t analog state. * @delta: State delta since last event. * @controller: The controller identifier. * @time: The time of the event. * * Analog event. **/ // clang-format off // https://gitlab.gnome.org/GNOME/gtk-doc/-/issues/91 typedef struct { gboolean active; graphene_vec3_t state; graphene_vec3_t delta; GxrController *controller; gfloat time; } GxrAnalogEvent; // clang-format on /** * GxrPoseEvent: * @active: Whether or not this action is currently available to be bound in the *active action set. * @pose: The #graphene_matrix_t pose. * @velocity: Velocity * @angular_velocity: Angular velocity. * @valid: Whether the pose is valid. * @device_connected: Whether the device is currently connected. * @controller: The controller identifier. * * Pose event. **/ // clang-format off // https://gitlab.gnome.org/GNOME/gtk-doc/-/issues/91 typedef struct { gboolean active; graphene_matrix_t pose; graphene_vec3_t velocity; graphene_vec3_t angular_velocity; gboolean valid; gboolean device_connected; GxrController *controller; } GxrPoseEvent; // clang-format on GxrController * gxr_controller_new (guint64 controller_handle); void gxr_controller_get_hand_grip_pose (GxrController *self, graphene_matrix_t *pose); void gxr_controller_update_pointer_pose (GxrController *self, GxrPoseEvent *event); void gxr_controller_update_hand_grip_pose (GxrController *self, GxrPoseEvent *event); gboolean gxr_controller_is_pointer_pose_valid (GxrController *self); gboolean gxr_controller_get_pointer_pose (GxrController *self, graphene_matrix_t *pose); G_END_DECLS #endif /* GXR_CONTROLLER_H_ */ 0707010000005B000081A4000000000000000000000001636E6486000019D8000000000000000000000000000000000000001D00000000gxr/src/gxr-device-manager.c/* * xrdesktop * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-device-manager.h" struct _GxrDeviceManager { GObject parent; GHashTable *devices; // gint64 -> XrdSceneDevice // controllers are also put into a list for easy controller only iteration GSList *controllers; GMutex device_mutex; }; G_DEFINE_TYPE (GxrDeviceManager, gxr_device_manager, G_TYPE_OBJECT) enum { DEVICE_ACTIVATE_EVENT, DEVICE_DEACTIVATE_EVENT, LAST_SIGNAL }; static guint dm_signals[LAST_SIGNAL] = {0}; static void gxr_device_manager_finalize (GObject *gobject); static void gxr_device_manager_class_init (GxrDeviceManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_device_manager_finalize; dm_signals[DEVICE_ACTIVATE_EVENT] = g_signal_new ("device-activate-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE); dm_signals[DEVICE_DEACTIVATE_EVENT] = g_signal_new ("device-deactivate-event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE); } static void gxr_device_manager_init (GxrDeviceManager *self) { self->devices = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_object_unref); self->controllers = NULL; g_mutex_init (&self->device_mutex); } GxrDeviceManager * gxr_device_manager_new (void) { return (GxrDeviceManager *) g_object_new (GXR_TYPE_DEVICE_MANAGER, 0); } static void gxr_device_manager_finalize (GObject *gobject) { GxrDeviceManager *self = GXR_DEVICE_MANAGER (gobject); g_slist_free (self->controllers); g_hash_table_unref (self->devices); g_mutex_clear (&self->device_mutex); } static void _insert_at_key (GHashTable *table, guint64 key, gpointer value) { guint64 *keyp = g_new0 (guint64, 1); *keyp = key; g_hash_table_insert (table, keyp, value); } GSList * gxr_device_manager_get_controllers (GxrDeviceManager *self) { g_mutex_lock (&self->device_mutex); GSList *controllers = self->controllers; g_mutex_unlock (&self->device_mutex); return controllers; } gboolean gxr_device_manager_add (GxrDeviceManager *self, guint64 device_id, bool is_controller) { g_mutex_lock (&self->device_mutex); if (g_hash_table_lookup (self->devices, &device_id) != NULL) { g_debug ("Device %lu already added", device_id); g_mutex_unlock (&self->device_mutex); return FALSE; } GxrDevice *device; if (is_controller) { device = GXR_DEVICE (gxr_controller_new (device_id)); g_signal_emit (self, dm_signals[DEVICE_ACTIVATE_EVENT], 0, device); } else { device = gxr_device_new (device_id); } g_debug ("Created device for %lu, is controller: %d", device_id, is_controller); if (is_controller) self->controllers = g_slist_append (self->controllers, device); _insert_at_key (self->devices, device_id, device); g_mutex_unlock (&self->device_mutex); return TRUE; } void gxr_device_manager_remove (GxrDeviceManager *self, guint64 device_id) { g_mutex_lock (&self->device_mutex); GxrDevice *device = g_hash_table_lookup (self->devices, &device_id); if (!device) { g_print ("Device Manager: Returned nonexistent device\n"); g_mutex_unlock (&self->device_mutex); return; } /* g_hash_table_remove destroys device */ g_signal_emit (self, dm_signals[DEVICE_DEACTIVATE_EVENT], 0, device); if (gxr_device_is_controller (device)) self->controllers = g_slist_remove (self->controllers, device); g_hash_table_remove (self->devices, &device_id); g_debug ("Destroyed device %lu", device_id); g_mutex_unlock (&self->device_mutex); } void gxr_device_manager_update_poses (GxrDeviceManager *self, GxrPose *poses) { g_mutex_lock (&self->device_mutex); GList *device_keys = g_hash_table_get_keys (self->devices); for (GList *l = device_keys; l; l = l->next) { guint64 *key = l->data; guint64 i = *key; GxrDevice *device = g_hash_table_lookup (self->devices, &i); if (!device) { g_print ("Device Manager: Device %lu not known?!\n", i); continue; } gxr_device_set_is_pose_valid (device, poses[i].is_valid); if (!poses[i].is_valid) continue; gxr_device_set_transformation_direct (device, &poses[i].transformation); } g_list_free (device_keys); g_mutex_unlock (&self->device_mutex); } GxrDevice * gxr_device_manager_get (GxrDeviceManager *self, guint64 device_id) { g_mutex_lock (&self->device_mutex); GxrDevice *d = g_hash_table_lookup (self->devices, &device_id); g_mutex_unlock (&self->device_mutex); return d; } GList * gxr_device_manager_get_devices (GxrDeviceManager *self) { g_mutex_lock (&self->device_mutex); GList *devices = g_hash_table_get_values (self->devices); g_mutex_unlock (&self->device_mutex); return devices; } static void _update_pointer_pose_cb (GxrAction *action, GxrPoseEvent *event, GxrDeviceManager *self) { (void) action; (void) self; g_mutex_lock (&self->device_mutex); gxr_controller_update_pointer_pose (event->controller, event); g_mutex_unlock (&self->device_mutex); } static void _update_hand_grip_pose_cb (GxrAction *action, GxrPoseEvent *event, GxrDeviceManager *self) { (void) action; (void) self; g_mutex_lock (&self->device_mutex); gxr_controller_update_hand_grip_pose (event->controller, event); g_mutex_unlock (&self->device_mutex); } void gxr_device_manager_connect_pose_actions (GxrDeviceManager *self, GxrActionSet *action_set, gchar *pointer_pose_url, gchar *hand_grip_pose_url) { if (pointer_pose_url) gxr_action_set_connect (action_set, GXR_ACTION_POSE, pointer_pose_url, (GCallback) _update_pointer_pose_cb, self); if (hand_grip_pose_url) gxr_action_set_connect (action_set, GXR_ACTION_POSE, hand_grip_pose_url, (GCallback) _update_hand_grip_pose_cb, self); } 0707010000005C000081A4000000000000000000000001636E64860000075B000000000000000000000000000000000000001D00000000gxr/src/gxr-device-manager.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXRDEVICE_MANAGER_H_ #define GXRDEVICE_MANAGER_H_ #include <glib-object.h> #include <gulkan.h> #include "gxr-action-set.h" #include "gxr-device.h" G_BEGIN_DECLS #define GXR_TYPE_DEVICE_MANAGER gxr_device_manager_get_type () G_DECLARE_FINAL_TYPE (GxrDeviceManager, gxr_device_manager, GXR, DEVICE_MANAGER, GObject) #ifndef __GTK_DOC_IGNORE__ typedef struct _GxrContext GxrContext; #endif /** * GxrPose: * @transformation: The #graphene_matrix_t. * @is_valid: Validity of the pose. * * A 4x4 matrix pose. **/ // clang-format off // https://gitlab.gnome.org/GNOME/gtk-doc/-/issues/91 typedef struct { graphene_matrix_t transformation; gboolean is_valid; } GxrPose; // clang-format on GxrDeviceManager * gxr_device_manager_new (void); gboolean gxr_device_manager_add (GxrDeviceManager *self, guint64 device_id, bool is_controller); void gxr_device_manager_remove (GxrDeviceManager *self, guint64 device_id); void gxr_device_manager_update_poses (GxrDeviceManager *self, GxrPose *poses); GSList * gxr_device_manager_get_controllers (GxrDeviceManager *self); GxrDevice * gxr_device_manager_get (GxrDeviceManager *self, guint64 device_id); GList * gxr_device_manager_get_devices (GxrDeviceManager *self); void gxr_device_manager_connect_pose_actions (GxrDeviceManager *self, GxrActionSet *action_set, gchar *pointer_pose_url, gchar *hand_grip_pose_url); G_END_DECLS #endif /* GXRDEVICE_MANAGER_H_ */ 0707010000005D000081A4000000000000000000000001636E648600000978000000000000000000000000000000000000001500000000gxr/src/gxr-device.c/* * xrdesktop * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-device.h" #include "gxr-controller.h" typedef struct _GxrDevicePrivate { GObject parent; guint64 device_id; gboolean pose_valid; graphene_matrix_t transformation; } GxrDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (GxrDevice, gxr_device, G_TYPE_OBJECT) static void gxr_device_finalize (GObject *gobject); static void gxr_device_class_init (GxrDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_device_finalize; } static void gxr_device_init (GxrDevice *self) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); priv->pose_valid = FALSE; } GxrDevice * gxr_device_new (guint64 device_id) { GxrDevice *self = (GxrDevice *) g_object_new (GXR_TYPE_DEVICE, 0); GxrDevicePrivate *priv = gxr_device_get_instance_private (self); priv->device_id = device_id; return self; } static void gxr_device_finalize (GObject *gobject) { G_OBJECT_CLASS (gxr_device_parent_class)->finalize (gobject); } gboolean gxr_device_is_controller (GxrDevice *self) { return GXR_IS_CONTROLLER (self); } void gxr_device_set_is_pose_valid (GxrDevice *self, bool valid) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); priv->pose_valid = valid; } gboolean gxr_device_is_pose_valid (GxrDevice *self) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); return priv->pose_valid; } /* * Set transformation without matrix decomposition and ability to rebuild * This will include scale as well. */ void gxr_device_set_transformation_direct (GxrDevice *self, graphene_matrix_t *mat) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); graphene_matrix_init_from_matrix (&priv->transformation, mat); } void gxr_device_get_transformation_direct (GxrDevice *self, graphene_matrix_t *mat) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); graphene_matrix_init_from_matrix (mat, &priv->transformation); } guint64 gxr_device_get_handle (GxrDevice *self) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); return priv->device_id; } void gxr_device_set_handle (GxrDevice *self, guint64 handle) { GxrDevicePrivate *priv = gxr_device_get_instance_private (self); priv->device_id = handle; } 0707010000005E000081A4000000000000000000000001636E6486000004A5000000000000000000000000000000000000001500000000gxr/src/gxr-device.h/* * xrdesktop * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_DEVICE_H_ #define GXR_DEVICE_H_ #if !defined(GXR_INSIDE) && !defined(GXR_COMPILATION) #error "Only <gxr.h> can be included directly." #endif #include <glib-object.h> #include <graphene.h> G_BEGIN_DECLS #define GXR_TYPE_DEVICE gxr_device_get_type () G_DECLARE_DERIVABLE_TYPE (GxrDevice, gxr_device, GXR, DEVICE, GObject) /** * GxrDeviceClass: * @parent: The parent class */ struct _GxrDeviceClass { GObjectClass parent; }; GxrDevice * gxr_device_new (guint64 device_id); gboolean gxr_device_initialize (GxrDevice *self); gboolean gxr_device_is_controller (GxrDevice *self); void gxr_device_set_is_pose_valid (GxrDevice *self, bool valid); gboolean gxr_device_is_pose_valid (GxrDevice *self); void gxr_device_set_transformation_direct (GxrDevice *self, graphene_matrix_t *mat); void gxr_device_get_transformation_direct (GxrDevice *self, graphene_matrix_t *mat); guint64 gxr_device_get_handle (GxrDevice *self); void gxr_device_set_handle (GxrDevice *self, guint64 handle); G_END_DECLS #endif /* GXR_DEVICE_H_ */ 0707010000005F000081A4000000000000000000000001636E64860000080D000000000000000000000000000000000000001100000000gxr/src/gxr-io.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include <gio/gio.h> #include "gxr-io.h" GString * gxr_io_get_cache_path (const gchar *dir_name) { GString *string = g_string_new (""); g_string_printf (string, "%s/%s", g_get_user_cache_dir (), dir_name); return string; } gboolean gxr_io_write_resource_to_file (const gchar *res_base_path, gchar *cache_path, const gchar *file_name, GString *file_path) { GError *error = NULL; GString *res_path = g_string_new (""); g_string_printf (res_path, "%s/%s", res_base_path, file_name); GInputStream *res_input_stream = g_resources_open_stream (res_path->str, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); g_string_free (res_path, TRUE); g_string_printf (file_path, "%s/%s", cache_path, file_name); GFile *out_file = g_file_new_for_path (file_path->str); GFileOutputStream *output_stream; output_stream = g_file_replace (out_file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error); if (error != NULL) { g_printerr ("Unable to open output stream: %s\n", error->message); g_error_free (error); return FALSE; } gssize n_bytes_written = g_output_stream_splice ( G_OUTPUT_STREAM (output_stream), res_input_stream, (GOutputStreamSpliceFlags) (G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET | G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE), NULL, &error); g_object_unref (output_stream); g_object_unref (res_input_stream); g_object_unref (out_file); if (error != NULL) { g_printerr ("Unable to write to output stream: %s\n", error->message); g_error_free (error); return FALSE; } if (n_bytes_written == 0) { g_printerr ("No bytes written\n"); return FALSE; } return TRUE; } 07070100000060000081A4000000000000000000000001636E6486000002B0000000000000000000000000000000000000001100000000gxr/src/gxr-io.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_IO_H_ #define GXR_IO_H_ #if !defined(GXR_INSIDE) && !defined(GXR_COMPILATION) #error "Only <gxr.h> can be included directly." #endif #include <glib-object.h> gboolean gxr_io_create_directory_if_needed (gchar *path); gboolean gxr_io_write_resource_to_file (const gchar *res_base_path, gchar *cache_path, const gchar *file_name, GString *file_path); GString * gxr_io_get_cache_path (const gchar *dir_name); #endif /* GXR_IO_H_ */ 07070100000061000081A4000000000000000000000001636E6486000041F7000000000000000000000000000000000000001700000000gxr/src/gxr-manifest.c/* * gxr * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr-manifest.h" #include <json-glib/json-glib.h> struct _GxrManifest { GObject parent; GSList *action_manifest_entries; /* list of GxrBindingManifest */ GSList *bindings; GSList *binding_filenames; }; G_DEFINE_TYPE (GxrManifest, gxr_manifest, G_TYPE_OBJECT) static void gxr_manifest_finalize (GObject *gobject); static void gxr_manifest_class_init (GxrManifestClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxr_manifest_finalize; } static void gxr_manifest_init (GxrManifest *self) { self->action_manifest_entries = NULL; self->bindings = NULL; self->binding_filenames = NULL; } static GxrBindingType _get_binding_type (const gchar *type_string) { if (g_str_equal (type_string, "boolean")) return GXR_BINDING_TYPE_BOOLEAN; if (g_str_equal (type_string, "vector1")) return GXR_BINDING_TYPE_FLOAT; if (g_str_equal (type_string, "vector2")) return GXR_BINDING_TYPE_VEC2; if (g_str_equal (type_string, "pose")) return GXR_BINDING_TYPE_POSE; if (g_str_equal (type_string, "vibration")) return GXR_BINDING_TYPE_HAPTIC; g_printerr ("Binding type %s is not known\n", type_string); return GXR_BINDING_TYPE_UNKNOWN; } static GxrBindingMode _get_binding_mode (const gchar *mode_string) { if (mode_string == NULL) return GXR_BINDING_MODE_NONE; if (g_str_equal (mode_string, "button")) return GXR_BINDING_MODE_BUTTON; if (g_str_equal (mode_string, "trackpad")) return GXR_BINDING_MODE_TRACKPAD; if (g_str_equal (mode_string, "joystick")) return GXR_BINDING_MODE_ANALOG_STICK; g_printerr ("Binding mode %s is not known\n", mode_string); return GXR_BINDING_MODE_UNKNOWN; } static GxrBindingComponent _get_binding_component (const gchar *component_string) { if (g_str_equal (component_string, "click")) return GXR_BINDING_COMPONENT_CLICK; if (g_str_equal (component_string, "pull")) return GXR_BINDING_COMPONENT_PULL; if (g_str_equal (component_string, "position")) return GXR_BINDING_COMPONENT_POSITION; if (g_str_equal (component_string, "touch")) return GXR_BINDING_COMPONENT_TOUCH; if (g_str_equal (component_string, "force")) return GXR_BINDING_COMPONENT_FORCE; g_printerr ("Binding component %s is not known\n", component_string); return GXR_BINDING_COMPONENT_UNKNOWN; } static gboolean _parse_default_binding_filenames (GxrManifest *self, JsonObject *joroot) { JsonArray *jobindings = json_object_get_array_member (joroot, "default_bindings"); guint len = json_array_get_length (jobindings); g_debug ("parsing %d default binding filenames", len); for (guint i = 0; i < len; i++) { JsonObject *jobinding = json_array_get_object_element (jobindings, i); const gchar *controller_type = json_object_get_string_member (jobinding, "controller_type"); const gchar *binding_url = json_object_get_string_member (jobinding, "binding_url"); g_debug ("Parsed default binding filename %s: %s", controller_type, binding_url); self->binding_filenames = g_slist_append (self->binding_filenames, g_strdup (binding_url)); } return TRUE; } static gboolean _parse_actions (GxrManifest *self, GInputStream *stream) { GError *error = NULL; JsonParser *parser = json_parser_new (); json_parser_load_from_stream (parser, stream, NULL, &error); if (error) { g_print ("Unable to parse actions: %s\n", error->message); g_error_free (error); g_object_unref (parser); return FALSE; } JsonNode *jnroot = json_parser_get_root (parser); JsonObject *joroot; joroot = json_node_get_object (jnroot); _parse_default_binding_filenames (self, joroot); JsonArray *joactions = json_object_get_array_member (joroot, "actions"); guint len = json_array_get_length (joactions); g_debug ("parsing %d actions", len); for (guint i = 0; i < len; i++) { JsonObject *joaction = json_array_get_object_element (joactions, i); const gchar *name = json_object_get_string_member (joaction, "name"); const gchar *binding_type = json_object_get_string_member (joaction, "type"); g_debug ("\tParsed action %s: %s", name, binding_type); GxrActionManifestEntry *action = g_malloc (sizeof (GxrActionManifestEntry)); action->name = g_strdup (name); action->type = _get_binding_type (binding_type); self->action_manifest_entries = g_slist_append (self->action_manifest_entries, action); } g_object_unref (parser); return TRUE; } static GxrActionManifestEntry * _find_action_manifest_entry (GxrManifest *self, const gchar *action_name) { for (GSList *l = self->action_manifest_entries; l; l = l->next) { GxrActionManifestEntry *action = l->data; if (g_strcmp0 (action_name, action->name) == 0) { return action; } } return NULL; } static GxrBinding * _find_binding_for_action (GxrBindingManifest *bindings, GxrActionManifestEntry *action) { for (GSList *l = bindings->gxr_bindings; l; l = l->next) { GxrBinding *binding = l->data; if (binding->action == action) { return binding; } } return NULL; } static void _add_input_path_or_binding (GxrManifest *self, GxrBindingManifest *bindings, const gchar *action_name, GxrBindingMode mode, GxrBindingComponent component, const gchar *input_path) { GxrActionManifestEntry *action = _find_action_manifest_entry (self, action_name); if (action == NULL) { g_printerr ("%s: Attempted to add input %s for action %s " " which is not in action manifest!", bindings->filename, input_path, action_name); return; } GxrBinding *binding = _find_binding_for_action (bindings, action); if (binding == NULL) { binding = g_malloc (sizeof (GxrBinding)); binding->action = action; binding->input_paths = NULL; bindings->gxr_bindings = g_slist_append (bindings->gxr_bindings, binding); } GxrBindingPath *binding_path = g_malloc (sizeof (GxrBindingPath)); binding_path->component = component; binding_path->path = g_strdup (input_path); binding_path->mode = mode; binding->input_paths = g_slist_append (binding->input_paths, binding_path); } static void _parse_sources (GxrManifest *self, JsonArray *jasources, GxrBindingManifest *bindings) { guint sources_len = json_array_get_length (jasources); for (guint i = 0; i < sources_len; i++) { JsonObject *josource = json_array_get_object_element (jasources, i); const gchar *path = json_object_get_string_member (josource, "path"); const gchar *mode = NULL; if (json_object_has_member (josource, "mode")) json_object_get_string_member (josource, "mode"); // g_debug ("\tParsed path %s with mode %s\n", path, mode); JsonObject *joinputs = json_object_get_object_member (josource, "inputs"); GList *input_list = json_object_get_members (joinputs); for (GList *m = input_list; m != NULL; m = m->next) { const gchar *component = m->data; JsonObject *joinput = json_object_get_object_member (joinputs, component); const gchar *action_name = json_object_get_string_member (joinput, "output"); g_debug ("\t%s: Parsed output %s for component %s", path, action_name, component); _add_input_path_or_binding (self, bindings, (gchar *) action_name, _get_binding_mode (mode), _get_binding_component (component), path); } g_list_free (input_list); } } static void _parse_haptics (GxrManifest *self, JsonArray *jahaptics, GxrBindingManifest *bindings) { guint haptics_len = json_array_get_length (jahaptics); for (guint i = 0; i < haptics_len; i++) { JsonObject *johaptics = json_array_get_object_element (jahaptics, i); const gchar *path = json_object_get_string_member (johaptics, "path"); const gchar *action_name = json_object_get_string_member (johaptics, "output"); g_debug ("\t%s: Parsed haptics %s", path, action_name); _add_input_path_or_binding (self, bindings, (gchar *) action_name, GXR_BINDING_MODE_NONE, GXR_BINDING_COMPONENT_NONE, path); } } static void _parse_poses (GxrManifest *self, JsonArray *japose, GxrBindingManifest *bindings) { guint pose_len = json_array_get_length (japose); for (guint i = 0; i < pose_len; i++) { JsonObject *jopose = json_array_get_object_element (japose, i); const gchar *path = json_object_get_string_member (jopose, "path"); const gchar *action_name = json_object_get_string_member (jopose, "output"); g_debug ("\t%s: Parsed pose %s", path, action_name); _add_input_path_or_binding (self, bindings, (gchar *) action_name, GXR_BINDING_MODE_NONE, GXR_BINDING_COMPONENT_NONE, path); } } static gboolean _parse_bindings (GxrManifest *self, GInputStream *stream, GxrBindingManifest *bindings) { GError *error = NULL; JsonParser *parser = json_parser_new (); json_parser_load_from_stream (parser, stream, NULL, &error); if (error) { g_printerr ("Unable to parse bindings: %s\n", error->message); g_error_free (error); g_object_unref (parser); return FALSE; } JsonNode *jnroot = json_parser_get_root (parser); JsonObject *joroot; joroot = json_node_get_object (jnroot); JsonObject *jobinding = json_object_get_object_member (joroot, "bindings"); if (json_object_has_member (joroot, "interaction_profile")) bindings->interaction_profile = g_strdup (json_object_get_string_member (joroot, "interaction_profile")); GList *binding_list = json_object_get_members (jobinding); for (GList *l = binding_list; l != NULL; l = l->next) { const gchar *actionset = l->data; g_debug ("Parsing Action Set %s", actionset); JsonObject *joactionset = json_object_get_object_member (jobinding, actionset); if (json_object_has_member (joactionset, "sources")) { JsonArray *jasources = json_object_get_array_member (joactionset, "sources"); _parse_sources (self, jasources, bindings); } if (json_object_has_member (joactionset, "haptics")) { JsonArray *jahaptics = json_object_get_array_member (joactionset, "haptics"); _parse_haptics (self, jahaptics, bindings); } if (json_object_has_member (joactionset, "poses")) { JsonArray *japose = json_object_get_array_member (joactionset, "poses"); _parse_poses (self, japose, bindings); } } g_list_free (binding_list); g_object_unref (parser); return TRUE; } gboolean gxr_manifest_load_actions (GxrManifest *self, GInputStream *action_stream) { if (!_parse_actions (self, action_stream)) return FALSE; /* actions manifest parsing and bindings parsing are separate because * for openvr we only need binding filenames from actions manifest. */ return TRUE; } gboolean gxr_manifest_load_bindings (GxrManifest *self, const char *resource_path) { for (GSList *l = self->binding_filenames; l; l = l->next) { gchar *binding_filename = l->data; GError *error = NULL; GString *bindings_res_path = g_string_new (""); g_string_printf (bindings_res_path, "%s/%s", resource_path, binding_filename); g_debug ("Parsing bindings file %s", bindings_res_path->str); GInputStream *bindings_res_input_stream = g_resources_open_stream (bindings_res_path->str, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); if (error) { g_printerr ("skipping %s: %s\n", binding_filename, error->message); g_error_free (error); g_string_free (bindings_res_path, TRUE); if (bindings_res_input_stream) g_object_unref (bindings_res_input_stream); continue; } GxrBindingManifest *bindings = g_malloc (sizeof (GxrBindingManifest)); bindings->gxr_bindings = NULL; bindings->interaction_profile = NULL; bindings->filename = g_strdup (binding_filename); if (!_parse_bindings (self, bindings_res_input_stream, bindings)) { g_free (bindings); g_printerr ("Failed to parse bindings manifest %s\n", bindings_res_path->str); g_string_free (bindings_res_path, TRUE); g_object_unref (bindings_res_input_stream); continue; } self->bindings = g_slist_append (self->bindings, bindings); g_string_free (bindings_res_path, TRUE); g_object_unref (bindings_res_input_stream); } return TRUE; } GSList * gxr_manifest_get_binding_filenames (GxrManifest *self) { return self->binding_filenames; } GSList * gxr_manifest_get_binding_manifests (GxrManifest *self) { return self->bindings; } static void gxr_manifest_finalize (GObject *gobject) { GxrManifest *self = GXR_MANIFEST (gobject); for (GSList *l = self->action_manifest_entries; l; l = l->next) { GxrActionManifestEntry *action = l->data; g_free (action->name); } g_slist_free_full (self->action_manifest_entries, g_free); g_slist_free_full (self->binding_filenames, g_free); for (GSList *l = self->bindings; l; l = l->next) { GxrBindingManifest *binding_manifest = l->data; for (GSList *m = binding_manifest->gxr_bindings; m; m = m->next) { GxrBinding *binding = m->data; for (GSList *n = binding->input_paths; n; n = n->next) { GxrBindingPath *binding_path = n->data; g_free (binding_path->path); } g_slist_free_full (binding->input_paths, g_free); } g_slist_free_full (binding_manifest->gxr_bindings, g_free); g_free (binding_manifest->interaction_profile); g_free (binding_manifest->filename); } g_slist_free_full (self->bindings, g_free); } GxrManifest * gxr_manifest_new (const char *resource_path, const char *manifest_name) { GxrManifest *self = (GxrManifest *) g_object_new (GXR_TYPE_MANIFEST, 0); GError *error = NULL; GString *actions_res_path = g_string_new (""); g_string_printf (actions_res_path, "%s/%s", resource_path, manifest_name); /* stream can not be reset/reused, has to be recreated */ GInputStream *actions_res_input_stream = g_resources_open_stream (actions_res_path->str, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); if (error) { g_print ("Unable to open stream: %s\n", error->message); g_error_free (error); g_object_unref (self); return NULL; } if (!gxr_manifest_load_actions (self, actions_res_input_stream)) { g_printerr ("Failed to load action manifest %s/%s\n", resource_path, manifest_name); g_object_unref (self); return NULL; } if (!gxr_manifest_load_bindings (self, resource_path)) { g_printerr ("Failed to load action binding manifests\n"); g_object_unref (self); return NULL; } g_object_unref (actions_res_input_stream); g_string_free (actions_res_path, TRUE); g_debug ("Loaded action manifest"); return self; } 07070100000062000081A4000000000000000000000001636E648600000B7F000000000000000000000000000000000000001700000000gxr/src/gxr-manifest.h/* * gxr * Copyright 2019 Collabora Ltd. * Author: Christoph Haag <christoph.haag@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_MANIFEST_H_ #define GXR_MANIFEST_H_ #include <gio/gio.h> #include <glib-object.h> /** * GxrBindingType: * @GXR_BINDING_TYPE_UNKNOWN: An unknown binding type. * @GXR_BINDING_TYPE_POSE: A pose binding type. * @GXR_BINDING_TYPE_BOOLEAN: A pose boolean type. * @GXR_BINDING_TYPE_FLOAT: A pose float type. * @GXR_BINDING_TYPE_VEC2: A pose vec2 type. * @GXR_BINDING_TYPE_HAPTIC: A pose haptic type. * * The type of GxrBinding. * **/ typedef enum { GXR_BINDING_TYPE_UNKNOWN, GXR_BINDING_TYPE_POSE, GXR_BINDING_TYPE_BOOLEAN, GXR_BINDING_TYPE_FLOAT, GXR_BINDING_TYPE_VEC2, GXR_BINDING_TYPE_HAPTIC } GxrBindingType; /** * GxrBindingMode: * @GXR_BINDING_MODE_NONE: None. * @GXR_BINDING_MODE_UNKNOWN: Unknown. * @GXR_BINDING_MODE_BUTTON: Button. * @GXR_BINDING_MODE_TRACKPAD: Trackpad. * @GXR_BINDING_MODE_ANALOG_STICK: Thumbstick. * * The mode of the GxrBinding. * **/ typedef enum { GXR_BINDING_MODE_NONE, GXR_BINDING_MODE_UNKNOWN, GXR_BINDING_MODE_BUTTON, GXR_BINDING_MODE_TRACKPAD, GXR_BINDING_MODE_ANALOG_STICK, } GxrBindingMode; /** * GxrBindingComponent: * @GXR_BINDING_COMPONENT_NONE: None. * @GXR_BINDING_COMPONENT_UNKNOWN: Unknown. * @GXR_BINDING_COMPONENT_CLICK: Click. * @GXR_BINDING_COMPONENT_PULL: Pull. * @GXR_BINDING_COMPONENT_POSITION: Position. * @GXR_BINDING_COMPONENT_TOUCH: Touch. * @GXR_BINDING_COMPONENT_FORCE: Force. * * The component of the GxrBindingPath. * **/ typedef enum { GXR_BINDING_COMPONENT_NONE, GXR_BINDING_COMPONENT_UNKNOWN, GXR_BINDING_COMPONENT_CLICK, GXR_BINDING_COMPONENT_PULL, GXR_BINDING_COMPONENT_POSITION, GXR_BINDING_COMPONENT_TOUCH, GXR_BINDING_COMPONENT_FORCE } GxrBindingComponent; typedef struct { gchar *name; GxrBindingType type; } GxrActionManifestEntry; typedef struct { GxrBindingComponent component; gchar *path; GxrBindingMode mode; } GxrBindingPath; typedef struct { GxrActionManifestEntry *action; GSList *input_paths; } GxrBinding; typedef struct { gchar *filename; /* list of GxrBinding */ GSList *gxr_bindings; /* Only set for OpenXR manifest */ gchar *interaction_profile; } GxrBindingManifest; G_BEGIN_DECLS #define GXR_TYPE_MANIFEST gxr_manifest_get_type () G_DECLARE_FINAL_TYPE (GxrManifest, gxr_manifest, GXR, MANIFEST, GObject) GxrManifest * gxr_manifest_new (const char *resource_path, const char *manifest_name); gboolean gxr_manifest_load_actions (GxrManifest *self, GInputStream *action_stream); gboolean gxr_manifest_load_bindings (GxrManifest *self, const char *resource_path); GSList * gxr_manifest_get_binding_filenames (GxrManifest *self); /* GxrBindingManifest */ GSList * gxr_manifest_get_binding_manifests (GxrManifest *manifest); G_END_DECLS #endif /* GXR_MANIFEST_H_ */ 07070100000063000081A4000000000000000000000001636E6486000007C8000000000000000000000000000000000000001900000000gxr/src/gxr-version.h.in/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #ifndef GXR_VERSION_H #define GXR_VERSION_H #if !defined(GXR_INSIDE) && !defined(GXR_COMPILATION) # error "Only <gxr.h> can be included directly." #endif /** * SECTION:gxrversion * @short_description: gxr version checking * * gxr provides macros to check the version of the library * at compile-time */ /** * GXR_MAJOR_VERSION: * * gxr major version component (e.g. 1 if %GXR_VERSION is 1.2.3) */ #define GXR_MAJOR_VERSION (@MAJOR_VERSION@) /** * GXR_MINOR_VERSION: * * gxr minor version component (e.g. 2 if %GXR_VERSION is 1.2.3) */ #define GXR_MINOR_VERSION (@MINOR_VERSION@) /** * GXR_MICRO_VERSION: * * gxr micro version component (e.g. 3 if %GXR_VERSION is 1.2.3) */ #define GXR_MICRO_VERSION (@MICRO_VERSION@) /** * GXR_VERSION * * gxr version. */ #define GXR_VERSION (@VERSION@) /** * GXR_VERSION_S: * * gxr version, encoded as a string, useful for printing and * concatenation. */ #define GXR_VERSION_S "@VERSION@" #define GXR_ENCODE_VERSION(major,minor,micro) \ ((major) << 24 | (minor) << 16 | (micro) << 8) /** * GXR_VERSION_HEX: * * gxr version, encoded as an hexadecimal number, useful for * integer comparisons. */ #define GXR_VERSION_HEX \ (GXR_ENCODE_VERSION (GXR_MAJOR_VERSION, GXR_MINOR_VERSION, GXR_MICRO_VERSION)) /** * GXR_CHECK_VERSION: * @major: required major version * @minor: required minor version * @micro: required micro version * * Compile-time version checking. Evaluates to %TRUE if the version * of gxr is greater than the required one. */ #define GXR_CHECK_VERSION(major,minor,micro) \ (GXR_MAJOR_VERSION > (major) || \ (GXR_MAJOR_VERSION == (major) && GXR_MINOR_VERSION > (minor)) || \ (GXR_MAJOR_VERSION == (major) && GXR_MINOR_VERSION == (minor) && \ GXR_MICRO_VERSION >= (micro))) #endif /* GXR_VERSION_H */ 07070100000064000081A4000000000000000000000001636E6486000001E0000000000000000000000000000000000000000E00000000gxr/src/gxr.h/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #pragma once #ifndef GXR_H #define GXR_H #include <glib.h> #define GXR_INSIDE #include "gxr-action-set.h" #include "gxr-action.h" #include "gxr-context.h" #include "gxr-controller.h" #include "gxr-device-manager.h" #include "gxr-device.h" #include "gxr-io.h" #include "gxr-manifest.h" #include "gxr-version.h" #undef GXR_INSIDE #endif 07070100000065000081A4000000000000000000000001636E648600000A48000000000000000000000000000000000000001400000000gxr/src/meson.buildso_version = 0 api_version = '0.16' api_path = 'gxr-' + api_version gxr_sources = [ 'gxr-context.c', 'gxr-action.c', 'gxr-action-set.c', 'gxr-io.c', 'gxr-manifest.c', 'gxr-controller.c', 'graphene-ext.c', 'gxr-device-manager.c', 'gxr-device.c' ] gxr_headers = [ 'gxr.h', 'gxr-context.h', 'gxr-action.h', 'gxr-action-set.h', 'gxr-io.h', 'gxr-manifest.h', 'gxr-controller.h', 'graphene-ext.h', 'gxr-device-manager.h', 'gxr-device.h' ] version_split = meson.project_version().split('.') MAJOR_VERSION = version_split[0] MINOR_VERSION = version_split[1] MICRO_VERSION = version_split[2] version_conf = configuration_data() version_conf.set('VERSION', meson.project_version()) version_conf.set('MAJOR_VERSION', MAJOR_VERSION) version_conf.set('MINOR_VERSION', MINOR_VERSION) version_conf.set('MICRO_VERSION', MICRO_VERSION) configure_file( input: 'gxr-version.h.in', output: 'gxr-version.h', configuration: version_conf, install_dir: join_paths(get_option('includedir'), api_path) ) gxr_deps = [ gulkan_dep, gdk_dep, m_dep, gmodule_dep, json_glib_dep, openxr_dep ] gxr_inc = include_directories('.') gxr_lib = shared_library(api_path, gxr_sources, version: meson.project_version(), soversion: so_version, dependencies: gxr_deps, include_directories: gxr_inc, install: true, c_args : ['-DGXR_COMPILATION'] ) gxr_dep = declare_dependency( sources: [], link_with: gxr_lib, include_directories: gxr_inc, dependencies: gxr_deps, ) install_headers(gxr_headers, subdir: api_path) pkg = import('pkgconfig') pkg_requires = ['gulkan-' + gulkan_dep_major_minor_ver, 'gdk-3.0'] pkg.generate( description: 'GLib abtration for XR APIs.', libraries: gxr_lib, name: 'gxr', filebase: api_path, version: meson.project_version(), subdirs: api_path, requires: pkg_requires, install_dir: join_paths(get_option('libdir'), 'pkgconfig') ) if meson.version().version_compare('>=0.54') gulkan_girdir = gulkan_dep.get_variable(pkgconfig: 'girdir', internal: 'girdir') else gulkan_girdir = gulkan_dep.get_variable(pkgconfig: 'girdir') endif if get_option('introspection') gxr_gir = gnome.generate_gir( gxr_lib, dependencies: gulkan_dep, sources: gxr_sources + gxr_headers, namespace: 'Gxr', nsversion: api_version, identifier_prefix: 'Gxr', symbol_prefix: 'gxr', export_packages: api_path, include_directories: gulkan_girdir, includes: [ 'cairo-1.0', 'GdkPixbuf-2.0', 'Gdk-3.0', 'Graphene-1.0', 'Gulkan-' + gulkan_dep_major_minor_ver], header: 'gxr.h', install: true, ) endif 07070100000066000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000A00000000gxr/tests07070100000067000081A4000000000000000000000001636E6486000003D6000000000000000000000000000000000000001600000000gxr/tests/meson.buildtest_io = executable( 'test_io', ['test_io.c', test_resources], dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) test('test_io', test_io) test_context = executable( 'test_context', ['test_context.c', test_resources], dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) test('test_context', test_context, suite: ['xr'], is_parallel : false) test_actions = executable( 'test_actions', ['test_actions.c', test_resources], dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) test('test_actions', test_actions, suite: ['xr'], is_parallel : false) test_matrix_decomposition = executable( 'test_matrix_decomposition', 'test_matrix_decomposition.c', dependencies: gxr_deps, link_with: gxr_lib, include_directories: gxr_inc, install: false) test('test_matrix_decomposition', test_matrix_decomposition)07070100000068000081A4000000000000000000000001636E648600000270000000000000000000000000000000000000001900000000gxr/tests/test_actions.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr.h" #include <glib.h> #include <gulkan.h> static void _test_load_action_manifest () { GxrContext *context = gxr_context_new ("Test Actions", 1); g_assert_nonnull (context); GulkanContext *gc = gulkan_context_new (); g_assert_nonnull (gc); GxrManifest *manifest = gxr_manifest_new ("/res/bindings", "actions.json"); g_assert (manifest); g_object_unref (manifest); g_object_unref (context); } int main () { _test_load_action_manifest (); return 0; } 07070100000069000081A4000000000000000000000001636E6486000002B3000000000000000000000000000000000000001900000000gxr/tests/test_context.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include "gxr.h" #include <glib.h> #include <gulkan.h> static void _test_init_context () { GxrContext *context = gxr_context_new ("Test Context", 1); g_assert_nonnull (context); g_object_unref (context); } static void _test_quit_event () { GxrContext *context = gxr_context_new ("Test Context", 1); g_assert_nonnull (context); g_print ("Requesting exit\n"); gxr_context_request_quit (context); g_object_unref (context); g_print ("Exit completed\n"); } int main () { _test_init_context (); _test_quit_event (); return 0; } 0707010000006A000081A4000000000000000000000001636E64860000050B000000000000000000000000000000000000001400000000gxr/tests/test_io.c/* * gxr * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include <glib.h> #include "gxr.h" #define CACHE_DIR "gxr" #define RES_BASE_PATH "/res/bindings" static gboolean _cache_bindings (GString *actions_path) { GString *cache_path = gxr_io_get_cache_path (CACHE_DIR); if (g_mkdir_with_parents (cache_path->str, 0700) == -1) { g_printerr ("Unable to create directory %s\n", cache_path->str); return FALSE; } if (!gxr_io_write_resource_to_file (RES_BASE_PATH, cache_path->str, "actions.json", actions_path)) return FALSE; GString *bindings_path = g_string_new (""); if (!gxr_io_write_resource_to_file (RES_BASE_PATH, cache_path->str, "bindings_valve_index_controller.json", bindings_path)) return FALSE; g_string_free (bindings_path, TRUE); g_string_free (cache_path, TRUE); return TRUE; } int main () { GString *action_manifest_path = g_string_new (""); g_assert (_cache_bindings (action_manifest_path)); g_print ("Created manifest file: %s\n", action_manifest_path->str); g_string_free (action_manifest_path, TRUE); return 0; } 0707010000006B000081A4000000000000000000000001636E648600001442000000000000000000000000000000000000002600000000gxr/tests/test_matrix_decomposition.c/* * xrdesktop * Copyright 2018 Collabora Ltd. * Author: Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * SPDX-License-Identifier: MIT */ #include <glib.h> #include "graphene-ext.h" static void _print_translation (graphene_matrix_t *m) { graphene_vec3_t t; graphene_ext_matrix_get_translation_vec3 (m, &t); float f[3]; graphene_vec3_to_float (&t, f); g_print ("Translation: [%f %f %f]\n", (double) f[0], (double) f[1], (double) f[2]); } static void _print_scale (graphene_matrix_t *m) { graphene_point3d_t s; graphene_ext_matrix_get_scale (m, &s); g_print ("Scale: [%f %f %f]\n", (double) s.x, (double) s.y, (double) s.z); } static void _print_rotation (graphene_matrix_t *m) { float x, y, z; graphene_ext_matrix_get_rotation_angles (m, &x, &y, &z); g_print ("Angles: [%f %f %f]\n", (double) x, (double) y, (double) z); } static void _test_translation_rotation_scale () { graphene_matrix_t mat; graphene_matrix_init_identity (&mat); g_print ("Identity:\n"); graphene_matrix_print (&mat); _print_translation (&mat); _print_scale (&mat); _print_rotation (&mat); graphene_matrix_init_scale (&mat, 1, 2, 3); g_print ("Scale:\n"); graphene_matrix_print (&mat); _print_translation (&mat); _print_scale (&mat); _print_rotation (&mat); graphene_point3d_t point = {.x = 1, .y = 2, .z = 3}; graphene_matrix_init_translate (&mat, &point); g_print ("Translation:\n"); graphene_matrix_print (&mat); _print_translation (&mat); _print_scale (&mat); _print_rotation (&mat); graphene_quaternion_t orientation; graphene_quaternion_init_from_angles (&orientation, 1, 2, 3); graphene_matrix_init_identity (&mat); graphene_matrix_rotate_quaternion (&mat, &orientation); g_print ("Rotation:\n"); graphene_matrix_print (&mat); _print_translation (&mat); _print_scale (&mat); _print_rotation (&mat); g_print ("Rotation quat: "); graphene_ext_quaternion_print (&orientation); graphene_quaternion_t q; graphene_point3d_t unused_scale; graphene_ext_matrix_get_rotation_quaternion (&mat, &unused_scale, &q); g_print ("Result quat: "); graphene_ext_quaternion_print (&q); graphene_quaternion_init_from_angles (&orientation, 1, 2, 3); graphene_matrix_init_identity (&mat); graphene_matrix_init_scale (&mat, 1, 2, 4); graphene_matrix_rotate_quaternion (&mat, &orientation); g_print ("Scaled rotation:\n"); graphene_matrix_print (&mat); _print_translation (&mat); _print_scale (&mat); _print_rotation (&mat); g_print ("Rotation quat: "); graphene_ext_quaternion_print (&orientation); graphene_ext_matrix_get_rotation_quaternion (&mat, &unused_scale, &q); g_print ("Result quat: "); graphene_ext_quaternion_print (&q); } static bool _does_recompose (graphene_matrix_t *m) { graphene_point3d_t pos; graphene_quaternion_t rot; graphene_point3d_t scale; graphene_ext_matrix_decompose (m, &scale, &rot, &pos); graphene_matrix_t recomposed; graphene_matrix_init_scale (&recomposed, scale.x, scale.y, scale.z); graphene_matrix_rotate_quaternion (&recomposed, &rot); graphene_matrix_translate (&recomposed, &pos); graphene_point3d_t scale_r; graphene_quaternion_t rot_r; graphene_point3d_t pos_r; graphene_ext_matrix_decompose (&recomposed, &scale_r, &rot_r, &pos_r); const float epsilon = 0.1f; if (!graphene_ext_point3d_near (&pos, &pos_r, epsilon)) { return false; } if (!graphene_ext_quaternion_near (&rot, &rot_r, epsilon)) { return false; } if (!graphene_ext_point3d_near (&scale, &scale_r, epsilon)) { return false; } return true; } static void _test_recompose () { // clang-format off float m[16] = { +0.727574f, -0.046446f, +0.680384f, +0.000000f, -0.220853f, +0.812468f, +0.241867f, +0.000000f, -0.573449f, -0.324470f, +0.573523f, +0.000000f, +2.769740f, +2.492541f, -2.140265f, +1.000000f, }; float m2[16] = { +0.527033f, +0.218246f, -0.696080f, +0.000000f, -0.163887f, +0.926114f, +0.042852f, +0.000000f, +0.710689f, +0.196215f, +0.518015f, +0.000000f, -4.600538f, -0.035784f, -3.095682f, +1.000000f, }; // Flips one sign in quaternion float m3[16] = { -0.710718f, -0.121415f, +0.692921f, +0.000000f, -0.121415f, -0.949041f, -0.290825f, +0.000000f, +0.692921f, -0.290825f, +0.659759f, +0.000000f, -7.132993f, +4.231433f, -7.415701f, +1.000000f, }; // Flips two signs in quaternion float m4[16] = { +0.883787f, +0.421399f, -0.203328f, +0.000000f, +0.421399f, -0.905734f, -0.045484f, +0.000000f, -0.203328f, -0.045484f, -0.978054f, +0.000000f, +0.495907f, +1.366194f, +0.923377f, +1.000000f, }; // clang-format on graphene_matrix_t mat; graphene_matrix_init_from_float (&mat, m); g_assert (_does_recompose (&mat)); graphene_matrix_init_from_float (&mat, m2); g_assert (_does_recompose (&mat)); graphene_matrix_init_from_float (&mat, m3); g_assert (_does_recompose (&mat)); graphene_matrix_init_from_float (&mat, m4); g_assert (_does_recompose (&mat)); } int main () { _test_translation_rotation_scale (); _test_recompose (); return 0; } 0707010000006C000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000000D00000000gxr/valgrind0707010000006D000081ED000000000000000000000001636E648600000A3C000000000000000000000000000000000000002300000000gxr/valgrind/parse_suppressions.sh#! /usr/bin/awk -f # A script to extract the actual suppression info from the output of (for example) valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all ./minimal # The desired bits are between ^{ and ^} (including the braces themselves). # The combined output should either be appended to /usr/lib/valgrind/default.supp, or placed in a .supp of its own # If the latter, either tell valgrind about it each time with --suppressions=<filename>, or add that line to ~/.valgrindrc # NB This script uses the |& operator, which I believe is gawk-specific. In case of failure, check that you're using gawk rather than some other awk # The script looks for suppressions. When it finds one it stores it temporarily in an array, # and also feeds it line by line to the external app 'md5sum' which generates a unique checksum for it. # The checksum is used as an index in a different array. If an item with that index already exists the suppression must be a duplicate and is discarded. BEGIN { suppression=0; md5sum = "md5sum" } # If the line begins with '{', it's the start of a supression; so set the var and initialise things /^{/ { suppression=1; i=0; next } # If the line begins with '}' its the end of a suppression /^}/ { if (suppression) { suppression=0; close(md5sum, "to") # We've finished sending data to md5sum, so close that part of the pipe ProcessInput() # Do the slightly-complicated stuff in functions delete supparray # We don't want subsequent suppressions to append to it! } } # Otherwise, it's a normal line. If we're inside a supression, store it, and pipe it to md5sum. Otherwise it's cruft, so ignore it { if (suppression) { supparray[++i] = $0 print |& md5sum } } function ProcessInput() { # Pipe the result from md5sum, then close it md5sum |& getline result close(md5sum) # gawk can't cope with enormous ints like $result would be, so stringify it first by prefixing a definite string resultstring = "prefix"result if (! (resultstring in chksum_array) ) { chksum_array[resultstring] = 0; # This checksum hasn't been seen before, so add it to the array OutputSuppression() # and output the contents of the suppression } } function OutputSuppression() { # A suppression is surrounded by '{' and '}'. Its data was stored line by line in the array print "{" for (n=1; n <= i; ++n) { print supparray[n] } print "}" } 0707010000006E000081ED000000000000000000000001636E648600000886000000000000000000000000000000000000001400000000gxr/valgrind/run.sh#!/usr/bin/sh export G_SLICE=always-malloc export G_DEBUG=gc-friendly SUPP_DIR=valgrind/suppressions BIN_NAME=$(basename $1) generate_suppressions() { valgrind \ -v \ --tool=memcheck \ --leak-check=full \ --show-leak-kinds=all \ --num-callers=20 \ --show-reachable=yes \ --error-limit=no \ --track-origins=yes \ --suppressions=$SUPP_DIR/external/GNOME.supp/base.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gdk.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/glib.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gtk3.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gio.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gobject.supp \ --suppressions=$SUPP_DIR/external/glib.supp \ --suppressions=$SUPP_DIR/mesa.supp \ --suppressions=$SUPP_DIR/glib.supp \ --suppressions=$SUPP_DIR/misc.supp \ --suppressions=$SUPP_DIR/openvr.supp \ --log-file=memcheck.log \ --gen-suppressions=all \ $1 cat ./memcheck.log | valgrind/parse_suppressions.sh > $SUPP_DIR/$BIN_NAME.supp } show_memleaks() { valgrind \ -v \ --tool=memcheck \ --leak-check=full \ --show-leak-kinds=all \ --leak-resolution=high \ --num-callers=20 \ --show-reachable=yes \ --error-limit=no \ --track-origins=yes \ --suppressions=$SUPP_DIR/external/GNOME.supp/base.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gdk.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/glib.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gtk3.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gio.supp \ --suppressions=$SUPP_DIR/external/GNOME.supp/gobject.supp \ --suppressions=$SUPP_DIR/external/glib.supp \ --suppressions=$SUPP_DIR/mesa.supp \ --suppressions=$SUPP_DIR/glib.supp \ --suppressions=$SUPP_DIR/misc.supp \ --suppressions=$SUPP_DIR/openvr.supp \ $1 } if [ -z ${1+x} ]; then echo "Usage: $0 <test_name>" exit 1; fi if [ -z ${2+x} ]; then show_memleaks $1 else case $2 in -g) generate_suppressions $1 ;; *) # unknown option echo "Unknown argument $2" ;; esac fi 0707010000006F000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000001A00000000gxr/valgrind/suppressions07070100000070000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000002300000000gxr/valgrind/suppressions/external07070100000071000041ED000000000000000000000002636E648600000000000000000000000000000000000000000000002E00000000gxr/valgrind/suppressions/external/GNOME.supp07070100000072000081A4000000000000000000000001636E648600001288000000000000000000000000000000000000003800000000gxr/valgrind/suppressions/external/GNOME.supp/base.supp { <insert_a_suppression_name_here> Memcheck:Cond fun:__GI___strcasecmp_l } { <insert_a_suppression_name_here> Memcheck:Value8 fun:__GI___strcasecmp_l } { <insert_a_suppression_name_here> Memcheck:Addr8 fun:__strspn_sse42 } { <insert_a_suppression_name_here> Memcheck:Cond fun:__strspn_sse42 } { <insert_a_suppression_name_here> Memcheck:Addr4 fun:FcConfigFileExists } { g_quark_init() in gquark.c Memcheck:Leak match-leak-kinds: reachable ... fun:g_quark_init } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_double } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_double_range } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_int } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_int_range } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_set_seed } { g_get_home_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_home_dir } { g_get_host_name() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_host_name } { g_get_real_name() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_real_name } { g_get_system_config_dirs() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_system_config_dirs } { g_get_system_data_dirs() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_system_data_dirs } { g_get_tmp_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_tmp_dir } { g_get_user_cache_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_cache_dir } { g_get_user_config_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_config_dir } { g_get_user_data_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_data_dir } { g_get_user_name() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_name } { g_get_user_runtime_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_runtime_dir } { g_get_user_special_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_special_dir } { g_hash_table_insert_node() in ghash.c Memcheck:Leak ... fun:g_memdup fun:g_hash_table_insert_node } { g_hash_table_resize() in ghash.c Memcheck:Leak ... fun:g_hash_table_resize } { calloc() within gobject_init_ctor() Memcheck:Leak fun:calloc ... fun:gobject_init_ctor } { malloc() within gobject_init_ctor() Memcheck:Leak fun:malloc ... fun:gobject_init_ctor } { realloc() within gobject_init_ctor() Memcheck:Leak fun:realloc ... fun:gobject_init_ctor } { _g_dbus_initialize() in gio/gdbusprivate.c Memcheck:Leak ... fun:_g_dbus_initialize } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_bus_get fun:g_bus_own_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_hash_table_new fun:g_bus_own_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_main_context_ref_thread_default fun:g_bus_own_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_hash_table_new fun:g_bus_own_name_on_connection } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_variant_new fun:g_bus_unown_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:FcConfigSubstitute* fun:pango_cairo_fc_font_map_fontset_key_substitute } { _gtk_accessibility_init() in gail.c Memcheck:Leak ... fun:atk_add_focus_tracker fun:_gtk_accessibility_init } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gdk_display_get_default } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gdk_display_manager_get } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gdk_display_manager_get_default_display } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_clipboard_get_for_display } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_clipboard_request_contents } { gtk_im_module_initialize() in gtkimmodule.c Memcheck:Leak ... fun:gtk_im_module_initialize } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_widget_get_style_context } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_source_style_scheme_get_style } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_source_style_scheme_manager_get_default } 07070100000073000081A4000000000000000000000001636E648600000132000000000000000000000000000000000000003700000000gxr/valgrind/suppressions/external/GNOME.supp/gdk.supp { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gdk_display_get_default } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gdk_display_manager_get } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gdk_display_manager_get_default_display } 07070100000074000081A4000000000000000000000001636E6486000002A8000000000000000000000000000000000000003700000000gxr/valgrind/suppressions/external/GNOME.supp/gio.supp { _g_dbus_initialize() in gio/gdbusprivate.c Memcheck:Leak ... fun:_g_dbus_initialize } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_bus_get fun:g_bus_own_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_hash_table_new fun:g_bus_own_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_main_context_ref_thread_default fun:g_bus_own_name } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_hash_table_new fun:g_bus_own_name_on_connection } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_variant_new fun:g_bus_unown_name } 07070100000075000081A4000000000000000000000001636E648600000837000000000000000000000000000000000000003800000000gxr/valgrind/suppressions/external/GNOME.supp/glib.supp { g_quark_init() in gquark.c Memcheck:Leak match-leak-kinds: reachable ... fun:g_quark_init } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_double } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_double_range } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_int } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_int_range } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:g_random_set_seed } { g_get_home_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_home_dir } { g_get_host_name() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_host_name } { g_get_real_name() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_real_name } { g_get_system_config_dirs() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_system_config_dirs } { g_get_system_data_dirs() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_system_data_dirs } { g_get_tmp_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_tmp_dir } { g_get_user_cache_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_cache_dir } { g_get_user_config_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_config_dir } { g_get_user_data_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_data_dir } { g_get_user_name() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_name } { g_get_user_runtime_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_runtime_dir } { g_get_user_special_dir() Memcheck:Leak match-leak-kinds: reachable ... fun:g_get_user_special_dir } { g_hash_table_insert_node() in ghash.c Memcheck:Leak ... fun:g_memdup fun:g_hash_table_insert_node } { g_hash_table_resize() in ghash.c Memcheck:Leak ... fun:g_hash_table_resize } 07070100000076000081A4000000000000000000000001636E648600000143000000000000000000000000000000000000003B00000000gxr/valgrind/suppressions/external/GNOME.supp/gobject.supp { calloc() within gobject_init_ctor() Memcheck:Leak fun:calloc ... fun:gobject_init_ctor } { malloc() within gobject_init_ctor() Memcheck:Leak fun:malloc ... fun:gobject_init_ctor } { realloc() within gobject_init_ctor() Memcheck:Leak fun:realloc ... fun:gobject_init_ctor } 07070100000077000081A4000000000000000000000001636E6486000001A0000000000000000000000000000000000000003800000000gxr/valgrind/suppressions/external/GNOME.supp/gtk3.supp { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_clipboard_get_for_display } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_clipboard_request_contents } { gtk_im_module_initialize() in gtkimmodule.c Memcheck:Leak ... fun:gtk_im_module_initialize } { <insert_a_suppression_name_here> Memcheck:Leak ... fun:gtk_widget_get_style_context } 07070100000078000081A4000000000000000000000001636E648600001DE3000000000000000000000000000000000000002D00000000gxr/valgrind/suppressions/external/glib.supp# GLib Valgrind suppressions file # # This provides a list of suppressions for all of GLib (including GIO), for all # Valgrind tools (memcheck, drd, helgrind, etc.) for the false positives and # deliberate one-time leaks which GLib causes to be reported when running under # Valgrind. # # When running an application which links to GLib under Valgrind, you can pass # this suppression file to Valgrind using --suppressions=/path/to/glib-2.0.supp. # # http://valgrind.org/docs/manual/manual-core.html#manual-core.suppress # # Note that there is currently no way for Valgrind to load this automatically # (https://bugs.kde.org/show_bug.cgi?id=160905), so the best GLib can currently # do is to install this file as part of its development package. # # This file should be updated if GLib introduces a new deliberate one-time leak, # or another false race positive in Valgrind: please file bugs at: # # https://gitlab.gnome.org/GNOME/glib/issues/new { gnutls-init-calloc Memcheck:Leak fun:calloc ... fun:gtls_gnutls_init } { gnutls-init-realloc Memcheck:Leak fun:realloc ... fun:gtls_gnutls_init } { g-tls-backend-gnutls-init Memcheck:Leak fun:g_once_impl fun:g_tls_backend_gnutls_init } { p11-tokens-init Memcheck:Leak fun:calloc ... fun:create_tokens_inlock fun:initialize_module_inlock_reentrant } # One-time allocation from libc for getpwnam() results { g-local-vfs-getpwnam Memcheck:Leak fun:malloc ... fun:getpwnam fun:g_local_vfs_parse_name } { gobject-init-malloc Memcheck:Leak fun:malloc ... fun:gobject_init_ctor } { gobject-init-realloc Memcheck:Leak fun:realloc ... fun:gobject_init_ctor } { gobject-init-calloc Memcheck:Leak fun:calloc ... fun:gobject_init_ctor } { g-type-register-dynamic Memcheck:Leak fun:malloc ... fun:g_type_register_dynamic } { g-type-register-static Memcheck:Leak fun:malloc ... fun:g_type_register_static } { g-type-register-static-realloc Memcheck:Leak fun:realloc ... fun:g_type_register_static } { g-type-register-static-calloc Memcheck:Leak fun:calloc ... fun:g_type_register_static } { g-type-add-interface-dynamic Memcheck:Leak fun:malloc ... fun:g_type_add_interface_dynamic } { g-type-add-interface-static Memcheck:Leak fun:malloc ... fun:g_type_add_interface_static } { g-test-rand-init Memcheck:Leak fun:calloc ... fun:g_rand_new_with_seed_array fun:test_run_seed ... fun:g_test_run } { g-test-rand-init2 Memcheck:Leak fun:calloc ... fun:g_rand_new_with_seed_array ... fun:get_global_random ... fun:g_test_init } { g-quark-table-new Memcheck:Leak fun:g_hash_table_new ... fun:quark_new } { g-quark-table-resize Memcheck:Leak fun:g_hash_table_resize ... fun:quark_new } { g-type-interface-init Memcheck:Leak fun:malloc ... fun:type_iface_vtable_base_init_Wm } { g-type-class-init Memcheck:Leak fun:calloc ... fun:type_class_init_Wm } { g-io-module-default-singleton-malloc Memcheck:Leak fun:malloc ... fun:g_type_create_instance ... fun:_g_io_module_get_default } { g-io-module-default-singleton-module Memcheck:Leak fun:calloc ... fun:g_module_open ... fun:_g_io_module_get_default } { g-get-language-names Memcheck:Leak fun:malloc ... fun:g_get_language_names } { g-static-mutex Memcheck:Leak fun:malloc ... fun:g_static_mutex_get_mutex_impl } { g-system-thread-init Memcheck:Leak fun:calloc ... fun:g_system_thread_new } { g-io-module-default-proxy-resolver-gnome Memcheck:Leak fun:calloc ... fun:g_proxy_resolver_gnome_init ... fun:_g_io_module_get_default } # One-time getaddrinfo() configuration loading { g-threaded-resolver-getaddrinfo-config Memcheck:Leak fun:malloc ... fun:__resolv_conf_allocate ... fun:getaddrinfo fun:do_lookup_by_name } # memcheck checks that the third argument to ioctl() is a valid pointer, but # some ioctls use that argument as an integer { ioctl-with-non-pointer-param Memcheck:Param ioctl(generic) fun:ioctl fun:btrfs_reflink_with_progress } { g-private-get drd:ConflictingAccess fun:g_private_get } { g-private-get-helgrind Helgrind:Race fun:g_private_get } { g-private-set drd:ConflictingAccess fun:g_private_set } { g-private-set-helgrind Helgrind:Race fun:g_private_set } { g-type-construct-free drd:ConflictingAccess fun:g_type_free_instance } { g-type-construct-free-helgrind Helgrind:Race fun:g_type_free_instance } { g-variant-unref drd:ConflictingAccess fun:g_variant_unref } { g-variant-unref-helgrind Helgrind:Race fun:g_variant_unref } { g-unix-signals-main drd:ConflictingAccess fun:_g_main_create_unix_signal_watch } { g-unix-signals-dispatch drd:ConflictingAccess ... fun:dispatch_unix_signals* } { g-unix-signals-dispatch-helgrind Helgrind:Race ... fun:dispatch_unix_signals* } { g-unix-signals-other drd:ConflictingAccess fun:g_unix_signal_watch* } { g-unix-signals-other-helgrind Helgrind:Race fun:g_unix_signal_watch* } { g-unix-signals-handler drd:ConflictingAccess fun:g_unix_signal_handler* } { g-unix-signals-handler-helgrind Helgrind:Race fun:g_unix_signal_handler* } { g-unix-signals-worker drd:ConflictingAccess fun:glib_worker_main } { g-unix-signals-worker-helgrind Helgrind:Race fun:glib_worker_main } { g-wakeup-acknowledge drd:ConflictingAccess fun:read fun:g_wakeup_acknowledge } { g-type-fundamental drd:ConflictingAccess fun:g_type_fundamental } { g-type-fundamental-helgrind Helgrind:Race fun:g_type_fundamental } { g-type-class-peek-static drd:ConflictingAccess fun:g_type_class_peek_static } { g-type-class-peek-static-helgrind Helgrind:Race fun:g_type_class_peek_static } { g-type-is-a drd:ConflictingAccess ... fun:g_type_is_a } { g-type-is-a-helgrind Helgrind:Race ... fun:g_type_is_a } { g-inet-address-get-type drd:ConflictingAccess fun:g_inet_address_get_type } { g-inet-address-get-type-helgrind Helgrind:Race fun:g_inet_address_get_type } # From: https://github.com/fredericgermain/valgrind/blob/master/glibc-2.X-drd.supp { drd-libc-stdio drd:ConflictingAccess obj:*/lib*/libc-* } { drd-libc-recv drd:ConflictingAccess fun:recv } { drd-libc-send drd:ConflictingAccess fun:send } # GSources do an opportunistic ref count check { g-source-set-ready-time drd:ConflictingAccess fun:g_source_set_ready_time } { g-source-set-ready-time-helgrind Helgrind:Race fun:g_source_set_ready_time } { g-source-iter-next Helgrind:Race fun:g_source_iter_next fun:g_main_context_* fun:g_main_context_iterate } { g-object-instance-private drd:ConflictingAccess fun:*_get_instance_private } { g-object-instance-private-helgrind Helgrind:Race fun:*_get_instance_private } # GLib legitimately calls pthread_cond_signal without a mutex held { g-task-thread-complete drd:CondErr ... fun:g_cond_signal fun:g_task_thread_complete } { g-task-thread-complete Helgrind:Misc ... fun:g_cond_signal fun:g_task_thread_complete } # False positive, but I can't explain how (FIXME) { g-task-cond Helgrind:Misc ... fun:g_cond_clear fun:g_task_finalize } # Real race, but is_cancelled() is an opportunistic function anyway { g-cancellable-is-cancelled Helgrind:Race fun:g_cancellable_is_cancelled } # False positive { g-main-context-cond Helgrind:Misc ... fun:g_cond_clear fun:g_main_context_unref } # False positives { g-source-unlocked Helgrind:Race fun:g_source_*_unlocked } { g-source-internal Helgrind:Race fun:g_source_*_internal } # False positive { g_object_real_dispose Helgrind:Race fun:g_object_real_dispose } # False positive { g_object_new_valist Helgrind:Race ... fun:g_object_new_valist } 07070100000079000081A4000000000000000000000001636E648600000901000000000000000000000000000000000000002400000000gxr/valgrind/suppressions/glib.supp{ g_type_register_fundamental Memcheck:Leak ... fun:g_type_register_fundamental } { g_value_register_transform_func Memcheck:Leak ... fun:g_value_register_transform_func } { g_object_new_with_properties Memcheck:Leak ... fun:g_object_new_with_properties } { libglib / ld reachable Memcheck:Leak ... obj:/usr/lib/libglib-2.0.so.* fun:call_init.part.0 fun:_dl_init obj:/usr/lib/ld-*.so } { g_hash_table_new_full Memcheck:Leak ... fun:g_hash_table_new_full } { g_param_type_register_static Memcheck:Leak ... fun:g_param_type_register_static } { libgobject Memcheck:Leak ... fun:calloc fun:g_malloc0 obj:/usr/lib/libgobject-2.0.so.* ... fun:call_init.part.0 fun:_dl_init obj:/usr/lib/ld-2.28.so } { g_rw_lock_writer_lock Memcheck:Leak ... fun:g_rw_lock_writer_lock } { g_intern_static_string Memcheck:Leak ... fun:g_intern_static_string } { g_print Memcheck:Leak ... fun:g_print } { gdk_pixbuf_new_from_resource Memcheck:Leak ... fun:gdk_pixbuf_new_from_resource } { gdk_pixbuf_loader_write Memcheck:Leak match-leak-kinds: reachable ... fun:gdk_pixbuf_loader_write } { g_main_loop_run malloc Memcheck:Leak match-leak-kinds: reachable fun:malloc ... fun:g_main_loop_run } { g_main_loop_new Memcheck:Leak ... fun:g_main_loop_new } { g_timeout_add_full Memcheck:Leak match-leak-kinds: reachable fun:malloc fun:g_malloc fun:g_memdup obj:/usr/lib/libglib-2.0.so.* obj:/usr/lib/libglib-2.0.so.* obj:/usr/lib/libglib-2.0.so.* fun:g_source_attach fun:g_timeout_add_full } { g_resources_lookup_data Memcheck:Leak match-leak-kinds: reachable fun:malloc obj:/usr/lib/libglib-2.0.so.* fun:g_rec_mutex_lock fun:g_variant_type_info_get fun:g_variant_serialised_get_child fun:g_variant_get_child_value obj:/usr/lib/libgio-2.0.so.* obj:/usr/lib/libgio-2.0.so.* fun:g_resource_lookup_data fun:g_resources_lookup_data } { libgobject _dl_init Memcheck:Leak match-leak-kinds: reachable fun:malloc ... obj:/usr/lib/libgobject-2.0.so.* obj:/usr/lib/libgobject-2.0.so.* fun:call_init.part.0 fun:_dl_init obj:/usr/lib/ld-*.so }0707010000007A000081A4000000000000000000000001636E6486000001CB000000000000000000000000000000000000002400000000gxr/valgrind/suppressions/mesa.supp{ mesa: vkEnumerateInstanceExtensionProperties Memcheck:Leak match-leak-kinds: reachable ... fun:vkEnumerateInstanceExtensionProperties } { vkDestroyInstance Memcheck:Leak ... fun:vkDestroyInstance } { vkCreateInstance Memcheck:Leak ... obj:/usr/lib/libvulkan.so.* fun:vkCreateInstance } { vkEnumeratePhysicalDevices Memcheck:Cond ... obj:/usr/lib/libvulkan.so.* ... fun:vkEnumeratePhysicalDevices }0707010000007B000081A4000000000000000000000001636E648600000180000000000000000000000000000000000000002400000000gxr/valgrind/suppressions/misc.supp{ libpixman Memcheck:Leak match-leak-kinds: reachable fun:calloc ... obj:/usr/lib/libpixman-1.so.* ... } { glfwInit Memcheck:Leak match-leak-kinds: reachable ... fun:glfwInit } { XcursorImageLoadCursor Memcheck:Leak match-leak-kinds: reachable ... fun:_XcursorGetDisplayInfo fun:XcursorSupportsARGB fun:XcursorImageLoadCursor }0707010000007C000081A4000000000000000000000001636E648600000095000000000000000000000000000000000000002600000000gxr/valgrind/suppressions/openvr.supp{ VR_InitInternal2 Memcheck:Leak ... fun:VR_InitInternal2 } { _dl_catch_exception Memcheck:Leak ... fun:_dl_catch_exception }07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!769 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor