Compare commits

...
Author SHA1 Message Date
bridgewaterrobbie 31f96c9065 Gemini tinkering 3 2025-05-22 11:09:26 -04:00
bridgewaterrobbie 1305303f2e Fix typo in getVertexFormat 2025-05-20 18:13:19 -04:00
bridgewaterrobbie 8532511107 Add enforced check for compressed texture block width/height offset 2025-05-20 16:10:17 -04:00
bridgewaterrobbie b762ccb78c Use the aspect to skip drawing for depth only. TODO is why this is even coming up? 2025-05-20 16:10:17 -04:00
bridgewaterrobbie 3296eb153c If necessary, apply padding to uploaded texture. 2025-05-20 16:10:17 -04:00
bridgewaterrobbie ae6d1028f7 Initial update3DImage implementation for webgpu 2025-05-20 16:10:17 -04:00
bridgewaterrobbie 92579b6307 Expose bytes per pixel function separately from computeDataSize 2025-05-20 16:10:17 -04:00
Benjamin Doherty 315b53c339 Release Filament 1.60.1 2025-05-20 12:42:33 -07:00
Andy Hovingh c55d459986 webgpu: samples/hellopbr.cpp CLI allows for selecting webgpu backend api 2025-05-20 14:40:48 -05:00
Andy Hovingh 7876695545 webgpu: add CLI to texturedquad.cpp sample to select backend + updated WebGPU warning message. 2025-05-20 14:40:48 -05:00
Matthew Hoffman e1a1464c5d Add helpers for creating RenderPipelineParams for backend tests (#8751)
* Add helpers for creating RenderPipelineParams for backend tests

BUGS=[398199600]

* Move all backend tests over to use cyan as the clear color
2025-05-20 19:17:12 +00:00
bridgewaterrobbie 67ed5141bf Avoid using paramater name type which is reserved in WGSL and thus can't be compiled for WebGPU 2025-05-20 14:09:23 -04:00
Juan Caldas b9d7a25b91 webgpu: remove Dummies and set layouts (#8746)
* Remove dummies, set sample types and do minor cleanup
2025-05-19 13:51:08 -04:00
Matthew Hoffman ba63ac63a0 Add a helper for creating a standard PipelineState to backend tests. (#8750)
BUGS=[398199600]
2025-05-19 16:58:54 +00:00
Sungun Park 1e77947758 Add glFramebufferTextureMultisampleMultiviewOVR for multiview MSAA (#8755)
This commit adds the `glFramebufferTextureMultisampleMultiviewOVR`
function, enabling MSAA to work in conjunction with the multiview
extension.

The function's declaration is present in the latest main branch of the
Khronos GLES2 API headers. While directly pulling these updated headers
is the ideal approach, it would necessitate a significant reorganization
of Filament's current header file structure and incorporate more files
than currently needed.

To minimize immediate changes and limit scope, this change adopts
only the necessary function declaration by copying it directly.

BUGS=[417311684]
2025-05-16 23:50:12 +00:00
bridgewaterrobbie 53df4012fe Enforce buffer offset rules for UBO and regular copies. 2025-05-16 17:40:45 -04:00
bridgewaterrobbie d880fab93f Rearrange buffer logic, and handle buffer writes not divisible by 4 2025-05-16 17:40:45 -04:00
bridgewaterrobbie fb8030b80a Revert "Dawn change to enable writeBuffer for size != multiple of 4"
This reverts commit b94c802076.
2025-05-16 17:40:45 -04:00
David Neto e2d009ff2a Update glslang (#8734)
Update glslang/tnt/README to point to updated commit hash

Glslang switched from using spirv.hpp to using spirv.hpp11
which collides with spirv.hpp when used in the same .cpp file.

This change requires https://github.com/google/filament/pull/8729
before it can compile. That has already landed.
2025-05-16 20:35:54 +00:00
Mathias Agopian 0a67ef4d80 Add multisampled descriptor types 2025-05-16 13:06:04 -07:00
Mathias Agopian 79d6f46f1a enforce offsets to be a multiple of 4 bytes
this affects VerteBuffer, BufferObject and IndexBuffer, this restriction
already existed in the Metal backend, it is now promoted to all
backends.
2025-05-16 13:05:40 -07:00
rafadevai 6481fa2acf GL: Fix the format of an ExternalImage being uninitialized (#8727)
When creating a new ExternalImage in EGLAndroid the
format was not set, causing random crashes when creating
new textures.
2025-05-16 19:29:20 +00:00
Mathias Agopian 7638b3ddd9 fix typo in SamplerParams comparison operator
FIXES=8740
2025-05-16 11:28:13 -07:00
Doris Wu fe474a9d7e Add GTAO shader support (#8688)
* Add GTAO options into the struct

* Add gtao mat and shader

* Set type specific parameters in ppm

* Fix inconsistent name

* Fix incorrect param type

* Add bent normal calculation

* Fix the gui

* Adjust precision

* Use mix

* Add some comments

* Refactoring

* Update comments

* Add comments for aoOptions

* Update

* Update

* Address the comments

* Split .mat into bent and non-bent

* Update

* Update

* Use sqrt on mobile

* Omit default case

* Commit beamsplitter generated changes

* Use built-in acosFast

* Remove unused function

* Fix the mismatched parameter type

* Some optimizations

* Commit beamsplitter results

* Update the comment

* Update default value

* Commit beamsplitter changes

* Extract common parameters out
2025-05-16 00:13:12 +00:00
Powei Feng 81008abe06 backend-test: fix push constant test for vk (#8732)
FIXES=417477740
2025-05-15 23:02:28 +00:00
Juan Caldas ef1d079c53 Update Descriptor Types (#8743) 2025-05-15 15:39:31 -07:00
Mathias AgopianandPowei Feng 5286dec70b Make sure that all descriptors are always initialized (#8739)
* Make sure that all descriptors are always initialized

* improvements to committing material instances

- from now on, only MaterialDomain::SURFACE material instances are
  automatically committed by FEngine at the beginning of a frame.
  COMPUTE and POST_PROCESS domains must be committed manually via
  a new API on MaterialInstance. Both domains are not public APIs so
  this shouldn't have an impact anywhere.


- DescriptorSet now validates that all descriptors are set and emits
  an error in the log if not.

- these changes prevent PostProcess material instances to be committed
  before all descriptors are set.

* Update filament/src/details/Engine.cpp

Co-authored-by: Powei Feng <powei@google.com>

---------

Co-authored-by: Powei Feng <powei@google.com>
2025-05-15 15:38:16 -07:00
bridgewaterrobbie d748dc590e Use WebGPU platform explicitly in shaders rather than rely on the mostly similar cases to Vulkan, with the exception of flipping the Y axis where they are different 2025-05-15 18:02:29 -04:00
Jeremy Nelson 93a0aec246 Resize metal layer for webgpu (#8738) 2025-05-15 13:40:50 -07:00
Powei Feng f3fa734793 vk: Clean up image layout - transitions and definitions (#8724)
We rename and modify the mapping form the backend layouts
to VK version of the layouts.  We fix some of the barrier
definitions which were inaccurate.

Also fix and clarify some of the VulkanBuffer barrier related
details.
2025-05-15 20:06:35 +00:00
Juan Caldas 33d3c3a897 webgpu: Adjust dynamic offset to fix the draw calls (#8736)
BUGS=[407961733]
2025-05-15 12:53:49 +00:00
Mathias AgopianandPowei Feng bbabf3480b re-do "Use the Perfetto SDK instead of ATRACE" (#8701) (#8728)
* re-do "Use the Perfetto SDK instead of ATRACE" (#8701)

This time we create an entirely new private header: Tracing.h which
uses the perfetto SDK instead of ATRACE. The old Systrace.h is
unchanged to presever backward compatibility but is essentially
deprecated and no longer used within the filament repo.

The new TRACING_ macros use an explicit CATEGORY parameter, which is
declared in Tracing.h.

Moreover, tracing can be compiled out by defining
FILAMENT_TRACING_ENABLED to false before including Tracing.h

iOS tracing is still supported and still controlled via
FILAMENT_APPLE_SYSTRACE.

There are three perfetto categories defined:

- "filament/filament"
- "filament/jobsystem"
- "filament/gltfio"

The "filament/jobsystem" category is compiled out by default.

And they can be enabled in AGI / perfetto by adding:

```
data_sources {
  config {
    name: "track_event"
    track_event_config {
      disabled_categories: "*"
      enabled_categories: "filament/filament"
      enabled_categories: "filament/jobsystem"
      enabled_categories: "filament/gltfio"
    }
  }
}
```

* Update libs/utils/include/private/utils/Tracing.h

Co-authored-by: Powei Feng <powei@google.com>

* Update libs/utils/src/android/Tracing.cpp

Co-authored-by: Powei Feng <powei@google.com>

* remove all references to SYSTRACE_TAG

---------

Co-authored-by: Powei Feng <powei@google.com>
2025-05-14 20:51:57 -07:00
Mathias Agopian e7a8f471a1 add sampler type in DescriptorSetLayout (#8705)
* add sampler type in DescriptorSetLayout

[**New Material Version**]

* select the proper descriptor set layout in the rendering loop

Now that the per-view layout can be different based on the type of
shadow, we need to make sure we set the proper layout in the pipeline
and use the corresponding descriptor set.

There are now 16 "per-view" layouts and their corresponding descriptor
set. There are two sets of 8, the current set is determined by the
shadow type on the view.

Then during rendering, when we set the pipeline we need to select the
corresponding layout; each material now offers both versions of the
layout and we just pick the right one.

DescriptorSet::setBuffer and setSampler now need the layout in order
to do some validation that the correct type of descriptor is used.
This will help catch problems earlier.

* DescriptorType now includes the "dimension" of the texture

DescriptorType now also encodes wether the descriptor expects a 2D, 3D,
Cubemap and/or Array.

This is needed by both Metal and WebGPU.

[**New Material Version**]

* Fix typos.

* fix fog sampler descriptor type
2025-05-14 17:02:17 -07:00
Ben Doherty d45a5cc926 Add documentation on using Instruments (#8737) 2025-05-14 14:53:12 -07:00
Powei Feng 361ba2afea renderdiff: separate comparison from rendering (#8733)
- Move the comparison logic into its own script
- Add entry point bash script to generate the renderings
- Separate the preamble bash logic into its own file
2025-05-14 09:48:16 -07:00
Ben Doherty 78419cd992 Add initialize method to PlatformMetal (#8708) 2025-05-13 14:30:02 -07:00
Sungun Park cc7361dba5 Release Filament 1.60.0 2025-05-13 21:27:27 +00:00
Powei Feng 5c0841ff56 Update release note after cherry-pick 2025-05-13 14:26:13 -07:00
Matthew Hoffman 10af183756 Document which vulkan tests have issues with associated bugs. (#8731)
BUGS=[409100093]
2025-05-13 21:10:34 +00:00
David Neto 56e0e9a424 Move calls to SPV remapper to another .cpp file (#8729) 2025-05-13 20:33:49 +00:00
Mathias Agopian 86a500c846 clean public header dependencies (#8725)
* remove io::ostream dependency from CString.h + clang tidy

* remove ostream.h dependency from Invocable.h

* remove Panic.h dependency from FixedCapacityVector.h

* remove ostream.h dependency from backend/ headers

* clang tidy Panic.h/Panic.cpp
2025-05-13 11:32:45 -07:00
Sungun Park 5b3f13fc1d Revert "Add getter functions for settings to build ColorGrading object (#8699)"
This reverts commit f10d226565.
2025-05-13 11:22:33 -07:00
Powei Feng 1b1dfaa57c build.sh ndk version fix again (#8726)
Fix again after #8717
2025-05-13 06:44:21 +00:00
Powei Feng bf8c84bbe5 Clean-up build.sh android (#8722)
Failed to build android when using CMake >= 4.0.

Remove a no longer required check for the android build. This check
should be covered by the cmake_minimum_required() call, which is
present in relevant CMakeLists.txts.
2025-05-13 06:29:17 +00:00
Jeremy Nelson fb179cabbc Using usage and format from filament 2025-05-12 16:15:21 -07:00
Jeremy Nelson a80ea743e8 Simplifying aspect cases 2025-05-12 16:15:21 -07:00
Jeremy Nelson 97f8106909 removing duplicate return 2025-05-12 16:15:21 -07:00
Jeremy Nelson d455899b93 Simplify cases and default to All 2025-05-12 16:15:21 -07:00
Jeremy Nelson 63fe439a5e Infer TextureView Aspect from format and usage
Picks an appropriate aspect based on formate and usage.
2025-05-12 16:15:21 -07:00
Ben Doherty 7847220aba Materials: allow specifying the shader stages for samplers (#8720) 2025-05-12 14:20:11 -07:00
Matthew Hoffman d76ae4395b Mark ExternalImageCocoaGL as final not just its destructor. (#8719)
This is necessary to build on MacOS with homebrew's llvm as opposed to Apple's.
2025-05-12 19:43:30 +00:00
Powei Feng f994cb58ce gl: add invalidate_framebuffer workaround for Mesa+Intel (#8706)
FIXES=405252622
2025-05-12 18:18:13 +00:00
Sungun Park 52eb682498 Update MATERIAL_VERSION to 60 2025-05-12 16:45:36 +00:00
Mathias Agopian 5a5168a191 CString tidy cleanup and rvalue methods
We add rvalue version of append/insert/replace so that calling
those on a temporary yields to a temporary.

Also add string literal versions of those so that we can 
append/insert/replace a literal without allocation, e.g.:

`CString foo = CString{ bar }.append("baz");`

This will not end-up creating a temporary CString for "baz".
2025-05-09 16:14:01 -07:00
Powei Feng e85dfe75c8 github: move test script to /test and same for /build (#8714)
- Moved get_mesa.sh to build/common/get-mesa.sh
- Moved check-headers.sh to test/check-headers/test.sh
- Moved check-metal-shaders.sh to test/check-metal-shaders/test.sh
2025-05-09 22:35:18 +00:00
Logan Lawrence 6beb40b0a1 Update release.yml to include native android build files (#8694) 2025-05-09 17:25:00 +00:00
Powei Feng 5cb96e5732 Clean up ndk version in build.sh (#8717)
Follow up to #8663
2025-05-09 15:51:30 +00:00
Matthew Hoffman 927aa57a4e Document ASAN (with leak detection) on MacOS (#8716)
* Revert "Optional CMake flag for enabling ASAN for backend and its tests. (#8696)"

This reverts commit 543b93939a.
There were other already existing ways to achieve this without the need for new flags.

* Add documentation on running with ASAN and leak detection on mac.

BUGS=[398198310]
2025-05-09 10:22:29 -05:00
Powei Feng 36e775902d renderdiff: script for updating golden images (#8709)
Adding a python script to enable updating new goldens into
a staging branch in the golden repo (filament-assets).

The same script can be used in github workflow to automatically
create a golden staging branch. This will be useful for users
without access to a mac (the only platform for generating
goldens as of now).
2025-05-08 22:07:17 +00:00
Powei Feng 53e28f3b33 github: fix release mac build (#8713) 2025-05-08 11:44:08 -07:00
Powei Feng 7ccbdb4633 Release Filament 1.59.5 2025-05-08 09:06:44 -07:00
Daisuke KasugaandDaisuke Kasuga f10d226565 Add getter functions for settings to build ColorGrading object (#8699)
* add getter methods for ColorGrading builder settings
* add tone mapper impl clone funcs
* update the release notes
---------

Co-authored-by: Daisuke Kasuga <dkasuga@google.com>
2025-05-09 00:43:40 +09:00
Powei Feng 28ecf5c35d renderdiff: add golden repo support (#8689)
- Add GoldenManager to manage access to the repo containing the
  goldens
- Add tif comparison code
- Enable comparison by default for actual test
2025-05-07 21:20:22 +00:00
Matthew Hoffman 9683eb649c Backend test python script updated to display side by side images. (#8695) 2025-05-07 20:48:55 +00:00
Powei Feng 3d10ae3ee3 github: more build file refactoring (#8678)
- Move emscripten download into its own script
 - Refactor the common "CI choice" prompt into its own file.
 - Move the content of `build/common/ci-common.sh` to the
   "CI choice" script.
 - Mention the get-emscripten.sh script in BUILDING.md
2025-05-07 19:39:11 +00:00
44a75dd44b VK: Add a new platform method to check UMA (#8704)
* VK: Add a new context method to check UMA

This heuristic should work on almost all devices,
can be extended later as needed.

* Addressing PR comments

---------

Co-authored-by: Powei Feng <powei@google.com>
Co-authored-by: Serge Metral <sergemetral@google.com>
2025-05-07 12:08:34 -07:00
Juan David Caldas de7bcd2df7 explicitly set bindings 2025-05-07 14:39:32 -04:00
bridgewaterrobbie 6adf140fb4 Simple and possibly incomplete pipeline hashing 2025-05-07 14:39:32 -04:00
Syed Idris Shah b94c802076 Dawn change to enable writeBuffer for size != multiple of 4 2025-05-07 14:39:32 -04:00
bridgewaterrobbieandAndy Hovingh b231de0b5b WebGPU: HelloTraingle hacks
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
2025-05-07 14:39:32 -04:00
bridgewaterrobbie fca200d549 Use variant layout label if available 2025-05-07 14:39:32 -04:00
bridgewaterrobbie ba1d3f6c76 Minimal labels implementation for descriptorset layout 2025-05-07 14:39:32 -04:00
bridgewaterrobbie 7734fd4ad9 Add CString Append function 2025-05-07 13:34:00 -04:00
Mathias Agopian 7e6839f535 remove dependence on per-view descset layout from filamat
I've been going back and forth on whether we information about the
per-view descriptor set layout should be written in the material
file.

In this change, we remove that dependency. By definition the 
"per view" descriptor-set layout should only depend on view parameters
and obviously, all materials must be compatible.

In practice, a material does affect the layout so a reconciliation 
needs to happen somewhere. It's easier to maintain to have all this
logic in one place (in Filament) instead of split between material
generation and filament. 


All this change really does is to remove the information about the
per-view layout from the material file, and move it to Material in
filament, where it is hardcoded (before it was hardcoded in filamat),
but because both sides needed to match there was shared code.

**Material recompilation needed**
2025-05-06 11:10:06 -07:00
Mathias Agopian 3d78322058 clang-tidy cleanup 2025-05-06 11:10:06 -07:00
Mathias Agopian 3e1ea7cdfd clean-up our handling of descriptor-sets a bit more
the main aim of this PR is to consolidate how we access the
"per view" descriptor set to a single place.

some validation code is moved into DescriptorSets.cpp, so we get a
more centralized idea of what we do with the descriptors.

also factorize in one place the filtering of sampler list by layouts.
2025-05-06 11:10:06 -07:00
Mathias Agopian 6d413a4faf add missing depth formats to getMetalFormat()
this prevented to upload depth data into a depth texture
2025-05-06 11:08:34 -07:00
Powei Feng af09c517b6 Revert "Use the Perfetto SDK instead of ATRACE" (#8701)
This reverts commit ca3ff7e08e.
2025-05-06 17:41:21 +00:00
342 changed files with 23717 additions and 18423 deletions
+1 -1
View File
@@ -3,5 +3,5 @@ runs:
using: "composite" using: "composite"
steps: steps:
- name: Set up dependency versions - name: Set up dependency versions
shell: bash shell: bash
run: cat ./build/common/versions >> $GITHUB_ENV run: cat ./build/common/versions >> $GITHUB_ENV
+16
View File
@@ -0,0 +1,16 @@
name: 'Web Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Cache EMSDK
id: emsdk-cache
uses: actions/cache@v4 # Use a specific version
with:
path: emsdk
key: ${{ runner.os }}-emsdk-${{ env.GITHUB_EMSDK_VERSION }}
- name: Install Web Prerequisites
shell: bash
run: |
bash ./build/common/get-emscripten.sh
echo "EMSDK=$PWD/emsdk" >> $GITHUB_ENV
+1 -1
View File
@@ -26,4 +26,4 @@ jobs:
path: out/filament-release-darwin.tgz path: out/filament-release-darwin.tgz
- name: Check public headers - name: Check public headers
run: | run: |
build/common/check-headers.sh out/release/filament/include test/check-headers/test.sh out/release/filament/include
+9 -6
View File
@@ -96,6 +96,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: ./.github/actions/linux-prereq - uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script - name: Run build script
run: | run: |
cd build/web && printf "y" | ./build.sh presubmit cd build/web && printf "y" | ./build.sh presubmit
@@ -123,13 +124,15 @@ jobs:
- uses: ./.github/actions/mac-prereq - uses: ./.github/actions/mac-prereq
- name: Cache Mesa and deps - name: Cache Mesa and deps
id: mesa-cache id: mesa-cache
uses: actions/cache@v4 # Use a specific version uses: actions/cache@v4
with: with:
path: mesa path: mesa
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }} key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
- name: Get Mesa - name: Prerequisites
id: mesa-prereq id: prereqs
run: bash test/utils/get_mesa.sh run: |
bash build/common/get-mesa.sh
pip install tifffile numpy
- name: Run Test - name: Run Test
run: bash test/renderdiff/test.sh run: bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
@@ -150,8 +153,8 @@ jobs:
- name: Run test - name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl* run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
code-correcteness: test-code-correctness:
name: code-correctness name: test-code-correctness
runs-on: 'macos-14-xlarge' runs-on: 'macos-14-xlarge'
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
+6 -6
View File
@@ -65,13 +65,9 @@ jobs:
build-mac: build-mac:
name: build-mac name: build-mac
runs-on: ${{ matrix.os }} runs-on: macos-14-xlarge
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop' if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
strategy:
matrix:
os: [macos-14-xlarge, ubuntu-22.04-32core]
steps: steps:
- name: Decide Git ref - name: Decide Git ref
id: git_ref id: git_ref
@@ -118,6 +114,7 @@ jobs:
with: with:
ref: ${{ steps.git_ref.outputs.ref }} ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq - uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script - name: Run build script
env: env:
TAG: ${{ steps.git_ref.outputs.tag }} TAG: ${{ steps.git_ref.outputs.tag }}
@@ -166,6 +163,9 @@ jobs:
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
cd out/android-release/filament
tar -czf ../../filament-${TAG}-android-native.tgz .
cd ../../..
- name: Sign sample-gltf-viewer - name: Sign sample-gltf-viewer
run: | run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64 echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
@@ -187,7 +187,7 @@ jobs:
script: | script: |
const upload = require('./build/common/upload-release-assets'); const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env; const { TAG } = process.env;
const globber = await glob.create(['out/*.aar', 'out/*.apk'].join('\n')); const globber = await glob.create(['out/*.aar', 'out/*.apk', 'out/*.tgz'].join('\n'));
await upload({ github, context }, await globber.glob(), TAG); await upload({ github, context }, await globber.glob(), TAG);
build-ios: build-ios:
+1
View File
@@ -17,6 +17,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: ./.github/actions/linux-prereq - uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script - name: Run build script
run: | run: |
cd build/web && printf "y" | ./build.sh continuous cd build/web && printf "y" | ./build.sh continuous
+2
View File
@@ -363,6 +363,8 @@ python ./emsdk.py activate latest
source ./emsdk_env.sh source ./emsdk_env.sh
``` ```
Alternatively, you can try running the script `build/common/get-emscripten.sh`.
After this you can invoke the [easy build](#easy-build) script as follows: After this you can invoke the [easy build](#easy-build) script as follows:
```shell ```shell
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.59.4' implementation 'com.google.android.filament:filament-android:1.60.1'
} }
``` ```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release: iOS projects can use CocoaPods to install the latest release:
```shell ```shell
pod 'Filament', '~> 1.59.4' pod 'Filament', '~> 1.60.1'
``` ```
## Documentation ## Documentation
+16
View File
@@ -7,6 +7,22 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.61.0
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]
- samples/texturedquad.cpp now has CLI to select backend api
- samples/hellopbr.cpp CLI now allows for selecting webgpu
## v1.60.1
## v1.60.0
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]
- matc non-functional change: Update GLSL postprocessor to
isolate calls to SPVRemap from calls to SPIRV-Cross.
## v1.59.5 ## v1.59.5
@@ -1801,6 +1801,22 @@ public class View {
* @see setAmbientOcclusionOptions() * @see setAmbientOcclusionOptions()
*/ */
public static class AmbientOcclusionOptions { public static class AmbientOcclusionOptions {
public enum AmbientOcclusionType {
/**
* use Scalable Ambient Occlusion
*/
SAO,
/**
* use Ground Truth-Based Ambient Occlusion
*/
GTAO,
}
/**
* Type of ambient occlusion algorithm.
*/
@NonNull
public AmbientOcclusionOptions.AmbientOcclusionType aoType = AmbientOcclusionOptions.AmbientOcclusionType.SAO;
/** /**
* Ambient Occlusion radius in meters, between 0 and ~10. * Ambient Occlusion radius in meters, between 0 and ~10.
*/ */
@@ -1810,7 +1826,8 @@ public class View {
*/ */
public float power = 1.0f; public float power = 1.0f;
/** /**
* Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. * Self-occlusion bias in meters. Use to avoid self-occlusion.
* Between 0 and a few mm. No effect when aoType set to GTAO
*/ */
public float bias = 0.0005f; public float bias = 0.0005f;
/** /**
@@ -1826,12 +1843,12 @@ public class View {
*/ */
public float bilateralThreshold = 0.05f; public float bilateralThreshold = 0.05f;
/** /**
* affects # of samples used for AO. * affects # of samples used for AO and params for filtering
*/ */
@NonNull @NonNull
public QualityLevel quality = QualityLevel.LOW; public QualityLevel quality = QualityLevel.LOW;
/** /**
* affects AO smoothness * affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO.
*/ */
@NonNull @NonNull
public QualityLevel lowPassFilter = QualityLevel.MEDIUM; public QualityLevel lowPassFilter = QualityLevel.MEDIUM;
@@ -1849,7 +1866,7 @@ public class View {
*/ */
public boolean bentNormals = false; public boolean bentNormals = false;
/** /**
* min angle in radian to consider * min angle in radian to consider. No effect when aoType set to GTAO.
*/ */
public float minHorizonAngleRad = 0.0f; public float minHorizonAngleRad = 0.0f;
/** /**
@@ -1904,6 +1921,19 @@ public class View {
*/ */
public boolean ssctEnabled = false; public boolean ssctEnabled = false;
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
public int gtaoSampleSliceCount = 4;
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
public int gtaoSampleStepsPerSlice = 3;
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
public float gtaoThicknessHeuristic = 0.004f;
} }
/** /**
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament GROUP=com.google.android.filament
VERSION_NAME=1.59.4 VERSION_NAME=1.60.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android. POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -11
View File
@@ -151,7 +151,7 @@ function print_fgviewer_help {
} }
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain # Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/android/ndk.version | cut -f 1 -d ".")} FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | cut -f 1 -d ".")}
# Requirements # Requirements
CMAKE_MAJOR=3 CMAKE_MAJOR=3
@@ -463,16 +463,6 @@ function ensure_android_build {
echo "Error: Android NDK side-by-side version ${FILAMENT_NDK_VERSION} or compatible must be installed, exiting" echo "Error: Android NDK side-by-side version ${FILAMENT_NDK_VERSION} or compatible must be installed, exiting"
exit 1 exit 1
fi fi
local cmake_version=$(cmake --version)
if [[ "${cmake_version}" =~ ([0-9]+)\.([0-9]+)\.[0-9]+ ]]; then
if [[ "${BASH_REMATCH[1]}" -lt "${CMAKE_MAJOR}" ]] || \
[[ "${BASH_REMATCH[2]}" -lt "${CMAKE_MINOR}" ]]; then
echo "Error: cmake version ${CMAKE_MAJOR}.${CMAKE_MINOR}+ is required," \
"${BASH_REMATCH[1]}.${BASH_REMATCH[2]} installed, exiting"
exit 1
fi
fi
} }
function build_android { function build_android {
+1 -28
View File
@@ -1,28 +1,6 @@
#!/bin/bash #!/bin/bash
# Usage: the first argument selects the build type: source `dirname $0`/../common/ci-check.sh
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
set -e set -e
set -x set -x
@@ -30,11 +8,6 @@ set -x
UNAME=`echo $(uname)` UNAME=`echo $(uname)`
LC_UNAME=`echo $UNAME | tr '[:upper:]' '[:lower:]'` LC_UNAME=`echo $UNAME | tr '[:upper:]' '[:lower:]'`
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh source `dirname $0`/../common/build-common.sh
if [[ "$GITHUB_WORKFLOW" ]]; then if [[ "$GITHUB_WORKFLOW" ]]; then
+15
View File
@@ -1,5 +1,20 @@
#!/bin/bash #!/bin/bash
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
# Typically a build script (build.sh) would source this script. For example,
# source `dirname $0`/../common/build-common.sh
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
if [[ ! "$TARGET" ]]; then if [[ ! "$TARGET" ]]; then
if [[ "$1" ]]; then if [[ "$1" ]]; then
TARGET=$1 TARGET=$1
+19
View File
@@ -0,0 +1,19 @@
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
fi
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
fi
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
if [ -d "./emsdk" ]; then
echo "emsdk folder found. Assume emsdk has been installed."
cd emsdk
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
exit 0
fi
# Install emscripten.
EMSDK_VERSION=${GITHUB_EMSDK_VERSION-3.1.60}
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
@@ -14,9 +14,9 @@
#!/usr/bin/bash #!/usr/bin/bash
set -x set -e
if [[ "$GITHUB_WORKFLOW" ]]; then if [[ "$GITHUB_WORKFLOW" ]]; then
set -e set -x
fi fi
OS_NAME=$(uname -s) OS_NAME=$(uname -s)
@@ -35,7 +35,7 @@ source ${ORIG_DIR}/venv/bin/activate
NEEDED_PYTHON_DEPS=("mako" "setuptools" "pyyaml") NEEDED_PYTHON_DEPS=("mako" "setuptools" "pyyaml")
for cmd in "${NEEDED_PYTHON_DEPS[@]}"; do for cmd in "${NEEDED_PYTHON_DEPS[@]}"; do
if ! python3 -m pip show "${cmd}" >/dev/null 2>&1; then if ! python3 -m pip show -q "${cmd}" >/dev/null 2>&1; then
python3 -m pip install ${cmd} python3 -m pip install ${cmd}
fi fi
done done
@@ -145,6 +145,6 @@ deactivate
popd popd
if [[ "$GITHUB_WORKFLOW" ]]; then if [[ "$GITHUB_WORKFLOW" ]]; then
set +e set +x
fi fi
set +x set +e
+2 -1
View File
@@ -3,4 +3,5 @@ GITHUB_CMAKE_VERSION=3.19.5
GITHUB_NINJA_VERSION=1.10.2 GITHUB_NINJA_VERSION=1.10.2
GITHUB_MESA_VERSION=24.2.1 GITHUB_MESA_VERSION=24.2.1
GITHUB_LLVM_VERSION=16 GITHUB_LLVM_VERSION=16
GITHUB_NDK_VERSION=27.0.11718014 GITHUB_NDK_VERSION=27.0.11718014
GITHUB_EMSDK_VERSION=3.1.60
+1 -25
View File
@@ -1,35 +1,11 @@
#!/bin/bash #!/bin/bash
# Usage: the first argument selects the build type: source `dirname $0`/../common/ci-check.sh
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
set -e set -e
set -x set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null pushd `dirname $0`/../.. > /dev/null
# If we're generating an archive for release or continuous builds, then we'll also build for the # If we're generating an archive for release or continuous builds, then we'll also build for the
+2 -29
View File
@@ -1,38 +1,11 @@
#!/bin/bash #!/bin/bash
# Usage: the first argument selects the build type: source `dirname $0`/../common/ci-check.sh
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
set -e set -e
set -x set -x
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null pushd `dirname $0`/../.. > /dev/null
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE ./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
+2 -25
View File
@@ -1,34 +1,11 @@
#!/bin/bash #!/bin/bash
# Usage: the first argument selects the build type: source `dirname $0`/../common/ci-check.sh
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
set -e set -e
set -x set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/../common/build-common.sh source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null pushd `dirname $0`/../.. > /dev/null
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE ./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
+1 -25
View File
@@ -1,34 +1,10 @@
#!/bin/bash #!/bin/bash
# Usage: the first argument selects the build type: source `dirname $0`/../common/ci-check.sh
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
set -e set -e
set -x set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null pushd `dirname $0`/../.. > /dev/null
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash
# Install emscripten.
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.60.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
+6 -2
View File
@@ -1023,8 +1023,12 @@ samplerCubemap | Cubemap texture
[Table [materialParamsTypes]: Material parameter types] [Table [materialParamsTypes]: Material parameter types]
Samplers Samplers
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to : Sampler types can specify additional options:
`float`).
- `format`: either `int` or `float` (defaults to `float`).
- `stages`: array of strings containing the list of shader stages this
sampler can be accessed from. Each entry must be either `vertex` or
`fragment` (defaults to both).
Arrays Arrays
: A parameter can define an array of values by appending `[size]` after the type name, where : A parameter can define an array of values by appending `[size]` after the type name, where
+2
View File
@@ -18,6 +18,8 @@
- [Metal](./notes/metal_debugging.md) - [Metal](./notes/metal_debugging.md)
- [Vulkan](./notes/vulkan_debugging.md) - [Vulkan](./notes/vulkan_debugging.md)
- [SPIR-V](./notes/spirv_debugging.md) - [SPIR-V](./notes/spirv_debugging.md)
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
- [Using Instruments on macOS](./notes/instruments.md)
- [Libraries](./notes/libs.md) - [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md) - [bluegl](./dup/bluegl.md)
- [bluevk](./dup/bluevk.md) - [bluevk](./dup/bluevk.md)
+41
View File
@@ -0,0 +1,41 @@
# Running with ASAN/UBSAN
## Enabling
When building though build.sh, pass the `-b` flag. This sets the cmake variable
`FILAMENT_ENABLE_ASAN_UBSAN=ON` which eventually passes `"-fsanitize=address -fsanitize=undefined"`
to all compile and link operations.
If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
`-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to cmake in order to get the same result.
## Getting memory leak detection on Mac
Memory leak detection isn't enabled by default on MacOS. There are two issues to address, first is
using a version of clang that supports memory leak detection and second is enabling it at runtime.
The version of clang distributed by Apple (with a version like "Apple clang version 16.0.0") doesn't
currently support leak detection at all. Instead you will need to get or build a different LLVM,
such as the one distributed through homebrew and get CMake to use that instead.
Then during runtime you'll need to have the environment variable `ASAN_OPTIONS` include the option
`detect_leaks=1`. Multiple `ASAN_OPTIONS` values are concatenated with `:`.
## Getting memory leak output in CLion
### Setting variables
Under `Settings | Build, Execution, Deployment | Dynamic Analysis Tools | Sanitizers` there is an
ASAN Settings field that overrides whatever other `ASAN_OPTIONS` you might set elsewhere, so you
must use that instead of setting it through your Run/Debug Configuration.
To pass `-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to CMake you'll want to create a new CMake Profile and
pass it as a CMake argument.
### Avoiding losing output
CMake will consume ASAN output and display it through a separate "Sanitizers" tab. Unfortunately
certain leak detection errors that interrupt the executable seem to not show up in this tab, but are
still removed from the user-visible console output. If this is happening and you need to see the
unfiltered console output you'll need to go to `Settings | Build, Execution, Deployment | Dynamic
Analysis Tools | Sanitizers` and uncheck "Use visual representation for Sanitizer's output".
+36
View File
@@ -0,0 +1,36 @@
# Using Instruments on macOS
When running a binary under Instruments on macOS, you may run into the following issue when
launching or attaching to an executable:
```
Failed to gain authorization
Recovery Suggestion: Target binary needs to be debuggable and signed with 'get-task-allow'
```
This is a security precaution; the solution is to code sign the binary with the
`com.apple.security.get-task-allow` entitlement.
1. Create an `entitlements.plist` file with the following contents:
```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
```
2. Run the following command:
```
codesign -s - --entitlements entitlements.plist <binary>
```
Replace `<binary>` with the name of the binary, for example: `out/cmake-debug/samples/gltf_viewer`.
Afterwards, you should be able to successfully launch and attach to the executable using
Instruments.
+24 -2
View File
@@ -262,6 +262,8 @@ set(MATERIAL_SRCS
src/materials/ssao/mipmapDepth.mat src/materials/ssao/mipmapDepth.mat
src/materials/ssao/sao.mat src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat src/materials/ssao/saoBentNormals.mat
src/materials/ssao/gtao.mat
src/materials/ssao/gtaoBentNormals.mat
src/materials/vsmMipmap.mat src/materials/vsmMipmap.mat
) )
@@ -318,8 +320,6 @@ endif()
# Definitions # Definitions
# ================================================================================================== # ==================================================================================================
# "2" corresponds to SYSTRACE_TAG_FILEMENT (See: utils/Systrace.h)
add_definitions(-DSYSTRACE_TAG=2)
add_definitions(-DFILAMENT_DFG_LUT_SIZE=${DFG_LUT_SIZE}) add_definitions(-DFILAMENT_DFG_LUT_SIZE=${DFG_LUT_SIZE})
add_definitions( add_definitions(
-DFILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB=${FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB} -DFILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB=${FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB}
@@ -463,6 +463,28 @@ add_custom_command(
APPEND APPEND
) )
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
add_custom_command( add_custom_command(
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat" OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs DEPENDS src/materials/ssao/ssaoUtils.fs
-26
View File
@@ -5,18 +5,6 @@ set(TARGET backend)
set(PUBLIC_HDR_DIR include) set(PUBLIC_HDR_DIR include)
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR}) set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
# ==================================================================================================
# Compilation options
# ==================================================================================================
#
set(BACKEND_SANITIZATION "" CACHE STRING "Sanitization option")
set_property(CACHE BACKEND_SANITIZATION PROPERTY STRINGS ";ASAN")
set(BACKEND_SANITIZERS)
if (BACKEND_SANITIZATION STREQUAL "ASAN")
set(BACKEND_SANITIZERS -fsanitize=address)
endif()
# ================================================================================================== # ==================================================================================================
# Sources and headers # Sources and headers
# ================================================================================================== # ==================================================================================================
@@ -302,12 +290,6 @@ if (ANDROID)
list(APPEND SRCS src/BackendUtilsAndroid.cpp) list(APPEND SRCS src/BackendUtilsAndroid.cpp)
endif() endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
# "2" corresponds to SYSTRACE_TAG_FILEMENT (See: utils/Systrace.h)
add_definitions(-DSYSTRACE_TAG=2 )
# ================================================================================================== # ==================================================================================================
# Includes & target definition # Includes & target definition
# ================================================================================================== # ==================================================================================================
@@ -484,7 +466,6 @@ target_compile_options(${TARGET} PRIVATE
${OSMESA_COMPILE_FLAGS} ${OSMESA_COMPILE_FLAGS}
$<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}> $<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}>
$<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}> $<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}>
${BACKEND_SANITIZERS}
) )
if (FILAMENT_SUPPORTS_METAL) if (FILAMENT_SUPPORTS_METAL)
@@ -495,8 +476,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>) target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
endif() endif()
target_link_options(${TARGET} PRIVATE ${BACKEND_SANITIZERS})
target_link_libraries(${TARGET} PRIVATE target_link_libraries(${TARGET} PRIVATE
${OSMESA_LINKER_FLAGS} ${OSMESA_LINKER_FLAGS}
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}> $<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
@@ -566,8 +545,6 @@ if (APPLE AND NOT IOS)
test/test_RenderExternalImage.cpp) test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC}) add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS}) target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
target_compile_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
target_link_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
set(BACKEND_TEST_DEPS set(BACKEND_TEST_DEPS
OSDependent OSDependent
@@ -606,7 +583,6 @@ if (APPLE AND NOT IOS)
# linker from removing "unused" symbols. # linker from removing "unused" symbols.
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test) target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests) set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
target_link_options(backend_test_mac PRIVATE ${BACKEND_SANITIZERS})
# This is needed after XCode 15.3 # This is needed after XCode 15.3
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
@@ -616,8 +592,6 @@ endif()
if (LINUX) if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC}) add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_compile_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
target_link_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS}) target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests) set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
endif() endif()
@@ -20,10 +20,15 @@
#define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H #define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/ostream.h>
#include <utility>
#include <stddef.h> #include <stddef.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
class CallbackHandler; class CallbackHandler;
@@ -89,8 +94,8 @@ public:
* @param callback A callback used to release the CPU buffer from this BufferDescriptor * @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called * @param user An opaque user pointer passed to the callback function when it's called
*/ */
BufferDescriptor(void const* buffer, size_t size, BufferDescriptor(void const* buffer, size_t const size,
Callback callback = nullptr, void* user = nullptr) noexcept Callback const callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) { : buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) {
} }
@@ -98,11 +103,12 @@ public:
* Creates a BufferDescriptor that references a CPU memory-buffer * Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference * @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes * @param size Size of the CPU buffer in bytes
* @param handler A custom handler for the callback
* @param callback A callback used to release the CPU buffer from this BufferDescriptor * @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called * @param user An opaque user pointer passed to the callback function when it's called
*/ */
BufferDescriptor(void const* buffer, size_t size, BufferDescriptor(void const* buffer, size_t const size,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), : buffer(const_cast<void*>(buffer)), size(size),
mCallback(callback), mUser(user), mHandler(handler) { mCallback(callback), mUser(user), mHandler(handler) {
} }
@@ -116,8 +122,9 @@ public:
* *
* @param buffer Memory address of the CPU buffer to reference * @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes * @param size Size of the CPU buffer in bytes
* @param data A pointer to the data
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler * @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return a new BufferDescriptor * @return A new BufferDescriptor
*/ */
template<typename T, void(T::*method)(void const*, size_t)> template<typename T, void(T::*method)(void const*, size_t)>
static BufferDescriptor make(void const* buffer, size_t size, T* data, static BufferDescriptor make(void const* buffer, size_t size, T* data,
@@ -164,7 +171,7 @@ public:
* @param callback The new callback function * @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called * @param user An opaque user pointer passed to the callbeck function when it's called
*/ */
void setCallback(Callback callback, void* user = nullptr) noexcept { void setCallback(Callback const callback, void* user = nullptr) noexcept {
this->mCallback = callback; this->mCallback = callback;
this->mUser = user; this->mUser = user;
this->mHandler = nullptr; this->mHandler = nullptr;
@@ -176,7 +183,7 @@ public:
* @param callback The new callback function * @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called * @param user An opaque user pointer passed to the callbeck function when it's called
*/ */
void setCallback(CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept { void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept {
mCallback = callback; mCallback = callback;
mUser = user; mUser = user;
mHandler = handler; mHandler = handler;
@@ -19,8 +19,6 @@
#include <backend/DriverApiForward.h> #include <backend/DriverApiForward.h>
#include <utils/ostream.h>
#include <initializer_list> #include <initializer_list>
#include <memory> #include <memory>
@@ -28,6 +26,10 @@
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept; void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept;
+324 -37
View File
@@ -25,21 +25,26 @@
#include <backend/PresentCallable.h> #include <backend/PresentCallable.h>
#include <utils/BitmaskEnum.h> #include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h> #include <utils/Invocable.h>
#include <utils/compiler.h> #include <utils/StaticString.h>
#include <utils/debug.h> #include <utils/debug.h>
#include <utils/ostream.h>
#include <math/vec4.h> #include <math/vec4.h>
#include <array> #include <array>
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <string_view>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
/** /**
* Types and enums used by filament's driver. * Types and enums used by filament's driver.
* *
@@ -158,7 +163,7 @@ enum class TimerQueryResult : int8_t {
AVAILABLE = 1, // result is available AVAILABLE = 1, // result is available
}; };
static constexpr const char* backendToString(Backend backend) { constexpr std::string_view to_string(Backend const backend) noexcept {
switch (backend) { switch (backend) {
case Backend::NOOP: case Backend::NOOP:
return "Noop"; return "Noop";
@@ -170,9 +175,10 @@ static constexpr const char* backendToString(Backend backend) {
return "Metal"; return "Metal";
case Backend::WEBGPU: case Backend::WEBGPU:
return "WebGPU"; return "WebGPU";
default: case Backend::DEFAULT:
return "Unknown"; return "Default";
} }
return "Unknown";
} }
/** /**
@@ -189,7 +195,7 @@ enum class ShaderLanguage {
WGSL = 5, WGSL = 5,
}; };
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) { constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noexcept {
switch (shaderLanguage) { switch (shaderLanguage) {
case ShaderLanguage::ESSL1: case ShaderLanguage::ESSL1:
return "ESSL 1.0"; return "ESSL 1.0";
@@ -204,6 +210,7 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
case ShaderLanguage::WGSL: case ShaderLanguage::WGSL:
return "WGSL"; return "WGSL";
} }
return "UNKNOWN";
} }
enum class ShaderStage : uint8_t { enum class ShaderStage : uint8_t {
@@ -221,7 +228,7 @@ enum class ShaderStageFlags : uint8_t {
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE
}; };
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
switch (type) { switch (type) {
case ShaderStage::VERTEX: case ShaderStage::VERTEX:
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX));
@@ -232,14 +239,239 @@ static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage t
} }
} }
enum class DescriptorType : uint8_t { enum class TextureType : uint8_t {
UNIFORM_BUFFER, FLOAT,
SHADER_STORAGE_BUFFER, INT,
SAMPLER, UINT,
INPUT_ATTACHMENT, DEPTH,
SAMPLER_EXTERNAL STENCIL,
DEPTH_STENCIL
}; };
constexpr std::string_view to_string(TextureType type) noexcept {
switch (type) {
case TextureType::FLOAT: return "FLOAT";
case TextureType::INT: return "INT";
case TextureType::UINT: return "UINT";
case TextureType::DEPTH: return "DEPTH";
case TextureType::STENCIL: return "STENCIL";
case TextureType::DEPTH_STENCIL: return "DEPTH_STENCIL";
}
return "UNKNOWN";
}
enum class DescriptorType : uint8_t {
SAMPLER_2D_FLOAT,
SAMPLER_2D_INT,
SAMPLER_2D_UINT,
SAMPLER_2D_DEPTH,
SAMPLER_2D_ARRAY_FLOAT,
SAMPLER_2D_ARRAY_INT,
SAMPLER_2D_ARRAY_UINT,
SAMPLER_2D_ARRAY_DEPTH,
SAMPLER_CUBE_FLOAT,
SAMPLER_CUBE_INT,
SAMPLER_CUBE_UINT,
SAMPLER_CUBE_DEPTH,
SAMPLER_CUBE_ARRAY_FLOAT,
SAMPLER_CUBE_ARRAY_INT,
SAMPLER_CUBE_ARRAY_UINT,
SAMPLER_CUBE_ARRAY_DEPTH,
SAMPLER_3D_FLOAT,
SAMPLER_3D_INT,
SAMPLER_3D_UINT,
SAMPLER_2D_MS_FLOAT,
SAMPLER_2D_MS_INT,
SAMPLER_2D_MS_UINT,
SAMPLER_2D_MS_ARRAY_FLOAT,
SAMPLER_2D_MS_ARRAY_INT,
SAMPLER_2D_MS_ARRAY_UINT,
SAMPLER_EXTERNAL,
UNIFORM_BUFFER,
SHADER_STORAGE_BUFFER,
INPUT_ATTACHMENT,
};
constexpr bool isDepthDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
return true;
default: ;
}
return false;
}
constexpr bool isFloatDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
return true;
default: ;
}
return false;
}
constexpr bool isIntDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
return true;
default: ;
}
return false;
}
constexpr bool isUnsignedIntDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
return true;
default: ;
}
return false;
}
constexpr bool is3dTypeDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
return true;
default: ;
}
return false;
}
constexpr bool is2dTypeDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
return true;
default: ;
}
return false;
}
constexpr bool is2dArrayTypeDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
return true;
default: ;
}
return false;
}
constexpr bool isCubeTypeDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
return true;
default: ;
}
return false;
}
constexpr bool isCubeArrayTypeDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
return true;
default: ;
}
return false;
}
constexpr bool isMultiSampledTypeDescriptor(DescriptorType const type) noexcept {
switch (type) {
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
return true;
default: ;
}
return false;
}
constexpr std::string_view to_string(DescriptorType type) noexcept {
#define DESCRIPTOR_TYPE_CASE(TYPE) case DescriptorType::TYPE: return #TYPE;
switch (type) {
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_DEPTH)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_DEPTH)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_DEPTH)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_DEPTH)
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_FLOAT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_INT)
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_UINT)
DESCRIPTOR_TYPE_CASE(SAMPLER_EXTERNAL)
DESCRIPTOR_TYPE_CASE(UNIFORM_BUFFER)
DESCRIPTOR_TYPE_CASE(SHADER_STORAGE_BUFFER)
DESCRIPTOR_TYPE_CASE(INPUT_ATTACHMENT)
}
return "UNKNOWN";
#undef DESCRIPTOR_TYPE_CASE
}
enum class DescriptorFlags : uint8_t { enum class DescriptorFlags : uint8_t {
NONE = 0x00, NONE = 0x00,
DYNAMIC_OFFSET = 0x01 DYNAMIC_OFFSET = 0x01
@@ -250,6 +482,13 @@ using descriptor_set_t = uint8_t;
using descriptor_binding_t = uint8_t; using descriptor_binding_t = uint8_t;
struct DescriptorSetLayoutBinding { struct DescriptorSetLayoutBinding {
static bool isSampler(DescriptorType type) noexcept {
return int(type) <= int(DescriptorType::SAMPLER_EXTERNAL);
}
static bool isBuffer(DescriptorType type) noexcept {
return type == DescriptorType::UNIFORM_BUFFER ||
type == DescriptorType::SHADER_STORAGE_BUFFER;
}
DescriptorType type; DescriptorType type;
ShaderStageFlags stageFlags; ShaderStageFlags stageFlags;
descriptor_binding_t binding; descriptor_binding_t binding;
@@ -260,7 +499,7 @@ struct DescriptorSetLayoutBinding {
// no uninitialized padding bytes. // no uninitialized padding bytes.
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED; // uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
friend inline bool operator==(DescriptorSetLayoutBinding const& lhs, friend bool operator==(DescriptorSetLayoutBinding const& lhs,
DescriptorSetLayoutBinding const& rhs) noexcept { DescriptorSetLayoutBinding const& rhs) noexcept {
return lhs.type == rhs.type && return lhs.type == rhs.type &&
lhs.flags == rhs.flags && lhs.flags == rhs.flags &&
@@ -293,7 +532,7 @@ enum class TargetBufferFlags : uint32_t {
ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected. ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected.
}; };
inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept { constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
if (index == 0u) return TargetBufferFlags::COLOR0; if (index == 0u) return TargetBufferFlags::COLOR0;
if (index == 1u) return TargetBufferFlags::COLOR1; if (index == 1u) return TargetBufferFlags::COLOR1;
if (index == 2u) return TargetBufferFlags::COLOR2; if (index == 2u) return TargetBufferFlags::COLOR2;
@@ -450,6 +689,24 @@ enum class SamplerType : uint8_t {
SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2) SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2)
}; };
constexpr std::string_view to_string(SamplerType const type) noexcept {
switch (type) {
case SamplerType::SAMPLER_2D:
return "SAMPLER_2D";
case SamplerType::SAMPLER_2D_ARRAY:
return "SAMPLER_2D_ARRAY";
case SamplerType::SAMPLER_CUBEMAP:
return "SAMPLER_CUBEMAP";
case SamplerType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
case SamplerType::SAMPLER_3D:
return "SAMPLER_3D";
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return "SAMPLER_CUBEMAP_ARRAY";
}
return "Unknown";
}
//! Subpass type //! Subpass type
enum class SubpassType : uint8_t { enum class SubpassType : uint8_t {
SUBPASS_INPUT SUBPASS_INPUT
@@ -463,6 +720,20 @@ enum class SamplerFormat : uint8_t {
SHADOW = 3 //!< shadow sampler (PCF) SHADOW = 3 //!< shadow sampler (PCF)
}; };
constexpr std::string_view to_string(SamplerFormat const format) noexcept {
switch (format) {
case SamplerFormat::INT:
return "INT";
case SamplerFormat::UINT:
return "UINT";
case SamplerFormat::FLOAT:
return "FLOAT";
case SamplerFormat::SHADOW:
return "SHADOW";
}
return "Unknown";
}
/** /**
* Supported element types * Supported element types
*/ */
@@ -502,6 +773,15 @@ enum class BufferObjectBinding : uint8_t {
SHADER_STORAGE SHADER_STORAGE
}; };
constexpr std::string_view to_string(BufferObjectBinding type) noexcept {
switch (type) {
case BufferObjectBinding::VERTEX: return "VERTEX";
case BufferObjectBinding::UNIFORM: return "UNIFORM";
case BufferObjectBinding::SHADER_STORAGE: return "SHADER_STORAGE";
}
return "UNKNOWN";
}
//! Face culling Mode //! Face culling Mode
enum class CullingMode : uint8_t { enum class CullingMode : uint8_t {
NONE, //!< No culling, front and back faces are visible NONE, //!< No culling, front and back faces are visible
@@ -763,6 +1043,8 @@ enum class TextureFormat : uint16_t {
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
}; };
TextureType getTextureType(TextureFormat format) noexcept;
//! Bitmask describing the intended Texture Usage //! Bitmask describing the intended Texture Usage
enum class TextureUsage : uint16_t { enum class TextureUsage : uint16_t {
NONE = 0x0000, NONE = 0x0000,
@@ -790,7 +1072,7 @@ enum class TextureSwizzle : uint8_t {
}; };
//! returns whether this format a depth format //! returns whether this format a depth format
static constexpr bool isDepthFormat(TextureFormat format) noexcept { constexpr bool isDepthFormat(TextureFormat format) noexcept {
switch (format) { switch (format) {
case TextureFormat::DEPTH32F: case TextureFormat::DEPTH32F:
case TextureFormat::DEPTH24: case TextureFormat::DEPTH24:
@@ -803,7 +1085,7 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept {
} }
} }
static constexpr bool isStencilFormat(TextureFormat format) noexcept { constexpr bool isStencilFormat(TextureFormat format) noexcept {
switch (format) { switch (format) {
case TextureFormat::STENCIL8: case TextureFormat::STENCIL8:
case TextureFormat::DEPTH24_STENCIL8: case TextureFormat::DEPTH24_STENCIL8:
@@ -814,7 +1096,7 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept {
} }
} }
inline constexpr bool isColorFormat(TextureFormat format) noexcept { constexpr bool isColorFormat(TextureFormat format) noexcept {
switch (format) { switch (format) {
// Standard color formats // Standard color formats
case TextureFormat::R8: case TextureFormat::R8:
@@ -841,7 +1123,7 @@ inline constexpr bool isColorFormat(TextureFormat format) noexcept {
return false; return false;
} }
static constexpr bool isUnsignedIntFormat(TextureFormat format) { constexpr bool isUnsignedIntFormat(TextureFormat format) {
switch (format) { switch (format) {
case TextureFormat::R8UI: case TextureFormat::R8UI:
case TextureFormat::R16UI: case TextureFormat::R16UI:
@@ -862,7 +1144,7 @@ static constexpr bool isUnsignedIntFormat(TextureFormat format) {
} }
} }
static constexpr bool isSignedIntFormat(TextureFormat format) { constexpr bool isSignedIntFormat(TextureFormat format) {
switch (format) { switch (format) {
case TextureFormat::R8I: case TextureFormat::R8I:
case TextureFormat::R16I: case TextureFormat::R16I:
@@ -884,35 +1166,35 @@ static constexpr bool isSignedIntFormat(TextureFormat format) {
} }
//! returns whether this format is a compressed format //! returns whether this format is a compressed format
static constexpr bool isCompressedFormat(TextureFormat format) noexcept { constexpr bool isCompressedFormat(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11; return format >= TextureFormat::EAC_R11;
} }
//! returns whether this format is an ETC2 compressed format //! returns whether this format is an ETC2 compressed format
static constexpr bool isETC2Compression(TextureFormat format) noexcept { constexpr bool isETC2Compression(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8; return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8;
} }
//! returns whether this format is an S3TC compressed format //! returns whether this format is an S3TC compressed format
static constexpr bool isS3TCCompression(TextureFormat format) noexcept { constexpr bool isS3TCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA; return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA;
} }
static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept { constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA; return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA;
} }
//! returns whether this format is an RGTC compressed format //! returns whether this format is an RGTC compressed format
static constexpr bool isRGTCCompression(TextureFormat format) noexcept { constexpr bool isRGTCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2; return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2;
} }
//! returns whether this format is an BPTC compressed format //! returns whether this format is an BPTC compressed format
static constexpr bool isBPTCCompression(TextureFormat format) noexcept { constexpr bool isBPTCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM; return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM;
} }
static constexpr bool isASTCCompression(TextureFormat format) noexcept { constexpr bool isASTCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12; return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12;
} }
@@ -1033,19 +1315,23 @@ struct SamplerParams { // NOLINT
assert_invariant(lhs.padding2 == 0); assert_invariant(lhs.padding2 == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs)); auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs)); auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs; return *pLhs < *pRhs;
} }
}; };
bool isFiltered() const noexcept {
return filterMag != SamplerMagFilter::NEAREST || filterMin != SamplerMinFilter::NEAREST;
}
private: private:
friend inline bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept { friend bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept {
return SamplerParams::EqualTo{}(lhs, rhs); return EqualTo{}(lhs, rhs);
} }
friend inline bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept { friend bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept {
return !SamplerParams::EqualTo{}(lhs, rhs); return !EqualTo{}(lhs, rhs);
} }
friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept { friend bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept {
return SamplerParams::LessThan{}(lhs, rhs); return LessThan{}(lhs, rhs);
} }
}; };
@@ -1095,15 +1381,15 @@ struct SamplerYcbcrConversion {// NOLINT
}; };
private: private:
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) friend bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept { noexcept {
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs); return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
} }
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) friend bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept { noexcept {
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs); return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
} }
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) friend bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept { noexcept {
return SamplerYcbcrConversion::LessThan{}(lhs, rhs); return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
} }
@@ -1139,6 +1425,7 @@ struct ExternalSamplerDatum {
static_assert(sizeof(ExternalSamplerDatum) == 12); static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout { struct DescriptorSetLayout {
std::variant<utils::StaticString, utils::CString, std::monostate> label;
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings; utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed // TODO: uncomment when needed
+4 -3
View File
@@ -17,9 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H #ifndef TNT_FILAMENT_BACKEND_HANDLE_H
#define TNT_FILAMENT_BACKEND_HANDLE_H #define TNT_FILAMENT_BACKEND_HANDLE_H
#if !defined(NDEBUG)
#include <utils/ostream.h>
#endif
#include <utils/debug.h> #include <utils/debug.h>
#include <type_traits> // FIXME: STL headers are not allowed in public headers #include <type_traits> // FIXME: STL headers are not allowed in public headers
@@ -27,6 +24,10 @@
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
struct HwBufferObject; struct HwBufferObject;
@@ -20,12 +20,14 @@
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/Handle.h> #include <backend/Handle.h>
#include <utils/ostream.h>
#include <array> #include <array>
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
//! \privatesection //! \privatesection
@@ -24,11 +24,14 @@
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/debug.h> #include <utils/debug.h>
#include <utils/ostream.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
/** /**
@@ -201,23 +204,15 @@ public:
}, new T(std::forward<T>(functor)) }, new T(std::forward<T>(functor))
}; };
} }
// --------------------------------------------------------------------------------------------
/** /**
* Computes the size in bytes needed to fit an image of given dimensions and format * Computes the size in bytes for a pixel of given dimensions and format
* *
* @param format Format of the image pixels * @param format Format of the image pixels
* @param type Type of the image pixels * @param type Type of the image pixels
* @param stride Stride of a row in pixels * @return The size of the specified pixel in bytes
* @param height Height of the image in rows
* @param alignment Alignment in bytes of pixel rows
* @return The buffer size needed to fit this image in bytes
*/ */
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
size_t stride, size_t height, size_t alignment) noexcept {
assert_invariant(alignment);
static constexpr size_t computePixelSize(PixelDataFormat format, PixelDataType type) noexcept {
if (type == PixelDataType::COMPRESSED) { if (type == PixelDataType::COMPRESSED) {
return 0; return 0;
} }
@@ -239,7 +234,7 @@ public:
case PixelDataFormat::RGB_INTEGER: case PixelDataFormat::RGB_INTEGER:
n = 3; n = 3;
break; break;
case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm) case PixelDataFormat::UNUSED:// shouldn't happen (used to be rgbm)
case PixelDataFormat::RGBA: case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER: case PixelDataFormat::RGBA_INTEGER:
n = 4; n = 4;
@@ -248,7 +243,7 @@ public:
size_t bpp = n; size_t bpp = n;
switch (type) { switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings case PixelDataType::COMPRESSED:// Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE: case PixelDataType::UBYTE:
case PixelDataType::BYTE: case PixelDataType::BYTE:
// nothing to do // nothing to do
@@ -279,16 +274,35 @@ public:
bpp = 2; bpp = 2;
break; break;
} }
return bpp;
}
// --------------------------------------------------------------------------------------------
/**
* Computes the size in bytes needed to fit an image of given dimensions and format
*
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param stride Stride of a row in pixels
* @param height Height of the image in rows
* @param alignment Alignment in bytes of pixel rows
* @return The buffer size needed to fit this image in bytes
*/
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
size_t stride, size_t height, size_t alignment) noexcept {
assert_invariant(alignment);
size_t bpp = computePixelSize(format, type);
size_t const bpr = bpp * stride; size_t const bpr = bpp * stride;
size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1); size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
return bprAligned * height; return bprAligned * height;
} }
//! left coordinate in pixels //! left coordinate in pixels
uint32_t left = 0; uint32_t left = 0;
//! top coordinate in pixels //! top coordinate in pixels
uint32_t top = 0; uint32_t top = 0;
union { union {
struct { struct {
//! stride in pixels //! stride in pixels
+6 -4
View File
@@ -20,12 +20,10 @@
#include <utils/CString.h> #include <utils/CString.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h> #include <utils/Invocable.h>
#include <utils/ostream.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <array> #include <array>
#include <unordered_map>
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <variant> #include <variant>
@@ -33,6 +31,10 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
class Program { class Program {
@@ -44,8 +46,8 @@ public:
struct Descriptor { struct Descriptor {
utils::CString name; utils::CString name;
backend::DescriptorType type; DescriptorType type;
backend::descriptor_binding_t binding; descriptor_binding_t binding;
}; };
struct SpecializationConstant { struct SpecializationConstant {
@@ -19,22 +19,26 @@
#include <backend/Handle.h> #include <backend/Handle.h>
#include <utils/ostream.h> #include <utility>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend { namespace filament::backend {
//! \privatesection //! \privatesection
struct TargetBufferInfo { struct TargetBufferInfo {
// note: the parameters of this constructor are not in the order of this structure's fields // note: the parameters of this constructor are not in the order of this structure's fields
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level, uint16_t const layer) noexcept
: handle(handle), level(level), layer(layer) { : handle(std::move(handle)), level(level), layer(layer) {
} }
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level) noexcept TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level) noexcept
: handle(handle), level(level) { : handle(handle), level(level) {
} }
@@ -70,11 +74,11 @@ private:
TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT]; TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT];
public: public:
TargetBufferInfo const& operator[](size_t i) const noexcept { TargetBufferInfo const& operator[](size_t const i) const noexcept {
return mInfos[i]; return mInfos[i];
} }
TargetBufferInfo& operator[](size_t i) noexcept { TargetBufferInfo& operator[](size_t const i) noexcept {
return mInfos[i]; return mInfos[i];
} }
@@ -39,18 +39,40 @@ public:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override; Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override { return 0; } int getOSVersion() const noexcept override { return 0; }
/**
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
*
* This method attempts to acquire a Metal device and command queue, returning true if both are
* successfully obtained, or false otherwise. Typically, these objects are acquired when
* the Metal backend is initialized. This method allows clients to check for their availability
* earlier.
*
* Calling initialize() is optional and safe to do so multiple times. After initialize() returns
* true, subsequent calls will continue to return true but have no effect.
*
* initialize() must be called from the main thread.
*
* @returns true if the device and command queue have been successfully obtained; false
* otherwise.
*/
bool initialize() noexcept;
/** /**
* Obtain the preferred Metal device object for the backend to use. * Obtain the preferred Metal device object for the backend to use.
* *
* On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is * On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is
* free to decide which one to use. On mobile systems with a single GPU, implementations should * free to decide which one to use. On mobile systems with a single GPU, implementations should
* simply return the result of MTLCreateSystemDefaultDevice(); * simply return the result of MTLCreateSystemDefaultDevice();
*
* createDevice is called by the Metal backend from the backend thread.
*/ */
virtual void createDevice(MetalDevice& outDevice) noexcept; virtual void createDevice(MetalDevice& outDevice) noexcept;
/** /**
* Create a command submission queue on the Metal device object. * Create a command submission queue on the Metal device object.
* *
* createCommandQueue is called by the Metal backend from the backend thread.
*
* @param device The device which was returned from createDevice() * @param device The device which was returned from createDevice()
*/ */
virtual void createCommandQueue( virtual void createCommandQueue(
@@ -60,6 +82,8 @@ public:
* Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is * Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is
* guaranteed to execute before all subsequent command buffers created either by Filament, or * guaranteed to execute before all subsequent command buffers created either by Filament, or
* further calls to this method. * further calls to this method.
*
* createAndEnqueueCommandBuffer must be called from the main thread.
*/ */
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept; void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
@@ -68,6 +92,8 @@ public:
* *
* Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the * Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the
* completion of each frame. These are limited and provided round-robin style by the system. * completion of each frame. These are limited and provided round-robin style by the system.
*
* setDrawableFailureBehavior must be called from the main thread.
*/ */
enum class DrawableFailureBehavior : uint8_t { enum class DrawableFailureBehavior : uint8_t {
/** /**
@@ -27,8 +27,11 @@
#include <utils/Hash.h> #include <utils/Hash.h>
#include <utils/PrivateImplementation.h> #include <utils/PrivateImplementation.h>
#include <cstddef>
#include <functional>
#include <tuple> #include <tuple>
#include <unordered_set> #include <unordered_set>
#include <string>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
+139
View File
@@ -18,6 +18,8 @@
#include "DataReshaper.h" #include "DataReshaper.h"
#include <backend/DriverEnums.h>
#include <utils/CString.h> #include <utils/CString.h>
#include <string_view> #include <string_view>
@@ -223,6 +225,143 @@ size_t getFormatSize(TextureFormat format) noexcept {
} }
} }
TextureType getTextureType(TextureFormat const format) noexcept {
switch (format) {
case TextureFormat::UNUSED:
// should not happen
return TextureType::FLOAT;
case TextureFormat::R8:
case TextureFormat::R16F:
case TextureFormat::RG8:
case TextureFormat::RGB565:
case TextureFormat::RGB5_A1:
case TextureFormat::RGBA4:
case TextureFormat::R32F:
case TextureFormat::RGB8:
case TextureFormat::SRGB8:
case TextureFormat::RG16F:
case TextureFormat::R11F_G11F_B10F:
case TextureFormat::RGB9_E5:
case TextureFormat::RGBA8:
case TextureFormat::SRGB8_A8:
case TextureFormat::RGB10_A2:
case TextureFormat::RGB16F:
case TextureFormat::RG32F:
case TextureFormat::RGBA16F:
case TextureFormat::RGB32F:
case TextureFormat::RGBA32F:
return TextureType::FLOAT;
case TextureFormat::R8_SNORM:
case TextureFormat::RG8_SNORM:
case TextureFormat::RGB8_SNORM:
case TextureFormat::RGBA8_SNORM:
// SNORM are treated as float
return TextureType::FLOAT;
case TextureFormat::R8UI:
case TextureFormat::R16UI:
case TextureFormat::RG8UI:
case TextureFormat::RGB8UI:
case TextureFormat::R32UI:
case TextureFormat::RG16UI:
case TextureFormat::RGBA8UI:
case TextureFormat::RGB16UI:
case TextureFormat::RG32UI:
case TextureFormat::RGBA16UI:
case TextureFormat::RGB32UI:
case TextureFormat::RGBA32UI:
return TextureType::UINT;
case TextureFormat::R8I:
case TextureFormat::R16I:
case TextureFormat::RG8I:
case TextureFormat::RGB8I:
case TextureFormat::R32I:
case TextureFormat::RG16I:
case TextureFormat::RGBA8I:
case TextureFormat::RGB16I:
case TextureFormat::RG32I:
case TextureFormat::RGBA16I:
case TextureFormat::RGB32I:
case TextureFormat::RGBA32I:
return TextureType::INT;
case TextureFormat::DEPTH16:
case TextureFormat::DEPTH24:
case TextureFormat::DEPTH32F:
return TextureType::DEPTH;
case TextureFormat::STENCIL8:
return TextureType::STENCIL;
case TextureFormat::DEPTH24_STENCIL8:
case TextureFormat::DEPTH32F_STENCIL8:
return TextureType::DEPTH_STENCIL;
// Compressed formats ---------------------------------------------------------------------
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
case TextureFormat::ETC2_EAC_RGBA8:
case TextureFormat::ETC2_EAC_SRGBA8:
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::ETC2_RGB8_A1:
case TextureFormat::ETC2_SRGB8_A1:
case TextureFormat::DXT1_RGB:
case TextureFormat::DXT1_RGBA:
case TextureFormat::DXT1_SRGB:
case TextureFormat::DXT1_SRGBA:
case TextureFormat::DXT3_RGBA:
case TextureFormat::DXT3_SRGBA:
case TextureFormat::DXT5_RGBA:
case TextureFormat::DXT5_SRGBA:
case TextureFormat::RED_RGTC1:
case TextureFormat::SIGNED_RED_RGTC1:
case TextureFormat::RED_GREEN_RGTC2:
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
case TextureFormat::RGBA_BPTC_UNORM:
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:
case TextureFormat::RGBA_ASTC_5x5:
case TextureFormat::RGBA_ASTC_6x5:
case TextureFormat::RGBA_ASTC_6x6:
case TextureFormat::RGBA_ASTC_8x5:
case TextureFormat::RGBA_ASTC_8x6:
case TextureFormat::RGBA_ASTC_8x8:
case TextureFormat::RGBA_ASTC_10x5:
case TextureFormat::RGBA_ASTC_10x6:
case TextureFormat::RGBA_ASTC_10x8:
case TextureFormat::RGBA_ASTC_10x10:
case TextureFormat::RGBA_ASTC_12x10:
case TextureFormat::RGBA_ASTC_12x12:
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return TextureType::FLOAT;
}
return TextureType::FLOAT;
}
size_t getFormatComponentCount(TextureFormat format) noexcept { size_t getFormatComponentCount(TextureFormat format) noexcept {
switch (format) { switch (format) {
case TextureFormat::R8: case TextureFormat::R8:
+4 -3
View File
@@ -18,12 +18,13 @@
#include "private/backend/CircularBuffer.h" #include "private/backend/CircularBuffer.h"
#include "private/backend/CommandStream.h" #include "private/backend/CommandStream.h"
#include <private/utils/Tracing.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Mutex.h> #include <utils/Mutex.h>
#include <utils/ostream.h> #include <utils/ostream.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/debug.h> #include <utils/debug.h>
#include <algorithm> #include <algorithm>
@@ -79,7 +80,7 @@ bool CommandBufferQueue::isExitRequested() const {
void CommandBufferQueue::flush() noexcept { void CommandBufferQueue::flush() noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
CircularBuffer& circularBuffer = mCircularBuffer; CircularBuffer& circularBuffer = mCircularBuffer;
if (circularBuffer.empty()) { if (circularBuffer.empty()) {
@@ -129,7 +130,7 @@ void CommandBufferQueue::flush() noexcept {
mHighWatermark = std::max(mHighWatermark, totalUsed); mHighWatermark = std::max(mHighWatermark, totalUsed);
#endif #endif
SYSTRACE_NAME("waiting: CircularBuffer::flush()"); FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "waiting: CircularBuffer::flush()");
FILAMENT_CHECK_POSTCONDITION(!mPaused) << FILAMENT_CHECK_POSTCONDITION(!mPaused) <<
"CommandStream is full, but since the rendering thread is paused, " "CommandStream is full, but since the rendering thread is paused, "
+12 -10
View File
@@ -16,6 +16,8 @@
#include "private/backend/CommandStream.h" #include "private/backend/CommandStream.h"
#include <private/utils/Tracing.h>
#if DEBUG_COMMAND_STREAM #if DEBUG_COMMAND_STREAM
#include <utils/CallStack.h> #include <utils/CallStack.h>
#endif #endif
@@ -24,7 +26,6 @@
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/ostream.h> #include <utils/ostream.h>
#include <utils/Profiler.h> #include <utils/Profiler.h>
#include <utils/Systrace.h>
#include <cstddef> #include <cstddef>
#include <functional> #include <functional>
@@ -79,12 +80,13 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
} }
void CommandStream::execute(void* buffer) { void CommandStream::execute(void* buffer) {
// NOTE: we can't use SYSTRACE_CALL() or similar here because, execute() below, also // NOTE: we can't use FILAMENT_TRACING_CALL() or similar here because, execute() below, also
// uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope. // uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope.
Profiler profiler; Profiler profiler;
if constexpr (SYSTRACE_TAG) {
if constexpr (FILAMENT_TRACING_ENABLED) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) { if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled // we want to remove all this when tracing is completely disabled
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES); profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
@@ -100,17 +102,17 @@ void CommandStream::execute(void* buffer) {
} }
}); });
if constexpr (SYSTRACE_TAG) { if constexpr (FILAMENT_TRACING_ENABLED) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) { if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled // we want to remove all this when tracing is completely disabled
profiler.stop(); profiler.stop();
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters(); UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions()); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (I)", counters.getInstructions());
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles()); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (C)", counters.getCpuCycles());
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (CPI x10)", counters.getCPI() * 10);
SYSTRACE_VALUE32("GLThread (BPU miss)", counters.getBranchMisses()); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (BPU miss)", counters.getBranchMisses());
SYSTRACE_VALUE32("GLThread (I / BPU miss)", FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (I / BPU miss)",
counters.getInstructions() / counters.getBranchMisses()); counters.getInstructions() / counters.getBranchMisses());
} }
} }
@@ -20,8 +20,9 @@
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
#include "private/backend/CommandStream.h" #include "private/backend/CommandStream.h"
#include <private/utils/Tracing.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/Systrace.h>
#include <utility> #include <utility>
@@ -38,7 +39,7 @@
#if DEBUG_LEVEL == DEBUG_LEVEL_NONE #if DEBUG_LEVEL == DEBUG_LEVEL_NONE
# define SYSTRACE() # define SYSTRACE()
#elif DEBUG_LEVEL == DEBUG_LEVEL_SYSTRACE #elif DEBUG_LEVEL == DEBUG_LEVEL_SYSTRACE
# define SYSTRACE() SYSTRACE_CALL(); # define SYSTRACE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
#else #else
# error "invalid debug level" # error "invalid debug level"
#endif #endif
+3 -3
View File
@@ -16,7 +16,7 @@
#include "CompilerThreadPool.h" #include "CompilerThreadPool.h"
#include <utils/Systrace.h> #include <private/utils/Tracing.h>
#include <memory> #include <memory>
@@ -41,7 +41,7 @@ void CompilerThreadPool::init(uint32_t threadCount,
for (size_t i = 0; i < threadCount; i++) { for (size_t i = 0; i < threadCount; i++) {
mCompilerThreads.emplace_back([this, setup, cleanup]() { mCompilerThreads.emplace_back([this, setup, cleanup]() {
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
(*setup)(); (*setup)();
@@ -54,7 +54,7 @@ void CompilerThreadPool::init(uint32_t threadCount,
[](auto&& q) { return q.empty(); })); [](auto&& q) { return q.empty(); }));
}); });
SYSTRACE_VALUE32("CompilerThreadPool Jobs", FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "CompilerThreadPool Jobs",
mQueues[0].size() + mQueues[1].size()); mQueues[0].size() + mQueues[1].size());
if (UTILS_LIKELY(!mExitRequested)) { if (UTILS_LIKELY(!mExitRequested)) {
+12 -10
View File
@@ -23,11 +23,12 @@
#include <backend/BufferDescriptor.h> #include <backend/BufferDescriptor.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <private/utils/Tracing.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/debug.h> #include <utils/debug.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/ostream.h> #include <utils/ostream.h>
#include <utils/Systrace.h>
#include <math/half.h> #include <math/half.h>
#include <math/vec2.h> #include <math/vec2.h>
@@ -151,30 +152,31 @@ void DriverBase::debugCommandBegin(CommandStream* cmds, bool synchronous, const
utils::slog.d << methodName << utils::io::endl; utils::slog.d << methodName << utils::io::endl;
} }
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) { if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) {
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_BEGIN(methodName); FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, methodName);
if (!synchronous) { if (!synchronous) {
cmds->queueCommand([=]() { cmds->queueCommand([=]() {
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_BEGIN(methodName); FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, methodName);
}); });
} }
} }
} }
} }
void DriverBase::debugCommandEnd(CommandStream* cmds, bool synchronous, const char* methodName) noexcept { void DriverBase::debugCommandEnd(CommandStream* cmds, bool synchronous,
const char* methodName) noexcept {
if constexpr (bool(FILAMENT_DEBUG_COMMANDS > FILAMENT_DEBUG_COMMANDS_NONE)) { if constexpr (bool(FILAMENT_DEBUG_COMMANDS > FILAMENT_DEBUG_COMMANDS_NONE)) {
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) { if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) {
if (!synchronous) { if (!synchronous) {
cmds->queueCommand([]() { cmds->queueCommand([]() {
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_END(); FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
}); });
} }
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_END(); FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
} }
} }
} }
+1
View File
@@ -17,6 +17,7 @@
#include <backend/Platform.h> #include <backend/Platform.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/ostream.h>
#include <atomic> #include <atomic>
#include <utility> #include <utility>
+3 -2
View File
@@ -16,7 +16,8 @@
#include <private/backend/PlatformFactory.h> #include <private/backend/PlatformFactory.h>
#include <utils/Systrace.h> #include <private/utils/Tracing.h>
#include <utils/debug.h> #include <utils/debug.h>
// We need to keep this up top for the linux (X11) name collisions. // We need to keep this up top for the linux (X11) name collisions.
@@ -85,7 +86,7 @@ namespace filament::backend {
// responsible for destroying it. Initialization of the backend API is deferred until // responsible for destroying it. Initialization of the backend API is deferred until
// createDriver(). The passed-in backend hint is replaced with the resolved backend. // createDriver(). The passed-in backend hint is replaced with the resolved backend.
Platform* PlatformFactory::create(Backend* backend) noexcept { Platform* PlatformFactory::create(Backend* backend) noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
assert_invariant(backend); assert_invariant(backend);
#if defined(__ANDROID__) #if defined(__ANDROID__)
+1 -1
View File
@@ -17,7 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H #ifndef TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
#define TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H #define TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
#include <utils/Systrace.h> #include <private/utils/Tracing.h>
#define PROFILE_SCOPE(marker) SYSTRACE_NAME(marker) #define PROFILE_SCOPE(marker) SYSTRACE_NAME(marker)
@@ -19,6 +19,8 @@
#include "MetalContext.h" #include "MetalContext.h"
#include <utils/Panic.h>
namespace filament { namespace filament {
namespace backend { namespace backend {
+13 -17
View File
@@ -672,22 +672,8 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS // nothing to do, timer query was constructed in createTimerQueryS
} }
const char* toString(DescriptorType type) { UTILS_UNUSED
switch (type) { static const char* toString(ShaderStageFlags flags) {
case DescriptorType::UNIFORM_BUFFER:
return "UNIFORM_BUFFER";
case DescriptorType::SHADER_STORAGE_BUFFER:
return "SHADER_STORAGE_BUFFER";
case DescriptorType::SAMPLER:
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
const char* toString(ShaderStageFlags flags) {
std::vector<const char*> stages; std::vector<const char*> stages;
if (any(flags & ShaderStageFlags::VERTEX)) { if (any(flags & ShaderStageFlags::VERTEX)) {
stages.push_back("VERTEX"); stages.push_back("VERTEX");
@@ -721,15 +707,25 @@ const char* toString(DescriptorFlags flags) {
void MetalDriver::createDescriptorSetLayoutR( void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info) { Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info) {
#if FILAMENT_METAL_DEBUG_LOG == 1
const char* labelStr = "";
std::visit([&labelStr](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, utils::CString> || std::is_same_v<T, utils::StaticString>) {
labelStr = arg.c_str();
}
}, info.label);
std::sort(info.bindings.begin(), info.bindings.end(), std::sort(info.bindings.begin(), info.bindings.end(),
[](const auto& a, const auto& b) { return a.binding < b.binding; }); [](const auto& a, const auto& b) { return a.binding < b.binding; });
DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = { label = %s,\n", dslh.getId(),
labelStr);
for (size_t i = 0; i < info.bindings.size(); i++) { for (size_t i = 0; i < info.bindings.size(); i++) {
DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s},\n", DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s},\n",
info.bindings[i].binding, toString(info.bindings[i].type), info.bindings[i].count, info.bindings[i].binding, toString(info.bindings[i].type), info.bindings[i].count,
toString(info.bindings[i].stageFlags), toString(info.bindings[i].flags)); toString(info.bindings[i].stageFlags), toString(info.bindings[i].flags));
} }
DEBUG_LOG("})\n"); DEBUG_LOG("})\n");
#endif
construct_handle<MetalDescriptorSetLayout>(dslh, std::move(info)); construct_handle<MetalDescriptorSetLayout>(dslh, std::move(info));
} }
+1
View File
@@ -185,6 +185,7 @@ inline MTLPixelFormat getMetalFormat(PixelDataFormat format, PixelDataType type)
CONVERT(RGBA_INTEGER, UINT, RGBA32Uint); CONVERT(RGBA_INTEGER, UINT, RGBA32Uint);
CONVERT(RGBA_INTEGER, INT, RGBA32Sint); CONVERT(RGBA_INTEGER, INT, RGBA32Sint);
CONVERT(RGBA, FLOAT, RGBA32Float); CONVERT(RGBA, FLOAT, RGBA32Float);
CONVERT(DEPTH_COMPONENT, FLOAT, Depth32Float);
#undef CONVERT #undef CONVERT
return MTLPixelFormatInvalid; return MTLPixelFormatInvalid;
+56 -2
View File
@@ -1356,7 +1356,31 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
[arguments addObject:bufferArgument]; [arguments addObject:bufferArgument];
break; break;
} }
case DescriptorType::SAMPLER: case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
case DescriptorType::SAMPLER_EXTERNAL: { case DescriptorType::SAMPLER_EXTERNAL: {
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor]; MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
textureArgument.index = binding.binding * 2; textureArgument.index = binding.binding * 2;
@@ -1382,6 +1406,9 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
break; break;
} }
} }
if (arguments.count == 0) {
return nil;
}
return [device newArgumentEncoderWithArguments:arguments]; return [device newArgumentEncoderWithArguments:arguments];
} }
@@ -1442,6 +1469,9 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
id<MTLArgumentEncoder> encoder = id<MTLArgumentEncoder> encoder =
layout->getArgumentEncoder(context.device, stage, textureTypes); layout->getArgumentEncoder(context.device, stage, textureTypes);
if (!encoder) {
return nil;
}
{ {
ScopedAllocationTimer timer("descriptor_set"); ScopedAllocationTimer timer("descriptor_set");
@@ -1472,7 +1502,31 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
atIndex:binding.binding * 2]; atIndex:binding.binding * 2];
break; break;
} }
case DescriptorType::SAMPLER: case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
case DescriptorType::SAMPLER_EXTERNAL: { case DescriptorType::SAMPLER_EXTERNAL: {
auto found = textures.find(binding.binding); auto found = textures.find(binding.binding);
if (found == textures.end()) { if (found == textures.end()) {
+6 -5
View File
@@ -20,18 +20,19 @@
#include <Metal/Metal.h> #include <Metal/Metal.h>
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
#include "backend/Program.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h> #include <utils/Hash.h>
#include <utils/Invocable.h> #include <utils/Invocable.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament { namespace filament {
namespace backend { namespace backend {
+69 -19
View File
@@ -24,14 +24,22 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include <atomic> #include <atomic>
#include <mutex>
namespace filament::backend { namespace filament::backend {
struct PlatformMetalImpl { struct PlatformMetalImpl {
std::mutex mLock; // locks mDevice and mCommandQueue
id<MTLDevice> mDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil; id<MTLCommandQueue> mCommandQueue = nil;
// read form driver thread, read/written to from client thread // read form driver thread, read/written to from client thread
std::atomic<PlatformMetal::DrawableFailureBehavior> mDrawableFailureBehavior = std::atomic<PlatformMetal::DrawableFailureBehavior> mDrawableFailureBehavior =
PlatformMetal::DrawableFailureBehavior::PANIC; PlatformMetal::DrawableFailureBehavior::PANIC;
// These methods must be called with mLock held
void createDeviceImpl(MetalDevice& outDevice);
void createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue);
}; };
Platform* createDefaultMetalPlatform() { Platform* createDefaultMetalPlatform() {
@@ -48,7 +56,59 @@ Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::Dri
return MetalDriverFactory::create(this, driverConfig); return MetalDriverFactory::create(this, driverConfig);
} }
bool PlatformMetal::initialize() noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
MetalDevice device{};
pImpl->createDeviceImpl(device);
if (device.device == nil) {
return false;
}
MetalCommandQueue commandQueue{};
pImpl->createCommandQueueImpl(device, commandQueue);
if (commandQueue.commandQueue == nil) {
return false;
}
return true;
}
void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept { void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createDeviceImpl(outDevice);
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createCommandQueueImpl(device, outCommandQueue);
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
}
// -------------------------------------------------------------------------------------------------
void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
if (mDevice) {
outDevice.device = mDevice;
return;
}
id<MTLDevice> result; id<MTLDevice> result;
#if !defined(FILAMENT_IOS) #if !defined(FILAMENT_IOS)
@@ -74,27 +134,17 @@ void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
<< utils::io::endl; << utils::io::endl;
outDevice.device = result; outDevice.device = result;
mDevice = result;
} }
void PlatformMetal::createCommandQueue( void PlatformMetalImpl::createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue) {
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept { if (mCommandQueue) {
pImpl->mCommandQueue = [device.device newCommandQueue]; outCommandQueue.commandQueue = mCommandQueue;
pImpl->mCommandQueue.label = @"Filament"; return;
outCommandQueue.commandQueue = pImpl->mCommandQueue; }
} mCommandQueue = [device.device newCommandQueue];
mCommandQueue.label = @"Filament";
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept { outCommandQueue.commandQueue = mCommandQueue;
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
} }
} // namespace filament } // namespace filament
+2 -3
View File
@@ -69,9 +69,8 @@ public:
assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT); assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT);
assert_invariant(binding < MAX_DESCRIPTOR_COUNT); assert_invariant(binding < MAX_DESCRIPTOR_COUNT);
assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now
mStorage[set][binding] = { (uint8_t)entry.binding, mStorage[set][binding] = { uint8_t(entry.binding),
entry.type == DescriptorType::SAMPLER || DescriptorSetLayoutBinding::isSampler(entry.type) };
entry.type == DescriptorType::SAMPLER_EXTERNAL };
mActiveDescriptors[set].set(binding); mActiveDescriptors[set].set(binding);
} }
@@ -95,7 +95,32 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds
} }
break; break;
} }
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
case DescriptorType::SAMPLER_EXTERNAL: case DescriptorType::SAMPLER_EXTERNAL:
if (UTILS_UNLIKELY(gl.isES2())) { if (UTILS_UNLIKELY(gl.isES2())) {
desc.emplace<SamplerGLES2>(); desc.emplace<SamplerGLES2>();
@@ -21,7 +21,7 @@
#include <backend/Platform.h> #include <backend/Platform.h>
#include <backend/Program.h> #include <backend/Program.h>
#include <utils/Systrace.h> #include <private/utils/Tracing.h>
namespace filament::backend { namespace filament::backend {
@@ -36,7 +36,7 @@ OpenGLBlobCache::OpenGLBlobCache(OpenGLContext& gl) noexcept
GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
Program const& program) const noexcept { Program const& program) const noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
if (!mCachingSupported || !platform.hasRetrieveBlobFunc()) { if (!mCachingSupported || !platform.hasRetrieveBlobFunc()) {
// the key is never updated in that case // the key is never updated in that case
return 0; return 0;
@@ -68,7 +68,7 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
programId = glCreateProgram(); programId = glCreateProgram();
{ // scope for systrace { // scope for systrace
SYSTRACE_NAME("glProgramBinary"); FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glProgramBinary");
glProgramBinary(programId, blob->format, blob->data, programBinarySize); glProgramBinary(programId, blob->format, blob->data, programBinarySize);
} }
@@ -90,7 +90,7 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
void OpenGLBlobCache::insert(Platform& platform, void OpenGLBlobCache::insert(Platform& platform,
BlobCacheKey const& key, GLuint program) noexcept { BlobCacheKey const& key, GLuint program) noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
if (!mCachingSupported || !platform.hasInsertBlobFunc()) { if (!mCachingSupported || !platform.hasInsertBlobFunc()) {
// the key is never updated in that case // the key is never updated in that case
return; return;
@@ -100,7 +100,7 @@ void OpenGLBlobCache::insert(Platform& platform,
GLenum format; GLenum format;
GLint programBinarySize = 0; GLint programBinarySize = 0;
{ // scope for systrace { // scope for systrace
SYSTRACE_NAME("glGetProgramiv"); FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glGetProgramiv");
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize); glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize);
} }
if (programBinarySize) { if (programBinarySize) {
@@ -108,7 +108,7 @@ void OpenGLBlobCache::insert(Platform& platform,
std::unique_ptr<Blob, decltype(&::free)> blob{ (Blob*)malloc(size), &::free }; std::unique_ptr<Blob, decltype(&::free)> blob{ (Blob*)malloc(size), &::free };
if (UTILS_LIKELY(blob)) { if (UTILS_LIKELY(blob)) {
{ // scope for systrace { // scope for systrace
SYSTRACE_NAME("glGetProgramBinary"); FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glGetProgramBinary");
glGetProgramBinary(program, programBinarySize, glGetProgramBinary(program, programBinarySize,
&programBinarySize, &format, blob->data); &programBinarySize, &format, blob->data);
} }
@@ -542,6 +542,13 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "Intel")) { } else if (strstr(renderer, "Intel")) {
// Intel GPU // Intel GPU
bugs->vao_doesnt_store_element_array_buffer_binding = true; bugs->vao_doesnt_store_element_array_buffer_binding = true;
if (strstr(renderer, "Mesa")) {
// Mesa Intel driver on Linux/Android
// Renderer of the form [Mesa Intel(R) HD Graphics 505 (APL 3)]
// b/405252622
bugs->disable_invalidate_framebuffer = true;
}
} else if (strstr(renderer, "PowerVR")) { } else if (strstr(renderer, "PowerVR")) {
// PowerVR GPU // PowerVR GPU
// On PowerVR (Rogue GE8320) glFlush doesn't seem to do anything, in particular, // On PowerVR (Rogue GE8320) glFlush doesn't seem to do anything, in particular,
+14 -13
View File
@@ -44,6 +44,8 @@
#include "private/backend/Dispatcher.h" #include "private/backend/Dispatcher.h"
#include "private/backend/DriverApi.h" #include "private/backend/DriverApi.h"
#include <private/utils/Tracing.h>
#include <type_traits> #include <type_traits>
#include <utils/BitmaskEnum.h> #include <utils/BitmaskEnum.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
@@ -51,7 +53,6 @@
#include <utils/Invocable.h> #include <utils/Invocable.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/Slice.h> #include <utils/Slice.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/debug.h> #include <utils/debug.h>
@@ -98,13 +99,13 @@
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker #define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support) #define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace) #define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (perfetto)
#define DEBUG_GROUP_MARKER_ALL 0xFF // all markers #define DEBUG_GROUP_MARKER_ALL 0xFF // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker #define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support) #define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace) #define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (perfetto)
#define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (systrace) #define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (perfetto)
#define DEBUG_MARKER_ALL (0xFF & ~DEBUG_MARKER_PROFILE) // all markers #define DEBUG_MARKER_ALL (0xFF & ~DEBUG_MARKER_PROFILE) // all markers
// set to the desired debug marker level (for user markers [default: All]) // set to the desired debug marker level (for user markers [default: All])
@@ -248,8 +249,8 @@ OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string)
#endif #endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND #if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_BEGIN(string); FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, string);
#endif #endif
#endif #endif
} }
@@ -265,8 +266,8 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
#endif #endif
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND #if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_END(); FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
#endif #endif
#endif #endif
} }
@@ -3348,8 +3349,8 @@ void OpenGLDriver::pushGroupMarker(char const* string) {
#endif #endif
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND #if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_BEGIN(string); FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, string);
#endif #endif
#endif #endif
} }
@@ -3365,8 +3366,8 @@ void OpenGLDriver::popGroupMarker(int) {
#endif #endif
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND #if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_NAME_END(); FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
#endif #endif
#endif #endif
} }
@@ -3685,7 +3686,7 @@ void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
gl.depthFunc(GL_LESS); gl.depthFunc(GL_LESS);
gl.disable(GL_SCISSOR_TEST); gl.disable(GL_SCISSOR_TEST);
#endif #endif
//SYSTRACE_NAME("glFinish"); //FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glFinish");
//glFinish(); //glFinish();
mPlatform.endFrame(frameId); mPlatform.endFrame(frameId);
insertEventMarker("endFrame"); insertEventMarker("endFrame");
+29 -6
View File
@@ -25,12 +25,13 @@
#include <backend/Program.h> #include <backend/Program.h>
#include <backend/Handle.h> #include <backend/Handle.h>
#include <private/utils/Tracing.h>
#include <utils/BitmaskEnum.h> #include <utils/BitmaskEnum.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/debug.h> #include <utils/debug.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Systrace.h>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@@ -96,8 +97,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
} }
void OpenGLProgram::initialize(OpenGLDriver& gld) { void OpenGLProgram::initialize(OpenGLDriver& gld) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_CALL();
assert_invariant(gl.program == 0); assert_invariant(gl.program == 0);
assert_invariant(mToken); assert_invariant(mToken);
@@ -122,8 +122,7 @@ void OpenGLProgram::initialize(OpenGLDriver& gld) {
*/ */
void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program, void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program,
LazyInitializationData& lazyInitializationData) noexcept { LazyInitializationData& lazyInitializationData) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_CALL();
// from the pipeline layout we compute a mapping from {set, binding} to {binding} // from the pipeline layout we compute a mapping from {set, binding} to {binding}
// for both buffers and textures // for both buffers and textures
@@ -175,7 +174,31 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
} }
break; break;
} }
case DescriptorType::SAMPLER: case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
case DescriptorType::SAMPLER_EXTERNAL: { case DescriptorType::SAMPLER_EXTERNAL: {
if (!entry.name.empty()) { if (!entry.name.empty()) {
GLint const loc = glGetUniformLocation(program, entry.name.c_str()); GLint const loc = glGetUniformLocation(program, entry.name.c_str());
@@ -23,12 +23,13 @@
#include <backend/platforms/OpenGLPlatform.h> #include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <private/utils/Tracing.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/debug.h> #include <utils/debug.h>
#include <utils/JobSystem.h> #include <utils/JobSystem.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Mutex.h> #include <utils/Mutex.h>
#include <utils/Systrace.h>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
@@ -240,8 +241,8 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
if (state) { if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER); platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
state->then = clock::now().time_since_epoch().count(); state->then = clock::now().time_since_epoch().count();
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_ASYNC_BEGIN("OpenGLTimerQueryFence", intptr_t(state.get())); FILAMENT_TRACING_ASYNC_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, "OpenGLTimerQueryFence", intptr_t(state.get()));
} }
platform.destroyFence(fence); platform.destroyFence(fence);
}); });
@@ -257,8 +258,8 @@ void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq
platform.waitFence(fence, FENCE_WAIT_FOR_EVER); platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
int64_t const now = clock::now().time_since_epoch().count(); int64_t const now = clock::now().time_since_epoch().count();
state->elapsed.store(now - state->then, std::memory_order_relaxed); state->elapsed.store(now - state->then, std::memory_order_relaxed);
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_ASYNC_END("OpenGLTimerQueryFence", intptr_t(state.get())); FILAMENT_TRACING_ASYNC_END(FILAMENT_TRACING_CATEGORY_FILAMENT, "OpenGLTimerQueryFence", intptr_t(state.get()));
} }
platform.destroyFence(fence); platform.destroyFence(fence);
}); });
@@ -28,6 +28,8 @@
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/Program.h> #include <backend/Program.h>
#include <private/utils/Tracing.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/CString.h> #include <utils/CString.h>
#include <utils/debug.h> #include <utils/debug.h>
@@ -36,7 +38,6 @@
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/ostream.h> #include <utils/ostream.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#include <utils/Systrace.h>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@@ -367,7 +368,7 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
GLuint ShaderCompilerService::initialize(program_token_t& token) { GLuint ShaderCompilerService::initialize(program_token_t& token) {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
assert_invariant(token);// This function should be called when the token is still alive. assert_invariant(token);// This function should be called when the token is still alive.
@@ -454,8 +455,8 @@ void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority,
}); });
ops.emplace(pos, priority, token, std::move(job)); ops.emplace(pos, priority, token, std::move(job));
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_VALUE32("ShaderCompilerService Jobs", mRunAtNextTickOps.size()); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "ShaderCompilerService Jobs", mRunAtNextTickOps.size());
} }
bool ShaderCompilerService::cancelTickOp(program_token_t const& token) noexcept { bool ShaderCompilerService::cancelTickOp(program_token_t const& token) noexcept {
@@ -468,8 +469,8 @@ bool ShaderCompilerService::cancelTickOp(program_token_t const& token) noexcept
ops.erase(pos); ops.erase(pos);
return true; return true;
} }
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size()); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "ShaderCompilerService Jobs", ops.size());
return false; return false;
} }
@@ -485,15 +486,15 @@ void ShaderCompilerService::executeTickOps() noexcept {
++it; ++it;
} }
} }
SYSTRACE_CONTEXT(); FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size()); FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "ShaderCompilerService Jobs", ops.size());
} }
/* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context, /* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context,
Program::ShaderSource shadersSource, Program::ShaderSource shadersSource,
FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants, FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview, program_token_t const& token) noexcept { bool multiview, program_token_t const& token) noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
auto const appendSpecConstantString = +[](std::string& s, Program::SpecializationConstant const& sc) { auto const appendSpecConstantString = +[](std::string& s, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' '; s += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
@@ -616,7 +617,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
} }
/* static */ void ShaderCompilerService::checkCompileStatus(program_token_t const& token) noexcept { /* static */ void ShaderCompilerService::checkCompileStatus(program_token_t const& token) noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
UTILS_NOUNROLL UTILS_NOUNROLL
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) { for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
@@ -639,7 +640,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
/* static */ void ShaderCompilerService::linkProgram(OpenGLContext const& context, /* static */ void ShaderCompilerService::linkProgram(OpenGLContext const& context,
program_token_t const& token) noexcept { program_token_t const& token) noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// Shader compilation should be completed by now. Check the status and log errors on failure. // Shader compilation should be completed by now. Check the status and log errors on failure.
checkCompileStatus(token); checkCompileStatus(token);
@@ -675,7 +676,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
/* static */ bool ShaderCompilerService::checkLinkStatusAndCleanupShaders( /* static */ bool ShaderCompilerService::checkLinkStatusAndCleanupShaders(
program_token_t const& token) noexcept { program_token_t const& token) noexcept {
SYSTRACE_CALL(); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
assert_invariant(token->gl.program); assert_invariant(token->gl.program);
bool linked = true; bool linked = true;
@@ -54,7 +54,7 @@ struct PlatformCocoaGLImpl {
CVOpenGLTextureCacheRef mTextureCache = nullptr; CVOpenGLTextureCacheRef mTextureCache = nullptr;
std::unique_ptr<CocoaExternalImage::SharedGl> mExternalImageSharedGl; std::unique_ptr<CocoaExternalImage::SharedGl> mExternalImageSharedGl;
void updateOpenGLContext(NSView *nsView, bool resetView, bool clearView); void updateOpenGLContext(NSView *nsView, bool resetView, bool clearView);
struct ExternalImageCocoaGL : public Platform::ExternalImage { struct ExternalImageCocoaGL final : public Platform::ExternalImage {
CVPixelBufferRef cvBuffer; CVPixelBufferRef cvBuffer;
protected: protected:
~ExternalImageCocoaGL() noexcept final; ~ExternalImageCocoaGL() noexcept final;
@@ -255,6 +255,7 @@ Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareB
p->height = hardwareBufferDescription.height; p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width; p->width = hardwareBufferDescription.width;
auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB); auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->format = textureFormat;
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat); p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return ExternalImageHandle{ p }; return ExternalImageHandle{ p };
} }
+17 -16
View File
@@ -28,7 +28,7 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
: mAllocator(allocator), : mAllocator(allocator),
mStagePool(stagePool), mStagePool(stagePool),
mUsage(usage), mUsage(usage),
mUpdatedOffset(0), mUpdatedOffset(0),
mUpdatedBytes(0) { mUpdatedBytes(0) {
// for now make sure that only 1 bit is set in usage // for now make sure that only 1 bit is set in usage
// (because loadFromCpu() assumes that somewhat) // (because loadFromCpu() assumes that somewhat)
@@ -56,11 +56,12 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
vmaMapMemory(mAllocator, stage->memory, &mapped); vmaMapMemory(mAllocator, stage->memory, &mapped);
memcpy(mapped, cpuData, numBytes); memcpy(mapped, cpuData, numBytes);
vmaUnmapMemory(mAllocator, stage->memory); vmaUnmapMemory(mAllocator, stage->memory);
vmaFlushAllocation(mAllocator, stage->memory, byteOffset, numBytes); vmaFlushAllocation(mAllocator, stage->memory, 0, numBytes);
// If there was a previous update, then we need to make sure the following write is properly // If there was a previous update, then we need to make sure the following write is properly
// synced with the previous read. // synced with the previous read.
if (mUpdatedBytes > 0) { if (mUpdatedBytes > 0 &&
(byteOffset >= mUpdatedOffset && byteOffset <= (mUpdatedOffset + mUpdatedBytes))) {
VkAccessFlags srcAccess = 0; VkAccessFlags srcAccess = 0;
VkPipelineStageFlags srcStage = 0; VkPipelineStageFlags srcStage = 0;
if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) { if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
@@ -74,28 +75,28 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} }
VkBufferMemoryBarrier barrier{ VkBufferMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = srcAccess, .srcAccessMask = srcAccess,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = mGpuBuffer, .buffer = mGpuBuffer,
.offset = mUpdatedOffset, .offset = byteOffset,
.size = mUpdatedBytes, .size = numBytes,
}; };
vkCmdPipelineBarrier(cmdbuf, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, vkCmdPipelineBarrier(cmdbuf, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1,
&barrier, 0, nullptr); &barrier, 0, nullptr);
} }
VkBufferCopy region { VkBufferCopy region = {
.srcOffset = 0, .srcOffset = 0,
.dstOffset = byteOffset, .dstOffset = byteOffset,
.size = numBytes, .size = numBytes,
}; };
vkCmdCopyBuffer(cmdbuf, stage->buffer, mGpuBuffer, 1, &region); vkCmdCopyBuffer(cmdbuf, stage->buffer, mGpuBuffer, 1, &region);
mUpdatedOffset = byteOffset; mUpdatedOffset = byteOffset;
mUpdatedBytes = numBytes; mUpdatedBytes = numBytes;
// Firstly, ensure that the copy finishes before the next draw call. // Firstly, ensure that the copy finishes before the next draw call.
@@ -112,21 +113,21 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
dstAccessMask |= VK_ACCESS_INDEX_READ_BIT; dstAccessMask |= VK_ACCESS_INDEX_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} else if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) { } else if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
dstAccessMask |= VK_ACCESS_UNIFORM_READ_BIT; dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
dstStageMask |= dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
} else if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) { } else if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
// TODO: implement me // TODO: implement me
} }
VkBufferMemoryBarrier barrier{ VkBufferMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = dstAccessMask, .dstAccessMask = dstAccessMask,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = mGpuBuffer, .buffer = mGpuBuffer,
.size = VK_WHOLE_SIZE, .offset = byteOffset,
.size = numBytes,
}; };
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, 0, 0, nullptr, 1, vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, 0, 0, nullptr, 1,
@@ -151,13 +151,13 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE) #elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h> #include <private/utils/Tracing.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT() #define FVK_SYSTRACE_CONTEXT() FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT)
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker) #define FVK_SYSTRACE_START(marker) FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END() #define FVK_SYSTRACE_END() FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT)
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL() #define FVK_SYSTRACE_SCOPE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL() #define FVK_PROFILE_MARKER(marker) FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
#else #else
#define FVK_SYSTRACE_CONTEXT() #define FVK_SYSTRACE_CONTEXT()
@@ -142,6 +142,10 @@ public:
return mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE; return mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
} }
inline bool isUnifiedMemoryArchitecture() const noexcept {
return mIsUnifiedMemoryArchitecture;
}
private: private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
@@ -164,6 +168,7 @@ private:
bool mDebugUtilsSupported = false; bool mDebugUtilsSupported = false;
bool mLazilyAllocatedMemorySupported = false; bool mLazilyAllocatedMemorySupported = false;
bool mProtectedMemorySupported = false; bool mProtectedMemorySupported = false;
bool mIsUnifiedMemoryArchitecture = false;
fvkutils::VkFormatList mDepthStencilFormats; fvkutils::VkFormatList mDepthStencilFormats;
fvkutils::VkFormatList mBlittableDepthStencilFormats; fvkutils::VkFormatList mBlittableDepthStencilFormats;
@@ -358,12 +358,11 @@ void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t
range.levelCount = 1; range.levelCount = 1;
range.layerCount = 1; range.layerCount = 1;
} }
VkDescriptorImageInfo info{ VkDescriptorImageInfo info = {
.sampler = sampler, .sampler = sampler,
.imageView = texture->getView(range), .imageView = texture->getView(range),
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()), .imageLayout = fvkutils::getVkLayout(texture->getSamplerLayout()),
}; };
VkWriteDescriptorSet descriptorWrite = { VkWriteDescriptorSet descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr, .pNext = nullptr,
+1 -1
View File
@@ -622,7 +622,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
auto& commands = mCommands.get(); auto& commands = mCommands.get();
// Unlike uploaded textures or swapchains, we need to explicit transition this // Unlike uploaded textures or swapchains, we need to explicit transition this
// texture into the read layout. // texture into the read layout.
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY); texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::FRAG_READ);
if (imgData.external.valid()) { if (imgData.external.valid()) {
mExternalImageManager.addExternallySampledTexture(texture, externalImage); mExternalImageManager.addExternallySampledTexture(texture, externalImage);
+28 -2
View File
@@ -93,7 +93,32 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
case DescriptorType::SAMPLER_EXTERNAL: case DescriptorType::SAMPLER_EXTERNAL:
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler); fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
UTILS_FALLTHROUGH; UTILS_FALLTHROUGH;
case DescriptorType::SAMPLER: {
case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: {
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler); fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
break; break;
} }
@@ -495,7 +520,8 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands
} }
} else { } else {
texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT); texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT);
if (!texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE)) { if (texture->isSampleable() &&
!texture->transitionLayout(&commands, range, VulkanLayout::FRAG_READ)) {
texture->attachmentToSamplerBarrier(&commands, range); texture->attachmentToSamplerBarrier(&commands, range);
} }
} }
@@ -234,6 +234,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
VulkanAttachment const srcAttachment = srcTarget->getColor0(); VulkanAttachment const srcAttachment = srcTarget->getColor0();
VkImageSubresourceRange const srcRange = srcAttachment.getSubresourceRange(); VkImageSubresourceRange const srcRange = srcAttachment.getSubresourceRange();
VulkanLayout const srcLayout = srcAttachment.getLayout();
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC); srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
VkImageCopy const imageCopyRegion = { VkImageCopy const imageCopyRegion = {
@@ -270,7 +271,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
fvkutils::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion); fvkutils::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion);
// Restore the source image layout. // Restore the source image layout.
srcTexture->transitionLayout(cmdbuffer, srcRange, srcTexture->getDefaultLayout()); srcTexture->transitionLayout(cmdbuffer, srcRange, srcLayout);
vkEndCommandBuffer(cmdbuffer); vkEndCommandBuffer(cmdbuffer);
@@ -125,7 +125,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
fvkutils::transitionLayout(cmdbuffer, { fvkutils::transitionLayout(cmdbuffer, {
.image = image->image, .image = image->image,
.oldLayout = VulkanLayout::UNDEFINED, .oldLayout = VulkanLayout::UNDEFINED,
.newLayout = VulkanLayout::READ_WRITE, // (= VK_IMAGE_LAYOUT_GENERAL) .newLayout = VulkanLayout::STAGING, // (= VK_IMAGE_LAYOUT_GENERAL)
.subresources = { aspectFlags, 0, 1, 0, 1 }, .subresources = { aspectFlags, 0, 1, 0, 1 },
}); });
return image; return image;
+21 -21
View File
@@ -125,7 +125,7 @@ inline VulkanLayout getDefaultLayoutImpl(TextureUsage usage) {
return VulkanLayout::COLOR_ATTACHMENT; return VulkanLayout::COLOR_ATTACHMENT;
} }
// Finally, the layout for an immutable texture is optimal read-only. // Finally, the layout for an immutable texture is optimal read-only.
return VulkanLayout::READ_ONLY; return VulkanLayout::FRAG_READ;
} }
inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) { inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) {
@@ -187,7 +187,7 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
VkFormatProperties props; VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props); vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not " FVK_LOGW << "Texture usage is SAMPLEABLE but format " << vkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl; "sampleable with optimal tiling." << utils::io::endl;
} }
} }
@@ -691,6 +691,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
<< " is skipped because of no change in layout" << utils::io::endl; << " is skipped because of no change in layout" << utils::io::endl;
#endif #endif
} }
return hasTransitions; return hasTransitions;
} }
@@ -700,16 +701,15 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
VkImageLayout const layout = VkImageLayout const layout =
fvkutils::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel)); fvkutils::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
VkImageMemoryBarrier barrier = { VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
.dstAccessMask = .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .oldLayout = layout,
.oldLayout = layout, .newLayout = layout,
.newLayout = layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = mState->mTextureImage,
.image = mState->mTextureImage, .subresourceRange = range,
.subresourceRange = range,
}; };
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
@@ -723,15 +723,15 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
VkImageLayout const layout VkImageLayout const layout
= fvkutils::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel)); = fvkutils::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
VkImageMemoryBarrier barrier = { VkImageMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = layout, .oldLayout = layout,
.newLayout = layout, .newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = mState->mTextureImage, .image = mState->mTextureImage,
.subresourceRange = range, .subresourceRange = range,
}; };
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
+12 -2
View File
@@ -145,8 +145,14 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; } VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; }
VulkanLayout getPrimaryImageLayout() const { VulkanLayout getSamplerLayout() const {
return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel); if (!isSampleable()) {
return VulkanLayout::UNDEFINED;
}
if (mState->mUsage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
return VulkanLayout::DEPTH_SAMPLER;
}
return VulkanLayout::FRAG_READ;
} }
// Returns the layout for the intended use of this texture (and not the expected layout at the // Returns the layout for the intended use of this texture (and not the expected layout at the
@@ -180,6 +186,10 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
} }
bool isSampleable() const {
return mState->mUsage & VK_IMAGE_USAGE_SAMPLED_BIT;
}
bool getIsProtected() const { bool getIsProtected() const {
return mState->mIsProtected; return mState->mIsProtected;
} }
@@ -631,6 +631,21 @@ fvkutils::VkFormatList findBlittableDepthStencilFormats(VkPhysicalDevice device)
return ret; return ret;
} }
/**
* Check if the GPU has a unified memory architecture.
*/
bool hasUnifiedMemoryArchitecture(VkPhysicalDeviceMemoryProperties memoryProperties) noexcept {
// Try to identify if the platform is running on a Unified Memory Architecture by inspecting the
// memory heap flags, if they are all VK_MEMORY_HEAP_DEVICE_LOCAL_BIT it's UMA, otherwise not
// enough information to make a decision, so default to false.
for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; ++i) {
if ((memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) == 0) {
return false;
}
}
return true;
}
}// anonymous namespace }// anonymous namespace
using SwapChainPtr = VulkanPlatform::SwapChainPtr; using SwapChainPtr = VulkanPlatform::SwapChainPtr;
@@ -864,6 +879,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
} }
} }
context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties);
#ifdef NDEBUG #ifdef NDEBUG
// If we are in release build, we should not have turned on debug extensions // If we are in release build, we should not have turned on debug extensions
FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported) FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported)
+24 -22
View File
@@ -42,20 +42,22 @@ getVkTransition(const VulkanLayoutTransition& transition) {
srcStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; srcStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
break; break;
case VulkanLayout::COLOR_ATTACHMENT: case VulkanLayout::COLOR_ATTACHMENT:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break; break;
case VulkanLayout::READ_WRITE: case VulkanLayout::STAGING:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break; break;
case VulkanLayout::READ_ONLY: case VulkanLayout::FRAG_READ:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT; srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break; break;
case VulkanLayout::VERT_READ:
srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
break;
case VulkanLayout::TRANSFER_SRC: case VulkanLayout::TRANSFER_SRC:
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
@@ -69,7 +71,7 @@ getVkTransition(const VulkanLayoutTransition& transition) {
srcStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; srcStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break; break;
case VulkanLayout::DEPTH_SAMPLER: case VulkanLayout::DEPTH_SAMPLER:
srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break; break;
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE: case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
@@ -84,19 +86,21 @@ getVkTransition(const VulkanLayoutTransition& transition) {
switch (transition.newLayout) { switch (transition.newLayout) {
case VulkanLayout::COLOR_ATTACHMENT: case VulkanLayout::COLOR_ATTACHMENT:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break; break;
case VulkanLayout::READ_WRITE: case VulkanLayout::STAGING:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break; break;
case VulkanLayout::READ_ONLY: case VulkanLayout::FRAG_READ:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT; dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VulkanLayout::VERT_READ:
dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dstStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
break; break;
case VulkanLayout::TRANSFER_SRC: case VulkanLayout::TRANSFER_SRC:
dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
@@ -112,10 +116,8 @@ getVkTransition(const VulkanLayoutTransition& transition) {
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
break; break;
case VulkanLayout::DEPTH_SAMPLER: case VulkanLayout::DEPTH_SAMPLER:
dstAccessMask = dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
break; break;
case VulkanLayout::PRESENT: case VulkanLayout::PRESENT:
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE: case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
@@ -138,7 +140,6 @@ bool transitionLayout(VkCommandBuffer cmdbuffer,
} }
auto [srcAccessMask, dstAccessMask, srcStage, dstStage, oldLayout, newLayout] auto [srcAccessMask, dstAccessMask, srcStage, dstStage, oldLayout, newLayout]
= getVkTransition(transition); = getVkTransition(transition);
if (oldLayout == newLayout) { if (oldLayout == newLayout) {
return false; return false;
} }
@@ -155,7 +156,7 @@ bool transitionLayout(VkCommandBuffer cmdbuffer,
.image = transition.image, .image = transition.image,
.subresourceRange = transition.subresources, .subresourceRange = transition.subresources,
}; };
vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
return true; return true;
} }
@@ -264,8 +265,9 @@ utils::io::ostream& operator<<(utils::io::ostream& out,
const filament::backend::VulkanLayout& layout) { const filament::backend::VulkanLayout& layout) {
switch (layout) { switch (layout) {
CASE(UNDEFINED) CASE(UNDEFINED)
CASE(READ_ONLY) CASE(FRAG_READ)
CASE(READ_WRITE) CASE(VERT_READ)
CASE(STAGING)
CASE(TRANSFER_SRC) CASE(TRANSFER_SRC)
CASE(TRANSFER_DST) CASE(TRANSFER_DST)
CASE(DEPTH_ATTACHMENT) CASE(DEPTH_ATTACHMENT)
+10 -8
View File
@@ -32,9 +32,11 @@ enum class VulkanLayout : uint8_t {
// any transition. // any transition.
UNDEFINED, UNDEFINED,
// Fragment/vertex shader accessible layout for reading and writing. // Fragment/vertex shader accessible layout for reading and writing.
READ_WRITE, STAGING,
// Fragment/vertex shader accessible layout for reading only. // Fragment shader accessible layout for reading only.
READ_ONLY, FRAG_READ,
// Vertex shader accessible layout for reading only.
VERT_READ,
// For the source of a copy operation. // For the source of a copy operation.
TRANSFER_SRC, TRANSFER_SRC,
// For the destination of a copy operation. // For the destination of a copy operation.
@@ -65,9 +67,10 @@ constexpr inline VkImageLayout getVkLayout(VulkanLayout layout) {
switch (layout) { switch (layout) {
case VulkanLayout::UNDEFINED: case VulkanLayout::UNDEFINED:
return VK_IMAGE_LAYOUT_UNDEFINED; return VK_IMAGE_LAYOUT_UNDEFINED;
case VulkanLayout::READ_WRITE: case VulkanLayout::STAGING:
return VK_IMAGE_LAYOUT_GENERAL; return VK_IMAGE_LAYOUT_GENERAL;
case VulkanLayout::READ_ONLY: case VulkanLayout::FRAG_READ:
case VulkanLayout::VERT_READ:
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
case VulkanLayout::TRANSFER_SRC: case VulkanLayout::TRANSFER_SRC:
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
@@ -80,9 +83,8 @@ constexpr inline VkImageLayout getVkLayout(VulkanLayout layout) {
case VulkanLayout::PRESENT: case VulkanLayout::PRESENT:
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// Filament sometimes samples from one miplevel while writing to another level in the // Filament sometimes samples from one miplevel while writing to another level in the
// same texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive // same texture (e.g. bloom does this). So, keep it simple and use GENERAL for all
// layout transitions. So, keep it simple and use GENERAL for all color-attachable // color-attachable textures.
// textures.
case VulkanLayout::COLOR_ATTACHMENT: case VulkanLayout::COLOR_ATTACHMENT:
return VK_IMAGE_LAYOUT_GENERAL; return VK_IMAGE_LAYOUT_GENERAL;
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE: case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
+266 -162
View File
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include <utils/Hash.h>
#include "webgpu/WebGPUDriver.h" #include "webgpu/WebGPUDriver.h"
#include "WebGPUPipelineCreation.h" #include "WebGPUPipelineCreation.h"
@@ -26,7 +26,7 @@
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/Handle.h> #include <backend/Handle.h>
#include <backend/TargetBufferInfo.h> #include <backend/TargetBufferInfo.h>
#include <private/backend/BackendUtils.h>
#include <math/mat3.h> #include <math/mat3.h>
#include <utils/CString.h> #include <utils/CString.h>
#include <utils/Panic.h> #include <utils/Panic.h>
@@ -39,6 +39,7 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <memory>
#include <sstream> #include <sstream>
#include <string_view> #include <string_view>
#include <utility> #include <utility>
@@ -234,6 +235,9 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
printAdapterDetails(mAdapter); printAdapterDetails(mAdapter);
#endif #endif
mDevice = mPlatform.requestDevice(mAdapter); mDevice = mPlatform.requestDevice(mAdapter);
wgpu::Limits supportedLimits{};
mDevice.GetLimits(&supportedLimits);
mMinUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) #if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice); printDeviceDetails(mDevice);
#endif #endif
@@ -350,6 +354,17 @@ void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
} }
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) { void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
if (rth) {
WGPURenderTarget* rt = handleCast<WGPURenderTarget>(rth);
if (rt == mDefaultRenderTarget) {
mDefaultRenderTarget = nullptr;
}
// WGPURenderTarget destructor is trivial.
// The HwTexture handles stored within WGPURenderTarget (via MRT, TargetBufferInfo)
// are not owned by WGPURenderTarget, so they are not destroyed here.
// They are destroyed via WebGPUDriver::destroyTexture.
destructHandle<WGPURenderTarget>(rth);
}
} }
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) { void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
@@ -468,11 +483,10 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter, mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
mDevice, flags); mDevice, flags);
assert_invariant(mSwapChain); assert_invariant(mSwapChain);
WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(mDevice,
mSwapChain->getColorFormat()); FWGPU_LOGW << "WebGPU support is highly experimental, in development, and tested for only a "
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only " "small set of simple samples (e.g. hellotriangle and texturedquad), thus issues "
"background components have been instantiated/selected, such as surface/screen, " "are likely to be encountered at this stage."
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
<< utils::io::endl; << utils::io::endl;
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG) #if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
@@ -577,7 +591,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets, void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {} TargetBufferInfo depth, TargetBufferInfo stencil) {
// The `targets` flags indicate which of the `color`, `depth`, `stencil` TargetBufferInfo
// are actually active for this render target.
// We'll pass all TargetBufferInfo to WGPURenderTarget; it will use them if their handles are valid.
// Ensure that textures intended for use as attachments were created with
// wgpu::TextureUsage::RenderAttachment. This check should ideally be in createTextureR
// or validated here if possible.
// The `layerCount` parameter might be for creating array textures that this RT targets.
// Individual attachments (color[i].layer, depth.layer, stencil.layer) specify which layer
// of an array texture to bind. For now, we assume textures are pre-configured.
constructHandle<WGPURenderTarget>(rth, width, height, samples, color, depth, stencil);
}
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {} void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
@@ -721,17 +749,20 @@ size_t WebGPUDriver::getMaxArrayTextureLayers() {
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p, void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) { uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUIndexBuffer>(ibh), std::move(p), byteOffset); handleCast<WGPUIndexBuffer>(ibh)->updateGPUBuffer(p, byteOffset, mQueue);
scheduleDestroy(std::move(p));
} }
void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor&& p, void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
uint32_t byteOffset) { uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset); handleCast<WGPUBufferObject>(ibh)->updateGPUBuffer(p, byteOffset, mQueue);
scheduleDestroy(std::move(p));
} }
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh,
BufferDescriptor&& p, uint32_t byteOffset) { BufferDescriptor&& p, uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset); handleCast<WGPUBufferObject>(ibh)->updateGPUBuffer(p, byteOffset, mQueue);
scheduleDestroy(std::move(p));
} }
void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) { void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
@@ -743,15 +774,76 @@ void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh); auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* bufferObject = handleCast<WGPUBufferObject>(boh); auto* bufferObject = handleCast<WGPUBufferObject>(boh);
assert_invariant(index < vertexBuffer->buffers.size()); assert_invariant(index < vertexBuffer->buffers.size());
assert_invariant(bufferObject->buffer.GetUsage() & wgpu::BufferUsage::Vertex); assert_invariant(bufferObject->getBuffer().GetUsage() & wgpu::BufferUsage::Vertex);
vertexBuffer->buffers[index] = bufferObject->buffer; vertexBuffer->buffers[index] = bufferObject->getBuffer();
} }
void WebGPUDriver::update3DImage(Handle<HwTexture> th, void WebGPUDriver::update3DImage(Handle<HwTexture> th, uint32_t level, uint32_t xoffset,
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth,
uint32_t width, uint32_t height, uint32_t depth, PixelBufferDescriptor&& p) {
PixelBufferDescriptor&& data) { PixelBufferDescriptor* data = &p;
scheduleDestroy(std::move(data)); PixelBufferDescriptor reshapedData;
if (reshape(p, reshapedData)) {
data = &reshapedData;
}
auto texture = handleCast<WGPUTexture>(th);
// TODO: Writing to a depth texture is illegal and errors. I'm not sure why Filament is trying
// to do so, but early returning is working?
if(texture->getAspect() == wgpu::TextureAspect::DepthOnly){
scheduleDestroy(std::move(p));
return;
}
size_t blockWidth = texture->getBlockWidth();
size_t blockHeight = texture->getBlockHeight();
// WebGPU specification requires that for compressed textures, the x and y offsets
// must be a multiple of the compressed texture format's block width and height.
// See: https://www.w3.org/TR/webgpu/#abstract-opdef-validating-gputexelcopytextureinfo
if (blockWidth > 1 || blockHeight > 1) {
FILAMENT_CHECK_PRECONDITION(xoffset % blockWidth == 0)
<< "xoffset must be aligned to blockwidth, but offset is " << blockWidth
<< "and offset is " << xoffset;
FILAMENT_CHECK_PRECONDITION(yoffset % blockHeight == 0)
<< "yoffset must be aligned to blockHeight, but offset is " << blockHeight
<< "and offset is " << yoffset;
}
auto copyInfo = wgpu::TexelCopyTextureInfo{ .texture = texture->getTexture(),
.mipLevel = level,
.origin = { .x = xoffset, .y = yoffset, .z = zoffset },
.aspect = texture->getAspect() };
uint32_t bytesPerRow = static_cast<uint32_t>(
PixelBufferDescriptor::computePixelSize(data->format, data->type) * width);
auto extent = wgpu::Extent3D{ .width = width, .height = height, .depthOrArrayLayers = depth };
const uint8_t* dataBuff = static_cast<const uint8_t*>(data->buffer);
size_t dataSize = data->size;
std::unique_ptr<uint8_t[]> paddedBuffer;
if (bytesPerRow % 256 != 0) {
uint32_t padding = 256 - (bytesPerRow % 256);
uint32_t paddedBytesPerRow = bytesPerRow + padding;
size_t paddedBufferSize = static_cast<size_t>(paddedBytesPerRow) * height * depth;
paddedBuffer = std::make_unique<uint8_t[]>(paddedBufferSize);
uint8_t* dest = paddedBuffer.get();
for (uint32_t z = 0; z < depth; ++z) {
for (uint32_t y = 0; y < height; ++y) {
std::memcpy(dest, dataBuff, bytesPerRow);
dest += paddedBytesPerRow;
dataBuff += bytesPerRow;
}
}
dataBuff = paddedBuffer.get();
dataSize = paddedBufferSize;
bytesPerRow = paddedBytesPerRow;
}
auto layout = wgpu::TexelCopyBufferLayout{ .bytesPerRow = bytesPerRow, .rowsPerImage = height };
mQueue.WriteTexture(&copyInfo, dataBuff, dataSize, &layout, &extent);
scheduleDestroy(std::move(p));
} }
void WebGPUDriver::setupExternalImage(void* image) { void WebGPUDriver::setupExternalImage(void* image) {
@@ -778,56 +870,74 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) { void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
assert_invariant(mCommandEncoder); assert_invariant(mCommandEncoder);
auto* renderTarget = handleCast<WGPURenderTarget>(rth); auto* renderTarget = handleCast<WGPURenderTarget>(rth);
// if (renderTarget == mDefaultRenderTarget) {
// FWGPU_LOGW << "Default render target" wgpu::RenderPassDescriptor renderPassDescriptor{};
// << utils::io::endl; wgpu::TextureView defaultColorView = nullptr;
// } else { wgpu::TextureView defaultDepthStencilView = nullptr;
// FWGPU_LOGW << "Non Default render target"
// << utils::io::endl; std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
// } uint32_t customColorViewCount = 0;
wgpu::RenderPassDescriptor renderPassDescriptor2; wgpu::TextureView customDepthView = nullptr;
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{ wgpu::TextureView customStencilView = nullptr;
.view = mSwapChain->getDepthTextureView(),
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH), if (renderTarget->isDefaultRenderTarget()) {
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH), assert_invariant(mSwapChain && mTextureView);
.depthClearValue = static_cast<float>(params.clearDepth), defaultColorView = mTextureView;
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0, defaultDepthStencilView = mSwapChain->getDepthTextureView();
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0
};
renderTarget->setUpRenderPassAttachments(renderPassDescriptor2, mTextureView, params);
renderPassDescriptor2.depthStencilAttachment = &depthStencilAttachment;
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
red -= 0.01;
} else { } else {
red = 1.0f; // Resolve views for custom render target
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
if (colorInfos[i].handle) {
auto* hwTexture = handleCast<WGPUTexture>(colorInfos[i].handle);
if (hwTexture) {
// TODO: Consider colorInfos[i].level and colorInfos[i].layer for view creation
// if WGPUTexture::getTexView() isn't sufficient or needs parameters.
customColorViews[customColorViewCount++] = hwTexture->getTexView();
}
}
}
const auto& depthInfo = renderTarget->getDepthAttachmentInfo();
if (depthInfo.handle) {
auto* hwTexture = handleCast<WGPUTexture>(depthInfo.handle);
if (hwTexture) {
customDepthView = hwTexture->getTexView();
}
}
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
if (stencilInfo.handle) {
// If depth and stencil use the same texture handle, this will re-cast but that's fine.
auto* hwTexture = handleCast<WGPUTexture>(stencilInfo.handle);
if (hwTexture) {
customStencilView = hwTexture->getTexView();
}
}
} }
assert_invariant(mTextureView);
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
.view = mTextureView,
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
.depthSlice = wgpu::kDepthSliceUndefined,
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
.clearValue = wgpu::Color{red, 0 , 0 , 1},
};
wgpu::RenderPassDescriptor renderPassDescriptor = { renderTarget->setUpRenderPassAttachments(renderPassDescriptor,
.colorAttachmentCount = 1, params,
.colorAttachments = &renderPassColorAttachment, defaultColorView,
.depthStencilAttachment = nullptr, defaultDepthStencilView,
.timestampWrites = nullptr, customColorViews.data(),
}; customColorViewCount,
customDepthView,
customStencilView);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor2); mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom, // Ensure viewport dimensions are positive
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far); uint32_t viewportWidth = params.viewport.width > 0 ? params.viewport.width : 1;
uint32_t viewportHeight = params.viewport.height > 0 ? params.viewport.height : 1;
mRenderPassEncoder.SetViewport(
static_cast<float>(params.viewport.left),
static_cast<float>(params.viewport.bottom),
static_cast<float>(viewportWidth),
static_cast<float>(viewportHeight),
params.depthRange.near,
params.depthRange.far);
} }
void WebGPUDriver::endRenderPass(int /* dummy */) { void WebGPUDriver::endRenderPass(int /* dummy */) {
@@ -916,6 +1026,14 @@ void WebGPUDriver::blit(
} }
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) { void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
// TODO Investigate implications of this hash more closely. Vulkan has a whole class
// VulkanPipelineCache to handle this, may be missing nuance
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
auto hash = pipleineStateHasher(pipelineState);
if(mPipelineMap.find(hash) != mPipelineMap.end()){
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
return;
}
const auto* program = handleCast<WGPUProgram>(pipelineState.program); const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program); assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr && assert_invariant(program->computeShaderModule == nullptr &&
@@ -952,7 +1070,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState, *vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(), pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat()); mSwapChain->getDepthFormat());
// TODO: uncomment once we have a valid pipeline to set mPipelineMap[hash] = pipeline;
mRenderPassEncoder.SetPipeline(pipeline); mRenderPassEncoder.SetPipeline(pipeline);
} }
@@ -968,18 +1086,12 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]); mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]);
} }
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->buffer, mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(),
renderPrimitive->indexBuffer->indexFormat); renderPrimitive->indexBuffer->indexFormat);
} }
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
// Calling DrawIndexed with "firstInstance = 0" results in a NON spinning triangle mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
// Calling DrawIndexed with "firstInstance = 1" results in a spinning triangle
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 1);
// Calling Draw with "firstInstance = 0" results in a NON spinning triangle
// Calling Draw with "firstInstance = 1" results in a spinning triangle
// mRenderPassEncoder.Draw(indexCount, instanceCount, 0, 1);
} }
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset, void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
@@ -1010,8 +1122,11 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
auto buffer = handleCast<WGPUBufferObject>(boh); auto buffer = handleCast<WGPUBufferObject>(boh);
if (!bindGroup->getIsLocked()) { if (!bindGroup->getIsLocked()) {
// TODO making assumptions that size and offset mean the same thing here. // TODO making assumptions that size and offset mean the same thing here.
FILAMENT_CHECK_PRECONDITION(offset % mMinUniformBufferOffsetAlignment == 0)
<< "Binding offset must be multiple of " << mMinUniformBufferOffsetAlignment
<< "But requested offset is " << offset;
wgpu::BindGroupEntry entry{ .binding = static_cast<uint32_t>(binding * 2), wgpu::BindGroupEntry entry{ .binding = static_cast<uint32_t>(binding * 2),
.buffer = buffer->buffer, .buffer = buffer->getBuffer(),
.offset = offset, .offset = offset,
.size = size }; .size = size };
bindGroup->addEntry(entry.binding, std::move(entry)); bindGroup->addEntry(entry.binding, std::move(entry));
@@ -1043,115 +1158,105 @@ void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh); const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
const auto wbg = bindGroup->lockAndReturn(mDevice); const auto wbg = bindGroup->lockAndReturn(mDevice);
assert_invariant(mRenderPassEncoder); assert_invariant(mRenderPassEncoder);
// TODO is this how we should be getting the dynamic offsets?
// should we add offsets for unused entries or is the input already have them?
// this implementation assumes unused entries are not provided, and adds dummy values.
// The count also includes unused entities, as not doing so produces errors
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets(); const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
uint32_t const* const dynamicOffsetsWithUnused = bindGroup->setDynamicOffsets(offsets.data()); mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, offsets.data());
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, dynamicOffsetsWithUnused);
} }
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) { void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
} }
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) { wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
wgpu::SamplerDescriptor desc; wgpu::SamplerDescriptor desc{};
desc.label = "TODO"; desc.label = "TODO";
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS); desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR); desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT); desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
switch (params.filterMag) { if (params.compareMode == SamplerCompareMode::COMPARE_TO_TEXTURE) {
case SamplerMagFilter::NEAREST: { switch (params.filterMag) {
desc.magFilter = wgpu::FilterMode::Nearest; case SamplerMagFilter::NEAREST: {
break; desc.magFilter = wgpu::FilterMode::Nearest;
break;
}
case SamplerMagFilter::LINEAR: {
desc.magFilter = wgpu::FilterMode::Linear;
break;
}
} }
case SamplerMagFilter::LINEAR: { switch (params.filterMin) {
desc.magFilter = wgpu::FilterMode::Linear; case SamplerMinFilter::NEAREST: {
break; desc.minFilter = wgpu::FilterMode::Nearest;
} desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
} break;
switch (params.filterMin) { }
case SamplerMinFilter::NEAREST: { case SamplerMinFilter::LINEAR: {
desc.minFilter = wgpu::FilterMode::Nearest; desc.minFilter = wgpu::FilterMode::Linear;
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
// suffice break;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest; }
break; case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
} desc.minFilter = wgpu::FilterMode::Nearest;
case SamplerMinFilter::LINEAR: { desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
desc.minFilter = wgpu::FilterMode::Linear; break;
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should }
// suffice case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest; break;
break; }
} case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: { desc.minFilter = wgpu::FilterMode::Nearest;
desc.minFilter = wgpu::FilterMode::Nearest; desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break; break;
}
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
} }
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: { switch (params.compareFunc) {
desc.minFilter = wgpu::FilterMode::Nearest; case SamplerCompareFunc::LE: {
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear; desc.compare = wgpu::CompareFunction::LessEqual;
break;
break; }
} case SamplerCompareFunc::GE: {
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: { desc.compare = wgpu::CompareFunction::GreaterEqual;
desc.minFilter = wgpu::FilterMode::Linear; break;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear; }
break; case SamplerCompareFunc::L: {
} desc.compare = wgpu::CompareFunction::Less;
} break;
switch (params.compareFunc) { }
case SamplerCompareFunc::LE: { case SamplerCompareFunc::G: {
desc.compare = wgpu::CompareFunction::LessEqual; desc.compare = wgpu::CompareFunction::Greater;
break; break;
} }
case SamplerCompareFunc::GE: { case SamplerCompareFunc::E: {
desc.compare = wgpu::CompareFunction::GreaterEqual; desc.compare = wgpu::CompareFunction::Equal;
break; break;
} }
case SamplerCompareFunc::L: { case SamplerCompareFunc::NE: {
desc.compare = wgpu::CompareFunction::Less; desc.compare = wgpu::CompareFunction::NotEqual;
break; break;
} }
case SamplerCompareFunc::G: { case SamplerCompareFunc::A: {
desc.compare = wgpu::CompareFunction::Greater; desc.compare = wgpu::CompareFunction::Always;
break; break;
} }
case SamplerCompareFunc::E: { case SamplerCompareFunc::N: {
desc.compare = wgpu::CompareFunction::Equal; desc.compare = wgpu::CompareFunction::Never;
break; break;
} }
case SamplerCompareFunc::NE: {
desc.compare = wgpu::CompareFunction::NotEqual;
break;
}
case SamplerCompareFunc::A: {
desc.compare = wgpu::CompareFunction::Always;
break;
}
case SamplerCompareFunc::N: {
desc.compare = wgpu::CompareFunction::Never;
break;
} }
} }
desc.maxAnisotropy = 1u << params.anisotropyLog2; desc.maxAnisotropy = 1u << params.anisotropyLog2;
// Unused: WGPU lodMinClamp/lodMaxClamp unnecessary
// Unused: Filament's compareMode, WGPU lodMinClamp/lodMaxClamp return mDevice.CreateSampler(&desc);
//TODO Once we can properly map to descriptorsetlayout use the sampler.
return mDevice.CreateSampler(/*&desc*/);
} }
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) { wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
switch (fWrapMode) { switch (fWrapMode) {
@@ -1171,5 +1276,4 @@ wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& f
return wgpu::AddressMode::Undefined; return wgpu::AddressMode::Undefined;
} }
} // namespace filament } // namespace filament
+3 -20
View File
@@ -58,32 +58,13 @@ private:
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final; [[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params); [[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage); [[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
template<typename GPUBufferObject>
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
<< "copyIntoBuffer called with a null buffer";
FILAMENT_CHECK_PRECONDITION(
bufferDescriptor.size + byteOffset <= gpuBufferObject->buffer.GetSize())
<< "Attempting to copy " << bufferDescriptor.size << " bytes into a buffer of size "
<< gpuBufferObject->buffer.GetSize() << " at offset " << byteOffset;
// TODO: All buffer objects are created with CopyDst usage.
// This may have some performance implications. That should be investigated later.
assert_invariant(gpuBufferObject->buffer.GetUsage() & wgpu::BufferUsage::CopyDst);
// WriteBuffer is an async call. But cpu buffer data is already written to the staging
// buffer on return from the WriteBuffer.
mQueue.WriteBuffer(gpuBufferObject->buffer, byteOffset, bufferDescriptor.buffer,
bufferDescriptor.size);
scheduleDestroy(std::move(bufferDescriptor));
}
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only // the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform // handled in the WebGPUPlatform
WebGPUPlatform& mPlatform; WebGPUPlatform& mPlatform;
wgpu::Adapter mAdapter = nullptr; wgpu::Adapter mAdapter = nullptr;
wgpu::Device mDevice = nullptr; wgpu::Device mDevice = nullptr;
uint32_t mMinUniformBufferOffsetAlignment;
wgpu::Queue mQueue = nullptr; wgpu::Queue mQueue = nullptr;
void* mNativeWindow = nullptr; void* mNativeWindow = nullptr;
WebGPUSwapChain* mSwapChain = nullptr; WebGPUSwapChain* mSwapChain = nullptr;
@@ -93,6 +74,8 @@ private:
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr; wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr; wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr; WGPURenderTarget* mDefaultRenderTarget = nullptr;
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
/* /*
* Driver interface * Driver interface
*/ */
+454 -247
View File
@@ -19,7 +19,8 @@
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h> #include <utils/BitmaskEnum.h>
#include <utils/Panic.h>
#include <private/backend/BackendUtils.h>
#include <webgpu/webgpu_cpp.h> #include <webgpu/webgpu_cpp.h>
#include <algorithm> #include <algorithm>
@@ -40,15 +41,6 @@ constexpr wgpu::BufferUsage getBufferObjectUsage(
} }
} }
wgpu::Buffer createBuffer(wgpu::Device const& device, wgpu::BufferUsage usage, uint32_t size,
char const* label) {
wgpu::BufferDescriptor descriptor{ .label = label,
.usage = usage,
.size = size,
.mappedAtCreation = false };
return device.CreateBuffer(&descriptor);
}
wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool normalized, bool integer) { wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool normalized, bool integer) {
using ElementType = filament::backend::ElementType; using ElementType = filament::backend::ElementType;
using VertexFormat = wgpu::VertexFormat; using VertexFormat = wgpu::VertexFormat;
@@ -76,7 +68,7 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
case ElementType::BYTE4: return VertexFormat::Snorm8x4; case ElementType::BYTE4: return VertexFormat::Snorm8x4;
case ElementType::UBYTE4: return VertexFormat::Unorm8x4; case ElementType::UBYTE4: return VertexFormat::Unorm8x4;
case ElementType::SHORT4: return VertexFormat::Snorm16x4; case ElementType::SHORT4: return VertexFormat::Snorm16x4;
case ElementType::USHORT4: return VertexFormat::Unorm8x4; case ElementType::USHORT4: return VertexFormat::Unorm16x4;
default: default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist."; FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
return VertexFormat::Float32x3; return VertexFormat::Float32x3;
@@ -119,10 +111,91 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
} }
} }
wgpu::StringView getUserTextureLabel(filament::backend::SamplerType target) {
// TODO will be helpful to get more useful info than this
using filament::backend::SamplerType;
switch (target) {
case SamplerType::SAMPLER_2D:
return "a_2D_user_texture";
case SamplerType::SAMPLER_2D_ARRAY:
return "a_2D_array_user_texture";
case SamplerType::SAMPLER_CUBEMAP:
return "a_cube_map_user_texture";
case SamplerType::SAMPLER_EXTERNAL:
return "an_external_user_texture";
case SamplerType::SAMPLER_3D:
return "a_3D_user_texture";
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return "a_cube_map_array_user_texture";
}
}
wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target) {
// TODO will be helpful to get more useful info than this
using filament::backend::SamplerType;
switch (target) {
case SamplerType::SAMPLER_2D:
return "a_2D_user_texture_view";
case SamplerType::SAMPLER_2D_ARRAY:
return "a_2D_array_user_texture_view";
case SamplerType::SAMPLER_CUBEMAP:
return "a_cube_map_user_texture_view";
case SamplerType::SAMPLER_EXTERNAL:
return "an_external_user_texture_view";
case SamplerType::SAMPLER_3D:
return "a_3D_user_texture_view";
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return "a_cube_map_array_user_texture_view";
}
}
}// namespace }// namespace
namespace filament::backend { namespace filament::backend {
void WGPUBufferBase::createBuffer(const wgpu::Device& device, wgpu::BufferUsage usage,
uint32_t size, const char* label) {
// Write size must be divisible by 4. If the whole buffer is written to as is common, so must
// the buffer size.
size += (4 - (size % 4)) % 4;
wgpu::BufferDescriptor descriptor{ .label = label,
.usage = usage,
.size = size,
.mappedAtCreation = false };
buffer = device.CreateBuffer(&descriptor);
}
void WGPUBufferBase::updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_t byteOffset,
wgpu::Queue queue) {
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
<< "copyIntoBuffer called with a null buffer";
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= buffer.GetSize())
<< "Attempting to copy " << bufferDescriptor.size << " bytes into a buffer of size "
<< buffer.GetSize() << " at offset " << byteOffset;
FILAMENT_CHECK_PRECONDITION(byteOffset % 4 == 0)
<< "Byte offset must be a multiple of 4 but is " << byteOffset;
// TODO: All buffer objects are created with CopyDst usage.
// This may have some performance implications. That should be investigated later.
assert_invariant(buffer.GetUsage() & wgpu::BufferUsage::CopyDst);
size_t remainder = bufferDescriptor.size % 4;
// WriteBuffer is an async call. But cpu buffer data is already written to the staging
// buffer on return from the WriteBuffer.
auto legalSize = bufferDescriptor.size - remainder;
queue.WriteBuffer(buffer, byteOffset, bufferDescriptor.buffer, legalSize);
if (remainder != 0) {
const uint8_t* remainderStart =
static_cast<const uint8_t*>(bufferDescriptor.buffer) + legalSize;
memcpy(mRemainderChunk.data(), remainderStart, remainder);
// Pad the remainder with zeros to ensure deterministic behavior, though GPU shouldn't
// access this
std::memset(mRemainderChunk.data() + remainder, 0, 4 - remainder);
queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4);
}
}
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes) AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount), : HwVertexBufferInfo(bufferCount, attributeCount),
@@ -165,9 +238,10 @@ WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attribut
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize, WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
uint32_t indexCount) uint32_t indexCount)
: buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index, : indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {
elementSize * indexCount, "index_buffer")), createBuffer(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {} elementSize * indexCount, "index_buffer");
}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCount, WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCount,
@@ -178,10 +252,10 @@ WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCo
WGPUBufferObject::WGPUBufferObject(wgpu::Device const& device, BufferObjectBinding bindingType, WGPUBufferObject::WGPUBufferObject(wgpu::Device const& device, BufferObjectBinding bindingType,
uint32_t byteCount) uint32_t byteCount)
: HwBufferObject(byteCount), : HwBufferObject(byteCount) {
buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType), createBuffer(device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType), byteCount,
byteCount, "buffer_object")), "buffer_object");
bufferObjectBinding(bindingType) {} }
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) { wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
wgpu::ShaderStage retStages = wgpu::ShaderStage::None; wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
@@ -201,14 +275,22 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
wgpu::Device const& device) { wgpu::Device const& device) {
assert_invariant(device); assert_invariant(device);
std::string baseLabel;
if (std::holds_alternative<utils::StaticString>(layout.label)) {
const auto& temp = std::get_if<utils::StaticString>(&layout.label);
baseLabel = temp->c_str();
} else if (std::holds_alternative<utils::CString>(layout.label)) {
const auto& temp = std::get_if<utils::CString>(&layout.label);
baseLabel = temp->c_str();
}
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for // TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value. // debugging. For now, hack an incrementing value.
static int layoutNum = 0; static int layoutNum = 0;
unsigned int samplerCount = unsigned int samplerCount =
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) { std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
return fEntry.type == DescriptorType::SAMPLER || return DescriptorSetLayoutBinding::isSampler(fEntry.type);
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
}); });
@@ -224,18 +306,44 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
entryInfo.binding = wEntry.binding; entryInfo.binding = wEntry.binding;
switch (fEntry.type) { switch (fEntry.type) {
case DescriptorType::SAMPLER_EXTERNAL: case DescriptorType::SAMPLER_2D_FLOAT:
case DescriptorType::SAMPLER: { case DescriptorType::SAMPLER_2D_INT:
case DescriptorType::SAMPLER_2D_UINT:
case DescriptorType::SAMPLER_2D_DEPTH:
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_ARRAY_INT:
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
case DescriptorType::SAMPLER_CUBE_FLOAT:
case DescriptorType::SAMPLER_CUBE_INT:
case DescriptorType::SAMPLER_CUBE_UINT:
case DescriptorType::SAMPLER_CUBE_DEPTH:
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
case DescriptorType::SAMPLER_3D_FLOAT:
case DescriptorType::SAMPLER_3D_INT:
case DescriptorType::SAMPLER_3D_UINT:
case DescriptorType::SAMPLER_2D_MS_FLOAT:
case DescriptorType::SAMPLER_2D_MS_INT:
case DescriptorType::SAMPLER_2D_MS_UINT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: {
auto& samplerEntry = wEntries.emplace_back(); auto& samplerEntry = wEntries.emplace_back();
auto& samplerEntryInfo = mBindGroupEntries.emplace_back(); auto& samplerEntryInfo = mBindGroupEntries.emplace_back();
samplerEntry.binding = fEntry.binding * 2 + 1; samplerEntry.binding = fEntry.binding * 2 + 1;
samplerEntryInfo.binding = samplerEntry.binding; samplerEntryInfo.binding = samplerEntry.binding;
samplerEntryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER;
samplerEntry.visibility = wEntry.visibility; samplerEntry.visibility = wEntry.visibility;
// We are simply hoping that undefined and defaults suffices here. wEntry.texture.multisampled = isMultiSampledTypeDescriptor(fEntry.type);
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering; // Example default // TODO: Set once we have the filtering values
wEntry.texture.sampleType = wgpu::TextureSampleType::Float; // Example default if (isDepthDescriptor(fEntry.type)) {
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW; samplerEntry.sampler.type = wgpu::SamplerBindingType::Comparison;
} else {
samplerEntry.sampler.type =
wgpu::SamplerBindingType::NonFiltering;
}
break; break;
} }
case DescriptorType::UNIFORM_BUFFER: { case DescriptorType::UNIFORM_BUFFER: {
@@ -246,7 +354,6 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
// TODO: Ideally we fill minBindingSize // TODO: Ideally we fill minBindingSize
break; break;
} }
case DescriptorType::INPUT_ATTACHMENT: { case DescriptorType::INPUT_ATTACHMENT: {
PANIC_POSTCONDITION("Input Attachment is not supported"); PANIC_POSTCONDITION("Input Attachment is not supported");
break; break;
@@ -255,10 +362,52 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
PANIC_POSTCONDITION("Shader storage is not supported"); PANIC_POSTCONDITION("Shader storage is not supported");
break; break;
} }
case DescriptorType::SAMPLER_EXTERNAL: {
PANIC_POSTCONDITION("External Sampler is not supported");
break;
}
}
if (isDepthDescriptor(fEntry.type))
{
wEntry.texture.sampleType = wgpu::TextureSampleType::Depth;
}
else if (isFloatDescriptor(fEntry.type))
{
// TODO: Set once we have the filtering values
wEntry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
}
else if (isIntDescriptor(fEntry.type))
{
wEntry.texture.sampleType = wgpu::TextureSampleType::Sint;
}
else if (isUnsignedIntDescriptor(fEntry.type))
{
wEntry.texture.sampleType = wgpu::TextureSampleType::Uint;
}
if (is3dTypeDescriptor(fEntry.type))
{
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e3D;
}
else if (is2dTypeDescriptor(fEntry.type))
{
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
}
else if (is2dArrayTypeDescriptor(fEntry.type))
{
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
}
else if (isCubeTypeDescriptor(fEntry.type))
{
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
}
else if (isCubeArrayTypeDescriptor(fEntry.type))
{
wEntry.texture.viewDimension = wgpu::TextureViewDimension::CubeArray;
} }
// fEntry.count is unused currently // fEntry.count is unused currently
} }
std::string label = "layout_"+ std::to_string(++layoutNum) ; std::string label = "layout_" + baseLabel + std::to_string(++layoutNum) ;
wgpu::BindGroupLayoutDescriptor layoutDescriptor{ wgpu::BindGroupLayoutDescriptor layoutDescriptor{
.label{label.c_str()}, // Use .c_str() if label needs to be const char* .label{label.c_str()}, // Use .c_str() if label needs to be const char*
.entryCount = wEntries.size(), .entryCount = wEntries.size(),
@@ -269,114 +418,26 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {} WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
wgpu::Buffer WebGPUDescriptorSet::sDummyUniformBuffer = nullptr;
wgpu::Texture WebGPUDescriptorSet::sDummyTexture = nullptr;
wgpu::TextureView WebGPUDescriptorSet::sDummyTextureView = nullptr;
wgpu::Sampler WebGPUDescriptorSet::sDummySampler = nullptr;
void WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(wgpu::Device const& device,
wgpu::TextureFormat aColorFormat) {
if (!sDummyUniformBuffer) {
wgpu::BufferDescriptor bufferDescriptor{
.label = "dummy_uniform_not_to_be_used",
.usage = wgpu::BufferUsage::Uniform,
.size = 4
};
sDummyUniformBuffer = device.CreateBuffer(&bufferDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
<< "Failed to create dummy uniform buffer?";
}
if (!sDummyTexture || !sDummyTextureView) {
wgpu::TextureDescriptor textureDescriptor{
.label = "dummy_texture_not_to_be_used",
.usage = wgpu::TextureUsage::TextureBinding,
.dimension = wgpu::TextureDimension::e2D,
.size = wgpu::Extent3D{ .width = 4, .height = 4, .depthOrArrayLayers = 1 },
.format = aColorFormat,
};
if (!sDummyTexture) {
sDummyTexture = device.CreateTexture(&textureDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy texture?";
}
if (!sDummyTextureView) {
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = "dummy_texture_view_not_to_be_used"
};
sDummyTextureView = sDummyTexture.CreateView(&textureViewDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
<< "Failed to create dummy texture view?";
}
}
if (!sDummySampler) {
wgpu::SamplerDescriptor samplerDescriptor{
.label = "dummy_sampler_not_to_be_used"
};
sDummySampler = device.CreateSampler(&samplerDescriptor);
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy sampler?";
}
}
std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedByBinding(
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&
bindGroupEntries) {
assert_invariant(WebGPUDescriptorSet::sDummyUniformBuffer &&
"Dummy uniform buffer must have been created before "
"creating dummy bind group entries.");
assert_invariant(
WebGPUDescriptorSet::sDummyTexture &&
"Dummy texture must have been created before creating dummy bind group entries.");
assert_invariant(
WebGPUDescriptorSet::sDummyTextureView &&
"Dummy texture view must have been created before creating dummy bind group entries.");
assert_invariant(
WebGPUDescriptorSet::sDummySampler &&
"Dummy sampler must have been created before creating dummy bind group entries.");
using filament::backend::WebGPUDescriptorSetLayout;
std::vector<wgpu::BindGroupEntry> entries;
entries.reserve(bindGroupEntries.size());
for (auto const& entryInfo: bindGroupEntries) {
auto& entry = entries.emplace_back();
entry.binding = entryInfo.binding;
switch (entryInfo.type) {
case WebGPUDescriptorSetLayout::BindGroupEntryType::UNIFORM_BUFFER:
entry.buffer = WebGPUDescriptorSet::sDummyUniformBuffer;
break;
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW:
entry.textureView = WebGPUDescriptorSet::sDummyTextureView;
break;
case WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER:
entry.sampler = WebGPUDescriptorSet::sDummySampler;
break;
}
}
std::sort(entries.begin(), entries.end(),
[](wgpu::BindGroupEntry const& a, wgpu::BindGroupEntry const& b) {
return a.binding < b.binding;
});
return entries;
}
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout, WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries) std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
: mLayout(layout), : mLayout(layout),
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) { mEntriesWithDynamicOffsetsCount(std::count_if(bindGroupEntries.begin(),
bindGroupEntries.end(), [](auto const& entry) { return entry.hasDynamicOffset; })) {
mEntries.resize(bindGroupEntries.size());
for (size_t i = 0; i < bindGroupEntries.size(); ++i) {
mEntries[i].binding = bindGroupEntries[i].binding;
}
// Establish the size of entries based on the layout. This should be reliable and efficient. // Establish the size of entries based on the layout. This should be reliable and efficient.
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size()); assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
for (size_t i = 0; i < mEntryIndexByBinding.size(); i++) { for (size_t i = 0; i < mEntryIndexByBinding.size(); i++) {
mEntryIndexByBinding[i] = INVALID_INDEX; mEntryIndexByBinding[i] = INVALID_INDEX;
} }
for (size_t index = 0; index < mEntriesSortedByBinding.size(); index++) { for (size_t index = 0; index < mEntries.size(); index++) {
wgpu::BindGroupEntry const& entry = mEntriesSortedByBinding[index]; wgpu::BindGroupEntry const& entry = mEntries[index];
assert_invariant(entry.binding < mEntryIndexByBinding.size()); assert_invariant(entry.binding < mEntryIndexByBinding.size());
mEntryIndexByBinding[entry.binding] = static_cast<uint8_t>(index); mEntryIndexByBinding[entry.binding] = static_cast<uint8_t>(index);
} }
for (auto const& entry : bindGroupEntries) {
if (entry.hasDynamicOffset) {
assert_invariant(entry.binding < mEntriesByBindingWithDynamicOffsets.size());
mEntriesByBindingWithDynamicOffsets[entry.binding] = true;
}
}
mDynamicOffsets.reserve(mEntriesSortedByBinding.size());
} }
WebGPUDescriptorSet::~WebGPUDescriptorSet() { WebGPUDescriptorSet::~WebGPUDescriptorSet() {
@@ -391,15 +452,15 @@ wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
// TODO label? Should we just copy layout label? // TODO label? Should we just copy layout label?
wgpu::BindGroupDescriptor desc{ wgpu::BindGroupDescriptor desc{
.layout = mLayout, .layout = mLayout,
.entryCount = mEntriesSortedByBinding.size(), .entryCount = mEntries.size(),
.entries = mEntriesSortedByBinding.data() .entries = mEntries.data()
}; };
mBindGroup = device.CreateBindGroup(&desc); mBindGroup = device.CreateBindGroup(&desc);
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?"; FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
// once we have created the bind group itself we should no longer need any other state // once we have created the bind group itself we should no longer need any other state
mLayout = nullptr; mLayout = nullptr;
mEntriesSortedByBinding.clear(); mEntries.clear();
mEntriesSortedByBinding.shrink_to_fit(); mEntries.shrink_to_fit();
return mBindGroup; return mBindGroup;
} }
@@ -417,85 +478,86 @@ void WebGPUDescriptorSet::addEntry(unsigned int index, wgpu::BindGroupEntry&& en
<< index; << index;
uint8_t entryIndex = mEntryIndexByBinding[index]; uint8_t entryIndex = mEntryIndexByBinding[index];
FILAMENT_CHECK_POSTCONDITION( FILAMENT_CHECK_POSTCONDITION(
entryIndex != INVALID_INDEX && entryIndex < mEntriesSortedByBinding.size()) entryIndex != INVALID_INDEX && entryIndex < mEntries.size())
<< "Invalid binding " << index; << "Invalid binding " << index;
entry.binding = index; entry.binding = index;
mEntriesSortedByBinding[entryIndex] = std::move(entry); mEntries[entryIndex] = std::move(entry);
mEntriesByBindingAdded[index] = true;
}
uint32_t const* WebGPUDescriptorSet::setDynamicOffsets(uint32_t const* offsets) {
// mDynamicOffsets already reserves enough memory for the number of entries in the set
mDynamicOffsets.clear();
// this implementation copies the offsets to mDynamicOffsets, but also adds values for
// unused entries TODO: is this necessary?
size_t inputIndex = 0;
size_t outputIndex = 0;
for (auto const& entry : mEntriesSortedByBinding) {
if (mEntriesByBindingWithDynamicOffsets[entry.binding]) {
if (mEntriesByBindingAdded[entry.binding]) {
mDynamicOffsets[outputIndex++] = offsets[inputIndex++];
} else {
mDynamicOffsets[outputIndex++] = 0; // dummy offset, as it was never added
}
}
}
return mDynamicOffsets.data();
} }
size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const { size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
return mEntriesByBindingWithDynamicOffsets.count(); return mEntriesWithDynamicOffsetsCount;
} }
// From createTextureR
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device device) noexcept { wgpu::Device const& device) noexcept {
assert_invariant(
// First the texture aspect samples == 1 ||
wgpu::TextureDescriptor desc; samples == 4 &&
"An invalid number of samples were requested, as WGPU requires the sample "
"count to either be 1 (no multisampling) or 4, at least as of April 2025 of "
"the spec. See https://www.w3.org/TR/webgpu/#texture-creation or "
"https://gpuweb.github.io/gpuweb/#multisample-state");
// First, the texture aspect, starting with the defaults/basic configuration
mUsage = fToWGPUTextureUsage(usage);
mFormat = fToWGPUTextureFormat(format);
mAspect = fToWGPUTextureViewAspect(usage, format);
mBlockWidth = filament::backend::getBlockWidth(format);
mBlockHeight = filament::backend::getBlockHeight(format);
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(target),
.usage = mUsage,
.dimension = target == SamplerType::SAMPLER_3D ? wgpu::TextureDimension::e3D
: wgpu::TextureDimension::e2D,
.size = { .width = width, .height = height, .depthOrArrayLayers = depth },
.format = mFormat,
.mipLevelCount = levels,
.sampleCount = samples,
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from
// Filament
.viewFormatCount = 0,
.viewFormats = nullptr,
};
// adjust for specific cases
switch (target) { switch (target) {
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
case SamplerType::SAMPLER_2D: case SamplerType::SAMPLER_2D:
mArrayLayerCount = 1;
break;
case SamplerType::SAMPLER_2D_ARRAY: case SamplerType::SAMPLER_2D_ARRAY:
// Should be safe to assume external is 2d mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
case SamplerType::SAMPLER_EXTERNAL: {
desc.dimension = wgpu::TextureDimension::e2D;
break; break;
} case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_3D: { textureDescriptor.size.depthOrArrayLayers = 6;
desc.dimension = wgpu::TextureDimension::e3D; mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
break;
case SamplerType::SAMPLER_EXTERNAL:
case SamplerType::SAMPLER_3D:
mArrayLayerCount = 1;
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
textureDescriptor.size.depthOrArrayLayers = depth * 6;
mArrayLayerCount = textureDescriptor.size.depthOrArrayLayers;
break; break;
}
} }
desc.size = { .width = width, .height = height, .depthOrArrayLayers = depth }; assert_invariant(textureDescriptor.format != wgpu::TextureFormat::Undefined &&
desc.format = fToWGPUTextureFormat(format); "Could not find appropriate WebGPU format");
assert_invariant(desc.format != wgpu::TextureFormat::Undefined); mTexture = device.CreateTexture(&textureDescriptor);
FILAMENT_CHECK_POSTCONDITION(mTexture)
// WGPU requires this to be true. Filament should comply << "Failed to create texture for " << textureDescriptor.label;
assert(samples == 1 || samples || 4); // Second, the texture view aspect
mTexView = makeTextureView(0, levels, target);
desc.sampleCount = samples;
desc.usage = fToWGPUTextureUsage(usage);
desc.mipLevelCount = levels;
// TODO Is this fine? Could do all-the-things, a naive mapping or get something from Filament
desc.viewFormats = nullptr;
texture = device.CreateTexture(&desc);
// TODO should a default levelCount be something other than 0? Sample count?
texView = makeTextureView(0, 1);
} }
// From createTextureViewR
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept { WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
texture = src->texture; mTexture = src->mTexture;
texView = makeTextureView(baseLevel, levelCount); mAspect = src->mAspect;
mBlockWidth = src->mBlockWidth;
mBlockHeight = src->mBlockHeight;
mTexView = makeTextureView(baseLevel, levelCount, target);
} }
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) { wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None; wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini // Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
@@ -543,8 +605,9 @@ wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage)
// PROTECTED // PROTECTED
return retUsage; return retUsage;
} }
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
switch (fUsage) { wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat) {
switch (fFormat) {
case filament::backend::TextureFormat::R8: case filament::backend::TextureFormat::R8:
return wgpu::TextureFormat::R8Unorm; return wgpu::TextureFormat::R8Unorm;
case filament::backend::TextureFormat::R8_SNORM: case filament::backend::TextureFormat::R8_SNORM:
@@ -783,80 +846,224 @@ wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsag
} }
} }
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, wgpu::TextureAspect WGPUTexture::fToWGPUTextureViewAspect(TextureUsage const& fUsage,
const uint8_t& levelCount) { TextureFormat const& fFormat) {
wgpu::TextureViewDescriptor desc;
desc.baseMipLevel = baseLevel;
desc.mipLevelCount = levelCount;
// baseArrayLayer is required, making a guess const bool isDepth = any(fUsage & TextureUsage::DEPTH_ATTACHMENT);
desc.baseArrayLayer = 0; const bool isStencil = any(fUsage & TextureUsage::STENCIL_ATTACHMENT);
// Have not found an analouge to aspect in other drivers, but ALL should be unrestrictive. const bool isColor = any(fUsage & TextureUsage::COLOR_ATTACHMENT);
// TODO Can we make this better? const bool isSample = (fUsage == TextureUsage::SAMPLEABLE);
desc.aspect = wgpu::TextureAspect::All;
// The rest of the properties should be fine to leave as default, using the texture params.
desc.label = "TODO";
desc.format = wgpu::TextureFormat::Undefined;
desc.dimension = wgpu::TextureViewDimension::Undefined;
desc.usage = wgpu::TextureUsage::None;
return texture.CreateView(&desc);
}
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
auto result = color[index];
if (index == 0 && defaultRenderTarget) {
if (isDepth && !isColor && !isStencil) {
return wgpu::TextureAspect::DepthOnly;
} }
return result; if (isStencil && !isColor && !isDepth) {
return wgpu::TextureAspect::StencilOnly;
}
if (fFormat == filament::backend::TextureFormat::DEPTH32F ||
fFormat == filament::backend::TextureFormat::DEPTH24 ||
fFormat == filament::backend::TextureFormat::DEPTH16) {
return wgpu::TextureAspect::DepthOnly;
}
if (fFormat == filament::backend::TextureFormat::STENCIL8) {
return wgpu::TextureAspect::StencilOnly;
}
if (fFormat == filament::backend::TextureFormat::DEPTH24_STENCIL8 ||
fFormat == filament::backend::TextureFormat::DEPTH32F_STENCIL8) {
if (isSample) {
return wgpu::TextureAspect::DepthOnly;
}
}
return wgpu::TextureAspect::All;
} }
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
SamplerType target) {
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = getUserTextureViewLabel(target),
.format = mFormat,
.baseMipLevel = baseLevel,
.mipLevelCount = levelCount,
// TODO: check if this baseArrayLayer assumption is correct
.baseArrayLayer = 0,
.arrayLayerCount = mArrayLayerCount,
.aspect = mAspect,
.usage = mUsage
};
switch (target) {
case SamplerType::SAMPLER_2D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
break;
case SamplerType::SAMPLER_2D_ARRAY:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray;
break;
case SamplerType::SAMPLER_CUBEMAP:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube;
break;
case SamplerType::SAMPLER_EXTERNAL:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
break;
case SamplerType::SAMPLER_3D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e3D;
break;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::CubeArray;
break;
}
wgpu::TextureView textureView = mTexture.CreateView(&textureViewDescriptor);
FILAMENT_CHECK_POSTCONDITION(textureView)
<< "Failed to create texture view " << textureViewDescriptor.label;
return textureView;
}
WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
const MRT& colorAttachmentsMRT,
const Attachment& depthAttachmentInfo,
const Attachment& stencilAttachmentInfo)
: HwRenderTarget(width, height),
defaultRenderTarget(false),
samples(samples),
mColorAttachments(colorAttachmentsMRT),
mDepthAttachment(depthAttachmentInfo),
mStencilAttachment(stencilAttachmentInfo) {
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
}
// Static helper to map MRT index to TargetBufferFlags
TargetBufferFlags WGPURenderTarget::getTargetBufferFlagsAt(int mrtIndex) {
if (mrtIndex < 0 || mrtIndex >= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT) {
return TargetBufferFlags::NONE;
}
// This mapping assumes TargetBufferFlags::COLOR_0, COLOR_1 etc. are contiguous
return static_cast<TargetBufferFlags>(
static_cast<uint32_t>(TargetBufferFlags::COLOR0) << mrtIndex);
}
// Corrected getLoadOperation
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params, wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
TargetBufferFlags buffer) { TargetBufferFlags bufferToOperateOn) {
auto clearFlags = params.flags.clear; if (any(params.flags.clear & bufferToOperateOn)) {
auto discardStartFlags = params.flags.discardStart;
if (any(clearFlags & buffer)) {
return wgpu::LoadOp::Clear;
} else if (any(discardStartFlags & buffer)) {
return wgpu::LoadOp::Clear; return wgpu::LoadOp::Clear;
} }
if (any(params.flags.discardStart & bufferToOperateOn)) {
return wgpu::LoadOp::Clear; // Or wgpu::LoadOp::Undefined if clear is not desired on discard
}
return wgpu::LoadOp::Load; return wgpu::LoadOp::Load;
} }
// Corrected getStoreOperation
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params, wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
TargetBufferFlags buffer) { TargetBufferFlags bufferToOperateOn) {
const auto discardEndFlags = params.flags.discardEnd; if (any(params.flags.discardEnd & bufferToOperateOn)) {
if (any(discardEndFlags & buffer)) {
return wgpu::StoreOp::Discard; return wgpu::StoreOp::Discard;
} }
return wgpu::StoreOp::Store; return wgpu::StoreOp::Store;
} }
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
wgpu::TextureView const& textureView, RenderPassParams const& params) { void WGPURenderTarget::setUpRenderPassAttachments(
// auto discardFlags = params.flags.discardEnd; wgpu::RenderPassDescriptor& descriptor,
// (void) discardFlags; RenderPassParams const& params,
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments; wgpu::TextureView const& defaultColorTextureView,
colorAttachments.clear(); wgpu::TextureView const& defaultDepthStencilTextureView,
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) { wgpu::TextureView const* customColorTextureViews,
// auto attachment = getDrawColorAttachment(i); uint32_t customColorTextureViewCount,
// if (attachment) { wgpu::TextureView const& customDepthTextureView,
wgpu::RenderPassColorAttachment colorAttachment; wgpu::TextureView const& customStencilTextureView) {
colorAttachment.view = textureView; mColorAttachmentDescriptors.clear();
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i)); mHasDepthStencilAttachment = false;
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a }; if (defaultRenderTarget) {
colorAttachments.emplace_back(colorAttachment); assert_invariant(defaultColorTextureView);
// } wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = defaultColorTextureView;
colorAttDesc.resolveTarget = nullptr;
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
if (defaultDepthStencilTextureView) {
mDepthStencilAttachmentDescriptor = {
.view = defaultDepthStencilTextureView,
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
};
mHasDepthStencilAttachment = true;
}
} else { // Custom Render Target
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
if (customColorTextureViews[i]) {
wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = customColorTextureViews[i];
colorAttDesc.resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
}
}
wgpu::TextureView combinedDsView = customDepthTextureView ? customDepthTextureView : customStencilTextureView;
if (combinedDsView) {
mDepthStencilAttachmentDescriptor = {};
mDepthStencilAttachmentDescriptor.view = combinedDsView;
if (customDepthTextureView) {
mDepthStencilAttachmentDescriptor.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue = static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
} else {
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.depthReadOnly = true;
}
if (customStencilTextureView) {
mDepthStencilAttachmentDescriptor.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilClearValue = params.clearStencil;
mDepthStencilAttachmentDescriptor.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
} else {
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilReadOnly = true;
}
mHasDepthStencilAttachment = true;
}
} }
descriptor.colorAttachments = colorAttachments.data();
descriptor.colorAttachmentCount = colorAttachments.size(); descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
descriptor.depthStencilAttachment = nullptr; descriptor.colorAttachments = mColorAttachmentDescriptors.data();
descriptor.timestampWrites = nullptr; descriptor.depthStencilAttachment = mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
// descriptor.sampleCount was removed from the core spec. If your webgpu.h still has it,
// and your Dawn version expects it, you might need to set it here based on this->samples.
// e.g., descriptor.sampleCount = this->samples;
} }
math::uint2 WGPURenderTarget::getAttachmentSize() const noexcept {
if (!defaultRenderTarget) {
return {width, height};
}
// For default RT, size is dynamic and usually obtained from the swapchain.
// The caller (WebGPUDriver::beginRenderPass) should know the current swapchain size.
return {0,0};
}
}// namespace filament::backend }// namespace filament::backend
+88 -81
View File
@@ -28,7 +28,6 @@
#include <webgpu/webgpu_cpp.h> #include <webgpu/webgpu_cpp.h>
#include <array> #include <array>
#include <bitset>
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
@@ -44,7 +43,6 @@ public:
std::vector<wgpu::ConstantEntry> constants; std::vector<wgpu::ConstantEntry> constants;
}; };
struct WGPUBufferObject;
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each // VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
// VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout // VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout
@@ -74,8 +72,8 @@ public:
private: private:
// TODO: can we do better in terms on heap management. // TODO: can we do better in terms on heap management.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {}; std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {}; std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{};
}; };
struct WGPUVertexBuffer : public HwVertexBuffer { struct WGPUVertexBuffer : public HwVertexBuffer {
@@ -86,33 +84,37 @@ struct WGPUVertexBuffer : public HwVertexBuffer {
utils::FixedCapacityVector<wgpu::Buffer> buffers; utils::FixedCapacityVector<wgpu::Buffer> buffers;
}; };
struct WGPUIndexBuffer : public HwIndexBuffer { class WGPUBufferBase {
public:
void createBuffer(wgpu::Device const& device, wgpu::BufferUsage usage, uint32_t size,
char const* label);
void updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_t byteOffset,
wgpu::Queue queue);
const wgpu::Buffer& getBuffer() const { return buffer; }
protected:
wgpu::Buffer buffer;
private:
// 4 bytes to hold any extra chunk we need.
std::array<uint8_t,4> mRemainderChunk;
};
class WGPUIndexBuffer : public HwIndexBuffer, public WGPUBufferBase {
public:
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize, WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
uint32_t indexCount); uint32_t indexCount);
wgpu::Buffer buffer;
wgpu::IndexFormat indexFormat; wgpu::IndexFormat indexFormat;
}; };
struct WGPUBufferObject : HwBufferObject { class WGPUBufferObject : public HwBufferObject, public WGPUBufferBase {
public:
WGPUBufferObject(wgpu::Device const &device, BufferObjectBinding bindingType, uint32_t byteCount); WGPUBufferObject(wgpu::Device const &device, BufferObjectBinding bindingType, uint32_t byteCount);
wgpu::Buffer buffer = nullptr;
const BufferObjectBinding bufferObjectBinding;
}; };
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout { class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
public: public:
enum class BindGroupEntryType : uint8_t {
UNIFORM_BUFFER,
TEXTURE_VIEW,
SAMPLER
};
struct BindGroupEntryInfo final { struct BindGroupEntryInfo final {
uint8_t binding = 0; uint8_t binding = 0;
BindGroupEntryType type = BindGroupEntryType::UNIFORM_BUFFER;
bool hasDynamicOffset = false; bool hasDynamicOffset = false;
}; };
@@ -125,7 +127,7 @@ public:
private: private:
// TODO: If this is useful elsewhere, remove it from this class // TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant // Convert Filament Shader Stage Flags bitmask to webgpu equivalent
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags); static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
std::vector<BindGroupEntryInfo> mBindGroupEntries; std::vector<BindGroupEntryInfo> mBindGroupEntries;
wgpu::BindGroupLayout mLayout; wgpu::BindGroupLayout mLayout;
@@ -133,8 +135,6 @@ private:
class WebGPUDescriptorSet final : public HwDescriptorSet { class WebGPUDescriptorSet final : public HwDescriptorSet {
public: public:
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
wgpu::TextureFormat aColorFormat);
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout, WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries); std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
@@ -142,30 +142,15 @@ public:
wgpu::BindGroup lockAndReturn(wgpu::Device const&); wgpu::BindGroup lockAndReturn(wgpu::Device const&);
void addEntry(unsigned int index, wgpu::BindGroupEntry&& entry); void addEntry(unsigned int index, wgpu::BindGroupEntry&& entry);
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; } [[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const; [[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
private: private:
static wgpu::Buffer sDummyUniformBuffer;
static wgpu::Texture sDummyTexture;
static wgpu::TextureView sDummyTextureView;
static wgpu::Sampler sDummySampler;
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);
// TODO: Consider storing what we used to make the layout. However we need to essentially
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
// there isn't a mismatch may be easiest.
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
wgpu::BindGroupLayout mLayout = nullptr; wgpu::BindGroupLayout mLayout = nullptr;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1; static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {}; std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding{};
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding; std::vector<wgpu::BindGroupEntry> mEntries;
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets {}; const size_t mEntriesWithDynamicOffsetsCount;
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded {};
std::vector<uint32_t> mDynamicOffsets;
wgpu::BindGroup mBindGroup = nullptr; wgpu::BindGroup mBindGroup = nullptr;
}; };
@@ -173,24 +158,37 @@ class WGPUTexture : public HwTexture {
public: public:
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device device) noexcept; wgpu::Device const& device) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept; WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
wgpu::TextureAspect getAspect() const { return mAspect; }
size_t getBlockWidth() const { return mBlockWidth; }
size_t getBlockHeight() const { return mBlockHeight; }
const wgpu::Texture& getTexture() const { return texture; } [[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
const wgpu::TextureView& getTexView() const { return texView; } [[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
// Public to allow checking for support of a texture format static wgpu::TextureFormat fToWGPUTextureFormat(
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage); filament::backend::TextureFormat const& fFormat);
static wgpu::TextureAspect fToWGPUTextureViewAspect(
filament::backend::TextureUsage const& fUsage,
filament::backend::TextureFormat const& fFormat);
private: private:
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount); wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
SamplerType target);
// CreateTextureR has info for a texture and sampler. Texture Views are needed for binding, // CreateTextureR has info for a texture and sampler. Texture Views are needed for binding,
// along with a sampler Current plan: Inherit the sampler and Texture to always exist (It is a // along with a sampler Current plan: Inherit the sampler and Texture to always exist (It is a
// ref counted pointer) when making views. View is optional // ref counted pointer) when making views. View is optional
wgpu::Texture texture = nullptr; wgpu::Texture mTexture = nullptr;
wgpu::TextureView texView = nullptr; wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage); wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
uint32_t mArrayLayerCount = 1;
wgpu::TextureView mTexView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
size_t mBlockWidth;
size_t mBlockHeight;
}; };
struct WGPURenderPrimitive : public HwRenderPrimitive { struct WGPURenderPrimitive : public HwRenderPrimitive {
@@ -199,57 +197,66 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPUIndexBuffer* indexBuffer = nullptr; WGPUIndexBuffer* indexBuffer = nullptr;
}; };
class WGPURenderTarget : public HwRenderTarget { class WGPURenderTarget : public HwRenderTarget {
public: public:
class Attachment { using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
public:
friend class WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->getTexture()),
mWGPUTexture(gpuTexture) {}
operator bool() const {
return mWGPUTexture != nullptr;
}
uint8_t level = 0;
uint16_t layer = 0;
private:
wgpu::Texture texture = nullptr;
WGPUTexture* mWGPUTexture = nullptr;
};
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples, WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]); const MRT& colorAttachments,
const Attachment& depthAttachment,
const Attachment& stencilAttachment);
// Default constructor for the default render target
WGPURenderTarget() WGPURenderTarget()
: HwRenderTarget(0, 0), : HwRenderTarget(0, 0),
defaultRenderTarget(true) {} defaultRenderTarget(true),
samples(1) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor, // Updated signature: takes resolved views for custom RTs, and default views for default RT
wgpu::TextureView const& textureView, RenderPassParams const& params); void setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params,
// For default render target:
wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
// For custom render targets:
wgpu::TextureView const* customColorTextureViews, // Array of views
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView);
math::uint2 getAttachmentSize() noexcept;
math::uint2 getAttachmentSize() const noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; } bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; } uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index); // Accessors for the driver to get stored attachment info
Attachment getReadColorAttachment(size_t index); const MRT& getColorAttachmentInfos() const { return mColorAttachments; }
const Attachment& getDepthAttachmentInfo() const { return mDepthAttachment; }
const Attachment& getStencilAttachmentInfo() const { return mStencilAttachment; }
// Static helpers for load/store operations
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer); static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer); static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
private: private:
// Helper to map MRT index to TargetBufferFlags
static TargetBufferFlags getTargetBufferFlagsAt(int mrtIndex);
bool defaultRenderTarget = false; bool defaultRenderTarget = false;
uint8_t samples = 1; uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; MRT mColorAttachments{};
math::uint2 attachmentSize = {}; Attachment mDepthAttachment{};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments {}; Attachment mStencilAttachment{};
};
// Cached descriptors for the render pass
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
bool mHasDepthStencilAttachment = false;
};
}// namespace filament::backend }// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H #endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
+54 -21
View File
@@ -32,6 +32,7 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND
using namespace filament; using namespace filament;
using namespace filament::backend; using namespace filament::backend;
using namespace filament::math;
#ifndef FILAMENT_IOS #ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h> #include <imageio/ImageEncoder.h>
@@ -45,6 +46,7 @@ namespace test {
Backend BackendTest::sBackend = Backend::NOOP; Backend BackendTest::sBackend = Backend::NOOP;
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER; OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
bool BackendTest::sIsMobilePlatform = false; bool BackendTest::sIsMobilePlatform = false;
std::vector<std::string> BackendTest::sFailedImages;
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) { void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
sBackend = backend; sBackend = backend;
@@ -63,11 +65,12 @@ BackendTest::~BackendTest() {
flushAndWait(); flushAndWait();
mImageExpectations->evaluate(); mImageExpectations->evaluate();
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen. // Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
if (sBackend == Backend::OPENGL) { if (sBackend != Backend::OPENGL) {
return; driver->terminate();
delete driver;
} }
driver->terminate();
delete driver; recordFailedImages();
} }
void BackendTest::initializeDriver() { void BackendTest::initializeDriver() {
@@ -104,16 +107,35 @@ Handle<HwSwapChain> BackendTest::createSwapChain() {
return getDriverApi().createSwapChain(view.ptr, 0); return getDriverApi().createSwapChain(view.ptr, 0);
} }
void BackendTest::fullViewport(RenderPassParams& params) { PipelineState BackendTest::getColorWritePipelineState() {
fullViewport(params.viewport); PipelineState result;
result.rasterState.colorWrite = true;
result.rasterState.depthWrite = false;
result.rasterState.depthFunc = RasterState::DepthFunc::A;
return result;
} }
void BackendTest::fullViewport(Viewport& viewport) { filament::backend::Viewport BackendTest::getFullViewport() const {
const NativeView& view = getNativeView(); const NativeView& view = getNativeView();
viewport.left = 0; return Viewport {
viewport.bottom = 0; .left = 0,
viewport.width = view.width; .bottom = 0,
viewport.height = view.height; .width = static_cast<uint32_t>(view.width),
.height = static_cast<uint32_t>(view.height)
};
}
filament::backend::RenderPassParams BackendTest::getClearColorRenderPass(float4 color) {
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.clearColor = color;
return params;
}
filament::backend::RenderPassParams BackendTest::getNoClearRenderPass() {
return RenderPassParams{};
} }
void BackendTest::renderTriangle( void BackendTest::renderTriangle(
@@ -121,14 +143,9 @@ void BackendTest::renderTriangle(
Handle<filament::backend::HwRenderTarget> renderTarget, Handle<filament::backend::HwRenderTarget> renderTarget,
Handle<filament::backend::HwSwapChain> swapChain, Handle<filament::backend::HwSwapChain> swapChain,
Handle<filament::backend::HwProgram> program) { Handle<filament::backend::HwProgram> program) {
RenderPassParams params = {}; RenderPassParams params = getClearColorRenderPass();
fullViewport(params);
params.flags.clear = TargetBufferFlags::COLOR;
params.clearColor = {0.f, 0.f, 1.f, 1.f};
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512; params.viewport.width = 512;
params.viewport.height = 512;
renderTriangle(pipelineLayout, renderTarget, swapChain, program, params); renderTriangle(pipelineLayout, renderTarget, swapChain, program, params);
} }
@@ -167,8 +184,24 @@ bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
return sOperatingSystem == operatingSystem; return sOperatingSystem == operatingSystem;
} }
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) { void BackendTest::markImageAsFailure(std::string failedImageName) {
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend); sFailedImages.emplace_back(std::move(failedImageName));
}
void BackendTest::recordFailedImages() {
if (!sFailedImages.empty()) {
std::string failedImages;
for (auto& failedTestImageName: sFailedImages) {
if (failedImages.empty()) {
failedImages = failedTestImageName;
} else {
failedImages.append(",");
failedImages.append(failedTestImageName);
}
}
RecordProperty("FailedImages", failedImages);
}
sFailedImages.clear();
} }
class Environment : public ::testing::Environment { class Environment : public ::testing::Environment {
+17 -4
View File
@@ -38,6 +38,9 @@ public:
static OperatingSystem sOperatingSystem; static OperatingSystem sOperatingSystem;
static bool sIsMobilePlatform; static bool sIsMobilePlatform;
// Takes the name of the image that wasn't correct, without the .png suffix
static void markImageAsFailure(std::string failedImageName);
protected: protected:
BackendTest(); BackendTest();
@@ -49,9 +52,14 @@ protected:
filament::backend::Handle<filament::backend::HwSwapChain> createSwapChain(); filament::backend::Handle<filament::backend::HwSwapChain> createSwapChain();
// Helper methods to set the viewport to the full extent of the swap chain. static filament::backend::PipelineState getColorWritePipelineState();
static void fullViewport(filament::backend::RenderPassParams& params);
static void fullViewport(filament::backend::Viewport& viewport); // Gets the full back buffer's viewport
filament::backend::Viewport getFullViewport() const;
// If color is unset this defaults to using opaque cyan
static filament::backend::RenderPassParams getClearColorRenderPass(
filament::math::float4 color = filament::math::float4(0, 1, 1, 1));
static filament::backend::RenderPassParams getNoClearRenderPass();
void renderTriangle( void renderTriangle(
filament::backend::PipelineLayout const& pipelineLayout, filament::backend::PipelineLayout const& pipelineLayout,
@@ -73,8 +81,13 @@ protected:
static bool matchesEnvironment(Backend backend); static bool matchesEnvironment(Backend backend);
static bool matchesEnvironment(OperatingSystem operatingSystem); static bool matchesEnvironment(OperatingSystem operatingSystem);
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
private: private:
// Adds all the images that failed an ImageExpectation to the XML metadata for the current tests
// case. Add --gtest_output=xml as a command line argument to generate a test_detail.xml file in
// the directory where the tests are run.
static void recordFailedImages();
static std::vector<std::string> sFailedImages;
filament::backend::Driver* driver = nullptr; filament::backend::Driver* driver = nullptr;
filament::backend::CommandBufferQueue commandBufferQueue; filament::backend::CommandBufferQueue commandBufferQueue;
+18 -1
View File
@@ -21,6 +21,7 @@
#include "utils/Hash.h" #include "utils/Hash.h"
#include <fstream> #include <fstream>
#include "BackendTest.h"
#include "backend/PixelBufferDescriptor.h" #include "backend/PixelBufferDescriptor.h"
#include "private/backend/DriverApi.h" #include "private/backend/DriverApi.h"
@@ -32,6 +33,8 @@
#endif #endif
namespace test {
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName, ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
uint32_t expectedHash, bool isSrgb) uint32_t expectedHash, bool isSrgb)
: mWidth(width), : mWidth(width),
@@ -80,6 +83,10 @@ std::string ScreenshotParams::expectedFilePath() const {
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName()); return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
} }
const std::string ScreenshotParams::filePrefix() const {
return mFileName;
}
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber, ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
filament::backend::DriverApi& api, ScreenshotParams params, filament::backend::DriverApi& api, ScreenshotParams params,
filament::backend::RenderTargetHandle renderTarget) filament::backend::RenderTargetHandle renderTarget)
@@ -113,7 +120,11 @@ void ImageExpectation::compareImage() const {
#ifndef FILAMENT_IOS #ifndef FILAMENT_IOS
LoadedPng loadedImage(mParams.expectedFilePath()); LoadedPng loadedImage(mParams.expectedFilePath());
uint32_t loadedImageHash = loadedImage.hash(); uint32_t loadedImageHash = loadedImage.hash();
EXPECT_THAT(actualHash, testing::Eq(loadedImageHash)) << mParams.expectedFileName(); auto compareToImageMatcher = testing::Eq(loadedImageHash);
if (!testing::Matches(compareToImageMatcher)(actualHash)) {
BackendTest::markImageAsFailure(mParams.filePrefix());
}
EXPECT_THAT(actualHash, compareToImageMatcher) << mParams.expectedFileName();
#endif #endif
// For builds that can't load PNGs (currently iOS only) use the expected hash. // For builds that can't load PNGs (currently iOS only) use the expected hash.
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash())) << mParams.expectedFileName(); EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash())) << mParams.expectedFileName();
@@ -200,6 +211,10 @@ uint32_t RenderTargetDump::hash() const {
return mInternal->hash(); return mInternal->hash();
} }
const std::vector<unsigned char>& RenderTargetDump::bytes() const {
return mInternal->bytes;
}
bool RenderTargetDump::bytesFilled() const { bool RenderTargetDump::bytesFilled() const {
return mInternal->bytesFilled; return mInternal->bytesFilled;
} }
@@ -237,3 +252,5 @@ uint32_t LoadedPng::hash() const {
const std::vector<unsigned char>& LoadedPng::bytes() const { const std::vector<unsigned char>& LoadedPng::bytes() const {
return mBytes; return mBytes;
} }
} // namespace test
+11
View File
@@ -35,6 +35,8 @@ do { \
screenshotParams); \ screenshotParams); \
} while (0) } while (0)
namespace test {
/** /**
* Stores user-provided configuration values for an image expectation * Stores user-provided configuration values for an image expectation
*/ */
@@ -54,6 +56,7 @@ public:
static std::string expectedDirectoryPath(); static std::string expectedDirectoryPath();
std::string expectedFileName() const; std::string expectedFileName() const;
std::string expectedFilePath() const; std::string expectedFilePath() const;
const std::string filePrefix() const;
private: private:
int mWidth; int mWidth;
@@ -82,6 +85,12 @@ public:
* @return The hash of the stored bytes. * @return The hash of the stored bytes.
*/ */
uint32_t hash() const; uint32_t hash() const;
/**
* Gets the bytes of the render target. The hash should usually be preferable for comparisons
* but this is available for debugging.
* @return The stored bytes.
*/
const std::vector<unsigned char>& bytes() const;
/** /**
* Thread safe as this is backed by an atomic. * Thread safe as this is backed by an atomic.
* Once this returns true it will never return false. * Once this returns true it will never return false.
@@ -153,4 +162,6 @@ private:
std::vector<std::unique_ptr<ImageExpectation>> mExpectations; std::vector<std::unique_ptr<ImageExpectation>> mExpectations;
}; };
} // namespace test
#endif //TNT_IMAGE_EXPECTATIONS_H #endif //TNT_IMAGE_EXPECTATIONS_H
+14 -5
View File
@@ -56,26 +56,35 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
mProgram = cleanup.add(api.createProgram(std::move(prog))); mProgram = cleanup.add(api.createProgram(std::move(prog)));
if (!kLayouts.empty()) { if (!kLayouts.empty()) {
mDescriptorSetLayout = mDescriptorSetLayout = cleanup.add(
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts })); api.createDescriptorSetLayout(DescriptorSetLayout{ .bindings = kLayouts }));
} }
} }
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const { DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout)); return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
} }
filament::backend::ProgramHandle Shader::getProgram() const { ProgramHandle Shader::getProgram() const {
assert(mProgram); assert(mProgram);
EXPECT_THAT(mProgram, ::testing::IsTrue()) EXPECT_THAT(mProgram, ::testing::IsTrue())
<< "Shader program accessed despite being null."; << "Shader program accessed despite being null.";
return mProgram; return mProgram;
} }
filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const { DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const {
EXPECT_THAT(mDescriptorSetLayout, ::testing::IsTrue()) EXPECT_THAT(mDescriptorSetLayout, ::testing::IsTrue())
<< "Shader descriptor set layout accessed despite being null."; << "Shader descriptor set layout accessed despite being null.";
return mDescriptorSetLayout; return mDescriptorSetLayout;
} }
void Shader::addProgramToPipelineState(PipelineState& state) const {
state.program = getProgram();
// In case another shader was set first, clear the set layout and then set this shader's values.
state.pipelineLayout.setLayout = PipelineLayout::SetLayout();
if (mDescriptorSetLayout) {
state.pipelineLayout.setLayout[0] = { getDescriptorSetLayout() };
}
}
} // namespace test } // namespace test
+2
View File
@@ -90,6 +90,8 @@ public:
filament::backend::ProgramHandle getProgram() const; filament::backend::ProgramHandle getProgram() const;
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const; filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
void addProgramToPipelineState(filament::backend::PipelineState& state) const;
filament::backend::DescriptorSetHandle createDescriptorSet( filament::backend::DescriptorSetHandle createDescriptorSet(
filament::backend::DriverApi& api) const; filament::backend::DriverApi& api) const;
+13 -2
View File
@@ -17,7 +17,6 @@
#include "ShaderGenerator.h" #include "ShaderGenerator.h"
#include <GlslangToSpv.h> #include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv_glsl.hpp> #include <spirv_glsl.hpp>
#include <spirv_msl.hpp> #include <spirv_msl.hpp>
@@ -119,7 +118,7 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
} else if (backend == Backend::VULKAN) { } else if (backend == Backend::VULKAN) {
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n"); shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
} else if (backend == Backend::WEBGPU) { } else if (backend == Backend::WEBGPU) {
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n"); shader.insert(pos, "#define TARGET_WEBGPU_ENVIRONMENT\n");
} }
const char* shaderCString = shader.c_str(); const char* shaderCString = shader.c_str();
@@ -187,4 +186,16 @@ Program ShaderGenerator::getProgram(filament::backend::DriverApi&) noexcept {
return program; return program;
} }
Program ShaderGenerator::getProgramWithPushConstants(filament::backend::DriverApi&,
std::array<PushConstants, filament::backend::Program::SHADER_TYPE_COUNT> constants) {
Program program;
program.shaderLanguage(mShaderLanguage);
program.shader(ShaderStage::VERTEX, mVertexBlob.data(), mVertexBlob.size());
program.shader(ShaderStage::FRAGMENT, mFragmentBlob.data(), mFragmentBlob.size());
for (auto const stage : {ShaderStage::VERTEX, ShaderStage::FRAGMENT }) {
program.pushConstants(stage, constants[uint8_t(stage)]);
}
return program;
}
} // namespace test } // namespace test
+4
View File
@@ -48,6 +48,10 @@ public:
filament::backend::Program getProgram(filament::backend::DriverApi&) noexcept; filament::backend::Program getProgram(filament::backend::DriverApi&) noexcept;
using PushConstants = utils::FixedCapacityVector<filament::backend::Program::PushConstant>;
filament::backend::Program getProgramWithPushConstants(filament::backend::DriverApi&,
std::array<PushConstants, filament::backend::Program::SHADER_TYPE_COUNT> constants);
private: private:
using ShaderStage = filament::backend::ShaderStage; using ShaderStage = filament::backend::ShaderStage;
+1 -1
View File
@@ -184,7 +184,7 @@ std::vector<UniformConfig> GetUniformConfig(ShaderUniformType type) {
"backend_test", "test_tex", 0, "backend_test", "test_tex", 0,
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false }; SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
return {{ return {{
"test_tex", DescriptorType::SAMPLER, samplerInfo "test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
}}; }};
} }
default: default:
+12 -1
View File
@@ -32,13 +32,24 @@ do {
} \ } \
} while (false) } while (false)
#define NONFATAL_FAIL_IF(skipEnvironment, rationale) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
ADD_FAILURE() \
<< "Failing test as the " << skip.describe() << "\n" \
<< " This test has a known failure where " \
<< rationale; \
} \
} while (false)
#define FAIL_IF(skipEnvironment, rationale) \ #define FAIL_IF(skipEnvironment, rationale) \
do { \ do { \
SkipEnvironment skip(skipEnvironment); \ SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \ if (skip.matches()) { \
GTEST_FAIL() \ GTEST_FAIL() \
<< "Failing test as the " << skip.describe() << "\n" \ << "Failing test as the " << skip.describe() << "\n" \
<< " This test should be able to succeed but it needs to fail early because" \ << " This test should be able to succeed but it needs to fail early because " \
<< rationale; \ << rationale; \
} \ } \
} while (false) } while (false)
@@ -55,6 +55,8 @@ public:
void updateIndices(const index_type* indices) noexcept; void updateIndices(const index_type* indices) noexcept;
void updateIndices(const index_type* indices, int count, int offset) noexcept; void updateIndices(const index_type* indices, int count, int offset) noexcept;
VertexInfoHandle getVertexBufferInfo() const { return mVertexBufferInfo; }
private: private:
size_t mVertexCount = 3; size_t mVertexCount = 3;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Some files were not shown because too many files have changed in this diff Show More