diff --git a/.github/ISSUE_TEMPLATE/1-bug.yaml b/.github/ISSUE_TEMPLATE/1-bug.yaml index 2e2c65485..a75003f63 100644 --- a/.github/ISSUE_TEMPLATE/1-bug.yaml +++ b/.github/ISSUE_TEMPLATE/1-bug.yaml @@ -31,7 +31,7 @@ body: - '1.10' - '1.11' - master branch - default: 2 + default: 3 validations: required: true - type: textarea diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 6290ff525..760041b6a 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -16,7 +16,8 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 56d663921..9b7c5c965 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -87,7 +87,8 @@ jobs: git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -118,7 +119,8 @@ jobs: git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7c28121fd..fdc8e80c9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,8 @@ jobs: git lfs pull - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Build run: | ./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8 diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 7d7213a8d..7d4c71666 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7edc1b9d2c7fbd32fcf778814deb719c71781f657da050ac0c7c78984aeb360d +oid sha256:b73d774c71bd7b46c9c4198a4c957055e6447e31d8252813b272db92301475e7 size 29533 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index 5969c90fa..2d732c086 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac6023e5d6525c3b7c385a380ed9d6fc71ec9d683c587391d14c9daf6653e31a +oid sha256:c4ec07a3b7e0a2dfd4332598a982c3192c0c357c6bcd128d7a7797fb483780e7 size 31445 diff --git a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax index fc45d33cc..d082bd8e7 100644 --- a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax +++ b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecd573c40f534f293d4827b1a8150d439d4f5e7729552474926208c5814f3d3e +oid sha256:2830919bea988e1f8bd8299ceac34b8a3695418e2f22ca670f2fec3b3d6d1a2f size 41149 diff --git a/Content/Editor/DebugMaterials/SingleColor/Decal.flax b/Content/Editor/DebugMaterials/SingleColor/Decal.flax index 05e99be76..b94f22bc8 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Decal.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Decal.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0b2ad25738c2bc55bb3e76fc94fc81992b1d65b8b3091b132c75b2ed064c517 -size 10398 +oid sha256:588c29a4b239c32d4b125052e4054a29cf5140562e90ca6fac4d2952e03f66c7 +size 10397 diff --git a/Content/Editor/DebugMaterials/SingleColor/Particle.flax b/Content/Editor/DebugMaterials/SingleColor/Particle.flax index 7a328e7a0..de2043874 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Particle.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Particle.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02ddea5bcb3fccb697081e47cc26a0b546b23b89ceca299e702a1d431775dfd6 +oid sha256:b39cd76254f341c93e83625475b6e7896ef34f1d6d650da52e649bc055d0d03e size 33503 diff --git a/Content/Editor/DebugMaterials/SingleColor/Surface.flax b/Content/Editor/DebugMaterials/SingleColor/Surface.flax index 84e05ee36..7ae8a69c3 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Surface.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Surface.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:741a7619b5aebc6c7c6a573a0407e8b7aa42d1b50d0ed5cf6a21026932807d0e +oid sha256:5861e912cf822c9478f824390f6258d84821b7289e3e993a7dee38b77c5a2f80 size 29398 diff --git a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax index ab4591176..fdcb880df 100644 --- a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax +++ b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:358370943d21a97f8b45ff2181b7c6c2d7a6297e3f166ae7a77363aadf89b152 +oid sha256:b9ed2869a2a754423e0b8c456eed621bd06bdb50cacf7a972a7f024e40a1ea6a size 32954 diff --git a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax index 54151179a..ad27a422c 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:486b4db3e1d825d026753d944a04defe4d72eb73eb03a438944c366f19de824e +oid sha256:05c27ac416ef922ee247adc12a115fd522eb3a1d8873e1056914cd96893a3097 size 21096 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 8d48c5827..d84425aab 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebdfc478caabc84a3a75384a64d262d2d509bbac3540eea462e45911719c288f +oid sha256:8e3d4ca149e143fee31e2d038b8efec526ca995dbe13258fbb68c89cd43ecbf7 size 29627 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index 79385ada6..eb7e784c9 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa4f1a733150c62064cac60c07980df7c84bb6163dc9507782aa98df07f48874 +oid sha256:7af1150d6e7cb6ecce5cd039f0edc92967c986a13903a201d6dc15ed0751dc57 size 39637 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index ace3bde90..bbb114662 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26e1832496c01cb31bd4dc9000d3cd326ea4fd54de02910d3801d2641bff685c +oid sha256:d575ca1b202c84b8268687b391be5fc8d55497ffa23fb3cd4287fa667de654ab size 34240 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 7ea0a596f..fb4b8acca 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca8bc1ac9d45534d3efd3b4308d7492fa016726b4ec744be26619069ce911b73 +oid sha256:26f2d88aab9c0cad36ae527b038a36b69755ff3a5a920e8c4563dd5e1ed8ec65 size 32689 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 0c1461b72..b5d224d58 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09f7dff17af9cd055352e0da534f3466c8efa235c40faf5e56da92c788342f6a -size 17394 +oid sha256:5bb75934622d9251a8a9e72cfe4905091770798ffed22de680a70f98434d0ed7 +size 16241 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index bd4935d96..5a5262e2b 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bc0005c64c561a430a17e4707abc000e06498af968890e2c4e223dc07f07c12 +oid sha256:a1afa76c3f9400da065c150a6a58adc904c3596f650e04dfd87b5e1c1b34695e size 30655 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index ccecb98aa..9d09ea792 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95d172cd12bb3c818fbccf737e78ab282bc8d0880aa8f45af0562850b0eabe4b -size 31616 +oid sha256:1290ae85e4fe41f9d8c1919b33e165287f79377aeddc68f9117c1795ca341003 +size 31267 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index b24941463..2ccbce8c9 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ca4baa1419080395dcf2b5757676406288f112754bc3cd2f27610b58d199622 +oid sha256:340cc500a160344b43b21ed8c4c22b6d776f406581f606ced62a3e92c5bef18a size 31300 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index 99bc2662c..b3a382132 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3b4c61b04d372d2430a7c08dec612af6caa0e57b1cb47ea44d171d729d3f8f8 +oid sha256:d444cd33ec8d2e1e0e6651c3979260f05c06c8bac33ce2441d6974ae4fa178e4 size 20443 diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 19eb7a3c2..91b06b2fb 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f3b8a7c48c55e33a41f9fe4dbf9b3109b0e734ff154d6cbd3e4101013b01649 +oid sha256:906443c7db821361b32780c17735bc9477ea96c8979dee371a4899635246af48 size 31708 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index 527d19842..e6396c194 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2275282d4e3b5e012a0bbc93fca0d6ffdad89e5a5f0c289678f70748f2efab56 -size 40655 +oid sha256:16db9c1a18b64aea2dcdf3e74f9a44c652bf8bd9b33a5bfda39555d8c002a358 +size 39774 diff --git a/Content/Editor/Primitives/Cube.flax b/Content/Editor/Primitives/Cube.flax index 6328a7bbc..d082eaa5b 100644 --- a/Content/Editor/Primitives/Cube.flax +++ b/Content/Editor/Primitives/Cube.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5a18bf58e0b93c8bba9459fd77a92898f0fae373b58d3acbcb9e36f66c89cd7 -size 23537 +oid sha256:6a56dc14746606f0065d136ad0a69ae1aa41e8732ea380c657d75c187aa09f54 +size 5031 diff --git a/Content/Editor/SpriteMaterial.flax b/Content/Editor/SpriteMaterial.flax index d967a4ea4..2a05418b2 100644 --- a/Content/Editor/SpriteMaterial.flax +++ b/Content/Editor/SpriteMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f5e82be7efa6489cfdfd1babeb1fbb90507aaff7c04eb5f64a4971adf0a2164 +oid sha256:56254b02ffc937d61e8e8fa6492d4805e944ca639c7fcfc0f751b4ac2442365d size 30734 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index 6ddc5f3e9..f481be389 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c7fde7be7d6f9876f9c0db02632c098ab95ade7de57c583d2e495c8ae8665bd +oid sha256:16eefa75a2ae99bba658c4e9b8e8741187b90e577193f76394872764fff2ca0b size 28232 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index c573eb3ee..579db477c 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9369a554ea1776154f5e39d4aaed044f928d98f1f5955b7590b0972015b07438 +oid sha256:e25a3c9e130e51b28dfe5ce43678f52c277c0def83142a2853c4c8ca84dbf417 size 21179 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index 2c91f9d8f..d75e19d5e 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d61f178e72e4d983a919b76368e03c66995ecf50935f6f55b660e34f58755a2 +oid sha256:79de09ba0616eb6066171c2b80cdb6c4235cb52be4836d23162bb9c2585760a0 size 11058 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index 308a6230a..b1f87a7d0 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7a42b1bc5a34f9c47d1aeb773ef26ce470b2d88c2b092828f0fcb439583ef27 -size 31616 +oid sha256:02d4c767fb59c67fef16ccc081f6f371bad329a5333047f9f79fd3d50b911f93 +size 31753 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index a397d1ad8..1244ae3ec 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be21bb7eecd9c774196dbaa89d1b049b108fc0929d648795056c977fe00364ab -size 19582 +oid sha256:d1f556b230cea8e83d00bd4357d34a77e5e468389a5f3bb615e30f6a3ce3ace4 +size 19734 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index eddcbace8..bd57e7d44 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a8a4ad5e763704263b94a7a7e0cc30ab7b1cd1abcb5ccae2d4c6062a65920df -size 31928 +oid sha256:c4ec872b3433d58f8aed640c6efee3d911f226740b4844cb07ed0bf94c00ea18 +size 32080 diff --git a/Content/Engine/DefaultRadialMenu.flax b/Content/Engine/DefaultRadialMenu.flax index 60e2ba5f9..5fba9092e 100644 --- a/Content/Engine/DefaultRadialMenu.flax +++ b/Content/Engine/DefaultRadialMenu.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4151a58e5314937efcd3bdcb9fe0bdd5047b8705931e45e0a4e71a4470e16a0 +oid sha256:0da99403c069966d05daea7fc11d32f20f88bac0463fbc08724840e249ee3bd2 size 21700 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index b302ade35..4147fe0e4 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5cf6924809b9bd7ad3c09722a93f327a0d111676060d136df9c14ab34e8475b -size 23930 +oid sha256:bdfa3b4842a5734d2cd8110af03599b4a5280b33a72b2ba435cd19487cebcde6 +size 24082 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index d6d179150..6d556af2b 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:750f69ce59ef020d2e2186ed6c4bf7aac67ecb1692287e358eaed969fc36381a +oid sha256:6ff8f127d46e68e3423339a352f623c079f2c5d93512c5e9b25841edc7cd0f05 size 29615 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index cc369ceee..b51c5bce7 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3eecc9556af6c2a79d39a7e1c52e4019bdccfb43b074eaddd18600a5854dbffe +oid sha256:14c9833ed19302ea7c6e730fff63f1b72dbac71dc2b49c1d62edb61ccaa68b6f size 31974 diff --git a/Content/Shaders/ColorGrading.flax b/Content/Shaders/ColorGrading.flax index 15f723620..716e18593 100644 --- a/Content/Shaders/ColorGrading.flax +++ b/Content/Shaders/ColorGrading.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad1d9597dd930f0b63358dbe8326d131661f9cc2251181eeec0cb44ffc530d7a -size 12311 +oid sha256:bce57f0ccf6d808985f4d79cc4e15d85cae999bee598d8e93ff5b1b126bc42b8 +size 12310 diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 6739b2436..257953bf9 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5577ef4ce821b08a38afe17b9e5d11cb0b409eb05dd89b2ca76ea95d88085dc0 -size 32893 +oid sha256:5b017cf857f443553020e4bc7c8c8c5da3a826a2514322664a023ffa6005f7a5 +size 38217 diff --git a/Content/Shaders/GI/GlobalSurfaceAtlas.flax b/Content/Shaders/GI/GlobalSurfaceAtlas.flax index c0ee573e0..57990c249 100644 --- a/Content/Shaders/GI/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GI/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64a53e850ac662cb98ce91a8122dece7a43562f55a85c37f830f44324e4d16c5 -size 12925 +oid sha256:615dff65b01507be6c4de722e126324aba20fc197f8e12dafaa94a05e46cba6e +size 13222 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 2cdf124ca..5afcb4bf4 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e593e15c500b14f6e49d5e4ca6156135fd84d9cc1072a4597ee8bcea77d9339e -size 13255 +oid sha256:1f07ebb16820897e8598ae7a0627cb75b3d28e9dceea3ad4bd9ff543d5cdd01c +size 13979 diff --git a/Content/Shaders/MotionBlur.flax b/Content/Shaders/MotionBlur.flax index b713e814b..8a8bb4e4a 100644 --- a/Content/Shaders/MotionBlur.flax +++ b/Content/Shaders/MotionBlur.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af63c3e954919a968e21f19313af1a704c2afa42c5a02744200e1dbf132dc88f -size 9418 +oid sha256:0e8fb3a9a966969ab731455c1d50b280cba0703163cffca93a51fd504262e77e +size 9497 diff --git a/Content/Shaders/Reflections.flax b/Content/Shaders/Reflections.flax index a4605e048..d5ac73593 100644 --- a/Content/Shaders/Reflections.flax +++ b/Content/Shaders/Reflections.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5c99a81c70ccb7e9b2e1e4bc5f271c2fe7b36630489fbb269adfaa34de2cc80 -size 3183 +oid sha256:28c80074ac502d5a931b91089fea7bc0c9d573e0bdd317623577833ff328aa8a +size 3107 diff --git a/Content/Shaders/SDF.flax b/Content/Shaders/SDF.flax index 83009c326..709cc20f2 100644 --- a/Content/Shaders/SDF.flax +++ b/Content/Shaders/SDF.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1abacca0d63b575f0c88d54839f30ce4ec1bb8912478cb81ada8219d417001f9 -size 7891 +oid sha256:d3922811f0eb56cbb515c93cd53d80316740ea78219aa81118d2c9dee4a9d230 +size 4142 diff --git a/Content/Shaders/SSR.flax b/Content/Shaders/SSR.flax index 79085c297..1d5932820 100644 --- a/Content/Shaders/SSR.flax +++ b/Content/Shaders/SSR.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9602d25ef9300636aa23112b3ba3a4f45bf1c4c99b1484a86074da5a787abfc -size 11085 +oid sha256:e1a51391e285346f0a67a75ab03b8262cae02d02dddf0d625a6814a51a3b7394 +size 11007 diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 6f8776336..6dfecbb26 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a3e331b7d688d4d3a11da6bb50d4608dbc6437978433a83cc79a365f520bc58 -size 13206 +oid sha256:872ac3560279bfd0aeb989ebac1b49750dd142b985bc40058888dfd2b63fe9b2 +size 13214 diff --git a/Flax.flaxproj b/Flax.flaxproj index 284bc0652..100d4e9ff 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6801 + "Build": 6805 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", diff --git a/README.md b/README.md index 8e9de0424..f4cc82ee2 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source. * Fedora: `sudo dnf install dotnet-sdk-8.0` * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) - * Ubuntu: `sudo apt install vulkan-sdk` + * Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs) * Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers` * Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers` * Install Git with LFS @@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source. * Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev` * Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel` * Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib` -* Install Clang compiler (version 6 or later): +* Install Clang compiler (version 14 or later): * Ubuntu: `sudo apt-get install clang lldb lld` * Fedora: `sudo dnf install clang llvm lldb lld` * Arch: `sudo pacman -S clang lldb lld` diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 8eea041ce..5b2f505d7 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -281,6 +281,13 @@ namespace FlaxEditor.Content private void CacheData() { + if (!_asset) + { + _parameters = Utils.GetEmptyArray(); + _methods = Utils.GetEmptyArray(); + _attributes = Utils.GetEmptyArray(); + return; + } if (_parameters != null) return; if (_asset.WaitForLoaded()) @@ -344,13 +351,13 @@ namespace FlaxEditor.Content } /// - public string Name => Path.GetFileNameWithoutExtension(_asset.Path); + public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null; /// public string Namespace => string.Empty; /// - public string TypeName => JsonSerializer.GetStringID(_asset.ID); + public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null; /// public bool IsPublic => true; diff --git a/Source/Editor/Content/Proxy/AssetProxy.cs b/Source/Editor/Content/Proxy/AssetProxy.cs index b96b90383..06de5369a 100644 --- a/Source/Editor/Content/Proxy/AssetProxy.cs +++ b/Source/Editor/Content/Proxy/AssetProxy.cs @@ -130,6 +130,11 @@ namespace FlaxEditor.Content eyeAdaptation.Mode = EyeAdaptationMode.None; eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode; preview.PostFxVolume.EyeAdaptation = eyeAdaptation; + + var antiAliasing = preview.PostFxVolume.AntiAliasing; + antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing; + antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode; + preview.PostFxVolume.AntiAliasing = antiAliasing; } } } diff --git a/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp b/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp index f9a8f1b82..256a3478d 100644 --- a/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp @@ -15,26 +15,32 @@ #include "Editor/ProjectInfo.h" #include "Editor/Utilities/EditorUtilities.h" -GDKPlatformTools::GDKPlatformTools() +String GetGDK() { - // Find GDK - Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath); - if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath)) + String gdk; + Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk); + if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk)) { - _gdkPath.Clear(); - Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath); - if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath)) + gdk.Clear(); + Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk); + if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk)) { - _gdkPath.Clear(); + gdk.Clear(); } else { - if (_gdkPath.EndsWith(TEXT("GRDK\\"))) - _gdkPath.Remove(_gdkPath.Length() - 6); - else if (_gdkPath.EndsWith(TEXT("GRDK"))) - _gdkPath.Remove(_gdkPath.Length() - 5); + if (gdk.EndsWith(TEXT("GRDK\\"))) + gdk.Remove(gdk.Length() - 6); + else if (gdk.EndsWith(TEXT("GRDK"))) + gdk.Remove(gdk.Length() - 5); } } + return gdk; +} + +GDKPlatformTools::GDKPlatformTools() +{ + _gdkPath = GetGDK(); } DotNetAOTModes GDKPlatformTools::UseAOT() const @@ -121,7 +127,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla validName.Add('\0'); sb.Append(TEXT("\n")); - sb.Append(TEXT("\n")); + sb.Append(TEXT("\n")); sb.AppendFormat(TEXT(" \n"), validName.Get(), platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName, diff --git a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp index c0a8e452a..1fdb1e7c0 100644 --- a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp +++ b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp @@ -10,9 +10,10 @@ #include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonWriters.h" #include "Editor/Cooker/PlatformTools.h" +#include "Engine/Engine/Globals.h" #include "Editor/Editor.h" #include "Editor/ProjectInfo.h" -#include "Engine/Engine/Globals.h" +#include "Editor/Utilities/EditorUtilities.h" #if PLATFORM_MAC #include #endif @@ -127,7 +128,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c const String dst = dstPath / StringUtils::GetFileName(file); if (dst == file) continue; - if (FileSystem::CopyFile(dst, file)) + if (EditorUtilities::CopyFileIfNewer(dst, file)) { data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst)); return true; diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 030c31c41..c95306505 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -526,6 +526,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass #if PLATFORM_TOOLS_XBOX_SCARLETT case BuildPlatform::XboxScarlett: { + options.Platform = PlatformType::XboxScarlett; const char* platformDefineName = "PLATFORM_XBOX_SCARLETT"; COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE); break; @@ -1367,7 +1368,10 @@ bool CookAssetsStep::Perform(CookingData& data) { typeName = e.TypeName; } - LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize)); + if (e.Count == 1) + LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize)); + else + LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize)); } LOG(Info, ""); } diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index b5b24c251..775b040de 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -265,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data) } if (version.IsEmpty()) { - data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer)); + data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName)); return true; } } diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp index 472a3cca6..626b532a6 100644 --- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp +++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp @@ -59,6 +59,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data) data.StepProgress(infoMsg, 0); // Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit) + // TODO: remove it since EngineModule does properly reference AOT lib now EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll")); FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml")); FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb")); diff --git a/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs b/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs index 8f2173a4e..7649da514 100644 --- a/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -11,7 +12,7 @@ namespace FlaxEditor.CustomEditors.Dedicated [CustomEditor(typeof(EnvironmentProbe)), DefaultEditor] public class EnvironmentProbeEditor : ActorEditor { - private FlaxEngine.GUI.Button _bake; + private Button _bake; /// public override void Initialize(LayoutElementsContainer layout) @@ -20,8 +21,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Values.HasDifferentTypes == false) { - layout.Space(10); - _bake = layout.Button("Bake").Button; + var group = layout.Group("Bake"); + group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2); + _bake = group.Button("Bake").Button; _bake.Clicked += BakeButtonClicked; } } diff --git a/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs b/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs index 53dade508..653478ecd 100644 --- a/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs @@ -1,7 +1,6 @@ // Copyright (c) Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Reflection.Emit; using FlaxEditor.CustomEditors.GUI; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 356ae5ee4..954599347 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -914,9 +914,11 @@ namespace FlaxEditor.CustomEditors.Dedicated // Remove drop down arrows and containment lines if no objects in the group if (group.Children.Count == 0) { + group.Panel.Close(); group.Panel.ArrowImageOpened = null; group.Panel.ArrowImageClosed = null; group.Panel.EnableContainmentLines = false; + group.Panel.CanOpenClose = false; } // Scripts arrange bar diff --git a/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs b/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs index 0a38e0dfe..ee3f2a504 100644 --- a/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -19,8 +20,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Values.HasDifferentTypes == false) { // Add 'Bake' button - layout.Space(10); - var button = layout.Button("Bake"); + var group = layout.Group("Bake"); + group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2); + var button = group.Button("Bake"); button.Button.Clicked += BakeButtonClicked; } } diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 4fae716f1..b407d9a3c 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -123,6 +123,8 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); + if (Picker == null) + return; var differentValues = HasDifferentValues; Picker.DifferentValues = differentValues; if (!differentValues) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index b977dab63..b3fff5644 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Copy", linkedEditor.Copy); var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index)); - b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize; + b.Enabled = !Editor._readOnly && Editor._canResize; b = menu.AddButton("Paste", linkedEditor.Paste); b.Enabled = linkedEditor.CanPaste && !Editor._readOnly; @@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Copy", linkedEditor.Copy); var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index)); - b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize; + b.Enabled = !Editor._readOnly && Editor._canResize; var paste = menu.AddButton("Paste", linkedEditor.Paste); paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly; @@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors panel.Panel.Size = new Float2(0, 18); panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0); - var removeButton = panel.Button("-", "Remove the last item"); + var removeButton = panel.Button("-", "Remove the last item."); removeButton.Button.Size = new Float2(16, 16); removeButton.Button.Enabled = size > _minCount; removeButton.Button.AnchorPreset = AnchorPresets.TopRight; @@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors Resize(Count - 1); }; - var addButton = panel.Button("+", "Add a new item"); + var addButton = panel.Button("+", "Add a new item."); addButton.Button.Size = new Float2(16, 16); addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount; addButton.Button.AnchorPreset = AnchorPresets.TopRight; diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 1ed5d6cf8..462902181 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors public event Action TypePickerValueChanged; /// - /// The custom callback for types validation. Cane be used to implement a rule for types to pick. + /// The custom callback for types validation. Can be used to implement a rule for types to pick. /// public Func CheckValid; @@ -353,7 +353,13 @@ namespace FlaxEditor.CustomEditors.Editors } if (!string.IsNullOrEmpty(typeReference.CheckMethod)) { - var parentType = ParentEditor.Values[0].GetType(); + var parentEditor = ParentEditor; + // Find actual parent editor if parent editor is collection editor + while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor))) + parentEditor = parentEditor.ParentEditor; + + var parentType = parentEditor.Values[0].GetType(); + var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 99b7f1522..58739917e 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -268,8 +268,8 @@ bool Editor::CheckProjectUpgrade() // Check if last version was older else if (lastVersion.Major < FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor < FLAXENGINE_VERSION_MINOR)) { - LOG(Warning, "The project was opened with the older editor version last time"); - const auto result = MessageBox::Show(TEXT("The project was opened with the older editor version last time. Loading it may modify existing data so older editor version won't open it. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question); + LOG(Warning, "The project was last opened with an older editor version"); + const auto result = MessageBox::Show(TEXT("The project was last opened with an older editor version.\nLoading it may modify existing data, which can result in older editor versions being unable to open it.\n\nDo you want to perform a backup before or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question); if (result == DialogResult::Yes) { if (BackupProject()) @@ -291,8 +291,8 @@ bool Editor::CheckProjectUpgrade() // Check if last version was newer else if (lastVersion.Major > FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor > FLAXENGINE_VERSION_MINOR)) { - LOG(Warning, "The project was opened with the newer editor version last time"); - const auto result = MessageBox::Show(TEXT("The project was opened with the newer editor version last time. Loading it may fail and corrupt existing data. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning); + LOG(Warning, "The project was last opened with a newer editor version"); + const auto result = MessageBox::Show(TEXT("The project was last opened with a newer editor version.\nLoading it may fail and corrupt existing data.\n\nDo you want to perform a backup before loading or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning); if (result == DialogResult::Yes) { if (BackupProject()) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 85e579ec9..58466e35d 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1390,6 +1390,7 @@ namespace FlaxEditor public void BuildAllMeshesSDF() { var models = new List(); + var forceRebuild = Input.GetKey(KeyboardKeys.F); Scene.ExecuteOnGraph(node => { if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel) @@ -1399,7 +1400,7 @@ namespace FlaxEditor model != null && !models.Contains(model) && !model.IsVirtual && - model.SDF.Texture == null) + (forceRebuild || model.SDF.Texture == null)) { models.Add(model); } @@ -1412,7 +1413,17 @@ namespace FlaxEditor { var model = models[i]; Log($"[{i}/{models.Count}] Generating SDF for {model}"); - if (!model.GenerateSDF()) + float resolutionScale = 1.0f, backfacesThreshold = 0.6f; + int lodIndex = 6; + bool useGPU = true; + var sdf = model.SDF; + if (sdf.Texture != null) + { + // Preserve options set on this model + resolutionScale = sdf.ResolutionScale; + lodIndex = sdf.LOD; + } + if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU)) model.Save(); } }); @@ -1587,7 +1598,7 @@ namespace FlaxEditor if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null) result = dockedTo.SelectedTab.Size; else - result = gameWin.Viewport.Size; + result = gameWin.Viewport.ContentSize; result *= root.DpiScale; result = Float2.Round(result); diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index e627ec2a2..8af955a46 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -129,11 +129,39 @@ namespace FlaxEditor.GUI.Input { base.Draw(); - var style = Style.Current; - var r = new Rectangle(0, 0, Width, Height); + bool isTransparent = _value.A < 1; - Render2D.FillRectangle(r, _value); - Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black); + var style = Style.Current; + var fullRect = new Rectangle(0, 0, Width, Height); + var colorRect = new Rectangle(0, 0, isTransparent ? Width * 0.7f : Width, Height); + + if (isTransparent) + { + var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height); + + // Draw checkerboard pattern to part of the color value box + Render2D.FillRectangle(alphaRect, Color.White); + var smallRectSize = 7.9f; + var numHor = Mathf.CeilToInt(alphaRect.Width / smallRectSize); + var numVer = Mathf.CeilToInt(alphaRect.Height / smallRectSize); + for (int i = 0; i < numHor; i++) + { + for (int j = 0; j < numVer; j++) + { + if ((i + j) % 2 == 0) + { + var rect = new Rectangle(alphaRect.X + smallRectSize * i, alphaRect.Y + smallRectSize * j, new Float2(smallRectSize)); + Render2D.PushClip(alphaRect); + Render2D.FillRectangle(rect, Color.Gray); + Render2D.PopClip(); + } + } + } + Render2D.FillRectangle(alphaRect, _value); + } + + Render2D.FillRectangle(colorRect, _value with { A = 1 }); + Render2D.DrawRectangle(fullRect, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black); } /// diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index 5530a1738..3f60572f6 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -447,8 +447,8 @@ namespace FlaxEditor.GUI.Tree // Select previous parent child var select = nodeParent.GetChild(myIndex - 1) as TreeNode; - // Select last child if is valid and expanded and has any children - if (select != null && select.IsExpanded && select.HasAnyVisibleChild) + // Get bottom most child node + while (select != null && select.IsExpanded && select.HasAnyVisibleChild) { select = select.GetChild(select.ChildrenCount - 1) as TreeNode; } diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index fe89026c5..c02992041 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -669,7 +669,7 @@ namespace FlaxEditor.Modules if (item != null) Editor.ContentEditing.Open(item); }); - cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show()); + cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show()); // Scene MenuScene = MainMenu.AddButton("Scene"); @@ -714,6 +714,7 @@ namespace FlaxEditor.Modules _menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG); _menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh); _menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF); + _menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one."); cm.AddSeparator(); cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow); _menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel()); diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 5c9c613d2..218394a3b 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -896,9 +896,11 @@ namespace FlaxEditor.Modules if (type.IsAssignableTo(typeof(AssetEditorWindow))) { - var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) }); var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID); + var assetType = assetItem.GetType(); + var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType }); var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem }); + win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue); if (winData.DockState == DockState.Float) { diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index af919c1f3..ab473ebed 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -650,6 +650,10 @@ namespace FlaxEditor.Options [EditorDisplay("Windows"), EditorOrder(4020)] public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None); + [DefaultValue(typeof(InputBinding), "Control+Comma")] + [EditorDisplay("Windows"), EditorOrder(4030)] + public InputBinding EditorOptionsWindow = new InputBinding(KeyboardKeys.Comma, KeyboardKeys.Control); + #endregion #region Node Editors diff --git a/Source/Editor/Scripting/TypeUtils.cs b/Source/Editor/Scripting/TypeUtils.cs index 2451e1d87..3537b6218 100644 --- a/Source/Editor/Scripting/TypeUtils.cs +++ b/Source/Editor/Scripting/TypeUtils.cs @@ -406,6 +406,8 @@ namespace FlaxEngine.Utilities { if (type == ScriptType.Null) return null; + if (type.BaseType == null) + return type.Type; while (type.Type == null) type = type.BaseType; return type.Type; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 99bf8bd1a..29d5860c3 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -2,11 +2,8 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Loader; -using System.Runtime.Serialization.Formatters.Binary; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI.ContextMenu; @@ -18,6 +15,7 @@ namespace FlaxEditor.Surface class AttributesEditor : ContextMenuBase { private CustomEditorPresenter _presenter; + private Proxy _proxy; private byte[] _oldData; private class Proxy @@ -72,11 +70,11 @@ namespace FlaxEditor.Surface /// Initializes a new instance of the class. /// /// The attributes list to edit. - /// The allowed attribute types to use. - public AttributesEditor(Attribute[] attributes, IList attributeType) + /// The allowed attribute types to use. + public AttributesEditor(Attribute[] attributes, IList attributeTypes) { // Context menu dimensions - const float width = 340.0f; + const float width = 375.0f; const float height = 370.0f; Size = new Float2(width, height); @@ -88,61 +86,68 @@ namespace FlaxEditor.Surface Parent = this }; - // Buttons - float buttonsWidth = (width - 16.0f) * 0.5f; + // Ok and Cancel Buttons + float buttonsWidth = (width - 12.0f) * 0.5f; float buttonsHeight = 20.0f; - var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight) + var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight) + { + Text = "Ok", + Parent = this + }; + okButton.Clicked += OnOkButtonClicked; + var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight) { Text = "Cancel", Parent = this }; cancelButton.Clicked += Hide; - var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight) - { - Text = "OK", - Parent = this - }; - okButton.Clicked += OnOkButtonClicked; - // Actual panel + // Actual panel used to display attributes var panel1 = new Panel(ScrollBars.Vertical) { - Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f), + Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f), Parent = this }; var editor = new CustomEditorPresenter(null); editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop; editor.Panel.IsScrollable = true; editor.Panel.Parent = panel1; - editor.Panel.Tag = attributeType; + editor.Panel.Tag = attributeTypes; _presenter = editor; // Cache 'previous' state to check if attributes were edited after operation _oldData = SurfaceMeta.GetAttributesData(attributes); - editor.Select(new Proxy + _proxy = new Proxy { Value = attributes, - }); + }; + editor.Select(_proxy); + + _presenter.Modified += OnPresenterModified; + OnPresenterModified(); + } + + private void OnPresenterModified() + { + if (_proxy.Value.Length == 0) + { + var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center); + label.Label.Wrapping = TextWrapping.WrapWords; + label.Control.Height = 35f; + label.Label.Margin = new Margin(10f); + label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey; + } } private void OnOkButtonClicked() { var newValue = ((Proxy)_presenter.Selection[0]).Value; - for (int i = 0; i < newValue.Length; i++) - { - if (newValue[i] == null) - { - MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } + newValue = newValue.Where(v => v != null).ToArray(); var newData = SurfaceMeta.GetAttributesData(newValue); if (!_oldData.SequenceEqual(newData)) - { Edited?.Invoke(newValue); - } Hide(); } @@ -183,7 +188,9 @@ namespace FlaxEditor.Surface { _presenter = null; _oldData = null; + _proxy = null; Edited = null; + _presenter.Modified -= OnPresenterModified; base.OnDestroy(); } diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 0138a1b66..10e9fc776 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -214,22 +214,25 @@ namespace FlaxEditor.Surface if (!_isRenaming) Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); - // Close button - Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - - // Color button - Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - - // Check if is resizing - if (_isResizing) + if (Surface.CanEdit) { - // Draw overlay - Render2D.FillRectangle(_resizeButtonRect, style.Selection); - Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); - } + // Close button + Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - // Resize button - Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + // Color button + Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + + // Check if is resizing + if (_isResizing) + { + // Draw overlay + Render2D.FillRectangle(_resizeButtonRect, style.Selection); + Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); + } + + // Resize button + Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + } // Selection outline if (_isSelected) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index e1d7c131f..6312bd68d 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -431,27 +431,6 @@ namespace FlaxEditor.Surface /// public bool HasIndependentBoxes => Archetype.IndependentBoxes != null; - /// - /// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned. - /// - public bool HasDependentBoxesSetup - { - get - { - if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null) - return true; - - for (int i = 0; i < Archetype.DependentBoxes.Length; i++) - { - var b = GetBox(Archetype.DependentBoxes[i]); - if (b != null && b.CurrentType == b.DefaultType) - return false; - } - - return true; - } - } - private static readonly List UpdateStack = new List(); /// diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index d75efb5a0..7a19567fa 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -400,7 +400,7 @@ namespace FlaxEditor.Surface return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>); } var managedType = TypeUtils.GetType(scriptType); - return !TypeUtils.IsDelegate(managedType); + return managedType != null && !TypeUtils.IsDelegate(managedType); } internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType) @@ -408,7 +408,7 @@ namespace FlaxEditor.Surface if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true)) return false; var managedType = TypeUtils.GetType(scriptType); - return !TypeUtils.IsDelegate(managedType); + return managedType != null && !TypeUtils.IsDelegate(managedType); } internal static string GetVisualScriptTypeDescription(ScriptType type) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 51fd96ad6..cbc041c09 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -469,7 +469,8 @@ namespace FlaxEditor.Surface bool handled = base.OnMouseDown(location, button); if (!handled) CustomMouseDown?.Invoke(ref location, button, ref handled); - if (handled) + var root = Root; + if (handled || root == null) { // Clear flags _isMovingSelection = false; @@ -523,11 +524,11 @@ namespace FlaxEditor.Surface if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation)) { // Check if user is pressing control - if (Root.GetKey(KeyboardKeys.Control)) + if (root.GetKey(KeyboardKeys.Control)) { AddToSelection(controlUnderMouse); } - else if (Root.GetKey(KeyboardKeys.Shift)) + else if (root.GetKey(KeyboardKeys.Shift)) { RemoveFromSelection(controlUnderMouse); } @@ -539,7 +540,7 @@ namespace FlaxEditor.Surface } // Start moving selected nodes - if (!Root.GetKey(KeyboardKeys.Shift)) + if (!root.GetKey(KeyboardKeys.Shift)) { StartMouseCapture(); _movingSelectionViewPos = _rootControl.Location; @@ -559,7 +560,7 @@ namespace FlaxEditor.Surface // Start selecting or commenting StartMouseCapture(); - if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift)) + if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift)) { ClearSelection(); } diff --git a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs index a00d37aef..ed19b937f 100644 --- a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs +++ b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs @@ -178,19 +178,31 @@ namespace FlaxEditor.Surface // Update boxes types for nodes that dependant box types based on incoming connections { - bool keepUpdating = false; - int updateLimit = 100; + bool keepUpdating = true; + int updatesMin = 2, updatesMax = 100; do { + keepUpdating = false; for (int i = 0; i < RootControl.Children.Count; i++) { - if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup) + if (RootControl.Children[i] is SurfaceNode node) { node.UpdateBoxesTypes(); - keepUpdating = true; + var arch = node.Archetype; + if (arch.DependentBoxes != null && arch.IndependentBoxes != null) + { + foreach (var boxId in arch.DependentBoxes) + { + var b = node.GetBox(boxId); + if (b != null && b.CurrentType == b.DefaultType) + { + keepUpdating = true; + } + } + } } } - } while (keepUpdating && updateLimit-- > 0); + } while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0); } Loaded?.Invoke(this); diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 065f7ffeb..861cb975b 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -74,11 +74,6 @@ struct TextureDataResult PixelFormat Format; Int2 Mip0Size; BytesContainer* Mip0DataPtr; - - TextureDataResult() - : Lock(FlaxStorage::LockData::Invalid) - { - } }; bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 362aeacf6..a22b4042f 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -74,7 +74,7 @@ namespace FlaxEditor.Viewport private PrefabUIEditorRoot _uiRoot; private bool _showUI = false; - private int _defaultScaleActiveIndex = 0; + private int _defaultScaleActiveIndex = -1; private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; private ContextMenuChildMenu _uiViewOptions; diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index bc1437d97..dcaf98462 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -140,7 +140,7 @@ namespace FlaxEditor.Windows } private string _cacheFolder; - private Guid _assetId; + private AssetItem _item; private Surface _surface; private Label _loadingLabel; private CancellationTokenSource _token; @@ -163,13 +163,13 @@ namespace FlaxEditor.Windows public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem) : base(editor, false, ScrollBars.None) { - Title = assetItem.ShortName + " References"; + _item = assetItem; + Title = _item.ShortName + " References"; _tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder)); _cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References"); if (!Directory.Exists(_cacheFolder)) Directory.CreateDirectory(_cacheFolder); - _assetId = assetItem.ID; _surface = new Surface(this) { AnchorPreset = AnchorPresets.StretchAll, @@ -194,6 +194,7 @@ namespace FlaxEditor.Windows _nodesAssets.Add(assetId); var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId); _nodes.Add(node); + return node; } @@ -392,8 +393,7 @@ namespace FlaxEditor.Windows _nodesAssets = new HashSet(); var searchLevel = 4; // TODO: make it as an option (somewhere in window UI) // TODO: add option to filter assets by type (eg. show only textures as leaf nodes) - var assetNode = SpawnNode(_assetId); - // TODO: add some outline or tint color to the main node + var assetNode = SpawnNode(_item.ID); BuildGraph(assetNode, searchLevel, false); ArrangeGraph(assetNode, false); BuildGraph(assetNode, searchLevel, true); @@ -402,6 +402,10 @@ namespace FlaxEditor.Windows return; _progress = 100.0f; + var commentRect = assetNode.EditorBounds; + commentRect.Expand(80f); + _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); + // Update UI FlaxEngine.Scripting.InvokeOnUpdate(() => { diff --git a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs index 4a896c674..cb39930f9 100644 --- a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs @@ -14,7 +14,6 @@ using FlaxEditor.Surface; using FlaxEditor.Viewport.Previews; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Utilities; namespace FlaxEditor.Windows.Assets { @@ -430,7 +429,7 @@ namespace FlaxEditor.Windows.Assets for (var i = 0; i < parameters.Length; i++) { var p = parameters[i]; - if (p.IsOverride) + if (p.IsOverride && p.IsPublic) { p.IsOverride = false; actions.Add(new EditParamOverrideAction diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index 1dc30cae3..19fc932d5 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -90,25 +90,15 @@ namespace FlaxEditor.Windows.Assets var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage)."); gpu.CheckBox.Checked = sdfOptions.GPU; + gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; }; var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh."); var backfacesThreshold = backfacesThresholdProp.FloatValue(); - var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last(); backfacesThreshold.ValueBox.MinValue = 0.001f; backfacesThreshold.ValueBox.MaxValue = 1.0f; backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold; backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; }; - // Toggle Backfaces Threshold visibility (CPU-only option) - gpu.CheckBox.StateChanged += c => - { - Window._sdfOptions.GPU = c.Checked; - backfacesThresholdLabel.Visible = !c.Checked; - backfacesThreshold.ValueBox.Visible = !c.Checked; - }; - backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked; - backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked; - var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building."); lodIndex.IntValue.MinValue = 0; lodIndex.IntValue.MaxValue = Asset.LODsCount - 1; diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 3d2ec4a66..41382e60c 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -142,6 +142,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; + var style = Style.Current; FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); @@ -164,6 +165,8 @@ namespace FlaxEditor.Windows _navigationBar = new NavigationBar { Parent = _toolStrip, + ScrollbarTrackColor = style.Background, + ScrollbarThumbColor = style.ForegroundGrey, }; // Split panel @@ -179,7 +182,7 @@ namespace FlaxEditor.Windows var headerPanel = new ContainerControl { AnchorPreset = AnchorPresets.HorizontalStretchTop, - BackgroundColor = Style.Current.Background, + BackgroundColor = style.Background, IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index 6d01432ba..03d435efe 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -116,6 +116,11 @@ namespace FlaxEditor.Windows if (InputOptions.WindowShortcutsAvaliable) Editor.Windows.VisualScriptDebuggerWin.FocusOrShow(); }); + InputActions.Add(options => options.EditorOptionsWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.EditorOptionsWin.FocusOrShow(); + }); // Register Editor.Windows.OnWindowAdd(this); diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 0088ddd71..872e78509 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -10,17 +10,117 @@ using FlaxEditor.Modules; using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Json; namespace FlaxEditor.Windows { + /// + /// Render output control with content scaling support. + /// + public class ScaledRenderOutputControl : RenderOutputControl + { + /// + /// Custom scale. + /// + public float ContentScale = 1.0f; + + /// + /// Actual bounds size for content (incl. scale). + /// + public Float2 ContentSize => Size / ContentScale; + + /// + public ScaledRenderOutputControl(SceneRenderTask task) + : base(task) + { + } + + /// + public override void Draw() + { + DrawSelf(); + + // Draw children with scale + var scaling = new Float3(ContentScale, ContentScale, 1); + Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale); + Render2D.PushTransform(scale); + if (ClipChildren) + { + GetDesireClientArea(out var clientArea); + Render2D.PushClip(ref clientArea); + DrawChildren(); + Render2D.PopClip(); + } + else + { + DrawChildren(); + } + + Render2D.PopTransform(); + } + + /// + public override void GetDesireClientArea(out Rectangle rect) + { + // Scale the area for the client controls + rect = new Rectangle(Float2.Zero, Size / ContentScale); + } + + /// + public override bool IntersectsContent(ref Float2 locationParent, out Float2 location) + { + // Skip local PointFromParent but use base code + location = base.PointFromParent(ref locationParent); + return ContainsPoint(ref location); + } + + /// + public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation) + { + location /= ContentScale; + return base.IntersectsChildContent(child, location, out childSpaceLocation); + } + + /// + public override bool ContainsPoint(ref Float2 location, bool precise = false) + { + if (precise) // Ignore as utility-only element + return false; + return base.ContainsPoint(ref location, precise); + } + + /// + public override bool RayCast(ref Float2 location, out Control hit) + { + var p = location / ContentScale; + if (RayCastChildren(ref p, out hit)) + return true; + return base.RayCast(ref location, out hit); + } + + /// + public override Float2 PointToParent(ref Float2 location) + { + var result = base.PointToParent(ref location); + result *= ContentScale; + return result; + } + + /// + public override Float2 PointFromParent(ref Float2 location) + { + var result = base.PointFromParent(ref location); + result /= ContentScale; + return result; + } + } + /// /// Provides in-editor play mode simulation. /// /// public class GameWindow : EditorWindow { - private readonly RenderOutputControl _viewport; + private readonly ScaledRenderOutputControl _viewport; private readonly GameRoot _guiRoot; private bool _showGUI = true, _editGUI = true; private bool _showDebugDraw = false; @@ -77,7 +177,7 @@ namespace FlaxEditor.Windows /// /// Gets the viewport. /// - public RenderOutputControl Viewport => _viewport; + public ScaledRenderOutputControl Viewport => _viewport; /// /// Gets or sets a value indicating whether show game GUI in the view or keep it hidden. @@ -295,7 +395,7 @@ namespace FlaxEditor.Windows var task = MainRenderTask.Instance; // Setup viewport - _viewport = new RenderOutputControl(task) + _viewport = new ScaledRenderOutputControl(task) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, @@ -396,11 +496,8 @@ namespace FlaxEditor.Windows { if (v == null) return; - if (v.Size.Y <= 0 || v.Size.X <= 0) - { return; - } if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1)) { @@ -448,15 +545,7 @@ namespace FlaxEditor.Windows private void ResizeViewport() { - if (!_freeAspect) - { - _windowAspectRatio = Width / Height; - } - else - { - _windowAspectRatio = 1; - } - + _windowAspectRatio = _freeAspect ? 1 : Width / Height; var scaleWidth = _viewportAspectRatio / _windowAspectRatio; var scaleHeight = _windowAspectRatio / _viewportAspectRatio; @@ -468,6 +557,24 @@ namespace FlaxEditor.Windows { _viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height); } + + if (_viewport.KeepAspectRatio) + { + var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size; + if (scaleHeight < 1) + { + _viewport.ContentScale = _viewport.Width / resolution.X; + } + else + { + _viewport.ContentScale = _viewport.Height / resolution.Y; + } + } + else + { + _viewport.ContentScale = 1; + } + _viewport.SyncBackbufferSize(); PerformLayout(); } @@ -907,6 +1014,7 @@ namespace FlaxEditor.Windows return child.OnNavigate(direction, Float2.Zero, this, visited); } } + return null; } @@ -957,7 +1065,7 @@ namespace FlaxEditor.Windows else _defaultScaleActiveIndex = 0; } - + if (_customScaleActiveIndex != -1) { var options = Editor.UI.CustomViewportScaleOptions; diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs index 74f14b584..11b8a1980 100644 --- a/Source/Editor/Windows/Profiler/MemoryGPU.cs +++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs @@ -296,13 +296,15 @@ namespace FlaxEditor.Windows.Profiler var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex); if (resources == null || resources.Length == 0) return; - var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage); + var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0); // Add rows var rowColor2 = Style.Current.Background * 1.4f; int rowIndex = 0; foreach (var e in resourcesOrdered) { + if (e == null) + continue; ClickableRow row; if (_tableRowsCache.Count != 0) { diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 75a3723cf..d9ba67aa3 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -319,22 +319,22 @@ namespace FlaxEditor.Windows { if (assetItem.IsOfType()) return true; - return assetItem.OnEditorDrag(this); + return assetItem.OnEditorDrag(this) && Level.IsAnySceneLoaded; } private static bool ValidateDragActorType(ScriptType actorType) { - return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType); + return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType) && Level.IsAnySceneLoaded; } private static bool ValidateDragControlType(ScriptType controlType) { - return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType); + return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType) && Level.IsAnySceneLoaded; } private static bool ValidateDragScriptItem(ScriptItem script) { - return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null; + return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null && Level.IsAnySceneLoaded; } /// @@ -490,6 +490,7 @@ namespace FlaxEditor.Windows if (result == DragDropEffect.None) { _isDropping = true; + // Drag assets if (_dragAssets != null && _dragAssets.HasValidDrag) { @@ -504,7 +505,7 @@ namespace FlaxEditor.Windows } var actor = item.OnEditorDrop(this); actor.Name = item.ShortName; - Level.SpawnActor(actor); + Editor.SceneEditing.Spawn(actor); var graphNode = Editor.Scene.GetActorNode(actor.ID); if (graphNode != null) graphNodes.Add(graphNode); @@ -527,7 +528,7 @@ namespace FlaxEditor.Windows continue; } actor.Name = item.Name; - Level.SpawnActor(actor); + Editor.SceneEditing.Spawn(actor); Editor.Scene.MarkSceneEdited(actor.Scene); } result = DragDropEffect.Move; @@ -549,7 +550,7 @@ namespace FlaxEditor.Windows Control = control, Name = item.Name, }; - Level.SpawnActor(uiControl); + Editor.SceneEditing.Spawn(uiControl); Editor.Scene.MarkSceneEdited(uiControl.Scene); } result = DragDropEffect.Move; @@ -571,7 +572,7 @@ namespace FlaxEditor.Windows continue; } actor.Name = actorType.Name; - Level.SpawnActor(actor); + Editor.SceneEditing.Spawn(actor); var graphNode = Editor.Scene.GetActorNode(actor.ID); if (graphNode != null) graphNodes.Add(graphNode); diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 7e2889ca1..54a1a7418 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -137,6 +137,7 @@ const Char* SplashScreenQuotes[] = TEXT("Good Luck Have Fun"), TEXT("GG Well Played"), TEXT("Now with documentation."), + TEXT("We do this not because it is easy,\nbut because we thought it would be easy"), }; SplashScreen::~SplashScreen() diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index c76bddf3f..1e4444af0 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -246,11 +246,19 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f; #define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type }) if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration - && (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration()))) + && (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) + && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration()))) // Handle the edge case of an event on 0 or on max animation duration during looping - || (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration()) + || (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration())) || (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) || (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) + || (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration())) + || (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration())) + || (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f) + || (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f) + || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f)) + || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f)) + || (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f) ) { int32 stateIndex = -1; @@ -2433,10 +2441,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { if (bucket.LoopsLeft == 0) { - // End playing animation + // End playing animation and reset bucket params value = tryGetValue(node->GetBox(1), Value::Null); bucket.Index = -1; slot.Animation = nullptr; + bucket.TimePosition = 0.0f; + bucket.BlendInPosition = 0.0f; + bucket.BlendOutPosition = 0.0f; + bucket.LoopsDone = 0; return; } @@ -2545,9 +2557,15 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va // Function Input case 1: { + // Skip when graph is too small (eg. preview) and fallback with default value from the function graph + if (context.GraphStack.Count() < 2) + { + value = tryGetValue(node->TryGetBox(1), Value::Zero); + break; + } + // Find the function call AnimGraphNode* functionCallNode = nullptr; - ASSERT(context.GraphStack.Count() >= 2); Graph* graph; for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { diff --git a/Source/Engine/Audio/AudioListener.cpp b/Source/Engine/Audio/AudioListener.cpp index 1921bd373..9c5898b4a 100644 --- a/Source/Engine/Audio/AudioListener.cpp +++ b/Source/Engine/Audio/AudioListener.cpp @@ -23,11 +23,11 @@ void AudioListener::Update() { // Update the velocity const Vector3 pos = GetPosition(); - const float dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds(); + const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), 0.00001f); const auto prevVelocity = _velocity; _velocity = (pos - _prevPos) / dt; _prevPos = pos; - if (_velocity != prevVelocity) + if (_velocity != prevVelocity && !_velocity.IsNanOrInfinity()) { AudioBackend::Listener::VelocityChanged(_velocity); } diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index cb711013b..9fd4cee9c 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -666,7 +666,7 @@ void Asset::onLoaded() { onLoaded_MainThread(); } - else if (OnLoaded.IsBinded()) + else if (OnLoaded.IsBinded() || _references.HasItems()) { Function action; action.Bind(this); diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index b3710ebbd..8d142626e 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -218,10 +218,14 @@ Asset::LoadResult MaterialInstance::load() Guid baseMaterialId; headerStream.Read(baseMaterialId); auto baseMaterial = Content::LoadAsync(baseMaterialId); + if (baseMaterial) + baseMaterial->AddReference(); // Load parameters if (Params.Load(&headerStream)) { + if (baseMaterial) + baseMaterial->RemoveReference(); LOG(Warning, "Cannot load material parameters."); return LoadResult::CannotLoadData; } @@ -239,6 +243,8 @@ Asset::LoadResult MaterialInstance::load() ParamsChanged(); } + if (baseMaterial) + baseMaterial->RemoveReference(); return LoadResult::Ok; } diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 829eaeb11..66ca0aa2e 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -262,6 +262,7 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f LOG(Warning, "Cannot generate SDF for virtual models on a main thread."); return true; } + auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData(); lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1); // Generate SDF diff --git a/Source/Engine/Content/Assets/ModelBase.cpp b/Source/Engine/Content/Assets/ModelBase.cpp index 599caa810..1847e96d9 100644 --- a/Source/Engine/Content/Assets/ModelBase.cpp +++ b/Source/Engine/Content/Assets/ModelBase.cpp @@ -61,7 +61,7 @@ public: model->GetLODData(_lodIndex, data); if (data.IsInvalid()) { - LOG(Warning, "Missing data chunk"); + LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString()); return true; } MemoryReadStream stream(data.Get(), data.Length()); @@ -234,6 +234,7 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path) LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data)."); return true; } + auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData(); ScopeLock lock(Locker); // Use a temporary chunks for data storage for virtual assets diff --git a/Source/Engine/Content/AssetsContainer.h b/Source/Engine/Content/AssetsContainer.h index aceba1f07..dd9b549b5 100644 --- a/Source/Engine/Content/AssetsContainer.h +++ b/Source/Engine/Content/AssetsContainer.h @@ -18,7 +18,7 @@ public: /// The asset id. /// Loaded asset of null. template - T* LoadAsync(const Guid& id) + T* Load(const Guid& id) { for (auto& e : *this) { @@ -26,8 +26,10 @@ public: return (T*)e.Get(); } auto asset = (T*)::LoadAsset(id, T::TypeInitializer); - if (asset) + if (asset && !asset->WaitForLoaded()) Add(asset); + else + asset = nullptr; return asset; } diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index b82a329a8..4a9ea500b 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -30,6 +30,7 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Scripting/ManagedCLR/MClass.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #include "Engine/Scripting/Scripting.h" #if USE_EDITOR #include "Editor/Editor.h" @@ -346,17 +347,21 @@ int32 LoadingThread::Run() ContentLoadTask* task; ThisLoadThread = this; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; while (Platform::AtomicRead(&_exitFlag) == 0) { if (LoadTasks.try_dequeue(task)) { Run(task); + MONO_THREAD_INFO_GET(monoThreadInfo); } else { + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); LoadTasksMutex.Lock(); LoadTasksSignal.Wait(LoadTasksMutex); LoadTasksMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } @@ -700,6 +705,7 @@ Asset* Content::GetAsset(const StringView& outputPath) { if (outputPath.IsEmpty()) return nullptr; + PROFILE_CPU(); ScopeLock lock(AssetsLocker); for (auto i = Assets.Begin(); i.IsNotEnd(); ++i) { diff --git a/Source/Engine/Content/Factories/BinaryAssetFactory.cpp b/Source/Engine/Content/Factories/BinaryAssetFactory.cpp index e9479a107..c492bd5b0 100644 --- a/Source/Engine/Content/Factories/BinaryAssetFactory.cpp +++ b/Source/Engine/Content/Factories/BinaryAssetFactory.cpp @@ -146,6 +146,10 @@ bool BinaryAssetFactoryBase::UpgradeAsset(const AssetInfo& info, FlaxStorage* st context.Input = context.Output; } while (upgrader->ShouldUpgrade(context.Input.SerializedVersion)); + // Prevent other threads from loading the storage when it is upgrading + // It works because CriticalSection allows recursion + ScopeLock upgradeLock(storage->_loadLocker); + // Release storage internal data (should also close file handles) { // HACK: file is locked by some tasks: the current one that called asset data upgrade (LoadAssetTask) diff --git a/Source/Engine/Content/Storage/FlaxChunk.h b/Source/Engine/Content/Storage/FlaxChunk.h index 6e9887574..1d3bdce1a 100644 --- a/Source/Engine/Content/Storage/FlaxChunk.h +++ b/Source/Engine/Content/Storage/FlaxChunk.h @@ -87,6 +87,10 @@ public: /// double LastAccessTime = 0.0; + /// + /// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads). + /// + int64 IsLoading = 0; /// /// The chunk data. /// @@ -146,7 +150,7 @@ public: /// FORCE_INLINE bool IsLoaded() const { - return Data.IsValid(); + return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0; } /// @@ -154,7 +158,7 @@ public: /// FORCE_INLINE bool IsMissing() const { - return Data.IsInvalid(); + return !IsLoaded(); } /// diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index ed8b623ae..6967bb5ad 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -5,6 +5,7 @@ #include "FlaxPackage.h" #include "ContentStorageManager.h" #include "Engine/Core/Log.h" +#include "Engine/Core/ScopeExit.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Platform/File.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -74,8 +75,6 @@ FlaxChunk* FlaxChunk::Clone() const const int32 FlaxStorage::MagicCode = 1180124739; -FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr); - struct Header { int32 MagicCode; @@ -246,6 +245,7 @@ FlaxStorage::~FlaxStorage() ASSERT(IsDisposed()); CHECK(_chunksLock == 0); CHECK(_refCount == 0); + CHECK(_isUnloadingData == 0); ASSERT(_chunks.IsEmpty()); #if USE_EDITOR @@ -261,6 +261,22 @@ FlaxStorage::~FlaxStorage() #endif } +void FlaxStorage::LockChunks() +{ +RETRY: + Platform::InterlockedIncrement(&_chunksLock); + if (Platform::AtomicRead(&_isUnloadingData) != 0) + { + // Someone else is closing file handles or freeing chunks so wait for it to finish and retry + Platform::InterlockedDecrement(&_chunksLock); + do + { + Platform::Sleep(1); + } while (Platform::AtomicRead(&_isUnloadingData) != 0); + goto RETRY; + } +} + FlaxStorage::LockData FlaxStorage::LockSafe() { auto lock = LockData(this); @@ -689,7 +705,6 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data) return true; } - // Load header return LoadAssetHeader(e, data); } @@ -699,7 +714,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) ASSERT(IsLoaded()); ASSERT(chunk != nullptr && _chunks.Contains(chunk)); - // Check if already loaded + // Protect against loading the same chunk from multiple threads at once + while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0) + Platform::Sleep(1); + SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); }; if (chunk->IsLoaded()) return false; @@ -776,12 +794,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) // Raw data chunk->Data.Read(stream, size); } - ASSERT(chunk->IsLoaded()); chunk->RegisterUsage(); } UnlockChunks(); - return failed; } @@ -1420,10 +1436,12 @@ FileReadStream* FlaxStorage::OpenFile() bool FlaxStorage::CloseFileHandles() { + // Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end + Platform::InterlockedIncrement(&_isUnloadingData); + SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); }; + if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0) - { - return false; - } + return false; // Early out when no files are opened PROFILE_CPU(); PROFILE_MEM(ContentFiles); @@ -1496,9 +1514,21 @@ void FlaxStorage::Tick(double time) { auto chunk = _chunks.Get()[i]; const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime; - if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory)) + if (!wasUsed && + chunk->IsLoaded() && + EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) && + Platform::AtomicRead(&chunk->IsLoading) == 0) { + // Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end + Platform::InterlockedIncrement(&_isUnloadingData); + if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0) + { + // Someone started loading so skip ticking + Platform::InterlockedDecrement(&_isUnloadingData); + return; + } chunk->Unload(); + Platform::InterlockedDecrement(&_isUnloadingData); } wasAnyUsed |= wasUsed; } diff --git a/Source/Engine/Content/Storage/FlaxStorage.h b/Source/Engine/Content/Storage/FlaxStorage.h index 450de9808..f01384b68 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.h +++ b/Source/Engine/Content/Storage/FlaxStorage.h @@ -90,6 +90,7 @@ protected: int64 _refCount = 0; int64 _chunksLock = 0; int64 _files = 0; + int64 _isUnloadingData = 0; double _lastRefLostTime; CriticalSection _loadLocker; @@ -129,10 +130,7 @@ public: /// /// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked. /// - FORCE_INLINE void LockChunks() - { - Platform::InterlockedIncrement(&_chunksLock); - } + void LockChunks(); /// /// Unlocks the storage chunks data. @@ -148,7 +146,6 @@ public: struct LockData { friend FlaxStorage; - static LockData Invalid; private: FlaxStorage* _storage; @@ -161,6 +158,11 @@ public: } public: + LockData() + : _storage(nullptr) + { + } + LockData(const LockData& other) : _storage(other._storage) { diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 4f660d2a9..919ff8f80 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -658,7 +658,10 @@ public: --_count; T* data = _allocation.Get(); if (index < _count) - Memory::MoveAssignItems(data + index, data + (index + 1), _count - index); + { + for (int32 i = index; i < _count; i++) + data[i] = MoveTemp(data[i + 1]); + } Memory::DestructItems(data + _count, 1); } diff --git a/Source/Engine/Core/Collections/HashSetBase.h b/Source/Engine/Core/Collections/HashSetBase.h index 36fcf275d..94bbd149d 100644 --- a/Source/Engine/Core/Collections/HashSetBase.h +++ b/Source/Engine/Core/Collections/HashSetBase.h @@ -409,27 +409,36 @@ protected: else { // Rebuild entire table completely + const int32 elementsCount = _elementsCount; + const int32 oldSize = _size; AllocationData oldAllocation; - AllocationUtils::MoveToEmpty(oldAllocation, _allocation, _size, _size); + AllocationUtils::MoveToEmpty(oldAllocation, _allocation, oldSize, oldSize); _allocation.Allocate(_size); BucketType* data = _allocation.Get(); - for (int32 i = 0; i < _size; ++i) + for (int32 i = 0; i < oldSize; ++i) data[i]._state = HashSetBucketState::Empty; BucketType* oldData = oldAllocation.Get(); FindPositionResult pos; - for (int32 i = 0; i < _size; ++i) + for (int32 i = 0; i < oldSize; ++i) { BucketType& oldBucket = oldData[i]; if (oldBucket.IsOccupied()) { FindPosition(oldBucket.GetKey(), pos); - ASSERT(pos.FreeSlotIndex != -1); + if (pos.FreeSlotIndex == -1) + { + // Grow and retry to handle pathological cases (eg. heavy collisions) + EnsureCapacity(_size + 1, true); + FindPosition(oldBucket.GetKey(), pos); + ASSERT(pos.FreeSlotIndex != -1); + } BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex]; bucket = MoveTemp(oldBucket); } } - for (int32 i = 0; i < _size; ++i) + for (int32 i = 0; i < oldSize; ++i) oldData[i].Free(); + _elementsCount = elementsCount; } _deletedCount = 0; } diff --git a/Source/Engine/Core/Compiler.h b/Source/Engine/Core/Compiler.h index a45a4628a..d84713857 100644 --- a/Source/Engine/Core/Compiler.h +++ b/Source/Engine/Core/Compiler.h @@ -4,8 +4,9 @@ #if defined(__clang__) -#define DLLEXPORT __attribute__ ((__visibility__ ("default"))) +#define DLLEXPORT __attribute__((__visibility__("default"))) #define DLLIMPORT +#define USED __attribute__((used)) #define THREADLOCAL __thread #define STDCALL __attribute__((stdcall)) #define CDECL __attribute__((cdecl)) @@ -19,7 +20,7 @@ #define PACK_BEGIN() #define PACK_END() __attribute__((__packed__)) #define ALIGN_BEGIN(_align) -#define ALIGN_END(_align) __attribute__( (aligned(_align) ) ) +#define ALIGN_END(_align) __attribute__((aligned(_align))) #define OFFSET_OF(X, Y) __builtin_offsetof(X, Y) #define PRAGMA_DISABLE_DEPRECATION_WARNINGS \ _Pragma("clang diagnostic push") \ @@ -37,8 +38,9 @@ #elif defined(__GNUC__) -#define DLLEXPORT __attribute__ ((__visibility__ ("default"))) +#define DLLEXPORT __attribute__((__visibility__("default"))) #define DLLIMPORT +#define USED __attribute__((used)) #define THREADLOCAL __thread #define STDCALL __attribute__((stdcall)) #define CDECL __attribute__((cdecl)) @@ -52,7 +54,7 @@ #define PACK_BEGIN() #define PACK_END() __attribute__((__packed__)) #define ALIGN_BEGIN(_align) -#define ALIGN_END(_align) __attribute__( (aligned(_align) ) ) +#define ALIGN_END(_align) __attribute__((aligned(_align))) #define OFFSET_OF(X, Y) __builtin_offsetof(X, Y) #define PRAGMA_DISABLE_DEPRECATION_WARNINGS #define PRAGMA_ENABLE_DEPRECATION_WARNINGS @@ -67,6 +69,7 @@ #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) +#define USED #define THREADLOCAL __declspec(thread) #define STDCALL __stdcall #define CDECL __cdecl diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 5a40fae6e..dee415e4a 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -620,14 +620,9 @@ bool Collision::RayIntersectsTriangle(const Ray& ray, const Vector3& a, const Ve Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z; rayDistance *= inverseDeterminant; - // Check if the triangle is behind the ray origin - if (rayDistance < 0.0f) - { - return false; - } - + // Check if the triangle is in front the ray origin distance = rayDistance; - return true; + return rayDistance >= 0.0f; } bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal) diff --git a/Source/Engine/Core/Math/Ray.cpp b/Source/Engine/Core/Math/Ray.cpp index 414814c4a..5bee8c2c4 100644 --- a/Source/Engine/Core/Math/Ray.cpp +++ b/Source/Engine/Core/Math/Ray.cpp @@ -12,11 +12,6 @@ String Ray::ToString() const return String::Format(TEXT("{}"), *this); } -Vector3 Ray::GetPoint(Real distance) const -{ - return Position + Direction * distance; -} - Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp) { Vector3 nearPoint(x, y, 0.0f); diff --git a/Source/Engine/Core/Math/Ray.h b/Source/Engine/Core/Math/Ray.h index cb2aa6419..65e9f1512 100644 --- a/Source/Engine/Core/Math/Ray.h +++ b/Source/Engine/Core/Math/Ray.h @@ -79,7 +79,10 @@ public: /// /// The distance from ray origin. /// The calculated point. - Vector3 GetPoint(Real distance) const; + FORCE_INLINE Vector3 GetPoint(Real distance) const + { + return Position + Direction * distance; + } /// /// Determines if there is an intersection between ray and a point. diff --git a/Source/Engine/Core/Memory/Allocation.h b/Source/Engine/Core/Memory/Allocation.h index d6958d2c3..f274d37a5 100644 --- a/Source/Engine/Core/Memory/Allocation.h +++ b/Source/Engine/Core/Memory/Allocation.h @@ -18,9 +18,7 @@ namespace AllocationUtils capacity |= capacity >> 8; capacity |= capacity >> 16; uint64 capacity64 = (uint64)(capacity + 1) * 2; - if (capacity64 > MAX_int32) - capacity64 = MAX_int32; - return (int32)capacity64; + return capacity64 >= MAX_int32 ? MAX_int32 : (int32)capacity64 / 2; } // Aligns the input value to the next power of 2 to be used as bigger memory allocation block. @@ -208,7 +206,7 @@ public: typedef typename FallbackAllocation::template Data FallbackData; bool _useFallback = false; - byte _data[Capacity * sizeof(T)]; + alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)]; FallbackData _fallback; public: diff --git a/Source/Engine/Core/Types/StringBuilder.h b/Source/Engine/Core/Types/StringBuilder.h index 925111cc3..620ac8a13 100644 --- a/Source/Engine/Core/Types/StringBuilder.h +++ b/Source/Engine/Core/Types/StringBuilder.h @@ -215,6 +215,11 @@ public: return String(_data.Get(), _data.Count()); } + StringAnsi ToStringAnsi() const + { + return StringAnsi(_data.Get(), _data.Count()); + } + StringView ToStringView() const; }; diff --git a/Source/Engine/Engine/EngineService.cpp b/Source/Engine/Engine/EngineService.cpp index 433cf089f..240f60a63 100644 --- a/Source/Engine/Engine/EngineService.cpp +++ b/Source/Engine/Engine/EngineService.cpp @@ -26,7 +26,7 @@ static bool CompareEngineServices(EngineService* const& a, EngineService* const& { \ ZoneScoped; \ auto& services = GetServices(); \ - for (int32 i = 0; i < services.Count(); i++) \ + for (int32 i = services.Count() - 1; i >= 0; i--) \ services[i]->name(); \ } diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 2d8f8db84..564c53801 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -79,10 +79,11 @@ namespace FlaxEngine.Interop NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), NativeLibraryImportResolver); // Change default culture to match with Mono runtime default culture - CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; - CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; - System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; - System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + var culture = CultureInfo.InvariantCulture; + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + System.Threading.Thread.CurrentThread.CurrentCulture = culture; + System.Threading.Thread.CurrentThread.CurrentUICulture = culture; } @@ -425,6 +426,8 @@ namespace FlaxEngine.Interop var fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags) return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync) +#else + throw new NotImplementedException(); #endif } diff --git a/Source/Engine/Graphics/GPUContext.cpp b/Source/Engine/Graphics/GPUContext.cpp index 55f87ba3b..905919794 100644 --- a/Source/Engine/Graphics/GPUContext.cpp +++ b/Source/Engine/Graphics/GPUContext.cpp @@ -67,7 +67,7 @@ void GPUContext::FrameBegin() void GPUContext::FrameEnd() { - ClearState(); + ResetState(); FlushState(); } diff --git a/Source/Engine/Graphics/GPUContext.h b/Source/Engine/Graphics/GPUContext.h index 4f1306567..1144d6f49 100644 --- a/Source/Engine/Graphics/GPUContext.h +++ b/Source/Engine/Graphics/GPUContext.h @@ -189,7 +189,7 @@ public: /// [Deprecated in v1.10] /// /// true if depth buffer is binded; otherwise, false. - DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in ") + DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in future") virtual bool IsDepthBufferBinded() = 0; public: @@ -617,8 +617,17 @@ public: /// /// Clears the context state. + /// [Deprecated in v1.12] /// - API_FUNCTION() virtual void ClearState() = 0; + API_FUNCTION() DEPRECATED("Use ResetState instead") void ClearState() + { + ResetState(); + } + + /// + /// Resets the context state. + /// + API_FUNCTION() virtual void ResetState() = 0; /// /// Flushes the internal cached context state with a command buffer. diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index bbcdc8207..7b31a606e 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -201,6 +201,7 @@ bool DeferredMaterialShader::Load() psDesc.DepthWriteEnable = true; psDesc.DepthEnable = true; psDesc.DepthFunc = ComparisonFunc::Less; + psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None; psDesc.HS = nullptr; psDesc.DS = nullptr; GPUShaderProgramVS* instancedDepthPassVS; diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 72ec3c7bd..d8d986163 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -195,5 +195,10 @@ bool ForwardMaterialShader::Load() psDesc.VS = _shader->GetVS("VS_Skinned"); _cache.DepthSkinned.Init(psDesc); +#if PLATFORM_PS5 + // Fix shader binding issues on forward shading materials on PS5 + _drawModes = DrawPass::None; +#endif + return false; } diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 5da4ee04f..bb68520c0 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 178 +#define MATERIAL_GRAPH_VERSION 179 class Material; class GPUShader; diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 64dfe8303..19f2042f4 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -191,7 +191,7 @@ bool GlobalIlluminationFeature::Bind(MaterialShader::BindParameters& params, Spa { // Unbind SRVs to prevent issues data.DDGI.CascadesCount = 0; - data.DDGI.FallbackIrradiance = Float3::Zero; + data.DDGI.FallbackIrradiance = Float4::Zero; params.GPUContext->UnBindSR(srv + 0); params.GPUContext->UnBindSR(srv + 1); params.GPUContext->UnBindSR(srv + 2); diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 6c780cc55..a2c45a6ee 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -264,5 +264,10 @@ bool ParticleMaterialShader::Load() // Lazy initialization _cacheVolumetricFog.Desc.PS = nullptr; +#if PLATFORM_PS5 + // Fix shader binding issues on forward shading materials on PS5 + _drawModes = DrawPass::None; +#endif + return false; } diff --git a/Source/Engine/Graphics/Models/MeshAccessor.h b/Source/Engine/Graphics/Models/MeshAccessor.h index 67b30e502..25fc01a1a 100644 --- a/Source/Engine/Graphics/Models/MeshAccessor.h +++ b/Source/Engine/Graphics/Models/MeshAccessor.h @@ -17,7 +17,7 @@ public: /// /// Mesh data stream. /// - struct Stream + struct FLAXENGINE_API Stream { friend MeshAccessor; diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 670d99611..a300063e7 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -378,7 +378,7 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable /// The irradiance lighting outside the GI range used as a fallback to prevent pure-black scene outside the Global Illumination range. /// API_FIELD(Attributes="EditorOrder(40), PostProcessSetting((int)GlobalIlluminationSettingsOverride.FallbackIrradiance)") - Color FallbackIrradiance = Color::Black; + Color FallbackIrradiance = Color::Transparent; public: /// diff --git a/Source/Engine/Graphics/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp index ed49260ec..931312aa5 100644 --- a/Source/Engine/Graphics/RenderBuffers.cpp +++ b/Source/Engine/Graphics/RenderBuffers.cpp @@ -113,7 +113,8 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context) PixelFormat RenderBuffers::GetOutputFormat() const { - return _useAlpha ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float; + // TODO: fix incorrect alpha leaking into reflections on PS5 with R11G11B10_Float + return _useAlpha || PLATFORM_PS5 ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float; } bool RenderBuffers::GetUseAlpha() const diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 07926fd64..8cba1006e 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -450,7 +450,7 @@ public: /// /// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering. /// -API_STRUCT(NoDefault) struct RenderContext +API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext { DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext); @@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext /// /// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once). /// -API_STRUCT(NoDefault) struct RenderContextBatch +API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch { DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch); diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index b0d587c8d..effbe6e1b 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -620,6 +620,40 @@ void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Flo resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias } +Float3 RenderTools::GetColorQuantizationError(PixelFormat format) +{ + Float3 mantissaBits; + switch (format) + { + case PixelFormat::R11G11B10_Float: + mantissaBits = Float3(6, 6, 5); + break; + case PixelFormat::R10G10B10A2_UNorm: + mantissaBits = Float3(10, 10, 10); + break; + case PixelFormat::R16G16B16A16_Float: + mantissaBits = Float3(16, 16, 16); + break; + case PixelFormat::R32G32B32A32_Float: + mantissaBits = Float3(23, 23, 23); + break; + case PixelFormat::R9G9B9E5_SharedExp: + mantissaBits = Float3(5, 6, 5); + break; + case PixelFormat::R8G8B8A8_UNorm: + case PixelFormat::B8G8R8A8_UNorm: + mantissaBits = Float3(8, 8, 8); + break; + default: + return Float3::Zero; + } + return { + Math::Pow(0.5f, mantissaBits.X), + Math::Pow(0.5f, mantissaBits.Y), + Math::Pow(0.5f, mantissaBits.Z) + }; +} + int32 MipLevelsCount(int32 width) { int32 result = 1; diff --git a/Source/Engine/Graphics/RenderTools.h b/Source/Engine/Graphics/RenderTools.h index 18357a13a..5f0dc23dc 100644 --- a/Source/Engine/Graphics/RenderTools.h +++ b/Source/Engine/Graphics/RenderTools.h @@ -140,6 +140,9 @@ public: static void CalculateTangentFrame(Float3& resultNormal, Float4& resultTangent, const Float3& normal, const Float3& tangent); static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside); + + // Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl). + static Float3 GetColorQuantizationError(PixelFormat format); }; // Calculates mip levels count for a texture 1D. diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp index 585568d60..23382673f 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp @@ -216,20 +216,21 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span& layouts) return result; } -GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride) +GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride, bool referenceOrder) { GPUVertexLayout* result = base ? base : reference; if (base && reference && base != reference) { bool elementsModified = false; Elements newElements = base->GetElements(); + const Elements& refElements = reference->GetElements(); if (removeUnused) { for (int32 i = newElements.Count() - 1; i >= 0; i--) { bool missing = true; const VertexElement& e = newElements.Get()[i]; - for (const VertexElement& ee : reference->GetElements()) + for (const VertexElement& ee : refElements) { if (ee.Type == e.Type) { @@ -247,7 +248,7 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* } if (addMissing) { - for (const VertexElement& e : reference->GetElements()) + for (const VertexElement& e : refElements) { bool missing = true; for (const VertexElement& ee : base->GetElements()) @@ -282,6 +283,32 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* } } } + if (referenceOrder) + { + for (int32 i = 0, j = 0; i < newElements.Count() && j < refElements.Count(); j++) + { + if (newElements[i].Type == refElements[j].Type) + { + // Elements match so move forward + i++; + continue; + } + + // Find reference element in a new list + for (int32 k = i + 1; k < newElements.Count(); k++) + { + if (newElements[k].Type == refElements[j].Type) + { + // Move matching element to the reference position + VertexElement e = newElements[k]; + newElements.RemoveAt(k); + newElements.Insert(i, e); + i++; + break; + } + } + } + } if (elementsModified) result = Get(newElements, true); } diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h index 9d33566fb..04815565b 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h @@ -84,8 +84,9 @@ public: /// True to remove elements from base layout that don't exist in a reference layout. /// True to add missing elements to base layout that exist in a reference layout. /// Allows to override the input slot for missing elements. Use value -1 to inherit slot from the reference layout. + /// True to reorder result elements to match the reference layout. For example, if input vertex buffer layout is different than vertex shader then it can match those. /// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime. - static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1); + static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1, bool referenceOrder = false); public: // [GPUResource] diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index 735bc162e..71d209301 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -338,10 +338,10 @@ public: StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex, Task* rootTask) : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), 0, 0, false) , _streamingTexture(texture) - , _rootTask(rootTask ? rootTask : this) + , _rootTask(rootTask) , _dataLock(_streamingTexture->GetOwner()->LockData()) { - _streamingTexture->_streamingTasks.Add(_rootTask); + _streamingTexture->_streamingTasks.Add(this); _texture.Released.Bind(this); } @@ -357,7 +357,7 @@ private: if (_streamingTexture) { ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker()); - _streamingTexture->_streamingTasks.Remove(_rootTask); + _streamingTexture->_streamingTasks.Remove(this); _streamingTexture = nullptr; } } @@ -422,6 +422,15 @@ protected: GPUUploadTextureMipTask::OnFail(); } + + void OnCancel() override + { + GPUUploadTextureMipTask::OnCancel(); + + // Cancel the root task too (eg. mip loading from asset) + if (_rootTask != nullptr) + _rootTask->Cancel(); + } }; Task* StreamingTexture::CreateStreamingTask(int32 residency) diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index 81b5f159c..eb838fa07 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -771,7 +771,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex) FlaxStorage::LockData TextureBase::LockData() { - return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData::Invalid; + return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData(); } void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index a0ec80bb1..f623f53b5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -724,7 +724,7 @@ void GPUContextDX11::SetState(GPUPipelineState* state) } } -void GPUContextDX11::ClearState() +void GPUContextDX11::ResetState() { if (!_context) return; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h index 7dc693019..eee2699df 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h @@ -158,7 +158,7 @@ public: void SetScissor(const Rectangle& scissorRect) override; GPUPipelineState* GetState() const override; void SetState(GPUPipelineState* state) override; - void ClearState() override; + void ResetState() override; void FlushState() override; void Flush() override; void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp index a05585696..bce8271db 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp @@ -553,7 +553,7 @@ void GPUTextureDX11::initHandles() if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) { PixelFormat stencilFormat; - switch (_dxgiFormatDSV) + switch (static_cast(_dxgiFormatDSV)) { case PixelFormat::D24_UNorm_S8_UInt: srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp index 4bfed7b01..5bb825e65 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp @@ -92,9 +92,8 @@ float GPUTimerQueryDX11::GetResult() { if (!_finalized) { -#if BUILD_DEBUG - ASSERT(HasResult()); -#endif + if (!HasResult()) + return 0; UINT64 timeStart, timeEnd; auto context = _device->GetIM(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp index 81fef4965..a384b6383 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp @@ -143,6 +143,8 @@ void CommandQueueDX12::WaitForFence(uint64 fenceValue) void CommandQueueDX12::WaitForGPU() { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); const uint64 value = _fence.Signal(this); _fence.WaitCPU(value); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index 0d9ff88d4..6f3ff5fba 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -137,7 +137,7 @@ bool GPUBufferDX12::OnInit() // Create resource ID3D12Resource* resource; #if PLATFORM_WINDOWS - D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; + D3D12_HEAP_FLAGS heapFlags = EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer | GPUBufferFlags::IndexBuffer) || _desc.InitData ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE; #else D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 64c384447..98143c7c3 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -29,6 +29,7 @@ #include "GPUVertexLayoutDX12.h" #include "CommandQueueDX12.h" #include "DescriptorHeapDX12.h" +#include "RootSignatureDX12.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" #include "Engine/Debug/Exceptions/NotImplementedException.h" @@ -307,6 +308,7 @@ void GPUContextDX12::Reset() _device->DummyVB = _device->CreateBuffer(TEXT("DummyVertexBuffer")); auto* layout = GPUVertexLayout::Get({ { VertexElement::Types::Attribute3, 0, 0, 0, PixelFormat::R32G32B32A32_Float } }); _device->DummyVB->Init(GPUBufferDescription::Vertex(layout, sizeof(Color), 1, &Color::Transparent)); + SetResourceState(_device->DummyVB, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, 0); } ((GPUBufferDX12*)_device->DummyVB)->GetVBView(dummyVBView); _commandList->IASetVertexBuffers(GPU_MAX_VB_BINDED, 1, &dummyVBView); @@ -491,7 +493,7 @@ void GPUContextDX12::flushUAVs() ASSERT(uaCount <= GPU_MAX_UA_BINDED); // Fill table with source descriptors - DxShaderHeader& header = _currentCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header; + DxShaderHeader& header = _isCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header; D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_UA_BINDED]; for (uint32 i = 0; i < uaCount; i++) { @@ -628,7 +630,9 @@ void GPUContextDX12::flushPS() LOG(Error, "Missing Vertex Layout (not assigned to GPUBuffer). Vertex Shader won't read valid data resulting incorrect visuals."); } #endif - _commandList->SetPipelineState(_currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout)); + ID3D12PipelineState* pso = _currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout); + ASSERT(pso); + _commandList->SetPipelineState(pso); if (_primitiveTopology != _currentState->PrimitiveTopology) { _primitiveTopology = _currentState->PrimitiveTopology; @@ -1300,7 +1304,7 @@ void GPUContextDX12::SetState(GPUPipelineState* state) } } -void GPUContextDX12::ClearState() +void GPUContextDX12::ResetState() { if (!_commandList) return; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 4bd1b54a1..51f24f4a6 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -201,7 +201,7 @@ public: void SetScissor(const Rectangle& scissorRect) override; GPUPipelineState* GetState() const override; void SetState(GPUPipelineState* state) override; - void ClearState() override; + void ResetState() override; void FlushState() override; void Flush() override; void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 88334be60..4b9298b6c 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -12,6 +12,9 @@ #include "GPUSamplerDX12.h" #include "GPUVertexLayoutDX12.h" #include "GPUSwapChainDX12.h" +#include "RootSignatureDX12.h" +#include "UploadBufferDX12.h" +#include "CommandQueueDX12.h" #include "Engine/Engine/Engine.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Graphics/RenderTask.h" @@ -21,20 +24,23 @@ #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Core/Log.h" #include "Engine/Core/Config/PlatformSettings.h" -#include "UploadBufferDX12.h" -#include "CommandQueueDX12.h" +#include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Utilities.h" #include "Engine/Threading/Threading.h" #include "CommandSignatureDX12.h" static bool CheckDX12Support(IDXGIAdapter* adapter) { +#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE + return true; +#else // Try to create device if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { return true; } return false; +#endif } GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets) @@ -55,6 +61,310 @@ GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& } } +RootSignatureDX12::RootSignatureDX12() +{ + // Clear structures + Platform::MemoryClear(this, sizeof(*this)); + + // Descriptor tables + { + // SRVs + D3D12_DESCRIPTOR_RANGE& range = _ranges[0]; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range.NumDescriptors = GPU_MAX_SR_BINDED; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + { + // UAVs + D3D12_DESCRIPTOR_RANGE& range = _ranges[1]; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + range.NumDescriptors = GPU_MAX_UA_BINDED; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + { + // Samplers + D3D12_DESCRIPTOR_RANGE& range = _ranges[2]; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT; + range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + + // Root parameters + for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++) + { + // CBs + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_CB + i]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.Descriptor.ShaderRegister = i; + param.Descriptor.RegisterSpace = 0; + } + { + // SRVs + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SR]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &_ranges[0]; + } + { + // UAVs + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_UA]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &_ranges[1]; + } + { + // Samplers + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SAMPLER]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &_ranges[2]; + } + + // Static samplers + static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(_staticSamplers), "Update static samplers setup."); + // Linear Clamp + InitSampler(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); + // Point Clamp + InitSampler(1, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); + // Linear Wrap + InitSampler(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP); + // Point Wrap + InitSampler(3, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_WRAP); + // Shadow + InitSampler(4, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL); + // Shadow PCF + InitSampler(5, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL); + + // Init + _desc.NumParameters = ARRAY_COUNT(_parameters); + _desc.pParameters = _parameters; + _desc.NumStaticSamplers = ARRAY_COUNT(_staticSamplers); + _desc.pStaticSamplers = _staticSamplers; + _desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; +} + +void RootSignatureDX12::InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc) +{ + auto& sampler = _staticSamplers[i]; + sampler.Filter = filter; + sampler.AddressU = address; + sampler.AddressV = address; + sampler.AddressW = address; + sampler.MipLODBias = 0.0f; + sampler.MaxAnisotropy = 1; + sampler.ComparisonFunc = comparisonFunc; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; + sampler.MinLOD = 0; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = i; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; +} + +ComPtr RootSignatureDX12::Serialize() const +{ + ComPtr signature; + ComPtr error; + VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&_desc, D3D_ROOT_SIGNATURE_VERSION_1_0, &signature, &error)); + if (error.Get()) + { + LOG(Error, "D3D12SerializeRootSignature failed with error: {}", String((const char*)error->GetBufferPointer())); + } + return signature; +} + +#if USE_EDITOR + +const Char* GetRootSignatureShaderVisibility(D3D12_SHADER_VISIBILITY visibility) +{ + switch (visibility) + { + case D3D12_SHADER_VISIBILITY_VERTEX: + return TEXT(", visibility=SHADER_VISIBILITY_VERTEX"); + case D3D12_SHADER_VISIBILITY_HULL: + return TEXT(", visibility=SHADER_VISIBILITY_HULL"); + case D3D12_SHADER_VISIBILITY_DOMAIN: + return TEXT(", visibility=SHADER_VISIBILITY_DOMAIN"); + case D3D12_SHADER_VISIBILITY_GEOMETRY: + return TEXT(", visibility=SHADER_VISIBILITY_GEOMETRY"); + case D3D12_SHADER_VISIBILITY_PIXEL: + return TEXT(", visibility=SHADER_VISIBILITY_PIXEL"); + case D3D12_SHADER_VISIBILITY_AMPLIFICATION: + return TEXT(", visibility=SHADER_VISIBILITY_AMPLIFICATION"); + case D3D12_SHADER_VISIBILITY_MESH: + return TEXT(", visibility=SHADER_VISIBILITY_MESH"); + case D3D12_SHADER_VISIBILITY_ALL: + default: + return TEXT(""); // Default + } +} + +const Char* GetRootSignatureSamplerFilter(D3D12_FILTER filter) +{ + switch (filter) + { + case D3D12_FILTER_MIN_MAG_MIP_POINT: + return TEXT("FILTER_MIN_MAG_MIP_POINT"); + case D3D12_FILTER_MIN_MAG_MIP_LINEAR: + return TEXT("FILTER_MIN_MAG_MIP_LINEAR"); + case D3D12_FILTER_ANISOTROPIC: + return TEXT("FILTER_ANISOTROPIC"); + case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT: + return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_POINT"); + case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR: + return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_LINEAR"); + default: + CRASH; // Not implemented + } +} + +const Char* GetRootSignatureSamplerAddress(D3D12_TEXTURE_ADDRESS_MODE address) +{ + switch (address) + { + case D3D12_TEXTURE_ADDRESS_MODE_WRAP: + return TEXT("TEXTURE_ADDRESS_WRAP"); + case D3D12_TEXTURE_ADDRESS_MODE_MIRROR: + return TEXT("TEXTURE_ADDRESS_MIRROR"); + case D3D12_TEXTURE_ADDRESS_MODE_CLAMP: + return TEXT("TEXTURE_ADDRESS_CLAMP"); + case D3D12_TEXTURE_ADDRESS_MODE_BORDER: + return TEXT("TEXTURE_ADDRESS_BORDER"); + case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE: + return TEXT("TEXTURE_ADDRESS_MIRROR_ONCE"); + default: + return TEXT(""); + } +} + +const Char* GetRootSignatureSamplerComparisonFunc(D3D12_COMPARISON_FUNC func) +{ + switch (func) + { + case D3D12_COMPARISON_FUNC_NEVER: + return TEXT("COMPARISON_NEVER"); + case D3D12_COMPARISON_FUNC_LESS: + return TEXT("COMPARISON_LESS"); + case D3D12_COMPARISON_FUNC_EQUAL: + return TEXT("COMPARISON_EQUAL"); + case D3D12_COMPARISON_FUNC_LESS_EQUAL: + return TEXT("COMPARISON_LESS_EQUAL"); + case D3D12_COMPARISON_FUNC_GREATER: + return TEXT("COMPARISON_GREATER"); + case D3D12_COMPARISON_FUNC_NOT_EQUAL: + return TEXT("COMPARISON_NOT_EQUAL"); + case D3D12_COMPARISON_FUNC_GREATER_EQUAL: + return TEXT("COMPARISON_GREATER_EQUAL"); + case D3D12_COMPARISON_FUNC_ALWAYS: + default: + return TEXT("COMPARISON_ALWAYS"); + } +} + +void RootSignatureDX12::ToString(StringBuilder& sb, bool singleLine) const +{ + // Flags + sb.Append(TEXT("RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)")); + + // Parameters + const Char newLine = singleLine ? ' ' : '\n'; + for (const D3D12_ROOT_PARAMETER& param : _parameters) + { + const Char* visibility = GetRootSignatureShaderVisibility(param.ShaderVisibility); + switch (param.ParameterType) + { + case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: + sb.AppendFormat(TEXT(",{}DescriptorTable("), newLine); + for (uint32 rangeIndex = 0; rangeIndex < param.DescriptorTable.NumDescriptorRanges; rangeIndex++) + { + if (rangeIndex) + sb.Append(TEXT(", ")); + const D3D12_DESCRIPTOR_RANGE& range = param.DescriptorTable.pDescriptorRanges[rangeIndex]; + switch (range.RangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + sb.AppendFormat(TEXT("SRV(t{}"), range.BaseShaderRegister); + break; + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + sb.AppendFormat(TEXT("UAV(u{}"), range.BaseShaderRegister); + break; + case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: + sb.AppendFormat(TEXT("CBV(b{}"), range.BaseShaderRegister); + break; + case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: + sb.AppendFormat(TEXT("Sampler(s{}"), range.BaseShaderRegister); + break; + } + if (range.NumDescriptors != 1) + { + if (range.NumDescriptors == UINT_MAX) + sb.Append(TEXT(", numDescriptors=unbounded")); + else + sb.AppendFormat(TEXT(", numDescriptors={}"), range.NumDescriptors); + } + if (range.OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + sb.AppendFormat(TEXT(", offset={}"), range.OffsetInDescriptorsFromTableStart); + sb.Append(')'); + } + sb.AppendFormat(TEXT("{})"), visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: + sb.AppendFormat(TEXT(",{}RootConstants(num32BitConstants={}, b{}{})"), newLine, param.Constants.Num32BitValues, param.Constants.ShaderRegister, visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_CBV: + sb.AppendFormat(TEXT(",{}CBV(b{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_SRV: + sb.AppendFormat(TEXT(",{}SRV(t{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_UAV: + sb.AppendFormat(TEXT(",{}UAV(u{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); + break; + } + } + + // Static Samplers + for (const D3D12_STATIC_SAMPLER_DESC& sampler : _staticSamplers) + { + const Char* visibility = GetRootSignatureShaderVisibility(sampler.ShaderVisibility); + sb.AppendFormat(TEXT(",{}StaticSampler(s{}"), newLine, sampler.ShaderRegister); + sb.AppendFormat(TEXT(", filter={}"), GetRootSignatureSamplerFilter(sampler.Filter)); + sb.AppendFormat(TEXT(", addressU={}"), GetRootSignatureSamplerAddress(sampler.AddressU)); + sb.AppendFormat(TEXT(", addressV={}"), GetRootSignatureSamplerAddress(sampler.AddressV)); + sb.AppendFormat(TEXT(", addressW={}"), GetRootSignatureSamplerAddress(sampler.AddressW)); + sb.AppendFormat(TEXT(", comparisonFunc={}"), GetRootSignatureSamplerComparisonFunc(sampler.ComparisonFunc)); + sb.AppendFormat(TEXT(", maxAnisotropy={}"), sampler.MaxAnisotropy); + sb.Append(TEXT(", borderColor=STATIC_BORDER_COLOR_OPAQUE_BLACK")); + sb.AppendFormat(TEXT("{})"), visibility); + } +} + +String RootSignatureDX12::ToString() const +{ + StringBuilder sb; + ToString(sb); + return sb.ToString(); +} + +StringAnsi RootSignatureDX12::ToStringAnsi() const +{ + StringBuilder sb; + ToString(sb); + return sb.ToStringAnsi(); +} + +#endif + GPUDevice* GPUDeviceDX12::Create() { #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE @@ -318,6 +628,7 @@ bool GPUDeviceDX12::Init() VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT); UINT modesCount = 0; +#ifdef _GAMING_XBOX_SCARLETT VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeList(backbufferFormat, 0, &modesCount, NULL)); Array modes; modes.Resize((int32)modesCount); @@ -332,6 +643,11 @@ bool GPUDeviceDX12::Init() videoOutput.RefreshRate = Math::Max(videoOutput.RefreshRate, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator); } modes.Resize(0); +#else + videoOutput.Width = 1920; + videoOutput.Height = 1080; + videoOutput.RefreshRate = 60; +#endif #if PLATFORM_GDK GDKPlatform::Suspended.Bind(this); @@ -561,170 +877,10 @@ bool GPUDeviceDX12::Init() } // Create root signature - // TODO: maybe create set of different root signatures? for UAVs, for compute, for simple drawing, for post fx? { - // Descriptor tables - D3D12_DESCRIPTOR_RANGE r[3]; // SRV+UAV+Sampler - { - D3D12_DESCRIPTOR_RANGE& range = r[0]; - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - range.NumDescriptors = GPU_MAX_SR_BINDED; - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - { - D3D12_DESCRIPTOR_RANGE& range = r[1]; - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - range.NumDescriptors = GPU_MAX_UA_BINDED; - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - { - D3D12_DESCRIPTOR_RANGE& range = r[2]; - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT; - range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - - // Root parameters - D3D12_ROOT_PARAMETER rootParameters[GPU_MAX_CB_BINDED + 3]; - for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++) - { - // CB - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_CB + i]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.Descriptor.ShaderRegister = i; - rootParam.Descriptor.RegisterSpace = 0; - } - { - // SRVs - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SR]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.DescriptorTable.NumDescriptorRanges = 1; - rootParam.DescriptorTable.pDescriptorRanges = &r[0]; - } - { - // UAVs - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_UA]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.DescriptorTable.NumDescriptorRanges = 1; - rootParam.DescriptorTable.pDescriptorRanges = &r[1]; - } - { - // Samplers - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SAMPLER]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.DescriptorTable.NumDescriptorRanges = 1; - rootParam.DescriptorTable.pDescriptorRanges = &r[2]; - } - - // Static samplers - D3D12_STATIC_SAMPLER_DESC staticSamplers[6]; - static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(staticSamplers), "Update static samplers setup."); - // Linear Clamp - staticSamplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[0].MipLODBias = 0.0f; - staticSamplers[0].MaxAnisotropy = 1; - staticSamplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[0].MinLOD = 0; - staticSamplers[0].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[0].ShaderRegister = 0; - staticSamplers[0].RegisterSpace = 0; - staticSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Point Clamp - staticSamplers[1].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - staticSamplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[1].MipLODBias = 0.0f; - staticSamplers[1].MaxAnisotropy = 1; - staticSamplers[1].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[1].MinLOD = 0; - staticSamplers[1].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[1].ShaderRegister = 1; - staticSamplers[1].RegisterSpace = 0; - staticSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Linear Wrap - staticSamplers[2].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[2].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[2].MipLODBias = 0.0f; - staticSamplers[2].MaxAnisotropy = 1; - staticSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[2].MinLOD = 0; - staticSamplers[2].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[2].ShaderRegister = 2; - staticSamplers[2].RegisterSpace = 0; - staticSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Point Wrap - staticSamplers[3].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - staticSamplers[3].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[3].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[3].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[3].MipLODBias = 0.0f; - staticSamplers[3].MaxAnisotropy = 1; - staticSamplers[3].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[3].MinLOD = 0; - staticSamplers[3].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[3].ShaderRegister = 3; - staticSamplers[3].RegisterSpace = 0; - staticSamplers[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Shadow - staticSamplers[4].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT; - staticSamplers[4].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[4].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[4].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[4].MipLODBias = 0.0f; - staticSamplers[4].MaxAnisotropy = 1; - staticSamplers[4].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - staticSamplers[4].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[4].MinLOD = 0; - staticSamplers[4].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[4].ShaderRegister = 4; - staticSamplers[4].RegisterSpace = 0; - staticSamplers[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Shadow PCF - staticSamplers[5].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; - staticSamplers[5].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[5].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[5].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[5].MipLODBias = 0.0f; - staticSamplers[5].MaxAnisotropy = 1; - staticSamplers[5].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - staticSamplers[5].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[5].MinLOD = 0; - staticSamplers[5].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[5].ShaderRegister = 5; - staticSamplers[5].RegisterSpace = 0; - staticSamplers[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - // Init - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; - rootSignatureDesc.NumParameters = ARRAY_COUNT(rootParameters); - rootSignatureDesc.pParameters = rootParameters; - rootSignatureDesc.NumStaticSamplers = ARRAY_COUNT(staticSamplers); - rootSignatureDesc.pStaticSamplers = staticSamplers; - rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; - - // Serialize - ComPtr signature; - ComPtr error; - VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); - - // Create - VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); + RootSignatureDX12 signature; + ComPtr signatureBlob = signature.Serialize(); + VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); } if (TimestampQueryHeap.Init()) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h index 064ed9a01..e9c1cacaa 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h @@ -18,11 +18,6 @@ #define DX12_BACK_BUFFER_COUNT 2 #endif -#define DX12_ROOT_SIGNATURE_CB 0 -#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0) -#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1) -#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2) - class Engine; class WindowsWindow; class GPUContextDX12; @@ -215,7 +210,7 @@ public: /// The graphics device. /// The resource name. GPUResourceDX12(GPUDeviceDX12* device, const StringView& name) - : GPUResourceBase(device, name) + : GPUResourceBase(device, name) { } }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index e7e4c5ca2..eada546e3 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -159,7 +159,7 @@ bool GPUTextureDX12::OnInit() initialState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; // Create texture -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS && 0 D3D12_HEAP_FLAGS heapFlags = useRTV || useDSV ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE; #else D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; @@ -732,7 +732,7 @@ void GPUTextureDX12::initHandles() if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) { PixelFormat stencilFormat; - switch (_dxgiFormatDSV) + switch (static_cast(_dxgiFormatDSV)) { case PixelFormat::D24_UNorm_S8_UInt: srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h new file mode 100644 index 000000000..156b6ae04 --- /dev/null +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h @@ -0,0 +1,33 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Graphics/Config.h" +#include "../IncludeDirectXHeaders.h" + +#define DX12_ROOT_SIGNATURE_CB 0 +#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0) +#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1) +#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2) + +struct RootSignatureDX12 +{ +private: + D3D12_ROOT_SIGNATURE_DESC _desc; + D3D12_DESCRIPTOR_RANGE _ranges[3]; + D3D12_ROOT_PARAMETER _parameters[GPU_MAX_CB_BINDED + 3]; + D3D12_STATIC_SAMPLER_DESC _staticSamplers[6]; + +public: + RootSignatureDX12(); + + ComPtr Serialize() const; +#if USE_EDITOR + void ToString(class StringBuilder& sb, bool singleLine = false) const; + String ToString() const; + StringAnsi ToStringAnsi() const; +#endif + +private: + void InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL); +}; diff --git a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h index 0ea111d24..22786c157 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h +++ b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h @@ -177,7 +177,7 @@ public: { } - void ClearState() override + void ResetState() override { } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 00af99154..979ccc0f8 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -114,7 +114,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue) #if GPU_ENABLE_TRACY #if VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset && !PLATFORM_SWITCH // Use calibrated timestamps extension - if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT) + if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT && _device->PhysicalDeviceFeatures12.hostQueryReset) { _tracyContext = tracy::CreateVkContext(_device->Adapter->Gpu, _device->Device, vkResetQueryPoolEXT, vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, vkGetCalibratedTimestampsEXT); } @@ -1329,7 +1329,7 @@ void GPUContextVulkan::SetState(GPUPipelineState* state) } } -void GPUContextVulkan::ClearState() +void GPUContextVulkan::ResetState() { ResetRenderTarget(); ResetSR(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index 8ed541089..fa94aa139 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -193,7 +193,7 @@ public: void SetScissor(const Rectangle& scissorRect) override; GPUPipelineState* GetState() const override; void SetState(GPUPipelineState* state) override; - void ClearState() override; + void ResetState() override; void FlushState() override; void Flush() override; void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 7c54be973..e96f7b3b3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1568,7 +1568,15 @@ bool GPUDeviceVulkan::Init() vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queueCount, QueueFamilyProps.Get()); // Query device features + RenderToolsVulkan::ZeroStruct(PhysicalDeviceFeatures12, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES); vkGetPhysicalDeviceFeatures(gpu, &PhysicalDeviceFeatures); + if (vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceFeatures2 features2; + RenderToolsVulkan::ZeroStruct(features2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); + features2.pNext = &PhysicalDeviceFeatures12; + vkGetPhysicalDeviceFeatures2(gpu, &features2); + } // Get extensions and layers Array deviceExtensions; @@ -1671,6 +1679,16 @@ bool GPUDeviceVulkan::Init() VulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(PhysicalDeviceFeatures, enabledFeatures); deviceInfo.pEnabledFeatures = &enabledFeatures; +#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset + VkPhysicalDeviceHostQueryResetFeatures resetFeatures; + if (PhysicalDeviceFeatures12.hostQueryReset) + { + RenderToolsVulkan::ZeroStruct(resetFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES); + resetFeatures.hostQueryReset = VK_TRUE; + deviceInfo.pNext = &resetFeatures; + } +#endif + // Create the device VALIDATE_VULKAN_RESULT(vkCreateDevice(gpu, &deviceInfo, nullptr, &Device)); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index ae265100c..09fa93f3e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -496,6 +496,7 @@ public: /// The physical device enabled features. /// VkPhysicalDeviceFeatures PhysicalDeviceFeatures; + VkPhysicalDeviceVulkan12Features PhysicalDeviceFeatures12; Array TimestampQueryPools; Array OcclusionQueryPools; diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index ee66c6c77..f75174f72 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -554,10 +554,11 @@ void AnimatedModel::StopSlotAnimation(const StringView& slotName, Animation* ani { for (auto& slot : GraphInstance.Slots) { - if (slot.Animation == anim && slot.Name == slotName) + if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName) { //slot.Animation = nullptr; // TODO: make an immediate version of this method and set the animation to nullptr. - slot.Reset = true; + if (slot.Animation != nullptr) + slot.Reset = true; break; } } @@ -573,7 +574,7 @@ void AnimatedModel::PauseSlotAnimation(const StringView& slotName, Animation* an { for (auto& slot : GraphInstance.Slots) { - if (slot.Animation == anim && slot.Name == slotName) + if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName) { slot.Pause = true; break; @@ -595,7 +596,7 @@ bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName, Animation { for (auto& slot : GraphInstance.Slots) { - if (slot.Animation == anim && slot.Name == slotName && !slot.Pause) + if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName && !slot.Pause) return true; } return false; @@ -667,7 +668,11 @@ void AnimatedModel::RunBlendShapeDeformer(const MeshBase* mesh, MeshDeformationD { if (q.First == blendShape.Name) { - const float weight = q.Second * blendShape.Weight; + float weight = q.Second; + if (!Math::IsZero(blendShape.Weight)) + weight *= blendShape.Weight; + if (Math::IsZero(weight)) + break; blendShapes.Add(Pair(blendShape, weight)); minVertexIndex = Math::Min(minVertexIndex, blendShape.MinVertexIndex); maxVertexIndex = Math::Max(maxVertexIndex, blendShape.MaxVertexIndex); diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index a011be0d0..a520d6723 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -412,8 +412,8 @@ public: /// Stops the animation playback on the slot in Anim Graph. /// /// The name of the slot. - /// The animation to stop. - API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim); + /// The animation to check. Null to use slot name only. + API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim = nullptr); /// /// Pauses all the animations playback on the all slots in Anim Graph. @@ -424,8 +424,8 @@ public: /// Pauses the animation playback on the slot in Anim Graph. /// /// The name of the slot. - /// The animation to pause. - API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim); + /// The animation to check. Null to use slot name only. + API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim = nullptr); /// /// Checks if any animation playback is active on any slot in Anim Graph (not paused). @@ -436,8 +436,8 @@ public: /// Checks if the animation playback is active on the slot in Anim Graph (not paused). /// /// The name of the slot. - /// The animation to check. - API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim); + /// The animation to check. Null to use slot name only. + API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr); private: void ApplyRootMotion(const Transform& rootMotionDelta); diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.h b/Source/Engine/Level/Actors/EnvironmentProbe.h index 09f771437..4a77a811e 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.h +++ b/Source/Engine/Level/Actors/EnvironmentProbe.h @@ -42,38 +42,38 @@ public: /// /// The reflections texture resolution. /// - API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Quality\")") ProbeCubemapResolution CubemapResolution = ProbeCubemapResolution::UseGraphicsSettings; + /// + /// The probe update mode. + /// + API_FIELD(Attributes = "EditorOrder(10), EditorDisplay(\"Quality\")") + ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; + /// /// The reflections brightness. /// - API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(0), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")") float Brightness = 1.0f; /// /// The probe rendering order. The higher values are render later (on top). /// - API_FIELD(Attributes = "EditorOrder(25), EditorDisplay(\"Probe\")") + API_FIELD(Attributes = "EditorOrder(20), EditorDisplay(\"Probe\")") int32 SortOrder = 0; - /// - /// The probe update mode. - /// - API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Probe\")") - ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; - /// /// The probe capture camera near plane distance. /// - API_FIELD(Attributes="EditorOrder(30), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(25), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")") float CaptureNearPlane = 10.0f; public: /// /// Gets the probe radius. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")") + API_PROPERTY(Attributes="EditorOrder(15), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")") float GetRadius() const; /// diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 25bdc4ce9..d6c2417c8 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -518,7 +518,7 @@ namespace Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, NodeSizeByDistance(nextPos, scaleByDistance)), color, 0.0f, depthTest); const float d = (next->Time - prev->Time) / 3.0f; - DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); + DEBUG_DRAW_BEZIER(prevPos, transform.LocalToWorld(prev->Value.Translation + prev->TangentOut.Translation * d), transform.LocalToWorld(next->Value.Translation + next->TangentIn.Translation * d), nextPos, color, 0.0f, depthTest); prev = next; prevPos = nextPos; } diff --git a/Source/Engine/Main/MainUtil.h b/Source/Engine/Main/MainUtil.h index 668c69c3e..9e1a1503e 100644 --- a/Source/Engine/Main/MainUtil.h +++ b/Source/Engine/Main/MainUtil.h @@ -16,7 +16,7 @@ const Char* GetCommandLine(int argc, char* argv[]) const Char* cmdLine; if (length != 0) { - Char* str = (Char*)malloc(length * sizeof(Char)); + Char* str = (Char*)malloc((length + 1) * sizeof(Char)); cmdLine = str; for (int i = 1; i < argc; i++) { diff --git a/Source/Engine/Networking/NetworkInternal.h b/Source/Engine/Networking/NetworkInternal.h index 7eb4f7d52..2aade3811 100644 --- a/Source/Engine/Networking/NetworkInternal.h +++ b/Source/Engine/Networking/NetworkInternal.h @@ -8,7 +8,7 @@ #endif // Internal version number of networking implementation. Updated once engine changes serialization or connection rules. -#define NETWORK_PROTOCOL_VERSION 4 +#define NETWORK_PROTOCOL_VERSION 5 // Enables encoding object ids and typenames via uint32 keys rather than full data send. #define USE_NETWORK_KEYS 1 @@ -29,6 +29,7 @@ enum class NetworkMessageIDs : uint8 ObjectDespawn, ObjectRole, ObjectRpc, + ObjectRpcPart, MAX, }; @@ -48,6 +49,7 @@ public: static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); static void OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); static void OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); + static void OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); #if COMPILE_WITH_PROFILER diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index 784bbf51e..36ff94c5c 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -391,6 +391,7 @@ namespace NetworkInternal::OnNetworkMessageObjectDespawn, NetworkInternal::OnNetworkMessageObjectRole, NetworkInternal::OnNetworkMessageObjectRpc, + NetworkInternal::OnNetworkMessageObjectRpcPart, }; } diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h index 9db851996..29cf0b30f 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.h +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -100,6 +100,35 @@ API_STRUCT(NoDefault, Namespace="FlaxEngine.Networking") struct FLAXENGINE_API N return Word0 + Word1 != 0; } + NetworkClientsMask operator&(const NetworkClientsMask& other) const + { + return { Word0 & other.Word0, Word1 & other.Word1 }; + } + + NetworkClientsMask operator|(const NetworkClientsMask& other) const + { + return { Word0 | other.Word0, Word1 | other.Word1 }; + } + + NetworkClientsMask operator~() const + { + return { ~Word0, ~Word1 }; + } + + NetworkClientsMask& operator|=(const NetworkClientsMask& other) + { + Word0 |= other.Word0; + Word1 |= other.Word1; + return *this; + } + + NetworkClientsMask& operator&=(const NetworkClientsMask& other) + { + Word0 &= other.Word0; + Word1 &= other.Word1; + return *this; + } + bool operator==(const NetworkClientsMask& other) const { return Word0 == other.Word0 && Word1 == other.Word1; diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index fba916891..e5a7d232e 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -55,14 +55,14 @@ PACK_STRUCT(struct NetworkMessageObjectReplicate uint32 OwnerFrame; }); -PACK_STRUCT(struct NetworkMessageObjectReplicatePayload +PACK_STRUCT(struct NetworkMessageObjectPartPayload { uint16 DataSize; uint16 PartsCount; uint16 PartSize; }); -PACK_STRUCT(struct NetworkMessageObjectReplicatePart +PACK_STRUCT(struct NetworkMessageObjectPart { NetworkMessageIDs ID = NetworkMessageIDs::ObjectReplicatePart; uint32 OwnerFrame; @@ -111,22 +111,11 @@ PACK_STRUCT(struct NetworkMessageObjectRole PACK_STRUCT(struct NetworkMessageObjectRpc { NetworkMessageIDs ID = NetworkMessageIDs::ObjectRpc; - uint16 ArgsSize; + uint32 OwnerFrame; }); struct NetworkReplicatedObject { - ScriptingObjectReference Object; - Guid ObjectId; - Guid ParentId; - uint32 OwnerClientId; - uint32 LastOwnerFrame = 0; - NetworkObjectRole Role; - uint8 Spawned : 1; - uint8 Synced : 1; - DataContainer TargetClientIds; - INetworkObject* AsNetworkObject; - struct { NetworkClientsMask Mask; @@ -139,6 +128,17 @@ struct NetworkReplicatedObject } } RepCache; + ScriptingObjectReference Object; + Guid ObjectId; + Guid ParentId; + DataContainer TargetClientIds; + INetworkObject* AsNetworkObject; + uint32 OwnerClientId; + uint32 LastOwnerFrame = 0; + NetworkObjectRole Role; + uint8 Spawned : 1; + uint8 Synced : 1; + NetworkReplicatedObject() { Spawned = 0; @@ -152,12 +152,12 @@ struct NetworkReplicatedObject bool operator==(const NetworkReplicatedObject& other) const { - return Object == other.Object; + return ObjectId == other.ObjectId; } bool operator==(const ScriptingObject* other) const { - return Object == other; + return other && ObjectId == other->GetID(); } bool operator==(const Guid& other) const @@ -176,19 +176,25 @@ inline uint32 GetHash(const NetworkReplicatedObject& key) return GetHash(key.ObjectId); } +inline uint32 GetHash(const ScriptingObject* key) +{ + return key ? GetHash(key->GetID()) : 0; +} + struct Serializer { NetworkReplicator::SerializeFunc Methods[2]; void* Tags[2]; }; -struct ReplicateItem +struct PartsItem { ScriptingObjectReference Object; Guid ObjectId; uint16 PartsLeft; uint32 OwnerFrame; uint32 OwnerClientId; + const void* Tag; Array Data; }; @@ -220,7 +226,7 @@ struct DespawnItem DataContainer Targets; }; -struct RpcItem +struct RpcSendItem { ScriptingObjectReference Object; NetworkRpcName Name; @@ -233,11 +239,12 @@ namespace { CriticalSection ObjectsLock; HashSet Objects; - Array ReplicationParts; + Array ReplicationParts; + Array RpcParts; Array SpawnParts; Array SpawnQueue; Array DespawnQueue; - Array RpcQueue; + Array RpcQueue; Dictionary IdsRemappingTable; NetworkStream* CachedWriteStream = nullptr; NetworkStream* CachedReadStream = nullptr; @@ -251,6 +258,7 @@ namespace #endif Array DespawnedObjects; uint32 SpawnId = 0; + uint32 RpcId = 0; #if USE_EDITOR void OnScriptsReloading() @@ -505,6 +513,76 @@ void SetupObjectSpawnMessageItem(SpawnItem* e, NetworkMessage& msg) msg.WriteStructure(msgDataItem); } +void SendInParts(NetworkPeer* peer, NetworkChannelType channel, const byte* data, const uint16 dataSize, NetworkMessage& msg, const NetworkRpcName& name, bool toServer, const Guid& objectId, uint32 ownerFrame, NetworkMessageIDs partId) +{ + NetworkMessageObjectPartPayload msgDataPayload; + msgDataPayload.DataSize = dataSize; + const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid); + const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectPartPayload); + const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectPart) - networkKeyIdWorstCaseSize; + uint32 partsCount = 1; + uint32 dataStart = 0; + uint32 msgDataSize = dataSize; + if (dataSize > msgMaxData) + { + // Send msgMaxData within first message + msgDataSize = msgMaxData; + dataStart += msgMaxData; + + // Send rest of the data in separate parts + partsCount += Math::DivideAndRoundUp(dataSize - dataStart, partMaxData); + + // TODO: promote channel to Ordered when using parts? + } + else + dataStart += dataSize; + ASSERT(partsCount <= MAX_uint8); + msgDataPayload.PartsCount = partsCount; + msgDataPayload.PartSize = msgDataSize; + msg.WriteStructure(msgDataPayload); + msg.WriteBytes(data, msgDataSize); + uint32 messageSize = msg.Length; + if (toServer) + peer->EndSendMessage(channel, msg); + else + peer->EndSendMessage(channel, msg, CachedTargets); + + // Send all other parts + for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) + { + NetworkMessageObjectPart msgDataPart; + msgDataPart.ID = partId; + msgDataPart.OwnerFrame = ownerFrame; + msgDataPart.DataSize = msgDataPayload.DataSize; + msgDataPart.PartsCount = msgDataPayload.PartsCount; + msgDataPart.PartStart = dataStart; + msgDataPart.PartSize = Math::Min(dataSize - dataStart, partMaxData); + msg = peer->BeginSendMessage(); + msg.WriteStructure(msgDataPart); + msg.WriteNetworkId(objectId); + msg.WriteBytes(data + msgDataPart.PartStart, msgDataPart.PartSize); + messageSize += msg.Length; + dataStart += msgDataPart.PartSize; + if (toServer) + peer->EndSendMessage(channel, msg); + else + peer->EndSendMessage(channel, msg, CachedTargets); + } + ASSERT_LOW_LAYER(dataStart == dataSize); + +#if COMPILE_WITH_PROFILER + // Network stats recording + if (NetworkInternal::EnableProfiling) + { + auto& profileEvent = NetworkInternal::ProfilerEvents[name]; + profileEvent.Count++; + profileEvent.DataSize += dataSize; + profileEvent.MessageSize += messageSize; + profileEvent.Receivers += toServer ? 1 : CachedTargets.Count(); + } +#endif +} + void SendObjectSpawnMessage(const SpawnGroup& group, const Array& clients) { PROFILE_CPU(); @@ -589,7 +667,7 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli msg.WriteNetworkId(objectId); if (NetworkManager::IsClient()) { - NetworkManager::Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); + peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); } else { @@ -598,6 +676,169 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli } } +void SendDespawn(DespawnItem& e) +{ + NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString()); + NetworkMessageObjectDespawn msgData; + Guid objectId = e.Id; + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(objectId, &objectId); + } + auto peer = NetworkManager::Peer; + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(objectId); + BuildCachedTargets(NetworkManager::Clients, e.Targets); + if (NetworkManager::IsClient()) + peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); + else + peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets); +} + +void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) +{ + auto it = Objects.Find(obj->GetID()); + if (it.IsEnd()) + return; + auto& item = it->Item; + const bool isClient = NetworkManager::IsClient(); + const NetworkClientsMask fullTargetClients = targetClients; + + // If server has no recipients, skip early. + if (!isClient && !targetClients) + return; + + if (item.AsNetworkObject) + item.AsNetworkObject->OnNetworkSerialize(); + + // Serialize object + NetworkStream* stream = CachedWriteStream; + stream->Initialize(); + const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); + if (failed) + { + //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); + return; + } + const uint32 size = stream->GetPosition(); + if (size > MAX_uint16) + { + LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16); + return; + } + +#if USE_NETWORK_REPLICATOR_CACHE + // Process replication cache to skip sending object data if it didn't change + if (item.RepCache.Data.Length() == size && Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) + { + // Check if only newly joined clients are missing this data to avoid resending it to everyone + NetworkClientsMask missingClients = targetClients & ~item.RepCache.Mask; + + // If data is the same and only the client set changed, replicate to missing clients only + if (!missingClients) + return; + targetClients = missingClients; + } + item.RepCache.Mask = fullTargetClients; + item.RepCache.Data.Copy(stream->GetBuffer(), size); +#endif + // TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state) + constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable; + + // Skip serialization of objects that none will receive + if (!isClient) + { + BuildCachedTargets(item, targetClients); + if (CachedTargets.Count() == 0) + return; + } + + // Send object to clients + NetworkMessageObjectReplicate msgData; + msgData.OwnerFrame = NetworkManager::Frame; + Guid objectId = item.ObjectId, parentId = item.ParentId; + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(objectId, &objectId); + IdsRemappingTable.KeyOf(parentId, &parentId); + } + NetworkPeer* peer = NetworkManager::Peer; + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(objectId); + msg.WriteNetworkId(parentId); + msg.WriteNetworkName(obj->GetType().Fullname); + const NetworkRpcName name(obj->GetTypeHandle(), StringAnsiView::Empty); + SendInParts(peer, repChannel, stream->GetBuffer(), size, msg, name, isClient, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectReplicatePart); +} + +void SendRpc(RpcSendItem& e) +{ + ScriptingObject* obj = e.Object.Get(); + if (!obj) + return; + auto it = Objects.Find(obj->GetID()); + if (it == Objects.End()) + { +#if !BUILD_RELEASE + if (!DespawnedObjects.Contains(obj->GetID())) + LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID()); +#endif + return; + } + auto& item = it->Item; + if (e.ArgsData.Length() > MAX_uint16) + { + LOG(Error, "Too much data for object RPC method '{}.{}' on object '{}' ({} bytes provided while limit is {}).", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID(), e.ArgsData.Length(), MAX_uint16); + return; + } + const NetworkManagerMode mode = NetworkManager::Mode; + NetworkPeer* peer = NetworkManager::Peer; + + bool toServer; + if (e.Info.Server && mode == NetworkManagerMode::Client) + { + // Client -> Server +#if USE_NETWORK_REPLICATOR_LOG + if (e.Targets.Length() != 0) + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}.{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); +#endif + toServer = true; + } + else if (e.Info.Client && (mode == NetworkManagerMode::Server || mode == NetworkManagerMode::Host)) + { + // Server -> Client(s) + BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); + if (CachedTargets.IsEmpty()) + return; + toServer = false; + } + else + return; + + // Send RPC message + //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}.{} object ID={}", e.Name.First.ToString(), e.Name.Second.ToString(), item.ToString()); + NetworkMessageObjectRpc msgData; + msgData.OwnerFrame = ++RpcId; + Guid objectId = item.ObjectId; + Guid parentId = item.ParentId; + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(objectId, &objectId); + IdsRemappingTable.KeyOf(parentId, &parentId); + } + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(objectId); + msg.WriteNetworkId(parentId); + msg.WriteNetworkName(obj->GetType().Fullname); + msg.WriteNetworkName(e.Name.First.GetType().Fullname); + msg.WriteNetworkName(e.Name.Second); + NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; + SendInParts(peer, channel, e.ArgsData.Get(), e.ArgsData.Length(), msg, e.Name, toServer, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectRpcPart); +} + void DeleteNetworkObject(ScriptingObject* obj) { // Remove from the mapping table @@ -708,38 +949,43 @@ FORCE_INLINE void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject Hierarchy->DirtyObject(obj); } -ReplicateItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) +PartsItem* AddPartsItem(Array& items, NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) { // Reuse or add part item - ReplicateItem* replicateItem = nullptr; - for (auto& e : ReplicationParts) + PartsItem* item = nullptr; + for (auto& e : items) { if (e.OwnerFrame == ownerFrame && e.Data.Count() == dataSize && e.ObjectId == objectId) { // Reuse - replicateItem = &e; + item = &e; break; } } - if (!replicateItem) + if (!item) { // Add - replicateItem = &ReplicationParts.AddOne(); - replicateItem->ObjectId = objectId; - replicateItem->PartsLeft = partsCount; - replicateItem->OwnerFrame = ownerFrame; - replicateItem->OwnerClientId = senderClientId; - replicateItem->Data.Resize(dataSize); + item = &items.AddOne(); + item->ObjectId = objectId; + item->PartsLeft = partsCount; + item->OwnerFrame = ownerFrame; + item->OwnerClientId = senderClientId; + item->Data.Resize(dataSize); } // Copy part data - ASSERT(replicateItem->PartsLeft > 0); - replicateItem->PartsLeft--; - ASSERT(partStart + partSize <= replicateItem->Data.Count()); + ASSERT(item->PartsLeft > 0); + item->PartsLeft--; + ASSERT(partStart + partSize <= item->Data.Count()); const void* partData = event.Message.SkipBytes(partSize); - Platform::MemoryCopy(replicateItem->Data.Get() + partStart, partData, partSize); + Platform::MemoryCopy(item->Data.Get() + partStart, partData, partSize); - return replicateItem; + return item; +} + +FORCE_INLINE PartsItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) +{ + return AddPartsItem(ReplicationParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId); } void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, byte* data, uint32 dataSize, uint32 senderClientId) @@ -787,6 +1033,24 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b DirtyObjectImpl(item, obj); } +FORCE_INLINE PartsItem* AddObjectRpcItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) +{ + return AddPartsItem(RpcParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId); +} + +void InvokeObjectRpc(const NetworkRpcInfo* info, byte* data, uint32 dataSize, uint32 senderClientId, ScriptingObject* obj) +{ + // Setup message reading stream + if (CachedReadStream == nullptr) + CachedReadStream = New(); + NetworkStream* stream = CachedReadStream; + stream->SenderId = senderClientId; + stream->Initialize(data, dataSize); + + // Execute RPC + info->Execute(obj, stream, info->Tag); +} + void InvokeObjectSpawn(const NetworkMessageObjectSpawn& msgData, const Guid& prefabId, const NetworkMessageObjectSpawnItem* msgDataItems) { ScopeLock lock(ObjectsLock); @@ -1280,7 +1544,21 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) // Register for despawning (batched during update) auto& despawn = DespawnQueue.AddOne(); despawn.Id = obj->GetID(); - despawn.Targets = item.TargetClientIds; + if (item.TargetClientIds.IsValid()) + { + despawn.Targets = item.TargetClientIds; + } + else + { + // Snapshot current recipients to avoid sending despawn to clients that connect later (and never got the spawn) + Array> clientIds; + for (const NetworkClient* client : NetworkManager::Clients) + { + if (client->State == NetworkConnectionState::Connected && client->ClientId != item.OwnerClientId) + clientIds.Add(client->ClientId); + } + despawn.Targets.Copy(clientIds); + } // Prevent spawning for (int32 i = 0; i < SpawnQueue.Count(); i++) @@ -1573,6 +1851,31 @@ void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client) { ScopeLock lock(ObjectsLock); NewClients.Add(client); + + // Ensure cached replication acknowledges the new client without resending to others. + // Clear the new client's bit in RepCache and schedule a near-term replication. + const int32 clientIndex = NetworkManager::Clients.Find(client); + if (clientIndex != -1) + { + const uint64 bitMask = 1ull << (uint64)(clientIndex % 64); + const int32 wordIndex = clientIndex / 64; + for (auto it = Objects.Begin(); it.IsNotEnd(); ++it) + { + auto& item = it->Item; + ScriptingObject* obj = item.Object.Get(); + if (!obj || !item.Spawned || item.Role != NetworkObjectRole::OwnedAuthoritative) + continue; + + // Mark this client as missing cached data + uint64* word = wordIndex == 0 ? &item.RepCache.Mask.Word0 : &item.RepCache.Mask.Word1; + *word &= ~bitMask; + + // Force next replication tick for this object so the new client gets data promptly + if (Hierarchy) + Hierarchy->DirtyObject(obj); + } + } + ASSERT(sizeof(NetworkClientsMask) * 8 >= (uint32)NetworkManager::Clients.Count()); // Ensure that clients mask can hold all of clients } @@ -1652,9 +1955,6 @@ void NetworkInternal::NetworkReplicatorUpdate() if (Objects.Count() == 0) return; const bool isClient = NetworkManager::IsClient(); - const bool isServer = NetworkManager::IsServer(); - const bool isHost = NetworkManager::IsHost(); - NetworkPeer* peer = NetworkManager::Peer; if (!isClient && NewClients.Count() != 0) { @@ -1694,22 +1994,7 @@ void NetworkInternal::NetworkReplicatorUpdate() PROFILE_CPU_NAMED("DespawnQueue"); for (DespawnItem& e : DespawnQueue) { - // Send despawn message - NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString()); - NetworkMessageObjectDespawn msgData; - Guid objectId = e.Id; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(objectId, &objectId); - } - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(objectId); - BuildCachedTargets(NetworkManager::Clients, e.Targets); - if (isClient) - peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); - else - peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets); + SendDespawn(e); } DespawnQueue.Clear(); } @@ -1830,6 +2115,7 @@ void NetworkInternal::NetworkReplicatorUpdate() } } + // TODO: remove items from RpcParts after some TTL to reduce memory usage // TODO: remove items from SpawnParts after some TTL to reduce memory usage // Replicate all owned networked objects with other clients or server @@ -1871,136 +2157,11 @@ void NetworkInternal::NetworkReplicatorUpdate() PROFILE_CPU_NAMED("Replication"); if (CachedWriteStream == nullptr) CachedWriteStream = New(); - NetworkStream* stream = CachedWriteStream; - stream->SenderId = NetworkManager::LocalClientId; + CachedWriteStream->SenderId = NetworkManager::LocalClientId; // TODO: use Job System when replicated objects count is large for (auto& e : CachedReplicationResult->_entries) { - ScriptingObject* obj = e.Object; - auto it = Objects.Find(obj->GetID()); - if (it.IsEnd()) - continue; - auto& item = it->Item; - - // Skip serialization of objects that none will receive - if (!isClient) - { - BuildCachedTargets(item, e.TargetClients); - if (CachedTargets.Count() == 0) - continue; - } - - if (item.AsNetworkObject) - item.AsNetworkObject->OnNetworkSerialize(); - - // Serialize object - stream->Initialize(); - const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); - if (failed) - { - //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); - continue; - } - const uint32 size = stream->GetPosition(); - if (size > MAX_uint16) - { - LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16); - continue; - } - -#if USE_NETWORK_REPLICATOR_CACHE - // Process replication cache to skip sending object data if it didn't change - if (item.RepCache.Data.Length() == size && - item.RepCache.Mask == e.TargetClients && - Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) - { - continue; - } - item.RepCache.Mask = e.TargetClients; - item.RepCache.Data.Copy(stream->GetBuffer(), size); -#endif - // TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state) - constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable; - - // Send object to clients - NetworkMessageObjectReplicate msgData; - msgData.OwnerFrame = NetworkManager::Frame; - Guid objectId = item.ObjectId, parentId = item.ParentId; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(objectId, &objectId); - IdsRemappingTable.KeyOf(parentId, &parentId); - } - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(objectId); - msg.WriteNetworkId(parentId); - msg.WriteNetworkName(obj->GetType().Fullname); - NetworkMessageObjectReplicatePayload msgDataPayload; - msgDataPayload.DataSize = size; - const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid); - const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectReplicatePayload); - const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart) - networkKeyIdWorstCaseSize; - uint32 partsCount = 1; - uint32 dataStart = 0; - uint32 msgDataSize = size; - if (size > msgMaxData) - { - // Send msgMaxData within first message - msgDataSize = msgMaxData; - dataStart += msgMaxData; - - // Send rest of the data in separate parts - partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); - } - else - dataStart += size; - ASSERT(partsCount <= MAX_uint8); - msgDataPayload.PartsCount = partsCount; - msgDataPayload.PartSize = msgDataSize; - msg.WriteStructure(msgDataPayload); - msg.WriteBytes(stream->GetBuffer(), msgDataSize); - uint32 dataSize = msgDataSize, messageSize = msg.Length; - if (isClient) - peer->EndSendMessage(repChannel, msg); - else - peer->EndSendMessage(repChannel, msg, CachedTargets); - - // Send all other parts - for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) - { - NetworkMessageObjectReplicatePart msgDataPart; - msgDataPart.OwnerFrame = msgData.OwnerFrame; - msgDataPart.DataSize = msgDataPayload.DataSize; - msgDataPart.PartsCount = msgDataPayload.PartsCount; - msgDataPart.PartStart = dataStart; - msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); - msg = peer->BeginSendMessage(); - msg.WriteStructure(msgDataPart); - msg.WriteNetworkId(objectId); - msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); - messageSize += msg.Length; - dataSize += msgDataPart.PartSize; - dataStart += msgDataPart.PartSize; - if (isClient) - peer->EndSendMessage(repChannel, msg); - else - peer->EndSendMessage(repChannel, msg, CachedTargets); - } - ASSERT_LOW_LAYER(dataStart == size); - -#if COMPILE_WITH_PROFILER - // Network stats recording - if (EnableProfiling) - { - const Pair name(obj->GetTypeHandle(), StringAnsiView::Empty); - auto& profileEvent = ProfilerEvents[name]; - profileEvent.Count++; - profileEvent.DataSize += dataSize; - profileEvent.MessageSize += messageSize; - profileEvent.Receivers += isClient ? 1 : CachedTargets.Count(); - } -#endif + SendReplication(e.Object, e.TargetClients); } } @@ -2009,70 +2170,7 @@ void NetworkInternal::NetworkReplicatorUpdate() PROFILE_CPU_NAMED("Rpc"); for (auto& e : RpcQueue) { - ScriptingObject* obj = e.Object.Get(); - if (!obj) - continue; - auto it = Objects.Find(obj->GetID()); - if (it == Objects.End()) - { -#if USE_EDITOR || !BUILD_RELEASE - if (!DespawnedObjects.Contains(obj->GetID())) - LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID()); -#endif - continue; - } - auto& item = it->Item; - - // Send RPC message - //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString()); - NetworkMessageObjectRpc msgData; - Guid msgObjectId = item.ObjectId; - Guid msgParentId = item.ParentId; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId); - IdsRemappingTable.KeyOf(msgParentId, &msgParentId); - } - msgData.ArgsSize = (uint16)e.ArgsData.Length(); - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(msgObjectId); - msg.WriteNetworkId(msgParentId); - msg.WriteNetworkName(obj->GetType().Fullname); - msg.WriteNetworkName(e.Name.First.GetType().Fullname); - msg.WriteNetworkName(e.Name.Second); - msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length()); - uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0; - NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; - if (e.Info.Server && isClient) - { - // Client -> Server -#if USE_NETWORK_REPLICATOR_LOG - if (e.Targets.Length() != 0) - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); -#endif - peer->EndSendMessage(channel, msg); - receivers = 1; - } - else if (e.Info.Client && (isServer || isHost)) - { - // Server -> Client(s) - BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); - peer->EndSendMessage(channel, msg, CachedTargets); - receivers = CachedTargets.Count(); - } - -#if COMPILE_WITH_PROFILER - // Network stats recording - if (EnableProfiling && receivers) - { - auto& profileEvent = ProfilerEvents[e.Name]; - profileEvent.Count++; - profileEvent.DataSize += dataSize; - profileEvent.MessageSize += messageSize; - profileEvent.Receivers += receivers; - } -#endif + SendRpc(e); } RpcQueue.Clear(); } @@ -2085,7 +2183,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo { PROFILE_CPU(); NetworkMessageObjectReplicate msgData; - NetworkMessageObjectReplicatePayload msgDataPayload; + NetworkMessageObjectPartPayload msgDataPayload; Guid objectId, parentId; StringAnsiView objectTypeName; event.Message.ReadStructure(msgData); @@ -2095,7 +2193,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo event.Message.ReadStructure(msgDataPayload); ScopeLock lock(ObjectsLock); if (DespawnedObjects.Contains(objectId)) - return; // Skip replicating not-existing objects + return; // Skip replicating non-existing objects NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName); if (!e) return; @@ -2114,7 +2212,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo else { // Add to replication from multiple parts - ReplicateItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); + PartsItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); replicateItem->Object = e->Object; } } @@ -2122,13 +2220,13 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { PROFILE_CPU(); - NetworkMessageObjectReplicatePart msgData; + NetworkMessageObjectPart msgData; Guid objectId; event.Message.ReadStructure(msgData); event.Message.ReadNetworkId(objectId); ScopeLock lock(ObjectsLock); if (DespawnedObjects.Contains(objectId)) - return; // Skip replicating not-existing objects + return; // Skip replicating non-existing objects const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId; AddObjectReplicateItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId); @@ -2230,7 +2328,9 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network } else { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to despawn object {}", objectId); + // If this client never had the object (eg. it was targeted to other clients only), drop the message quietly + DespawnedObjects.Add(objectId); + NETWORK_REPLICATOR_LOG(Warning, "[NetworkReplicator] Failed to despawn object {}", objectId); } } @@ -2288,14 +2388,16 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie { PROFILE_CPU(); NetworkMessageObjectRpc msgData; - Guid msgObjectId, msgParentId; + NetworkMessageObjectPartPayload msgDataPayload; + Guid objectId, parentId; StringAnsiView objectTypeName, rpcTypeName, rpcName; event.Message.ReadStructure(msgData); - event.Message.ReadNetworkId(msgObjectId); - event.Message.ReadNetworkId(msgParentId); + event.Message.ReadNetworkId(objectId); + event.Message.ReadNetworkId(parentId); event.Message.ReadNetworkName(objectTypeName); event.Message.ReadNetworkName(rpcTypeName); event.Message.ReadNetworkName(rpcName); + event.Message.ReadStructure(msgDataPayload); ScopeLock lock(ObjectsLock); // Find RPC info @@ -2305,11 +2407,11 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name); if (!info) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), msgObjectId); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), objectId); return; } - NetworkReplicatedObject* e = ResolveObject(msgObjectId, msgParentId, objectTypeName); + NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName); if (e) { auto& item = *e; @@ -2329,18 +2431,50 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie return; } - // Setup message reading stream - if (CachedReadStream == nullptr) - CachedReadStream = New(); - NetworkStream* stream = CachedReadStream; - stream->SenderId = client ? client->ClientId : NetworkManager::ServerClientId; - stream->Initialize(event.Message.Buffer + event.Message.Position, msgData.ArgsSize); - - // Execute RPC - info->Execute(obj, stream, info->Tag); + const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId; + if (msgDataPayload.PartsCount == 1) + { + // Call RPC + InvokeObjectRpc(info, event.Message.Buffer + event.Message.Position, msgDataPayload.DataSize, senderClientId, obj); + } + else + { + // Add to RPC from multiple parts + PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); + rpcItem->Object = e->Object; + rpcItem->Tag = info; + } } else if (info->Channel != static_cast(NetworkChannelType::Unreliable) && info->Channel != static_cast(NetworkChannelType::UnreliableOrdered)) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgObjectId, String(rpcTypeName), String(rpcName)); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", objectId, String(rpcTypeName), String(rpcName)); + } +} + +void NetworkInternal::OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) +{ + PROFILE_CPU(); + NetworkMessageObjectPart msgData; + Guid objectId; + event.Message.ReadStructure(msgData); + event.Message.ReadNetworkId(objectId); + ScopeLock lock(ObjectsLock); + if (DespawnedObjects.Contains(objectId)) + return; // Skip replicating non-existing objects + + const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId; + PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId); + if (rpcItem && rpcItem->PartsLeft == 0) + { + // Got all parts so invoke RPC + ScriptingObject* obj = rpcItem->Object.Get(); + if (obj) + { + InvokeObjectRpc((const NetworkRpcInfo*)rpcItem->Tag, rpcItem->Data.Get(), rpcItem->Data.Count(), rpcItem->OwnerClientId, obj); + } + + // Remove item + int32 partIndex = (int32)((RpcParts.Get() - rpcItem) / sizeof(rpcItem)); + RpcParts.RemoveAt(partIndex); } } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index a47e15275..1a7ba37d1 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -482,9 +482,15 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, // Function Input case 1: { + // Skip when graph is too small (eg. preview) and fallback with default value from the function graph + if (context.GraphStack.Count() < 2) + { + value = tryGetValue(node->TryGetBox(1), Value::Zero); + break; + } + // Find the function call Node* functionCallNode = nullptr; - ASSERT(context.GraphStack.Count() >= 2); ParticleEmitterGraphCPU* graph; for (int32 i = context.CallStackSize - 1; i >= 0; i--) { diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp index 197b53e13..6ae810d82 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp @@ -425,8 +425,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va case 300: { // Load function asset - const auto function = Assets.LoadAsync((Guid)node->Values[0]); - if (!function || function->WaitForLoaded()) + const auto function = Assets.Load((Guid)node->Values[0]); + if (!function) { OnError(node, box, TEXT("Missing or invalid function.")); value = Value::Zero; @@ -439,7 +439,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va { if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) { - const auto callFunc = Assets.LoadAsync((Guid)_callStack[i]->Values[0]); + const auto callFunc = Assets.Load((Guid)_callStack[i]->Values[0]); if (callFunc == function) { OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString())); @@ -514,7 +514,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupFunction(Box* box, Node* node, Val value = Value::Zero; break; } - const auto function = Assets.LoadAsync((Guid)functionCallNode->Values[0]); + const auto function = Assets.Load((Guid)functionCallNode->Values[0]); if (!_functions.TryGet(functionCallNode, graph) || !function) { OnError(node, box, TEXT("Missing calling function graph.")); diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index d25127e2f..bc1fa9eea 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -156,7 +156,7 @@ void ParticleSystemInstance::Sync(ParticleSystem* system) if (GPUParticlesCountReadback) GPUParticlesCountReadback->ReleaseGPU(); } - ASSERT(Emitters.Count() == system->Emitters.Count()); + CHECK(Emitters.Count() == system->Emitters.Count()); } bool ParticleSystemInstance::ContainsEmitter(ParticleEmitter* emitter) const diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index f93a9bd88..96bd9c7ae 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -194,7 +194,6 @@ void Collider::UpdateLayerBits() // Own layer mask const uint32 mask1 = Physics::LayerMasks[GetLayer()]; - ASSERT(_shape); PhysicsBackend::SetShapeFilterMask(_shape, mask0, mask1); } @@ -244,10 +243,14 @@ void Collider::UpdateGeometry() if (actor) { const auto rigidBody = dynamic_cast(GetParent()); - if (_staticActor != nullptr || (rigidBody && CanAttach(rigidBody))) + if (rigidBody && CanAttach(rigidBody)) { Attach(rigidBody); } + else if (_staticActor != nullptr) + { + PhysicsBackend::AttachShape(_shape, actor); + } else { // Be static triangle mesh diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 8d61e9ba5..3edfaa0c6 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1969,6 +1969,7 @@ void PhysicsBackend::EndSimulateScene(void* scene) scenePhysX->EventsCallback.SendTriggerEvents(); scenePhysX->EventsCallback.SendCollisionEvents(); scenePhysX->EventsCallback.SendJointEvents(); + scenePhysX->EventsCallback.Clear(); } // Clear delta after simulation ended @@ -2786,7 +2787,8 @@ float PhysicsBackend::ComputeShapeSqrDistanceToPoint(void* shape, const Vector3& #if USE_LARGE_WORLDS PxVec3 closestPointPx; float result = PxGeometryQuery::pointDistance(C2P(point), shapePhysX->getGeometry(), trans, &closestPointPx); - *closestPoint = P2C(closestPointPx); + if (closestPoint) + *closestPoint = P2C(closestPointPx); return result; #else return PxGeometryQuery::pointDistance(C2P(point), shapePhysX->getGeometry(), trans, (PxVec3*)closestPoint); @@ -4466,14 +4468,14 @@ void PhysicsBackend::FlushRequests(void* scene) } if (scenePhysX->RemoveColliders.HasItems()) { - for (int32 i = 0; i < scenePhysX->RemoveColliders.Count(); i++) - scenePhysX->EventsCallback.OnColliderRemoved(scenePhysX->RemoveColliders[i]); + for (PhysicsColliderActor* e : scenePhysX->RemoveColliders) + scenePhysX->EventsCallback.OnColliderRemoved(e); scenePhysX->RemoveColliders.Clear(); } if (scenePhysX->RemoveJoints.HasItems()) { - for (int32 i = 0; i < scenePhysX->RemoveJoints.Count(); i++) - scenePhysX->EventsCallback.OnJointRemoved(scenePhysX->RemoveJoints[i]); + for (Joint* e : scenePhysX->RemoveJoints) + scenePhysX->EventsCallback.OnJointRemoved(e); scenePhysX->RemoveJoints.Clear(); } diff --git a/Source/Engine/Physics/PhysicsBackendEmpty.cpp b/Source/Engine/Physics/PhysicsBackendEmpty.cpp index 2e816157b..03973e6fd 100644 --- a/Source/Engine/Physics/PhysicsBackendEmpty.cpp +++ b/Source/Engine/Physics/PhysicsBackendEmpty.cpp @@ -3,6 +3,7 @@ #if COMPILE_WITH_EMPTY_PHYSICS #include "Engine/Core/Log.h" +#include "Engine/Physics/PhysicsBackend.h" #include "Engine/Physics/CollisionData.h" #include "Engine/Physics/PhysicalMaterial.h" #include "Engine/Physics/PhysicsScene.h" @@ -226,26 +227,6 @@ bool PhysicsBackend::CheckConvex(void* scene, const Vector3& center, const Colli return false; } -bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) -{ - return false; -} - -bool PhysicsBackend::OverlapSphere(void* scene, const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers) -{ - return false; -} - -bool PhysicsBackend::OverlapCapsule(void* scene, const Vector3& center, const float radius, const float height, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) -{ - return false; -} - -bool PhysicsBackend::OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) -{ - return false; -} - bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) { return false; @@ -353,7 +334,7 @@ Vector3 PhysicsBackend::GetRigidDynamicActorCenterOfMass(void* actor) return Vector3::Zero; } -void PhysicsBackend::SetRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value) +void PhysicsBackend::AddRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value) { } @@ -698,6 +679,15 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value) { } +Vector3 PhysicsBackend::GetControllerBasePosition(void* controller) +{ + return Vector3::Zero; +} + +void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value) +{ +} + Vector3 PhysicsBackend::GetControllerUpDirection(void* controller) { return Vector3::Up; diff --git a/Source/Engine/Platform/Android/AndroidFileSystem.cpp b/Source/Engine/Platform/Android/AndroidFileSystem.cpp index 930b4017b..27fefb94e 100644 --- a/Source/Engine/Platform/Android/AndroidFileSystem.cpp +++ b/Source/Engine/Platform/Android/AndroidFileSystem.cpp @@ -422,24 +422,8 @@ bool AndroidFileSystem::getFilesFromDirectoryTop(Array& results, const c if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - const int32 fullPathLength = StringUtils::Length(fullPath); - const int32 searchPatternLength = StringUtils::Length(searchPattern); - if (searchPatternLength == 0 || StringUtils::Compare(searchPattern, "*") == 0) - { - // All files - } - else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1) == 0) - { - // Path ending - } - else - { - // TODO: implement all cases in a generic way - continue; - } - - // Add file - results.Add(String(fullPath)); + if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Platform/Base/FileSystemBase.cpp b/Source/Engine/Platform/Base/FileSystemBase.cpp index 1ac5aa6ea..f414bbd01 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.cpp +++ b/Source/Engine/Platform/Base/FileSystemBase.cpp @@ -7,6 +7,7 @@ #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Math.h" +#include "Engine/Core/Log.h" #include "Engine/Engine/Globals.h" bool FileSystemBase::ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array& filenames) @@ -313,3 +314,39 @@ bool FileSystemBase::DirectoryCopyHelper(const String& dst, const String& src, b return false; } + +bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPattern) +{ + // Validate with filter + const int32 pathLength = StringUtils::Length(path); + const int32 searchPatternLength = StringUtils::Length(searchPattern); + if (searchPatternLength == 0 || + StringUtils::Compare(searchPattern, "*") == 0 || + StringUtils::Compare(searchPattern, "*.*") == 0) + { + // All files + return true; + } + else if (searchPattern[0] == '*' && StringUtils::Find(searchPattern + 1, "*") == nullptr) + { + // Path ending + return searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0; + } + else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength - 1] == '*') + { + // Contains pattern + bool match = false; + for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++) + { + int32 len = Math::Min(searchPatternLength - 2, pathLength - i); + if (StringUtils::Compare(&path[i], &searchPattern[1], len) == 0) + return true; + } + } + else + { + // TODO: implement all cases in a generic way + LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented ({})", String(searchPattern)); + } + return false; +} diff --git a/Source/Engine/Platform/Base/FileSystemBase.h b/Source/Engine/Platform/Base/FileSystemBase.h index 844d5fa5f..1a4980d95 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.h +++ b/Source/Engine/Platform/Base/FileSystemBase.h @@ -284,6 +284,6 @@ public: /// Relative path static String ConvertAbsolutePathToRelative(const String& basePath, const String& path); -private: static bool DirectoryCopyHelper(const String& dst, const String& src, bool withSubDirectories); + static bool PathFilterHelper(const char* path, const char* searchPattern); }; diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index e472f7e1a..4512a59b3 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -51,6 +51,7 @@ Array> PlatformBase::Users; Delegate PlatformBase::UserAdded; Delegate PlatformBase::UserRemoved; void* OutOfMemoryBuffer = nullptr; +volatile int64 FatalReporting = 0; const Char* ToString(NetworkConnectionType value) { @@ -306,11 +307,20 @@ int32 PlatformBase::GetCacheLineSize() void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType error) { + // Let only one thread to report the error (and wait for it to end to have valid log before crash) +RETRY: + if (Platform::InterlockedCompareExchange(&FatalReporting, 1, 0) != 0) + { + Platform::Sleep(1); + goto RETRY; + } + // Check if is already during fatal state if (Engine::FatalError != FatalErrorType::None) { // Just send one more error to the log and back LOG(Error, "Error after fatal error: {0}", msg); + Platform::AtomicStore(&FatalReporting, 0); return; } @@ -429,6 +439,8 @@ void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType er } #endif + Platform::AtomicStore(&FatalReporting, 0); + // Show error message if (Engine::ReportCrash.IsBinded()) Engine::ReportCrash(msg, context); diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index a2bd17222..e7996229b 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -18,6 +18,7 @@ #include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Graphics/Textures/TextureData.h" #include "IncludeX11.h" +#include "ThirdParty/X11/Xutil.h" // ICCCM #define WM_NormalState 1L // window normal state @@ -178,6 +179,20 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) X11::XSetTransientForHint(display, window, (X11::Window)((LinuxWindow*)settings.Parent)->GetNativePtr()); } + // Provides class hint for WMs like Hyprland to hook onto and apply window rules + X11::XClassHint* classHint = X11::XAllocClassHint(); + if (classHint) + { + const char* className = settings.IsRegularWindow ? "FlexEditor" : "FlaxPopup"; + + classHint->res_name = const_cast(className); + classHint->res_class = const_cast(className); + + X11::XSetClassHint(display, window, classHint); + + XFree(classHint); + } + _dpi = Platform::GetDpi(); _dpiScale = (float)_dpi / (float)DefaultDPI; diff --git a/Source/Engine/Platform/Unix/UnixFileSystem.cpp b/Source/Engine/Platform/Unix/UnixFileSystem.cpp index 528df5b88..df854c138 100644 --- a/Source/Engine/Platform/Unix/UnixFileSystem.cpp +++ b/Source/Engine/Platform/Unix/UnixFileSystem.cpp @@ -367,43 +367,8 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array& results, const char if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - const int32 fullPathLength = StringUtils::Length(fullPath); - const int32 searchPatternLength = StringUtils::Length(searchPattern); - if (searchPatternLength == 0 || - StringUtils::Compare(searchPattern, "*") == 0 || - StringUtils::Compare(searchPattern, "*.*") == 0) - { - // All files - } - else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) - { - // Path ending - } - else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength-1] == '*') - { - // Contains pattern - bool match = false; - for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++) - { - int32 len = Math::Min(searchPatternLength - 2, pathLength - i); - if (StringUtils::Compare(&entry->d_name[i], &searchPattern[1], len) == 0) - { - match = true; - break; - } - } - if (!match) - continue; - } - else - { - // TODO: implement all cases in a generic way - LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); - continue; - } - - // Add file - results.Add(String(fullPath)); + if (FileSystemBase::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 116cb3deb..42accf298 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -543,11 +543,8 @@ void WindowsPlatform::ReleaseMutex() } } -void WindowsPlatform::PreInit(void* hInstance) +void CheckInstructionSet() { - ASSERT(hInstance); - Instance = hInstance; - #if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64 // Check the minimum vector instruction set support int32 cpuInfo[4] = { -1 }; @@ -597,10 +594,19 @@ void WindowsPlatform::PreInit(void* hInstance) { // Not supported CPU CPUBrand cpu; - Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer))); + Platform::Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer))); exit(-1); } #endif +} +PRAGMA_ENABLE_OPTIMIZATION; + +void WindowsPlatform::PreInit(void* hInstance) +{ + ASSERT(hInstance); + Instance = hInstance; + + CheckInstructionSet(); // Disable the process from being showing "ghosted" while not responding messages during slow tasks DisableProcessWindowsGhosting(); diff --git a/Source/Engine/Renderer/ColorGradingPass.cpp b/Source/Engine/Renderer/ColorGradingPass.cpp index bbb45ef4c..c0b40d3f6 100644 --- a/Source/Engine/Renderer/ColorGradingPass.cpp +++ b/Source/Engine/Renderer/ColorGradingPass.cpp @@ -37,8 +37,45 @@ GPU_CB_STRUCT(Data { Float3 Dummy; float LutWeight; + + void Init(const PostProcessSettings& settings, GPUTexture*& lut) + { + Dummy = Float2::Zero; + auto& toneMapping = settings.ToneMapping; + auto& colorGrading = settings.ColorGrading; + // White Balance + WhiteTemp = toneMapping.WhiteTemperature; + WhiteTint = toneMapping.WhiteTint; + // Shadows + ColorSaturationShadows = colorGrading.ColorSaturationShadows * colorGrading.ColorSaturation; + ColorContrastShadows = colorGrading.ColorContrastShadows * colorGrading.ColorContrast; + ColorGammaShadows = colorGrading.ColorGammaShadows * colorGrading.ColorGamma; + ColorGainShadows = colorGrading.ColorGainShadows * colorGrading.ColorGain; + ColorOffsetShadows = colorGrading.ColorOffsetShadows + colorGrading.ColorOffset; + ColorCorrectionShadowsMax = colorGrading.ShadowsMax; + // Midtones + ColorSaturationMidtones = colorGrading.ColorSaturationMidtones * colorGrading.ColorSaturation; + ColorContrastMidtones = colorGrading.ColorContrastMidtones * colorGrading.ColorContrast; + ColorGammaMidtones = colorGrading.ColorGammaMidtones * colorGrading.ColorGamma; + ColorGainMidtones = colorGrading.ColorGainMidtones * colorGrading.ColorGain; + ColorOffsetMidtones = colorGrading.ColorOffsetMidtones + colorGrading.ColorOffset; + // Highlights + ColorSaturationHighlights = colorGrading.ColorSaturationHighlights * colorGrading.ColorSaturation; + ColorContrastHighlights = colorGrading.ColorContrastHighlights * colorGrading.ColorContrast; + ColorGammaHighlights = colorGrading.ColorGammaHighlights * colorGrading.ColorGamma; + ColorGainHighlights = colorGrading.ColorGainHighlights * colorGrading.ColorGain; + ColorOffsetHighlights = colorGrading.ColorOffsetHighlights + colorGrading.ColorOffset; + ColorCorrectionHighlightsMin = colorGrading.HighlightsMin; + // + Texture* lutTexture = colorGrading.LutTexture.Get(); + const bool useLut = lutTexture && lutTexture->IsLoaded() && lutTexture->GetResidentMipLevels() > 0 && colorGrading.LutWeight > ZeroTolerance; + LutWeight = useLut ? colorGrading.LutWeight : 0.0f; + lut = useLut ? lutTexture->GetTexture() : nullptr; + } }); +Data DefaultData; + // Custom render buffer for caching Color Grading LUT. class ColorGradingCustomBuffer : public RenderBuffers::CustomBuffer { @@ -46,7 +83,10 @@ public: GPUTexture* LUT = nullptr; Data CachedData; ToneMappingMode Mode = ToneMappingMode::None; - Texture* LutTexture = nullptr; + GPUTexture* LutTexture = nullptr; +#if COMPILE_WITH_DEV_ENV + uint64 FrameRendered = 0; +#endif ~ColorGradingCustomBuffer() { @@ -54,6 +94,17 @@ public: } }; +#if COMPILE_WITH_DEV_ENV + +void ColorGradingPass::OnShaderReloading(Asset* obj) +{ + _psLut.Release(); + invalidateResources(); + _reloadedFrame = Engine::FrameCount; +} + +#endif + String ColorGradingPass::ToString() const { return TEXT("ColorGradingPass"); @@ -68,6 +119,9 @@ bool ColorGradingPass::Init() #if COMPILE_WITH_DEV_ENV _shader.Get()->OnReloading.Bind(this); #endif + PostProcessSettings defaultSettings; + GPUTexture* defaultLut; + DefaultData.Init(defaultSettings, defaultLut); return false; } @@ -111,6 +165,18 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) { PROFILE_CPU(); + // Prepare the parameters + Data data; + GPUTexture* lutTexture; + auto& toneMapping = renderContext.List->Settings.ToneMapping; + data.Init(renderContext.List->Settings, lutTexture); + + // Skip if color grading is unsued + if (Platform::MemoryCompare(&DefaultData, &data, sizeof(Data)) == 0 && + lutTexture == nullptr && + toneMapping.Mode == ToneMappingMode::None) + return nullptr; + // Check if can use volume texture (3D) for a LUT (faster on modern platforms, requires geometry shader) const auto device = GPUDevice::Instance; bool use3D = GPU_ALLOW_GEOMETRY_SHADERS && Graphics::PostProcessing::ColorGradingVolumeLUT; @@ -158,42 +224,12 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) RENDER_TARGET_POOL_SET_NAME(colorGradingBuffer.LUT, "ColorGrading.LUT"); } - // Prepare the parameters - Data data; - data.Dummy = Float2::Zero; - auto& toneMapping = renderContext.List->Settings.ToneMapping; - auto& colorGrading = renderContext.List->Settings.ColorGrading; - // White Balance - data.WhiteTemp = toneMapping.WhiteTemperature; - data.WhiteTint = toneMapping.WhiteTint; - // Shadows - data.ColorSaturationShadows = colorGrading.ColorSaturationShadows * colorGrading.ColorSaturation; - data.ColorContrastShadows = colorGrading.ColorContrastShadows * colorGrading.ColorContrast; - data.ColorGammaShadows = colorGrading.ColorGammaShadows * colorGrading.ColorGamma; - data.ColorGainShadows = colorGrading.ColorGainShadows * colorGrading.ColorGain; - data.ColorOffsetShadows = colorGrading.ColorOffsetShadows + colorGrading.ColorOffset; - data.ColorCorrectionShadowsMax = colorGrading.ShadowsMax; - // Midtones - data.ColorSaturationMidtones = colorGrading.ColorSaturationMidtones * colorGrading.ColorSaturation; - data.ColorContrastMidtones = colorGrading.ColorContrastMidtones * colorGrading.ColorContrast; - data.ColorGammaMidtones = colorGrading.ColorGammaMidtones * colorGrading.ColorGamma; - data.ColorGainMidtones = colorGrading.ColorGainMidtones * colorGrading.ColorGain; - data.ColorOffsetMidtones = colorGrading.ColorOffsetMidtones + colorGrading.ColorOffset; - // Highlights - data.ColorSaturationHighlights = colorGrading.ColorSaturationHighlights * colorGrading.ColorSaturation; - data.ColorContrastHighlights = colorGrading.ColorContrastHighlights * colorGrading.ColorContrast; - data.ColorGammaHighlights = colorGrading.ColorGammaHighlights * colorGrading.ColorGamma; - data.ColorGainHighlights = colorGrading.ColorGainHighlights * colorGrading.ColorGain; - data.ColorOffsetHighlights = colorGrading.ColorOffsetHighlights + colorGrading.ColorOffset; - data.ColorCorrectionHighlightsMin = colorGrading.HighlightsMin; - // - Texture* lutTexture = colorGrading.LutTexture.Get(); - const bool useLut = lutTexture && lutTexture->IsLoaded() && lutTexture->GetResidentMipLevels() > 0 && colorGrading.LutWeight > ZeroTolerance; - data.LutWeight = useLut ? colorGrading.LutWeight : 0.0f; - // Check if LUT parameter hasn't been changed since the last time - if (Platform::MemoryCompare(&colorGradingBuffer.CachedData , &data, sizeof(Data)) == 0 && + if (Platform::MemoryCompare(&colorGradingBuffer.CachedData, &data, sizeof(Data)) == 0 && colorGradingBuffer.Mode == toneMapping.Mode && +#if COMPILE_WITH_DEV_ENV + colorGradingBuffer.FrameRendered > _reloadedFrame && +#endif Engine::FrameCount > 30 && // Skip caching when engine is starting TODO: find why this hack is needed colorGradingBuffer.LutTexture == lutTexture) { @@ -203,6 +239,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) colorGradingBuffer.CachedData = data; colorGradingBuffer.Mode = toneMapping.Mode; colorGradingBuffer.LutTexture = lutTexture; +#if COMPILE_WITH_DEV_ENV + colorGradingBuffer.FrameRendered = Engine::FrameCount; +#endif // Render LUT PROFILE_GPU("Color Grading LUT"); @@ -212,7 +251,7 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) context->BindCB(0, cb); context->SetViewportAndScissors((float)lutDesc.Width, (float)lutDesc.Height); context->SetState(_psLut.Get((int32)toneMapping.Mode)); - context->BindSR(0, useLut ? lutTexture->GetTexture() : nullptr); + context->BindSR(0, lutTexture); #if GPU_ALLOW_GEOMETRY_SHADERS if (use3D) { diff --git a/Source/Engine/Renderer/ColorGradingPass.h b/Source/Engine/Renderer/ColorGradingPass.h index 612940e76..dd17e7786 100644 --- a/Source/Engine/Renderer/ColorGradingPass.h +++ b/Source/Engine/Renderer/ColorGradingPass.h @@ -25,11 +25,8 @@ public: private: #if COMPILE_WITH_DEV_ENV - void OnShaderReloading(Asset* obj) - { - _psLut.Release(); - invalidateResources(); - } + uint64 _reloadedFrame = 0; + void OnShaderReloading(Asset* obj); #endif public: diff --git a/Source/Engine/Renderer/DepthOfFieldPass.h b/Source/Engine/Renderer/DepthOfFieldPass.h index b7351c4b1..098c4061e 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.h +++ b/Source/Engine/Renderer/DepthOfFieldPass.h @@ -52,9 +52,12 @@ private: void OnShaderReloading(Asset* obj) { _psDofDepthBlurGeneration->ReleaseGPU(); - _psBokehGeneration->ReleaseGPU(); - _psBokeh->ReleaseGPU(); - _psBokehComposite->ReleaseGPU(); + if (_psBokehGeneration) + _psBokehGeneration->ReleaseGPU(); + if (_psBokeh) + _psBokeh->ReleaseGPU(); + if (_psBokehComposite) + _psBokehComposite->ReleaseGPU(); invalidateResources(); } #endif diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index 3cf44af1b..b539cb212 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -98,6 +98,7 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu if (mode == EyeAdaptationMode::Manual) { // Apply fixed manual exposure + context->ResetSR(); context->SetRenderTarget(*colorBuffer); context->SetViewportAndScissors((float)colorBuffer->Width(), (float)colorBuffer->Height()); context->SetState(_psManual); diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index a954cf31f..25550ecd8 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -11,6 +11,7 @@ #include "Engine/Core/Math/Quaternion.h" #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/Engine.h" +#include "Engine/Engine/Units.h" #include "Engine/Content/Content.h" #include "Engine/Debug/DebugDraw.h" #include "Engine/Graphics/GPUContext.h" @@ -41,6 +42,7 @@ #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 +#define DDGI_PROBE_EMPTY_AREA_DENSITY 8 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count #define DDGI_DEBUG_INSTABILITY 0 // Enables additional probe irradiance instability debugging @@ -68,11 +70,14 @@ GPU_CB_STRUCT(Data0 { Int4 ProbeScrollClears[4]; Float3 ViewDir; float Padding1; + Float3 QuantizationError; + int32 FrameIndexMod8; }); GPU_CB_STRUCT(Data1 { // TODO: use push constants on Vulkan or root signature data on DX12 to reduce overhead of changing single DWORD - Float2 Padding2; + float Padding2; + int32 StepSize; uint32 CascadeIndex; uint32 ProbeIndexOffset; }); @@ -214,6 +219,7 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources() return true; _csClassify = shader->GetCS("CS_Classify"); _csUpdateProbesInitArgs = shader->GetCS("CS_UpdateProbesInitArgs"); + _csUpdateInactiveProbes = shader->GetCS("CS_UpdateInactiveProbes"); _csTraceRays[0] = shader->GetCS("CS_TraceRays", 0); _csTraceRays[1] = shader->GetCS("CS_TraceRays", 1); _csTraceRays[2] = shader->GetCS("CS_TraceRays", 2); @@ -245,6 +251,7 @@ void DynamicDiffuseGlobalIlluminationPass::OnShaderReloading(Asset* obj) LastFrameShaderReload = Engine::FrameCount; _csClassify = nullptr; _csUpdateProbesInitArgs = nullptr; + _csUpdateInactiveProbes = nullptr; _csTraceRays[0] = nullptr; _csTraceRays[1] = nullptr; _csTraceRays[2] = nullptr; @@ -322,7 +329,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont const float indirectLightingIntensity = settings.Intensity; const float probeHistoryWeight = Math::Clamp(settings.TemporalResponse, 0.0f, 0.98f); const float distance = settings.Distance; - const Color fallbackIrradiance = settings.FallbackIrradiance; // Automatically calculate amount of cascades to cover the GI distance at the current probes spacing const int32 idealProbesCount = 20; // Ideal amount of probes per-cascade to try to fit in order to cover whole distance @@ -335,7 +341,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont } // Calculate the probes count based on the amount of cascades and the distance to cover - const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.0f, 10.0f }; // Scales each cascade further away from the camera origin + const float cascadesDistanceScales[] = { 1.0f, 3.0f, 5.0f, 10.0f }; // Scales each cascade further away from the camera origin const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1]; const float verticalRangeScale = 0.8f; // Scales the probes volume size at Y axis (horizontal aspect ratio makes the DDGI use less probes vertically to cover whole screen) Int3 probesCounts(Float3::Ceil(Float3(distanceExtent, distanceExtent * verticalRangeScale, distanceExtent) / probesSpacing)); @@ -351,6 +357,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont // Initialize cascades float probesSpacings[4]; Float3 viewOrigins[4]; + Float3 blendOrigins[4]; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Each cascade has higher spacing between probes @@ -361,14 +368,15 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont // Calculate view origin for cascade by shifting it towards the view direction to account for better view frustum coverage Float3 viewOrigin = renderContext.View.Position; Float3 viewDirection = renderContext.View.Direction; - const Float3 probesDistance = Float3(probesCounts) * cascadeProbesSpacing; + const Float3 probesDistance = Float3(probesCounts - 1) * cascadeProbesSpacing; const float probesDistanceMax = probesDistance.MaxValue(); const Float3 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance); const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.6f; viewOrigin += viewDirection * viewOriginOffset; + //viewOrigin = Float3::Zero; + blendOrigins[cascadeIndex] = viewOrigin; const float viewOriginSnapping = cascadeProbesSpacing; viewOrigin = Float3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; - //viewOrigin = Float3::Zero; viewOrigins[cascadeIndex] = viewOrigin; } @@ -500,6 +508,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont { auto& cascade = ddgiData.Cascades[cascadeIndex]; ddgiData.Result.Constants.ProbesOriginAndSpacing[cascadeIndex] = Float4(cascade.ProbesOrigin, cascade.ProbesSpacing); + ddgiData.Result.Constants.BlendOrigin[cascadeIndex] = Float4(blendOrigins[cascadeIndex], 0.0f); ddgiData.Result.Constants.ProbesScrollOffsets[cascadeIndex] = Int4(cascade.ProbeScrollOffsets, 0); } ddgiData.Result.Constants.RayMaxDistance = distance; @@ -508,7 +517,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont ddgiData.Result.Constants.ProbeHistoryWeight = probeHistoryWeight; ddgiData.Result.Constants.IrradianceGamma = 1.5f; ddgiData.Result.Constants.IndirectLightingIntensity = indirectLightingIntensity; - ddgiData.Result.Constants.FallbackIrradiance = fallbackIrradiance.ToFloat3() * fallbackIrradiance.A; + ddgiData.Result.Constants.FallbackIrradiance = settings.FallbackIrradiance.ToFloat4(); ddgiData.Result.ProbesData = ddgiData.ProbesData->View(); ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View(); ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View(); @@ -535,6 +544,8 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f; data.ViewDir = renderContext.View.Direction; data.SkyboxIntensity = renderContext.List->Sky ? renderContext.List->Sky->GetIndirectLightingIntensity() : 1.0f; + data.QuantizationError = RenderTools::GetColorQuantizationError(ddgiData.ProbesIrradiance->Format()); + data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8); GBufferPass::SetInputs(renderContext.View, data.GBuffer); context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); @@ -581,6 +592,23 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont context->ResetUA(); } + // For inactive probes, search nearby ones to find the closest valid for quick fallback when sampling irradiance + { + PROFILE_GPU_CPU_NAMED("Update Inactive Probes"); + // TODO: this could run within GPUComputePass during Trace Rays or Update Probes to overlap compute works + context->BindUA(0, ddgiData.Result.ProbesData); + Data1 data; + data.CascadeIndex = cascadeIndex; + int32 iterations = Math::CeilToInt(Math::Log2((float)Math::Min(probesCounts.MaxValue(), DDGI_PROBE_EMPTY_AREA_DENSITY) + 1.0f)); + for (int32 i = iterations - 1; i >= 0; i--) + { + data.StepSize = Math::FloorToInt(Math::Pow(2, (float)i) + 0.5f); // Jump Flood step size + context->UpdateCB(_cb1, &data); + context->Dispatch(_csUpdateInactiveProbes, threadGroupsX, 1, 1); + } + context->ResetUA(); + } + // Update probes in batches so ProbesTrace texture can be smaller uint32 arg = 0; // TODO: use rays allocator to dispatch raytracing in packets (eg. 8 threads in a group instead of hardcoded limit) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index e6ace0373..5953da887 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -15,7 +15,8 @@ public: // Constant buffer data for DDGI access on a GPU. GPU_CB_STRUCT(ConstantsData { Float4 ProbesOriginAndSpacing[4]; - Int4 ProbesScrollOffsets[4]; + Float4 BlendOrigin[4]; // w is unused + Int4 ProbesScrollOffsets[4]; // w is unused uint32 ProbesCounts[3]; uint32 CascadesCount; float IrradianceGamma; @@ -24,8 +25,7 @@ public: float IndirectLightingIntensity; Float3 ViewPos; uint32 RaysCount; - Float3 FallbackIrradiance; - float Padding0; + Float4 FallbackIrradiance; }); // Binding data for the GPU. @@ -44,6 +44,7 @@ private: GPUConstantBuffer* _cb1 = nullptr; GPUShaderProgramCS* _csClassify; GPUShaderProgramCS* _csUpdateProbesInitArgs; + GPUShaderProgramCS* _csUpdateInactiveProbes; GPUShaderProgramCS* _csTraceRays[4]; GPUShaderProgramCS* _csUpdateProbesIrradiance; GPUShaderProgramCS* _csUpdateProbesDistance; diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 1c1b9e4e3..7216a8fa8 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -428,6 +428,7 @@ public: // Write to objects buffer (this must match unpacking logic in HLSL) uint32 objectAddress = ObjectsBuffer.Data.Count() / sizeof(Float4); ObjectsListBuffer.Write(objectAddress); + ObjectsBuffer.Data.EnsureCapacity(ObjectsBuffer.Data.Count() + sizeof(Float4) * (GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE + 6 * GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE)); auto* objectData = ObjectsBuffer.WriteReserve(GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE); objectData[0] = Float4(object.Position, object.Radius); objectData[1] = Float4::Zero; @@ -511,6 +512,7 @@ public: { // Dirty object to redraw object->LastFrameUpdated = 0; + return; } GlobalSurfaceAtlasLight* light = Lights.TryGet(a->GetID()); if (light) @@ -551,9 +553,6 @@ bool GlobalSurfaceAtlasPass::Init() // Check platform support const auto device = GPUDevice::Instance; _supported = device->GetFeatureLevel() >= FeatureLevel::SM5 && device->Limits.HasCompute && device->Limits.HasTypedUAVLoad; -#if PLATFORM_APPLE_FAMILY - _supported = false; // Vulkan over Metal has some issues in complex scenes with DDGI -#endif return false; } diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 3077e4cdb..f00df522c 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -30,6 +30,9 @@ GPU_CB_STRUCT(Data { Float2 Input0SizeInv; Float2 Input2SizeInv; + + Float3 PrevWorldOriginOffset; + float Dummy1; }); MotionBlurPass::MotionBlurPass() @@ -194,6 +197,7 @@ void MotionBlurPass::RenderMotionVectors(RenderContext& renderContext) Matrix::Transpose(renderContext.View.ViewProjection(), data.CurrentVP); Matrix::Transpose(renderContext.View.PrevViewProjection, data.PreviousVP); data.TemporalAAJitter = renderContext.View.TemporalAAJitter; + data.PrevWorldOriginOffset = renderContext.View.Origin - renderContext.View.PrevOrigin; auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index a9eba14d2..5ac204523 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -269,7 +269,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, int32 bloomMipCount = CalculateBloomMipCount(w1, h1); // Ensure to have valid data and if at least one effect should be applied - if (!(useBloom || useToneMapping || useCameraArtifacts) || checkIfSkipPass() || w8 <= 1 || h8 <= 1) + if (!(useBloom || useToneMapping || useCameraArtifacts || colorGradingLUT) || checkIfSkipPass() || w8 <= 1 || h8 <= 1) { // Resources are missing. Do not perform rendering. Just copy raw frame context->SetViewportAndScissors((float)output->Width(), (float)output->Height()); @@ -375,6 +375,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, RENDER_TARGET_POOL_SET_NAME(bloomBuffer1, "PostProcessing.Bloom"); RENDER_TARGET_POOL_SET_NAME(bloomBuffer2, "PostProcessing.Bloom"); + // TODO: skip this clear? or do it at once for the whole textures (2 calls instead of per-mip) for (int32 mip = 0; mip < bloomMipCount; mip++) { context->Clear(bloomBuffer1->View(0, mip), Color::Transparent); diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index eaf7a53ca..4c0a43cb6 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -509,7 +509,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) // Render frame Renderer::Render(_task); - context->ClearState(); + context->ResetState(); // Copy frame to cube face { @@ -568,7 +568,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) } // Cleanup - context->ClearState(); + context->ResetState(); if (_workStep < 7) return; // Continue rendering next frame diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index bb580aaf3..544438bb5 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -920,6 +920,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL constexpr int32 vbMax = ARRAY_COUNT(DrawCall::Geometry.VertexBuffers); if (useInstancing) { + context->UpdateCB(perDrawCB, &perDraw); GPUBuffer* vb[vbMax + 1]; uint32 vbOffsets[vbMax + 1]; vb[3] = _instanceBuffer.GetBuffer(); // Pass object index in a vertex stream at slot 3 (used by VS in Surface.shader) @@ -1057,7 +1058,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL materialBinds += list.PreBatchedDrawCalls.Count(); if (list.Batches.IsEmpty() && list.Indices.Count() != 0) { - // Draw calls list has bot been batched so execute draw calls separately + // Draw calls list has not been batched so execute draw calls separately for (int32 j = 0; j < list.Indices.Count(); j++) { perDraw.DrawObjectIndex = listData[j]; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 7a3ac867a..8eb3540e0 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -273,7 +273,7 @@ struct DrawCallsList /// /// True if draw calls batches list can be rendered using hardware instancing, otherwise false. /// - bool CanUseInstancing; + bool CanUseInstancing = true; void Clear(); bool IsEmpty() const; diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 278c8237d..fd7d43c8b 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -200,7 +200,7 @@ void Renderer::Render(SceneRenderTask* task) // Prepare GPU context auto context = GPUDevice::Instance->GetMainContext(); - context->ClearState(); + context->ResetState(); context->FlushState(); const Viewport viewport = task->GetViewport(); context->SetViewportAndScissors(viewport); @@ -402,6 +402,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont case ViewMode::MaterialComplexity: case ViewMode::Wireframe: case ViewMode::NoPostFx: + case ViewMode::VertexColors: + case ViewMode::QuadOverdraw: setup.UseTemporalAAJitter = false; break; } diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 07feedf10..4d26e678b 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -477,8 +477,8 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba } // Duplicate vtable - Script.VTable = (void**)((byte*)Platform::Allocate(totalSize, 16) + prefixSize); - Utilities::UnsafeMemoryCopy((byte*)Script.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); + void** scriptVTable = (void**)((byte*)Platform::Allocate(totalSize, 16) + prefixSize); + Utilities::UnsafeMemoryCopy((byte*)scriptVTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); // Override vtable entries if (interfacesCount) @@ -492,7 +492,7 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba if (eType.Script.SetupScriptObjectVTable) { // Override vtable entries for this class - eType.Script.SetupScriptObjectVTable(Script.ScriptVTable, Script.ScriptVTableBase, Script.VTable, entriesCount, wrapperIndex); + eType.Script.SetupScriptObjectVTable(Script.ScriptVTable, Script.ScriptVTableBase, scriptVTable, entriesCount, wrapperIndex); } auto interfaces = eType.Interfaces; @@ -511,13 +511,13 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba const int32 interfaceSize = interfaceCount * sizeof(void*); // Duplicate interface vtable - Utilities::UnsafeMemoryCopy((byte*)Script.VTable + interfaceOffset, (byte*)vtableInterface - prefixSize, prefixSize + interfaceSize); + Utilities::UnsafeMemoryCopy((byte*)scriptVTable + interfaceOffset, (byte*)vtableInterface - prefixSize, prefixSize + interfaceSize); // Override interface vtable entries const auto scriptOffset = interfaces->ScriptVTableOffset; const auto nativeOffset = interfaceOffset + prefixSize; - void** interfaceVTable = (void**)((byte*)Script.VTable + nativeOffset); - interfaceType.Interface.SetupScriptObjectVTable(Script.ScriptVTable + scriptOffset, Script.ScriptVTableBase + scriptOffset, interfaceVTable, interfaceCount, wrapperIndex); + void** interfaceVTable = (void**)((byte*)scriptVTable + nativeOffset); + interfaceType.Interface.SetupScriptObjectVTable(scriptVTable + scriptOffset, Script.ScriptVTableBase + scriptOffset, interfaceVTable, interfaceCount, wrapperIndex); Script.InterfacesOffsets[interfacesCount++] = (uint16)nativeOffset; interfaceOffset += prefixSize + interfaceSize; @@ -527,6 +527,9 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba } e = eType.GetBaseType(); } + + // Assign once it's ready + Script.VTable = scriptVTable; } void ScriptingType::HackObjectVTable(void* object, ScriptingTypeHandle baseTypeHandle, int32 wrapperIndex) diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index b72993234..4535952ea 100644 --- a/Source/Engine/Scripting/Internal/InternalCalls.h +++ b/Source/Engine/Scripting/Internal/InternalCalls.h @@ -9,7 +9,7 @@ #if defined(__clang__) // Helper utility to override vtable entry with automatic restore -// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method +// See BindingsGenerator.Cpp.cs that generates virtual method wrappers for scripting to properly call overriden base method struct FLAXENGINE_API VTableFunctionInjector { void** VTableAddr; @@ -100,3 +100,50 @@ T& InternalGetReference(T* obj) DebugLog::ThrowNullReference(); return *obj; } + +#ifdef USE_MONO_AOT_COOP + +// Cooperative Suspend - where threads suspend themselves when the runtime requests it. +// https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/ +typedef struct _MonoStackData { + void* stackpointer; + const char* function_name; +} MonoStackData; +#if BUILD_DEBUG +#define MONO_STACKDATA(x) MonoStackData x = { &x, __func__ } +#else +#define MONO_STACKDATA(x) MonoStackData x = { &x, NULL } +#endif +#define MONO_THREAD_INFO_TYPE struct MonoThreadInfo +DLLIMPORT extern "C" MONO_THREAD_INFO_TYPE* mono_thread_info_attach(void); +DLLIMPORT extern "C" void* mono_threads_enter_gc_safe_region_with_info(MONO_THREAD_INFO_TYPE* info, MonoStackData* stackdata); +DLLIMPORT extern "C" void mono_threads_exit_gc_safe_region_internal(void* cookie, MonoStackData* stackdata); +#ifndef _MONO_UTILS_FORWARD_ +typedef struct _MonoDomain MonoDomain; +DLLIMPORT extern "C" MonoDomain* mono_domain_get(void); +#endif +#define MONO_ENTER_GC_SAFE \ + do { \ + MONO_STACKDATA(__gc_safe_dummy); \ + void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_internal(&__gc_safe_dummy) +#define MONO_EXIT_GC_SAFE \ + mono_threads_exit_gc_safe_region_internal(__gc_safe_cookie, &__gc_safe_dummy); \ + } while (0) +#define MONO_ENTER_GC_SAFE_WITH_INFO(info) \ + do { \ + MONO_STACKDATA(__gc_safe_dummy); \ + void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_with_info((info), &__gc_safe_dummy) +#define MONO_EXIT_GC_SAFE_WITH_INFO MONO_EXIT_GC_SAFE +#define MONO_THREAD_INFO_GET(info) if (!info && mono_domain_get()) info = mono_thread_info_attach() + +#else + +#define MONO_THREAD_INFO_TYPE void +#define MONO_ENTER_GC_SAFE +#define MONO_EXIT_GC_SAFE +#define MONO_ENTER_GC_SAFE_WITH_INFO(info) +#define MONO_EXIT_GC_SAFE_WITH_INFO +#define MONO_THREAD_INFO_GET(info) +#define mono_thread_info_attach() nullptr + +#endif diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.h b/Source/Engine/Scripting/ManagedCLR/MClass.h index 9484a2c44..61273dae7 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.h +++ b/Source/Engine/Scripting/ManagedCLR/MClass.h @@ -23,6 +23,10 @@ private: StringAnsiView _fullname; uint32 _types = 0; mutable uint32 _size = 0; +#else + StringAnsiView _name; + StringAnsiView _namespace; + StringAnsiView _fullname; #endif MAssembly* _assembly; diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index 4300434e3..350cc39d2 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -21,6 +21,7 @@ MDomain* MRootDomain = nullptr; MDomain* MActiveDomain = nullptr; Array> MDomains; +bool MCore::Ready = false; MClass* MCore::TypeCache::Void = nullptr; MClass* MCore::TypeCache::Object = nullptr; @@ -301,6 +302,11 @@ bool MProperty::IsStatic() const return false; } +void MCore::OnManagedEventAfterShutdown(const char* eventName) +{ + LOG(Error, "Found a binding leak on '{}' event used by C# scripting after shutdown. Ensure to unregister scripting events from objects during disposing.", ::String(eventName)); +} + MDomain* MCore::GetRootDomain() { return MRootDomain; diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index cedebe7b8..1f972af0d 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -57,6 +57,10 @@ public: static void UnloadScriptingAssemblyLoadContext(); #endif + // Utility for guarding against using C# scripting runtime after shutdown (eg. when asset delegate is not properly disposed). + static bool Ready; + static void OnManagedEventAfterShutdown(const char* eventName); + public: /// /// Utilities for C# object management. diff --git a/Source/Engine/Scripting/ManagedCLR/MEvent.h b/Source/Engine/Scripting/ManagedCLR/MEvent.h index 9d8551774..e5ef1d6df 100644 --- a/Source/Engine/Scripting/ManagedCLR/MEvent.h +++ b/Source/Engine/Scripting/ManagedCLR/MEvent.h @@ -19,6 +19,8 @@ protected: #elif USE_NETCORE void* _handle; StringAnsiView _name; +#else + StringAnsiView _name; #endif mutable MMethod* _addMethod; diff --git a/Source/Engine/Scripting/ManagedCLR/MField.h b/Source/Engine/Scripting/ManagedCLR/MField.h index 66213d6ab..9ef5271a5 100644 --- a/Source/Engine/Scripting/ManagedCLR/MField.h +++ b/Source/Engine/Scripting/ManagedCLR/MField.h @@ -24,6 +24,8 @@ protected: void* _type; int32 _fieldOffset; StringAnsiView _name; +#else + StringAnsiView _name; #endif MClass* _parentClass; diff --git a/Source/Engine/Scripting/ManagedCLR/MMethod.h b/Source/Engine/Scripting/ManagedCLR/MMethod.h index 700fa9593..51264d1b8 100644 --- a/Source/Engine/Scripting/ManagedCLR/MMethod.h +++ b/Source/Engine/Scripting/ManagedCLR/MMethod.h @@ -30,6 +30,8 @@ protected: mutable void* _returnType; mutable Array> _parameterTypes; void CacheSignature() const; +#else + StringAnsiView _name; #endif MClass* _parentClass; MVisibility _visibility; diff --git a/Source/Engine/Scripting/ManagedCLR/MProperty.h b/Source/Engine/Scripting/ManagedCLR/MProperty.h index e48d98375..7ef7e97dd 100644 --- a/Source/Engine/Scripting/ManagedCLR/MProperty.h +++ b/Source/Engine/Scripting/ManagedCLR/MProperty.h @@ -22,6 +22,8 @@ protected: #elif USE_NETCORE void* _handle; StringAnsiView _name; +#else + StringAnsiView _name; #endif mutable MMethod* _getMethod; diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index f69e0c060..1c8c2bcdd 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -316,7 +316,8 @@ bool MCore::LoadEngine() char* buildInfo = CallStaticMethod(GetStaticMethodPointer(TEXT("GetRuntimeInformation"))); LOG(Info, ".NET runtime version: {0}", ::String(buildInfo)); - MCore::GC::FreeMemory(buildInfo); + GC::FreeMemory(buildInfo); + Ready = true; return false; } @@ -327,6 +328,7 @@ void MCore::UnloadEngine() return; PROFILE_CPU(); CallStaticMethod(GetStaticMethodPointer(TEXT("Exit"))); + Ready = false; MDomains.ClearDelete(); MRootDomain = nullptr; ShutdownHostfxr(); @@ -1798,6 +1800,33 @@ bool InitHostfxr() get_hostfxr_params.dotnet_root = dotnetRoot.Get(); } #endif + String platformStr; + switch (PLATFORM_TYPE) + { + case PlatformType::Windows: + case PlatformType::UWP: + if (PLATFORM_ARCH == ArchitectureType::x64) + platformStr = "Windows x64"; + else if (PLATFORM_ARCH == ArchitectureType::ARM64) + platformStr = "Windows ARM64"; + else + platformStr = "Windows x86"; + break; + case PlatformType::Linux: + platformStr = PLATFORM_ARCH_ARM64 ? "Linux ARM64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86"; + break; + case PlatformType::Mac: + platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS ARM64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86"; + break; + default: + if (PLATFORM_ARCH == ArchitectureType::x64) + platformStr = "x64"; + else if (PLATFORM_ARCH == ArchitectureType::ARM64) + platformStr = "ARM64"; + else + platformStr = "x86"; + } + char_t hostfxrPath[1024]; size_t hostfxrPathSize = sizeof(hostfxrPath) / sizeof(char_t); int rc = get_hostfxr_path(hostfxrPath, &hostfxrPathSize, &get_hostfxr_params); @@ -1810,9 +1839,9 @@ bool InitHostfxr() Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet")); #endif #if USE_EDITOR - LOG(Fatal, "Missing .NET 8 or later SDK installation required to run Flax Editor."); + LOG(Fatal, "Missing .NET 8 or later SDK installation for {0} is required to run Flax Editor.", platformStr); #else - LOG(Fatal, "Missing .NET 8 or later Runtime installation required to run this application."); + LOG(Fatal, "Missing .NET 8 or later Runtime installation for {0} is required to run this application.", platformStr); #endif return true; } @@ -1870,27 +1899,6 @@ bool InitHostfxr() hostfxr_close(handle); if (rc == 0x80008096) // FrameworkMissingFailure { - String platformStr; - switch (PLATFORM_TYPE) - { - case PlatformType::Windows: - case PlatformType::UWP: - if (PLATFORM_ARCH == ArchitectureType::x64) - platformStr = "Windows x64"; - else if (PLATFORM_ARCH == ArchitectureType::ARM64) - platformStr = "Windows ARM64"; - else - platformStr = "Windows x86"; - break; - case PlatformType::Linux: - platformStr = PLATFORM_ARCH_ARM64 ? "Linux ARM64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86"; - break; - case PlatformType::Mac: - platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS ARM64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86"; - break; - default:; - platformStr = ""; - } LOG(Fatal, "Failed to resolve compatible .NET runtime version in '{0}'. Make sure the correct platform version for runtime is installed ({1})", platformStr, String(init_params.dotnet_root)); } else @@ -2022,13 +2030,13 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname) String fileName = name; if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe"))) fileName += TEXT(".dll"); - String path = fileName; + String path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName; if (!FileSystem::FileExists(path)) { path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName; if (!FileSystem::FileExists(path)) { - path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName; + path = fileName; } } @@ -2129,6 +2137,53 @@ static void* OnMonoDlFallbackClose(void* handle, void* user_data) #endif +#ifdef USE_MONO_AOT_MODULE + +#include "Engine/Threading/ThreadPoolTask.h" +#include "Engine/Engine/EngineService.h" + +class MonoAotPreloadTask : public ThreadPoolTask +{ +public: + bool Run() override; +}; + +// Preloads in-build AOT dynamic module in async +class MonoAotPreloadService : public EngineService +{ +public: + volatile int64 Ready = 0; + void* Library = nullptr; + + MonoAotPreloadService() + : EngineService(TEXT("AOT Preload"), -800) + { + } + + bool Init() override + { + New()->Start(); + return false; + } +}; + +MonoAotPreloadService MonoAotPreloadServiceInstance; + +bool MonoAotPreloadTask::Run() +{ + // Load AOT module + Stopwatch aotModuleLoadStopwatch; + //LOG(Info, "Loading Mono AOT module..."); + MonoAotPreloadServiceInstance.Library = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE)); + aotModuleLoadStopwatch.Stop(); + LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds()); + + Platform::AtomicStore(&MonoAotPreloadServiceInstance.Ready, 1); + return false; +} + +#endif + bool InitHostfxr() { #if DOTNET_HOST_MONO_DEBUG @@ -2139,7 +2194,13 @@ bool InitHostfxr() #endif // Adjust GC threads suspending mode to not block attached native threads (eg. Job System) + // https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/ +#if USE_MONO_AOT_COOP + Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("coop")); + Platform::SetEnvironmentVariable(TEXT("MONO_SLEEP_ABORT_LIMIT"), TEXT("5000")); // in ms +#else Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("preemptive")); +#endif #if defined(USE_MONO_AOT_MODE) // Enable AOT mode (per-platform) @@ -2147,16 +2208,18 @@ bool InitHostfxr() #endif // Platform-specific setup -#if PLATFORM_IOS || PLATFORM_SWITCH - setenv("MONO_AOT_MODE", "aot", 1); - setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", 1); +#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5 + Platform::SetEnvironmentVariable(TEXT("MONO_AOT_MODE"), TEXT("aot")); + Platform::SetEnvironmentVariable(TEXT("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"), TEXT("1")); #endif #ifdef USE_MONO_AOT_MODULE - // Load AOT module - Stopwatch aotModuleLoadStopwatch; - LOG(Info, "Loading Mono AOT module..."); - void* libAotModule = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE)); + // Wait for AOT module preloading + while (Platform::AtomicRead(&MonoAotPreloadServiceInstance.Ready) == 0) + Platform::Yield(); + + // Initialize AOT module + void* libAotModule = MonoAotPreloadServiceInstance.Library; if (libAotModule == nullptr) { LOG(Error, "Failed to laod Mono AOT module (" TEXT(USE_MONO_AOT_MODULE) ")"); @@ -2179,8 +2242,6 @@ bool InitHostfxr() mono_aot_register_module((void**)modules[i]); } Allocator::Free(modules); - aotModuleLoadStopwatch.Stop(); - LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds()); #endif // Setup debugger diff --git a/Source/Engine/Scripting/Scripting.Build.cs b/Source/Engine/Scripting/Scripting.Build.cs index d15b460c9..206513259 100644 --- a/Source/Engine/Scripting/Scripting.Build.cs +++ b/Source/Engine/Scripting/Scripting.Build.cs @@ -32,7 +32,7 @@ public class Scripting : EngineModule options.ScriptingAPI.Defines.Add("NET"); AddFrameworkDefines("NET{0}_{1}", dotnetSdk.Version.Major, 0); // "NET7_0" for (int i = 5; i <= dotnetSdk.Version.Major; i++) - AddFrameworkDefines("NET{0}_{1}_OR_GREATER", dotnetSdk.Version.Major, 0); // "NET7_0_OR_GREATER" + AddFrameworkDefines("NET{0}_{1}_OR_GREATER", i, 0); // "NET7_0_OR_GREATER" options.ScriptingAPI.Defines.Add("NETCOREAPP"); AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 3, 1); // "NETCOREAPP3_1_OR_GREATER" AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 2, 2); diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 2f7b9f9d6..4e17bb80a 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -568,10 +568,14 @@ bool Scripting::Load() #endif // Load FlaxEngine - const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); if (!flaxEngineModule->Assembly->IsLoaded()) { + String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); +#if USE_MONO_AOT + if (!FileSystem::FileExists(flaxEnginePath)) + flaxEnginePath = Globals::BinariesFolder / TEXT("Dotnet") / TEXT("FlaxEngine.CSharp.dll"); +#endif if (flaxEngineModule->Assembly->Load(flaxEnginePath)) { LOG(Error, "Failed to load FlaxEngine C# assembly."); diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index b308f5dae..7f9f2980c 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -198,7 +198,7 @@ namespace FlaxEngine private static void OnLocalizationChanged() { // Invariant-globalization only (see InitHostfxr with Mono) -#if !(PLATFORM_IOS || PLATFORM_SWITCH) +#if !(PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5) var currentThread = Thread.CurrentThread; var language = Localization.CurrentLanguage; if (language != null) @@ -261,7 +261,7 @@ namespace FlaxEngine internal static ManagedHandle CultureInfoToManaged(int lcid) { -#if PLATFORM_IOS || PLATFORM_SWITCH +#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5 // Invariant-globalization only (see InitHostfxr with Mono) lcid = 0; #endif diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 35bd16eb6..dee97a849 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -93,6 +93,8 @@ ScriptingObject::ScriptingObject(const SpawnParams& params) : _gcHandle((MGCHandle)params.Managed) #elif !COMPILE_WITHOUT_CSHARP : _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0) +#else + : _gcHandle(0) #endif , _type(params.Type) , _id(params.ID) diff --git a/Source/Engine/Scripting/ScriptingObjectReference.h b/Source/Engine/Scripting/ScriptingObjectReference.h index a71d22eae..58fed7668 100644 --- a/Source/Engine/Scripting/ScriptingObjectReference.h +++ b/Source/Engine/Scripting/ScriptingObjectReference.h @@ -33,6 +33,13 @@ public: { } + ScriptingObjectReferenceBase(ScriptingObjectReferenceBase&& other) noexcept + : _object(nullptr) + { + OnSet(other._object); + other.OnSet(nullptr); + } + /// /// Initializes a new instance of the class. /// @@ -96,6 +103,16 @@ protected: void OnSet(ScriptingObject* object); void OnDeleted(ScriptingObject* obj); + + ScriptingObjectReferenceBase& operator=(ScriptingObjectReferenceBase&& other) noexcept + { + if (this != &other) + { + OnSet(other._object); + other.OnSet(nullptr); + } + return *this; + } }; /// @@ -133,6 +150,11 @@ public: { } + ScriptingObjectReference(ScriptingObjectReference&& other) noexcept + : ScriptingObjectReferenceBase(MoveTemp(other)) + { + } + /// /// Finalizes an instance of the class. /// @@ -173,6 +195,12 @@ public: return *this; } + ScriptingObjectReference& operator=(ScriptingObjectReference&& other) noexcept + { + ScriptingObjectReferenceBase::operator=(MoveTemp(other)); + return *this; + } + FORCE_INLINE ScriptingObjectReference& operator=(const Guid& id) { OnSet(static_cast(FindObject(id, T::GetStaticClass()))); diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index cd7b05c73..e1fb3dc04 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -5,6 +5,9 @@ #include "Types.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/Guid.h" +#if PLATFORM_ARCH_ARM64 +#include "Engine/Core/Core.h" +#endif class MMethod; class BinaryModule; @@ -563,8 +566,8 @@ FORCE_INLINE int32 GetVTableIndex(void** vtable, int32 entriesCount, void* func) offset = ((*op & 0x3FFC00) >> 10) * ((*op & 0x40000000) != 0 ? 8 : 4); return offset / sizeof(void*); } - CRASH; } + CRASH; #elif defined(__clang__) // On Clang member function pointer represents the offset from the vtable begin. return (int32)(intptr)func / sizeof(void*); diff --git a/Source/Engine/ShadersCompilation/Config.h b/Source/Engine/ShadersCompilation/Config.h index 8d9730f70..65d596235 100644 --- a/Source/Engine/ShadersCompilation/Config.h +++ b/Source/Engine/ShadersCompilation/Config.h @@ -15,7 +15,6 @@ class MemoryWriteStream; struct FLAXENGINE_API ShaderCompilationOptions { public: - /// /// Name of the target object (name of the shader or material for better logging readability) /// @@ -37,12 +36,16 @@ public: uint32 SourceLength = 0; public: - /// /// Target shader profile /// ShaderProfile Profile = ShaderProfile::Unknown; + /// + /// Target platform, set to invalid value of 0 if not used (platform-independent compilation). + /// + PlatformType Platform = (PlatformType)0; + /// /// Disables shaders compiler optimizations. Can be used to debug shaders on a target platform or to speed up the shaders compilation time. /// @@ -64,7 +67,6 @@ public: Array Macros; public: - /// /// Output stream to write compiled shader cache to /// diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp index 3edad2488..01ce8faef 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp @@ -196,7 +196,7 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation default: return true; } - if (_profile == ShaderProfile::DirectX_SM5) + if (GetProfile() == ShaderProfile::DirectX_SM5) { profileName += "_5_0"; } diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index 5e49da710..7dd7182cf 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -59,7 +59,8 @@ public: *ppIncludeSource = nullptr; const char* source; int32 sourceLength; - const StringAnsi filename(pFilename); + StringAnsi filename(pFilename); + filename.Replace('\\', '/'); if (ShaderCompiler::GetIncludedFileSource(_context, "", filename.Get(), source, sourceLength)) return E_FAIL; IDxcBlobEncoding* textBlob; @@ -70,25 +71,28 @@ public: } }; -ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile) - : ShaderCompiler(profile) +ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform, void* dxcCreateInstanceProc) + : ShaderCompiler(profile, platform) { IDxcCompiler3* compiler = nullptr; IDxcLibrary* library = nullptr; + IDxcContainerBuilder* builder = nullptr; IDxcContainerReflection* containerReflection = nullptr; - if (FAILED(DxcCreateInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast(&compiler))) || - FAILED(DxcCreateInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast(&library))) || - FAILED(DxcCreateInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast(&containerReflection)))) + DxcCreateInstanceProc createInstance = dxcCreateInstanceProc ? (DxcCreateInstanceProc)dxcCreateInstanceProc : &DxcCreateInstance; + if (FAILED(createInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast(&compiler))) || + FAILED(createInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast(&library))) || + FAILED(createInstance(CLSID_DxcContainerBuilder, __uuidof(builder), reinterpret_cast(&builder))) || + FAILED(createInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast(&containerReflection)))) { LOG(Error, "DxcCreateInstance failed"); } _compiler = compiler; _library = library; + _builder = builder; _containerReflection = containerReflection; - static bool PrintVersion = true; - if (PrintVersion) + static HashSet PrintVersions; + if (PrintVersions.Add(createInstance)) { - PrintVersion = false; IDxcVersionInfo* version = nullptr; if (compiler && SUCCEEDED(compiler->QueryInterface(__uuidof(version), reinterpret_cast(&version)))) { @@ -102,14 +106,13 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile) ShaderCompilerDX::~ShaderCompilerDX() { - auto compiler = (IDxcCompiler2*)_compiler; - if (compiler) + if (auto compiler = (IDxcCompiler2*)_compiler) compiler->Release(); - auto library = (IDxcLibrary*)_library; - if (library) + if (auto library = (IDxcLibrary*)_library) library->Release(); - auto containerReflection = (IDxcContainerReflection*)_containerReflection; - if (containerReflection) + if (auto builder = (IDxcContainerBuilder*)_builder) + builder->Release(); + if (auto containerReflection = (IDxcContainerReflection*)_containerReflection) containerReflection->Release(); } @@ -221,6 +224,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD argsFull.Add(TEXT("-D")); argsFull.Add(*d); } + GetArgs(meta, argsFull); // Compile ComPtr results; @@ -252,7 +256,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD } // Get the output - ComPtr shaderBuffer = nullptr; + ComPtr shaderBuffer; if (FAILED(results->GetResult(&shaderBuffer))) { LOG(Error, "IDxcOperationResult::GetResult failed."); @@ -458,6 +462,28 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD } } + // Strip reflection data + if (!options->GenerateDebugData) + { + if (auto builder = (IDxcContainerBuilder*)_builder) + { + if (builder->Load(shaderBuffer) == S_OK) + { + builder->RemovePart(DXC_PART_PDB); + builder->RemovePart(DXC_PART_REFLECTION_DATA); + ComPtr serializeResult; + if (builder->SerializeContainer(&serializeResult) == S_OK) + { + ComPtr optimizedShaderBuffer; + if (SUCCEEDED(serializeResult->GetResult(&optimizedShaderBuffer))) + { + shaderBuffer = optimizedShaderBuffer; + } + } + } + } + } + if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize())) return true; diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h index 7973fe397..0b758cd3f 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h @@ -15,6 +15,7 @@ private: Array _funcNameDefineBuffer; void* _compiler; void* _library; + void* _builder; void* _containerReflection; public: @@ -22,7 +23,9 @@ public: /// Initializes a new instance of the class. /// /// The profile. - ShaderCompilerDX(ShaderProfile profile); + /// The platform. + /// The custom DXC Compiler factory function. + ShaderCompilerDX(ShaderProfile profile, PlatformType platform = (PlatformType)0, void* dxcCreateInstanceProc = nullptr); /// /// Finalizes an instance of the class. @@ -30,6 +33,10 @@ public: ~ShaderCompilerDX(); protected: + virtual void GetArgs(ShaderFunctionMeta& meta, Array>& args) + { + } + // [ShaderCompiler] bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override; bool OnCompileBegin() override; diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index b6b859afa..cfb08b925 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -278,6 +278,17 @@ bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* co return false; } +bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size) +{ + auto output = context->Output; + output->Write((uint32)(cache1Size + cache2Size + headerSize)); + output->WriteBytes(header, headerSize); + output->WriteBytes(cache1, cache1Size); + output->WriteBytes(cache2, cache2Size); + output->Write(bindings); + return false; +} + bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize) { auto output = context->Output; diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.h b/Source/Engine/ShadersCompilation/ShaderCompiler.h index 79cdb1cae..9666d8f13 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.h +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.h @@ -23,10 +23,11 @@ public: }; private: + ShaderProfile _profile; + PlatformType _platform; Array _funcNameDefineBuffer; protected: - ShaderProfile _profile; ShaderCompilationContext* _context = nullptr; Array _globalMacros; Array _macros; @@ -37,8 +38,10 @@ public: /// Initializes a new instance of the class. /// /// The profile. - ShaderCompiler(ShaderProfile profile) + /// The platform. + ShaderCompiler(ShaderProfile profile, PlatformType platform = (PlatformType)0) : _profile(profile) + , _platform(platform) { } @@ -51,12 +54,19 @@ public: /// /// Gets shader profile supported by this compiler. /// - /// The shader profile. FORCE_INLINE ShaderProfile GetProfile() const { return _profile; } + /// + /// Gets target platform supported by this compiler. Returns invalid value of '0' if any platform works. + /// + FORCE_INLINE PlatformType GetPlatform() const + { + return _platform; + } + /// /// Performs the shader compilation. /// @@ -98,6 +108,7 @@ protected: static bool WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta); static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize); + static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size); static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize); static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta); static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array& macros, void* additionalData); diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs b/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs index 0e203f656..03ab0a74e 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs @@ -63,6 +63,8 @@ public class ShadersCompilation : EngineModule options.PrivateDependencies.Add("ShaderCompilerPS4"); if (Sdk.HasValid("PS5Sdk")) options.PrivateDependencies.Add("ShaderCompilerPS5"); + if (Flax.Build.Platform.GetPlatform(TargetPlatform.XboxScarlett, true) != null) + options.PrivateDependencies.Add("ShaderCompilerXboxScarlett"); } /// diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp index accfeaad5..f7d1367ba 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp @@ -48,6 +48,9 @@ #if COMPILE_WITH_PS5_SHADER_COMPILER #include "Platforms/PS5/Engine/ShaderCompilerPS5/ShaderCompilerPS5.h" #endif +#if COMPILE_WITH_XBOX_SCARLETT_SHADER_COMPILER +#include "Platforms/XboxScarlett/Engine/ShaderCompilerXboxScarlett/ShaderCompilerXboxScarlett.h" +#endif namespace ShadersCompilationImpl { @@ -165,20 +168,16 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options) bool result; { ShaderCompilationContext context(&options, &meta); - - // Request shaders compiler - auto compiler = RequestCompiler(options.Profile); + auto compiler = RequestCompiler(options.Profile, options.Platform); if (compiler == nullptr) { LOG(Error, "Shader compiler request failed."); return true; } - ASSERT(compiler->GetProfile() == options.Profile); + ASSERT_LOW_LAYER(compiler->GetProfile() == options.Profile); // Call compilation process result = compiler->Compile(&context); - - // Dismiss compiler FreeCompiler(compiler); #if GPU_USE_SHADERS_DEBUG_LAYER @@ -210,65 +209,65 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options) return result; } -ShaderCompiler* ShadersCompilation::CreateCompiler(ShaderProfile profile) +ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile, PlatformType platform) { - ShaderCompiler* result = nullptr; - - switch (profile) - { -#if COMPILE_WITH_D3D_SHADER_COMPILER - case ShaderProfile::DirectX_SM4: - case ShaderProfile::DirectX_SM5: - result = New(profile); - break; -#endif -#if COMPILE_WITH_DX_SHADER_COMPILER - case ShaderProfile::DirectX_SM6: - result = New(profile); - break; -#endif -#if COMPILE_WITH_VK_SHADER_COMPILER - case ShaderProfile::Vulkan_SM5: - result = New(profile); - break; -#endif -#if COMPILE_WITH_PS4_SHADER_COMPILER - case ShaderProfile::PS4: - result = New(); - break; -#endif -#if COMPILE_WITH_PS5_SHADER_COMPILER - case ShaderProfile::PS5: - result = New(); - break; -#endif - default: - break; - } - ASSERT_LOW_LAYER(result == nullptr || result->GetProfile() == profile); - - return result; -} - -ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile) -{ - ShaderCompiler* compiler; ScopeLock lock(Locker); // Try to find ready compiler + ShaderCompiler* compiler = nullptr; for (int32 i = 0; i < ReadyCompilers.Count(); i++) { - compiler = ReadyCompilers[i]; - if (compiler->GetProfile() == profile) + compiler = ReadyCompilers.Get()[i]; + if (compiler->GetProfile() == profile && + (compiler->GetPlatform() == platform || (int32)compiler->GetPlatform() == 0)) { - // Use it ReadyCompilers.RemoveAt(i); return compiler; } } // Create new compiler for a target profile - compiler = CreateCompiler(profile); + switch (profile) + { +#if COMPILE_WITH_D3D_SHADER_COMPILER + case ShaderProfile::DirectX_SM4: + case ShaderProfile::DirectX_SM5: + compiler = New(profile); + break; +#endif +#if COMPILE_WITH_DX_SHADER_COMPILER + case ShaderProfile::DirectX_SM6: + switch (platform) + { + case PlatformType::XboxScarlett: +#if COMPILE_WITH_XBOX_SCARLETT_SHADER_COMPILER + compiler = New(); +#endif + break; + default: + compiler = New(profile); + break; + } + break; +#endif +#if COMPILE_WITH_VK_SHADER_COMPILER + case ShaderProfile::Vulkan_SM5: + compiler = New(profile); + break; +#endif +#if COMPILE_WITH_PS4_SHADER_COMPILER + case ShaderProfile::PS4: + compiler = New(); + break; +#endif +#if COMPILE_WITH_PS5_SHADER_COMPILER + case ShaderProfile::PS5: + compiler = New(); + break; +#endif + default: + break; + } if (compiler == nullptr) { LOG(Error, "Cannot create Shader Compiler for profile {0}", ::ToString(profile)); @@ -414,7 +413,8 @@ String ShadersCompilation::ResolveShaderPath(StringView path) // Skip to the last root start './' but preserve the leading one for (int32 i = path.Length() - 2; i >= 2; i--) { - if (StringUtils::Compare(path.Get() + i, TEXT("./"), 2) == 0) + const Char* pos = path.Get() + i; + if (pos[0] == '.' && pos[1] == '/') { path = path.Substring(i); break; diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.h b/Source/Engine/ShadersCompilation/ShadersCompilation.h index 0c251f61e..b32c9f51a 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.h +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.h @@ -48,9 +48,7 @@ public: static String CompactShaderPath(StringView path); private: - - static ShaderCompiler* CreateCompiler(ShaderProfile profile); - static ShaderCompiler* RequestCompiler(ShaderProfile profile); + static ShaderCompiler* RequestCompiler(ShaderProfile profile, PlatformType platform); static void FreeCompiler(ShaderCompiler* compiler); }; diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index a66a0e283..b77ee51b6 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -376,7 +376,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) EnableLightmapsUsage = _giBounceRunningIndex != 0; // Renderer::Render(_task); - context->ClearState(); + context->ResetState(); // IsRunningRadiancePass = false; EnableLightmapsUsage = true; @@ -515,7 +515,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) } // Cleanup after rendering - context->ClearState(); + context->ResetState(); // Mark job as done Platform::AtomicStore(&_wasJobDone, 1); diff --git a/Source/Engine/Tests/TestCollections.cpp b/Source/Engine/Tests/TestCollections.cpp index b477140cf..a96c6ce1c 100644 --- a/Source/Engine/Tests/TestCollections.cpp +++ b/Source/Engine/Tests/TestCollections.cpp @@ -27,6 +27,19 @@ void CheckBitArray(const BitArray& array) TEST_CASE("Array") { + SECTION("Test Capacity") + { + // Ensure correct collections capacity growing to meet proper memory usage vs safe slack + CHECK(AllocationUtils::CalculateCapacityGrow(1, 0) == 8); + CHECK(AllocationUtils::CalculateCapacityGrow(7, 0) == 8); + CHECK(AllocationUtils::CalculateCapacityGrow(1, 16) == 16); + CHECK(AllocationUtils::CalculateCapacityGrow(31, 0) == 32); + CHECK(AllocationUtils::CalculateCapacityGrow(32, 0) == 32); + CHECK(AllocationUtils::CalculateCapacityGrow(1000, 0) == 1024); + CHECK(AllocationUtils::CalculateCapacityGrow(1024, 0) == 1024); + CHECK(AllocationUtils::CalculateCapacityGrow(1025, 0) == 2048); + } + SECTION("Test Allocators") { Array a1; diff --git a/Source/Engine/Tests/TestScripting.h b/Source/Engine/Tests/TestScripting.h index 10a1cf2b8..ca13ac307 100644 --- a/Source/Engine/Tests/TestScripting.h +++ b/Source/Engine/Tests/TestScripting.h @@ -204,6 +204,9 @@ public: { return str.Length(); } + + // Test parameter passing with non-cost ref + API_FUNCTION() virtual void StringParamRef(String& str) {} }; // Test debug commands via static class. diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index e90a2e847..692a088b7 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -15,6 +15,7 @@ #include "Engine/Profiler/ProfilerMemory.h" #if USE_CSHARP #include "Engine/Scripting/ManagedCLR/MCore.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #endif #define JOB_SYSTEM_ENABLED 1 @@ -184,6 +185,7 @@ int32 JobSystemThread::Run() JobData data; Function job; bool attachCSharpThread = true; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; while (Platform::AtomicRead(&ExitFlag) == 0) { // Try to get a job @@ -205,6 +207,7 @@ int32 JobSystemThread::Run() { MCore::Thread::Attach(); attachCSharpThread = false; + monoThreadInfo = mono_thread_info_attach(); } #endif @@ -244,9 +247,11 @@ int32 JobSystemThread::Run() else { // Wait for signal + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); JobsMutex.Lock(); JobsSignal.Wait(JobsMutex); JobsMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } return 0; diff --git a/Source/Engine/Threading/Task.cpp b/Source/Engine/Threading/Task.cpp index bab794728..a640019d1 100644 --- a/Source/Engine/Threading/Task.cpp +++ b/Source/Engine/Threading/Task.cpp @@ -226,6 +226,27 @@ void Task::OnEnd() { ASSERT(!IsRunning()); - // Add to delete - DeleteObject(30.0f, false); + if (_continueWith && !_continueWith->IsEnded()) + { + // Let next task do the cleanup (to ensure whole tasks chain shares the lifetime) + _continueWith->_rootForRemoval = _rootForRemoval ? _rootForRemoval : this; + } + else + { + constexpr float timeToLive = 30.0f; + + // Remove task chain starting from the root + if (_rootForRemoval) + { + auto task = _rootForRemoval; + while (task != this) + { + task->DeleteObject(timeToLive, false); + task = task->_continueWith; + } + } + + // Add to delete + DeleteObject(timeToLive, false); + } } diff --git a/Source/Engine/Threading/Task.h b/Source/Engine/Threading/Task.h index eb450effd..19fbf4fad 100644 --- a/Source/Engine/Threading/Task.h +++ b/Source/Engine/Threading/Task.h @@ -63,6 +63,11 @@ protected: /// Task* _continueWith = nullptr; + /// + /// The task that's starts removal chain, used to sync whole task chain lifetime. + /// + Task* _rootForRemoval = nullptr; + void SetState(TaskState state) { Platform::AtomicStore((int64 volatile*)&_state, (uint64)state); diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 4943469a5..d7410d0fc 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -15,6 +15,7 @@ #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/Thread.h" #include "Engine/Profiler/ProfilerMemory.h" +#include "Engine/Scripting/Internal/InternalCalls.h" FLAXENGINE_API bool IsInMainThread() { @@ -117,6 +118,7 @@ int32 ThreadPool::ThreadProc() Platform::SetThreadAffinityMask(THREAD_POOL_AFFINITY_MASK((int32)index)); #endif ThreadPoolTask* task; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; // Work until end while (Platform::AtomicRead(&ThreadPoolImpl::ExitFlag) == 0) @@ -125,12 +127,15 @@ int32 ThreadPool::ThreadProc() if (ThreadPoolImpl::Jobs.try_dequeue(task)) { task->Execute(); + MONO_THREAD_INFO_GET(monoThreadInfo); } else { + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); ThreadPoolImpl::JobsMutex.Lock(); ThreadPoolImpl::JobsSignal.Wait(ThreadPoolImpl::JobsMutex); ThreadPoolImpl::JobsMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp index fd68ccb9b..c0f1ede7e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp @@ -43,32 +43,15 @@ void MaterialGenerator::AddLayer(MaterialLayer* layer) MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller) { // Find layer first - for (int32 i = 0; i < _layers.Count(); i++) + for (MaterialLayer* layer : _layers) { - if (_layers[i]->ID == id) - { - // Found - return _layers[i]; - } + if (layer->ID == id) + return layer; } // Load asset - Asset* asset = Assets.LoadAsync(id); - if (asset == nullptr || asset->WaitForLoaded(30000)) - { - OnError(caller, nullptr, TEXT("Failed to load material asset.")); - return nullptr; - } - - // Special case for engine exit event - if (Engine::ShouldExit()) - { - // End - return nullptr; - } - - // Check if load failed - if (!asset->IsLoaded()) + Asset* asset = Assets.Load(id); + if (asset == nullptr) { OnError(caller, nullptr, TEXT("Failed to load material asset.")); return nullptr; @@ -79,13 +62,11 @@ MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller) Asset* iterator = asset; while (material == nullptr) { - // Wait for material to be loaded if (iterator->WaitForLoaded()) { OnError(caller, nullptr, TEXT("Material asset load failed.")); return nullptr; } - if (iterator->GetTypeName() == MaterialInstance::TypeName) { auto instance = ((MaterialInstance*)iterator); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp index ea1f6cc7a..a15551b40 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp @@ -15,12 +15,14 @@ void MaterialGenerator::ProcessGroupLayers(Box* box, Node* node, Value& value) if (!id.IsValid()) { OnError(node, box, TEXT("Missing material.")); + value = MaterialValue::InitForZero(VariantType::Void); break; } ASSERT(GetRootLayer() != nullptr && GetRootLayer()->ID.IsValid()); if (id == GetRootLayer()->ID) { OnError(node, box, TEXT("Cannot use current material as layer.")); + value = MaterialValue::InitForZero(VariantType::Void); break; } @@ -29,6 +31,7 @@ void MaterialGenerator::ProcessGroupLayers(Box* box, Node* node, Value& value) if (layer == nullptr) { OnError(node, box, TEXT("Cannot load material.")); + value = MaterialValue::InitForZero(VariantType::Void); break; } ASSERT(_layers.Contains(layer)); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 4594446ec..09ac78e6b 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -285,8 +285,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 24: { // Load function asset - const auto function = Assets.LoadAsync((Guid)node->Values[0]); - if (!function || function->WaitForLoaded()) + const auto function = Assets.Load((Guid)node->Values[0]); + if (!function) { OnError(node, box, TEXT("Missing or invalid function.")); value = Value::Zero; @@ -299,7 +299,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) { if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(1, 24)) { - const auto callFunc = Assets.LoadAsync((Guid)_callStack[i]->Values[0]); + const auto callFunc = Assets.Load((Guid)_callStack[i]->Values[0]); if (callFunc == function) { OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString())); @@ -790,9 +790,18 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value) // Function Input case 1: { + // Check the stack count. If only 1 graph is present, + // we are processing the graph in isolation (e.g., in the Editor Preview). + // In this case, we skip the caller-finding logic and use the node's default value. + if (_graphStack.Count() < 2) + { + // Use the default value from the function input node's box (usually box 1) + value = tryGetValue(node->TryGetBox(1), Value::Zero); + break; + } + // Find the function call Node* functionCallNode = nullptr; - ASSERT(_graphStack.Count() >= 2); Graph* graph; for (int32 i = _callStack.Count() - 1; i >= 0; i--) { @@ -808,7 +817,7 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value) value = Value::Zero; break; } - const auto function = Assets.LoadAsync((Guid)functionCallNode->Values[0]); + const auto function = Assets.Load((Guid)functionCallNode->Values[0]); if (!_functions.TryGet(functionCallNode, graph) || !function) { OnError(node, box, TEXT("Missing calling function graph.")); diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp index 4e551e445..6e1b5ae6f 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp @@ -3,31 +3,44 @@ #if COMPILE_WITH_MODEL_TOOL #include "MeshAccelerationStructure.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Model.h" +#include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/Models/ModelData.h" #include "Engine/Profiler/ProfilerCPU.h" -void MeshAccelerationStructure::BuildBVH(int32 node, int32 maxLeafSize, Array& scratch) +PACK_STRUCT(struct GPUBVH { + Float3 BoundsMin; + uint32 Index; + Float3 BoundsMax; + int32 Count; // Negative for non-leaf nodes +}); +static_assert(sizeof(GPUBVH) == sizeof(Float4) * 2, "Invalid BVH structure size for GPU."); + +void MeshAccelerationStructure::BuildBVH(int32 node, BVHBuild& build) { - auto& root = _bvh[node]; - ASSERT_LOW_LAYER(root.Leaf.IsLeaf); - if (root.Leaf.TriangleCount <= maxLeafSize) + auto* root = &_bvh[node]; + ASSERT_LOW_LAYER(root->Leaf.IsLeaf); + if (build.MaxLeafSize > 0 && root->Leaf.TriangleCount <= build.MaxLeafSize) + return; + if (build.MaxDepth > 0 && build.NodeDepth >= build.MaxDepth) return; // Spawn two leaves const int32 childIndex = _bvh.Count(); _bvh.AddDefault(2); + root = &_bvh[node]; auto& left = _bvh.Get()[childIndex]; auto& right = _bvh.Get()[childIndex + 1]; left.Leaf.IsLeaf = 1; right.Leaf.IsLeaf = 1; - left.Leaf.MeshIndex = root.Leaf.MeshIndex; - right.Leaf.MeshIndex = root.Leaf.MeshIndex; + left.Leaf.MeshIndex = root->Leaf.MeshIndex; + right.Leaf.MeshIndex = root->Leaf.MeshIndex; // Mid-point splitting based on the largest axis - const Float3 boundsSize = root.Bounds.GetSize(); + const Float3 boundsSize = root->Bounds.GetSize(); int32 axisCount = 0; int32 axis = 0; RETRY: @@ -51,11 +64,11 @@ RETRY: // Go to the next axis axis = (axis + 1) % 3; } - const float midPoint = (float)(root.Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f); - const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; + const float midPoint = (float)(root->Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f); + const Mesh& meshData = _meshes[root->Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); - int32 indexStart = root.Leaf.TriangleIndex * 3; - int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; + int32 indexStart = root->Leaf.TriangleIndex * 3; + int32 indexEnd = indexStart + root->Leaf.TriangleCount * 3; left.Leaf.TriangleCount = 0; right.Leaf.TriangleCount = 0; if (meshData.Use16BitIndexBuffer) @@ -64,8 +77,8 @@ RETRY: { uint16 I0, I1, I2; }; - scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); - auto dst = (Tri*)scratch.Get(); + build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri)); + auto dst = (Tri*)build.Scratch.Get(); auto ib16 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { @@ -77,9 +90,9 @@ RETRY: if (centroid <= midPoint) dst[left.Leaf.TriangleCount++] = tri; // Left else - dst[root.Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right + dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right } - Platform::MemoryCopy(ib16 + indexStart, dst, root.Leaf.TriangleCount * 3 * sizeof(uint16)); + Platform::MemoryCopy(ib16 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint16)); if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) { axisCount++; @@ -90,13 +103,13 @@ RETRY: indexStart = 0; indexEnd = left.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - left.Bounds.Merge(vb[((uint16*)scratch.Get())[i]]); + left.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); - right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); + right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; - indexEnd = root.Leaf.TriangleCount * 3; + indexEnd = root->Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - right.Bounds.Merge(vb[((uint16*)scratch.Get())[i]]); + right.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); } else { @@ -104,8 +117,8 @@ RETRY: { uint32 I0, I1, I2; }; - scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); - auto dst = (Tri*)scratch.Get(); + build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri)); + auto dst = (Tri*)build.Scratch.Get(); auto ib32 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { @@ -117,9 +130,9 @@ RETRY: if (centroid <= midPoint) dst[left.Leaf.TriangleCount++] = tri; // Left else - dst[root.Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right + dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right } - Platform::MemoryCopy(ib32 + indexStart, dst, root.Leaf.TriangleCount * 3 * sizeof(uint32)); + Platform::MemoryCopy(ib32 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint32)); if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) { axisCount++; @@ -130,26 +143,31 @@ RETRY: indexStart = 0; indexEnd = left.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - left.Bounds.Merge(vb[((uint32*)scratch.Get())[i]]); + left.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); - right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); + right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; - indexEnd = root.Leaf.TriangleCount * 3; + indexEnd = root->Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - right.Bounds.Merge(vb[((uint32*)scratch.Get())[i]]); + right.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); } - ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root.Leaf.TriangleCount); - left.Leaf.TriangleIndex = root.Leaf.TriangleIndex; + ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root->Leaf.TriangleCount); + left.Leaf.TriangleIndex = root->Leaf.TriangleIndex; right.Leaf.TriangleIndex = left.Leaf.TriangleIndex + left.Leaf.TriangleCount; + build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); + build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); // Convert into a node - root.Node.IsLeaf = 0; - root.Node.ChildIndex = childIndex; - root.Node.ChildrenCount = 2; + root->Node.IsLeaf = 0; + root->Node.ChildIndex = childIndex; + root->Node.ChildrenCount = 2; // Split children - BuildBVH(childIndex, maxLeafSize, scratch); - BuildBVH(childIndex + 1, maxLeafSize, scratch); + build.NodeDepth++; + build.MaxNodeDepth = Math::Max(build.NodeDepth, build.MaxNodeDepth); + BuildBVH(childIndex, build); + BuildBVH(childIndex + 1, build); + build.NodeDepth--; } bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const @@ -160,7 +178,7 @@ bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, { // Find closest triangle Vector3 p; - const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; + const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; @@ -229,7 +247,7 @@ bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hit if (root.Leaf.IsLeaf) { // Ray cast along triangles in the leaf - const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; + const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; @@ -288,6 +306,15 @@ bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hit return hit; } +MeshAccelerationStructure::~MeshAccelerationStructure() +{ + for (auto& e : _meshes) + { + if (e.Asset) + e.Asset->RemoveReference(); + } +} + void MeshAccelerationStructure::Add(Model* model, int32 lodIndex) { PROFILE_CPU(); @@ -307,6 +334,8 @@ void MeshAccelerationStructure::Add(Model* model, int32 lodIndex) } auto& meshData = _meshes.AddOne(); + meshData.Asset = model; + model->AddReference(); if (model->IsVirtual()) { meshData.Indices = mesh.GetTriangleCount() * 3; @@ -350,6 +379,7 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex, } auto& meshData = _meshes.AddOne(); + meshData.Asset = nullptr; meshData.Indices = mesh->Indices.Count(); meshData.Vertices = mesh->Positions.Count(); if (copy) @@ -369,7 +399,9 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex, void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy) { + ASSERT(vertices % 3 == 0); auto& meshData = _meshes.AddOne(); + meshData.Asset = nullptr; if (copy) { meshData.VertexBuffer.Copy((const byte*)vb, vertices * sizeof(Float3)); @@ -382,43 +414,122 @@ void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 meshData.Vertices = vertices; meshData.Indices = indices; meshData.Use16BitIndexBuffer = use16BitIndex; + BoundingBox::FromPoints(meshData.VertexBuffer.Get(), vertices, meshData.Bounds); } -void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize) +void MeshAccelerationStructure::MergeMeshes(bool force16BitIndexBuffer) +{ + if (_meshes.Count() == 0) + return; + if (_meshes.Count() == 1 && (!force16BitIndexBuffer || !_meshes[0].Use16BitIndexBuffer)) + return; + PROFILE_CPU(); + auto meshes = _meshes; + _meshes.Clear(); + _meshes.Resize(1); + auto& mesh = _meshes[0]; + mesh.Asset = nullptr; + mesh.Use16BitIndexBuffer = true; + mesh.Indices = 0; + mesh.Vertices = 0; + mesh.Bounds = meshes[0].Bounds; + for (auto& e : meshes) + { + if (!e.Use16BitIndexBuffer) + mesh.Use16BitIndexBuffer = false; + mesh.Vertices += e.Vertices; + mesh.Indices += e.Indices; + BoundingBox::Merge(mesh.Bounds, e.Bounds, mesh.Bounds); + } + mesh.Use16BitIndexBuffer &= mesh.Indices <= MAX_uint16 && !force16BitIndexBuffer; + mesh.VertexBuffer.Allocate(mesh.Vertices * sizeof(Float3)); + mesh.IndexBuffer.Allocate(mesh.Indices * sizeof(uint32)); + int32 vertexCounter = 0, indexCounter = 0; + for (auto& e : meshes) + { + Platform::MemoryCopy(mesh.VertexBuffer.Get() + vertexCounter * sizeof(Float3), e.VertexBuffer.Get(), e.Vertices * sizeof(Float3)); + if (e.Use16BitIndexBuffer) + { + for (int32 i = 0; i < e.Indices; i++) + { + uint16 index = ((uint16*)e.IndexBuffer.Get())[i]; + ((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index; + } + } + else + { + for (int32 i = 0; i < e.Indices; i++) + { + uint16 index = ((uint32*)e.IndexBuffer.Get())[i]; + ((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index; + } + } + vertexCounter += e.Vertices; + indexCounter += e.Indices; + if (e.Asset) + e.Asset->RemoveReference(); + } +} + +void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize, int32 maxDepth) { if (_meshes.Count() == 0) return; PROFILE_CPU(); + BVHBuild build; + build.MaxLeafSize = maxLeafSize; + build.MaxDepth = maxDepth; + // Estimate memory usage int32 trianglesCount = 0; for (const Mesh& meshData : _meshes) trianglesCount += meshData.Indices / 3; _bvh.Clear(); - _bvh.EnsureCapacity(trianglesCount / maxLeafSize); + _bvh.EnsureCapacity(trianglesCount / Math::Max(maxLeafSize, 16)); - // Init with the root node and all meshes as leaves - auto& root = _bvh.AddOne(); - root.Node.IsLeaf = 0; - root.Node.ChildIndex = 1; - root.Node.ChildrenCount = _meshes.Count(); - root.Bounds = _meshes[0].Bounds; - for (int32 i = 0; i < _meshes.Count(); i++) + // Skip using root node if BVH contains only one mesh + if (_meshes.Count() == 1) { - const Mesh& meshData = _meshes[i]; + const Mesh& meshData = _meshes.First(); auto& child = _bvh.AddOne(); child.Leaf.IsLeaf = 1; - child.Leaf.MeshIndex = i; + child.Leaf.MeshIndex = 0; child.Leaf.TriangleIndex = 0; child.Leaf.TriangleCount = meshData.Indices / 3; child.Bounds = meshData.Bounds; - BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds); + Array scratch; + BuildBVH(0, build); + } + else + { + // Init with the root node and all meshes as leaves + auto& root = _bvh.AddOne(); + root.Node.IsLeaf = 0; + root.Node.ChildIndex = 1; + root.Node.ChildrenCount = _meshes.Count(); + root.Bounds = _meshes[0].Bounds; + for (int32 i = 0; i < _meshes.Count(); i++) + { + const Mesh& meshData = _meshes[i]; + auto& child = _bvh.AddOne(); + child.Leaf.IsLeaf = 1; + child.Leaf.MeshIndex = i; + child.Leaf.TriangleIndex = 0; + child.Leaf.TriangleCount = meshData.Indices / 3; + child.Bounds = meshData.Bounds; + BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds); + } + + // Sub-divide mesh nodes into smaller leaves + build.MaxNodeDepth = build.MaxDepth = 2; + Array scratch; + for (int32 i = 0; i < _meshes.Count(); i++) + BuildBVH(i + 1, build); + build.NodeDepth = 0; } - // Sub-divide mesh nodes into smaller leaves - Array scratch; - for (int32 i = 0; i < _meshes.Count(); i++) - BuildBVH(i + 1, maxLeafSize, scratch); + LOG(Info, "BVH nodes: {}, max depth: {}, max triangles: {}", _bvh.Count(), build.MaxNodeDepth, build.MaxNodeTriangles); } bool MeshAccelerationStructure::PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance) const @@ -566,4 +677,80 @@ bool MeshAccelerationStructure::RayCast(const Ray& ray, Real& hitDistance, Vecto } } +MeshAccelerationStructure::GPU::~GPU() +{ + SAFE_DELETE_GPU_RESOURCE(BVHBuffer); + SAFE_DELETE_GPU_RESOURCE(VertexBuffer); + SAFE_DELETE_GPU_RESOURCE(IndexBuffer); +} + +MeshAccelerationStructure::GPU::operator bool() const +{ + // Index buffer is initialized as last one so all other buffers are fine too + return IndexBuffer && IndexBuffer->GetSize() != 0; +} + +MeshAccelerationStructure::GPU MeshAccelerationStructure::ToGPU() +{ + PROFILE_CPU(); + GPU gpu; + + // GPU BVH operates on a single mesh with 32-bit indices + MergeMeshes(true); + + // Construct BVH + const int32 BVH_STACK_SIZE = 32; // This must match HLSL shader + BuildBVH(0, BVH_STACK_SIZE); + + // Upload BVH + { + Array bvhData; + bvhData.Resize(_bvh.Count()); + for (int32 i = 0; i < _bvh.Count(); i++) + { + const auto& src = _bvh.Get()[i]; + auto& dst = bvhData.Get()[i]; + dst.BoundsMin = src.Bounds.Minimum; + dst.BoundsMax = src.Bounds.Maximum; + if (src.Leaf.IsLeaf) + { + dst.Index = src.Leaf.TriangleIndex * 3; + dst.Count = src.Leaf.TriangleCount * 3; + } + else + { + dst.Index = src.Node.ChildIndex; + dst.Count = -(int32)src.Node.ChildrenCount; // Mark as non-leaf + ASSERT(src.Node.ChildrenCount == 2); // GPU shader is hardcoded for 2 children per node + } + } + gpu.BVHBuffer = GPUBuffer::New(); + auto desc =GPUBufferDescription::Structured(_bvh.Count(), sizeof(GPUBVH)); + desc.InitData = bvhData.Get(); + if (gpu.BVHBuffer->Init(desc)) + return gpu; + } + + // Upload vertex buffer + { + const Mesh& mesh = _meshes[0]; + gpu.VertexBuffer = GPUBuffer::New(); + auto desc = GPUBufferDescription::Raw(mesh.Vertices * sizeof(Float3), GPUBufferFlags::ShaderResource); + desc.InitData = mesh.VertexBuffer.Get(); + if (gpu.VertexBuffer->Init(desc)) + return gpu; + } + + // Upload index buffer + { + const Mesh& mesh = _meshes[0]; + gpu.IndexBuffer = GPUBuffer::New(); + auto desc = GPUBufferDescription::Raw(mesh.Indices * sizeof(uint32), GPUBufferFlags::ShaderResource); + desc.InitData = mesh.IndexBuffer.Get(); + gpu.IndexBuffer->Init(desc); + } + + return gpu; +} + #endif diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h index 7e158e742..60ff772f7 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h @@ -11,6 +11,7 @@ class Model; class ModelData; +class GPUBuffer; /// /// Acceleration Structure utility for robust ray tracing mesh geometry with optimized data structure. @@ -20,6 +21,7 @@ class FLAXENGINE_API MeshAccelerationStructure private: struct Mesh { + Model* Asset; BytesContainer IndexBuffer, VertexBuffer; int32 Indices, Vertices; bool Use16BitIndexBuffer; @@ -49,14 +51,25 @@ private: }; }; + struct BVHBuild + { + int32 MaxLeafSize, MaxDepth; + int32 NodeDepth = 0; + int32 MaxNodeDepth = 0; + int32 MaxNodeTriangles = 0; + Array Scratch; + }; + Array> _meshes; Array _bvh; - void BuildBVH(int32 node, int32 maxLeafSize, Array& scratch); + void BuildBVH(int32 node, BVHBuild& build); bool PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const; bool RayCastBVH(int32 node, const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle) const; public: + ~MeshAccelerationStructure(); + // Adds the model geometry for the build to the structure. void Add(Model* model, int32 lodIndex); @@ -66,14 +79,56 @@ public: // Adds the triangles geometry for the build to the structure. void Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy = false); + // Merges all added meshes into a single mesh (to reduce number of BVH nodes). Required for GPU BVH build. + void MergeMeshes(bool force16BitIndexBuffer = false); + // Builds Bounding Volume Hierarchy (BVH) structure for accelerated geometry queries. - void BuildBVH(int32 maxLeafSize = 16); + void BuildBVH(int32 maxLeafSize = 16, int32 maxDepth = 0); // Queries the closest triangle. bool PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance = MAX_Real) const; // Ray traces the triangles. bool RayCast(const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle, Real maxDistance = MAX_Real) const; + +public: + struct GPU + { + GPUBuffer* BVHBuffer; + GPUBuffer* VertexBuffer; + GPUBuffer* IndexBuffer; + bool Valid; + + GPU() + : BVHBuffer(nullptr) + , VertexBuffer(nullptr) + , IndexBuffer(nullptr) + { + } + + GPU(GPU&& other) noexcept + : BVHBuffer(other.BVHBuffer) + , VertexBuffer(other.VertexBuffer) + , IndexBuffer(other.IndexBuffer) + { + other.BVHBuffer = nullptr; + other.VertexBuffer = nullptr; + other.IndexBuffer = nullptr; + } + + GPU& operator=(GPU other) + { + Swap(*this, other); + return *this; + } + + ~GPU(); + + operator bool() const; + }; + + // Converts the acceleration structure data to GPU format for raytracing inside a shader. + GPU ToGPU(); }; #endif diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 0ae1e0639..843822b98 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -8,12 +8,14 @@ #include "Engine/Core/RandomStream.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Ray.h" +#include "Engine/Core/Utilities.h" #include "Engine/Platform/ConditionVariable.h" #include "Engine/Profiler/Profiler.h" #include "Engine/Threading/JobSystem.h" #include "Engine/Threading/Threading.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUBuffer.h" +#include "Engine/Graphics/GPUTimerQuery.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Async/GPUTask.h" #include "Engine/Graphics/Shaders/GPUShader.h" @@ -26,7 +28,6 @@ #include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Engine/Units.h" #if USE_EDITOR -#include "Engine/Core/Utilities.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" @@ -81,14 +82,18 @@ class GPUModelSDFTask : public GPUTask { ConditionVariable* _signal; AssetReference _shader; + MeshAccelerationStructure* _scene; Model* _inputModel; const ModelData* _modelData; int32 _lodIndex; + float _backfacesThreshold; Int3 _resolution; ModelBase::SDFData* _sdf; - GPUBuffer *_sdfSrc, *_sdfDst; GPUTexture* _sdfResult; Float3 _xyzToLocalMul, _xyzToLocalAdd; +#if GPU_ALLOW_PROFILE_EVENTS + GPUTimerQuery* _timerQuery; +#endif const uint32 ThreadGroupSize = 64; GPU_CB_STRUCT(Data { @@ -96,7 +101,7 @@ class GPUModelSDFTask : public GPUTask uint32 ResolutionSize; float MaxDistance; uint32 VertexStride; - int32 Index16bit; + float BackfacesThreshold; uint32 TriangleCount; Float3 VoxelToPosMul; float WorldUnitsPerVoxel; @@ -105,47 +110,46 @@ class GPUModelSDFTask : public GPUTask }); public: - GPUModelSDFTask(ConditionVariable& signal, Model* inputModel, const ModelData* modelData, int32 lodIndex, const Int3& resolution, ModelBase::SDFData* sdf, GPUTexture* sdfResult, const Float3& xyzToLocalMul, const Float3& xyzToLocalAdd) - : GPUTask(Type::Custom) + GPUModelSDFTask(ConditionVariable& signal, MeshAccelerationStructure* scene, Model* inputModel, const ModelData* modelData, int32 lodIndex, const Int3& resolution, ModelBase::SDFData* sdf, GPUTexture* sdfResult, const Float3& xyzToLocalMul, const Float3& xyzToLocalAdd, float backfacesThreshold) + : GPUTask(Type::Custom, GPU_ALLOW_PROFILE_EVENTS ? 4 : GPU_ASYNC_LATENCY) // Fix timer query result reading with some more latency , _signal(&signal) , _shader(Content::LoadAsyncInternal(TEXT("Shaders/SDF"))) + , _scene(scene) , _inputModel(inputModel) , _modelData(modelData) , _lodIndex(lodIndex) + , _backfacesThreshold(backfacesThreshold) , _resolution(resolution) , _sdf(sdf) - , _sdfSrc(GPUBuffer::New()) - , _sdfDst(GPUBuffer::New()) , _sdfResult(sdfResult) , _xyzToLocalMul(xyzToLocalMul) , _xyzToLocalAdd(xyzToLocalAdd) - { -#if GPU_ENABLE_RESOURCE_NAMING - _sdfSrc->SetName(TEXT("SDFSrc")); - _sdfDst->SetName(TEXT("SDFDst")); +#if GPU_ALLOW_PROFILE_EVENTS + , _timerQuery(GPUDevice::Instance->CreateTimerQuery()) #endif + { } ~GPUModelSDFTask() { - SAFE_DELETE_GPU_RESOURCE(_sdfSrc); - SAFE_DELETE_GPU_RESOURCE(_sdfDst); +#if GPU_ALLOW_PROFILE_EVENTS + SAFE_DELETE_GPU_RESOURCE(_timerQuery); +#endif } Result run(GPUTasksContext* tasksContext) override { PROFILE_GPU_CPU("GPUModelSDFTask"); GPUContext* context = tasksContext->GPU; +#if GPU_ALLOW_PROFILE_EVENTS + _timerQuery->Begin(); +#endif // Allocate resources if (_shader == nullptr || _shader->WaitForLoaded()) return Result::Failed; GPUShader* shader = _shader->GetShader(); const uint32 resolutionSize = _resolution.X * _resolution.Y * _resolution.Z; - auto desc = GPUBufferDescription::Typed(resolutionSize, PixelFormat::R32_UInt, true); - // TODO: use transient texture (single frame) - if (_sdfSrc->Init(desc) || _sdfDst->Init(desc)) - return Result::Failed; auto cb = shader->GetCB(0); Data data; data.Resolution = _resolution; @@ -154,6 +158,13 @@ public: data.WorldUnitsPerVoxel = _sdf->WorldUnitsPerVoxel; data.VoxelToPosMul = _xyzToLocalMul; data.VoxelToPosAdd = _xyzToLocalAdd; + data.BackfacesThreshold = _backfacesThreshold - 0.05f; // Bias a bit + + // Send BVH to the GPU + auto bvh = _scene->ToGPU(); + CHECK_RETURN(bvh.BVHBuffer && bvh.VertexBuffer && bvh.IndexBuffer, Result::Failed); + data.VertexStride = sizeof(Float3); + data.TriangleCount = bvh.IndexBuffer->GetElementsCount() / 3; // Dispatch in 1D and fallback to 2D when using large resolution Int3 threadGroups(Math::CeilToInt((float)resolutionSize / ThreadGroupSize), 1, 1); @@ -165,159 +176,34 @@ public: } data.ThreadGroupsX = threadGroups.X; - // Init SDF volume + // Init constants context->BindCB(0, cb); context->UpdateCB(cb, &data); - context->BindUA(0, _sdfSrc->View()); - context->Dispatch(shader->GetCS("CS_Init"), threadGroups.X, threadGroups.Y, threadGroups.Z); - // Rendering input triangles into the SDF volume - if (_inputModel) - { - PROFILE_GPU_CPU_NAMED("Rasterize"); - const ModelLOD& lod = _inputModel->LODs[Math::Clamp(_lodIndex, _inputModel->HighestResidentLODIndex(), _inputModel->LODs.Count() - 1)]; - GPUBuffer *vbTemp = nullptr, *ibTemp = nullptr; - for (int32 i = 0; i < lod.Meshes.Count(); i++) - { - const Mesh& mesh = lod.Meshes[i]; - const MaterialSlot& materialSlot = _inputModel->MaterialSlots[mesh.GetMaterialSlotIndex()]; - if (materialSlot.Material && !materialSlot.Material->WaitForLoaded()) - { - // Skip transparent materials - if (materialSlot.Material->GetInfo().BlendMode != MaterialBlendMode::Opaque) - continue; - } - - GPUBuffer* vb = mesh.GetVertexBuffer(0); - GPUBuffer* ib = mesh.GetIndexBuffer(); - data.Index16bit = mesh.Use16BitIndexBuffer() ? 1 : 0; - data.VertexStride = vb->GetStride(); - data.TriangleCount = mesh.GetTriangleCount(); - const uint32 groups = Math::CeilToInt((float)data.TriangleCount / ThreadGroupSize); - if (groups > GPU_MAX_CS_DISPATCH_THREAD_GROUPS) - { - // TODO: support larger meshes via 2D dispatch - LOG(Error, "Not supported mesh with {} triangles.", data.TriangleCount); - continue; - } - context->UpdateCB(cb, &data); - if (!EnumHasAllFlags(vb->GetDescription().Flags, GPUBufferFlags::RawBuffer | GPUBufferFlags::ShaderResource)) - { - desc = GPUBufferDescription::Raw(vb->GetSize(), GPUBufferFlags::ShaderResource); - // TODO: use transient buffer (single frame) - if (!vbTemp) - { - vbTemp = GPUBuffer::New(); -#if GPU_ENABLE_RESOURCE_NAMING - vbTemp->SetName(TEXT("SDFvb")); -#endif - } - vbTemp->Init(desc); - context->CopyBuffer(vbTemp, vb, desc.Size); - vb = vbTemp; - } - if (!EnumHasAllFlags(ib->GetDescription().Flags, GPUBufferFlags::RawBuffer | GPUBufferFlags::ShaderResource)) - { - desc = GPUBufferDescription::Raw(ib->GetSize(), GPUBufferFlags::ShaderResource); - // TODO: use transient buffer (single frame) - if (!ibTemp) - { - ibTemp = GPUBuffer::New(); -#if GPU_ENABLE_RESOURCE_NAMING - ibTemp->SetName(TEXT("SDFib")); -#endif - } - ibTemp->Init(desc); - context->CopyBuffer(ibTemp, ib, desc.Size); - ib = ibTemp; - } - context->BindSR(0, vb->View()); - context->BindSR(1, ib->View()); - context->Dispatch(shader->GetCS("CS_RasterizeTriangle"), groups, 1, 1); - } - SAFE_DELETE_GPU_RESOURCE(vbTemp); - SAFE_DELETE_GPU_RESOURCE(ibTemp); - } - else if (_modelData) - { - PROFILE_GPU_CPU_NAMED("Rasterize"); - const ModelLodData& lod = _modelData->LODs[Math::Clamp(_lodIndex, 0, _modelData->LODs.Count() - 1)]; - auto vb = GPUBuffer::New(); - auto ib = GPUBuffer::New(); -#if GPU_ENABLE_RESOURCE_NAMING - vb->SetName(TEXT("SDFvb")); - ib->SetName(TEXT("SDFib")); -#endif - for (int32 i = 0; i < lod.Meshes.Count(); i++) - { - const MeshData* mesh = lod.Meshes[i]; - const MaterialSlotEntry& materialSlot = _modelData->Materials[mesh->MaterialSlotIndex]; - auto material = Content::LoadAsync(materialSlot.AssetID); - if (material && !material->WaitForLoaded()) - { - // Skip transparent materials - if (material->GetInfo().BlendMode != MaterialBlendMode::Opaque) - continue; - } - - data.Index16bit = 0; - data.VertexStride = sizeof(Float3); - data.TriangleCount = mesh->Indices.Count() / 3; - const uint32 groups = Math::CeilToInt((float)data.TriangleCount / ThreadGroupSize); - if (groups > GPU_MAX_CS_DISPATCH_THREAD_GROUPS) - { - // TODO: support larger meshes via 2D dispatch - LOG(Error, "Not supported mesh with {} triangles.", data.TriangleCount); - continue; - } - context->UpdateCB(cb, &data); - desc = GPUBufferDescription::Raw(mesh->Positions.Count() * sizeof(Float3), GPUBufferFlags::ShaderResource); - desc.InitData = mesh->Positions.Get(); - // TODO: use transient buffer (single frame) - vb->Init(desc); - desc = GPUBufferDescription::Raw(mesh->Indices.Count() * sizeof(uint32), GPUBufferFlags::ShaderResource); - desc.InitData = mesh->Indices.Get(); - // TODO: use transient buffer (single frame) - ib->Init(desc); - context->BindSR(0, vb->View()); - context->BindSR(1, ib->View()); - context->Dispatch(shader->GetCS("CS_RasterizeTriangle"), groups, 1, 1); - } - SAFE_DELETE_GPU_RESOURCE(vb); - SAFE_DELETE_GPU_RESOURCE(ib); - } - - // Convert SDF volume data back to floats - context->Dispatch(shader->GetCS("CS_Resolve"), threadGroups.X, threadGroups.Y, threadGroups.Z); - - // Run linear flood-fill loop to populate all voxels with valid distances (spreads the initial values from triangles rasterization) - { - PROFILE_GPU_CPU_NAMED("FloodFill"); - auto csFloodFill = shader->GetCS("CS_FloodFill"); - const int32 floodFillIterations = Math::Max(_resolution.MaxValue() / 2 + 1, 8); - for (int32 floodFill = 0; floodFill < floodFillIterations; floodFill++) - { - context->ResetUA(); - context->BindUA(0, _sdfDst->View()); - context->BindSR(0, _sdfSrc->View()); - context->Dispatch(csFloodFill, threadGroups.X, threadGroups.Y, threadGroups.Z); - Swap(_sdfSrc, _sdfDst); - } - } - - // Encode SDF values into output storage - context->ResetUA(); - context->BindSR(0, _sdfSrc->View()); - // TODO: update GPU SDF texture within this task to skip additional CPU->GPU copy - auto sdfTextureDesc = GPUTextureDescription::New3D(_resolution.X, _resolution.Y, _resolution.Z, PixelFormat::R16_UNorm, GPUTextureFlags::UnorderedAccess | GPUTextureFlags::RenderTarget); + // Allocate output texture + auto sdfTextureDesc = GPUTextureDescription::New3D(_resolution.X, _resolution.Y, _resolution.Z, PixelFormat::R16_UNorm, GPUTextureFlags::UnorderedAccess); // TODO: use transient texture (single frame) auto sdfTexture = GPUTexture::New(); #if GPU_ENABLE_RESOURCE_NAMING sdfTexture->SetName(TEXT("SDFTexture")); #endif sdfTexture->Init(sdfTextureDesc); - context->BindUA(1, sdfTexture->ViewVolume()); - context->Dispatch(shader->GetCS("CS_Encode"), threadGroups.X, threadGroups.Y, threadGroups.Z); + + // Renders directly to the output texture + context->BindUA(0, sdfTexture->ViewVolume()); + + // Init the volume (rasterization mixes with existing contents) + context->Dispatch(shader->GetCS("CS_Init"), threadGroups.X, threadGroups.Y, threadGroups.Z); + + // Render input triangles into the SDF volume + { + PROFILE_GPU("Rasterize"); + context->BindSR(0, bvh.VertexBuffer->View()); + context->BindSR(1, bvh.IndexBuffer->View()); + context->BindSR(2, bvh.BVHBuffer->View()); + auto* rasterizeCS = shader->GetCS("CS_RasterizeTriangles"); + context->Dispatch(rasterizeCS, threadGroups.X, threadGroups.Y, threadGroups.Z); + } // Copy result data into readback buffer if (_sdfResult) @@ -329,6 +215,9 @@ public: SAFE_DELETE_GPU_RESOURCE(sdfTexture); +#if GPU_ALLOW_PROFILE_EVENTS + _timerQuery->End(); +#endif return Result::Ok; } @@ -336,6 +225,10 @@ public: { GPUTask::OnSync(); _signal->NotifyOne(); +#if GPU_ALLOW_PROFILE_EVENTS + if (_timerQuery->HasResult()) + LOG(Info, "GPU SDF generation took {} ms", Utilities::RoundTo1DecimalPlace(_timerQuery->GetResult())); +#endif } void OnFail() override @@ -366,9 +259,11 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, return true; ModelBase::SDFData sdf; sdf.WorldUnitsPerVoxel = METERS_TO_UNITS(0.1f) / Math::Max(resolutionScale, 0.0001f); // 1 voxel per 10 centimeters +#if 0 const float boundsMargin = sdf.WorldUnitsPerVoxel * 0.5f; // Add half-texel margin around the mesh bounds.Minimum -= boundsMargin; bounds.Maximum += boundsMargin; +#endif const Float3 size = bounds.GetSize(); Int3 resolution(Float3::Ceil(Float3::Clamp(size / sdf.WorldUnitsPerVoxel, 4, 256))); Float3 uvwToLocalMul = size; @@ -443,6 +338,13 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, // http://ramakarl.com/pdfs/2016_Hoetzlein_GVDB.pdf // https://www.cse.chalmers.se/~uffe/HighResolutionSparseVoxelDAGs.pdf + // Setup acceleration structure for fast ray tracing the mesh triangles + MeshAccelerationStructure scene; + if (inputModel) + scene.Add(inputModel, lodIndex); + else if (modelData) + scene.Add(modelData, lodIndex); + // Check if run SDF generation on a GPU via Compute Shader or on a Job System useGPU &= GPUDevice::Instance && GPUDevice::Instance->GetState() == GPUDevice::DeviceState::Ready @@ -463,7 +365,7 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, // Run SDF generation via GPU async task ConditionVariable signal; CriticalSection mutex; - Task* task = New(signal, inputModel, modelData, lodIndex, resolution, &sdf, sdfResult, xyzToLocalMul, xyzToLocalAdd); + Task* task = New(signal, &scene, inputModel, modelData, lodIndex, resolution, &sdf, sdfResult, xyzToLocalMul, xyzToLocalAdd, backfacesThreshold); task->Start(); mutex.Lock(); signal.Wait(mutex); @@ -487,16 +389,10 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, } else { - // Setup acceleration structure for fast ray tracing the mesh triangles - MeshAccelerationStructure scene; - if (inputModel) - scene.Add(inputModel, lodIndex); - else if (modelData) - scene.Add(modelData, lodIndex); scene.BuildBVH(); // Brute-force for each voxel to calculate distance to the closest triangle with point query and distance sign by raycasting around the voxel - constexpr int32 sampleCount = 12; + constexpr int32 sampleCount = BUILD_DEBUG ? 6 : 12; Float3 sampleDirections[sampleCount]; { RandomStream rand; @@ -524,36 +420,30 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, Real minDistance = sdf.MaxDistance; Vector3 voxelPos = Float3((float)x, (float)y, (float)z) * xyzToLocalMul + xyzToLocalAdd; - // Point query to find the distance to the closest surface - scene.PointQuery(voxelPos, minDistance, hitPoint, hitTriangle); - // Raycast samples around voxel to count triangle backfaces hit - int32 hitBackCount = 0, hitCount = 0; + int32 hitBackCount = 0, minBackfaceHitCount = (int32)(sampleCount * backfacesThreshold); for (int32 sample = 0; sample < sampleCount; sample++) { Ray sampleRay(voxelPos, sampleDirections[sample]); sampleRay.Position -= sampleRay.Direction * 0.0001f; // Apply small margin if (scene.RayCast(sampleRay, hitDistance, hitNormal, hitTriangle)) { - if (hitDistance < minDistance) - minDistance = hitDistance; - hitCount++; - const bool backHit = Float3::Dot(sampleRay.Direction, hitTriangle.GetNormal()) > 0; - if (backHit) - hitBackCount++; + minDistance = Math::Min(hitDistance, minDistance); + if (Float3::Dot(sampleRay.Direction, hitTriangle.GetNormal()) > 0) + { + if (++hitBackCount >= minBackfaceHitCount) + break; + } } } - float distance = (float)minDistance; - // TODO: surface thickness threshold? shift reduce distance for all voxels by something like 0.01 to enlarge thin geometry - // if ((float)hitBackCount > (float)hitCount * 0.3f && hitCount != 0) - if ((float)hitBackCount > (float)sampleCount * backfacesThreshold && hitCount != 0) - { - // Voxel is inside the geometry so turn it into negative distance to the surface - distance *= -1; - } + // Point query to find the distance to the closest surface + scene.PointQuery(voxelPos, minDistance, hitPoint, hitTriangle, minDistance); + if (hitBackCount >= minBackfaceHitCount) + minDistance *= -1; // Voxel is inside the geometry so turn it into negative distance to the surface + const int32 xAddress = x + yAddress; - formatWrite(voxels.Get() + xAddress * formatStride, distance * encodeMAD.X + encodeMAD.Y); + formatWrite(voxels.Get() + xAddress * formatStride, minDistance * encodeMAD.X + encodeMAD.Y); } } }; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index b88bc4ef3..7c74ef3f2 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -65,6 +65,8 @@ #endif +#undef MessageBox + static void stbWrite(void* context, void* data, int size) { auto file = (FileWriteStream*)context; diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 868db4b89..a227e5acd 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -303,6 +303,12 @@ namespace FlaxEngine.GUI [EditorDisplay("Text Style"), EditorOrder(2023), ExpandGroups] public Color TextColor { get; set; } + /// + /// Gets or sets the color used to display highlighted text. + /// + [EditorDisplay("Text Style"), EditorOrder(2024), ExpandGroups] + public Color TextColorHighlighted { get; set; } + /// /// Gets or sets the horizontal text alignment within the control bounds. /// @@ -386,6 +392,7 @@ namespace FlaxEngine.GUI var style = Style.Current; Font = new FontReference(style.FontMedium); TextColor = style.Foreground; + TextColorHighlighted = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; BackgroundColorSelected = BackgroundColor; @@ -587,8 +594,8 @@ namespace FlaxEngine.GUI X = margin, Size = new Float2(size.X - margin, size.Y), Font = Font, - TextColor = Color.White * 0.9f, - TextColorHighlighted = Color.White, + TextColor = TextColor * 0.9f, + TextColorHighlighted = TextColorHighlighted, HorizontalAlignment = HorizontalAlignment, VerticalAlignment = VerticalAlignment, Text = _items[i], @@ -749,7 +756,7 @@ namespace FlaxEngine.GUI // Draw text of the selected item var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); - var textColor = TextColor; + var textColor = (IsMouseOver || IsNavFocused) ? TextColorHighlighted : TextColor; string text = _items[_selectedIndex]; string format = TextFormat != null ? TextFormat : null; if (!string.IsNullOrEmpty(format)) diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 0bfa799c2..de80f9fc5 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -104,13 +104,20 @@ namespace FlaxEngine.GUI /// /// Gets or sets a value indicating whether enable drop down icon drawing. /// - [EditorOrder(1)] + [EditorOrder(2)] public bool EnableDropDownIcon { get; set; } + /// + /// Get or sets a value indicating whether the panel can be opened or closed via the user interacting with the ui. + /// Changing the open/ closed state from code or the Properties panel will still work regardless. + /// + [EditorOrder(1)] + public bool CanOpenClose { get; set; } = true; + /// /// Gets or sets a value indicating whether to enable containment line drawing, /// - [EditorOrder(2)] + [EditorOrder(3)] public bool EnableContainmentLines { get; set; } = false; /// @@ -369,7 +376,7 @@ namespace FlaxEngine.GUI } // Header - var color = _mouseOverHeader ? HeaderColorMouseOver : HeaderColor; + var color = _mouseOverHeader && CanOpenClose ? HeaderColorMouseOver : HeaderColor; if (color.A > 0.0f) { Render2D.FillRectangle(new Rectangle(0, 0, Width, HeaderHeight), color); @@ -510,7 +517,7 @@ namespace FlaxEngine.GUI if (button == MouseButton.Left && _mouseButtonLeftDown) { _mouseButtonLeftDown = false; - if (_mouseOverHeader) + if (_mouseOverHeader && CanOpenClose) Toggle(); return true; } @@ -540,7 +547,7 @@ namespace FlaxEngine.GUI if (button == MouseButton.Left && _mouseButtonLeftDown) { _mouseButtonLeftDown = false; - if (_mouseOverHeader) + if (_mouseOverHeader && CanOpenClose) Toggle(); return true; } diff --git a/Source/Engine/UI/GUI/RenderOutputControl.cs b/Source/Engine/UI/GUI/RenderOutputControl.cs index 9025cbc04..8dc6707bf 100644 --- a/Source/Engine/UI/GUI/RenderOutputControl.cs +++ b/Source/Engine/UI/GUI/RenderOutputControl.cs @@ -180,7 +180,7 @@ namespace FlaxEngine.GUI } /// - public override void Draw() + public override void DrawSelf() { var bounds = new Rectangle(Float2.Zero, Size); @@ -205,21 +205,6 @@ namespace FlaxEngine.GUI Render2D.DrawTexture(buffer, bounds, color); else Render2D.FillRectangle(bounds, Color.Black); - - // Push clipping mask - if (ClipChildren) - { - GetDesireClientArea(out var clientArea); - Render2D.PushClip(ref clientArea); - } - - DrawChildren(); - - // Pop clipping mask - if (ClipChildren) - { - Render2D.PopClip(); - } } /// diff --git a/Source/Engine/Video/MF/VideoBackendMF.cpp b/Source/Engine/Video/MF/VideoBackendMF.cpp index 75950f3aa..df24f5eed 100644 --- a/Source/Engine/Video/MF/VideoBackendMF.cpp +++ b/Source/Engine/Video/MF/VideoBackendMF.cpp @@ -12,6 +12,12 @@ #if USE_EDITOR #include "Editor/Editor.h" #endif +#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT +#define USE_STOCKD3D 0 +#include +#include +#include +#else #include #if WINVER >= _WIN32_WINNT_WINBLUE && WINVER < _WIN32_WINNT_WIN10 // Fix compilation for Windows 8.1 on the latest Windows SDK @@ -24,6 +30,7 @@ typedef enum _MFVideoSphericalFormat { } MFVideoSphericalFormat; #include #include #include +#endif #define VIDEO_API_MF_ERROR(api, err) LOG(Warning, "[VideoBackendMF] {} failed with error 0x{:x}", TEXT(#api), (uint64)err) @@ -111,7 +118,7 @@ namespace MF player.Format = PixelFormat::NV12; else if (subtype == MFVideoFormat_YUY2) player.Format = PixelFormat::YUY2; -#if (WDK_NTDDI_VERSION >= NTDDI_WIN10) +#if (WDK_NTDDI_VERSION >= NTDDI_WIN10) && PLATFORM_WINDOWS else if (subtype == MFVideoFormat_A2R10G10B10) player.Format = PixelFormat::R10G10B10A2_UNorm; else if (subtype == MFVideoFormat_A16B16G16R16F) @@ -120,6 +127,14 @@ namespace MF else { // Reconfigure decoder to output supported format by force +#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT + // Xbox supports NV12 via HV decoder + auto fallbackFormat = PixelFormat::NV12; + GUID fallbackFormatGuid = MFVideoFormat_NV12; +#else + auto fallbackFormat = PixelFormat::YUY2; + GUID fallbackFormatGuid = MFVideoFormat_YUY2; +#endif IMFMediaType* customType = nullptr; hr = MFCreateMediaType(&customType); if (FAILED(hr)) @@ -128,7 +143,7 @@ namespace MF goto END; } customType->SetGUID(MF_MT_MAJOR_TYPE, majorType); - customType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2); + customType->SetGUID(MF_MT_SUBTYPE, fallbackFormatGuid); MFSetAttributeSize(customType, MF_MT_FRAME_SIZE, width, height); hr = playerMF.SourceReader->SetCurrentMediaType(streamIndex, nullptr, customType); if (FAILED(hr)) @@ -136,12 +151,13 @@ namespace MF VIDEO_API_MF_ERROR(SetCurrentMediaType, hr); goto END; } - player.Format = PixelFormat::YUY2; + player.Format = fallbackFormat; customType->Release(); } } else if (majorType == MFMediaType_Audio) { +#if !(PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT) // TODO: fix missing MFAudioFormat_PCM/MFAudioFormat_Float convertion on Xbox (bug?) player.AudioInfo.SampleRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0); player.AudioInfo.NumChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0); player.AudioInfo.BitDepth = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16); @@ -165,6 +181,7 @@ namespace MF } customType->Release(); } +#endif } result = false; diff --git a/Source/Engine/Video/Video.Build.cs b/Source/Engine/Video/Video.Build.cs index fe99c1433..3975480ee 100644 --- a/Source/Engine/Video/Video.Build.cs +++ b/Source/Engine/Video/Video.Build.cs @@ -22,8 +22,6 @@ public class Video : EngineModule { case TargetPlatform.Windows: case TargetPlatform.UWP: - case TargetPlatform.XboxOne: - case TargetPlatform.XboxScarlett: // Media Foundation options.SourcePaths.Add(Path.Combine(FolderPath, "MF")); options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF"); @@ -34,6 +32,15 @@ public class Video : EngineModule options.OutputFiles.Add("mfreadwrite.lib"); options.OutputFiles.Add("mfuuid.lib"); break; + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + // Media Foundation + options.SourcePaths.Add(Path.Combine(FolderPath, "MF")); + options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF"); + options.OutputFiles.Add("mfplat.lib"); + options.OutputFiles.Add("mfreadwrite.lib"); + options.OutputFiles.Add("mfuuid.lib"); + break; case TargetPlatform.Mac: case TargetPlatform.iOS: // AVFoundation diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index b6616d159..faf344263 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -146,6 +146,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) Box* b2 = node->GetBox(1); Value v1 = tryGetValue(b1, 0, Value::Zero); Value v2 = tryGetValue(b2, 1, Value::Zero); + if (SanitizeMathValue(v1, node, b1, &value)) + break; if (b1->HasConnection()) v2 = v2.Cast(v1.Type); else @@ -251,7 +253,10 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) // Lerp case 25: { - Value a = tryGetValue(node->GetBox(0), 0, Value::Zero); + auto boxA = node->GetBox(0); + Value a = tryGetValue(boxA, 0, Value::Zero); + if (SanitizeMathValue(a, node, boxA, &value)) + break; Value b = tryGetValue(node->GetBox(1), 1, Value::One).Cast(a.Type); Value alpha = tryGetValue(node->GetBox(2), 2, Value::Zero).Cast(ValueType::Float); String text = String::Format(TEXT("lerp({0}, {1}, {2})"), a.Value, b.Value, alpha.Value); @@ -704,8 +709,8 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) case 16: { // Get the variable type - auto asset = Assets.LoadAsync((Guid)node->Values[0]); - if (!asset || asset->WaitForLoaded()) + auto asset = Assets.Load((Guid)node->Values[0]); + if (!asset) { OnError(node, box, TEXT("Failed to load Gameplay Global asset.")); value = Value::Zero; @@ -1364,6 +1369,20 @@ SerializedMaterialParam& ShaderGenerator::findOrAddGlobalSDF() return param; } +bool ShaderGenerator::SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid) +{ + bool invalid = value.Type == VariantType::Object; + if (invalid) + { + OnError(node, box, TEXT("Invalid input type for math operation")); + if (resultOnInvalid) + *resultOnInvalid = Value::Zero; + else + value = Value::Zero; + } + return invalid; +} + String ShaderGenerator::getLocalName(int32 index) { return TEXT("local") + StringUtils::ToString(index); diff --git a/Source/Engine/Visject/ShaderGraph.h b/Source/Engine/Visject/ShaderGraph.h index ab0e7d405..5b17604d0 100644 --- a/Source/Engine/Visject/ShaderGraph.h +++ b/Source/Engine/Visject/ShaderGraph.h @@ -255,6 +255,8 @@ protected: SerializedMaterialParam& findOrAddTextureGroupSampler(int32 index); SerializedMaterialParam& findOrAddGlobalSDF(); + bool SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid = nullptr); + static String getLocalName(int32 index); static String getParamName(int32 index); }; diff --git a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java index b19663fc9..ecb27b013 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +// Copyright (c) Wojciech Figat. All rights reserved. package com.flaxengine; diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a index 5c5eebbb9..6bdec7f7a 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7179291d2efd8d3c8c8cd494a2c65e5b506e52929b962223f0af9faf2ba09382 -size 20142390 +oid sha256:4743608626b06af2df45dfb3af6b5ca97ecf08fd35d2a7dd14ac9f19d3aadd9e +size 34362142 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a index 1a8237027..c5745f338 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c6add4697d7513883ea5583587a50b3b01ff0156d06e2ccc48070e23c415bf4 -size 31902 +oid sha256:794ce94a3f89c080a59889c7d49f98a5e3ce004c8c5fea533370e6dc9a388396 +size 30214 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a index 3ce822729..5e9764557 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:254756fe20ea96e73bd82a9a491c1f18c852aae33dd8bf9d6648c91d0247c7eb +oid sha256:0fd14ac5c4b03136ad2734a3b866c6524cf817afa1e6294c8f1408cac44f5b24 size 1068 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a index 0235bd398..56ac04634 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9e79710c30285e0c5e4fe2ff1e9c47d06d9457428c177c6580e1ec8966dd079 -size 5287654 +oid sha256:1d3af5ec52f4d1aa80ed28e41a40bd53f3162f2c42965e162a0fca8329d479a6 +size 5370940 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a index 573dfb245..51e71d359 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6234ee8e0cea08be467f8d13994fee8b94dfa2ed2147460a3268dbe6cfeb87a6 -size 553518 +oid sha256:62687b3036959f1b873623202dbfef3706c154ceb3fa26e87dea6702d9bab657 +size 553390 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a index 7ac2e3cc5..ca8f14082 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b78a2a48c4c17a066164b7d151260d836e01ddb0d4087f25a6266aafc220eb5 -size 3332 +oid sha256:90b0951e9d0fcbcd63d53c60981d342bcd5d3c40f04983ee281fb45821f84ab0 +size 3308 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a index ce233ff4f..b9dad7646 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67afb8e80b768c785905821f4ab4e4491598ab3a69f3fd514d4a9dadf6f80d36 -size 4966 +oid sha256:c5725e2cfe85e4c8a03f407562098306d3d1cc6715c4349288433cde0efcceb2 +size 4918 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a index 98a4a8286..7e702f665 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9cf1125a7e1d0f2fb1a12b6f04537228cd37e3322cf818696a2d3446f97b953 -size 278626 +oid sha256:9929fabaa9ca418b8e48ea1081652fa61180fe0bec0518f5603966f6b4918e5c +size 278562 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a index 242c33ca5..6b596b38b 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19d0245e6dce80bb596ed10a154a5e18a5902df6c3e95c087e856fa6ca25f6f6 -size 4596892 +oid sha256:8b8f2a44b37ac65ea3eaee9ad090f471be3848490017d06980e6224b443a2218 +size 4595548 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a index e7d99ad1f..851680c01 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d44653d8526acb4fd631cf007941a656ab054552b9406cf54199abf7bfab98c +oid sha256:a1126331d2ba6933b1fa70d8d735c766e67785d58c243d960d6c0225f207bdf3 size 28284 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a index 150fe2099..5b547830d 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0667c4d6515bb6c95c9ce5072edc56da7da45b6691eba2d3332f6b1db27c7511 -size 4081244 +oid sha256:d516b0b54861a86ed413f7a0e3358d8ea74952b5ef84871332453f758f0d8920 +size 4081602 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a index 94622de79..8d0a04c50 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db06448b20f3d0b14780730e7e884cb18be4b546dc228b84c0ad8d33b088dcd4 -size 121148 +oid sha256:ee3df94bba27db3a77864816c460c76f82c58a98fd1b3055554fb773232b3fef +size 121028 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a index e49794ebc..c22a96c31 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:419c688357cf3c4b781fa3cae601906be8e46d1de9d1238f8acb482166b5f837 -size 503210 +oid sha256:c22c955f6cf0d5b55033d0acdcae44b6e17e07eaaec884fa5ad80dcef8ddc15d +size 503146 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a index 2da3cad95..5163432ac 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5559f869db603d979003f6d8d3d35179db5193974f39481bc2ee821af27faf3 -size 195454 +oid sha256:12d23592318c74bd3d5aed5134bf8be50fa32c78e66e59236d25620aed7316e9 +size 195302 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a index abcca2a9a..488e90c95 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427613612343bd0ea40d523ea0c7458a6ed927357fe4a277428d4ee0228bad95 -size 1209552 +oid sha256:bd1af110b345dc5548cf07885ea4f0e95bd8f83b4f9c041bfc962a6e72d8cc71 +size 1209456 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a index 12db8e2e6..c4e817356 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b606e398be2f9772da1c0dd0f1b9aead20974cf03eeaa7a2df1a477eb6caff9 -size 6360866 +oid sha256:7d2e9591a28c5d0bf0aee830e340c6e9a1e978aa7f80cdbda002c5fa1fac7196 +size 6359770 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a index 5c0736d35..fca25c5e9 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d101fdbbdb514937ef3ccdbb2a0791542fdb880b0331a59599e1cab3fdaba8d0 -size 8949560 +oid sha256:5205664eaa07b9161d5c9daa4ca7a2e224a7e8d082d13390bd3743e7cd9937cf +size 8105308 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a index 3732827d0..7f73d4824 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dafaa04b6d0896fdb33738ea7104fa47ad150f287d046bd752d7f343efb6bc1a -size 2515558 +oid sha256:17699265b59441501b4837cef88d6f82d7404201f2d05cf2eb35e083b4b63c14 +size 2394830 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a index 2c9b04e3d..622011e31 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e8ee83a8a956e1e700aa5599107a5710d9c17f350a7d9025765b588146272c7 -size 1057884 +oid sha256:e5bdbd77d71f37080617b261aa92b589ea62a2bc0e567755367733535ddf9d60 +size 1071492 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a index 92b3c5545..dd916929d 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3510d6c5585f08fc9fcbf2044bb0dc0238e2501c1914e3b98aef36bc8cd2711 -size 10303430 +oid sha256:d9e8469042e4734543f142f9559347bd1e36bc897b8962ae4f58fcec985e70ba +size 10261802 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a index f722e3a16..f08214054 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b7f66f58d5e7dabe0a0ec7e01ae5dd3683c2ae474ac6dbc133393b2be715bbf -size 574086 +oid sha256:ab21e8f683649d34388dbdddafb33ac4ba152e0e0fb1d631e03641e3bfc484c3 +size 767214 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a index 5d8a644ec..a77930866 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:678c906be4431bbf7b1a5e915e587f0e1f4f0c14cd2dc30a90e44e669d7f690c -size 1123598 +oid sha256:0885cd644e330c5b622e1312395125c2f14ac78deaf9ca58b66fdcfa074d28bb +size 1136190 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a index 0e82c48ea..9f830b8e1 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5f1fad7d2bc3a7768ea1e8a075315c371064cfd5cfcc64f9d4a55837e304b3a -size 32314 +oid sha256:9a1f4379e8262064813ab001df54d5759077d5c19191ad464374a07073d7db8e +size 34130 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a index 2a0348bcd..b88eb07e6 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:762b5fd6fe17dc4a99fd49488b32486e6744aef278a44c9843bdda05972e1287 -size 1565506 +oid sha256:3e51434c5af3558b73ced5051a239b33077bce527d25e5c71602e7b27b65c80c +size 3506588 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a index 0feb18fa5..c606738e6 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3257e82c3740aec7b15d065ef0b89064b971f77db4251c220fd277b3a96ac19d -size 294326 +oid sha256:fdc79150a98f6f589d5a339dc50e4ad628f92767aa194c8ac4a202ebd78cec1d +size 296926 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a index 59ef26a02..1fc00718c 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50c8f8977fece27302c967c7d65b4a64b1c07f538b54edb0852169bba0bca91b -size 778766 +oid sha256:6d8cbdad00f1a7f0a59f500d6687a64fbcdfc3db7c3acc05a65f68b7ea6566cb +size 782150 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a index 9e6ebdd68..41abc9b37 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc394079fd2b794b637e879357494d0db3e1a8e9690949d7708766ccf360d5eb -size 38204 +oid sha256:1b8377b841a4a871487ff0f591970ada56306087b1f4e3f09a8fb457f6811ac4 +size 38804 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll index e64bb6cd7..c270e6da5 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a54bca64e921d32525ed58c52ce802b4ca2c3f8a92c342296da48c55ec1ea58d -size 1112064 +oid sha256:084675cd25a1090424ae7a31a93d69dd52d2752a9940e28104759e9f87c573b4 +size 1407488 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib index ae9715a8b..9acd14199 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a4ac5af61abb19624dc2ae23bc16af09a062655f3eab89fedf6c0ead6cd935a +oid sha256:b58f1412018cc3618ab579e280bf5fc167479494655dcf440d0239948e44a65d size 37562 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll index 0b80ba584..ae087886a 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:352c4ce7151fe9bc37dd9ceddb958a5ee75851c57df961f6381ec552affba198 -size 4916856 +oid sha256:0c38df5cc6a1263def77ae1c06216e8ec2e57c34be5087efc42e84154afad5cb +size 4741464 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib index 95e6bc166..21d7c5f14 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be5917a82c8dc43cad798f7a6c11b1dd885eec8ee9675057804ec367f322e7ad -size 8314 +oid sha256:2529bc6d166df699df5fd0f77d9101a2bd24f3231b4040ef72feac5851801674 +size 7930 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll index 4e56df12a..2c0773566 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f504ba5f5173c50ba4ce2771c3d32618f886e1c990960ba706f58872224541b -size 14722664 +oid sha256:c56309c84bc59540b1087ad4ea1930752fa37d10d730919fc7676749a4653ed0 +size 14316936 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib index c77fc6661..a83957886 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17dfb5b37d5aec03943083c04899b0815556d7f10f020fa8f9f9061c971dc010 -size 2086 +oid sha256:b40e1d5069fdd07c0e7675b2a48860588b89732760a5ff348c5a4de61aa084cd +size 2002 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll index ec6f74a5b..9dbf6a623 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c37738cd2fb4d659b0f49dead8311ae75c93b8c6602b991c00e070f7be20bc1 -size 1508472 +oid sha256:39dbd950f39f75f5c37bcd224c524accba76686adaa372b8287facfc46892b1c +size 1509720 diff --git a/Source/Shaders/AtmosphereFog.hlsl b/Source/Shaders/AtmosphereFog.hlsl index 81bb2e1fd..527260d5a 100644 --- a/Source/Shaders/AtmosphereFog.hlsl +++ b/Source/Shaders/AtmosphereFog.hlsl @@ -329,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength)) { return float3(offset / 10, 0, 0); - +#if 0 float pathLength = distance(viewPosition, surfacePos); //return pathLength.xxx; @@ -413,6 +413,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi float sunIntensity = 10; inscatteredLight *= sunIntensity; } +#endif } return inscatteredLight; @@ -420,8 +421,6 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 viewPosition, float3 viewVector, float sceneDepth, float3 sceneColor) { - float4 result = float4(1, 0, 0, 1); - #if 0 float scale = 0.00001f * atmosphericFog.AtmosphericFogDistanceScale;// convert cm to km @@ -536,8 +535,6 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float //return float4(sun + groundColor + inscatterColor, 1); #endif - - return result; } float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 worldPosition, float3 cameraPosition) diff --git a/Source/Shaders/Collisions.hlsl b/Source/Shaders/Collisions.hlsl index 2c503be9a..6d61a8c6a 100644 --- a/Source/Shaders/Collisions.hlsl +++ b/Source/Shaders/Collisions.hlsl @@ -18,6 +18,26 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3 return inExtentX && inExtentY; } +// Determines whether there is an intersection between a ray (rPos and rDir) and a triangle (v0, v1, v2). +// Returns true on intersection and outputs the distance along the ray to the intersection point. +// This method tests if the ray intersects either the front or back of the triangle. +bool RayIntersectsTriangle(float3 rPos, float3 rDir, float3 v0, float3 v1, float3 v2, out float distance) +{ + // [https://stackoverflow.com/a/42752998] + float3 edgeAB = v1 - v0; + float3 edgeAC = v2 - v0; + float3 triFaceVector = cross(edgeAB, edgeAC); + float3 vertRayOffset = rPos - v0; + float3 rayOffsetPerp = cross(vertRayOffset, rDir); + float determinant = -dot(rDir, triFaceVector); + float invDet = 1.0f / determinant; + distance = dot(vertRayOffset, triFaceVector) * invDet; + float u = dot(edgeAC, rayOffsetPerp) * invDet; + float v = -dot(edgeAB, rayOffsetPerp) * invDet; + float w = 1.0f - u - v; + return abs(determinant) >= 1E-8 && distance > 0 && u >= 0 && v >= 0 && w >= 0; +} + // Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd). // Returns the intersections on the line (x - closest, y - furthest). // Line hits the box if: intersections.x < intersections.y. @@ -42,4 +62,39 @@ bool BoxIntersectsSphere(float3 boxMin, float3 boxMax, float3 sphereCenter, floa return distance(sphereCenter, clampedCenter) <= sphereRadius; } +// Calculates unsigned distance from point to the AABB. If point is inside it, returns 0. +float PointDistanceBox(float3 boxMin, float3 boxMax, float3 pos) +{ + float3 clampedPos = clamp(pos, boxMin, boxMax); + return length(clampedPos - pos); +} + +float dot2(float3 v) +{ + return dot(v, v); +} + +// Calculates squared distance from point to the triangle. +float DistancePointToTriangle2(float3 p, float3 v1, float3 v2, float3 v3) +{ + // [Inigo Quilez, https://iquilezles.org/articles/triangledistance/] + float3 v21 = v2 - v1; float3 p1 = p - v1; + float3 v32 = v3 - v2; float3 p2 = p - v2; + float3 v13 = v1 - v3; float3 p3 = p - v3; + float3 nor = cross(v21, v13); + return // inside/outside test + (sign(dot(cross(v21, nor), p1)) + + sign(dot(cross(v32, nor), p2)) + + sign(dot(cross(v13, nor), p3)) < 2.0) + ? + // 3 edges + min(min( + dot2(v21 * saturate(dot(v21, p1) / dot2(v21)) - p1), + dot2(v32 * saturate(dot(v32, p2) / dot2(v32)) - p2)), + dot2(v13 * saturate(dot(v13, p3) / dot2(v13)) - p3)) + : + // 1 face + dot(nor, p1) * dot(nor, p1) / dot2(nor); +} + #endif diff --git a/Source/Shaders/ColorGrading.shader b/Source/Shaders/ColorGrading.shader index 9d1d71697..fe2ca4c91 100644 --- a/Source/Shaders/ColorGrading.shader +++ b/Source/Shaders/ColorGrading.shader @@ -164,16 +164,14 @@ float3 TonemapACES(float3 linearColor) // The code was originally written by Stephen Hill (@self_shadow). // sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT - static const float3x3 ACESInputMat = - { + static const float3x3 ACESInputMat = { {0.59719, 0.35458, 0.04823}, {0.07600, 0.90834, 0.01566}, {0.02840, 0.13383, 0.83777} }; // ODT_SAT => XYZ => D60_2_D65 => sRGB - static const float3x3 ACESOutputMat = - { + static const float3x3 ACESOutputMat = { { 1.60475, -0.53108, -0.07367}, {-0.10208, 1.10813, -0.00605}, {-0.00327, -0.07276, 1.07602} @@ -188,6 +186,7 @@ float3 TonemapACES(float3 linearColor) color = a / b; color = mul(ACESOutputMat, color); + return saturate(color); } diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl index 834348ade..66303546a 100644 --- a/Source/Shaders/Common.hlsl +++ b/Source/Shaders/Common.hlsl @@ -93,6 +93,15 @@ #endif +// Compiler support for HLSL 2021 that is stricter (need to use or/and/select for vector-based logical operators) +#if !defined(__DXC_VERSION_MAJOR) || (__DXC_VERSION_MAJOR <= 1 && __DXC_VERSION_MINOR < 7) + +#define and(a, b) (a) && (b) +#define or(a, b) (a) || (b) +#define select(c, a, b) (c) ? (a) : (b) + +#endif + // Compiler attribute fallback #ifndef UNROLL #define UNROLL diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index c116b597a..3e31c2e53 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -20,17 +20,23 @@ #define DDGI_PROBE_ATTENTION_MAX 0.98f // Maximum probe attention value that still makes it active (but not activated which is 1.0f). #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) -#define DDGI_CASCADE_BLEND_SIZE 2.5f // Distance in probes over which cascades blending happens +#define DDGI_CASCADE_BLEND_SIZE 2.0f // Distance in probes over which cascades blending happens #ifndef DDGI_CASCADE_BLEND_SMOOTH #define DDGI_CASCADE_BLEND_SMOOTH 0 // Enables smooth cascade blending, otherwise dithering will be used #endif #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space +#define DDGI_DEFAULT_BIAS 0.2f // Default value for DDGI sampling bias +#define DDGI_FALLBACK_COORDS_ENCODE(coord) ((float3)(coord + 1) / 128.0f) +#define DDGI_FALLBACK_COORDS_DECODE(data) (uint3)(data.xyz * 128.0f - 1) +#define DDGI_FALLBACK_COORDS_VALID(data) (length(data.xyz) > 0) +//#define DDGI_DEBUG_CASCADE 0 // Forces a specific cascade to be only in use (for debugging) // DDGI data for a constant buffer struct DDGIData { float4 ProbesOriginAndSpacing[4]; - int4 ProbesScrollOffsets[4]; // w unused + float4 BlendOrigin[4]; // w is unused + int4 ProbesScrollOffsets[4]; // w is unused uint3 ProbesCounts; uint CascadesCount; float IrradianceGamma; @@ -39,8 +45,7 @@ struct DDGIData float IndirectLightingIntensity; float3 ViewPos; uint RaysCount; - float3 FallbackIrradiance; - float Padding0; + float4 FallbackIrradiance; }; uint GetDDGIProbeIndex(DDGIData data, uint3 probeCoords) @@ -159,6 +164,8 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2 float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, uint cascadeIndex, float3 probesOrigin, float3 probesExtent, float probesSpacing, float3 biasedWorldPosition) { + bool invalidCascade = cascadeIndex >= data.CascadesCount; + cascadeIndex = min(cascadeIndex, data.CascadesCount - 1); uint3 probeCoordsEnd = data.ProbesCounts - uint3(1, 1, 1); uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), uint3(0, 0, 0), probeCoordsEnd); @@ -168,7 +175,6 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Loop over the closest probes to accumulate their contributions float4 irradiance = float4(0, 0, 0, 0); - const int3 SearchAxisMasks[3] = { int3(1, 0, 0), int3(0, 1, 0), int3(0, 0, 1) }; for (uint i = 0; i < 8; i++) { uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1; @@ -178,33 +184,23 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Load probe position and state float4 probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex); uint probeState = DecodeDDGIProbeState(probeData); + uint useVisibility = true; + float minWight = 0.000001f; if (probeState == DDGI_PROBE_STATE_INACTIVE) { - // Search nearby probes to find any nearby GI sample - for (int searchDistance = 1; searchDistance < 3 && probeState == DDGI_PROBE_STATE_INACTIVE; searchDistance++) - for (uint searchAxis = 0; searchAxis < 3; searchAxis++) - { - int searchAxisDir = probeCoordsOffset[searchAxis] ? 1 : -1; - int3 searchCoordsOffset = SearchAxisMasks[searchAxis] * searchAxisDir * searchDistance; - uint3 searchCoords = clamp((int3)probeCoords + searchCoordsOffset, int3(0, 0, 0), (int3)probeCoordsEnd); - uint searchIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, searchCoords); - float4 searchData = LoadDDGIProbeData(data, probesData, cascadeIndex, searchIndex); - uint searchState = DecodeDDGIProbeState(searchData); - if (searchState != DDGI_PROBE_STATE_INACTIVE) - { - // Use nearby probe as a fallback (visibility test might ignore it but with smooth gradient) - probeCoords = searchCoords; - probeIndex = searchIndex; - probeData = searchData; - probeState = searchState; - break; - } - } - if (probeState == DDGI_PROBE_STATE_INACTIVE) - continue; + // Use fallback probe that is closest to this one + uint3 fallbackCoords = DDGI_FALLBACK_COORDS_DECODE(probeData); + float fallbackToProbeDist = length((float3)probeCoords - (float3)fallbackCoords); + useVisibility = fallbackToProbeDist <= 1.0f; // Skip visibility test that blocks too far probes due to limiting max distance to 1.5 of probe spacing + if (fallbackToProbeDist > 2.0f) minWight = 1.0f; + probeCoords = fallbackCoords; + probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, fallbackCoords); + probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex); + //if (DecodeDDGIProbeState(probeData) == DDGI_PROBE_STATE_INACTIVE) continue; } - float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing); - float3 probePosition = probeBasePosition + probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing + + // Calculate probe position + float3 probePosition = baseProbeWorldPosition + (((float3)probeCoords - (float3)baseProbeCoords) * probesSpacing) + probeData.xyz * probesSpacing; // Calculate the distance and direction from the (biased and non-biased) shading point and the probe float3 worldPosToProbe = normalize(probePosition - worldPosition); @@ -213,6 +209,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Smooth backface test float weight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f); + weight = max(weight, 0.1f); // Sample distance texture float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe); @@ -220,24 +217,23 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg * 2.0f; // Visibility weight (Chebyshev) - if (biasedPosToProbeDist > probeDistance.x) + if (biasedPosToProbeDist > probeDistance.x && useVisibility) { float variance = abs(Square(probeDistance.x) - probeDistance.y); float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x)); - weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.05f); + weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.0f); } // Avoid a weight of zero - weight = max(weight, 0.000001f); + weight = max(weight, minWight); // Adjust weight curve to inject a small portion of light const float minWeightThreshold = 0.2f; - if (weight < minWeightThreshold) - weight *= Square(weight) / Square(minWeightThreshold); + if (weight < minWeightThreshold) weight *= (weight * weight) * (1.0f / (minWeightThreshold * minWeightThreshold)); // Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 probes float3 trilinear = lerp(1.0f - biasAlpha, biasAlpha, (float3)probeCoordsOffset); - weight *= max(trilinear.x * trilinear.y * trilinear.z, 0.001f); + weight *= saturate(trilinear.x * trilinear.y * trilinear.z * 2.0f); // Sample irradiance texture octahedralCoords = GetOctahedralCoords(worldNormal); @@ -269,7 +265,9 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (irradiance.a > 0.0f) { // Normalize irradiance - irradiance.rgb /= irradiance.a; + //irradiance.rgb /= irradiance.a; + //irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); + irradiance.rgb /= invalidCascade ? irradiance.a : lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); #if DDGI_SRGB_BLENDING irradiance.rgb *= irradiance.rgb; #endif @@ -281,22 +279,32 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes float3 GetDDGISurfaceBias(float3 viewDir, float probesSpacing, float3 worldNormal, float bias) { // Bias the world-space position to reduce artifacts - return (worldNormal * 0.2f + viewDir * 0.8f) * (0.75f * probesSpacing * bias); + return (worldNormal * 0.2f + viewDir * 0.8f) * (0.6f * probesSpacing * bias); +} + +// [Inigo Quilez, https://iquilezles.org/articles/distfunctions/] +float sdRoundBox(float3 p, float3 b, float r) +{ + float3 q = abs(p) - b + r; + return length(max(q, 0.0f)) + min(max(q.x, max(q.y, q.z)), 0.0f) - r; } // Samples DDGI probes volume at the given world-space position and returns the irradiance. // bias - scales the bias vector to the initial sample point to reduce self-shading artifacts // dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending -float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = 0.2f, float dither = 0.0f) +float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f) { // Select the highest cascade that contains the sample location - uint cascadeIndex = 0; float probesSpacing = 0, cascadeWeight = 0; float3 probesOrigin = (float3)0, probesExtent = (float3)0, biasedWorldPosition = (float3)0; float3 viewDir = normalize(data.ViewPos - worldPosition); #if DDGI_CASCADE_BLEND_SMOOTH dither = 0.0f; #endif +#ifdef DDGI_DEBUG_CASCADE + uint cascadeIndex = DDGI_DEBUG_CASCADE; +#else + uint cascadeIndex = 0; for (; cascadeIndex < data.CascadesCount; cascadeIndex++) { // Get cascade data @@ -306,26 +314,21 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias); // Calculate cascade blending weight (use input bias to smooth transition) - float cascadeBlendSmooth = frac(max(distance(data.ViewPos, worldPosition) - probesExtent.x, 0) / probesSpacing) * 0.1f; - float3 cascadeBlendPoint = worldPosition - probesOrigin - cascadeBlendSmooth * probesSpacing; float fadeDistance = probesSpacing * DDGI_CASCADE_BLEND_SIZE; -#if DDGI_CASCADE_BLEND_SMOOTH - fadeDistance *= 2.0f; // Make it even smoother when using linear blending -#endif - cascadeWeight = saturate(Min3(probesExtent - abs(cascadeBlendPoint)) / fadeDistance); + float3 blendPos = worldPosition - data.BlendOrigin[cascadeIndex].xyz; + cascadeWeight = sdRoundBox(blendPos, probesExtent - probesSpacing, probesSpacing * 2) + fadeDistance; + cascadeWeight = 1 - saturate(cascadeWeight / fadeDistance); if (cascadeWeight > dither) break; } - if (cascadeIndex == data.CascadesCount) - return data.FallbackIrradiance; +#endif // Sample cascade float3 result = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition); // Blend with the next cascade (or fallback irradiance outside the volume) +#if DDGI_CASCADE_BLEND_SMOOTH && !defined(DDGI_DEBUG_CASCADE) cascadeIndex++; -#if DDGI_CASCADE_BLEND_SMOOTH - result *= cascadeWeight; if (cascadeIndex < data.CascadesCount && cascadeWeight < 0.99f) { probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w; @@ -333,18 +336,16 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f); biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias); float3 resultNext = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition); + result *= cascadeWeight; result += resultNext * (1 - cascadeWeight); } - else - { - result += data.FallbackIrradiance * (1 - cascadeWeight); - } -#else - if (cascadeIndex == data.CascadesCount) - { - result += data.FallbackIrradiance * (1 - cascadeWeight); - } #endif + if (cascadeIndex >= data.CascadesCount) + { + // Blend between the last cascade and the fallback irradiance + float fallbackWeight = (1 - cascadeWeight) * data.FallbackIrradiance.a; + result = lerp(result, data.FallbackIrradiance.rgb, fallbackWeight); + } return result; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index daad2018d..b080efc0b 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -13,6 +13,7 @@ #include "./Flax/Math.hlsl" #include "./Flax/Noise.hlsl" #include "./Flax/Quaternion.hlsl" +#include "./Flax/MonteCarlo.hlsl" #include "./Flax/GlobalSignDistanceField.hlsl" #include "./Flax/GI/GlobalSurfaceAtlas.hlsl" #include "./Flax/GI/DDGI.hlsl" @@ -26,6 +27,7 @@ #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 #define DDGI_PROBE_RELOCATE_ITERATIVE 1 // If true, probes relocation algorithm tries to move them in additive way, otherwise all nearby locations are checked to find the best position #define DDGI_PROBE_RELOCATE_FIND_BEST 1 // If true, probes relocation algorithm tries to move to the best matching location within nearby area +#define DDGI_PROBE_EMPTY_AREA_DENSITY 8 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count #define DDGI_DEBUG_INSTABILITY 0 // Enables additional probe irradiance instability debugging @@ -42,10 +44,13 @@ float TemporalTime; int4 ProbeScrollClears[4]; float3 ViewDir; float Padding1; +float3 QuantizationError; +uint FrameIndexMod8; META_CB_END META_CB_BEGIN(1, Data1) -float2 Padding2; +float Padding2; +int StepSize; uint CascadeIndex; uint ProbeIndexOffset; META_CB_END @@ -98,6 +103,11 @@ float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3 return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; } +bool IsProbeAtBorder(uint3 probeCoords) +{ + return min(probeCoords.x, min(probeCoords.y, probeCoords.z)) == 0 || probeCoords.x == DDGI.ProbesCounts.x - 1 || probeCoords.y == DDGI.ProbesCounts.y - 1 || probeCoords.z == DDGI.ProbesCounts.z - 1; +} + // Compute shader for updating probes state between active and inactive and performing probes relocation. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)] @@ -112,6 +122,14 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords); +#ifdef DDGI_DEBUG_CASCADE + // Single cascade-only debugging + if (CascadeIndex != DDGI_DEBUG_CASCADE) + { + RWProbesData[probeDataCoords] = EncodeDDGIProbeData(float3(0, 0, 0), DDGI_PROBE_STATE_INACTIVE, 0.0f); + return; + } +#else // Disable probes that are is in the range of higher-quality cascade if (CascadeIndex > 0) { @@ -119,15 +137,15 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float prevProbesSpacing = DDGI.ProbesOriginAndSpacing[prevCascade].w; float3 prevProbesOrigin = DDGI.ProbesScrollOffsets[prevCascade].xyz * prevProbesSpacing + DDGI.ProbesOriginAndSpacing[prevCascade].xyz; float3 prevProbesExtent = (DDGI.ProbesCounts - 1) * (prevProbesSpacing * 0.5f); - prevProbesExtent -= probesSpacing * ceil(DDGI_CASCADE_BLEND_SIZE); // Apply safe margin to allow probes on cascade edges + prevProbesExtent -= probesSpacing * ceil(DDGI_CASCADE_BLEND_SIZE) * 2; // Apply safe margin to allow probes on cascade edges float prevCascadeWeight = Min3(prevProbesExtent - abs(probeBasePosition - prevProbesOrigin)); if (prevCascadeWeight > 0.1f) { - // Disable probe RWProbesData[probeDataCoords] = EncodeDDGIProbeData(float3(0, 0, 0), DDGI_PROBE_STATE_INACTIVE, 0.0f); return; } } +#endif // Check if probe was scrolled int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz; @@ -171,9 +189,29 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex] * 0.8f; float distanceLimit = probesSpacing * ProbesDistanceLimits[CascadeIndex]; float relocateLimit = probesSpacing * ProbesRelocateLimits[CascadeIndex]; - if (sdfDst > distanceLimit + length(probeOffset)) // Probe is too far from geometry (or deep inside) +#ifdef DDGI_PROBE_EMPTY_AREA_DENSITY + uint3 probeCoordsStable = GetDDGIProbeCoords(DDGI, probeIndex); + if (sdf > probesSpacing * DDGI.ProbesCounts.x * 0.3f +#if DDGI_PROBE_EMPTY_AREA_DENSITY > 1 + && ( + // Low-density grid grid + (probeCoordsStable.x % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.y % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.z % DDGI_PROBE_EMPTY_AREA_DENSITY == 0) + // Edge probes at the last cascade (for good fallback irradiance outside the GI distance) + //|| (CascadeIndex + 1 == DDGI.CascadesCount && IsProbeAtBorder(probeCoords)) + ) +#endif + ) { - // Disable it + // Addd some fallback probes in empty areas to provide valid GI for nearby dynamic objects or transparency + probeOffset = float3(0, 0, 0); + probeState = wasScrolled || probeStateOld == DDGI_PROBE_STATE_INACTIVE ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE; + probeAttention = DDGI_PROBE_ATTENTION_MIN; + } + else +#endif + if (sdfDst > distanceLimit + length(probeOffset)) + { + // Probe is too far from geometry (or deep inside) so disable it probeOffset = float3(0, 0, 0); probeState = DDGI_PROBE_STATE_INACTIVE; probeAttention = 0.0f; @@ -194,6 +232,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) probeAttention = clamp(probeAttention, DDGI_PROBE_ATTENTION_MIN, DDGI_PROBE_ATTENTION_MAX); // Relocate only if probe location is not good enough + BRANCH if (sdf <= voxelLimit) { #if DDGI_PROBE_RELOCATE_ITERATIVE @@ -265,6 +304,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) bool wasActivated = probeStateOld == DDGI_PROBE_STATE_INACTIVE; bool wasRelocated = distance(probeOffset, probeOffsetOld) > 2.0f; #if DDGI_PROBE_RELOCATE_FIND_BEST || DDGI_PROBE_RELOCATE_ITERATIVE + BRANCH if (wasRelocated && !wasActivated) { // If probe was relocated but the previous location is visible from the new one, then don't re-activate it for smoother blend @@ -323,6 +363,78 @@ void CS_UpdateProbesInitArgs() #endif +#ifdef _CS_UpdateInactiveProbes + +RWTexture2D RWProbesData : register(u0); + +void CheckNearbyProbe(inout uint3 fallbackCoords, inout uint probeState, inout float minDistance, uint3 probeCoords, int3 probeCoordsEnd, int3 offset) +{ + uint3 nearbyCoords = (uint3)clamp(((int3)probeCoords + offset), int3(0, 0, 0), probeCoordsEnd); + uint nearbyIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, nearbyCoords); + float4 nearbyData = RWProbesData[GetDDGIProbeTexelCoords(DDGI, CascadeIndex, nearbyIndex)]; + float nearbyDist = distance((float3)nearbyCoords, (float3)probeCoords); + if (DecodeDDGIProbeState(nearbyData) != DDGI_PROBE_STATE_INACTIVE && nearbyDist < minDistance) + { + // Use nearby probe + fallbackCoords = nearbyCoords; + probeState = DDGI_PROBE_STATE_ACTIVE; + minDistance = nearbyDist; + return; + } + nearbyCoords = DDGI_FALLBACK_COORDS_DECODE(nearbyData); + nearbyDist = distance((float3)nearbyCoords, (float3)probeCoords); + if (DDGI_FALLBACK_COORDS_VALID(nearbyData) && nearbyDist < minDistance) + { + // Use fallback probe + fallbackCoords = nearbyCoords; + probeState = DDGI_PROBE_STATE_ACTIVE; + minDistance = nearbyDist; + } +} + +// Compute shader to store closest valid probe coords inside inactive probes data for quick fallback lookup when sampling irradiance. +// Uses Jump Flood algorithm. +META_CS(true, FEATURE_LEVEL_SM5) +[numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)] +void CS_UpdateInactiveProbes(uint3 DispatchThreadId : SV_DispatchThreadID) +{ + uint probeIndex = min(DispatchThreadId.x, ProbesCount - 1); + uint3 fallbackCoords = uint3(1000, 1000, 1000); + + // Load probe data for the current thread + uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); + probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords); + int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex); + float4 probeData = RWProbesData[probeDataCoords]; + uint probeState = DecodeDDGIProbeState(probeData); + BRANCH + if (probeState == DDGI_PROBE_STATE_INACTIVE) + { + // Find the closest active probe (Jump Flood) + int3 probeCoordsEnd = (int3)DDGI.ProbesCounts - int3(1, 1, 1); + float minDistance = 1e27f; + UNROLL for (int z = -1; z <= 1; z++) + UNROLL for (int y = -1; y <= 1; y++) + UNROLL for (int x = -1; x <= 1; x++) + { + int3 offset = int3(x, y, z) * StepSize; + CheckNearbyProbe(fallbackCoords, probeState, minDistance, probeCoords, probeCoordsEnd, offset); + } + } + + // Ensure all threads (within dispatch) got proper data before writing back to the same memory + AllMemoryBarrierWithGroupSync(); + + // Write modified probe data back (remain inactive) + BRANCH + if (probeState != DDGI_PROBE_STATE_INACTIVE && DispatchThreadId.x < ProbesCount && fallbackCoords.x != 1000) + { + RWProbesData[probeDataCoords] = EncodeDDGIProbeData(DDGI_FALLBACK_COORDS_ENCODE(fallbackCoords), DDGI_PROBE_STATE_INACTIVE, 0.0f); + } +} + +#endif + #ifdef _CS_TraceRays RWTexture2D RWProbesTrace : register(u0); @@ -392,6 +504,8 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) // Add some bias to prevent self occlusion artifacts in Chebyshev due to Global SDF being very incorrect in small scale radiance.w = max(radiance.w + GlobalSDF.CascadeVoxelSize[hit.HitCascade] * 0.5f, 0); + float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; + radiance.w += probesSpacing * 0.05f; } } else @@ -639,7 +753,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Add distance (R), distance^2 (G) and weight (A) float rayDistance = CachedProbesTraceDistance[rayIndex]; - result += float4(rayDistance * rayWeight, (rayDistance * rayDistance) * rayWeight, 0.0f, rayWeight); + result += float4(rayDistance, rayDistance * rayDistance, 0.0f, 1.0f) * rayWeight; #endif } @@ -700,13 +814,17 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ //result.rgb = previous + (irradianceDelta * 0.25f); } result = float4(lerp(result.rgb, previous.rgb, historyWeight), 1.0f); + + // Apply quantization error to reduce yellowish artifacts due to R11G11B10 format + float noise = InterleavedGradientNoise(octahedralCoords, FrameIndexMod8); + result.rgb = QuantizeColor(result.rgb, noise, QuantizationError); #else result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f); #endif RWOutput[outputCoords] = result; - GroupMemoryBarrierWithGroupSync(); + uint2 baseCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex) * (DDGI_PROBE_RESOLUTION + 2); #if DDGI_PROBE_UPDATE_MODE == 0 @@ -786,10 +904,10 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) } // Sample irradiance - float bias = 0.2f; float dither = RandN2(input.TexCoord + TemporalTime).x; - float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither); - + float3 samplePos = gBuffer.WorldPos + gBuffer.Normal * (dither * 0.1f + 0.1f); + float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, samplePos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither); + // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuse = Diffuse_Lambert(diffuseColor); diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 2efa638f0..6930107d1 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -165,11 +165,19 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target BRANCH if (NoL > 0) { +#if RADIAL_LIGHT + // Shot a ray from light to the texel to see if there is any occluder + GlobalSDFTrace trace; + trace.Init(Light.Position, -L, bias, toLightDst); + GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 1.0f); + shadowMask = hit.IsHit() && hit.HitTime < toLightDst - bias * 3 ? LightShadowsStrength : 1; +#else // Shot a ray from texel into the light to see if there is any occluder GlobalSDFTrace trace; trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias); GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 2.0f); shadowMask = hit.IsHit() ? LightShadowsStrength : 1; +#endif } else { @@ -234,7 +242,7 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupId if (BoxIntersectsSphere(groupMin, groupMax, objectBounds.xyz, objectBounds.w)) { uint sharedIndex; - InterlockedAdd(SharedCulledObjectsCount, 1, sharedIndex); + InterlockedAdd(SharedCulledObjectsCount, 1u, sharedIndex); if (sharedIndex < GLOBAL_SURFACE_ATLAS_SHARED_CULL_SIZE) SharedCulledObjects[sharedIndex] = objectAddress; } @@ -263,7 +271,7 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupId // Allocate object data size in the buffer uint objectsStart; uint objectsSize = objectsCount + 1; // Include objects count before actual objects data - RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0, objectsSize, objectsStart); // Counter at 0 + RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0u, objectsSize, objectsStart); // Counter at 0 if (objectsStart + objectsSize > CulledObjectsCapacity) { // Not enough space in the buffer @@ -320,7 +328,6 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz; viewRay = normalize(viewRay - ViewWorldPos); trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane); - trace.NeedsHitNormal = true; GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); float3 color; @@ -329,7 +336,6 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target // Sample Global Surface Atlas at the hit location float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit); color = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold).rgb; - //color = hit.HitNormal * 0.5f + 0.5f; } else { diff --git a/Source/Shaders/GammaCorrectionCommon.hlsl b/Source/Shaders/GammaCorrectionCommon.hlsl index b687f9166..df188b347 100644 --- a/Source/Shaders/GammaCorrectionCommon.hlsl +++ b/Source/Shaders/GammaCorrectionCommon.hlsl @@ -37,7 +37,7 @@ float4 FastTonemapInvert(float4 c) return float4(FastTonemapInvert(c.rgb), c.a); } -float LinearToSrgbChannel(float linearColor) +float LinearToSrgb(float linearColor) { if (linearColor < 0.00313067) return linearColor * 12.92; @@ -46,16 +46,13 @@ float LinearToSrgbChannel(float linearColor) float3 LinearToSrgb(float3 linearColor) { - return float3( - LinearToSrgbChannel(linearColor.r), - LinearToSrgbChannel(linearColor.g), - LinearToSrgbChannel(linearColor.b)); + return float3(LinearToSrgb(linearColor.r), LinearToSrgb(linearColor.g), LinearToSrgb(linearColor.b)); } float3 sRGBToLinear(float3 color) { color = max(6.10352e-5, color); - return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92); + return select(color > 0.04045, pow(color * (1.0 / 1.055) + 0.0521327, 2.4), color * (1.0 / 12.92)); } float3 LogToLinear(float3 logColor) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index a2f09de68..c1bd4250b 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -11,6 +11,7 @@ #define GLOBAL_SDF_WORLD_SIZE 60000.0f #define GLOBAL_SDF_MIN_VALID 0.9f #define GLOBAL_SDF_CHUNK_MARGIN_SCALE 4.0f +#define GLOBAL_SDF_SAMPLER SamplerLinearClamp // Global SDF data for a constant buffer struct GlobalSDFData @@ -31,17 +32,13 @@ struct GlobalSDFTrace float MinDistance; float3 WorldDirection; float MaxDistance; - float StepScale; - bool NeedsHitNormal; - void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f) + void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance) { WorldPosition = worldPosition; WorldDirection = worldDirection; MinDistance = minDistance; MaxDistance = maxDistance; - StepScale = stepScale; - NeedsHitNormal = false; } }; @@ -74,6 +71,26 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis } +void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float3 cascadeUV, out float3 textureUV, out float3 textureMipUV) +{ + float4 cascadePosDistance = data.CascadePosDistance[cascade]; + float3 posInCascade = worldPosition - cascadePosDistance.xyz; + float cascadeSize = cascadePosDistance.w * 2; + cascadeUV = saturate(posInCascade / cascadeSize + 0.5f); + textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis + float halfTexelOffsetMip = (GLOBAL_SDF_RASTERIZE_MIP_FACTOR * 0.5f) / data.Resolution; + textureMipUV = textureUV + float3(halfTexelOffsetMip / (float)data.CascadesCount, halfTexelOffsetMip, halfTexelOffsetMip); // Mipmaps are offset by half texel to sample correctly +} + +// Clamps Global SDF cascade UV to ensure it can be used for gradient sampling (clamps first and last pixels). +void ClampGlobalSDFTextureGradientUV(const GlobalSDFData data, uint cascade, float texelOffset, inout float3 textureUV) +{ + float cascadeSizeUV = 1.0f / data.CascadesCount; + float cascadeUVStart = cascadeSizeUV * cascade + texelOffset * 2; + float cascadeUVEnd = cascadeUVStart + cascadeSizeUV - texelOffset * 4; + textureUV.x = clamp(textureUV.x, cascadeUVStart, cascadeUVEnd); +} + // Gets the Global SDF cascade index for the given world location. uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition) { @@ -96,7 +113,7 @@ float SampleGlobalSDFCascade(const GlobalSDFData data, Texture3D te float voxelSize = data.CascadeVoxelSize[cascade]; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) distance = distanceTex; return distance; @@ -115,7 +132,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, floa float voxelSize = data.CascadeVoxelSize[cascade]; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0); if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) { distance = distanceTex * maxDistanceTex; @@ -134,18 +151,18 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, Text startCascade = min(startCascade, data.CascadesCount - 1); for (uint cascade = startCascade; cascade < data.CascadesCount; cascade++) { - float3 cascadeUV, textureUV; - GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV); + float3 cascadeUV, textureUV, textureMipUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV, textureMipUV); float voxelSize = data.CascadeVoxelSize[cascade]; float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; - float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureMipUV, 0); if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { distance = distanceMip * maxDistanceMip; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin) distance = distanceTex; break; @@ -169,16 +186,17 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D float voxelSize = data.CascadeVoxelSize[cascade]; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0); if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) { float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); + float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; distance = distanceTex * maxDistanceTex; break; @@ -197,39 +215,41 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D startCascade = min(startCascade, data.CascadesCount - 1); for (uint cascade = startCascade; cascade < data.CascadesCount; cascade++) { - float3 cascadeUV, textureUV; - GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV); + float3 cascadeUV, textureUV, textureMipUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV, textureMipUV); float voxelSize = data.CascadeVoxelSize[cascade]; float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; - float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureMipUV, 0) * maxDistanceMip; if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin) { distance = distanceTex; float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); + float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; } else { distance = distanceMip; float texelOffset = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR / data.Resolution; - float xp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureMipUV); + float xp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x + texelOffset, textureMipUV.y, textureMipUV.z), 0).x; + float xn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x - texelOffset, textureMipUV.y, textureMipUV.z), 0).x; + float yp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y + texelOffset, textureMipUV.z), 0).x; + float yn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y - texelOffset, textureMipUV.z), 0).x; + float zp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y, textureMipUV.z + texelOffset), 0).x; + float zn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y, textureMipUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceMip; } break; @@ -253,15 +273,14 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D float4 cascadePosDistance = data.CascadePosDistance[cascade]; float voxelSize = data.CascadeVoxelSize[cascade]; float voxelExtent = voxelSize * 0.5f; - float3 worldPosition = trace.WorldPosition; // Skip until cascade that contains the start location - if (any(abs(worldPosition - cascadePosDistance.xyz) > cascadePosDistance.w)) + if (any(abs(trace.WorldPosition - cascadePosDistance.xyz) > cascadePosDistance.w)) continue; // Hit the cascade bounds to find the intersection points float traceStartBias = voxelSize * cascadeTraceStartBias; - float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); + float2 intersections = LineHitBox(trace.WorldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); intersections.xy *= traceMaxDistance; intersections.x = max(intersections.x, traceStartBias); intersections.x = max(intersections.x, nextIntersectionStart); @@ -278,58 +297,32 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; LOOP - for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) + for (; step < 100 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) { - float3 stepPosition = worldPosition + trace.WorldDirection * stepTime; - float stepScale = trace.StepScale; + float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime; // Sample SDF - float stepDistance, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR; - float3 cascadeUV, textureUV; - GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV); - float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; - if (distanceMip < chunkSize) - { - stepDistance = distanceMip; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; - if (distanceTex < chunkMargin) - { - stepDistance = distanceTex; - voxelSizeScale = 1.0f; - stepScale *= 0.63f; // Perform smaller steps nearby geometry - } - } - else - { - // Assume no SDF nearby so perform a jump tto the next chunk - stepDistance = chunkSize; - voxelSizeScale = 1.0f; - } + float stepDistance; + float3 cascadeUV, textureUV, textureMipUV; + GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV, textureMipUV); + stepDistance = min(mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureMipUV, 0) * maxDistanceMip, chunkSize); + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; + FLATTEN + if (distanceTex < chunkMargin) + stepDistance = distanceTex; // Detect surface hit - float minSurfaceThickness = voxelSizeScale * voxelExtent * saturate(stepTime / voxelSize); + float minSurfaceThickness = voxelExtent * saturate(stepTime / voxelSize); if (stepDistance < minSurfaceThickness) { // Surface hit hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f); hit.HitCascade = cascade; hit.HitSDF = stepDistance; - if (trace.NeedsHitNormal) - { - // Calculate hit normal from SDF gradient - float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; - hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); - } } // Move forward - stepTime += max(stepDistance * stepScale, voxelSize); + stepTime += max(stepDistance, voxelSize); } hit.StepsCount += step; } diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index f3a51addd..fe4bafda5 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -314,17 +314,36 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); // Debug draw - float3 color = saturate(hit.StepsCount / 80.0f).xxx; - if (!hit.IsHit()) - color.rg *= 0.4f; -#if 0 - else - { + float3 color = saturate(hit.StepsCount / 50.0f).xxx; + if (hit.IsHit()) + { +#if 1 + float3 hitPosition = hit.GetHitPosition(trace); + float hitSDF; + float3 hitNormal = SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, GlobalSDFMip, hitPosition, hitSDF, hit.HitCascade); +#if 1 + // Composite step count with SDF normals + //color.rgb *= saturate(normalize(hitNormal) * 0.5f + 0.7f) + 0.3f; + color = lerp(normalize(hitNormal) * 0.5f + 0.5f, 1 - color, saturate(hit.StepsCount / 80.0f)); +#else // Debug draw SDF normals - float dst; - color.rgb = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, hit.GetHitPosition(trace), dst)) * 0.5f + 0.5f; - } + color = normalize(hitNormal) * 0.5f + 0.5f; #endif +#else + // Heatmap with step count + if (hit.StepsCount > 40) + color = float3(saturate(hit.StepsCount / 80.0f), 0, 0); + else if (hit.StepsCount > 20) + color = float3(saturate(hit.StepsCount / 40.0f).xx, 0); + else + color = float3(0, saturate(hit.StepsCount / 20.0f), 0); +#endif + } + else + { + // Bluish sky + color.rg *= 0.4f; + } return float4(color, 1); } diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 02f754c94..8c9567c29 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -311,7 +311,7 @@ float3 AOMultiBounce(float visibility, float3 albedo) float2 Flipbook(float2 uv, float frame, float2 sizeXY, float2 flipXY = 0.0f) { - float tile = (int)fmod(frame, sizeXY.x * sizeXY.y); + float tile = (float)(int)fmod(frame, sizeXY.x * sizeXY.y); float2 tileCount = float2(1.0, 1.0) / sizeXY; float tileY = abs(flipXY.y * sizeXY.y - (floor(tile * tileCount.x) + flipXY.y * 1)); float tileX = abs(flipXY.x * sizeXY.x - ((tile - sizeXY.x * floor(tile * tileCount.x)) + flipXY.x * 1)); diff --git a/Source/Shaders/Math.hlsl b/Source/Shaders/Math.hlsl index 4ba7bbbbb..b590b60b4 100644 --- a/Source/Shaders/Math.hlsl +++ b/Source/Shaders/Math.hlsl @@ -8,7 +8,7 @@ uint NextPow2(uint value) { - uint mask = (1 << firstbithigh(value)) - 1; + uint mask = (1u << firstbithigh(value)) - 1u; return (value + mask) & ~mask; } diff --git a/Source/Shaders/MeshAccelerationStructure.hlsl b/Source/Shaders/MeshAccelerationStructure.hlsl new file mode 100644 index 000000000..0559e5f55 --- /dev/null +++ b/Source/Shaders/MeshAccelerationStructure.hlsl @@ -0,0 +1,161 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#ifndef __MESH_ACCELERATION_STRUCTURE__ +#define __MESH_ACCELERATION_STRUCTURE__ + +#include "./Flax/Collisions.hlsl" + +// This must match MeshAccelerationStructure::ToGPU +#define BVH_STACK_SIZE 32 + +struct BVHNode +{ + float3 BoundsMin; + uint Index; + float3 BoundsMax; + int Count; // Negative for non-leaf nodes +}; + +// Pass all data via separate params (SPIR-V doesn't support buffers in structures) +#define BVHBuffers_Param StructuredBuffer BVHBuffer, ByteAddressBuffer VertexBuffer, ByteAddressBuffer IndexBuffer, uint VertexStride +#define BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) BVHBuffer, VertexBuffer, IndexBuffer, VertexStride +#define BVHBuffers_Pass BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) + +struct BVHHit +{ + float Distance; + bool IsBackface; +}; + +float3 LoadVertexBVH(BVHBuffers_Param, uint index) +{ + int addr = index << 2u; + uint vertexIndex = IndexBuffer.Load(addr); + return asfloat(VertexBuffer.Load3(vertexIndex * VertexStride)); +} + +// [https://tavianator.com/2011/ray_box.html] +float RayTestBoxBVH(float3 rayPos, float3 rayDir, float3 boxMin, float3 boxMax) +{ + float3 rayInvDir = rcp(rayDir); + float3 tMin = (boxMin - rayPos) * rayInvDir; + float3 tMax = (boxMax - rayPos) * rayInvDir; + float3 t1 = min(tMin, tMax); + float tNear = max(max(t1.x, t1.y), t1.z); + float3 t2 = max(tMin, tMax); + float tFar = min(min(t2.x, t2.y), t2.z); + bool hit = tFar >= tNear && tFar > 0; + return hit ? max(tNear, 0) : -1; +} + +// Performs raytracing against the BVH acceleration structure to find the closest intersection with a triangle. +bool RayCastBVH(BVHBuffers_Param, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f) +{ + hit = (BVHHit)0; + hit.Distance = maxDistance; + + // Stack-based recursion, starts from root node + uint stack[BVH_STACK_SIZE]; + uint stackCount = 1; + stack[0] = 0; + + bool result = false; + LOOP + while (stackCount > 0) + { + BVHNode node = BVHBuffer[stack[--stackCount]]; + + // Raytrace bounds + float boundsHit = RayTestBoxBVH(rayPos, rayDir, node.BoundsMin, node.BoundsMax); + BRANCH + if (boundsHit >= 0 && boundsHit < hit.Distance) + { + BRANCH + if (node.Count > 0) // Is leaf? + { + // Ray cast along all triangles in the leaf + uint indexStart = node.Index; + uint indexEnd = indexStart + node.Count; + for (uint i = indexStart; i < indexEnd;) + { + // Load triangle + float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++); + + // Raytrace triangle + float distance; + if (RayIntersectsTriangle(rayPos, rayDir, v0, v1, v2, distance) && distance < hit.Distance) + { + float3 n = normalize(cross(v1 - v0, v2 - v0)); + hit.Distance = distance; + hit.IsBackface = dot(rayDir, n) > 0; + result = true; + } + } + } + else + { + // Push children onto the stack to be tested + stack[stackCount++] = node.Index + 0; + stack[stackCount++] = node.Index + 1; + } + } + } + return result; +} + +// Performs a query against the BVH acceleration structure to find the closest distance to a triangle from a given point. +bool PointQueryBVH(BVHBuffers_Param, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f) +{ + hit = (BVHHit)0; + hit.Distance = maxDistance; + + // Stack-based recursion, starts from root node + uint stack[BVH_STACK_SIZE]; + uint stackCount = 1; + stack[0] = 0; + + bool result = false; + LOOP + while (stackCount > 0) + { + BVHNode node = BVHBuffer[stack[--stackCount]]; + + // Skip too far nodes + if (PointDistanceBox(node.BoundsMin, node.BoundsMax, pos) >= hit.Distance) + continue; + + BRANCH + if (node.Count > 0) // Is leaf? + { + // Find the closest triangles in the leaf + uint indexStart = node.Index; + uint indexEnd = indexStart + node.Count; + for (uint i = indexStart; i < indexEnd;) + { + // Load triangle + float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++); + + // Check triangle + float distance = sqrt(DistancePointToTriangle2(pos, v0, v1, v2)); + if (distance < hit.Distance) + { + hit.Distance = distance; + result = true; + } + } + } + else + { + // Push children onto the stack to be tested + stack[stackCount++] = node.Index + 0; + stack[stackCount++] = node.Index + 1; + } + } + return result; +} + +#endif diff --git a/Source/Shaders/MotionBlur.shader b/Source/Shaders/MotionBlur.shader index 153b14bba..7041ec743 100644 --- a/Source/Shaders/MotionBlur.shader +++ b/Source/Shaders/MotionBlur.shader @@ -21,6 +21,8 @@ int MaxBlurSamples; uint VariableTileLoopCount; float2 Input0SizeInv; float2 Input2SizeInv; +float3 PrevWorldOriginOffset; +float Dummy1; META_CB_END DECLARE_GBUFFERDATA_ACCESS(GBuffer) @@ -39,7 +41,7 @@ float4 PS_CameraMotionVectors(Quad_VS2PS input) : SV_Target GBufferData gBufferData = GetGBufferData(); float4 worldPos = float4(GetWorldPos(gBufferData, input.TexCoord, deviceDepth), 1); - float4 prevClipPos = mul(worldPos, PreviousVP); + float4 prevClipPos = mul(worldPos + float4(PrevWorldOriginOffset, 0), PreviousVP); float4 curClipPos = mul(worldPos, CurrentVP); float2 prevHPos = prevClipPos.xy / prevClipPos.w; float2 curHPos = curClipPos.xy / curClipPos.w; diff --git a/Source/Shaders/Noise.hlsl b/Source/Shaders/Noise.hlsl index dc35f1efc..df5a041fa 100644 --- a/Source/Shaders/Noise.hlsl +++ b/Source/Shaders/Noise.hlsl @@ -54,6 +54,26 @@ float2 PerlinNoiseFade(float2 t) return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } +// "Next Generation Post Processing in Call of Duty: Advanced Warfare" +// http://advances.realtimerendering.com/s2014/index.html +float InterleavedGradientNoise(float2 uv, uint frameCount) +{ + const float2 magicFrameScale = float2(47, 17) * 0.695; + uv += frameCount * magicFrameScale; + const float3 magic = float3(0.06711056, 0.00583715, 52.9829189); + return frac(magic.z * frac(dot(uv, magic.xy))); +} + +// Removes error from the color to properly store it in lower precision formats (error = 2^(-mantissaBits)) +float3 QuantizeColor(float3 color, float noise, float3 error) +{ + float3 delta = color * error; + delta.x = asfloat(asuint(delta.x) & ~0x007fffff); + delta.y = asfloat(asuint(delta.y) & ~0x007fffff); + delta.z = asfloat(asuint(delta.z) & ~0x007fffff); + return color + delta * noise; +} + float rand2dTo1d(float2 value, float2 dotDir = float2(12.9898, 78.233)) { // https://www.ronja-tutorials.com/post/024-white-noise/ diff --git a/Source/Shaders/SDF.shader b/Source/Shaders/SDF.shader index ba2626ff9..77e27d8f9 100644 --- a/Source/Shaders/SDF.shader +++ b/Source/Shaders/SDF.shader @@ -1,18 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. -// Mesh SDF generation based on https://github.com/GPUOpen-Effects/TressFX - #include "./Flax/Common.hlsl" -#include "./Flax/ThirdParty/TressFX/TressFXSDF.hlsl" - -#define THREAD_GROUP_SIZE 64 +#include "./Flax/MeshAccelerationStructure.hlsl" META_CB_BEGIN(0, Data) int3 Resolution; uint ResolutionSize; float MaxDistance; uint VertexStride; -bool Index16bit; +float BackfacesThreshold; uint TriangleCount; float3 VoxelToPosMul; float WorldUnitsPerVoxel; @@ -20,21 +16,9 @@ float3 VoxelToPosAdd; uint ThreadGroupsX; META_CB_END -RWBuffer SDF : register(u0); - -uint GetVoxelIndex(uint3 groupId, uint groupIndex) +uint GetVoxelIndex(uint3 groupId, uint groupIndex, uint groupSize) { - return groupIndex + (groupId.x + groupId.y * ThreadGroupsX) * THREAD_GROUP_SIZE; -} - -int3 ClampVoxelCoord(int3 coord) -{ - return clamp(coord, 0, Resolution - 1); -} - -int GetVoxelIndex(int3 coord) -{ - return Resolution.x * Resolution.y * coord.z + Resolution.x * coord.y + coord.x; + return groupIndex + (groupId.x + groupId.y * ThreadGroupsX) * groupSize; } float3 GetVoxelPos(int3 coord) @@ -42,12 +26,6 @@ float3 GetVoxelPos(int3 coord) return float3((float)coord.x, (float)coord.y, (float)coord.z) * VoxelToPosMul + VoxelToPosAdd; } -int3 GetVoxelCoord(float3 pos) -{ - pos = (pos - VoxelToPosAdd) / VoxelToPosMul; - return int3((int)pos.x, (int)pos.y, (int)pos.z); -} - int3 GetVoxelCoord(uint index) { uint sizeX = (uint)Resolution.x; @@ -59,189 +37,84 @@ int3 GetVoxelCoord(uint index) return int3((int)coordX, (int)coordY, (int)coordZ); } -// Clears SDF texture with the initial distance. +#ifdef _CS_Init + +#define THREAD_GROUP_SIZE 64 + +RWTexture3D SDFtex : register(u0); + +// Clears SDF texture with the maximum distance. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(THREAD_GROUP_SIZE, 1, 1)] void CS_Init(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) { - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); + uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex, THREAD_GROUP_SIZE); if (voxelIndex >= ResolutionSize) return; - float distance = MaxDistance * 10.0f; // Start with a very large value - SDF[voxelIndex] = FloatFlip3(distance); + int3 voxelCoord = GetVoxelCoord(voxelIndex); + SDFtex[voxelCoord] = 1.0f; } -// Unpacks SDF texture into distances stores as normal float value (FloatFlip3 is used for interlocked operations on uint). -META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_Resolve(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) -{ - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); - if (voxelIndex >= ResolutionSize) - return; - SDF[voxelIndex] = IFloatFlip3(SDF[voxelIndex]); -} +#endif -#ifdef _CS_RasterizeTriangle +#ifdef _CS_RasterizeTriangles +#define THREAD_GROUP_SIZE 64 + +RWTexture3D SDFtex : register(u0); ByteAddressBuffer VertexBuffer : register(t0); ByteAddressBuffer IndexBuffer : register(t1); - -uint LoadIndex(uint i) -{ - if (Index16bit) - { - uint index = IndexBuffer.Load((i >> 1u) << 2u); - index = (i & 1u) == 1u ? (index >> 16) : index; - return index & 0xffff; - } - return IndexBuffer.Load(i << 2u); -} - -float3 LoadVertex(uint i) -{ - return asfloat(VertexBuffer.Load3(i * VertexStride)); -} +StructuredBuffer BVHBuffer : register(t2); // Renders triangle mesh into the SDF texture by writing minimum distance to the triangle into all intersecting voxels. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_RasterizeTriangle(uint3 DispatchThreadId : SV_DispatchThreadID) +void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID, uint GroupIndex : SV_GroupIndex) { - uint triangleIndex = DispatchThreadId.x; - if (triangleIndex >= TriangleCount) + uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex, THREAD_GROUP_SIZE); + if (voxelIndex >= ResolutionSize) return; + int3 voxelCoord = GetVoxelCoord(voxelIndex); + float3 voxelPos = GetVoxelPos(voxelCoord); - // Load triangle - triangleIndex *= 3; - uint i0 = LoadIndex(triangleIndex + 0); - uint i1 = LoadIndex(triangleIndex + 1); - uint i2 = LoadIndex(triangleIndex + 2); - float3 v0 = LoadVertex(i0); - float3 v1 = LoadVertex(i1); - float3 v2 = LoadVertex(i2); + // Point query to find the distance to the closest surface + BVHHit hit; + PointQueryBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, hit, MaxDistance); + float sdf = hit.Distance; - // Project triangle into SDF voxels - float3 vMargin = float3(WorldUnitsPerVoxel, WorldUnitsPerVoxel, WorldUnitsPerVoxel); - float3 vMin = min(min(v0, v1), v2) - vMargin; - float3 vMax = max(max(v0, v1), v2) + vMargin; - int3 voxelMargin = int3(1, 1, 1); - int3 voxelMin = GetVoxelCoord(vMin) - voxelMargin; - int3 voxelMax = GetVoxelCoord(vMax) + voxelMargin; - voxelMin = ClampVoxelCoord(voxelMin); - voxelMax = ClampVoxelCoord(voxelMax); - - // Rasterize into SDF voxels - for (int z = voxelMin.z; z <= voxelMax.z; z++) + // Raycast triangles around voxel to count triangle backfaces hit + #define CLOSEST_CACHE_SIZE 6 + float3 closestDirections[CLOSEST_CACHE_SIZE] = { - for (int y = voxelMin.y; y <= voxelMax.y; y++) + float3(+1, 0, 0), + float3(-1, 0, 0), + float3(0, +1, 0), + float3(0, -1, 0), + float3(0, 0, +1), + float3(0, 0, -1), + }; + uint hitBackCount = 0; + uint minBackfaceHitCount = (uint)(CLOSEST_CACHE_SIZE * BackfacesThreshold); + for (uint i = 0; i < CLOSEST_CACHE_SIZE; i++) + { + float3 rayDir = closestDirections[i]; + if (RayCastBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, rayDir, hit, MaxDistance)) { - for (int x = voxelMin.x; x <= voxelMax.x; x++) - { - int3 voxelCoord = int3(x, y, z); - int voxelIndex = GetVoxelIndex(voxelCoord); - float3 voxelPos = GetVoxelPos(voxelCoord); - float distance = SignedDistancePointToTriangle(voxelPos, v0, v1, v2); - if (distance < -10.0f) // TODO: find a better way to reject negative distance from degenerate triangles that break SDF shape - distance = abs(distance); - InterlockedMin(SDF[voxelIndex], FloatFlip3(distance)); - } + sdf = min(sdf, hit.Distance); + if (hit.IsBackface) + hitBackCount++; } } -} - -#endif - -#if defined(_CS_FloodFill) || defined(_CS_Encode) - -Buffer InSDF : register(t0); - -float GetVoxel(int voxelIndex) -{ - return asfloat(InSDF[voxelIndex]); -} - -float GetVoxel(int3 coord) -{ - coord = ClampVoxelCoord(coord); - int voxelIndex = GetVoxelIndex(coord); - return GetVoxel(voxelIndex); -} - -float CombineSDF(float sdf, int3 nearbyCoord, float nearbyDistance) -{ - // Sample nearby voxel - float sdfNearby = GetVoxel(nearbyCoord); - - // Include distance to that nearby voxel - if (sdfNearby < 0.0f) - nearbyDistance *= -1; - sdfNearby += nearbyDistance; - - if (sdfNearby > MaxDistance) + if (hitBackCount >= minBackfaceHitCount) { - // Ignore if nearby sample is invalid (see CS_Init) + // Voxel is inside the geometry so turn it into negative distance to the surface + sdf *= -1; } - else if (sdf > MaxDistance) - { - // Use nearby sample if current one is invalid (see CS_Init) - sdf = sdfNearby; - } - else - { - // Use distance closer to 0 - sdf = sdf >= 0 ? min(sdf, sdfNearby) : max(sdf, sdfNearby); - } - - return sdf; -} - -// Fills the voxels with minimum distances to the triangles. -META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_FloodFill(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) -{ - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); - if (voxelIndex >= ResolutionSize) - return; - float sdf = GetVoxel(voxelIndex); - - // Skip if the distance is already so small that we know that triangle is nearby - if (abs(sdf) > WorldUnitsPerVoxel * 1.2f) - { - int3 voxelCoord = GetVoxelCoord(voxelIndex); - int3 offset = int3(-1, 0, 1); - - // Sample nearby voxels - float nearbyDistance = WorldUnitsPerVoxel; - sdf = CombineSDF(sdf, voxelCoord + offset.zyy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yzy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yyz, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.xyy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yxy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yyx, nearbyDistance); - } - - SDF[voxelIndex] = asuint(sdf); -} - -RWTexture3D SDFtex : register(u1); - -// Encodes SDF values into the packed format with normalized distances. -META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_Encode(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) -{ - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); - if (voxelIndex >= ResolutionSize) - return; - float sdf = GetVoxel(voxelIndex); - sdf = min(sdf, MaxDistance); // Pack from range [-MaxDistance; +MaxDistance] to [0; 1] + sdf = clamp(sdf, -MaxDistance, MaxDistance); sdf = (sdf / MaxDistance) * 0.5f + 0.5f; - int3 voxelCoord = GetVoxelCoord(voxelIndex); SDFtex[voxelCoord] = sdf; } diff --git a/Source/Shaders/ShadowsSampling.hlsl b/Source/Shaders/ShadowsSampling.hlsl index 071b6f723..ac181732b 100644 --- a/Source/Shaders/ShadowsSampling.hlsl +++ b/Source/Shaders/ShadowsSampling.hlsl @@ -227,7 +227,8 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer // Increase the sharpness for higher cascades to match the filter radius const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f }; shadow.Sharpness *= SharpnessScale[cascadeIndex]; - + + result.TransmissionShadow = 1; #if defined(USE_GBUFFER_CUSTOM_DATA) // Subsurface shadowing BRANCH @@ -239,8 +240,6 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth); result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow); } -#else - result.TransmissionShadow = 1; #endif result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow); diff --git a/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl b/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl deleted file mode 100644 index 13c28f16d..000000000 --- a/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl +++ /dev/null @@ -1,121 +0,0 @@ -// Source: https://github.com/GPUOpen-Effects/TressFX -// License: MIT - -// -// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. -// -// 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. -// - -//When building the SDF we want to find the lowest distance at each SDF cell. In order to allow multiple threads to write to the same -//cells, it is necessary to use atomics. However, there is no support for atomics with 32-bit floats so we convert the float into unsigned int -//and use atomic_min() / InterlockedMin() as a workaround. -// -//When used with atomic_min, both FloatFlip2() and FloatFlip3() will store the float with the lowest magnitude. -//The difference is that FloatFlip2() will preper negative values ( InterlockedMin( FloatFlip2(1.0), FloatFlip2(-1.0) ) == -1.0 ), -//while FloatFlip3() prefers positive values ( InterlockedMin( FloatFlip3(1.0), FloatFlip3(-1.0) ) == 1.0 ). -//Using FloatFlip3() seems to result in a SDF with higher quality compared to FloatFlip2(). -uint FloatFlip2(float fl) -{ - uint f = asuint(fl); - return (f << 1) | (f >> 31 ^ 0x00000001); //Rotate sign bit to least significant and Flip sign bit so that (0 == negative) -} -uint IFloatFlip2(uint f2) -{ - return (f2 >> 1) | (f2 << 31 ^ 0x80000000); -} -uint FloatFlip3(float fl) -{ - uint f = asuint(fl); - return (f << 1) | (f >> 31); //Rotate sign bit to least significant -} -uint IFloatFlip3(uint f2) -{ - return (f2 >> 1) | (f2 << 31); -} - -float DistancePointToEdge(float3 p, float3 x0, float3 x1, out float3 n) -{ - float3 x10 = x1 - x0; - - float t = dot(x1 - p, x10) / dot(x10, x10); - t = max(0.0f, min(t, 1.0f)); - - float3 a = p - (t*x0 + (1.0f - t)*x1); - float d = length(a); - n = a / (d + 1e-30f); - - return d; -} - -// Check if p is in the positive or negative side of triangle (x0, x1, x2) -// Positive side is where the normal vector of triangle ( (x1-x0) x (x2-x0) ) is pointing to. -float SignedDistancePointToTriangle(float3 p, float3 x0, float3 x1, float3 x2) -{ - float d = 0; - float3 x02 = x0 - x2; - float l0 = length(x02) + 1e-30f; - x02 = x02 / l0; - float3 x12 = x1 - x2; - float l1 = dot(x12, x02); - x12 = x12 - l1*x02; - float l2 = length(x12) + 1e-30f; - x12 = x12 / l2; - float3 px2 = p - x2; - - float b = dot(x12, px2) / l2; - float a = (dot(x02, px2) - l1*b) / l0; - float c = 1 - a - b; - - // normal vector of triangle. Don't need to normalize this yet. - float3 nTri = cross((x1 - x0), (x2 - x0)); - float3 n; - - float tol = 1e-8f; - - if (a >= -tol && b >= -tol && c >= -tol) - { - n = p - (a*x0 + b*x1 + c*x2); - d = length(n); - - float3 n1 = n / d; - float3 n2 = nTri / (length(nTri) + 1e-30f); // if d == 0 - - n = (d > 0) ? n1 : n2; - } - else - { - float3 n_12; - float3 n_02; - d = DistancePointToEdge(p, x0, x1, n); - - float d12 = DistancePointToEdge(p, x1, x2, n_12); - float d02 = DistancePointToEdge(p, x0, x2, n_02); - - d = min(d, d12); - d = min(d, d02); - - n = (d == d12) ? n_12 : n; - n = (d == d02) ? n_02 : n; - } - - d = (dot(p - x0, nTri) < 0.f) ? -d : d; - - return d; -} diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index d3885c20b..c55242996 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -341,7 +341,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_ if (all(gridCoordinate < GridSizeInt)) { - scatteringAndExtinction = isnan(scatteringAndExtinction) || isinf(scatteringAndExtinction) ? 0 : scatteringAndExtinction; + scatteringAndExtinction = select(or(isnan(scatteringAndExtinction), isinf(scatteringAndExtinction)), 0, scatteringAndExtinction); RWLightScattering[gridCoordinate] = max(scatteringAndExtinction, 0); } } diff --git a/Source/ThirdParty/OpenAL/al.h b/Source/ThirdParty/OpenAL/al.h index 5071fa5e3..a4e3ad515 100644 --- a/Source/ThirdParty/OpenAL/al.h +++ b/Source/ThirdParty/OpenAL/al.h @@ -1,8 +1,40 @@ #ifndef AL_AL_H #define AL_AL_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { + +#ifdef _MSVC_LANG +#define AL_CPLUSPLUS _MSVC_LANG +#else +#define AL_CPLUSPLUS __cplusplus +#endif + +#ifndef AL_DISABLE_NOEXCEPT +#if AL_CPLUSPLUS >= 201103L +#define AL_API_NOEXCEPT noexcept +#else +#define AL_API_NOEXCEPT +#endif +#if AL_CPLUSPLUS >= 201703L +#define AL_API_NOEXCEPT17 noexcept +#else +#define AL_API_NOEXCEPT17 +#endif + +#else /* AL_DISABLE_NOEXCEPT */ + +#define AL_API_NOEXCEPT +#define AL_API_NOEXCEPT17 +#endif + +#undef AL_CPLUSPLUS + +#else /* __cplusplus */ + +#define AL_API_NOEXCEPT +#define AL_API_NOEXCEPT17 #endif #ifndef AL_API @@ -455,220 +487,221 @@ typedef void ALvoid; #ifndef AL_NO_PROTOTYPES /* Renderer State management. */ -AL_API void AL_APIENTRY alEnable(ALenum capability); -AL_API void AL_APIENTRY alDisable(ALenum capability); -AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability); +AL_API void AL_APIENTRY alEnable(ALenum capability) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDisable(ALenum capability) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) AL_API_NOEXCEPT; /* Context state setting. */ -AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); -AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); -AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); -AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); +AL_API void AL_APIENTRY alDopplerFactor(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel) AL_API_NOEXCEPT; /* Context state retrieval. */ -AL_API const ALchar* AL_APIENTRY alGetString(ALenum param); -AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values); -AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); -AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); -AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param); -AL_API ALint AL_APIENTRY alGetInteger(ALenum param); -AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param); -AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param); +AL_API const ALchar* AL_APIENTRY alGetString(ALenum param) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param) AL_API_NOEXCEPT; +AL_API ALint AL_APIENTRY alGetInteger(ALenum param) AL_API_NOEXCEPT; +AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param) AL_API_NOEXCEPT; +AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param) AL_API_NOEXCEPT; /** * Obtain the first error generated in the AL context since the last call to * this function. */ -AL_API ALenum AL_APIENTRY alGetError(void); +AL_API ALenum AL_APIENTRY alGetError(void) AL_API_NOEXCEPT; /** Query for the presence of an extension on the AL context. */ -AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname); +AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname) AL_API_NOEXCEPT; /** * Retrieve the address of a function. The returned function may be context- * specific. */ -AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname); +AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname) AL_API_NOEXCEPT; /** * Retrieve the value of an enum. The returned value may be context-specific. */ -AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename); +AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename) AL_API_NOEXCEPT; /* Set listener parameters. */ -AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value); -AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value); -AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values); +AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get listener parameters. */ -AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values) AL_API_NOEXCEPT; /** Create source objects. */ -AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources); +AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) AL_API_NOEXCEPT; /** Delete source objects. */ -AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Verify an ID is for a valid source. */ -AL_API ALboolean AL_APIENTRY alIsSource(ALuint source); +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) AL_API_NOEXCEPT; /* Set source parameters. */ -AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); -AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get source parameters. */ -AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; /** Play, restart, or resume a source, setting its state to AL_PLAYING. */ -AL_API void AL_APIENTRY alSourcePlay(ALuint source); +AL_API void AL_APIENTRY alSourcePlay(ALuint source) AL_API_NOEXCEPT; /** Stop a source, setting its state to AL_STOPPED if playing or paused. */ -AL_API void AL_APIENTRY alSourceStop(ALuint source); +AL_API void AL_APIENTRY alSourceStop(ALuint source) AL_API_NOEXCEPT; /** Rewind a source, setting its state to AL_INITIAL. */ -AL_API void AL_APIENTRY alSourceRewind(ALuint source); +AL_API void AL_APIENTRY alSourceRewind(ALuint source) AL_API_NOEXCEPT; /** Pause a source, setting its state to AL_PAUSED if playing. */ -AL_API void AL_APIENTRY alSourcePause(ALuint source); +AL_API void AL_APIENTRY alSourcePause(ALuint source) AL_API_NOEXCEPT; /** Play, restart, or resume a list of sources atomically. */ -AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Stop a list of sources atomically. */ -AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Rewind a list of sources atomically. */ -AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Pause a list of sources atomically. */ -AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Queue buffers onto a source */ -AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); +AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; /** Unqueue processed buffers from a source */ -AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); +AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; /** Create buffer objects */ -AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers); +AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; /** Delete buffer objects */ -AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers); +AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; /** Verify an ID is a valid buffer (including the NULL buffer) */ -AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer); +AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) AL_API_NOEXCEPT; /** * Copies data into the buffer, interpreting it using the specified format and * samplerate. */ -AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate); +AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; /* Set buffer parameters. */ -AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); -AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get buffer parameters. */ -AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; #endif /* AL_NO_PROTOTYPES */ /* Pointer-to-function types, useful for storing dynamically loaded AL entry * points. */ -typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability); -typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability); -typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability); -typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param); -typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values); -typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); -typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param); -typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param); -typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param); -typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param); -typedef ALenum (AL_APIENTRY *LPALGETERROR)(void); -typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname); -typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname); -typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename); -typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources); -typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources); -typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); -typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); -typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers); -typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers); -typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer); -typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate); -typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value); -typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value); -typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value); -typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel); +typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERROR)(void) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_API_NOEXCEPT17; #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_AL_H */ diff --git a/Source/ThirdParty/OpenAL/alc.h b/Source/ThirdParty/OpenAL/alc.h index 6d2103335..d048ca04d 100644 --- a/Source/ThirdParty/OpenAL/alc.h +++ b/Source/ThirdParty/OpenAL/alc.h @@ -1,8 +1,40 @@ #ifndef AL_ALC_H #define AL_ALC_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { + +#ifdef _MSVC_LANG +#define ALC_CPLUSPLUS _MSVC_LANG +#else +#define ALC_CPLUSPLUS __cplusplus +#endif + +#ifndef AL_DISABLE_NOEXCEPT +#if ALC_CPLUSPLUS >= 201103L +#define ALC_API_NOEXCEPT noexcept +#else +#define ALC_API_NOEXCEPT +#endif +#if ALC_CPLUSPLUS >= 201703L +#define ALC_API_NOEXCEPT17 noexcept +#else +#define ALC_API_NOEXCEPT17 +#endif + +#else /* AL_DISABLE_NOEXCEPT */ + +#define ALC_API_NOEXCEPT +#define ALC_API_NOEXCEPT17 +#endif + +#undef ALC_CPLUSPLUS + +#else /* __cplusplus */ + +#define ALC_API_NOEXCEPT +#define ALC_API_NOEXCEPT17 #endif #ifndef ALC_API @@ -172,34 +204,34 @@ typedef void ALCvoid; /* Context management. */ /** Create and attach a context to the given device. */ -ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist); +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT; /** * Makes the given context the active process-wide context. Passing NULL clears * the active context. */ -ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context); +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) ALC_API_NOEXCEPT; /** Resumes processing updates for the given context. */ -ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Suspends updates for the given context. */ -ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Remove a context from its device and destroys it. */ -ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Returns the currently active context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void); +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) ALC_API_NOEXCEPT; /** Returns the device that a particular context is attached to. */ -ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context); +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) ALC_API_NOEXCEPT; /* Device management. */ /** Opens the named playback device. */ -ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename); +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) ALC_API_NOEXCEPT; /** Closes the given playback device. */ -ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device); +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT; /* Error support. */ /** Obtain the most recent Device error. */ -ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) ALC_API_NOEXCEPT; /* Extension support. */ @@ -207,24 +239,24 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); * Query for the presence of an extension on the device. Pass a NULL device to * query a device-inspecific extension. */ -ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname); +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT; /** * Retrieve the address of a function. Given a non-NULL device, the returned * function may be device-specific. */ -ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname); +ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT; /** * Retrieve the value of an enum. Given a non-NULL device, the returned value * may be device-specific. */ -ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname); +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT; /* Query functions. */ /** Returns information about the device, and error strings. */ -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT; /** Returns information about the device and the version of OpenAL. */ -ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT; /* Capture functions. */ @@ -232,43 +264,44 @@ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum pa * Opens the named capture device with the given frequency, format, and buffer * size. */ -ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT; /** Closes the given capture device. */ -ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device); +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT; /** Starts capturing samples into the device buffer. */ -ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) ALC_API_NOEXCEPT; /** Stops capturing samples. Samples in the device buffer remain available. */ -ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) ALC_API_NOEXCEPT; /** Reads samples from the device buffer. */ -ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT; #endif /* ALC_NO_PROTOTYPES */ /* Pointer-to-function types, useful for storing dynamically loaded ALC entry * points. */ -typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist); -typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void); -typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context); -typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename); -typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device); -typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device); -typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname); -typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname); -typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname); -typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param); -typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); -typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); -typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT17; +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT17; +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT17; #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_ALC_H */ diff --git a/Source/ThirdParty/OpenAL/alext.h b/Source/ThirdParty/OpenAL/alext.h index d313a999a..3908e1940 100644 --- a/Source/ThirdParty/OpenAL/alext.h +++ b/Source/ThirdParty/OpenAL/alext.h @@ -1,6 +1,7 @@ #ifndef AL_ALEXT_H #define AL_ALEXT_H +/* NOLINTBEGIN */ #include /* Define int64 and uint64 types */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ @@ -25,6 +26,8 @@ typedef uint64_t _alsoft_uint64_t; extern "C" { #endif +struct _GUID; + #ifndef AL_LOKI_IMA_ADPCM_format #define AL_LOKI_IMA_ADPCM_format 1 #define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 @@ -141,9 +144,9 @@ extern "C" { #ifndef AL_EXT_STATIC_BUFFER #define AL_EXT_STATIC_BUFFER 1 -typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei); +typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq); +void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; #endif #endif @@ -159,11 +162,11 @@ void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid * #ifndef ALC_EXT_thread_local_context #define ALC_EXT_thread_local_context 1 -typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); +typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) ALC_API_NOEXCEPT; +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) ALC_API_NOEXCEPT; #endif #endif @@ -176,9 +179,9 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); #define AL_SOFT_buffer_sub_data 1 #define AL_BYTE_RW_OFFSETS_SOFT 0x1031 #define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 -typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); +typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); +AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length) AL_API_NOEXCEPT; #endif #endif @@ -195,12 +198,12 @@ AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const AL #define AL_FOLDBACK_EVENT_STOP 0x4113 #define AL_FOLDBACK_MODE_MONO 0x4101 #define AL_FOLDBACK_MODE_STEREO 0x4102 -typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); +typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); -AL_API void AL_APIENTRY alRequestFoldbackStop(void); +AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alRequestFoldbackStop(void) AL_API_NOEXCEPT; #endif #endif @@ -263,15 +266,15 @@ AL_API void AL_APIENTRY alRequestFoldbackStop(void); #define AL_SAMPLE_LENGTH_SOFT 0x200A #define AL_SEC_LENGTH_SOFT 0x200B -typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); -typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); +typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) AL_API_NOEXCEPT; #endif #endif @@ -302,13 +305,13 @@ AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); #define ALC_6POINT1_SOFT 0x1505 #define ALC_7POINT1_SOFT 0x1506 -typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); -typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); -typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); +typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); -ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); -ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) AL_API_NOEXCEPT; +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) AL_API_NOEXCEPT; +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) AL_API_NOEXCEPT; #endif #endif @@ -328,31 +331,31 @@ ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffe #define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 typedef _alsoft_int64_t ALint64SOFT; typedef _alsoft_uint64_t ALuint64SOFT; -typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); -AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); -AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); -AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); -AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); -AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); -AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); -AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); -AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); -AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); -AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); -AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); +AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; #endif #endif @@ -364,11 +367,11 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64 #ifndef AL_SOFT_deferred_updates #define AL_SOFT_deferred_updates 1 #define AL_DEFERRED_UPDATES_SOFT 0xC002 -typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void); -typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void); +typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alDeferUpdatesSOFT(void); -AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); +AL_API void AL_APIENTRY alDeferUpdatesSOFT(void) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alProcessUpdatesSOFT(void) AL_API_NOEXCEPT; #endif #endif @@ -400,11 +403,11 @@ AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); #ifndef ALC_SOFT_pause_device #define ALC_SOFT_pause_device 1 -typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device); -typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device); +typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device); -ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); +ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) ALC_API_NOEXCEPT; +ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) ALC_API_NOEXCEPT; #endif #endif @@ -448,11 +451,11 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); #define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 #define ALC_HRTF_SPECIFIER_SOFT 0x1995 #define ALC_HRTF_ID_SOFT 0x1996 -typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); -typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); +typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); -ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); +ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT; +ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT; #endif #endif @@ -467,9 +470,9 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi #define AL_DEFAULT_RESAMPLER_SOFT 0x1211 #define AL_SOURCE_RESAMPLER_SOFT 0x1212 #define AL_RESAMPLER_NAME_SOFT 0x1213 -typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index); +typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index); +AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index) AL_API_NOEXCEPT; #endif #endif @@ -493,9 +496,9 @@ typedef _alsoft_uint64_t ALCuint64SOFT; #define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 #define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202 #define AL_SEC_OFFSET_CLOCK_SOFT 0x1203 -typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT; #endif #endif @@ -552,27 +555,26 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, #define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x19A5 #define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x19A6 typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param, - ALsizei length, const ALchar *message, - void *userParam); -typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable); -typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam); -typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname); -typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values); + ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable); -AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam); -AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname); -AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values); +AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values) AL_API_NOEXCEPT; #endif #endif #ifndef ALC_SOFT_reopen_device #define ALC_SOFT_reopen_device typedef ALCboolean (ALC_APIENTRY*LPALCREOPENDEVICESOFT)(ALCdevice *device, - const ALCchar *deviceName, const ALCint *attribs); + const ALCchar *deviceName, const ALCint *attribs) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName, - const ALCint *attribs); + const ALCint *attribs) ALC_API_NOEXCEPT; #endif #endif @@ -580,16 +582,16 @@ ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *de #define AL_SOFT_callback_buffer #define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 #define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 -typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes); -typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value); -typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values); +typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr); -AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr); -AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2); -AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr); +AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; #endif #endif @@ -640,16 +642,451 @@ AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid #ifndef AL_SOFT_source_start_delay #define AL_SOFT_source_start_delay -typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time); -typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time); +typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time); -void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time); +void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; #endif #endif +#ifndef ALC_EXT_debug +#define ALC_EXT_debug +#define ALC_CONTEXT_FLAGS_EXT 0x19CF +#define ALC_CONTEXT_DEBUG_BIT_EXT 0x0001 +#endif + +#ifndef AL_EXT_debug +#define AL_EXT_debug +#define AL_DONT_CARE_EXT 0x0002 +#define AL_DEBUG_OUTPUT_EXT 0x19B2 +#define AL_DEBUG_CALLBACK_FUNCTION_EXT 0x19B3 +#define AL_DEBUG_CALLBACK_USER_PARAM_EXT 0x19B4 +#define AL_DEBUG_SOURCE_API_EXT 0x19B5 +#define AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT 0x19B6 +#define AL_DEBUG_SOURCE_THIRD_PARTY_EXT 0x19B7 +#define AL_DEBUG_SOURCE_APPLICATION_EXT 0x19B8 +#define AL_DEBUG_SOURCE_OTHER_EXT 0x19B9 +#define AL_DEBUG_TYPE_ERROR_EXT 0x19BA +#define AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT 0x19BB +#define AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT 0x19BC +#define AL_DEBUG_TYPE_PORTABILITY_EXT 0x19BD +#define AL_DEBUG_TYPE_PERFORMANCE_EXT 0x19BE +#define AL_DEBUG_TYPE_MARKER_EXT 0x19BF +#define AL_DEBUG_TYPE_PUSH_GROUP_EXT 0x19C0 +#define AL_DEBUG_TYPE_POP_GROUP_EXT 0x19C1 +#define AL_DEBUG_TYPE_OTHER_EXT 0x19C2 +#define AL_DEBUG_SEVERITY_HIGH_EXT 0x19C3 +#define AL_DEBUG_SEVERITY_MEDIUM_EXT 0x19C4 +#define AL_DEBUG_SEVERITY_LOW_EXT 0x19C5 +#define AL_DEBUG_SEVERITY_NOTIFICATION_EXT 0x19C6 +#define AL_DEBUG_LOGGED_MESSAGES_EXT 0x19C7 +#define AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT 0x19C8 +#define AL_MAX_DEBUG_MESSAGE_LENGTH_EXT 0x19C9 +#define AL_MAX_DEBUG_LOGGED_MESSAGES_EXT 0x19CA +#define AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT 0x19CB +#define AL_MAX_LABEL_LENGTH_EXT 0x19CC +#define AL_STACK_OVERFLOW_EXT 0x19CD +#define AL_STACK_UNDERFLOW_EXT 0x19CE +#define AL_CONTEXT_FLAGS_EXT 0x19CF +#define AL_BUFFER_EXT 0x1009 /* Same as AL_BUFFER */ +#define AL_SOURCE_EXT 0x19D0 +#define AL_FILTER_EXT 0x19D1 +#define AL_EFFECT_EXT 0x19D2 +#define AL_AUXILIARY_EFFECT_SLOT_EXT 0x19D3 + +typedef void (AL_APIENTRY*ALDEBUGPROCEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKEXT)(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLEXT)(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPEXT)(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPEXT)(void) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGEXT)(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTEREXT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVEXT)(ALenum pname, void **values) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +void AL_APIENTRY alDebugMessageCallbackEXT(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertEXT(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlEXT(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupEXT(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupEXT(void) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogEXT(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelEXT(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerEXT(ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervEXT(ALenum pname, void **values) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef ALC_SOFT_system_events +#define ALC_SOFT_system_events +#define ALC_PLAYBACK_DEVICE_SOFT 0x19D4 +#define ALC_CAPTURE_DEVICE_SOFT 0x19D5 +#define ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT 0x19D6 +#define ALC_EVENT_TYPE_DEVICE_ADDED_SOFT 0x19D7 +#define ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT 0x19D8 +#define ALC_EVENT_SUPPORTED_SOFT 0x19D9 +#define ALC_EVENT_NOT_SUPPORTED_SOFT 0x19DA +typedef void (ALC_APIENTRY*ALCEVENTPROCTYPESOFT)(ALCenum eventType, ALCenum deviceType, + ALCdevice *device, ALCsizei length, const ALCchar *message, void *userParam) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY*LPALCEVENTISSUPPORTEDSOFT)(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCEVENTCONTROLSOFT)(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCEVENTCALLBACKSOFT)(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT; +ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT; +void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT; +#endif +#endif + +#ifndef AL_EXT_direct_context +#define AL_EXT_direct_context +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS2)(ALCdevice *device, const ALCchar *funcname) AL_API_NOEXCEPT17; + +typedef void (AL_APIENTRY *LPALENABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLEDDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTORDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUNDDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODELDIRECT)(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANVDIRECT)(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEVDIRECT)(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEANDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGERDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOATDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLEDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERRORDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENTDIRECT)(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESSDIRECT)(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUEDIRECT)(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFVDIRECT)(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIVDIRECT)(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCESDIRECT)(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCESDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFERDIRECT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATADIRECT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +/* ALC_EXT_EFX */ +typedef void (AL_APIENTRY *LPALGENEFFECTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECTDIRECT)(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENFILTERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTERDIRECT)(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOTDIRECT)(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +/* AL_EXT_BUFFER_DATA_STATIC */ +typedef void (AL_APIENTRY *LPALBUFFERDATASTATICDIRECT)(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT17; +/* AL_EXT_debug */ +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKDIRECTEXT)(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPDIRECTEXT)(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPDIRECTEXT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGDIRECTEXT)(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTERDIRECTEXT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVDIRECTEXT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_EXT_FOLDBACK */ +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTARTDIRECT)(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTOPDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_buffer_sub_data */ +typedef void (AL_APIENTRY *LPALBUFFERSUBDATADIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; +/* AL_SOFT_source_latency */ +typedef void (AL_APIENTRY *LPALSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +/* AL_SOFT_deferred_updates */ +typedef void (AL_APIENTRY *LPALDEFERUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALPROCESSUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_source_resampler */ +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGIDIRECTSOFT)(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT17; +/* AL_SOFT_events */ +typedef void (AL_APIENTRY *LPALEVENTCONTROLDIRECTSOFT)(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEVENTCALLBACKDIRECTSOFT)(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPOINTERDIRECTSOFT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETPOINTERVDIRECTSOFT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_SOFT_callback_buffer */ +typedef void (AL_APIENTRY *LPALBUFFERCALLBACKDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3PTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRVDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; +/* AL_SOFT_source_start_delay */ +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEDIRECTSOFT)(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEVDIRECTSOFT)(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; +/* EAX */ +typedef ALenum (AL_APIENTRY *LPEAXSETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPEAXSETBUFFERMODEDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETBUFFERMODEDIRECT)(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCvoid* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *funcName) AL_API_NOEXCEPT; + +void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; + +void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alGetBooleanDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALint AL_APIENTRY alGetIntegerDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALfloat AL_APIENTRY alGetFloatDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALdouble AL_APIENTRY alGetDoubleDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetProcAddressDirect(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT; +ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT; + +void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT; +void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; + +void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectEXT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alRequestFoldbackStartDirect(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +void AL_APIENTRY alRequestFoldbackStopDirect(ALCcontext *context) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; +void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT; + +void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef AL_SOFT_bformat_hoa +#define AL_SOFT_bformat_hoa +#define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D +#endif + #ifdef __cplusplus } #endif +/* NOLINTEND */ #endif diff --git a/Source/ThirdParty/OpenAL/efx-presets.h b/Source/ThirdParty/OpenAL/efx-presets.h index 8539fd517..acd5bf398 100644 --- a/Source/ThirdParty/OpenAL/efx-presets.h +++ b/Source/ThirdParty/OpenAL/efx-presets.h @@ -2,6 +2,7 @@ #ifndef EFX_PRESETS_H #define EFX_PRESETS_H +/* NOLINTBEGIN */ #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED @@ -399,4 +400,5 @@ typedef struct { #define EFX_REVERB_PRESET_SMALLWATERROOM \ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } +/* NOLINTEND */ #endif /* EFX_PRESETS_H */ diff --git a/Source/ThirdParty/OpenAL/efx.h b/Source/ThirdParty/OpenAL/efx.h index 5ab64a64d..1e93bf222 100644 --- a/Source/ThirdParty/OpenAL/efx.h +++ b/Source/ThirdParty/OpenAL/efx.h @@ -1,6 +1,7 @@ #ifndef AL_EFX_H #define AL_EFX_H +/* NOLINTBEGIN */ #include #include "alc.h" @@ -204,80 +205,80 @@ extern "C" { /* Effect object function types. */ -typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); -typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; /* Filter object function types. */ -typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); -typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; /* Auxiliary Effect Slot object function types. */ -typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); -AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects); -AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); -AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); -AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters); -AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); -AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); -AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots); -AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); -AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; #endif /* Filter ranges and defaults. */ @@ -758,5 +759,6 @@ AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum par #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_EFX_H */ diff --git a/Source/ThirdParty/detex/detex.h b/Source/ThirdParty/detex/detex.h index adbeac512..ec61d7495 100644 --- a/Source/ThirdParty/detex/detex.h +++ b/Source/ThirdParty/detex/detex.h @@ -72,7 +72,11 @@ __BEGIN_DECLS #include #include +#if defined(_MSC_VER) +#define DETEX_INLINE_ONLY __forceinline +#else #define DETEX_INLINE_ONLY __attribute__((always_inline)) inline +#endif #define DETEX_RESTRICT __restrict /* Maximum uncompressed block size in bytes. */ diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index bac529778..2910e401d 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -48,9 +48,6 @@ public class nethost : ThirdPartyModule switch (options.Platform.Target) { case TargetPlatform.Windows: - case TargetPlatform.XboxOne: - case TargetPlatform.XboxScarlett: - case TargetPlatform.UWP: if (hostRuntime.Type == DotNetSdk.HostType.CoreCLR) { options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "nethost.lib")); @@ -63,6 +60,12 @@ public class nethost : ThirdPartyModule options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "coreclr.dll")); } break; + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "monosgen-2.0.lib")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "mono-profiler-aot.lib")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "System.Globalization.Native-Static.lib")); + break; case TargetPlatform.Linux: options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.a")); options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.so")); diff --git a/Source/ThirdParty/tracy/client/TracyAlloc.cpp b/Source/ThirdParty/tracy/client/TracyAlloc.cpp index 545a6062b..923c92494 100644 --- a/Source/ThirdParty/tracy/client/TracyAlloc.cpp +++ b/Source/ThirdParty/tracy/client/TracyAlloc.cpp @@ -6,6 +6,10 @@ #include "../common/TracyYield.hpp" +#if PLATFORM_WINDOWS +extern void CheckInstructionSet(); +#endif + namespace tracy { @@ -18,6 +22,10 @@ tracy_no_inline static void InitRpmallocPlumbing() const auto done = RpInitDone.load( std::memory_order_acquire ); if( !done ) { +#if PLATFORM_WINDOWS + // Check instruction set before executing any code (Tracy init static vars before others) + CheckInstructionSet(); +#endif int expected = 0; while( !RpInitLock.compare_exchange_weak( expected, 1, std::memory_order_release, std::memory_order_relaxed ) ) { expected = 0; YieldThread(); } const auto done = RpInitDone.load( std::memory_order_acquire ); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 54329634f..034803d25 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -703,6 +703,8 @@ namespace Flax.Build.Bindings else if (nativeType.EndsWith("[]")) { parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>))"; + if (!parameterInfo.IsOut && !parameterInfo.IsRef) + parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters. } if (!string.IsNullOrEmpty(parameterMarshalType)) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 2f251932f..46f0ec245 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -387,17 +387,7 @@ namespace Flax.Build.Bindings // Find namespace for this type to build a fullname if (apiType != null) - { - var e = apiType.Parent; - while (!(e is FileInfo)) - { - e = e.Parent; - } - if (e is FileInfo fileInfo && !managedType.StartsWith(fileInfo.Namespace)) - { - managedType = fileInfo.Namespace + '.' + managedType.Replace(".", "+"); - } - } + managedType = apiType.Namespace + '.' + managedType.Replace(".", "+"); // Use runtime lookup from fullname of the C# class return "Scripting::FindClass(\"" + managedType + "\")"; @@ -770,6 +760,11 @@ namespace Flax.Build.Bindings genericArgs += ", " + typeInfo.GenericArgs[1]; result = $"Array<{genericArgs}>({result})"; } + else if (arrayApiType?.Name == "bool") + { + type = "bool*"; + result = "Array({0}, {1})"; + } return result; } @@ -925,7 +920,7 @@ namespace Flax.Build.Bindings // BytesContainer if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) - return "MUtils::ToArray({0})"; + return $"MUtils::ToArray({value})"; // Construct native typename for MUtils template argument var nativeType = new StringBuilder(64); @@ -1244,8 +1239,12 @@ namespace Flax.Build.Bindings callParams += ", "; separator = true; var name = parameterInfo.Name; + var countParamName = $"__{parameterInfo.Name}Count"; if (CppParamsThatNeedConversion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false)) + { name = '*' + name; + countParamName = '*' + countParamName; + } string param = string.Empty; if (string.IsNullOrWhiteSpace(CppParamsWrappersCache[i])) @@ -1258,7 +1257,7 @@ namespace Flax.Build.Bindings else { // Convert value - param += string.Format(CppParamsWrappersCache[i], name); + param += string.Format(CppParamsWrappersCache[i], name, countParamName); } // Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter) @@ -1293,11 +1292,21 @@ namespace Flax.Build.Bindings callParams += parameterInfo.Name; callParams += "Temp"; } - // Instruct for more optoimized value move operation + // Instruct for more optimized value move operation else if (parameterInfo.Type.IsMoveRef) { callParams += $"MoveTemp({param})"; } + else if (parameterInfo.Type.IsRef && !parameterInfo.Type.IsConst) + { + // Non-const lvalue reference parameters needs to be passed via temporary value + if (parameterInfo.IsOut || parameterInfo.IsRef) + contents.Append(indent).AppendFormat("{2}& {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + else + contents.Append(indent).AppendFormat("{2} {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + callParams += parameterInfo.Name; + callParams += "Temp"; + } else { callParams += param; @@ -2043,6 +2052,7 @@ namespace Flax.Build.Bindings contents.Append(')').AppendLine(); contents.Append(" {").AppendLine(); contents.Append(" static MMethod* method = nullptr;").AppendLine(); + contents.AppendFormat(" if (!MCore::Ready) {{ MCore::OnManagedEventAfterShutdown(\"{0}.{1}\"); return; }}", classTypeNameManaged, eventInfo.Name).AppendLine(); contents.AppendFormat(" if (!method) {{ method = {1}::TypeInitializer.GetClass()->GetMethod(\"Internal_{0}_Invoke\", {2}); CHECK(method); }}", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine(); contents.Append(" MObject* exception = nullptr;").AppendLine(); if (paramsCount == 0) @@ -2996,16 +3006,19 @@ namespace Flax.Build.Bindings header.Append("template<>").AppendLine(); header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); - header.AppendFormat(" MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" auto managed = ToManaged(data);").AppendLine(); header.Append(" return MCore::Object::Box((void*)&managed, klass);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void Unbox({0}& result, MObject* data)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED void Unbox({0}& result, MObject* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(result);", wrapperName).AppendLine(); @@ -3015,7 +3028,8 @@ namespace Flax.Build.Bindings header.Append(" MCore::GC::WriteValue(&resultPtr[i], &managed, 1, klass);").AppendLine(); header.Append(" }").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); @@ -3107,7 +3121,7 @@ namespace Flax.Build.Bindings header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); - header.AppendFormat(" static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" MObject* obj = MCore::Object::New(klass);").AppendLine(); for (var i = 0; i < fields.Count; i++) @@ -3127,13 +3141,13 @@ namespace Flax.Build.Bindings header.Append(" return obj;").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" static MObject* Box(const {0}& data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); header.Append(" return Box(data, klass);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" static void Unbox({0}& result, MObject* obj)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static void Unbox({0}& result, MObject* obj)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" MClass* klass = MCore::Object::GetClass(obj);").AppendLine(); header.Append(" void* v = nullptr;").AppendLine(); @@ -3155,20 +3169,20 @@ namespace Flax.Build.Bindings } header.Append(" }").AppendLine(); - header.AppendFormat(" static {0} Unbox(MObject* data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static {0} Unbox(MObject* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" {0} result;", fullName).AppendLine(); header.Append(" Unbox(result, data);").AppendLine(); header.Append(" return result;").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine(); header.Append(" MCore::GC::WriteArrayRef(result, Box(data[i]), i);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" MObject** dataPtr = (MObject**)MCore::Array::GetAddress(data);").AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index e421b6999..96551a91d 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -462,7 +462,7 @@ namespace Flax.Build else { // Copy to the destination folder - Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName)); + Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName), Utilities.CopyMode.OverrideIfNewer); } }; if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index bf3c61f7e..28c3958df 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -115,7 +115,7 @@ namespace Flax.Build /// Init with a proper message. /// public MissingException() - : base(string.IsNullOrEmpty(Configuration.Dotnet) ? $"Missing .NET SDK {MinimumVersion} (or higher)." : $"Missing .NET SDK {Configuration.Dotnet}.") + : base(string.IsNullOrEmpty(Configuration.Dotnet) ? $"Missing .NET SDK {MinimumVersion} (or higher) for {Platform.BuildTargetPlatform} {Platform.BuildTargetArchitecture}." : $"Missing .NET SDK {Configuration.Dotnet}.") { } } @@ -135,7 +135,7 @@ namespace Flax.Build /// /// The maximum SDK version. /// - public static Version MaximumVersion => new Version(9, 0); + public static Version MaximumVersion => new Version(10, 0); /// public override TargetPlatform[] Platforms @@ -217,9 +217,17 @@ namespace Flax.Build using RegistryKey sdkVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sdk"); using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost"); - dotnetPath = (string)hostKey.GetValue("Path"); - dotnetSdkVersions = sdkVersionsKey.GetValueNames(); - dotnetRuntimeVersions = runtimeKey.GetValueNames(); + dotnetPath = (string)hostKey?.GetValue("Path"); + dotnetSdkVersions = sdkVersionsKey?.GetValueNames() ?? Enumerable.Empty(); + dotnetRuntimeVersions = runtimeKey?.GetValueNames() ?? Enumerable.Empty(); + + if (string.IsNullOrEmpty(dotnetPath)) + { + // The sharedhost registry key seems to be deprecated, assume the default installation location instead + var defaultPath = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Program Files", "dotnet"); + if (File.Exists(Path.Combine(defaultPath, "dotnet.exe"))) + dotnetPath = defaultPath; + } } #pragma warning restore CA1416 break; diff --git a/Source/Tools/Flax.Build/Build/EngineModule.cs b/Source/Tools/Flax.Build/Build/EngineModule.cs index 679a558d2..5fc470665 100644 --- a/Source/Tools/Flax.Build/Build/EngineModule.cs +++ b/Source/Tools/Flax.Build/Build/EngineModule.cs @@ -42,7 +42,8 @@ namespace Flax.Build BinaryModuleName = "FlaxEngine"; options.ScriptingAPI.Defines.Add("FLAX"); options.ScriptingAPI.Defines.Add("FLAX_ASSERTIONS"); - options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", "Newtonsoft.Json.dll"))); + var newtonsoftJsonPath = options.Platform?.HasDynamicCodeExecutionSupport ?? true ? "Newtonsoft.Json.dll" : "AOT/Newtonsoft.Json.dll"; + options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", newtonsoftJsonPath))); options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter"); } } diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 827205e91..e22a924d9 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -142,7 +142,7 @@ namespace Flax.Build } /// - /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking again executable file but only shared library (see HasExecutableFileReferenceSupport). + /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking (or symbol lookup) against executable file but only shared library (see HasExecutableFileReferenceSupport). /// public virtual bool UseSeparateMainExecutable(BuildOptions buildOptions) { @@ -217,7 +217,8 @@ namespace Flax.Build var engineLibraryType = LinkerOutput.SharedLibrary; if (buildOptions.Toolchain?.Compiler == TargetCompiler.MSVC) engineLibraryType = LinkerOutput.ImportLibrary; // MSVC links DLL against import library - exeBuildOptions.LinkEnv.InputLibraries.Add(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType))); + var engineLibraryPath = Utilities.NormalizePath(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType))); + exeBuildOptions.LinkEnv.InputLibraries.Add(engineLibraryPath); exeBuildOptions.LinkEnv.InputFiles.AddRange(mainModuleOptions.OutputFiles); exeBuildOptions.DependencyFiles.AddRange(mainModuleOptions.DependencyFiles); exeBuildOptions.NugetPackageReferences.AddRange(mainModuleOptions.NugetPackageReferences); diff --git a/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs b/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs index bd0ef2ad5..6c459b33e 100644 --- a/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs +++ b/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs @@ -134,7 +134,7 @@ namespace Flax.Build.Graph } /// - /// Performs tasks list sorting based on task dependencies and cost heuristics to to improve parallelism of the graph execution. + /// Performs tasks list sorting based on task dependencies and cost heuristics to improve parallelism of the graph execution. /// public void SortTasks() { @@ -149,12 +149,7 @@ namespace Flax.Build.Graph { if (FileToProducingTaskMap.TryGetValue(prerequisiteFile, out var prerequisiteTask)) { - HashSet dependentTasks; - if (taskToDependentActionsMap.ContainsKey(prerequisiteTask)) - { - dependentTasks = taskToDependentActionsMap[prerequisiteTask]; - } - else + if (!taskToDependentActionsMap.TryGetValue(prerequisiteTask, out var dependentTasks)) { dependentTasks = new HashSet(); taskToDependentActionsMap[prerequisiteTask] = dependentTasks; diff --git a/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs b/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs index cfcbf9866..afe5c8e3c 100644 --- a/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs +++ b/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs @@ -241,7 +241,11 @@ namespace Flax.Deploy if (!File.Exists(solutionFile)) { - throw new Exception(string.Format("Unable to build solution {0}. Solution file not found.", solutionFile)); + // CMake VS2026 generator prefers .slnx solution files, just swap the extension for CMake dependencies + if (File.Exists(Path.ChangeExtension(solutionFile, "slnx"))) + solutionFile = Path.ChangeExtension(solutionFile, "slnx"); + else + throw new Exception(string.Format("Unable to build solution {0}. Solution file not found.", solutionFile)); } string cmdLine = string.Format("\"{0}\" /m /t:Restore,Build /p:Configuration=\"{1}\" /p:Platform=\"{2}\" {3} /nologo", solutionFile, buildConfig, buildPlatform, Verbosity); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs index f72928bf8..bb1c4fa3c 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs @@ -127,11 +127,12 @@ namespace Flax.Deps.Dependencies { "CC", "clang-" + Configuration.LinuxClangMinVer }, { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; // Build for Linux RunCmake(root, platform, TargetArchitecture.x64, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig, envVars); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool, envVars); configHeaderFilePath = Path.Combine(root, "include", "assimp", "config.h"); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); Utilities.FileCopy(Path.Combine(root, "lib", "libassimp.a"), Path.Combine(depsFolder, "libassimp.a")); @@ -143,11 +144,11 @@ namespace Flax.Deps.Dependencies foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { RunCmake(root, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool); configHeaderFilePath = Path.Combine(root, "include", "assimp", "config.h"); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(root, "lib", "libassimp.a"), Path.Combine(depsFolder, "libassimp.a")); - Utilities.Run("make", "clean", null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("make", "clean", null, root, Utilities.RunOptions.DefaultTool); } break; } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs index 699e40f72..1120a94f8 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs @@ -115,6 +115,7 @@ namespace Flax.Deps.Dependencies var buildFolder = Path.Combine(nvCloth, "compiler", platform.ToString() + '_' + architecture.ToString()); var envVars = new Dictionary(); envVars["GW_DEPS_ROOT"] = root; + envVars["CMAKE_BUILD_PARALLEL_LEVEL"] = CmakeBuildParallel; switch (platform) { case TargetPlatform.Windows: @@ -166,6 +167,8 @@ namespace Flax.Deps.Dependencies cmakeArgs += " -DTARGET_BUILD_PLATFORM=linux"; cmakeName = "linux"; binariesPrefix = "lib"; + envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer); + envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer); break; default: throw new InvalidPlatformException(platform); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 5b2cfdd9a..319ad70b3 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -49,7 +49,8 @@ namespace Flax.Deps.Dependencies public override void Build(BuildOptions options) { var root = options.IntermediateFolder; - var version = "1.23.1"; + var version = "1.24.3"; + var configuration = "Release"; var dstIncludePath = Path.Combine(options.ThirdPartyFolder, "OpenAL"); var noSSL = true; // OpenAL Soft website has broken certs @@ -66,11 +67,9 @@ namespace Flax.Deps.Dependencies "OpenAL32.dll", }; - string configuration = "Release"; - // Get the source CloneGitRepo(root, "https://github.com/kcat/openal-soft.git"); - GitCheckout(root, "master", "d3875f333fb6abe2f39d82caca329414871ae53b"); // 1.23.1 + GitCheckout(root, "master", "dc7d7054a5b4f3bec1dc23a42fd616a0847af948"); // 1.24.3 // Build for Win64 and ARM64 foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) @@ -123,9 +122,17 @@ namespace Flax.Deps.Dependencies var envVars = new Dictionary { { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer } + { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; - var config = "-DALSOFT_REQUIRE_ALSA=ON -DALSOFT_REQUIRE_OSS=ON -DALSOFT_REQUIRE_PORTAUDIO=ON -DALSOFT_REQUIRE_PULSEAUDIO=ON -DALSOFT_REQUIRE_JACK=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var config = $"-DALSOFT_REQUIRE_ALSA=ON " + + $"-DALSOFT_REQUIRE_OSS=ON " + + $"-DALSOFT_REQUIRE_PORTAUDIO=ON " + + $"-DALSOFT_REQUIRE_PULSEAUDIO=ON " + + $"-DALSOFT_REQUIRE_JACK=ON " + + $"-DALSOFT_REQUIRE_PIPEWIRE=ON " + + $"-DALSOFT_EMBED_HRTF_DATA=YES "; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -139,8 +146,8 @@ namespace Flax.Deps.Dependencies SetupDirectory(buildDir, true); // Build for Linux - Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLIBTYPE=STATIC " + config + " ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + Utilities.Run("cmake", $"-G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE={configuration} -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLIBTYPE=STATIC {config} ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + BuildCmake(buildDir, configuration, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -152,7 +159,11 @@ namespace Flax.Deps.Dependencies { "libopenal.a", }; - var config = "-DALSOFT_REQUIRE_OBOE=OFF -DALSOFT_REQUIRE_OPENSL=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var envVars = new Dictionary + { + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, + }; + var config = " -DALSOFT_REQUIRE_OBOE=OFF -DALSOFT_REQUIRE_OPENSL=ON -DALSOFT_EMBED_HRTF_DATA=YES"; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -175,8 +186,8 @@ namespace Flax.Deps.Dependencies SetupDirectory(buildDir, true); // Build - RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - BuildCmake(buildDir); + RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=" + configuration + config, envVars); + BuildCmake(buildDir, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -188,7 +199,11 @@ namespace Flax.Deps.Dependencies { "libopenal.a", }; - var config = "-DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var envVars = new Dictionary + { + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, + }; + var config = " -DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -204,8 +219,8 @@ namespace Flax.Deps.Dependencies foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { SetupDirectory(buildDir, true); - RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - BuildCmake(buildDir); + RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=" + configuration + config, envVars); + BuildCmake(buildDir, envVars); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -218,7 +233,11 @@ namespace Flax.Deps.Dependencies { "libopenal.a", }; - var config = "-DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var envVars = new Dictionary + { + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, + }; + var config = " -DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -234,8 +253,8 @@ namespace Flax.Deps.Dependencies // Build for iOS SetupDirectory(buildDir, true); - RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_SYSTEM_NAME=iOS -DALSOFT_OSX_FRAMEWORK=ON -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - BuildCmake(buildDir); + RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_SYSTEM_NAME=iOS -DALSOFT_OSX_FRAMEWORK=ON -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=" + configuration + config, envVars); + BuildCmake(buildDir, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index 32e2d2f38..39f7ad975 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -238,6 +238,8 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Linux: envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer); envVars.Add("CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer); + envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer); + envVars.Add("CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel); break; case TargetPlatform.Mac: break; default: throw new InvalidPlatformException(BuildPlatform); @@ -258,7 +260,7 @@ namespace Flax.Deps.Dependencies Log.Info("Building PhysX version " + File.ReadAllText(Path.Combine(root, "physx", "version.txt")) + " to " + binariesSubDir); // Generate project files - Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.DefaultTool, envVars); switch (targetPlatform) { @@ -304,10 +306,10 @@ namespace Flax.Deps.Dependencies } break; case TargetPlatform.Linux: - Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput, envVars); break; case TargetPlatform.Mac: - Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput, envVars); break; default: throw new InvalidPlatformException(BuildPlatform); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs index a474b9566..447f573a7 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs @@ -107,13 +107,14 @@ namespace Flax.Deps.Dependencies { { "CC", "clang-" + Configuration.LinuxClangMinVer }, { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var buildDir = Path.Combine(root, "build"); SetupDirectory(buildDir, true); - Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("make", "install", null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run("make", "install", null, root, Utilities.RunOptions.DefaultTool); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); var filename = "libcurl.a"; Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename)); @@ -153,11 +154,11 @@ namespace Flax.Deps.Dependencies }; var buildDir = Path.Combine(root, "build"); SetupDirectory(buildDir, true); - Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("chmod", "+x install-sh", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --host=" + archName + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("make", "install", null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run("chmod", "+x install-sh", null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --host=" + archName + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run("make", "install", null, root, Utilities.RunOptions.DefaultTool); var depsFolder = GetThirdPartyFolder(options, platform, architecture); var filename = "libcurl.a"; Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index ac0079401..89ed09a72 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -114,19 +114,22 @@ namespace Flax.Deps.Dependencies } case TargetPlatform.Linux: { - var envVars = new Dictionary + var envVars = new Dictionary { { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer } + { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; // Fix scripts - Utilities.Run("sed", "-i -e \'s/\r$//\' autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("sed", "-i -e \'s/\r$//\' configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("dos2unix", "autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("dos2unix", "configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + //Utilities.Run("sed", "-i -e \'s/\r$//\' autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + //Utilities.Run("sed", "-i -e \'s/\r$//\' configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); Utilities.Run("chmod", "+x autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError); Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars); + Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); // Disable using libpng even if it's found on the system var cmakeFile = Path.Combine(root, "CMakeLists.txt"); @@ -140,8 +143,8 @@ namespace Flax.Deps.Dependencies // Build for Linux SetupDirectory(buildDir, true); var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); - Utilities.Run("cmake", string.Format("-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_TARGET={0} ..", toolchain), null, buildDir, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("cmake", string.Format("-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_TARGET={0} ..", toolchain), null, buildDir, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.DefaultTool, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index 11ab39f8d..66909f6b9 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -89,7 +89,12 @@ namespace Flax.Deps.Dependencies os = "windows"; runtimeFlavor = "Mono"; buildMonoAotCross = true; - buildArgs = $" -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; + var defines = "-D_GAMING_XBOX=1-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1"; + defines += targetPlatform == TargetPlatform.XboxScarlett ? "-D_GAMING_XBOX_SCARLETT=1" : "-D_GAMING_XBOX_XBOXONE=1"; + defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1"; + buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; + envVars.Add("_GAMING_XBOX", "1"); + envVars.Add(targetPlatform == TargetPlatform.XboxScarlett ? "_GAMING_XBOX_SCARLETT" : "_GAMING_XBOX_XBOXONE", "1"); break; case TargetPlatform.Linux: os = "linux"; @@ -237,15 +242,28 @@ namespace Flax.Deps.Dependencies switch (targetPlatform) { case TargetPlatform.Windows: - case TargetPlatform.XboxOne: - case TargetPlatform.XboxScarlett: libs1 = new[] { "lib/coreclr.dll", "lib/coreclr.import.lib", }; - libs2 = new string[] + libs2 = new[] { + "System.Globalization.Native.dll", + "System.IO.Compression.Native.dll", + }; + break; + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + libs1 = new[] + { + "lib/monosgen-2.0.lib", + "lib/mono-profiler-aot.lib", + }; + libs2 = new[] + { + "lib/System.Globalization.Native-Static.lib", + "lib/System.IO.Compression.Native-Static.lib", }; break; default: @@ -303,9 +321,9 @@ namespace Flax.Deps.Dependencies } // Ensure to have dependencies installed - Utilities.Run("ninja", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("python", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("ninja", "--version", null, null, Utilities.RunOptions.DefaultTool); + Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.DefaultTool); + Utilities.Run("python", "--version", null, null, Utilities.RunOptions.DefaultTool); // Get the source if (!Directory.Exists(Path.Combine(root, ".git"))) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index 45adc9188..d22f8696f 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -366,15 +366,17 @@ namespace Flax.Deps.Dependencies var envVars = new Dictionary { { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer } + { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var buildDir = Path.Combine(root, "build"); - Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars); + Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.DefaultTool, envVars); // Build for Linux var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); - Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.Default, envVars); + Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); SetupDirectory(buildDir, true); Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index c746cb0ae..010a45175 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -47,6 +47,24 @@ namespace Flax.Deps /// protected static TargetPlatform BuildPlatform => Platform.BuildPlatform.Target; + + private static Version? _cmakeVersion; + protected static Version CMakeVersion + { + get + { + if (_cmakeVersion == null) + { + var versionOutput = Utilities.ReadProcessOutput("cmake", "--version"); + var versionStart = versionOutput.IndexOf("cmake version ") + "cmake version ".Length; + var versionEnd = versionOutput.IndexOfAny(['-', '\n', '\r'], versionStart); // End of line or dash before Git hash + var versionString = versionOutput.Substring(versionStart, versionEnd - versionStart); + _cmakeVersion = new Version(versionString); + } + return _cmakeVersion; + } + } + /// /// Gets the platforms list supported by this dependency to build on the current build platform (based on ). /// @@ -248,14 +266,30 @@ namespace Flax.Deps Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.DefaultTool); } + /// + /// Gets the maximum concurrency level for a cmake command. See CMAKE_BUILD_PARALLEL_LEVEL or -j docs. + /// + public static string CmakeBuildParallel => Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency).ToString(); + /// /// Builds the cmake project. /// /// The path. /// Custom environment variables to pass to the child process. - public static void BuildCmake(string path, Dictionary envVars = null) + public static void BuildCmake(string path, Dictionary envVars) { - Utilities.Run("cmake", "--build . --config Release", null, path, Utilities.RunOptions.DefaultTool, envVars); + BuildCmake(path, "Release", envVars); + } + + /// + /// Builds the cmake project. + /// + /// The path. + /// The configuration preset. + /// Custom environment variables to pass to the child process. + public static void BuildCmake(string path, string config = "Release", Dictionary envVars = null) + { + Utilities.Run("cmake", $"--build . --config {config}", null, path, Utilities.RunOptions.DefaultTool, envVars); } /// @@ -293,7 +327,13 @@ namespace Flax.Deps break; default: throw new InvalidArchitectureException(architecture); } - cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -A {0}", arch); + if (CMakeVersion.Major > 4 || (CMakeVersion.Major == 4 && CMakeVersion.Minor >= 2)) + { + // This generates both .sln and .slnx solution files + cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -G \"Visual Studio 18 2026\" -A {0}", arch); + } + else + cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -A {0}", arch); break; } case TargetPlatform.PS4: @@ -440,7 +480,7 @@ namespace Flax.Deps case TargetPlatform.Mac: break; default: throw new InvalidPlatformException(BuildPlatform); } - Utilities.Run(path, args, null, workspace, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run(path, args, null, workspace, Utilities.RunOptions.DefaultTool, envVars); } internal bool GetMsBuildForPlatform(TargetPlatform targetPlatform, out VisualStudioVersion vsVersion, out string msBuildPath) diff --git a/Source/Tools/Flax.Build/Deps/Downloader.cs b/Source/Tools/Flax.Build/Deps/Downloader.cs index 85972debe..84b807b13 100644 --- a/Source/Tools/Flax.Build/Deps/Downloader.cs +++ b/Source/Tools/Flax.Build/Deps/Downloader.cs @@ -14,6 +14,7 @@ namespace Flax.Deps /// static class Downloader { + private static bool NoSSL = false; private const string GoogleDriveDomain = "drive.google.com"; private const string GoogleDriveDomain2 = "https://drive.google.com"; @@ -210,7 +211,7 @@ namespace Flax.Deps private static HttpClient GetHttpClient(bool noSSL) { - if (noSSL) + if (noSSL || NoSSL) { var handler = new HttpClientHandler(); handler.ClientCertificateOptions = ClientCertificateOption.Manual; diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index d19d931ce..4b22d6924 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -37,22 +37,22 @@ - ..\..\..\Source\Platforms\DotNet\Ionic.Zip.Reduced.dll + ..\..\Platforms\DotNet\Ionic.Zip.Reduced.dll - ..\..\..\Source\Platforms\DotNet\System.Text.Encoding.CodePages.dll + ..\..\Platforms\DotNet\System.Text.Encoding.CodePages.dll - ..\..\..\Source\Platforms\DotNet\Mono.Cecil.dll + ..\..\Platforms\DotNet\Mono.Cecil.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll + ..\..\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.CodeAnalysis.CSharp.dll + ..\..\Platforms\DotNet\Microsoft.CodeAnalysis.CSharp.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.CodeAnalysis.dll + ..\..\Platforms\DotNet\Microsoft.CodeAnalysis.dll diff --git a/Source/Tools/Flax.Build/Log.cs b/Source/Tools/Flax.Build/Log.cs index fbc96e576..834e51768 100644 --- a/Source/Tools/Flax.Build/Log.cs +++ b/Source/Tools/Flax.Build/Log.cs @@ -65,7 +65,10 @@ namespace Flax.Build if (ApplyConsoleColors) Console.ForegroundColor = color; - Console.WriteLine(Indent + message); + if (color != ConsoleColor.Red) + Console.WriteLine(Indent + message); + else + Console.Error.WriteLine(Indent + message); if (ApplyConsoleColors) Console.ResetColor(); diff --git a/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs b/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs index 220a07bef..45e39e63c 100644 --- a/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs @@ -13,6 +13,11 @@ namespace Flax.Build.Platforms /// public abstract class GDKToolchain : WindowsToolchainBase { + /// + /// Enables OpenMP library as dynamic dependency. + /// + protected bool OpenMP = false; + /// /// Gets the version of Xbox Services toolset. /// @@ -74,6 +79,12 @@ namespace Flax.Build.Platforms options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vccorlib140.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140_1.dll")); + if (OpenMP) + { + redistToolsPath = Path.Combine(paths[0], "x64", "Microsoft.VC" + (int)crtToolset + ".OpenMP"); + redistToolsPath = Utilities.RemovePathRelativeParts(redistToolsPath); + options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcomp140.dll")); + } } } } diff --git a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs index 174b2bcee..6d017806d 100644 --- a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs @@ -14,7 +14,7 @@ namespace Flax.Build /// Specifies the minimum Clang compiler version to use on Linux (eg. 10). /// [CommandLine("linuxClangMinVer", "", "Specifies the minimum Clang compiler version to use on Linux (eg. 10).")] - public static string LinuxClangMinVer = "13"; + public static string LinuxClangMinVer = "14"; } } diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 6d5aacdde..b6e2d7998 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -320,6 +320,17 @@ namespace Flax.Build.Platforms { } + /// + /// Gets linker argument to reference a specific shared library file. + /// + /// The graph. + /// The options. + /// The shared library file path. + protected virtual string GetSharedLibraryLinkArg(TaskGraph graph, BuildOptions options, string library) + { + return string.Format("\"-l{0}\"", GetLibName(library)); + } + /// public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List sourceFiles, string outputPath) { @@ -507,7 +518,7 @@ namespace Flax.Build.Platforms var args = new List(); args.AddRange(options.LinkEnv.CustomArgs); { - args.Add(string.Format("-o \"{0}\"", outputFilePath)); + args.Add(string.Format("-o \"{0}\"", outputFilePath.Replace('\\', '/'))); if (!options.LinkEnv.DebugInformation) { @@ -527,7 +538,8 @@ namespace Flax.Build.Platforms // Input libraries var libraryPaths = new HashSet(); - foreach (var library in linkEnvironment.InputLibraries) + var dynamicLibExt = Platform.SharedLibraryFileExtension; + foreach (var library in linkEnvironment.InputLibraries.Concat(options.Libraries)) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); @@ -539,37 +551,12 @@ namespace Flax.Build.Platforms { // Skip executable } - else if (ext == ".so") + else if (ext == dynamicLibExt) { // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", GetLibName(library))); - } - else - { - task.PrerequisiteFiles.Add(library); - args.Add(string.Format("\"{0}\"", GetLibName(library))); - } - } - foreach (var library in options.Libraries) - { - var dir = Path.GetDirectoryName(library); - var ext = Path.GetExtension(library); - if (string.IsNullOrEmpty(dir)) - { - args.Add(string.Format("\"-l{0}\"", library)); - } - else if (string.IsNullOrEmpty(ext)) - { - // Skip executable - } - else if (ext == ".so") - { - // Link against dynamic library - task.PrerequisiteFiles.Add(library); - libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + args.Add(GetSharedLibraryLinkArg(graph, options, library)); } else { @@ -580,8 +567,7 @@ namespace Flax.Build.Platforms // Input files (link static libraries last) task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); - foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")) - .Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) + foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")).Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); } @@ -619,7 +605,7 @@ namespace Flax.Build.Platforms /// public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { - outputFilePath = outputFilePath.Replace('\\', '/'); + outputFilePath = Utilities.NormalizePath(outputFilePath); Task linkTask; switch (options.LinkEnv.Output) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index 5154cdbf9..0c2d44c5d 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -19,8 +19,8 @@ namespace Flax.Build /// /// Specifies the minimum CPU architecture type to support (on x86/x64). /// - [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (om x86/x64).")] - public static CpuArchitecture WindowsCpuArch = CpuArchitecture.AVX2; // 94.48% support on PC according to Steam Hardware & Software Survey: May 2025 (https://store.steampowered.com/hwsurvey/) + [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (on x86/x64).")] + public static CpuArchitecture WindowsCpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) } } @@ -76,22 +76,27 @@ namespace Flax.Build.Platforms options.LinkEnv.InputLibraries.Add("oleaut32.lib"); options.LinkEnv.InputLibraries.Add("delayimp.lib"); - if (options.Architecture == TargetArchitecture.ARM64) + options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; + + if (options.Architecture == TargetArchitecture.x64) + { + if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) + { + // Old Windows had lower support ratio for latest CPU features + options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; + } + if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) + { + // Windows 11 has hard requirement on SSE4.2 + options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + } + } + else if (options.Architecture == TargetArchitecture.ARM64) { options.CompileEnv.PreprocessorDefinitions.Add("USE_SOFT_INTRINSICS"); options.LinkEnv.InputLibraries.Add("softintrin.lib"); - } - - options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; - if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) - { - // Old Windows had lower support ratio for latest CPU features - options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; - } - if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) - { - // Windows 11 has hard requirement on SSE4.2 - options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + if (options.CompileEnv.CpuArchitecture != CpuArchitecture.None) + options.CompileEnv.CpuArchitecture = CpuArchitecture.NEON; } } diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index fe5abe4a3..583b93b55 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -778,6 +778,7 @@ namespace Flax.Build.Platforms /// public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { + outputFilePath = Utilities.NormalizePath(outputFilePath); var linkEnvironment = options.LinkEnv; var task = graph.Add(); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index 671cfb5e8..ffc95e047 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -132,6 +132,8 @@ namespace Flax.Build.Projects.VisualStudio // Configurations foreach (var configuration in project.Configurations) { + if (configuration.Equals(defaultConfiguration)) + continue; WriteConfiguration(project, csProjectFileContent, projectDirectory, configuration); } @@ -309,6 +311,7 @@ namespace Flax.Build.Projects.VisualStudio } csProjectFileContent.AppendLine(string.Format(" {0}\\{1}.CSharp.xml", outputPath, project.BaseName)); csProjectFileContent.AppendLine(" true"); + csProjectFileContent.AppendLine(string.Format(" {0}", configuration.ConfigurationName)); csProjectFileContent.AppendLine(" "); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs index 1a52274a7..290d194cf 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs @@ -115,6 +115,7 @@ namespace Flax.Build.Projects.VisualStudio if (Version >= VisualStudioVersion.VisualStudio2022) vcProjectFileContent.AppendLine(" false"); vcProjectFileContent.AppendLine(" ./"); + vcProjectFileContent.AppendLine(" ./"); vcProjectFileContent.AppendLine(" "); // Default properties @@ -377,9 +378,13 @@ namespace Flax.Build.Projects.VisualStudio vcUserFileContent.AppendLine(""); - if (platforms.Any(x => x.Target == TargetPlatform.Linux)) + if (platforms.Any(x => x.Target == TargetPlatform.Linux || x.Target == TargetPlatform.Mac)) { - // Override MSBuild .targets file with one that runs NMake commands (workaround for Rider on Linux) + var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), "$(Configuration.Split('.')[2])", $"FlaxEditor{Utilities.GetPlatformExecutableExt()}")).Replace('\\', '/'); + var debuggerProjectPath = Globals.Project.Name == "Flax" ? "" : Globals.Project.ProjectFolderPath; + var debuggerWorkingDirectory = Globals.Project.ProjectFolderPath; + + // Override MSBuild .targets file with one that runs NMake commands (workaround for Rider not finding "Microsoft.Cpp.Default.props" file) var cppTargetsFileContent = new StringBuilder(); cppTargetsFileContent.AppendLine(""); cppTargetsFileContent.AppendLine(" "); @@ -395,6 +400,12 @@ namespace Flax.Build.Projects.VisualStudio cppTargetsFileContent.AppendLine(" "); cppTargetsFileContent.AppendLine(" $(RootNamespace)$(Configuration.Split('.')[0])"); cppTargetsFileContent.AppendLine(" $(OutDir)/$(TargetName)$(TargetExt)"); + if (!string.IsNullOrEmpty(debuggerProjectPath)) + cppTargetsFileContent.AppendLine(string.Format(" -project \"{0}\"", debuggerProjectPath)); + else + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(string.Format(" {0}", editorPath)); + cppTargetsFileContent.AppendLine(string.Format(" {0}", debuggerWorkingDirectory)); cppTargetsFileContent.AppendLine(" "); cppTargetsFileContent.AppendLine(""); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index d657f6247..be9f1235f 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -630,9 +630,10 @@ namespace Flax.Build.Projects.VisualStudio { var profiles = new Dictionary(); var profile = new StringBuilder(); - var configuration = "Development"; + var configuration = "$(FlaxConfiguration)"; var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), configuration, $"FlaxEditor{Utilities.GetPlatformExecutableExt()}")).Replace('\\', '/'); var workspacePath = Utilities.NormalizePath(solutionDirectory).Replace('\\', '/'); + var args = Globals.Project.Name == "Flax" ? "" : $"-project \\\"{workspacePath}\\\""; foreach (var project in projects) { if (project.Type == TargetType.DotNetCore) @@ -645,7 +646,7 @@ namespace Flax.Build.Projects.VisualStudio profile.AppendLine(" \"commandName\": \"Executable\","); profile.AppendLine($" \"workingDirectory\": \"{workspacePath}\","); profile.AppendLine($" \"executablePath\": \"{editorPath}\","); - profile.AppendLine($" \"commandLineArgs\": \"-project \\\"{workspacePath}\\\"\","); + profile.AppendLine($" \"commandLineArgs\": \"{args}\","); profile.AppendLine(" \"nativeDebugging\": false"); profile.Append(" }"); if (profiles.ContainsKey(path)) diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 0ce5c8b7b..917b8aa77 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -144,24 +144,51 @@ namespace Flax.Build return new TwoWayEnumerator(source.GetEnumerator()); } + /// + /// File copy modes. + /// + public enum CopyMode + { + /// + /// Copies the file to the destination, fails if it already exists. + /// + New, + + /// + /// If destination file exists, it will be overriden. + /// + OverrideIfExists, + + /// + /// If destination file exists, has the same size and is newer than source file, it won't be overriden (avoids unnecessary copies). + /// + OverrideIfNewer, + } + /// /// Copies the file. /// /// The source file path. /// The destination file path. - /// if the destination file can be overwritten; otherwise, . - public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite = true) + /// Copy operation modes. + public static void FileCopy(string srcFilePath, string dstFilePath, CopyMode mode = CopyMode.OverrideIfExists) { if (string.IsNullOrEmpty(srcFilePath)) throw new ArgumentNullException(nameof(srcFilePath)); if (string.IsNullOrEmpty(dstFilePath)) throw new ArgumentNullException(nameof(dstFilePath)); + if (mode == CopyMode.OverrideIfNewer && + File.Exists(dstFilePath) && + File.GetLastWriteTime(srcFilePath) <= File.GetLastWriteTime(dstFilePath) && + new FileInfo(dstFilePath).Length == new FileInfo(srcFilePath).Length) + return; + Log.Verbose(srcFilePath + " -> " + dstFilePath); try { - File.Copy(srcFilePath, dstFilePath, overwrite); + File.Copy(srcFilePath, dstFilePath, mode != CopyMode.New); } catch (Exception ex) { @@ -173,6 +200,17 @@ namespace Flax.Build } } + /// + /// Copies the file. + /// + /// The source file path. + /// The destination file path. + /// if the destination file can be overwritten; otherwise, . + public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite) + { + FileCopy(srcFilePath, dstFilePath, overwrite ? CopyMode.OverrideIfExists : CopyMode.New); + } + /// /// Copies the directories. ///