Compare commits

...

74 Commits

Author SHA1 Message Date
bridgewaterrobbie
3e1499d1a1 Sampler creation as part of the driver 2025-04-30 12:14:05 -04:00
bridgewaterrobbie
586c82ef10 Add comment explaining public texture format enum conversion 2025-04-30 12:13:32 -04:00
bridgewaterrobbie
7ece76a679 Add a required feature for Filament to requestDevice 2025-04-30 11:46:47 -04:00
bridgewaterrobbie
9959b104f9 Fix mistake- entries size is smaller than the max index, need to fill one at a time with emplace back 2025-04-30 11:44:56 -04:00
bridgewaterrobbie
f024214621 Texture and Texture View creation 2025-04-30 11:44:56 -04:00
Andy Hovingh
8a3bf04eef webgpu: initial render pipeline creation 2025-04-29 16:22:09 -05:00
Matthew Hoffman
44840a481e Working backend tests on MacOS for Metal as well as OpenGL (#8662)
BUGS=[409099263]
2025-04-29 12:21:36 -07:00
Powei Feng
8070643ba5 ci: refactor to centralize version definitions (#8663)
- move CI only prerequisites to github actions
 - Add linux, mac prereq actions
 - Add an action for indicating dependency versions
 - Move ninja installation into its own script
2025-04-29 10:02:51 -07:00
Doris Wu
0c33f9f2a3 [fgviewer] Add graphviz view mode to the web page (#8673)
* Export graphviz data to the webview

* Fix some errors

* Fix colors

* Only show toggle when view selected

* Update

* Merge graphviz data into json string

* Mark export_graphviz function as const

* Set SERVE_FROM_SOURCE_TREE to 0

* Remove unnecessary import

* Refactor

* Refactor

* New line when early return
2025-04-29 02:39:37 +00:00
Juan Caldas
db72bd024b wgpu: Update callback mode to WaitAnyOnly (#8677) 2025-04-28 20:29:38 -04:00
Powei Feng
7c6b470650 Improve code-correctness script (#8659)
- Sort failures alphabetically
- Add way to suppress error when necessary
- Add way to run test on a subset of files for testing
- Fix case where number of files is less than number of workers
2025-04-28 17:00:40 +00:00
Syed Idris Shah
68a99ef54c metal driver cleanup
Remove the unused functions and varibles from render primitve and target api's.
2025-04-25 23:49:12 -04:00
Powei Feng
39fabdbc3b vk: fix exception-escape failures (#8656)
And one drive-by fix to VulkanYcbcrConversionCache.h
2025-04-25 23:03:47 +00:00
KeremTAN
83967fdb1b std type trait specialization of is_arithmetic and is_floating_type removed 2025-04-25 12:14:26 -07:00
KeremTAN
b88c2d80a4 Fix Clang 20 compile error: invalid std type trait specializations 2025-04-25 12:14:26 -07:00
bridgewaterrobbie
4a36193f04 Uncomment updateDescriptorSetBuffer. What we have may actually suffice as-is, and no longer crashes 2025-04-25 14:28:39 -04:00
bridgewaterrobbie
8df19cb6b2 Indicate we do not support texture swizzling 2025-04-25 14:28:39 -04:00
bridgewaterrobbie
783c1c9c83 Upload wgpu cpp header to easily reference in discussions 2025-04-25 12:11:42 -04:00
Syed Idris Shah
a92771c5ef webgpu: create and bind render primitives 2025-04-25 10:15:27 -04:00
Syed Idris Shah
c98a76135b webgpu: Implement update buffer object methods in driver
Move queue instantion to the constructor. Its needed by the update buffer methods.
2025-04-25 10:15:27 -04:00
Syed Idris Shah
7f2836bb43 webgpu: Implement R functions for vertex and index buffers
Also create BufferObject and use it to update vertex buffers

Updating buffers will be in the next upcoming patch.
2025-04-25 10:15:27 -04:00
Syed Idris Shah
b60d02919b webgpu: Update VertexBufferInfo to populate VertexBufferLayout 2025-04-25 10:15:27 -04:00
bridgewaterrobbie
156dde6872 We can't cast handles that weren't actually constructed, so can't use the update descriptor funcs yet. 2025-04-24 14:47:16 -04:00
bridgewaterrobbie
dd2fbf01aa wgpu: Initial bindgroup and descriptorset setup.
Likely not handling textures correctly, need more understanding there to proceed.
2025-04-24 14:47:16 -04:00
Ajmal Kunnummal
beba1f63f1 Revert initializing the transform 2025-04-24 10:27:35 -07:00
Ajmal Kunnummal
e20dd9ec93 Fix bug where the transform parameter was mistakenly unused in setAcquiredImage
- Also initialized the transform field to identity
2025-04-24 10:27:35 -07:00
Ajmal Kunnummal
7f80d956b6 Fix bug where BufferInterfaceBlock::getTransformFieldOffset the offset should be reported in bytes instead of words 2025-04-24 10:26:59 -07:00
Rasmus Munk Larsen
3c941863dc Revert change to pow. 2025-04-23 16:25:46 -07:00
Rasmus Munk Larsen
57e2007aaf Fix typo again. 2025-04-23 16:25:46 -07:00
Rasmus Munk Larsen
f88f0607c7 Fix typo. 2025-04-23 16:25:46 -07:00
Rasmus Munk Larsen
91b98964cc Optimize pow and slerp for quaternions by using the identies
sin(acos(x)) = sqrt(1-x^2)
  cos(acos(x)) = x.
2025-04-23 16:25:46 -07:00
rafadevai
d92a90e22b VK: Small improvements in different places (#8651)
- Add markers to more VulkanDriver calls and other
functions related to VulkanDriver::collectGarbage
- Fix validation layer error calling vkWaitForFences
with a count of 0.
- In VulkanTextureState, call clearCachedImageViews
before destroying the vkImage.
- In VulkanStagePool, use a std::vector for keeping
track of used images and buffers. Also set the
TIME_BEFORE_EVICTION to 3 frames instead of the
FVK_MAX_COMMAND_BUFFERS.
2025-04-23 22:15:29 +00:00
Powei Feng
79a548ecae Fix broken ios build (#8658)
Missing include after #8654
2025-04-23 20:28:11 +00:00
Mathias Agopian
3c4c8940b2 clang-tidy cleanups 2025-04-23 09:42:02 -07:00
Mathias Agopian
75d2252d33 Add UTILS_VERY_[UN]LIKELY macros
clang optimizes code differently with very likely or unlikely 
conditions, so we add a `VERY` version of these macros, and we
make use of it for assertions.
2025-04-23 09:23:21 -07:00
Powei Feng
9d3a55291d Fix broken header check (#8655)
- PR #8641 introduced a requirement for including Systrace.h that
  wasn't covered by the header-check test.
- Make sure temp.cpp contains only one include
- Add a missing include for Package.h
2025-04-22 22:18:50 +00:00
Powei Feng
170dec0945 vk: external sampler work (#8608)
- Add a manager class for handling external image and sampling
 - The logic is to enable a slow path if the descriptor set
   layout has external samplers and an external image has been
   assigned to the corresponding descriptor set
 - The slow path will deduce the format of the external image to
   and create the corresponding YUV conversion if necessary.
 - Clean-up VulkanPlatform
2025-04-22 15:01:18 -07:00
Powei Feng
7967157fbb renderdiff: fix breakage (#8648)
- Re-enable renderdiff test.
- Cache the mesa directory so that we're not spending time pulling
  and compiling it.
- Move python prereqs into the script and use venv.
2025-04-22 18:23:05 +00:00
Andy Hovingh
597ced13e1 webgpu: initial shader compilation 2025-04-22 12:56:03 -05:00
Sungun Park
c3542b135e Fix crash for ShaderCompilerService (#8626)
This change fixes a crash that occurs in ShaderCompilerService under a
certain condition described below.

Functions are called in the order described below.
1. `ShaderCompilerService::initialize(...)` is called when a new gl
   program is created. There are cases whre the corresponding TickOp may
   be still alive at the end of this method.
2. `OpenGLProgram::~OpenGLProgram()` is called when the program is
   immediately destroyed without being used. This deletes gl.program.
3. `ShaderCompilerService::tick()` is called later, and it references
   the dangling gl object `gl.program` in the lambda function, and it
   crashes.

This change also includes refactoring the class `ShaderCompilerService`
to make code simpler and less error-prone.

TEST = Tested on Desktop, Android, and Web by running sample apps

BUGS = [394319326, 407090622]
2025-04-22 17:31:40 +00:00
Mathias Agopian
ca3ff7e08e Use the Perfetto SDK instead of ATRACE
Perfetto has significantly less overhead. The User facing API is
mostly unchanged:

Here are the differences:

- SYSTRACE_ENABLE() does nothing on ANDROID, initializes systraces on darwin.
- SYSTRACE_DISABLE() is removed.
- A new "gltfio" tag is added.
- SYSTRACE_TAG *must* be defined before including `utils/Systrace.h`
- `utils/Systrace.h` should not be used from a public header
- the new SYSTRACE_TAG_DISABLE disables systrace at compile time


For android a data source MUST be created in the perfetto config:

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

This can for example be added to AGI's custom/advanced config.

FIXES=[407572663]
2025-04-22 09:31:29 -07:00
Mathias Agopian
42c760a92f add the Perfetto SDK to libutils
for android NDK projects we also need to add it to the dependent
libraries.
2025-04-22 09:31:29 -07:00
Mathias Agopian
327a537bcc Add the Perfetto SDK to the build 2025-04-22 09:31:29 -07:00
Matthew Hoffman
cfc4f34c18 Use EXPECT_IMAGE in all backend tests. (#8628) 2025-04-21 19:57:40 -05:00
Sungun Park
6dac384bd9 Release Filament 1.59.3 2025-04-21 22:05:28 +00:00
rafadevai
d666f8a0ba VK: Create and use a VkPipelineCache (#8631)
To minimize the cost to recreated old pipelines that
has been evicted from VulkanPipelineCache, make the
driver cache the pipeline objects.
2025-04-21 18:14:58 +00:00
Matthew Hoffman
d6ae3a57b5 Replace backend test expected hash with loading the image. (#8622)
BUGS=[402474473]
2025-04-18 15:59:01 -05:00
Powei Feng
c2155f3f98 github: disable renderdiff (#8640)
The test is broken. Disable while investigating.
2025-04-18 11:10:10 -07:00
bridgewaterrobbie
e2a3637413 webgpu: Avoid using handle 'using' redefines for consistency 2025-04-18 11:42:10 -04:00
bridgewaterrobbie
390df4e42c Tweak constructor of WebGPUDescriptorSetLayout to take a ref rather than a pointer 2025-04-18 11:04:24 -04:00
Powei Feng
21dd1319df Release Filament 1.59.2 2025-04-16 15:47:19 -07:00
bridgewaterrobbie
444ac8d6a6 Handle WebGPUDescriptorSetLayout creation 2025-04-15 18:51:26 -04:00
Syed Idris Shah
aac1e7dc4c Move adapter and device creation to the begining 2025-04-15 18:51:26 -04:00
Matthew Hoffman
5b8a1e5e58 Add a macro for skipping tests based on backend/OS. (#8602)
BUGS=[398198557]
2025-04-15 17:03:48 -05:00
Andy Hovingh
ad1b36d2b3 fix: DEBUG_COMMAND_STREAM could not be compiled as true due to lack of BufferObjectStreamDescriptor insertion operator 2025-04-14 16:50:53 -05:00
Sungun Park
315c2c273f Cleanup shader compiler (#8613)
There's no functional difference.
2025-04-14 18:27:52 +00:00
Juan Caldas
005d835dbe wgpu: Add Vertex and Index Buffer (#8616)
* implement vertex and index buffers
2025-04-14 10:36:14 -04:00
Jeremy Nelson
cade94ab2c webgpu: surface window size for swapchain/surface 2025-04-11 17:30:08 -05:00
Max Rebuschatis
5cd2f5626c Fix bug in FMaterialInstance::setParameterImpl() where mTextureParameters
was not updated when a texture was reassigned if !texture->textureHandleCanMutate().

This could cause a crash in FMaterialInstance::commit() since the old texture
binding was still in mTextureParameters but may have been destroyed.
2025-04-11 12:51:46 -07:00
Jeremy Nelson
94bbcbf1c3 wgpu: combine swapchain classes 2025-04-10 09:20:12 -05:00
Andy Hovingh
52c998d2b6 webgpu: use handle allocator with swapchain 2025-04-10 07:37:53 -05:00
rafadevai
527d831c15 VK: Reuse renderpasses after swapchain resize (#8601)
When a swapchain is resized the FBO cache gets resets
which includes the framebuffer and renderpass objects.
This causes pipelines to be recreated instead of being
reused from the previous frames, causing stalls.

In the case of renderpasses there's no need to clear these
objects, since the state of the renderpass is the same, only
their refcount should be adjusted, fixing the issue mentioned
above.
2025-04-10 07:08:20 +00:00
Syed Idris Shah
2790f2e64c Cleanup the dead code from VulkanPlatformApple.mm
__APPLE__ is defined for both macos and ios platform.
#elif code will never be excuted.
2025-04-09 14:16:11 -04:00
Benjamin Doherty
757640e850 Release Filament 1.59.1 2025-04-09 10:20:51 -07:00
Ben Doherty
9d9abca33a Validate textures have non-zero dimensions (#8607) 2025-04-09 10:18:17 -07:00
Syed Idris Shah
bea7a7c73f Implement basic webgpu driver backend to render a background color
Add priliminay implementations of CommandBuffer, RenderColor Attachment,
RenderPass encoders and web gpu queue. This is sufficient to render
background color on a surface using webgpu api's.
2025-04-08 16:28:26 -04:00
Syed Idris Shah
dd62f98784 Treat native Window as CAMetalLayer
Attach MetalLayer to Native View same as vulkan and metal backend.
This change assumes native window is already a CAMetalLayer. This is no different
for IOS and Mac.
2025-04-08 16:28:26 -04:00
Syed Idris Shah
395a3eda9b Introduce basic HandleAllocator for webgpu
Implement stub implementations for different handles. This is a
place holder patch. These handles will be enhanced and used by webgpu
driver for backend implementations.
2025-04-08 16:28:26 -04:00
Syed Idris Shah
9d18cc35e0 Fix webgpu android build after dawn update 2025-04-08 14:58:59 -04:00
Andy Hovingh
2e37ebe70c webgpu: compile WGSL shaders into Android materials when needed 2025-04-08 13:13:21 -05:00
Matthew Hoffman
63ef412be1 Shared shader helpers for backend tests. (#8562) 2025-04-08 12:45:59 -05:00
rafadevai
f0996296c1 VK: Remove explicitImageReadyWait callback (#8603)
This function was created to make sure the swapchain
image was available before submitting the work to the GPU.

So the order of the process looked like

| Acquire | Begin | Record | Wait | Present | End |

After this change the expectation is that when the
swapchain acquires a new image it will also wait until
is actually available from the platform.

| Acquire | Begin | Record | Present | End |

In the case the image is not available, the expectation
is that the acquire will wait until it is and then start
processing the frame.
2025-04-08 06:09:49 +00:00
Powei Feng
a60d570b3b vk: preparing for Ycbcr conversion (#8596)
- Move utils functions to the right files
 - Add conversion from vk formats to filament
 - Refactor how to enable multiview and external sampler in
   VulkanPlatform
 - Make VuklanDescriptorSet and VuklanDescriptorSetLayout more
   general to allow for mutability
 - Reorder vulkan files in CMakeLists.txt to be sorted in
   alphabetical order.
2025-04-08 05:50:37 +00:00
Powei Feng
3fdb9f311a github: add code-correctness presubmit (#8595) 2025-04-04 15:17:07 -07:00
214 changed files with 258232 additions and 3092 deletions

View File

@@ -0,0 +1,7 @@
name: 'Set up dependency versions'
runs:
using: "composite"
steps:
- name: Set up dependency versions
shell: bash
run: cat ./build/common/versions >> $GITHUB_ENV

38
.github/actions/linux-prereq/action.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: 'Linux Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Install Linux Prerequisites
shell: bash
run: |
set -xe
# See https://askubuntu.com/questions/272248/processing-triggers-for-man-db/1476024#1476024
echo "set man-db/auto-update false" | sudo debconf-communicate
sudo dpkg-reconfigure man-db
# Install ninja
source ./build/common/get-ninja.sh
# Install CMake
mkdir -p cmake
cd cmake
sudo wget https://github.com/Kitware/CMake/releases/download/v$GITHUB_CMAKE_VERSION/cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo chmod +x ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh --skip-license > /dev/null
sudo update-alternatives --install /usr/bin/cmake cmake $(pwd)/bin/cmake 1000 --force
cd ..
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
set +xe

23
.github/actions/mac-prereq/action.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: 'Mac Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Cache Brew
id: brew-cache
uses: actions/cache@v4 # Use a specific version
with:
path: $HOME/Library/Caches/Homebrew
key: ${{ runner.os }}-brew-20250424
- name: Install Mac Prerequisites
shell: bash
run: |
# Install ninja
source ./build/common/get-ninja.sh

View File

@@ -16,6 +16,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
with:

View File

@@ -14,6 +14,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh continuous

View File

@@ -14,6 +14,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh continuous

View File

@@ -14,6 +14,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/mac && printf "y" | ./build.sh continuous

View File

@@ -9,20 +9,32 @@ on:
- main
jobs:
build-desktop:
name: build-desktop
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14-xlarge, ubuntu-22.04-16core]
build-desktop-mac:
name: build-mac
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh presubmit
cd build/mac && printf "y" | ./build.sh presubmit
- name: Test material parser
run: |
out/cmake-release/filament/test/test_material_parser
build-desktop-linux:
name: build-linux
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/linux && printf "y" | ./build.sh presubmit
- name: Test material parser
run: |
out/cmake-release/filament/test/test_material_parser
@@ -33,6 +45,8 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Run build script
run: |
build\windows\build-github.bat presubmit
@@ -44,6 +58,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
@@ -60,6 +77,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Run build script
run: |
cd build/ios && printf "y" | ./build.sh presubmit
@@ -73,6 +93,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh presubmit
@@ -95,18 +118,20 @@ jobs:
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install python prereqs
run: pip install mako setuptools pyyaml
- name: Run script
run: |
bash test/renderdiff/test.sh
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Cache Mesa and deps
id: mesa-cache
uses: actions/cache@v4 # Use a specific version
with:
path: mesa
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
- name: Get Mesa
id: mesa-prereq
run: bash test/utils/get_mesa.sh
- name: Run Test
run: bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
@@ -115,10 +140,33 @@ jobs:
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'ubuntu-24.04-8core'
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
run: ./build.sh -W debug test_filamat filament
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
code-correcteness:
name: code-correctness
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Install clang-tidy and deps
run: |
pip install pyyaml
brew install llvm@${GITHUB_LLVM_VERSION}
sudo ln -s "$(brew --prefix llvm)@${GITHUB_LLVM_VERSION}/bin/clang-tidy" "/usr/local/bin/clang-tidy"
- name: Run build script
# We need to build before clang-tidy can run analysis
run: |
# This will build for all three desktop backends on mac
./build.sh -p desktop debug gltf_viewer
- name: Run test
run: bash test/code-correctness/test.sh

View File

@@ -29,8 +29,42 @@ on:
types: [created]
jobs:
build-desktop:
name: build-desktop
build-linux:
name: build-linux
runs-on: ubuntu-22.04-32core
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
steps:
- name: Decide Git ref
id: git_ref
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/linux && printf "y" | ./build.sh release
cd ../..
mv out/filament-release-linux.tgz out/filament-${TAG}-linux.tgz
- uses: actions/github-script@v6
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
build-mac:
name: build-mac
runs-on: ${{ matrix.os }}
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
@@ -49,15 +83,14 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh release
cd build/mac && printf "y" | ./build.sh release
cd ../..
if [ -f out/filament-release-darwin.tgz ]; then mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz; fi;
if [ -f out/filament-release-linux.tgz ]; then mv out/filament-release-linux.tgz out/filament-${TAG}-linux.tgz; fi;
mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz
- uses: actions/github-script@v6
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -84,6 +117,7 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -121,6 +155,7 @@ jobs:
with:
distribution: 'temurin'
java-version: '17'
- uses: ./.github/actions/linux-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -171,6 +206,7 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}

View File

@@ -14,6 +14,9 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh continuous

View File

@@ -801,6 +801,7 @@ add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/perfetto/tnt)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
add_subdirectory(${LIBRARIES}/geometry)

View File

@@ -7,5 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Fix build/compile errors when upgrading to MacOS 15.4

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.59.0'
implementation 'com.google.android.filament:filament-android:1.59.3'
}
```
@@ -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:
```shell
pod 'Filament', '~> 1.59.0'
pod 'Filament', '~> 1.59.3'
```
## Documentation

View File

@@ -7,6 +7,16 @@ 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
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.59.4
## v1.59.3
## v1.59.2
- Fix build/compile errors when upgrading to MacOS 15.4
## v1.59.1

View File

@@ -142,6 +142,12 @@ abstract class MaterialCompiler extends TaskWithBinary {
if (!exclude_vulkan) {
matcArgs += ['-a', 'vulkan']
}
def include_webgpu = providers
.gradleProperty("com.google.android.filament.include-webgpu")
.forUseAtConfigurationTime().present
if (include_webgpu) {
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
}
def mat_no_opt = providers
.gradleProperty("com.google.android.filament.matnopt")

View File

@@ -26,6 +26,10 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
add_library(filabridge STATIC IMPORTED)
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
@@ -40,6 +44,7 @@ set_target_properties(shaders PROPERTIES IMPORTED_LOCATION
set(FILAMAT_INCLUDE_DIRS
../../libs/utils/include
../../third_party/perfetto
)
include_directories(${FILAMENT_DIR}/include)
@@ -55,6 +60,7 @@ target_link_libraries(filamat-jni
filabridge
shaders
utils
perfetto
log
smol-v
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>

View File

@@ -21,6 +21,10 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
add_library(ibl-lite STATIC IMPORTED)
set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a)
@@ -123,6 +127,7 @@ target_link_libraries(filament-jni
PRIVATE android
PRIVATE jnigraphics
PRIVATE utils
PRIVATE perfetto
# libgeometry is PUBLIC because gltfio uses it.
PUBLIC geometry
@@ -141,6 +146,7 @@ target_include_directories(filament-jni PRIVATE
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../third_party/perfetto
../../libs/utils/include)
# Force a relink when the version script is changed:

View File

@@ -35,6 +35,10 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
add_library(uberzlib STATIC IMPORTED)
set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a)
@@ -121,6 +125,7 @@ set(GLTFIO_INCLUDE_DIRS
../../third_party/meshoptimizer/src
../../third_party/robin-map
../../third_party/stb
../../third_party/perfetto
../../libs/utils/include
../../libs/ktxreader/include
)
@@ -129,7 +134,7 @@ add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni filament-jni utils perfetto uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.59.0
VERSION_NAME=1.59.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -14,12 +14,12 @@ 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..."
;;
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
*)
exit 0
;;
esac
@@ -30,30 +30,27 @@ set -x
UNAME=`echo $(uname)`
LC_UNAME=`echo $UNAME | tr '[:upper:]' '[:lower:]'`
FILAMENT_ANDROID_CI_BUILD=true
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
if [[ "$LC_UNAME" == "linux" ]]; then
source `dirname $0`/../linux/ci-common.sh
elif [[ "$LC_UNAME" == "darwin" ]]; then
source `dirname $0`/../mac/ci-common.sh
fi
source `dirname $0`/../common/build-common.sh
if [[ "$GITHUB_WORKFLOW" ]]; then
java_version=$(java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1)
if [[ "$java_version" < 17 ]]; then
echo "Android builds require Java 17, found version ${java_version} instead"
exit 0
exit 1
fi
fi
# Unless explicitly specified, NDK version will be set to match exactly the required one
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/ndk.version)}
FILAMENT_NDK_VERSION=${GITHUB_NDK_VERSION:-27.0.11718014}
(! grep "${FILAMENT_NDK_VERSION}" `dirname $0`/../../android/build.gradle > /dev/null) &&
echo "Mismatch of NDK versions: want ${FILAMENT_NDK_VERSION} and not found in android/build.gradle" &&
exit 1
# Install the required NDK version specifically (if not present)
if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then

View File

@@ -1 +0,0 @@
27.0.11718014

View File

@@ -56,10 +56,16 @@ popd >/dev/null
rm -rf out/check-headers
mkdir -p out/check-headers
TMP_FILE=out/check-headers/temp.cpp
echo "Checking that public headers compile independently..."
for include in "${includes[@]}"; do
rm -f ${TMP_FILE}
echo "Checking ${include}"
echo "#include <${include}>" >> out/check-headers/temp.cpp
clang -std=c++17 -I "${FILAMENT_HEADERS}" out/check-headers/temp.cpp -c -o /dev/null
if [[ "${include}" == "utils/Systrace.h" ]]; then
# A necessary define before we can include utils/Systrace.h
echo "#define SYSTRACE_TAG SYSTRACE_TAG_DISABLED" >> ${TMP_FILE}
fi
echo "#include <${include}>" >> ${TMP_FILE}
clang -std=c++17 -I "${FILAMENT_HEADERS}" ${TMP_FILE} -c -o /dev/null
done
echo "Done!"

View File

@@ -2,5 +2,4 @@
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
CONTINUOUS_INTEGRATION=true
fi

18
build/common/get-ninja.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
if [[ "$GITHUB_WORKFLOW" ]]; then
OS_NAME=$(uname -s)
NINJA_DL_DIR=/tmp/ninja-dl
NINJA_PLATFORM=
if [[ "$OS_NAME" == "Linux" ]]; then
NINJA_PLATFORM=linux
elif [[ "$OS_NAME" == "Darwin" ]]; then
NINJA_PLATFORM=mac
fi
curl -L -o /tmp/ninja-dl.zip https://github.com/ninja-build/ninja/releases/download/v${GITHUB_NINJA_VERSION}/ninja-${NINJA_PLATFORM}.zip
mkdir -p $NINJA_DL_DIR
unzip -q /tmp/ninja-dl.zip -d $NINJA_DL_DIR
chmod +x ${NINJA_DL_DIR}/ninja
# Install ninja globally for AGP
sudo cp ${NINJA_DL_DIR}/ninja /usr/local/bin/
fi

6
build/common/versions Normal file
View File

@@ -0,0 +1,6 @@
GITHUB_CLANG_VERSION=14
GITHUB_CMAKE_VERSION=3.19.5
GITHUB_NINJA_VERSION=1.10.2
GITHUB_MESA_VERSION=24.2.1
GITHUB_LLVM_VERSION=16
GITHUB_NDK_VERSION=27.0.11718014

View File

@@ -28,7 +28,6 @@ set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
@@ -41,4 +40,3 @@ if [[ "${GENERATE_ARCHIVES}" ]]; then
fi
./build.sh -i -p ios -c $BUILD_SIMULATOR $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE

View File

@@ -1,6 +0,0 @@
#!/bin/bash
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
chmod +x ninja
export PATH="$PWD:$PATH"

View File

@@ -32,7 +32,6 @@ set -x
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null

View File

@@ -1,38 +0,0 @@
#!/bin/bash
# version of clang we want to use
export GITHUB_CLANG_VERSION=14
# version of CMake to use instead of the default one
export GITHUB_CMAKE_VERSION=3.19.5
# version of ninja to use
export GITHUB_NINJA_VERSION=1.10.2
# Steps for GitHub Workflows
if [[ "$GITHUB_WORKFLOW" ]]; then
# Install ninja
wget -q https://github.com/ninja-build/ninja/releases/download/v$GITHUB_NINJA_VERSION/ninja-linux.zip
unzip -q ninja-linux.zip
export PATH="$PWD:$PATH"
# Install CMake
mkdir -p cmake
cd cmake
sudo wget https://github.com/Kitware/CMake/releases/download/v$GITHUB_CMAKE_VERSION/cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo chmod +x ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh
sudo ./cmake-$GITHUB_CMAKE_VERSION-Linux-x86_64.sh --skip-license > /dev/null
sudo update-alternatives --install /usr/bin/cmake cmake $(pwd)/bin/cmake 1000 --force
cd ..
sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
fi

View File

@@ -28,7 +28,6 @@ set -e
set -x
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null

View File

@@ -1,8 +0,0 @@
#!/bin/bash
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
chmod +x ninja
# Install ninja globally for AGP
cp ninja /usr/local/bin
export PATH="$PWD:$PATH"

View File

@@ -31,10 +31,16 @@ set(DIST_ARCH arm64-v8a)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -32,10 +32,16 @@ set(DIST_ARCH armeabi-v7a)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -31,10 +31,16 @@ set(DIST_ARCH x86)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -31,10 +31,16 @@ set(DIST_ARCH x86_64)
string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} HOST_NAME_L)
file(TO_CMAKE_PATH $ENV{ANDROID_HOME} ANDROID_HOME_UNIX)
message(STATUS "Try using NDK \'${FILAMENT_NDK_VERSION}\'")
if (NOT FILAMENT_NDK_VERSION)
file(READ "${CMAKE_CURRENT_LIST_DIR}/android/ndk.version" FILAMENT_NDK_VERSION)
string(REGEX MATCH "^\\d+" FILAMENT_NDK_VERSION ${FILAMENT_NDK_VERSION})
file(READ "${CMAKE_CURRENT_LIST_DIR}/common/versions" VERSIONS_STR)
string(REGEX MATCH "GITHUB_NDK_VERSION=(\\d+)" _UNUSED ${VERSIONS_STR})
if(CMAKE_MATCH_1)
set(FILAMENT_NDK_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
message(STATUS "Using NDK \'${FILAMENT_NDK_VERSION}\'")
file(GLOB NDK_VERSIONS LIST_DIRECTORIES true ${ANDROID_HOME_UNIX}/ndk/${FILAMENT_NDK_VERSION}*)
list(SORT NDK_VERSIONS)
list(GET NDK_VERSIONS -1 NDK_VERSION)

View File

@@ -1,16 +1,4 @@
#!/bin/bash
if [ `uname` == "Linux" ];then
source `dirname $0`/../linux/ci-common.sh
elif [ `uname` == "Darwin" ];then
curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
unzip -q ninja-mac.zip
else
echo "Unsupported OS"
exit 1
fi
chmod +x ninja
export PATH="$PWD:$PATH"
# Install emscripten.
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.60.zip > emsdk.zip

View File

@@ -177,29 +177,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/VulkanDescriptorSetCache.cpp
src/vulkan/VulkanDescriptorSetCache.h
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
src/vulkan/VulkanDescriptorSetLayoutCache.h
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
src/vulkan/memory/Resource.cpp
src/vulkan/memory/Resource.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/utils/Conversion.cpp
src/vulkan/utils/Conversion.h
src/vulkan/utils/Definitions.h
src/vulkan/utils/Helper.h
src/vulkan/utils/Image.h
src/vulkan/utils/Image.cpp
src/vulkan/utils/Spirv.h
src/vulkan/utils/Spirv.cpp
src/vulkan/utils/StaticVector.h
src/vulkan/VulkanAsyncHandles.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
@@ -210,31 +187,56 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanConstants.h
src/vulkan/VulkanContext.cpp
src/vulkan/VulkanContext.h
src/vulkan/VulkanDescriptorSetCache.cpp
src/vulkan/VulkanDescriptorSetCache.h
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
src/vulkan/VulkanDescriptorSetLayoutCache.h
src/vulkan/VulkanDriver.cpp
src/vulkan/VulkanDriver.h
src/vulkan/VulkanDriverFactory.h
src/vulkan/VulkanExternalImageManager.cpp
src/vulkan/VulkanExternalImageManager.h
src/vulkan/VulkanFboCache.cpp
src/vulkan/VulkanFboCache.h
src/vulkan/VulkanHandles.cpp
src/vulkan/VulkanHandles.h
src/vulkan/VulkanMemory.h
src/vulkan/VulkanMemory.cpp
src/vulkan/VulkanMemory.h
src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/VulkanQueryManager.cpp
src/vulkan/VulkanQueryManager.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanStagePool.cpp
src/vulkan/VulkanStagePool.h
src/vulkan/VulkanSwapChain.cpp
src/vulkan/VulkanSwapChain.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h
src/vulkan/VulkanYcbcrConversionCache.cpp
src/vulkan/VulkanYcbcrConversionCache.h
src/vulkan/memory/Resource.cpp
src/vulkan/memory/Resource.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/utils/Conversion.cpp
src/vulkan/utils/Conversion.h
src/vulkan/utils/Definitions.h
src/vulkan/utils/Helper.h
src/vulkan/utils/Image.cpp
src/vulkan/utils/Image.h
src/vulkan/utils/Spirv.cpp
src/vulkan/utils/Spirv.h
src/vulkan/utils/StaticVector.h
)
if (LINUX OR WIN32)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
@@ -255,8 +257,13 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUConstants.h
src/webgpu/WebGPUDriver.cpp
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUHandles.cpp
src/webgpu/WebGPUHandles.h
src/webgpu/WebGPUPipelineCreation.cpp
src/webgpu/WebGPUPipelineCreation.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WGPUProgram.cpp
)
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
@@ -505,7 +512,10 @@ if (APPLE OR LINUX)
test/Arguments.cpp
test/ImageExpectations.cpp
test/Lifetimes.cpp
test/PlatformRunner.cpp
test/Shader.cpp
test/SharedShaders.cpp
test/Skip.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp
@@ -529,6 +539,9 @@ if (APPLE OR LINUX)
filamat
SPIRV
spirv-cross-glsl)
# Create input/output directories for test result images.
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/actual_images)
file(COPY test/expected_images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images)
endif()
# TODO: Disabling IOS test due to breakage wrt glslang update

View File

@@ -55,4 +55,9 @@ public:
} // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out,
const filament::backend::BufferObjectStreamDescriptor& b);
#endif
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H

View File

@@ -104,10 +104,6 @@ public:
// Semaphore to be signaled once the image is available.
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
// A function called right before vkQueueSubmit. After this call, the image must be
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
};
VulkanPlatform();
@@ -298,6 +294,16 @@ public:
VkQueue getProtectedGraphicsQueue() const noexcept;
struct ExternalImageMetadata {
/**
* The Filament texture format.
*/
TextureFormat filamentFormat;
/**
* The Filament texture usage.
*/
TextureUsage filamentUsage;
/**
* The width of the external image
*/
@@ -308,11 +314,6 @@ public:
*/
uint32_t height;
/**
* The layerCount of the external image
*/
uint32_t layerCount;
/**
* The layer count of the external image
*/
@@ -328,11 +329,6 @@ public:
*/
VkFormat format;
/**
* An external buffer can be protected. This tells you if it is.
*/
bool isProtected;
/**
* The type of external format (opaque int) if used.
*/
@@ -352,20 +348,61 @@ public:
* Heap information
*/
uint32_t memoryTypeBits;
/**
* Ycbcr conversion components
*/
VkComponentMapping ycbcrConversionComponents;
/**
* Ycbcr model
*/
VkSamplerYcbcrModelConversion ycbcrModel;
/**
* Ycbcr range
*/
VkSamplerYcbcrRange ycbcrRange;
/**
* Ycbcr x chroma offset
*/
VkChromaLocation xChromaOffset;
/**
* Ycbcr y chroma offset
*/
VkChromaLocation yChromaOffset;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = std::pair<VkImage, VkDeviceMemory>;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat);
// Note that the image metadata might change per-frame, hence we need a method for extracting
// it.
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
return {};
}
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle);
struct ImageData {
struct Bundle {
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
inline bool valid() const noexcept {
return image != VK_NULL_HANDLE;
}
};
// It's possible for the external image to also have a known VK format. We need to create an
// image for that in case we are not looking to use an external "sampler" with this image.
Bundle internal;
// If we get a externalFormat in the metadata, then we should create an image with
// VK_FORMAT_UNDEFINED
Bundle external;
};
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
return {};
}
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
@@ -378,20 +415,6 @@ private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
static VkSampler createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma, SamplerParams sampler,
uint32_t internalFormat);
static VkImageView createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
// Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;

View File

@@ -26,7 +26,7 @@ namespace filament::backend {
class VulkanPlatformAndroid : public VulkanPlatform {
public:
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
@@ -39,31 +39,26 @@ public:
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
virtual ExternalImageMetadata extractExternalImageMetadata(
ExternalImageHandleRef image) const override;
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
protected:
~ExternalImageVulkanAndroid() override;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = VulkanPlatform::ImageData;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
};
}// namespace filament::backend

View File

@@ -38,6 +38,12 @@ public:
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
// TODO consider that this functionality is not WebGPU-specific, and thus could be
// placed in a generic place and even reused across backends. Alternatively,
// a 3rd party library could be considered. However, this was a simple and
// quick change and works for now.
// gets the size (height and width) of the surface/window
[[nodiscard]] wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const;
// either returns a valid surface or panics
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
// either returns a valid adapter or panics

View File

@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
Profiler profiler;
if (SYSTRACE_TAG) {
if constexpr (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
@@ -100,7 +100,7 @@ void CommandStream::execute(void* buffer) {
}
});
if (SYSTRACE_TAG) {
if constexpr (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled
profiler.stop();

View File

@@ -1408,8 +1408,8 @@ void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Primit
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
primitive->vertexBuffer = vertexBuffer;
primitive->indexBuffer = indexBuffer;
primitive->type = pt;
}

View File

@@ -194,12 +194,8 @@ struct MetalIndexBuffer : public HwIndexBuffer {
};
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
};
@@ -380,7 +376,6 @@ public:
math::uint2 getAttachmentSize() noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);

View File

@@ -536,15 +536,6 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
MetalRenderPrimitive::MetalRenderPrimitive() {
}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
}
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
mToken = context.shaderCompiler->createProgram(program.getName(), std::move(program));

View File

@@ -16,9 +16,15 @@
#include "GLUtils.h"
#include "private/backend/Driver.h"
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utils/trap.h>
#include "private/backend/Driver.h"
#include <string_view>
#include <stddef.h>
namespace filament::backend {
@@ -28,38 +34,31 @@ using namespace utils;
namespace GLUtils {
UTILS_NOINLINE
const char* getGLError(GLenum error) noexcept {
const char* string = "unknown";
std::string_view getGLErrorString(GLenum error) noexcept {
switch (error) {
case GL_NO_ERROR:
string = "GL_NO_ERROR";
break;
return "GL_NO_ERROR";
case GL_INVALID_ENUM:
string = "GL_INVALID_ENUM";
break;
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
string = "GL_INVALID_VALUE";
break;
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
string = "GL_INVALID_OPERATION";
break;
return "GL_INVALID_OPERATION";
case GL_INVALID_FRAMEBUFFER_OPERATION:
string = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
return "GL_INVALID_FRAMEBUFFER_OPERATION";
case GL_OUT_OF_MEMORY:
string = "GL_OUT_OF_MEMORY";
break;
return "GL_OUT_OF_MEMORY";
default:
break;
}
return string;
return "unknown";
}
UTILS_NOINLINE
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const error = glGetError();
if (error != GL_NO_ERROR) {
const char* string = getGLError(error);
if (UTILS_VERY_UNLIKELY(error != GL_NO_ERROR)) {
auto const string = getGLErrorString(error);
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
<< function << "\" at line " << io::dec << line << io::endl;
}
@@ -69,46 +68,39 @@ GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcep
UTILS_NOINLINE
void assertGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const err = checkGLError(out, function, line);
if (err != GL_NO_ERROR) {
if (UTILS_VERY_UNLIKELY(err != GL_NO_ERROR)) {
debug_trap();
}
}
UTILS_NOINLINE
const char* getFramebufferStatus(GLenum status) noexcept {
const char* string = "unknown";
std::string_view getFramebufferStatusString(GLenum status) noexcept {
switch (status) {
case GL_FRAMEBUFFER_COMPLETE:
string = "GL_FRAMEBUFFER_COMPLETE";
break;
return "GL_FRAMEBUFFER_COMPLETE";
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
string = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
string = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
case GL_FRAMEBUFFER_UNSUPPORTED:
string = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
return "GL_FRAMEBUFFER_UNSUPPORTED";
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
case GL_FRAMEBUFFER_UNDEFINED:
string = "GL_FRAMEBUFFER_UNDEFINED";
break;
return "GL_FRAMEBUFFER_UNDEFINED";
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
string = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
break;
return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
#endif
default:
break;
}
return string;
return "unknown";
}
UTILS_NOINLINE
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = glCheckFramebufferStatus(target);
if (status != GL_FRAMEBUFFER_COMPLETE) {
const char* string = getFramebufferStatus(status);
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
auto const string = getFramebufferStatusString(status);
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
<< function << "\" at line " << io::dec << line << io::endl;
}
@@ -118,7 +110,7 @@ GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* funct
UTILS_NOINLINE
void assertFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = checkFramebufferStatus(out, target, function, line);
if (status != GL_FRAMEBUFFER_COMPLETE) {
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
debug_trap();
}
}

View File

@@ -17,29 +17,26 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
#define TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <backend/DriverEnums.h>
#include <string_view>
#include <unordered_set>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#include "gl_headers.h"
namespace filament::backend {
namespace GLUtils {
namespace filament::backend::GLUtils {
const char* getGLError(GLenum error) noexcept;
std::string_view getGLErrorString(GLenum error) noexcept;
GLenum checkGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
void assertGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
const char* getFramebufferStatus(GLenum err) noexcept;
std::string_view getFramebufferStatusString(GLenum err) noexcept;
GLenum checkFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
@@ -53,7 +50,7 @@ void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char*
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
#endif
constexpr GLuint getComponentCount(ElementType type) noexcept {
constexpr GLuint getComponentCount(ElementType const type) noexcept {
using ElementType = ElementType;
switch (type) {
case ElementType::BYTE:
@@ -87,27 +84,29 @@ constexpr GLuint getComponentCount(ElementType type) noexcept {
case ElementType::USHORT4:
return 4;
}
// should never happen
return 1;
}
// ------------------------------------------------------------------------------------------------
// Our enums to GLenum conversions
// ------------------------------------------------------------------------------------------------
constexpr GLbitfield getAttachmentBitfield(TargetBufferFlags flags) noexcept {
constexpr GLbitfield getAttachmentBitfield(TargetBufferFlags const flags) noexcept {
GLbitfield mask = 0;
if (any(flags & TargetBufferFlags::COLOR_ALL)) {
mask |= (GLbitfield)GL_COLOR_BUFFER_BIT;
mask |= GLbitfield(GL_COLOR_BUFFER_BIT);
}
if (any(flags & TargetBufferFlags::DEPTH)) {
mask |= (GLbitfield)GL_DEPTH_BUFFER_BIT;
mask |= GLbitfield(GL_DEPTH_BUFFER_BIT);
}
if (any(flags & TargetBufferFlags::STENCIL)) {
mask |= (GLbitfield)GL_STENCIL_BUFFER_BIT;
mask |= GLbitfield(GL_STENCIL_BUFFER_BIT);
}
return mask;
}
constexpr GLenum getBufferUsage(BufferUsage usage) noexcept {
constexpr GLenum getBufferUsage(BufferUsage const usage) noexcept {
switch (usage) {
case BufferUsage::STATIC:
return GL_STATIC_DRAW;
@@ -116,7 +115,7 @@ constexpr GLenum getBufferUsage(BufferUsage usage) noexcept {
}
}
constexpr GLenum getBufferBindingType(BufferObjectBinding bindingType) noexcept {
constexpr GLenum getBufferBindingType(BufferObjectBinding const bindingType) noexcept {
switch (bindingType) {
case BufferObjectBinding::VERTEX:
return GL_ARRAY_BUFFER;
@@ -135,13 +134,15 @@ constexpr GLenum getBufferBindingType(BufferObjectBinding bindingType) noexcept
return 0x90D2; // just to return something
#endif
}
// should never happen
return GL_ARRAY_BUFFER;
}
constexpr GLboolean getNormalization(bool normalized) noexcept {
constexpr GLboolean getNormalization(bool const normalized) noexcept {
return GLboolean(normalized ? GL_TRUE : GL_FALSE);
}
constexpr GLenum getComponentType(ElementType type) noexcept {
constexpr GLenum getComponentType(ElementType const type) noexcept {
using ElementType = ElementType;
switch (type) {
case ElementType::BYTE:
@@ -184,9 +185,11 @@ constexpr GLenum getComponentType(ElementType type) noexcept {
return GL_HALF_FLOAT_OES;
#endif
}
// should never happen
return GL_INT;
}
constexpr GLenum getTextureTargetNotExternal(SamplerType target) noexcept {
constexpr GLenum getTextureTargetNotExternal(SamplerType const target) noexcept {
switch (target) {
case SamplerType::SAMPLER_2D:
return GL_TEXTURE_2D;
@@ -202,14 +205,16 @@ constexpr GLenum getTextureTargetNotExternal(SamplerType target) noexcept {
// we should never be here
return GL_TEXTURE_2D;
}
// should never happen
return GL_TEXTURE_2D;
}
constexpr GLenum getCubemapTarget(uint16_t layer) noexcept {
constexpr GLenum getCubemapTarget(uint16_t const layer) noexcept {
assert_invariant(layer <= 5);
return GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
}
constexpr GLenum getWrapMode(SamplerWrapMode mode) noexcept {
constexpr GLenum getWrapMode(SamplerWrapMode const mode) noexcept {
using SamplerWrapMode = SamplerWrapMode;
switch (mode) {
case SamplerWrapMode::REPEAT:
@@ -219,6 +224,8 @@ constexpr GLenum getWrapMode(SamplerWrapMode mode) noexcept {
case SamplerWrapMode::MIRRORED_REPEAT:
return GL_MIRRORED_REPEAT;
}
// should never happen
return GL_CLAMP_TO_EDGE;
}
constexpr GLenum getTextureFilter(SamplerMinFilter filter) noexcept {
@@ -234,6 +241,8 @@ constexpr GLenum getTextureFilter(SamplerMinFilter filter) noexcept {
return GL_NEAREST_MIPMAP_NEAREST
- GLenum(SamplerMinFilter::NEAREST_MIPMAP_NEAREST) + GLenum(filter);
}
// should never happen
return GL_NEAREST;
}
constexpr GLenum getTextureFilter(SamplerMagFilter filter) noexcept {
@@ -241,7 +250,7 @@ constexpr GLenum getTextureFilter(SamplerMagFilter filter) noexcept {
}
constexpr GLenum getBlendEquationMode(BlendEquation mode) noexcept {
constexpr GLenum getBlendEquationMode(BlendEquation const mode) noexcept {
using BlendEquation = BlendEquation;
switch (mode) {
case BlendEquation::ADD: return GL_FUNC_ADD;
@@ -250,9 +259,11 @@ constexpr GLenum getBlendEquationMode(BlendEquation mode) noexcept {
case BlendEquation::MIN: return GL_MIN;
case BlendEquation::MAX: return GL_MAX;
}
// should never happen
return GL_FUNC_ADD;
}
constexpr GLenum getBlendFunctionMode(BlendFunction mode) noexcept {
constexpr GLenum getBlendFunctionMode(BlendFunction const mode) noexcept {
using BlendFunction = BlendFunction;
switch (mode) {
case BlendFunction::ZERO: return GL_ZERO;
@@ -267,9 +278,11 @@ constexpr GLenum getBlendFunctionMode(BlendFunction mode) noexcept {
case BlendFunction::ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
case BlendFunction::SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE;
}
// should never happen
return GL_ONE;
}
constexpr GLenum getCompareFunc(SamplerCompareFunc func) noexcept {
constexpr GLenum getCompareFunc(SamplerCompareFunc const func) noexcept {
switch (func) {
case SamplerCompareFunc::LE: return GL_LEQUAL;
case SamplerCompareFunc::GE: return GL_GEQUAL;
@@ -280,28 +293,30 @@ constexpr GLenum getCompareFunc(SamplerCompareFunc func) noexcept {
case SamplerCompareFunc::A: return GL_ALWAYS;
case SamplerCompareFunc::N: return GL_NEVER;
}
// should never happen
return GL_LEQUAL;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
constexpr GLenum getTextureCompareMode(SamplerCompareMode mode) noexcept {
constexpr GLenum getTextureCompareMode(SamplerCompareMode const mode) noexcept {
return mode == SamplerCompareMode::NONE ?
GL_NONE : GL_COMPARE_REF_TO_TEXTURE;
}
constexpr GLenum getTextureCompareFunc(SamplerCompareFunc func) noexcept {
constexpr GLenum getTextureCompareFunc(SamplerCompareFunc const func) noexcept {
return getCompareFunc(func);
}
#endif
constexpr GLenum getDepthFunc(SamplerCompareFunc func) noexcept {
constexpr GLenum getDepthFunc(SamplerCompareFunc const func) noexcept {
return getCompareFunc(func);
}
constexpr GLenum getStencilFunc(SamplerCompareFunc func) noexcept {
constexpr GLenum getStencilFunc(SamplerCompareFunc const func) noexcept {
return getCompareFunc(func);
}
constexpr GLenum getStencilOp(StencilOperation op) noexcept {
constexpr GLenum getStencilOp(StencilOperation const op) noexcept {
switch (op) {
case StencilOperation::KEEP: return GL_KEEP;
case StencilOperation::ZERO: return GL_ZERO;
@@ -312,9 +327,11 @@ constexpr GLenum getStencilOp(StencilOperation op) noexcept {
case StencilOperation::DECR_WRAP: return GL_DECR_WRAP;
case StencilOperation::INVERT: return GL_INVERT;
}
// should never happen
return GL_KEEP;
}
constexpr GLenum getFormat(PixelDataFormat format) noexcept {
constexpr GLenum getFormat(PixelDataFormat const format) noexcept {
using PixelDataFormat = PixelDataFormat;
switch (format) {
case PixelDataFormat::RGB: return GL_RGB;
@@ -336,9 +353,11 @@ constexpr GLenum getFormat(PixelDataFormat format) noexcept {
default: return GL_NONE;
#endif
}
// should never happen
return GL_RGBA;
}
constexpr GLenum getType(PixelDataType type) noexcept {
constexpr GLenum getType(PixelDataType const type) noexcept {
using PixelDataType = PixelDataType;
switch (type) {
case PixelDataType::UBYTE: return GL_UNSIGNED_BYTE;
@@ -360,10 +379,12 @@ constexpr GLenum getType(PixelDataType type) noexcept {
default: return GL_NONE;
#endif
}
// should never happen
return GL_UNSIGNED_INT;
}
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
constexpr GLenum getSwizzleChannel(TextureSwizzle c) noexcept {
constexpr GLenum getSwizzleChannel(TextureSwizzle const c) noexcept {
using TextureSwizzle = TextureSwizzle;
switch (c) {
case TextureSwizzle::SUBSTITUTE_ZERO: return GL_ZERO;
@@ -373,10 +394,12 @@ constexpr GLenum getSwizzleChannel(TextureSwizzle c) noexcept {
case TextureSwizzle::CHANNEL_2: return GL_BLUE;
case TextureSwizzle::CHANNEL_3: return GL_ALPHA;
}
// should never happen
return GL_RED;
}
#endif
constexpr GLenum getCullingMode(CullingMode mode) noexcept {
constexpr GLenum getCullingMode(CullingMode const mode) noexcept {
switch (mode) {
case CullingMode::NONE:
// should never happen
@@ -388,11 +411,13 @@ constexpr GLenum getCullingMode(CullingMode mode) noexcept {
case CullingMode::FRONT_AND_BACK:
return GL_FRONT_AND_BACK;
}
// should never happen
return GL_FRONT_AND_BACK;
}
// ES2 supported internal formats for texturing and how they map to a format/type
constexpr std::pair<GLenum, GLenum> textureFormatToFormatAndType(
TextureFormat format) noexcept {
TextureFormat const format) noexcept {
switch (format) {
case TextureFormat::R8: return { 0x1909 /*GL_LUMINANCE*/, GL_UNSIGNED_BYTE };
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
@@ -413,7 +438,7 @@ constexpr std::pair<GLenum, GLenum> textureFormatToFormatAndType(
// clang loses it on this one, and generates a huge jump table when
// inlined. So we don't mark it as inline (only constexpr) which solves the problem,
// strangely, when not inlined, clang simply generates an array lookup.
constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept {
constexpr /* inline */ GLenum getInternalFormat(TextureFormat const format) noexcept {
switch (format) {
/* Formats supported by our ES2 implementations */
@@ -661,7 +686,7 @@ public:
unordered_string_set split(const char* extensions) noexcept;
} // namespace GLUtils
} // namespace filament::backend
} // namespace filament::backend::GLUtils
#endif // TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H

View File

@@ -2098,6 +2098,7 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer, const m
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
hwbuffer, cb, userData, handler });
glstream->user_thread.transform = transform;
if (glstream->user_thread.pending.image != nullptr) {
// If there's no pending image, do nothing. Note that GL_OES_EGL_image does not let you pass

View File

@@ -85,6 +85,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
delete lazyInitializationData;
ShaderCompilerService::terminate(mToken);
assert_invariant(!mToken);
}
delete [] mUniformsRecords;

File diff suppressed because it is too large Load Diff

View File

@@ -24,23 +24,23 @@
#include "OpenGLBlobCache.h"
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/JobSystem.h>
#include <atomic>
#include <condition_variable>
#include <deque>
#include <array>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
#include <stdint.h>
namespace filament::backend {
class OpenGLDriver;
@@ -57,6 +57,8 @@ class ShaderCompilerService {
public:
using program_token_t = std::shared_ptr<OpenGLProgramToken>;
using shaders_t = std::array<GLuint, Program::SHADER_TYPE_COUNT>;
using shaders_source_t = std::array<utils::CString, Program::SHADER_TYPE_COUNT>;
explicit ShaderCompilerService(OpenGLDriver& driver);
@@ -82,6 +84,7 @@ public:
void tick();
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
// This function is not called if `initialize(token)` is already invoked.
static void terminate(program_token_t& token);
// stores a user data pointer in the token
@@ -90,6 +93,12 @@ public:
// retrieves the user data pointer stored in the token
static void* getUserData(const program_token_t& token) noexcept;
// Issue one callback handle.
CallbackManager::Handle issueCallbackHandle() const noexcept;
// Return a callback handle to the callback manager.
void submitCallbackHandle(CallbackManager::Handle handle) noexcept;
// call the callback when all active programs are ready
void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
@@ -97,7 +106,7 @@ public:
private:
struct Job {
template<typename FUNC>
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {} // NOLINT(*-explicit-constructor)
Job(std::function<bool(Job const& job)> fn,
CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
: fn(std::move(fn)), handler(handler), user(user), callback(callback) {
@@ -126,39 +135,49 @@ private:
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
std::vector<ContainerType> mRunAtNextTickOps;
GLuint initialize(ShaderCompilerService::program_token_t& token) noexcept;
GLuint initialize(program_token_t& token);
void ensureTokenIsReady(program_token_t const& token);
static void getProgramFromCompilerPool(program_token_t& token) noexcept;
static void compileShaders(
OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview,
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept;
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
char* source, size_t len) noexcept;
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
static GLuint linkProgram(OpenGLContext& context,
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept;
static bool checkProgramStatus(program_token_t const& token) noexcept;
void runAtNextTick(CompilerPriorityQueue priority,
const program_token_t& token, Job job) noexcept;
void runAtNextTick(CompilerPriorityQueue priority, program_token_t const& token,
Job job) noexcept;
void executeTickOps() noexcept;
bool cancelTickOp(program_token_t token) noexcept;
// order of insertion is important
bool cancelTickOp(program_token_t const& token) noexcept;
// Compile shaders with the given `shaderSource`. `gl.shaders` is always populated with valid
// shader IDs after this method. But this doesn't necessarily mean the shaders are successfully
// compiled. Errors can be checked by calling `checkCompileStatus` later.
static void compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const&
specializationConstants,
bool multiview, program_token_t const& token) noexcept;
// Check if the shader compilation is completed. You may want to call this when the extension
// `KHR_parallel_shader_compile` is enabled.
static bool isCompileCompleted(program_token_t const& token) noexcept;
// Check compilation status of the shaders and log errors on failure.
static void checkCompileStatus(program_token_t const& token) noexcept;
// Create a program by linking the compiled shaders. `gl.program` is always populated with a
// valid program ID after this method. But this doesn't necessarily mean the program is
// successfully linked. Errors can be checked by calling `checkLinkStatusAndCleanupShaders`
// later.
static void linkProgram(OpenGLContext const& context, program_token_t const& token) noexcept;
// Check if the program link is completed. You may want to call this when the extension
// `KHR_parallel_shader_compile` is enabled.
static bool isLinkCompleted(program_token_t const& token) noexcept;
// Check link status of the program and log errors on failure. Return the result of the link.
// Also cleanup shaders regardless of the result.
static bool checkLinkStatusAndCleanupShaders(program_token_t const& token) noexcept;
// Try caching the program if we haven't done it yet. Cache it only when the program is valid.
static void tryCachingProgram(OpenGLBlobCache& cache, OpenGLPlatform& platform,
program_token_t const& token) noexcept;
// Cleanup GL resources.
static void cleanupProgramAndShaders(program_token_t const& token) noexcept;
};
} // namespace filament::backend

View File

@@ -17,9 +17,11 @@
#define COREVIDEO_SILENCE_GL_DEPRECATION
#include "CocoaExternalImage.h"
#include <utils/Panic.h>
#include "../GLUtils.h"
#include <utils/Panic.h>
#include <utils/Log.h>
namespace filament::backend {
static const char *s_vertex = R"SHADER(#version 410 core

View File

@@ -28,6 +28,7 @@
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <utils/Log.h>
namespace filament::backend {

View File

@@ -15,6 +15,7 @@
*/
#include <backend/BufferDescriptor.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include <backend/DescriptorSetOffsetArray.h>
#include <backend/DriverEnums.h>
#include <backend/PipelineState.h>
@@ -437,6 +438,10 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
<< ", user=" << b.getUser() << " }";
}
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
}
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
return out << "PixelBufferDescriptor{ " << base

View File

@@ -295,6 +295,8 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() {
}
void CommandBufferPool::gc() {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("CommandBufferPool::gc");
ActiveBuffers reclaimed;
mSubmitted.forEachSetBit([this,&reclaimed] (size_t index) {
auto& buffer = mBuffers[index];
@@ -304,6 +306,7 @@ void CommandBufferPool::gc() {
}
});
mSubmitted &= ~reclaimed;
FVK_SYSTRACE_END();
}
void CommandBufferPool::update() {
@@ -333,7 +336,9 @@ void CommandBufferPool::wait() {
mSubmitted.forEachSetBit([this, &count, &fences] (size_t index) {
fences[count++] = mBuffers[index]->getVkFence();
});
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
if (count) {
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
}
update();
}

View File

@@ -152,12 +152,12 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL()
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL()
#else
#define FVK_SYSTRACE_CONTEXT()

View File

@@ -70,13 +70,11 @@ struct VulkanRenderPass {
// context are stored in VulkanPlatform.
struct VulkanContext {
public:
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported() == true);
}
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties,
uint32_t flags, VkFlags reqs) {
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
if (flags & 1) {
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
if ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
return i;
}
}
@@ -85,6 +83,13 @@ public:
return (uint32_t) VK_MAX_MEMORY_TYPES;
}
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported());
}
return selectMemoryType(mMemoryProperties, flags, reqs);
}
inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const {
return mDepthStencilFormats;
}
@@ -118,7 +123,7 @@ public:
}
inline bool isMultiviewEnabled() const noexcept {
return mMultiviewEnabled;
return mPhysicalDeviceVk11Features.multiview == VK_TRUE;
}
inline bool isClipDistanceSupported() const noexcept {
@@ -142,6 +147,9 @@ private:
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
};
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
};
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
};
@@ -154,7 +162,6 @@ private:
};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
bool mMultiviewEnabled = false;
bool mLazilyAllocatedMemorySupported = false;
bool mProtectedMemorySupported = false;

View File

@@ -31,8 +31,8 @@ namespace filament::backend {
namespace {
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount;
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
@@ -203,11 +203,10 @@ public:
DescriptorInfinitePool(VkDevice device)
: mDevice(device) {}
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vklayout = layout->getVkLayout();
VkDescriptorSet obtainSet(DescriptorCount const& count, VkDescriptorSetLayout vklayout) {
DescriptorPool* sameTypePool = nullptr;
for (auto& pool: mPools) {
if (!pool->canAllocate(layout->count)) {
if (!pool->canAllocate(count)) {
continue;
}
if (auto set = pool->obtainSet(vklayout); set != VK_NULL_HANDLE) {
@@ -225,8 +224,7 @@ public:
}
// We need to increase the set of pools by one.
mPools.push_back(std::make_unique<DescriptorPool>(mDevice,
DescriptorCount::fromLayoutBitmask(layout->bitmask), capacity));
mPools.push_back(std::make_unique<DescriptorPool>(mDevice, count, capacity));
auto& pool = mPools.back();
auto ret = pool->obtainSet(vklayout);
assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?");
@@ -276,32 +274,36 @@ void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
}
void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& useExternalSamplers,
fvkutils::DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
fvkutils::DescriptorSetMask curMask = setMask;
auto& updateSets = mStashedSets;
auto& lastBoundSets = mLastBoundInfo.boundSets;
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
auto const& updateSets = mStashedSets;
curMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
curMask.unset(index);
}
});
if (curMask.none() &&
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
mLastBoundInfo.boundSets == updateSets)) {
return;
if (mLastBoundInfo.pipelineLayout == pipelineLayout) {
auto& lastBoundSets = mLastBoundInfo.boundSets;
curMask.forEachSetBit([&](size_t index) {
if (updateSets[index] == lastBoundSets[index] && !useExternalSamplers[index]) {
curMask.unset(index);
}
});
}
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
curMask.forEachSetBit([&](size_t index) {
// This code actually binds the descriptor sets.
auto set = updateSets[index];
VkCommandBuffer const cmdbuffer = commands->buffer();
VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
set->getVkSet();
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set);
});
@@ -327,25 +329,26 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
if (set->dynamicUboMask.test(binding)) {
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
VkWriteDescriptorSet const descriptorWrite = {
VkWriteDescriptorSet descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = set->vkSet,
.dstSet = set->getVkSet(),
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = type,
.pBufferInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
if (auto externalSamplerSet = set->getExternalSamplerVkSet();
externalSamplerSet != VK_NULL_HANDLE) {
descriptorWrite.dstSet = externalSamplerSet;
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
}
set->acquire(bufferObject);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
.sampler = sampler,
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
};
void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept {
VkImageSubresourceRange range = texture->getPrimaryViewRange();
VkImageViewType const expectedType = texture->getViewType();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -355,18 +358,35 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
range.levelCount = 1;
range.layerCount = 1;
}
info.imageView = texture->getView(range);
VkDescriptorImageInfo info{
.sampler = sampler,
.imageView = texture->getView(range),
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
};
VkWriteDescriptorSet const descriptorWrite = {
VkWriteDescriptorSet descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->vkSet,
.dstSet = vkset,
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
updateSamplerImpl(set->getVkSet(), binding, texture, sampler);
set->acquire(texture);
}
void VulkanDescriptorSetCache::updateSamplerForExternalSamplerSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept {
updateSamplerImpl(set->getExternalSamplerVkSet(), binding, texture, VK_NULL_HANDLE);
set->acquire(texture);
}
@@ -376,24 +396,34 @@ void VulkanDescriptorSetCache::updateInputAttachment(
// TOOD: fill this in.
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const vkSet = mDescriptorPool->obtainSet(layout->count, layout->getVkLayout());
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
});
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(
mResourceManager, handle, layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](
VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); },
vkSet);
return set;
}
void VulkanDescriptorSetCache::gc() {
mStashedSets = {};
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(DescriptorCount const& count,
VkDescriptorSetLayout vklayout) {
return mDescriptorPool->obtainSet(count, vklayout);
}
void VulkanDescriptorSetCache::manualRecycle(VulkanDescriptorSetLayout::Count const& count,
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
}
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
} // namespace filament::backend

View File

@@ -41,6 +41,9 @@ public:
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetArray =
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorCount = VulkanDescriptorSetLayout::Count;
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetCache();
@@ -54,6 +57,10 @@ public:
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept;
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept;
@@ -63,18 +70,28 @@ public:
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
fvkutils::DescriptorSetMask const& useExternalSamplerMask,
fvkutils::DescriptorSetMask const& setMask);
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
// This method is meant to be used with external samplers
VkDescriptorSet getVkSet(DescriptorCount const& count, VkDescriptorSetLayout vklayout);
// This method is meant to be used with external samplers
void manualRecycle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
VkDescriptorSet vkSet);
DescriptorSetArray const& getBoundSets() const { return mStashedSets; }
void gc();
private:
class DescriptorInfinitePool;
void updateSamplerImpl(VkDescriptorSet set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
using DescriptorSetArray =
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
class DescriptorInfinitePool;
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;

View File

@@ -18,6 +18,8 @@
#include "VulkanHandles.h"
#include <utils/Hash.h>
namespace filament::backend {
namespace {
@@ -58,6 +60,56 @@ uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType t
return count;
}
uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
fvkutils::SamplerBitmask const& mask, fvkutils::SamplerBitmask const& external,
utils::FixedCapacityVector<VkSampler> const& immutableSamplers) {
using Bitmask = fvkutils::SamplerBitmask;
uint32_t count = 0;
Bitmask alreadySeen;
uint8_t immutableIndex = 0;
size_t const immutableSamplerCount = immutableSamplers.size();
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = stages,
.pImmutableSamplers = external[index] && immutableSamplerCount > immutableIndex
? &immutableSamplers[immutableIndex++]
: nullptr,
};
}
});
return count;
}
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
size_t const size = samplers.size();
if (size == 0) {
return 0;
} else if (size == 1) {
return (uint64_t) samplers[0];
}
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
@@ -73,37 +125,45 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
}
}
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
};
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
return itr->second;
}
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, externalSamplers,
immutableSamplers);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = count,
.pBindings = toBind,
};
VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout;
return vklayout;
}
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
BitmaskGroup maskGroup = VulkanDescriptorSetLayout::Bitmask::fromLayoutDescription(info);
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
info);
VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
auto const& bitmasks = layout->bitmask;
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
vklayout = itr->second;
} else {
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
bitmasks.sampler);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count,
.pBindings = toBind,
};
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[bitmasks] = vklayout;
}
layout->setVkLayout(vklayout);
std::move(info), getVkLayout(maskGroup, maskGroup.externalSampler));
return layout;
}

View File

@@ -26,12 +26,11 @@
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
@@ -41,21 +40,35 @@ public:
void terminate() noexcept;
// Just a wrapper around getVkLayout()
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
// This method is meant to be used with external samplers
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
struct LayoutKey {
// this describes the layout using bitset.
VulkanDescriptorSetLayout::Bitmask bitmask = {};
// number of immutable samplers can be arbitrary; so we hash them into 64-bit.
uint64_t immutableSamplerHash = 0;
};
static_assert(sizeof(LayoutKey) == 48);
using LayoutKeyHashFn = utils::hash::MurmurHashFn<LayoutKey>;
struct LayoutKeyEqual {
bool operator()(LayoutKey const& k1, LayoutKey const& k2) const {
return k1.bitmask == k2.bitmask && k1.immutableSamplerHash == k2.immutableSamplerHash;
}
};
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
};
} // namespace filament::backend

View File

@@ -30,7 +30,6 @@
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h"
#include "vulkan/utils/Definitions.h"
#include "vulkan/vulkan_core.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
@@ -198,11 +197,9 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
}
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept
Platform::DriverConfig const& driverConfig)
: mPlatform(platform),
mResourceManager(
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck,
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
driverConfig.disableHeapHandleTags),
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
@@ -221,6 +218,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
@@ -251,7 +250,7 @@ VulkanDriver::~VulkanDriver() noexcept = default;
UTILS_NOINLINE
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept {
Platform::DriverConfig const& driverConfig) {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
@@ -313,7 +312,7 @@ void VulkanDriver::terminate() {
mCurrentSwapChain = {};
mDefaultRenderTarget = {};
mBoundPipeline = {};
mPipelineState = {};
mQueryManager.terminate();
@@ -325,9 +324,13 @@ void VulkanDriver::terminate() {
mCommands.terminate();
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
// descriptorSetLayoutCache
mExternalImageManager.terminate();
mStagePool.terminate();
mPipelineCache.terminate();
mFramebufferCache.reset();
mFramebufferCache.terminate();
mSamplerCache.terminate();
mDescriptorSetLayoutCache.terminate();
mDescriptorSetCache.terminate();
@@ -381,6 +384,10 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
// Do nothing.
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.onBeginFrame();
}
}
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
@@ -421,12 +428,17 @@ void VulkanDriver::updateDescriptorSetTexture(
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
// TODO: YcbcrConversion?
VulkanSamplerCache::Params cacheParams = {
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
if (mExternalImageManager.isExternallySampledTexture(texture)) {
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
mAppState.hasBoundExternalImages = true;
} else {
VulkanSamplerCache::Params cacheParams = {
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
mExternalImageManager.clearTextureBinding(set, binding);
}
}
void VulkanDriver::flush(int) {
@@ -446,6 +458,7 @@ void VulkanDriver::finish(int dummy) {
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
auto ptr = resource_ptr<VulkanRenderPrimitive>::make(&mResourceManager, rph, pt, vb, ib);
@@ -456,12 +469,14 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (!rph) {
return;
}
FVK_SYSTRACE_SCOPE();
auto ptr = resource_ptr<VulkanRenderPrimitive>::cast(&mResourceManager, rph);
ptr.dec();
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
attributeCount, attributes);
vbi.inc();
@@ -471,12 +486,14 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (!vbih) {
return;
}
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
vbi.dec();
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
vertexCount, vbi);
@@ -487,12 +504,14 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (!vbh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
vb.dec();
}
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
FVK_SYSTRACE_SCOPE();
auto elementSize = (uint8_t) getElementTypeSize(elementType);
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mAllocator, mStagePool,
elementSize, indexCount);
@@ -503,12 +522,14 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
if (!ibh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
ib.dec();
}
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mAllocator, mStagePool,
byteCount, bindingType);
bo.inc();
@@ -518,6 +539,7 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
if (!boh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
bo.dec();
}
@@ -563,41 +585,48 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE();
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
}
VkImageUsageFlags vkUsage = metadata.usage;
if (any(usage & TextureUsage::BLIT_SRC)) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
assert_invariant(width == metadata.width);
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t const memoryTypeIndex =
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
<< "failed to find a valid memory type for external image memory.";
// We do not check the format since AHB could return both a known format and an external format.
// In which case, we choose one or the other, but this choice is not known to the client.
// Therefore the following lines are commented out.
// assert_invariant(format == metadata.filamentFormat);
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkImage vkimg;
VkDeviceMemory deviceMemory;
std::tie(vkimg, deviceMemory) =
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
auto imgData = mPlatform->createVkImageFromExternal(externalImage);
assert_invariant(imgData.internal.valid() || imgData.external.valid());
VkFormat vkformat = metadata.format;
VkImage vkimage = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
if (imgData.internal.valid()) {
metadata.externalFormat = 0;
vkimage = imgData.internal.image;
memory = imgData.internal.memory;
} else { // imgData.external.valid()
vkformat = VK_FORMAT_UNDEFINED;
vkimage = imgData.external.image;
memory = imgData.external.memory;
}
VkSamplerYcbcrConversion const conversion =
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
metadata.layerCount, usage, mStagePool);
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimage, memory,
vkformat, conversion, metadata.samples, metadata.width, metadata.height,
metadata.layers, usage, mStagePool);
auto& commands = mCommands.get();
// Unlike uploaded textures or swapchains, we need to explicit transition this
// texture into the read layout.
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
if (imgData.external.valid()) {
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
}
texture.inc();
}
@@ -630,6 +659,8 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
}
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
texture.dec();
mExternalImageManager.removeExternallySampledTexture(texture);
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
@@ -746,6 +777,7 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
FVK_SYSTRACE_SCOPE();
// Running gc() to guard against an edge case where the old swapchains need to have been
// destroyed before the new swapchain can be created. Otherwise, we would fail
// vkCreateSwapchainKHR with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR.
@@ -787,6 +819,7 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
FVK_SYSTRACE_SCOPE();
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
layout.inc();
}
@@ -798,6 +831,10 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetCache.createSet(dsh, layout);
set.inc();
if (layout->hasExternalSamplers()) {
mAppState.hasExternalSamplerLayouts = true;
}
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
@@ -922,6 +959,10 @@ void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
set.dec();
if (mAppState.hasExternalSamplers() && set->getExternalSamplerVkSet() != VK_NULL_HANDLE) {
mExternalImageManager.removeDescriptorSet(set);
}
}
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
@@ -1482,7 +1523,6 @@ void VulkanDriver::endRenderPass(int) {
// pipeline barrier between framebuffer writes and shader reads.
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
mRenderPassFboInfo = {};
mCurrentRenderPass.renderTarget = {};
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
@@ -1523,7 +1563,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
swapChain->acquire(resized);
if (resized) {
mFramebufferCache.reset();
mFramebufferCache.resetFramebuffers();
}
if (UTILS_LIKELY(mDefaultRenderTarget)) {
@@ -1542,10 +1582,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
mBoundPipeline.pipelineLayout, stage, index, value);
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
mPipelineState.pipelineLayout, stage, index, value);
}
void VulkanDriver::insertEventMarker(char const* string) {
@@ -1724,16 +1764,69 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
// This resets all of the pipeline states; the most relevant (needing reset) is .bindInDraw.
mPipelineState = {};
auto& setLayouts = pipelineState.pipelineLayout.setLayout;
DescriptorSetLayoutHandleList layoutHandles;
uint8_t layoutCount = 0;
std::transform(setLayouts.begin(), setLayouts.end(), layoutHandles.begin(),
[&](auto const& handle) -> resource_ptr<VulkanDescriptorSetLayout> {
if (!handle) {
return {};
}
layoutCount++;
return resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, handle);
});
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
fvkutils::DescriptorSetMask const descriptorSetMask =
fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]);
if (mAppState.hasExternalSamplers()) {
auto const haveExternalSamplers = [&](auto layoutHandle) {
if (!layoutHandle) {
return false;
}
return layoutHandle->hasExternalSamplers();
};
if (std::any_of(layoutHandles.begin(), layoutHandles.end(), haveExternalSamplers)) {
BindInDrawBundle bundle = {
.pipelineState = pipelineState,
.dsLayoutHandles = layoutHandles,
.descriptorSetMask = descriptorSetMask,
};
mPipelineState.bindInDraw = { true, bundle };
return;
}
}
// The normal, non-external sampler path
using VkDescriptorSetLayoutArray = VulkanPipelineLayoutCache::DescriptorSetLayoutArray;
VkDescriptorSetLayoutArray vkLayouts;
std::transform(layoutHandles.begin(), layoutHandles.end(), vkLayouts.begin(),
[](auto const& layout) -> VkDescriptorSetLayout {
if (!layout) {
return VK_NULL_HANDLE;
}
return layout->getVkLayout();
});
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, pipelineState.program);
auto pipelineLayout = mPipelineLayoutCache.getLayout(vkLayouts, program);
bindPipelineImpl(pipelineState, pipelineLayout, descriptorSetMask);
}
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask descriptorSetMask) {
FVK_SYSTRACE_SCOPE();
auto commands = mCurrentRenderPass.commandBuffer;
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
pipelineState.vertexBufferInfo);
Handle<HwProgram> programHandle = pipelineState.program;
RasterState const& rasterState = pipelineState.rasterState;
PolygonOffset const& depthOffset = pipelineState.polygonOffset;
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, programHandle);
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, pipelineState.program);
commands->acquire(program);
// Update the VK raster state.
@@ -1775,28 +1868,11 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
mPipelineCache.bindPrimitiveTopology(topology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi->getAttributeCount());
auto& setLayouts = pipelineState.pipelineLayout.setLayout;
VulkanDescriptorSetLayout::DescriptorSetLayoutArray layoutList;
uint8_t layoutCount = 0;
std::transform(setLayouts.begin(), setLayouts.end(), layoutList.begin(),
[&](Handle<HwDescriptorSetLayout> handle) -> VkDescriptorSetLayout {
if (!handle) {
return VK_NULL_HANDLE;
}
auto layout =
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, handle);
layoutCount++;
return layout->getVkLayout();
});
auto pipelineLayout = mPipelineLayoutCache.getLayout(layoutList, program);
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
mBoundPipeline = {
.program = program,
.pipelineLayout = pipelineLayout,
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
};
// Note that we cannot reinit mPipeline because the .bindInDraw metadata that needs to carry
// over even on bind.
mPipelineState.program = program;
mPipelineState.pipelineLayout = pipelineLayout;
mPipelineState.descriptorSetMask = descriptorSetMask;
mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
@@ -1841,15 +1917,41 @@ void VulkanDriver::bindDescriptorSet(
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
auto const& [doBindInDraw, bundle] = mPipelineState.bindInDraw;
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask);
fvkutils::DescriptorSetMask setsWithExternalSamplers = {};
if (doBindInDraw) {
auto& layoutHandles = bundle.dsLayoutHandles;
setsWithExternalSamplers = mExternalImageManager.prepareBindSets(layoutHandles,
mDescriptorSetCache.getBoundSets());
VulkanDescriptorSetLayout::DescriptorSetLayoutArray vklayouts;
for (size_t i = 0; i < layoutHandles.size(); i++) {
if (!layoutHandles[i]) {
vklayouts[i] = VK_NULL_HANDLE;
continue;
}
if (setsWithExternalSamplers[i]) {
vklayouts[i] = layoutHandles[i]->getExternalSamplerVkLayout();
} else {
vklayouts[i] = layoutHandles[i]->getVkLayout();
}
}
auto program =
resource_ptr<VulkanProgram>::cast(&mResourceManager, bundle.pipelineState.program);
VkPipelineLayout const pipelineLayout = mPipelineLayoutCache.getLayout(vklayouts, program);
if (pipelineLayout != mPipelineState.pipelineLayout) {
bindPipelineImpl(bundle.pipelineState, pipelineLayout, bundle.descriptorSetMask);
}
mPipelineState.bindInDraw.first = false;
}
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer, mPipelineState.pipelineLayout,
setsWithExternalSamplers, mPipelineState.descriptorSetMask);
// Finally, make the actual draw call. TODO: support subranges
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
uint32_t const firstIndex = indexOffset;
constexpr int32_t vertexOffset = 0;
constexpr uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
}

View File

@@ -30,6 +30,7 @@
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanExternalImageManager.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
@@ -54,7 +55,7 @@ constexpr uint8_t MAX_RENDERTARGET_ATTACHMENT_TEXTURES =
class VulkanDriver final : public DriverBase {
public:
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept;
Platform::DriverConfig const& driverConfig);
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// Encapsulates the VK_EXT_debug_utils extension. In particular, we use
@@ -89,8 +90,8 @@ private:
void debugCommandBegin(CommandStream* cmds, bool synchronous,
const char* methodName) noexcept override;
inline VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept;
VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig);
~VulkanDriver() noexcept override;
@@ -119,6 +120,8 @@ private:
private:
void collectGarbage();
void bindPipelineImpl(PipelineState const& pipelineState, VkPipelineLayout pipelineLayout,
fvkutils::DescriptorSetMask descriptorSetMask);
VulkanPlatform* mPlatform = nullptr;
fvkmemory::ResourceManager mResourceManager;
@@ -143,21 +146,39 @@ private:
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager;
// This is necessary for us to write to push constants after binding a pipeline.
struct {
resource_ptr<VulkanProgram> program;
VkPipelineLayout pipelineLayout;
fvkutils::DescriptorSetMask descriptorSetMask;
} mBoundPipeline = {};
using DescriptorSetLayoutHandleList = std::array<resource_ptr<VulkanDescriptorSetLayout>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
struct BindInDrawBundle {
PipelineState pipelineState = {};
DescriptorSetLayoutHandleList dsLayoutHandles = {};
fvkutils::DescriptorSetMask descriptorSetMask = {};
resource_ptr<VulkanProgram> program = {};
};
// We need to store information about a render pass to enable better barriers at the end of a
// renderpass.
struct {
using AttachmentArray =
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
AttachmentArray attachments;
} mRenderPassFboInfo = {};
// For push constant
resource_ptr<VulkanProgram> program = {};
// For push commiting dynamic ubos in draw()
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
fvkutils::DescriptorSetMask descriptorSetMask = {};
std::pair<bool, BindInDrawBundle> bindInDraw = {false, {}};
} mPipelineState = {};
struct {
// This tracks whether the app has seen external samplers bound to a the descriptor set.
// This will force bindPipeline to take a slow path.
bool hasExternalSamplerLayouts = false;
bool hasBoundExternalImages = false;
bool hasExternalSamplers() const noexcept {
return hasExternalSamplerLayouts && hasBoundExternalImages;
}
} mAppState;
bool const mIsSRGBSwapChainSupported;
backend::StereoscopicType const mStereoscopicType;

View File

@@ -0,0 +1,294 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanExternalImageManager.h"
#include "VulkanDescriptorSetCache.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanSamplerCache.h"
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h"
#include <backend/platforms/VulkanPlatform.h>
#include <algorithm>
namespace filament::backend {
namespace {
using Bitmask = fvkutils::UniformBufferBitmask;
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
template<typename T>
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
auto newEnd = std::remove_if(v.begin(), v.end(), f);
v.erase(newEnd, v.end());
}
using ImageData = VulkanExternalImageManager::VulkanExternalImageManager::ImageData;
ImageData& findImage(std::vector<ImageData>& images,
fvkmemory::resource_ptr<VulkanTexture> texture) {
auto itr = std::find_if(images.begin(), images.end(), [&](ImageData const& data) {
return data.image == texture;
});
assert_invariant(itr != images.end());
return *itr;
}
void copySet(VkDevice device, VkDescriptorSet srcSet, VkDescriptorSet dstSet, Bitmask bindings) {
// TODO: fix the size for better memory management
std::vector<VkCopyDescriptorSet> copies;
bindings.forEachSetBit([&](size_t index) {
copies.push_back({
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
.srcSet = srcSet,
.srcBinding = (uint32_t) index,
.dstSet = dstSet,
.dstBinding = (uint32_t) index,
.descriptorCount = 1,
});
});
vkUpdateDescriptorSets(device, 0, nullptr, copies.size(), copies.data());
}
Bitmask foldBitsInHalf(Bitmask bitset) {
Bitmask outBitset;
bitset.forEachSetBit([&](size_t index) {
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
});
return outBitset;
}
}// namespace
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
: mPlatform(platform),
mSamplerCache(samplerCache),
mYcbcrConversionCache(ycbcrConversionCache),
mDescriptorSetCache(setCache),
mDescriptorSetLayoutCache(layoutCache) {
}
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
void VulkanExternalImageManager::terminate() {
mSetBindings.clear();
mImages.clear();
}
void VulkanExternalImageManager::onBeginFrame() {
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
image.hasBeenValidated = false;
});
std::for_each(mSetBindings.begin(), mSetBindings.end(), [](SetBindingInfo& info) {
info.bound = false;
});
}
fvkutils::DescriptorSetMask VulkanExternalImageManager::prepareBindSets(LayoutArray const& layouts,
SetArray const& sets) {
fvkutils::DescriptorSetMask shouldUseExternalSampler{};
for (uint8_t i = 0; i < sets.size(); i++) {
auto set = sets[i];
auto layout = layouts[i];
if (!set || !layout) {
continue;
}
if (hasExternalSampler(set)) {
updateSetAndLayout(set, layout);
shouldUseExternalSampler.set(i);
}
}
return shouldUseExternalSampler;
}
bool VulkanExternalImageManager::hasExternalSampler(
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(),
[&](SetBindingInfo const& info) { return info.set == set; });
return itr != mSetBindings.end();
}
void VulkanExternalImageManager::updateSetAndLayout(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
utils::FixedCapacityVector<
std::tuple<uint8_t, VkSampler, fvkmemory::resource_ptr<VulkanTexture>>>
samplerAndBindings;
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
fvkutils::SamplerBitmask actualExternalSamplers;
for (auto& bindingInfo : mSetBindings) {
if (bindingInfo.set != set || bindingInfo.bound) {
continue;
}
auto& imageData = findImage(mImages, bindingInfo.image);
updateImage(&imageData);
auto samplerParams = bindingInfo.samplerParams;
// according to spec, these must match chromaFilter
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
samplerParams.filterMag = SamplerMagFilter::NEAREST;
samplerParams.filterMin = SamplerMinFilter::NEAREST;
auto sampler = mSamplerCache->getSampler({
.sampler = samplerParams,
.conversion = imageData.conversion,
});
actualExternalSamplers.set(bindingInfo.binding);
samplerAndBindings.push_back({ bindingInfo.binding, sampler, bindingInfo.image });
bindingInfo.bound = true;
}
if (samplerAndBindings.empty()) {
return;
}
// Sort by binding number
std::sort(samplerAndBindings.begin(), samplerAndBindings.end(), [](auto const& a, auto const& b) {
return std::get<0>(a) < std::get<0>(b);
});
utils::FixedCapacityVector<VkSampler> outSamplers;
outSamplers.reserve(MAX_SAMPLER_COUNT);
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
[&](auto const& b) { outSamplers.push_back(std::get<1>(b)); });
VkDescriptorSetLayout const oldLayout = layout->getExternalSamplerVkLayout();
VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask,
actualExternalSamplers, outSamplers);
// Need to copy the set
VkDescriptorSet const oldSet = set->getExternalSamplerVkSet();
if (oldLayout != newLayout || oldSet == VK_NULL_HANDLE) {
// Build a new descriptor set from the new layout
VkDescriptorSet const newSet = mDescriptorSetCache->getVkSet(layout->count, newLayout);
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
auto const samplers = layout->bitmask.sampler & (~actualExternalSamplers);
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
// We fold the two stages into just the lower half of the bits to denote a combined set of
// bindings.
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
VkDescriptorSet const srcSet = oldSet != VK_NULL_HANDLE ? oldSet : set->getVkSet();
copySet(mPlatform->getDevice(), srcSet, newSet, copyBindings);
set->setExternalSamplerVkSet(newSet, [&](VulkanDescriptorSet*) {
mDescriptorSetCache->manualRecycle(layout->count, newLayout, newSet);
});
if (oldLayout != newLayout) {
layout->setExternalSamplerVkLayout(newLayout);
}
}
// Update the external samplers in the set
for (auto& [binding, sampler, image]: samplerAndBindings) {
mDescriptorSetCache->updateSamplerForExternalSamplerSet(set, binding, image);
}
}
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
VulkanPlatform::ExternalImageMetadata const& metadata) {
// This external image does not require external sampler (YUV conversion).
if (metadata.externalFormat == 0 && !fvkutils::isVKYcbcrConversionFormat(metadata.format)) {
return VK_NULL_HANDLE;
}
VulkanYcbcrConversionCache::Params ycbcrParams = {
.conversion = {
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
// Unclear where to get the chromaFilter, we just assume it's nearest.
.chromaFilter = SamplerMagFilter::NEAREST,
},
.format = metadata.format,
.externalFormat = metadata.externalFormat,
};
return mYcbcrConversionCache->getConversion(ycbcrParams);
}
void VulkanExternalImageManager::updateImage(ImageData* image) {
if (image->hasBeenValidated) {
return;
}
image->hasBeenValidated = true;
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
if (vkYcbcr == image->conversion) {
return;
}
image->image->setYcbcrConversion(vkYcbcr);
image->conversion = vkYcbcr;
return;
}
void VulkanExternalImageManager::removeDescriptorSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
erasep<SetBindingInfo>(mSetBindings,
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
}
void VulkanExternalImageManager::bindExternallySampledTexture(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
// Should we do duplicate validation here?
auto& imageData = findImage(mImages, image);
mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
}
void VulkanExternalImageManager::addExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image,
Platform::ExternalImageHandleRef platformHandleRef) {
mImages.push_back({ image, platformHandleRef, false });
}
void VulkanExternalImageManager::removeExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image) {
erasep<SetBindingInfo>(mSetBindings,
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
erasep<ImageData>(mImages, [&](auto const& imageData) {
return imageData.image == image;
});
}
bool VulkanExternalImageManager::isExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image) const {
return std::find_if(mImages.begin(), mImages.end(), [&](auto const& imageData) {
return imageData.image == image;
}) != mImages.end();
}
void VulkanExternalImageManager::clearTextureBinding(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint) {
erasep<SetBindingInfo>(mSetBindings, [&](auto const& bindingInfo) {
return (bindingInfo.set == set && bindingInfo.binding == bindingPoint);
});
}
} // namesapce filament::backend

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
#include "VulkanHandles.h"
#include <backend/DriverEnums.h>
#include <array>
#include <vector>
namespace filament::backend {
class VulkanYcbcrConversionCache;
class VulkanSamplerCache;
class VulkanDescriptorSetLayoutCache;
class VulkanDescriptorSetCache;
// Manages the logic of external images and their quirks wrt Vulikan.
class VulkanExternalImageManager {
public:
VulkanExternalImageManager(
VulkanPlatform* platform,
VulkanSamplerCache* samplerCache,
VulkanYcbcrConversionCache* ycbcrConversionCache,
VulkanDescriptorSetCache* setCache,
VulkanDescriptorSetLayoutCache* layoutCache);
~VulkanExternalImageManager();
void terminate();
void onBeginFrame();
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using LayoutArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSetLayout>,
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
using VkLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
// Returns bitmask to indicate whether or not to use the external sampler version of each
// descriptor set.
fvkutils::DescriptorSetMask prepareBindSets(LayoutArray const& layouts, SetArray const& sets);
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
SamplerParams samplerParams);
void clearTextureBinding(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint);
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> external,
Platform::ExternalImageHandleRef platformHandleRef);
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
VulkanPlatform::ExternalImageMetadata const& metadata);
struct ImageData {
fvkmemory::resource_ptr<VulkanTexture> image;
Platform::ExternalImageHandle platformHandle;
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
};
private:
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
void updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void updateImage(ImageData* imageData);
VulkanPlatform* mPlatform;
VulkanSamplerCache* mSamplerCache;
VulkanYcbcrConversionCache* mYcbcrConversionCache;
VulkanDescriptorSetCache* mDescriptorSetCache;
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
struct SetBindingInfo {
uint8_t binding = 0;
fvkmemory::resource_ptr<VulkanTexture> image;
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
SamplerParams samplerParams;
bool bound = false;
};
// Use vectors instead of hash maps because we only expect small number of entries.
std::vector<SetBindingInfo> mSetBindings;
std::vector<ImageData> mImages;
};
} // filament::backend
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H

View File

@@ -330,25 +330,39 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
mRenderPassCache[config] = {renderPass, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
FVK_LOGD << "Created render pass " << renderPass << " with "
<< "samples = " << int(config.samples) << ", "
<< "depth = " << (hasDepth ? 1 : 0) << ", "
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
<< utils::io::endl;
#endif
FVK_LOGD << "Created render pass " << renderPass << " with ";
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
FVK_LOGD << (int) config.colorFormat[i] << " ";
}
FVK_LOGD << ", "
<< "depth = " << config.depthFormat << ", "
<< "initialDepthLayout = " << (int) config.initialDepthLayout << ", "
<< "samples = " << int(config.samples) << ", "
<< "needsResolveMask = " << int(config.needsResolveMask) << ", "
<< "usesLazilyAllocatedMemory = " << int(config.usesLazilyAllocatedMemory) << ", "
<< "viewCount = " << int(config.viewCount) << ", "
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
<< utils::io::endl;
#endif
return renderPass;
}
void VulkanFboCache::reset() noexcept {
for (auto pair : mFramebufferCache) {
void VulkanFboCache::resetFramebuffers() noexcept {
for (const auto& pair: mFramebufferCache) {
mRenderPassRefCount[pair.first.renderPass]--;
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
}
mFramebufferCache.clear();
for (auto pair : mRenderPassCache) {
}
void VulkanFboCache::terminate() noexcept {
resetFramebuffers();
for (const auto& pair: mRenderPassCache) {
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
}
mRenderPassRefCount.clear();
mRenderPassCache.clear();
}

View File

@@ -106,8 +106,11 @@ public:
// Evicts old unused Vulkan objects. Call this once per frame.
void gc() noexcept;
// Frees all Framebuffer objects. Call this every time a the swapchain is resized
void resetFramebuffers() noexcept;
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
void reset() noexcept;
void terminate() noexcept;
private:
VkDevice mDevice;

View File

@@ -30,6 +30,7 @@
#include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION
using namespace bluevk;
@@ -89,8 +90,9 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
}
break;
}
// TODO: properly handle external sampler
case DescriptorType::SAMPLER_EXTERNAL:
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
UTILS_FALLTHROUGH;
case DescriptorType::SAMPLER: {
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
break;
@@ -147,11 +149,18 @@ void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> ob
mResources.push_back(obj);
}
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
VkDescriptorSetLayout vkLayout)
: bitmask(fromBackendLayout(layout)),
count(Count::fromLayoutBitmask(bitmask)) {}
count(Count::fromLayoutBitmask(bitmask)),
mVkLayout(vkLayout) {}
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayoutDescription(
DescriptorSetLayout const& layout) {
return fromBackendLayout(layout);
}
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
mRangeCount = 0;
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
auto const& constants = program.getPushConstants(stage);

View File

@@ -66,18 +66,23 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
// The bitmask representation of a set layout.
struct Bitmask {
// TODO: better utiltize the space below and use bitset instead.
fvkutils::UniformBufferBitmask ubo; // 8 bytes
fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes
fvkutils::SamplerBitmask sampler; // 8 bytes
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
// This is a subset of the sampler field.
fvkutils::SamplerBitmask externalSampler; // 8 bytes
bool operator==(Bitmask const& right) const {
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
inputAttachment == right.inputAttachment;
inputAttachment == right.inputAttachment &&
externalSampler == right.externalSampler;
}
static Bitmask fromLayoutDescription(DescriptorSetLayout const& layout);
};
static_assert(sizeof(Bitmask) == 32);
static_assert(sizeof(Bitmask) == 40);
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set
// pools.
@@ -117,18 +122,32 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
}
};
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
VulkanDescriptorSetLayout(DescriptorSetLayout&& layout, VkDescriptorSetLayout vkLayout);
// Note that we don't destroy the vklayout. This is done by the layout cache.
~VulkanDescriptorSetLayout() = default;
VkDescriptorSetLayout getVkLayout() const { return mVkLayout; }
void setVkLayout(VkDescriptorSetLayout vklayout) { mVkLayout = vklayout; }
VkDescriptorSetLayout getVkLayout() const noexcept { return mVkLayout; }
VkDescriptorSetLayout getExternalSamplerVkLayout() const noexcept {
return mExternalSamplerVkLayout;
}
void setExternalSamplerVkLayout(VkDescriptorSetLayout vklayout) noexcept {
mExternalSamplerVkLayout = vklayout;
}
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
Bitmask const bitmask;
Count const count;
private:
VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE;
// This is the layout without any immutable samplers.
VkDescriptorSetLayout const mVkLayout = VK_NULL_HANDLE;
// This is the layout with immutable samplers, and can be updated.
VkDescriptorSetLayout mExternalSamplerVkLayout = VK_NULL_HANDLE;
};
struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource {
@@ -137,19 +156,39 @@ public:
// can use to repackage the vk handle.
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet(VkDescriptorSet rawSet,
VulkanDescriptorSet(
fvkutils::UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn)
: vkSet(rawSet),
dynamicUboMask(dynamicUboMask),
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
: dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount),
mVkSet(vkSet),
mOnRecycleFn(std::move(onRecycleFn)) {}
// NOLINTNEXTLINE(bugprone-exception-escape)
~VulkanDescriptorSet() {
if (mOnRecycleFn) {
mOnRecycleFn(this);
}
if (mOnRecycleExternalSamplerFn) {
mOnRecycleExternalSamplerFn(this);
}
}
VkDescriptorSet getVkSet() const noexcept {
return mVkSet;
}
VkDescriptorSet getExternalSamplerVkSet() const noexcept {
return mExternalSamplerVkSet;
}
void setExternalSamplerVkSet(VkDescriptorSet vkset, OnRecycle onRecycle) {
mExternalSamplerVkSet = vkset;
if (mOnRecycleExternalSamplerFn) {
mOnRecycleExternalSamplerFn(this);
}
mOnRecycleExternalSamplerFn = onRecycle;
}
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
@@ -163,21 +202,24 @@ public:
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
VkDescriptorSet const vkSet;
fvkutils::UniformBufferBitmask const dynamicUboMask;
uint8_t const uniqueDynamicUboCount;
private:
VkDescriptorSet const mVkSet;
VkDescriptorSet mExternalSamplerVkSet = VK_NULL_HANDLE;
backend::DescriptorSetOffsetArray mOffsets;
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
OnRecycle mOnRecycleFn;
OnRecycle mOnRecycleExternalSamplerFn;
};
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
struct PushConstantDescription {
explicit PushConstantDescription(backend::Program const& program) noexcept;
explicit PushConstantDescription(backend::Program const& program);
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }

View File

@@ -35,7 +35,12 @@ using namespace bluevk;
namespace filament::backend {
VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
: mDevice(device) {}
: mDevice(device) {
VkPipelineCacheCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
};
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
}
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
mPipelineRequirements.layout = layout;
@@ -61,8 +66,12 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
// If an error occurred, allow higher levels to handle it gracefully.
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
mBoundPipeline = mPipelineRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
static PipelineEqual equal;
if (!equal(mBoundPipeline, mPipelineRequirements)) {
mBoundPipeline = mPipelineRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
}
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
@@ -215,7 +224,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
PipelineCacheEntry cacheEntry = {
.lastUsed = mCurrentTime,
};
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle);
assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) {
@@ -271,6 +280,8 @@ void VulkanPipelineCache::terminate() noexcept {
}
mPipelines.clear();
mBoundPipeline = {};
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
}
void VulkanPipelineCache::gc() noexcept {

View File

@@ -198,6 +198,10 @@ private:
// Immutable state.
VkDevice mDevice = VK_NULL_HANDLE;
// Vuklan Driver pipeline cache handle. In the cases a pipeline has been evicted by the `gc`,
// recreating the same pipeline is cheaper, helping with frame stalling.
VkPipelineCache mPipelineCache = VK_NULL_HANDLE;
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
PipelineKey mPipelineRequirements = {};

View File

@@ -335,7 +335,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
mTaskHandler->post(std::move(waitFenceFunc), std::move(cleanPbdFunc));
}
void VulkanReadPixels::runUntilComplete() noexcept {
void VulkanReadPixels::runUntilComplete() {
if (!mTaskHandler) {
return;
}

View File

@@ -79,7 +79,7 @@ public:
OnReadCompleteFunction const& readCompleteFunc);
// This method will block until all of the in-flight requests are complete.
void runUntilComplete() noexcept;
void runUntilComplete();
private:
VkDevice mDevice = VK_NULL_HANDLE;

View File

@@ -28,13 +28,20 @@ namespace filament::backend {
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
VkSampler VulkanSamplerCache::getSampler(Params params) {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;
}
VkSamplerYcbcrConversionInfo ycbcrConversion = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = params.conversion,
};
auto const& samplerParams = params.sampler;
VkSamplerCreateInfo samplerInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
VkSamplerCreateInfo samplerInfo{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = params.conversion != VK_NULL_HANDLE ? &ycbcrConversion : VK_NULL_HANDLE,
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
@@ -48,7 +55,8 @@ VkSampler VulkanSamplerCache::getSampler(Params params) noexcept {
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(samplerParams.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE };
.unnormalizedCoordinates = VK_FALSE,
};
VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."

View File

@@ -38,7 +38,7 @@ public:
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(Params params) noexcept;
VkSampler getSampler(Params params);
void terminate() noexcept;
private:
VkDevice mDevice;

View File

@@ -24,7 +24,7 @@
#include <utils/Panic.h>
static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS;
static constexpr uint32_t TIME_BEFORE_EVICTION = 3;
namespace filament::backend {
@@ -39,7 +39,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
auto stage = iter->second;
mFreeStages.erase(iter);
stage->lastAccessed = mCurrentFrame;
mUsedStages.insert(stage);
mUsedStages.push_back(stage);
return stage;
}
// We were not able to find a sufficiently large stage, so create a new one.
@@ -51,7 +51,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
});
// Create the VkBuffer.
mUsedStages.insert(stage);
mUsedStages.push_back(stage);
VkBufferCreateInfo bufferInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = numBytes,
@@ -77,7 +77,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
if (image->format == vkformat && image->width == width && image->height == height) {
mFreeImages.erase(image);
image->lastAccessed = mCurrentFrame;
mUsedImages.insert(image);
mUsedImages.push_back(image);
return image;
}
}
@@ -89,7 +89,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
.lastAccessed = mCurrentFrame,
});
mUsedImages.insert(image);
mUsedImages.push_back(image);
const VkImageCreateInfo imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
@@ -161,7 +161,7 @@ void VulkanStagePool::gc() noexcept {
stage->lastAccessed = mCurrentFrame;
mFreeStages.insert(std::make_pair(stage->capacity, stage));
} else {
mUsedStages.insert(stage);
mUsedStages.push_back(stage);
}
}
@@ -185,7 +185,7 @@ void VulkanStagePool::gc() noexcept {
image->lastAccessed = mCurrentFrame;
mFreeImages.insert(image);
} else {
mUsedImages.insert(image);
mUsedImages.push_back(image);
}
}
FVK_SYSTRACE_END();

View File

@@ -22,6 +22,7 @@
#include <map>
#include <unordered_set>
#include <vector>
namespace filament::backend {
@@ -73,10 +74,10 @@ private:
std::multimap<uint32_t, VulkanStage const*> mFreeStages;
// Simple unordered set for stashing a list of in-use stages that can be reclaimed later.
std::unordered_set<VulkanStage const*> mUsedStages;
std::vector<VulkanStage const*> mUsedStages;
std::unordered_set<VulkanStageImage const*> mFreeImages;
std::unordered_set<VulkanStageImage const*> mUsedImages;
std::vector<VulkanStageImage const*> mUsedImages;
// Store the current "time" (really just a frame count) and LRU eviction parameters.
uint64_t mCurrentFrame = 0;

View File

@@ -108,11 +108,6 @@ void VulkanSwapChain::present() {
mCommands->flush();
// call the image ready wait function
if (mExplicitImageReadyWait != nullptr) {
mExplicitImageReadyWait(swapChain);
}
// We only present if it is not headless. No-op for headless.
if (!mHeadless) {
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
@@ -146,7 +141,6 @@ void VulkanSwapChain::acquire(bool& resized) {
VulkanPlatform::ImageSyncData imageSyncData;
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex;
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {

View File

@@ -49,7 +49,9 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
void present();
void acquire(bool& reized);
// Acquire a new image from the swapchain. If the image is not available it would wait until it
// is.
void acquire(bool& resized);
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
uint32_t const imageIndex = mCurrentSwapIndex;
@@ -99,7 +101,6 @@ private:
VkExtent2D mExtent;
uint32_t mLayerCount;
uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired;
bool mIsFirstRenderPass;
};

View File

@@ -223,8 +223,7 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands,
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory,
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat, VkImageUsageFlags usage,
bool isProtected)
VkSamplerYcbcrConversion ycbcrConversion, VkImageUsageFlags usage, bool isProtected)
: mStagePool(stagePool),
mCommands(commands),
mAllocator(allocator),
@@ -234,17 +233,17 @@ VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommand
mVkFormat(format),
mViewType(viewType),
mFullViewRange{ fvkutils::getImageAspect(format), 0, levels, 0, layerCount },
mYcbcr{ ycbcrConversion, isExternalFormat },
mYcbcr{ ycbcrConversion },
mDefaultLayout(getDefaultLayoutImpl(usage)),
mUsage(usage),
mIsProtected(isProtected) {}
VulkanTextureState::~VulkanTextureState() {
clearCachedImageViews();
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
clearCachedImageViews();
}
void VulkanTextureState::clearCachedImageViews() noexcept {
@@ -272,7 +271,7 @@ VkImageView VulkanTextureState::getImageView(VkImageSubresourceRange range, VkIm
.flags = 0,
.image = mTextureImage,
.viewType = viewType,
.format = mYcbcr.isExternalFormat ? VK_FORMAT_UNDEFINED : mVkFormat,
.format = mYcbcr.conversion != VK_NULL_HANDLE ? VK_FORMAT_UNDEFINED : mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
@@ -294,7 +293,6 @@ VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaA
commands, allocator, device, image, memory, format,
fvkutils::getViewType(SamplerType::SAMPLER_2D),
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion,
/*isExternalFormat=*/false,
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
any(usage & TextureUsage::PROTECTED))) {
mPrimaryViewRange = mState->mFullViewRange;
@@ -424,8 +422,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
mState = fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
commands, allocator, device, textureImage, textureImageMemory, vkFormat,
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
VK_NULL_HANDLE /* ycbcrConversion */, false /*isExternalFormat*/, imageInfo.usage,
isProtected);
VK_NULL_HANDLE /* ycbcrConversion */, imageInfo.usage, isProtected);
// Spec out the "primary" VkImageView that shaders use to sample from the image.
mPrimaryViewRange = mState->mFullViewRange;
@@ -764,15 +761,13 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
}
}
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternalFormat) {
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion) {
// Note that this comparison is valid because we only ever create VkSamplerYcbcrConversion from
// a cache. So for each set of parameters, there is exactly one conversion (similar to
// samplers).
VulkanTextureState::Ycbcr ycbcr = {
.conversion = conversion,
.isExternalFormat = isExternalFormat,
};
if (mState->mYcbcr != ycbcr) {
mState->mYcbcr = ycbcr;
mState->clearCachedImageViews();

View File

@@ -40,8 +40,7 @@ struct VulkanTextureState : public fvkmemory::Resource {
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat,
VkImageUsageFlags usage, bool isProtected);
VkSamplerYcbcrConversion ycbcrConversion, VkImageUsageFlags usage, bool isProtected);
~VulkanTextureState();
@@ -86,10 +85,9 @@ private:
// conversion matrix per-frame.
struct Ycbcr {
VkSamplerYcbcrConversion conversion;
bool isExternalFormat;
bool operator==(Ycbcr const& other) const {
return conversion == other.conversion && isExternalFormat == other.isExternalFormat;
return conversion == other.conversion;
}
bool operator!=(Ycbcr const& other) const {
@@ -209,7 +207,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// This is used in the case of external images and external samplers. AHB might update the
// conversion per-frame. This implies that we need to invalidate the view cache when that
// happens.
void setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternal);
void setYcbcrConversion(VkSamplerYcbcrConversion conversion);
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
void print() const;

View File

@@ -33,7 +33,7 @@ VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
: mDevice(device) {}
VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
VulkanYcbcrConversionCache::Params params) noexcept {
VulkanYcbcrConversionCache::Params params) {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;
@@ -43,7 +43,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.format = VK_FORMAT_UNDEFINED,
.format = params.format,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
@@ -52,6 +52,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
// We could put this in the platform class, but that seems like a bit of an overkill
#if defined(__ANDROID__)
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
@@ -59,6 +60,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
};
if (params.externalFormat) {
conversionInfo.pNext = &externalFormat;
conversionInfo.format = VK_FORMAT_UNDEFINED;
}
#endif

View File

@@ -30,14 +30,14 @@ namespace filament::backend {
class VulkanYcbcrConversionCache {
public:
struct Params {
SamplerYcbcrConversion conversion = {};
uint32_t padding = 0;
uint64_t externalFormat = 0;
SamplerYcbcrConversion conversion = {}; // 4
VkFormat format; // 4
uint64_t externalFormat = 0; // 8
};
static_assert(sizeof(Params) == 16);
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params) noexcept;
VkSamplerYcbcrConversion getConversion(Params params);
void terminate() noexcept;
private:
@@ -47,7 +47,8 @@ private:
bool operator()(Params lhs, Params rhs) const noexcept {
SamplerYcbcrConversion::EqualTo equal;
return equal(lhs.conversion, rhs.conversion) &&
lhs.externalFormat == rhs.externalFormat;
lhs.externalFormat == rhs.externalFormat &&
lhs.format == rhs.format;
}
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;

View File

@@ -31,6 +31,8 @@ ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck, disablePoolHandleTags) {}
void ResourceManager::gc() noexcept {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("ResourceManager::gc");
auto destroyAll = [this](GcList& list) {
for (auto const& [type, id]: list) {
destroyWithType(type, id);
@@ -49,6 +51,7 @@ void ResourceManager::gc() noexcept {
GcList gcs;
std::swap(gcs, mGcList);
destroyAll(gcs);
FVK_SYSTRACE_END();
}
void ResourceManager::terminate() noexcept {

View File

@@ -132,6 +132,10 @@ public:
return id() == other.id() && type() == other.type();
}
inline bool operator!=(resource_ptr<D> const& other) const {
return !((*this) == other);
}
inline explicit operator bool() const {
return bool(mRef);
}

View File

@@ -211,6 +211,9 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
// This is needed for external images. See VulkanPlatformAndroid
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
#endif
// MoltenVk is the only non-conformant implementation we're interested in.
#if defined(__APPLE__)
@@ -326,7 +329,9 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
}
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
VkPhysicalDeviceFeatures2 const& features,
VkPhysicalDeviceVulkan11Features const& vk11Features,
uint32_t graphicsQueueFamilyIndex,
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
bool requestImageView2DOn3DImage) {
VkDevice device;
@@ -359,14 +364,28 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
VkPhysicalDeviceFeatures enabledFeatures = {
.depthClamp = features.features.depthClamp,
.samplerAnisotropy = features.features.samplerAnisotropy,
.textureCompressionETC2 = features.features.textureCompressionETC2,
.textureCompressionBC = features.features.textureCompressionBC,
.shaderClipDistance = features.features.shaderClipDistance,
};
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
VkPhysicalDeviceFeatures2 enabledFeatures2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.features = enabledFeatures,
};
chainStruct(&deviceCreateInfo, &enabledFeatures2);
VkPhysicalDeviceVulkan11Features enabledVk11Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
.multiview = vk11Features.multiview,
#if defined(__ANDROID__)
.samplerYcbcrConversion = vk11Features.samplerYcbcrConversion,
#endif
};
chainStruct(&deviceCreateInfo, &enabledVk11Features);
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
@@ -383,7 +402,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
.multiview = VK_TRUE,
.multiview = vk11Features.multiview,
.multiviewGeometryShader = VK_FALSE,
.multiviewTessellationShader = VK_FALSE,
};
@@ -734,6 +753,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
};
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPhysicalDeviceVk11Features);
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
@@ -795,10 +815,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
}
if (mImpl->mDevice == VK_NULL_HANDLE) {
mImpl->mDevice =
createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice,
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features,
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
}
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
@@ -826,12 +846,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
if (!mImpl->mSharedContext) {
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
} else {
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
context.mMultiviewEnabled = scontext->multiviewSupported;
}
// Check the availability of lazily allocated memory
@@ -977,30 +995,6 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
return mImpl->mProtectedGraphicsQueue;
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
usage);
}
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat) {
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
}
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle) {
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}

View File

@@ -15,14 +15,14 @@
*/
#include <backend/platforms/VulkanPlatformAndroid.h>
#include "vulkan/VulkanConstants.h"
#include "vulkan/VulkanContext.h"
#include <backend/DriverEnums.h>
#include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h"
#include <utils/Panic.h>
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Conversion.h"
#include <bluevk/BlueVK.h>
@@ -39,6 +39,14 @@ namespace {
VkFormat transformVkFormat(VkFormat format, bool sRGB) {
if (!sRGB) {
switch (format) {
case VK_FORMAT_R8G8B8A8_SRGB:
return VK_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_R8G8B8_SRGB:
return VK_FORMAT_R8G8B8_UNORM;
default:
break;
}
return format;
}
@@ -57,7 +65,7 @@ VkFormat transformVkFormat(VkFormat format, bool sRGB) {
}
bool isProtectedFromUsage(uint64_t usage) {
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
return usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
}
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
@@ -120,7 +128,9 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
usage = 0;
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
// We shouldn't be using external samplers as input attachments
// usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
if (isDepthFormat) {
@@ -136,87 +146,37 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
return { format, usage };
}
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage) {
VulkanPlatform::ImageData data;
// if external format we need to specifiy it in the allocation
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
const VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
// pass down the format (external means we don't have it VK defined)
.externalFormat = metadata.externalFormat,
std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardwareBuffer_Desc& desc,
bool sRGB) {
auto const format = mapToFilamentFormat(desc.format, sRGB);
return {
format,
mapToFilamentUsage(desc.usage, format),
};
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = useExternalFormat ? &externalFormat : nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imageInfo.pNext = &externalCreateInfo;
imageInfo.format = metadata.format;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent = {
metadata.width,
metadata.height,
1u,
};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = metadata.layers;
imageInfo.samples = metadata.samples;
imageInfo.usage = usage;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
// Allocate the memory
VkImportAndroidHardwareBufferInfoANDROID androidHardwareBufferInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.pNext = nullptr,
.buffer = buffer,
};
VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &androidHardwareBufferInfo,
.image = data.first,
.buffer = VK_NULL_HANDLE,
};
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex,
};
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
return data;
}
}// namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
if (__builtin_available(android 26, *)) {
if (aHardwareBuffer) {
AHardwareBuffer_release(aHardwareBuffer);
}
}
}
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
AHardwareBuffer const* buffer, bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
auto bufferImpl = const_cast<AHardwareBuffer*>(buffer);
AHardwareBuffer_acquire(bufferImpl);
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->format = textureFormat;
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return Platform::ExternalImageHandle{ p };
}
@@ -225,23 +185,20 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
auto metadata = extractExternalImageMetadata(externalImage);
return {
.width = fvkExternalImage->width,
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
.width = metadata.width,
.height = metadata.height,
.format = metadata.filamentFormat,
.usage = metadata.filamentUsage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::extractExternalImageMetadata(
ExternalImageHandleRef image) const {
auto const* fvkExternalImage = static_cast<ExternalImageVulkanAndroid const*>(image.get());
ExternalImageMetadata metadata;
ExternalImageMetadata metadata = {};
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc bufferDesc;
@@ -249,16 +206,29 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
metadata.width = bufferDesc.width;
metadata.height = bufferDesc.height;
metadata.layers = bufferDesc.layers;
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
std::tie(metadata.filamentFormat, metadata.filamentUsage) =
getFilamentFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
if (isProtectedFromUsage(bufferDesc.usage)) {
metadata.filamentUsage |= TextureUsage::PROTECTED;
}
// TODO: The following seems unnecessary. we should be able to discern directly from the
// bufferDesc.
if (any(metadata.filamentUsage & TextureUsage::BLIT_SRC)) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(metadata.filamentUsage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
}
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
.pNext = nullptr,
};
VkAndroidHardwareBufferPropertiesANDROID properties = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
@@ -268,138 +238,155 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
<< "mismatched image format( " << metadata.format << ") and queried format("
<< bufferPropertiesFormat << ") for external image (AHB)";
metadata.externalFormat = formatInfo.externalFormat;
bool const requiresConversion =
metadata.format == VK_FORMAT_UNDEFINED ||
fvkutils::isVKYcbcrConversionFormat(metadata.format);
if (requiresConversion) {
metadata.format = VK_FORMAT_UNDEFINED;
metadata.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
metadata.externalFormat = formatInfo.externalFormat;
} else {
metadata.externalFormat = 0;
}
metadata.allocationSize = properties.allocationSize;
metadata.memoryTypeBits = properties.memoryTypeBits;
metadata.ycbcrConversionComponents = formatInfo.samplerYcbcrConversionComponents;
metadata.ycbcrModel = formatInfo.suggestedYcbcrModel;
metadata.ycbcrRange = formatInfo.suggestedYcbcrRange;
metadata.xChromaOffset = formatInfo.suggestedXChromaOffset;
metadata.yChromaOffset = formatInfo.suggestedYChromaOffset;
return metadata;
}
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
ExternalImageHandleRef externalImage) const {
auto metadata = extractExternalImageMetadata(externalImage);
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
memoryTypeIndex, usage);
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return data;
}
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle){
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
VkDevice const device = getDevice();
VkPhysicalDevice const physicalDevice = getPhysicalDevice();
auto buildImage = [&](ExternalImageMetadata const& metadata) {
bool const isExternal = metadata.externalFormat != 0;
VkExternalFormatANDROID const externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = metadata.externalFormat,
};
VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
.pNext = isExternal ? &externalFormat : nullptr,
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkFormat formats[2] = {};
VkImageFormatListCreateInfo imageFormatListInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO,
.pNext = nullptr,
.viewFormatCount = 2,
.pViewFormats = formats,
};
if (fvkExternalImage->sRGB) {
formats[0] = metadata.format;
formats[1] = transformVkFormat(metadata.format, /*sRGB=*/false);
imageFormatListInfo.pNext = externalCreateInfo.pNext;
externalCreateInfo.pNext = &imageFormatListInfo;
}
VkImageCreateInfo const imageInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = &externalCreateInfo,
.flags = fvkExternalImage->sRGB ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0u,
.imageType = VK_IMAGE_TYPE_2D,
// For non external images, use the same format as the AHB, which isn't in SRGB
// Fix VUID-VkMemoryAllocateInfo-pNext-02387
.format = transformVkFormat(metadata.format, /*sRGB=*/false),
.extent = {
metadata.width,
metadata.height,
1u,
},
.mipLevels = 1,
.arrayLayers = metadata.layers,
.samples = metadata.samples,
.usage = metadata.usage,
};
VkImage image;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
return image;
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
auto allocMem = [&](VkImage image, ExternalImageMetadata const& metadata) {
bool const isExternal = metadata.externalFormat != 0;
// Allocate the memory
VkImportAndroidHardwareBufferInfoANDROID const androidHardwareBufferInfo = {
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
.buffer = buffer,
};
VkMemoryDedicatedAllocateInfo const memoryDedicatedAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
.pNext = &androidHardwareBufferInfo,
.image = image,
.buffer = VK_NULL_HANDLE,
};
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
VkMemoryPropertyFlags requiredMemoryFlags =
!isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = conversion,
if (any(metadata.filamentUsage & TextureUsage::PROTECTED)) {
requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
}
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
metadata.memoryTypeBits, requiredMemoryFlags);
VkMemoryAllocateInfo const allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex,
};
VkDeviceMemory memory;
VkResult result = vkAllocateMemory(device, &allocInfo, VKALLOC, &memory);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
result = vkBindImageMemory(getDevice(), image, memory, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return memory;
};
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.flags = 0,
.image = image,
.viewType = viewType,
.format = VK_FORMAT_UNDEFINED,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create VkImageView."
<< " error=" << static_cast<int32_t>(result);
VulkanPlatform::ImageData::Bundle internal = {}, external = {};
auto img = buildImage(metadata);
auto mem = allocMem(img, metadata);
return imageView;
}
// Note that we're always choosing a non-externally sampled format if it exists.
if (metadata.externalFormat == 0) {
internal = { img, mem };
} else {
external = { img, mem };
}
VkSampler VulkanPlatform::createExternalSamplerImpl(
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
uint32_t internalFormat) {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
return {
.internal = internal,
.external = external,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
};
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.magFilter = fvkutils::getFilter(params.filterMag),
.minFilter = fvkutils::getFilter(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
.addressModeU = fvkutils::getWrapMode(params.wrapS),
.addressModeV = fvkutils::getWrapMode(params.wrapT),
.addressModeW = fvkutils::getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VkSampler sampler;
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
return sampler;
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
@@ -413,7 +400,7 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
VkSurfaceKHR surface;
VkExtent2D extent;
VkAndroidSurfaceCreateInfoKHR const createInfo{
VkAndroidSurfaceCreateInfoKHR const createInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
.window = (ANativeWindow*) nativeWindow,
};
@@ -427,20 +414,9 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
// Deprecated platform dependent helper methods
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return ExternalImageMetadata{};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return ImageData{};
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
return SurfaceBundle{};
}
}// namespace filament::backend
} // namespace filament::backend

View File

@@ -24,28 +24,12 @@
#include <bluevk/BlueVK.h>
// Platform specific includes and defines
#if defined(__APPLE__)
#include <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#include <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#ifndef VK_MVK_macos_surface
#error VK_MVK_macos_surface is not defined
#endif
#elif defined(FILAMENT_IOS)
// Metal is not available when building for the iOS simulator on Desktop.
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
#if METAL_AVAILABLE
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#endif
#ifndef VK_MVK_ios_surface
#error VK_MVK_ios_surface is not defined
#endif
#define METALVIEW_TAG 255
#else
#error Not a supported Apple + Vulkan platform
#ifndef VK_MVK_macos_surface
#error VK_MVK_macos_surface is not defined
#endif
using namespace bluevk;
@@ -54,72 +38,28 @@ namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
ExtensionSet const ret = {
#if defined(__APPLE__)
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
VK_MVK_IOS_SURFACE_EXTENSION_NAME,
#endif
};
return ret;
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
#if defined(__APPLE__)
NSView* nsview = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
NSView* nsview = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
<< "Unable to load vkCreateMacOSSurfaceMVK.";
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pView = (__bridge void*) nsview;
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
<< "Unable to load vkCreateIOSSurfaceMVK function.";
VkIOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = metalLayer;
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
<< "Unable to load vkCreateMacOSSurfaceMVK.";
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pView = (__bridge void*) nsview;
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
#endif
return std::make_tuple(surface, VkExtent2D{});
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
return std::make_tuple(surface, VkExtent2D{});
}
} // namespace filament::backend

View File

@@ -84,30 +84,6 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)

View File

@@ -51,6 +51,8 @@ VkFormat getVkFormat(ElementType type, bool normalized, bool integer) {
return VK_FORMAT_UNDEFINED;
}
}
// Non-normalized case
switch (type) {
// Single Component Types
case ElementType::BYTE: return integer ? VK_FORMAT_R8_SINT : VK_FORMAT_R8_SSCALED;
@@ -668,4 +670,111 @@ VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
return flags;
}
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
SamplerYcbcrModelConversion model) {
switch (model) {
case SamplerYcbcrModelConversion::RGB_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_709:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
case SamplerYcbcrModelConversion::YCBCR_601:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
case SamplerYcbcrModelConversion::YCBCR_2020:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
switch (range) {
case SamplerYcbcrRange::ITU_FULL:
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
case SamplerYcbcrRange::ITU_NARROW:
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkChromaLocation getChromaLocation(ChromaLocation loc) {
switch (loc) {
case ChromaLocation::COSITED_EVEN:
return VK_CHROMA_LOCATION_COSITED_EVEN;
case ChromaLocation::MIDPOINT:
return VK_CHROMA_LOCATION_MIDPOINT;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model) {
switch (model) {
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY:
return SamplerYcbcrModelConversion::RGB_IDENTITY;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY:
return SamplerYcbcrModelConversion::YCBCR_IDENTITY;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709:
return SamplerYcbcrModelConversion::YCBCR_709;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601:
return SamplerYcbcrModelConversion::YCBCR_601;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020:
return SamplerYcbcrModelConversion::YCBCR_2020;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range) {
switch (range) {
case VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
return SamplerYcbcrRange::ITU_FULL;
case VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
return SamplerYcbcrRange::ITU_NARROW;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
ChromaLocation getChromaLocationFilament(VkChromaLocation loc) {
switch (loc) {
case VK_CHROMA_LOCATION_COSITED_EVEN:
return ChromaLocation::COSITED_EVEN;
case VK_CHROMA_LOCATION_MIDPOINT:
return ChromaLocation::MIDPOINT;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex) {
switch (c) {
case VK_COMPONENT_SWIZZLE_ZERO:
return TextureSwizzle::SUBSTITUTE_ZERO;
case VK_COMPONENT_SWIZZLE_ONE:
return TextureSwizzle::SUBSTITUTE_ONE;
case VK_COMPONENT_SWIZZLE_IDENTITY:
return (TextureSwizzle) (((uint8_t) TextureSwizzle::CHANNEL_0) + rgbaIndex);
case VK_COMPONENT_SWIZZLE_R:
return TextureSwizzle::CHANNEL_0;
case VK_COMPONENT_SWIZZLE_G:
return TextureSwizzle::CHANNEL_1;
case VK_COMPONENT_SWIZZLE_B:
return TextureSwizzle::CHANNEL_2;
case VK_COMPONENT_SWIZZLE_A:
return TextureSwizzle::CHANNEL_3;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
} // namespace filament::backend::fvkutils

View File

@@ -69,6 +69,12 @@ VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversio
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
VkChromaLocation getChromaLocation(ChromaLocation loc);
// Ycbcr related functions
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model);
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range);
ChromaLocation getChromaLocationFilament(VkChromaLocation loc);
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex);
inline VkImageViewType getViewType(SamplerType target) {
switch (target) {
case SamplerType::SAMPLER_CUBEMAP:

View File

@@ -348,6 +348,10 @@ using SamplerBitmask = utils::bitset64;
// general.
using InputAttachmentBitmask = utils::bitset64;
constexpr uint8_t MAX_DESCRIPTOR_SET_BITMASK_BITS =
std::max(std::max(sizeof(UniformBufferBitmask), sizeof(SamplerBitmask)),
sizeof(InputAttachmentBitmask)) * 8;
template<typename Bitmask>
static constexpr uint8_t getVertexStageShift() noexcept {
// We assume the bottom half of bits are for vertex stages.

View File

@@ -184,6 +184,46 @@ bool isVkStencilFormat(VkFormat format) {
return (getImageAspect(format) & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
}
bool isVKYcbcrConversionFormat(VkFormat format) {
switch (format) {
case VK_FORMAT_G8B8G8R8_422_UNORM:
case VK_FORMAT_B8G8R8G8_422_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16B16G16R16_422_UNORM:
case VK_FORMAT_B16G16R16G16_422_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_444_UNORM:
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16_B16R16_2PLANE_444_UNORM:
return true;
default:
return false;
}
}
static uint32_t mostSignificantBit(uint32_t x) { return 1ul << (31ul - utils::clz(x)); }
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
@@ -193,49 +233,6 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
return mostSignificantBit((sampleCount - 1) & mask);
}
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
SamplerYcbcrModelConversion model) {
switch (model) {
case SamplerYcbcrModelConversion::RGB_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_709:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
case SamplerYcbcrModelConversion::YCBCR_601:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
case SamplerYcbcrModelConversion::YCBCR_2020:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
switch (range) {
case SamplerYcbcrRange::ITU_FULL:
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
case SamplerYcbcrRange::ITU_NARROW:
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkChromaLocation getChromaLocation(ChromaLocation loc) {
switch (loc) {
case ChromaLocation::COSITED_EVEN:
return VK_CHROMA_LOCATION_COSITED_EVEN;
case ChromaLocation::MIDPOINT:
return VK_CHROMA_LOCATION_MIDPOINT;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
} // namespace filament::backend::fvkutils
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {

View File

@@ -98,6 +98,8 @@ bool isVkDepthFormat(VkFormat format);
bool isVkStencilFormat(VkFormat format);
bool isVKYcbcrConversionFormat(VkFormat format);
VkImageAspectFlags getImageAspect(VkFormat format);
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WebGPUHandles.h"
#include "WebGPUConstants.h"
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
#include <sstream>
#include <string_view>
#include <vector>
namespace filament::backend {
namespace {
[[nodiscard]] constexpr std::string_view toString(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX:
return "vertex";
case ShaderStage::FRAGMENT:
return "fragment";
case ShaderStage::COMPUTE:
return "compute";
}
}
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, const char* programName,
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
shaderSource,
ShaderStage stage) {
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
shaderSource[static_cast<size_t>(stage)];
if (sourceBytes.empty()) {
return nullptr;// nothing to compile, the shader was not provided
}
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.code = wgpu::StringView(reinterpret_cast<const char*>(sourceBytes.data()));
std::stringstream labelStream;
labelStream << programName << " " << toString(stage) << " shader";
auto label = labelStream.str();
wgpu::ShaderModuleDescriptor descriptor{
.nextInChain = &wgslDescriptor,
.label = label.data()
};
wgpu::ShaderModule module = device.CreateShaderModule(&descriptor);
FILAMENT_CHECK_POSTCONDITION(module != nullptr) << "Failed to create " << descriptor.label;
wgpu::Instance instance = device.GetAdapter().GetInstance();
instance.WaitAny(
module.GetCompilationInfo(wgpu::CallbackMode::WaitAnyOnly,
[&descriptor](auto const& status,
wgpu::CompilationInfo const* info) {
switch (status) {
case wgpu::CompilationInfoRequestStatus::CallbackCancelled:
FWGPU_LOGW << "Shader compilation info callback cancelled for "
<< descriptor.label << "?" << utils::io::endl;
return;
case wgpu::CompilationInfoRequestStatus::Success:
break;
}
if (info != nullptr) {
std::stringstream errorStream;
int errorCount = 0;
for (size_t msgIndex = 0; msgIndex < info->messageCount; msgIndex++) {
wgpu::CompilationMessage const& message = info->messages[msgIndex];
switch (message.type) {
case wgpu::CompilationMessageType::Info:
FWGPU_LOGI << descriptor.label << ": " << message.message
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length
<< utils::io::endl;
break;
case wgpu::CompilationMessageType::Warning:
FWGPU_LOGW
<< "Warning compiling " << descriptor.label << ": "
<< message.message << " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Error:
errorCount++;
errorStream << "Error " << errorCount << " : "
<< std::string_view(message.message)
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << "\n";
break;
}
}
FILAMENT_CHECK_POSTCONDITION(errorCount < 1)
<< errorCount << " error(s) compiling " << descriptor.label
<< ":\n"
<< errorStream.str();
}
FWGPU_LOGD << descriptor.label << " compiled successfully"
<< utils::io::endl;
}),
UINT16_MAX);
return module;
}
std::vector<wgpu::ConstantEntry> convertConstants(
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
constantsInfo) {
std::vector<wgpu::ConstantEntry> constants(constantsInfo.size());
for (size_t i = 0; i < constantsInfo.size(); i++) {
filament::backend::Program::SpecializationConstant const& specConstant = constantsInfo[i];
wgpu::ConstantEntry& constantEntry = constants[i];
constantEntry.key = wgpu::StringView(std::to_string(specConstant.id));
if (auto* v = std::get_if<int32_t>(&specConstant.value)) {
constantEntry.value = static_cast<double>(*v);
} else if (auto* f = std::get_if<float>(&specConstant.value)) {
constantEntry.value = static_cast<double>(*f);
} else if (auto* b = std::get_if<bool>(&specConstant.value)) {
constantEntry.value = *b ? 0.0 : 1.0;
}
}
return constants;
}
}// namespace
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
: HwProgram(program.getName()),
vertexShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::VERTEX)),
fragmentShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::FRAGMENT)),
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::COMPUTE)),
constants(convertConstants(program.getSpecializationConstants())) {}
}// namespace filament::backend

View File

@@ -16,7 +16,8 @@
#include "webgpu/WebGPUDriver.h"
#include "webgpu/WebGPUConstants.h"
#include "WebGPUPipelineCreation.h"
#include "WebGPUSwapChain.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "CommandStreamDispatcher.h"
@@ -24,18 +25,20 @@
#include "private/backend/Dispatcher.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/TargetBufferInfo.h>
#include <math/mat3.h>
#include <utils/CString.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
@@ -183,8 +186,6 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printDeviceDetails(wgpu::Device const& device) {
wgpu::SupportedFeatures supportedFeatures{};
@@ -228,6 +229,15 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(mPlatform.getInstance());
#endif
mAdapter = mPlatform.requestAdapter(nullptr);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(mAdapter);
#endif
mDevice = mPlatform.requestDevice(mAdapter);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
mQueue = mDevice.GetQueue();
}
WebGPUDriver::~WebGPUDriver() noexcept = default;
@@ -256,6 +266,7 @@ void WebGPUDriver::terminate() {
}
void WebGPUDriver::tick(int) {
mDevice.Tick();
}
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
@@ -285,37 +296,52 @@ void WebGPUDriver::finish(int) {
}
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (rph) {
destructHandle<WGPURenderPrimitive>(rph);
}
}
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (vbih) {
destructHandle<WGPUVertexBufferInfo>(vbih);
}
}
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (vbh) {
destructHandle<WGPUVertexBuffer>(vbh);
}
}
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
if (ibh) {
destructHandle<WGPUIndexBuffer>(ibh);
}
}
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
if (boh) {
destructHandle<WGPUBufferObject>(boh);
}
}
void WebGPUDriver::destroyTexture(Handle<HwTexture> th) {
}
void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
if (ph) {
destructHandle<WGPUProgram>(ph);
}
}
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
if (sch) {
destructHandle<WebGPUSwapChain>(sch);
}
mSwapChain = nullptr;
// TODO: use webgpu handle allocator from
// https://github.com/google/filament/pull/8566
// if (sch) {
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
// destruct(sch, hwSwapChain);
// }
}
void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
@@ -325,16 +351,19 @@ void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
if (tqh) {
destructHandle<WebGPUDescriptorSetLayout>(tqh);
}
}
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
if (tqh) {
destructHandle<WebGPUDescriptorSet>(tqh);
}
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
// TODO: use webgpu handle allocator from.
// https://github.com/google/filament/pull/8566
// return allocAndConstructHandle<HwSwapChain>();
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
return allocHandle<WebGPUSwapChain>();
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
@@ -342,15 +371,13 @@ Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WGPUTexture>(); }
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
return Handle<HwProgram>((Handle<HwProgram>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUProgram>();
}
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
@@ -366,74 +393,66 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
}
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUBufferObject>();
}
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
return allocHandle<WGPURenderTarget>();
}
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUVertexBuffer>();
}
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
return Handle<HwDescriptorSet>((Handle<HwDescriptorSet>::HandleId) mNextFakeHandle++);
return allocHandle<WebGPUDescriptorSet>();
}
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
return allocHandle<WGPURenderPrimitive>();
}
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUVertexBufferInfo>();
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
return allocHandle<WGPURenderTarget>();
}
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
return Handle<HwDescriptorSetLayout>(
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
return allocHandle<WebGPUDescriptorSetLayout>();
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
return allocHandle<WGPUTexture>();
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
// TODO: use webgpu handle allocator from.
// https://github.com/google/filament/pull/8566
// HwSwapChain* hwSwapChain = handleCast<HwSwapChain*>(sch);
mSwapChain = nullptr;
mNativeWindow = nativeWindow;
assert_invariant(!mSwapChain);
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
mAdapter = mPlatform.requestAdapter(surface);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(mAdapter);
#endif
mDevice = mPlatform.requestDevice(mAdapter);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
mQueue = mDevice.GetQueue();
mSwapChain = std::make_unique<WebGPUSwapChain>(std::move(surface), mAdapter, mDevice, flags);
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
mDevice, flags);
assert_invariant(mSwapChain);
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
@@ -446,59 +465,98 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
"rebuilding Filament with that flag, e.g. ./build.sh -x "
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
#endif
// TODO: use webgpu handle allocator from.
// https://github.com/google/filament/pull/8566
// hwSwapChain->swapChain = mSwapChain.get();
}
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {}
uint8_t attributeCount, AttributeArray attributes) {
constructHandle<WGPUVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {}
Handle<HwVertexBufferInfo> vbih) {
auto* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(vbih);
constructHandle<WGPUVertexBuffer>(vbh, mDevice, vertexCount, vertexBufferInfo->bufferCount,
vbih);
}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {}
uint32_t indexCount, BufferUsage usage) {
auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
constructHandle<WGPUIndexBuffer>(ibh, mDevice, elementSize, indexCount);
}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {}
BufferObjectBinding bindingType, BufferUsage usage) {
constructHandle<WGPUBufferObject>(boh, mDevice, bindingType, byteCount);
}
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {}
TextureUsage usage) {
constructHandle<WGPUTexture>(th, target, levels, format, samples, w, h, depth, usage, mDevice);
}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {}
uint8_t baseLevel, uint8_t levelCount) {
auto source = handleCast<WGPUTexture>(srch);
constructHandle<WGPUTexture>(th, source, baseLevel, levelCount);
}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {}
backend::TextureSwizzle a) {
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {}
Platform::ExternalImageHandleRef externalImage) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {}
void* externalImage) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {}
void* image, uint32_t plane) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage usage) {}
uint32_t depth, TextureUsage usage) {
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {
assert_invariant(mDevice);
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
auto* renderPrimitive = constructHandle<WGPURenderPrimitive>(rph);
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* indexBuffer = handleCast<WGPUIndexBuffer>(ibh);
renderPrimitive->vertexBuffer = vertexBuffer;
renderPrimitive->indexBuffer = indexBuffer;
renderPrimitive->type = pt;
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
constructHandle<WGPUProgram>(ph, mDevice, program);
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
assert_invariant(mDefaultRenderTarget);
}
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
@@ -509,10 +567,15 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {}
backend::DescriptorSetLayout&& info) {
constructHandle<WebGPUDescriptorSetLayout>(dslh, std::move(info), mDevice);
}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {}
Handle<HwDescriptorSetLayout> dslh) {
auto layout = handleCast<WebGPUDescriptorSetLayout>(dslh);
constructHandle<WebGPUDescriptorSet>(dsh, layout->getLayout(), layout->getLayoutSize());
}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
return {};
@@ -548,11 +611,11 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool WebGPUDriver::isTextureFormatSupported(TextureFormat format) {
return true;
return WGPUTexture::fToWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
}
bool WebGPUDriver::isTextureSwizzleSupported() {
return true;
return false;
}
bool WebGPUDriver::isTextureFormatMipmappable(TextureFormat format) {
@@ -641,24 +704,30 @@ size_t WebGPUDriver::getMaxArrayTextureLayers() {
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
updateGPUBuffer(handleCast<WGPUIndexBuffer>(ibh), std::move(p), byteOffset);
}
void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset);
}
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh,
BufferDescriptor&& p, uint32_t byteOffset) {
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset);
}
void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
// Is there something that needs to be done here? Vulkan has left it unimplemented.
}
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
Handle<HwBufferObject> boh) {
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
assert_invariant(index < vertexBuffer->buffers.size());
assert_invariant(bufferObject->buffer.GetUsage() & wgpu::BufferUsage::Vertex);
vertexBuffer->buffers[index] = bufferObject->buffer;
}
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
@@ -691,18 +760,72 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
}
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "command_encoder"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
red -= 0.01;
} else {
red = 1.0f;
}
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 = {
.colorAttachmentCount = 1,
.colorAttachments = &renderPassColorAttachment,
.depthStencilAttachment = nullptr,
.timestampWrites = nullptr,
};
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
}
void WebGPUDriver::endRenderPass(int) {
mRenderPassEncoder.End();
mRenderPassEncoder = nullptr;
wgpu::CommandBufferDescriptor commandBufferDescriptor {
.label = "command_buffer",
};
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
assert_invariant(mCommandBuffer);
}
void WebGPUDriver::nextSubpass(int) {
}
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
"WebGPU driver does not support distinct draw/read swap chains.");
auto* swapChain = handleCast<WebGPUSwapChain>(drawSch);
mSwapChain = swapChain;
assert_invariant(mSwapChain);
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mTextureView = mSwapChain->getCurrentSurfaceTextureView(surfaceSize);
assert_invariant(mTextureView);
}
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
mCommandEncoder = nullptr;
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
mTextureView = nullptr;
assert_invariant(mSwapChain);
mSwapChain->present();
}
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -730,7 +853,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> src,
scheduleDestroy(std::move(p));
}
void WebGPUDriver::readBufferSubData(backend::BufferObjectHandle boh,
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
scheduleDestroy(std::move(p));
}
@@ -753,9 +876,60 @@ void WebGPUDriver::blit(
}
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr &&
"WebGPU backend does not (yet) support compute pipelines.");
FILAMENT_CHECK_POSTCONDITION(program->vertexShaderModule)
<< "WebGPU backend requires a vertex shader module for a render pipeline";
std::array<wgpu::BindGroupLayout, MAX_DESCRIPTOR_SET_COUNT> bindGroupLayouts{};
assert_invariant(bindGroupLayouts.size() >= pipelineState.pipelineLayout.setLayout.size());
size_t bindGroupLayoutCount = 0;
for (size_t i = 0; i < bindGroupLayouts.size(); i++) {
const auto handle = pipelineState.pipelineLayout.setLayout[bindGroupLayoutCount];
if (handle.getId() == HandleBase::nullid) {
continue;
}
bindGroupLayouts[bindGroupLayoutCount++] =
handleCast<WebGPUDescriptorSetLayout>(handle)->getLayout();
}
std::stringstream layoutLabelStream;
layoutLabelStream << program->name.c_str() << " layout";
const auto layoutLabel = layoutLabelStream.str();
const wgpu::PipelineLayoutDescriptor layoutDescriptor{
.label = wgpu::StringView(layoutLabel),
.bindGroupLayoutCount = bindGroupLayoutCount,
.bindGroupLayouts = bindGroupLayouts.data()
// TODO investigate immediateDataRangeByteSize
};
const wgpu::PipelineLayout layout = mDevice.CreatePipelineLayout(&layoutDescriptor);
FILAMENT_CHECK_POSTCONDITION(layout)
<< "Failed to create wgpu::PipelineLayout for render pipeline for "
<< layoutDescriptor.label;
auto const* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(pipelineState.vertexBufferInfo);
assert_invariant(vertexBufferInfo);
const wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program,
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat());
// TODO: uncomment once we have a valid pipeline to set
// mRenderPassEncoder.SetPipeline(pipeline);
}
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
// This *must* match the WGPUVertexBufferInfo that was bound in bindPipeline(). But we want
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
assert_invariant(
vbi->getVertexBufferLayoutSize() == renderPrimitive->vertexBuffer->buffers.size());
for (uint32_t i = 0; i < vbi->getVertexBufferLayoutSize(); i++) {
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]);
}
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->buffer,
renderPrimitive->indexBuffer->indexFormat);
}
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
@@ -781,28 +955,165 @@ void WebGPUDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void WebGPUDriver::resetState(int) {
}
void WebGPUDriver::updateDescriptorSetBuffer(
backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding,
backend::BufferObjectHandle boh,
uint32_t offset,
void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding, Handle<HwBufferObject> boh, uint32_t offset,
uint32_t size) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
auto buffer = handleCast<WGPUBufferObject>(boh);
if (!bindGroup->getIsLocked()) {
// TODO making assumptions that size and offset mean the same thing here.
wgpu::BindGroupEntry entry{ .binding = static_cast<uint32_t>(binding * 2),
.buffer = buffer->buffer,
.offset = offset,
.size = size };
bindGroup->addEntry(entry.binding, std::move(entry));
}
}
void WebGPUDriver::updateDescriptorSetTexture(
backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding,
backend::TextureHandle th,
SamplerParams params) {
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
backend::descriptor_binding_t binding, Handle<HwTexture> th, SamplerParams params) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
auto texture = handleCast<WGPUTexture>(th);
if (!bindGroup->getIsLocked()) {
// Dawn will cache duplicate samplers, so we don't strictly need to maintain a cache.
// Making a cache might save us minor perf by reducing param translation
auto sampler = makeSampler(params);
// TODO making assumptions that size and offset mean the same thing here.
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
.textureView = texture->getTexView() };
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
.sampler = sampler };
bindGroup->addEntry(sEntry.binding, std::move(sEntry));
}
}
void WebGPUDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh, backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
// TODO: presume we need this, use it. Probably Encoder::SetBindGroup
auto wbg = bindGroup->lockAndReturn(mDevice);
}
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
wgpu::SamplerDescriptor desc;
desc.label = "TODO";
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
switch (params.filterMag) {
case SamplerMagFilter::NEAREST: {
desc.magFilter = wgpu::FilterMode::Nearest;
break;
}
case SamplerMagFilter::LINEAR: {
desc.magFilter = wgpu::FilterMode::Linear;
break;
}
}
switch (params.filterMin) {
case SamplerMinFilter::NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
// suffice
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
// suffice
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
}
switch (params.compareFunc) {
case SamplerCompareFunc::LE: {
desc.compare = wgpu::CompareFunction::LessEqual;
break;
}
case SamplerCompareFunc::GE: {
desc.compare = wgpu::CompareFunction::GreaterEqual;
break;
}
case SamplerCompareFunc::L: {
desc.compare = wgpu::CompareFunction::Less;
break;
}
case SamplerCompareFunc::G: {
desc.compare = wgpu::CompareFunction::Greater;
break;
}
case SamplerCompareFunc::E: {
desc.compare = wgpu::CompareFunction::Equal;
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;
// Unused: Filament's compareMode, WGPU lodMinClamp/lodMaxClamp
return mDevice.CreateSampler();
}
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
switch (fWrapMode) {
case SamplerWrapMode::CLAMP_TO_EDGE: {
return wgpu::AddressMode::ClampToEdge;
break;
}
case SamplerWrapMode::REPEAT: {
return wgpu::AddressMode::Repeat;
break;
}
case SamplerWrapMode::MIRRORED_REPEAT: {
return wgpu::AddressMode::MirrorRepeat;
break;
}
}
return wgpu::AddressMode::Undefined;
}
} // namespace filament

View File

@@ -17,7 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include "webgpu/WebGPUSwapChain.h"
#include "WebGPUHandles.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
@@ -34,11 +35,13 @@
#include <memory>
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
#define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
#endif
namespace filament::backend {
class WebGPUSwapChain;
/**
* WebGPU backend (driver) implementation
*/
@@ -53,6 +56,28 @@ private:
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
[[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
// handled in the WebGPUPlatform
@@ -60,10 +85,14 @@ private:
wgpu::Adapter mAdapter = nullptr;
wgpu::Device mDevice = nullptr;
wgpu::Queue mQueue = nullptr;
// TODO consider moving to handle allocator when ready
std::unique_ptr<WebGPUSwapChain> mSwapChain = nullptr;
void* mNativeWindow = nullptr;
WebGPUSwapChain* mSwapChain = nullptr;
uint64_t mNextFakeHandle = 1;
wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
/*
* Driver interface
*/
@@ -93,6 +122,21 @@ private:
return mHandleAllocator.allocate<D>();
}
template<typename D, typename B, typename... ARGS>
D* constructHandle(Handle<B>& handle, ARGS&&... args) noexcept {
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
}
template<typename D, typename B>
D* handleCast(Handle<B> handle) noexcept {
return mHandleAllocator.handle_cast<D*>(handle);
}
template<typename D, typename B>
void destructHandle(Handle<B>& handle) noexcept {
auto* p = mHandleAllocator.handle_cast<D*>(handle);
mHandleAllocator.deallocate(handle, p);
}
};
}// namespace filament::backend

View File

@@ -0,0 +1,659 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WebGPUHandles.h"
#include <utility>
namespace {
constexpr wgpu::BufferUsage getBufferObjectUsage(
filament::backend::BufferObjectBinding bindingType) noexcept {
switch (bindingType) {
case filament::backend::BufferObjectBinding::VERTEX:
return wgpu::BufferUsage::Vertex;
case filament::backend::BufferObjectBinding::UNIFORM:
return wgpu::BufferUsage::Uniform;
case filament::backend::BufferObjectBinding::SHADER_STORAGE:
return wgpu::BufferUsage::Storage;
}
}
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) {
using ElementType = filament::backend::ElementType;
using VertexFormat = wgpu::VertexFormat;
if (normalized) {
switch (type) {
// Single Component Types
case ElementType::BYTE: return VertexFormat::Snorm8;
case ElementType::UBYTE: return VertexFormat::Unorm8;
case ElementType::SHORT: return VertexFormat::Snorm16;
case ElementType::USHORT: return VertexFormat::Unorm16;
// Two Component Types
case ElementType::BYTE2: return VertexFormat::Snorm8x2;
case ElementType::UBYTE2: return VertexFormat::Unorm8x2;
case ElementType::SHORT2: return VertexFormat::Snorm16x2;
case ElementType::USHORT2: return VertexFormat::Unorm16x2;
// Three Component Types
// There is no vertex format type for 3 byte data in webgpu. Use
// 4 byte signed normalized type and ignore the last byte.
// TODO: This is to be verified.
case ElementType::BYTE3: return VertexFormat::Snorm8x4; // NOT MINSPEC
case ElementType::UBYTE3: return VertexFormat::Unorm8x4; // NOT MINSPEC
case ElementType::SHORT3: return VertexFormat::Snorm16x4; // NOT MINSPEC
case ElementType::USHORT3: return VertexFormat::Unorm16x4; // NOT MINSPEC
// Four Component Types
case ElementType::BYTE4: return VertexFormat::Snorm8x4;
case ElementType::UBYTE4: return VertexFormat::Unorm8x4;
case ElementType::SHORT4: return VertexFormat::Snorm16x4;
case ElementType::USHORT4: return VertexFormat::Unorm8x4;
default:
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
return VertexFormat::Float32x3;
}
}
switch (type) {
// Single Component Types
// There is no direct alternative for SSCALED in webgpu. Convert them to Float32 directly.
// This will result in increased memory on the cpu side.
// TODO: Is Float16 acceptable instead with some potential accuracy errors?
case ElementType::BYTE: return integer ? VertexFormat::Sint8 : VertexFormat::Float32;
case ElementType::UBYTE: return integer ? VertexFormat::Uint8 : VertexFormat::Float32;
case ElementType::SHORT: return integer ? VertexFormat::Sint16 : VertexFormat::Float32;
case ElementType::USHORT: return integer ? VertexFormat::Uint16 : VertexFormat::Float32;
case ElementType::HALF: return VertexFormat::Float16;
case ElementType::INT: return VertexFormat::Sint32;
case ElementType::UINT: return VertexFormat::Uint32;
case ElementType::FLOAT: return VertexFormat::Float32;
// Two Component Types
case ElementType::BYTE2: return integer ? VertexFormat::Sint8x2 : VertexFormat::Float32x2;
case ElementType::UBYTE2: return integer ? VertexFormat::Uint8x2 : VertexFormat::Float32x2;
case ElementType::SHORT2: return integer ? VertexFormat::Sint16x2 : VertexFormat::Float32x2;
case ElementType::USHORT2: return integer ? VertexFormat::Uint16x2 : VertexFormat::Float32x2;
case ElementType::HALF2: return VertexFormat::Float16x2;
case ElementType::FLOAT2: return VertexFormat::Float32x2;
// Three Component Types
case ElementType::BYTE3: return VertexFormat::Sint8x4; // NOT MINSPEC
case ElementType::UBYTE3: return VertexFormat::Uint8x4; // NOT MINSPEC
case ElementType::SHORT3: return VertexFormat::Sint16x4; // NOT MINSPEC
case ElementType::USHORT3: return VertexFormat::Uint16x4; // NOT MINSPEC
case ElementType::HALF3: return VertexFormat::Float16x4; // NOT MINSPEC
case ElementType::FLOAT3: return VertexFormat::Float32x3;
// Four Component Types
case ElementType::BYTE4: return integer ? VertexFormat::Sint8x4 : VertexFormat::Float32x4;
case ElementType::UBYTE4: return integer ? VertexFormat::Uint8x4 : VertexFormat::Float32x4;
case ElementType::SHORT4: return integer ? VertexFormat::Sint16x4 : VertexFormat::Float32x4;
case ElementType::USHORT4: return integer ? VertexFormat::Uint16x4 : VertexFormat::Float32x4;
case ElementType::HALF4: return VertexFormat::Float16x4;
case ElementType::FLOAT4: return VertexFormat::Float32x4;
}
}
}// namespace
namespace filament::backend {
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
mVertexBufferLayout(bufferCount),
mAttributes(bufferCount) {
assert_invariant(attributeCount > 0);
assert_invariant(bufferCount > 0);
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute const& attrib = attributes[attribIndex];
// Ignore the attributes which are not bind to vertex buffers.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
continue;
}
assert_invariant(attrib.buffer < bufferCount);
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
// Attributes are sequential per buffer
mAttributes[attrib.buffer].push_back({
.format = vertexFormat,
.offset = attrib.offset,
.shaderLocation = static_cast<uint32_t>(mAttributes[attrib.buffer].size()),
});
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
if (mVertexBufferLayout[attrib.buffer].arrayStride == 0) {
mVertexBufferLayout[attrib.buffer].arrayStride = attrib.stride;
} else {
assert_invariant(mVertexBufferLayout[attrib.buffer].arrayStride == attrib.stride);
}
}
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++) {
mVertexBufferLayout[bufferIndex].attributeCount = mAttributes[bufferIndex].size();
mVertexBufferLayout[bufferIndex].attributes = mAttributes[bufferIndex].data();
}
}
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
uint32_t indexCount)
: buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
elementSize * indexCount, "index_buffer")),
indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCount,
uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
vbih(vbih),
buffers(bufferCount) {}
WGPUBufferObject::WGPUBufferObject(wgpu::Device const& device, BufferObjectBinding bindingType,
uint32_t byteCount)
: HwBufferObject(byteCount),
buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType),
byteCount, "buffer_object")),
bufferObjectBinding(bindingType) {}
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
if (any(ShaderStageFlags::VERTEX & fFlags)) {
retStages |= wgpu::ShaderStage::Vertex;
}
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
retStages |= wgpu::ShaderStage::Fragment;
}
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
retStages |= wgpu::ShaderStage::Compute;
}
return retStages;
}
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
wgpu::Device const& device) {
assert_invariant(device);
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value.
static int layoutNum = 0;
uint samplerCount =
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
return fEntry.type == DescriptorType::SAMPLER ||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
});
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
wEntries.reserve(layout.bindings.size() + samplerCount);
for (auto fEntry: layout.bindings) {
auto& wEntry = wEntries.emplace_back();
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
wEntry.binding = fEntry.binding * 2;
switch (fEntry.type) {
// TODO Metal treats these the same. Is this fine?
case DescriptorType::SAMPLER_EXTERNAL:
case DescriptorType::SAMPLER: {
// Sampler binding is 2n+1 due to split.
auto& samplerEntry = wEntries.emplace_back();
samplerEntry.binding = fEntry.binding * 2 + 1;
samplerEntry.visibility = wEntry.visibility;
// We are simply hoping that undefined and defaults suffices here.
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
break;
}
case DescriptorType::UNIFORM_BUFFER: {
wEntry.buffer.hasDynamicOffset =
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
// TODO: Ideally we fill minBindingSize
break;
}
case DescriptorType::INPUT_ATTACHMENT: {
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
PANIC_POSTCONDITION("Input Attachment is not supported");
break;
}
case DescriptorType::SHADER_STORAGE_BUFFER: {
// TODO: Vulkan does not support this, can we?
PANIC_POSTCONDITION("Shader storage is not supported");
break;
}
}
// Currently flags are only used to specify dynamic offset.
// UNUSED
// fEntry.count
}
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value.
.label{ "layout_" + std::to_string(++layoutNum) },
.entryCount = wEntries.size(),
.entries = wEntries.data()
};
// TODO Do we need to defer this until we have more info on textures and samplers??
mLayoutSize = wEntries.size();
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
}
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
WebGPUDescriptorSet::WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize)
: mLayout(layout),
entries(layoutSize, wgpu::BindGroupEntry{.buffer = nullptr, .sampler = nullptr, .textureView = nullptr}) {
// Establish the size of entries based on the layout. This should be reliable and efficient.
}
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
mBindGroup = nullptr;
mLayout = nullptr;
entries.clear();
}
wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
if (mBindGroup) {
return mBindGroup;
}
// TODO label? Should we just copy layout label?
wgpu::BindGroupDescriptor desc{ .layout = mLayout,
.entryCount = entries.size(),
.entries = entries.data() };
mBindGroup = device.CreateBindGroup(&desc);
return mBindGroup;
}
void WebGPUDescriptorSet::addEntry(uint index, wgpu::BindGroupEntry&& entry) {
if (mBindGroup) {
// We will keep getting hits from future updates, but shouldn't do anything
// Filament guarantees this won't change after things have locked.
return;
}
// TODO: Putting some level of trust that Filament is not going to reuse indexes or go past the
// layout index for efficiency. Add guards if wrong.
entries.emplace_back(std::move(entry));
}
// From createTextureR
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device device) noexcept {
// First the texture aspect
wgpu::TextureDescriptor desc;
switch (target) {
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
// Should be safe to assume external is 2d
case SamplerType::SAMPLER_EXTERNAL: {
desc.dimension = wgpu::TextureDimension::e2D;
break;
}
case SamplerType::SAMPLER_3D: {
desc.dimension = wgpu::TextureDimension::e3D;
break;
}
}
desc.size = { .width = width, .height = height, .depthOrArrayLayers = depth };
desc.format = fToWGPUTextureFormat(format);
assert_invariant(desc.format != wgpu::TextureFormat::Undefined);
// WGPU requires this to be true. Filament should comply
assert(samples == 1 || samples || 4);
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 {
texture = src->texture;
texView = makeTextureView(baseLevel, levelCount);
}
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
// TODO Validate assumptions, revisit if issues.
if (any(TextureUsage::BLIT_SRC & fUsage)) {
retUsage |= wgpu::TextureUsage::CopySrc;
}
if (any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
retUsage |= wgpu::TextureUsage::CopyDst;
}
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
retUsage |= wgpu::TextureUsage::TextureBinding;
}
// WGPU Render attachment covers either color or stencil situation dependant
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
// issues, investigate here.
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
TextureUsage::DEPTH_ATTACHMENT) &
fUsage)) {
retUsage |= wgpu::TextureUsage::RenderAttachment;
}
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
// benefit though
const bool useTransientAttachment =
// Usage consists of attachment flags only.
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
// Depth resolve cannot use transient attachment because it uses a custom shader.
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
// restriction.
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
// and above to be able to support stencil resolve (along with depth).
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
if (useTransientAttachment) {
retUsage |= wgpu::TextureUsage::TransientAttachment;
}
// NOTE: Unused wgpu flags:
// StorageBinding
// StorageAttachment
// NOTE: Unused Filament flags:
// SUBPASS_INPUT VK goes to input attachment which we don't support right now
// PROTECTED
return retUsage;
}
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
switch (fUsage) {
case filament::backend::TextureFormat::R8:
return wgpu::TextureFormat::R8Unorm;
case filament::backend::TextureFormat::R8_SNORM:
return wgpu::TextureFormat::R8Snorm;
case filament::backend::TextureFormat::R8UI:
return wgpu::TextureFormat::R8Uint;
case filament::backend::TextureFormat::R8I:
return wgpu::TextureFormat::R8Sint;
case filament::backend::TextureFormat::STENCIL8:
return wgpu::TextureFormat::Stencil8;
case filament::backend::TextureFormat::R16F:
return wgpu::TextureFormat::R16Float;
case filament::backend::TextureFormat::R16UI:
return wgpu::TextureFormat::R16Uint;
case filament::backend::TextureFormat::R16I:
return wgpu::TextureFormat::R16Sint;
case filament::backend::TextureFormat::RG8:
return wgpu::TextureFormat::RG8Unorm;
case filament::backend::TextureFormat::RG8_SNORM:
return wgpu::TextureFormat::RG8Snorm;
case filament::backend::TextureFormat::RG8UI:
return wgpu::TextureFormat::RG8Uint;
case filament::backend::TextureFormat::RG8I:
return wgpu::TextureFormat::RG8Sint;
case filament::backend::TextureFormat::R32F:
return wgpu::TextureFormat::R32Float;
case filament::backend::TextureFormat::R32UI:
return wgpu::TextureFormat::R32Uint;
case filament::backend::TextureFormat::R32I:
return wgpu::TextureFormat::R32Sint;
case filament::backend::TextureFormat::RG16F:
return wgpu::TextureFormat::RG16Float;
case filament::backend::TextureFormat::RG16UI:
return wgpu::TextureFormat::RG16Uint;
case filament::backend::TextureFormat::RG16I:
return wgpu::TextureFormat::RG16Sint;
case filament::backend::TextureFormat::RGBA8:
return wgpu::TextureFormat::RGBA8Unorm;
case filament::backend::TextureFormat::SRGB8_A8:
return wgpu::TextureFormat::RGBA8UnormSrgb;
case filament::backend::TextureFormat::RGBA8_SNORM:
return wgpu::TextureFormat::RGBA8Snorm;
case filament::backend::TextureFormat::RGBA8UI:
return wgpu::TextureFormat::RGBA8Uint;
case filament::backend::TextureFormat::RGBA8I:
return wgpu::TextureFormat::RGBA8Sint;
case filament::backend::TextureFormat::DEPTH16:
return wgpu::TextureFormat::Depth16Unorm;
case filament::backend::TextureFormat::DEPTH24:
return wgpu::TextureFormat::Depth24Plus;
case filament::backend::TextureFormat::DEPTH32F:
return wgpu::TextureFormat::Depth32Float;
case filament::backend::TextureFormat::DEPTH24_STENCIL8:
return wgpu::TextureFormat::Depth24PlusStencil8;
case filament::backend::TextureFormat::DEPTH32F_STENCIL8:
return wgpu::TextureFormat::Depth32FloatStencil8;
case filament::backend::TextureFormat::RG32F:
return wgpu::TextureFormat::RG32Float;
case filament::backend::TextureFormat::RG32UI:
return wgpu::TextureFormat::RG32Uint;
case filament::backend::TextureFormat::RG32I:
return wgpu::TextureFormat::RG32Sint;
case filament::backend::TextureFormat::RGBA16F:
return wgpu::TextureFormat::RGBA16Float;
case filament::backend::TextureFormat::RGBA16UI:
return wgpu::TextureFormat::RGBA16Uint;
case filament::backend::TextureFormat::RGBA16I:
return wgpu::TextureFormat::RGBA16Sint;
case filament::backend::TextureFormat::RGBA32F:
return wgpu::TextureFormat::RGBA32Float;
case filament::backend::TextureFormat::RGBA32UI:
return wgpu::TextureFormat::RGBA32Uint;
case filament::backend::TextureFormat::RGBA32I:
return wgpu::TextureFormat::RGBA32Sint;
case filament::backend::TextureFormat::EAC_R11:
return wgpu::TextureFormat::EACR11Unorm;
case filament::backend::TextureFormat::EAC_R11_SIGNED:
return wgpu::TextureFormat::EACR11Snorm;
case filament::backend::TextureFormat::EAC_RG11:
return wgpu::TextureFormat::EACRG11Unorm;
case filament::backend::TextureFormat::EAC_RG11_SIGNED:
return wgpu::TextureFormat::EACRG11Snorm;
case filament::backend::TextureFormat::ETC2_RGB8:
return wgpu::TextureFormat::ETC2RGB8Unorm;
case filament::backend::TextureFormat::ETC2_SRGB8:
return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
case filament::backend::TextureFormat::ETC2_RGB8_A1:
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
case filament::backend::TextureFormat::ETC2_SRGB8_A1:
return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
case filament::backend::TextureFormat::ETC2_EAC_RGBA8:
return wgpu::TextureFormat::ETC2RGBA8Unorm;
case filament::backend::TextureFormat::ETC2_EAC_SRGBA8:
return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_4x4:
return wgpu::TextureFormat::ASTC4x4Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
return wgpu::TextureFormat::ASTC4x4UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_5x4:
return wgpu::TextureFormat::ASTC5x4Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
return wgpu::TextureFormat::ASTC5x4UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_5x5:
return wgpu::TextureFormat::ASTC5x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
return wgpu::TextureFormat::ASTC5x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_6x5:
return wgpu::TextureFormat::ASTC6x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
return wgpu::TextureFormat::ASTC6x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_6x6:
return wgpu::TextureFormat::ASTC6x6Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
return wgpu::TextureFormat::ASTC6x6UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_8x5:
return wgpu::TextureFormat::ASTC8x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
return wgpu::TextureFormat::ASTC8x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_8x6:
return wgpu::TextureFormat::ASTC8x6Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
return wgpu::TextureFormat::ASTC8x6UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_8x8:
return wgpu::TextureFormat::ASTC8x8Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
return wgpu::TextureFormat::ASTC8x8UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x5:
return wgpu::TextureFormat::ASTC10x5Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
return wgpu::TextureFormat::ASTC10x5UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x6:
return wgpu::TextureFormat::ASTC10x6Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
return wgpu::TextureFormat::ASTC10x6UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x8:
return wgpu::TextureFormat::ASTC10x8Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
return wgpu::TextureFormat::ASTC10x8UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_10x10:
return wgpu::TextureFormat::ASTC10x10Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
return wgpu::TextureFormat::ASTC10x10UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_12x10:
return wgpu::TextureFormat::ASTC12x10Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
return wgpu::TextureFormat::ASTC12x10UnormSrgb;
case filament::backend::TextureFormat::RGBA_ASTC_12x12:
return wgpu::TextureFormat::ASTC12x12Unorm;
case filament::backend::TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return wgpu::TextureFormat::ASTC12x12UnormSrgb;
case filament::backend::TextureFormat::RED_RGTC1:
return wgpu::TextureFormat::BC4RUnorm;
case filament::backend::TextureFormat::SIGNED_RED_RGTC1:
return wgpu::TextureFormat::BC4RSnorm;
case filament::backend::TextureFormat::RED_GREEN_RGTC2:
return wgpu::TextureFormat::BC5RGUnorm;
case filament::backend::TextureFormat::SIGNED_RED_GREEN_RGTC2:
return wgpu::TextureFormat::BC5RGSnorm;
case filament::backend::TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
return wgpu::TextureFormat::BC6HRGBUfloat;
case filament::backend::TextureFormat::RGB_BPTC_SIGNED_FLOAT:
return wgpu::TextureFormat::BC6HRGBFloat;
case filament::backend::TextureFormat::RGBA_BPTC_UNORM:
return wgpu::TextureFormat::BC7RGBAUnorm;
case filament::backend::TextureFormat::SRGB_ALPHA_BPTC_UNORM:
return wgpu::TextureFormat::BC7RGBAUnormSrgb;
case filament::backend::TextureFormat::RGB565:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and discard the alpha and lower precision.
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGB9_E5:
return wgpu::TextureFormat::RGB9E5Ufloat;
case filament::backend::TextureFormat::RGB5_A1:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and handle the packing/unpacking in shaders.
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGBA4:
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
// and handle the packing/unpacking in shaders.
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGB8:
// No direct sRGB equivalent in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Unorm;
case filament::backend::TextureFormat::SRGB8:
// No direct sRGB equivalent in wgpu without alpha.
return wgpu::TextureFormat::RGBA8UnormSrgb;
case filament::backend::TextureFormat::RGB8_SNORM:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Snorm;
case filament::backend::TextureFormat::RGB8UI:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Uint;
case filament::backend::TextureFormat::RGB8I:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA8Sint;
case filament::backend::TextureFormat::R11F_G11F_B10F:
return wgpu::TextureFormat::RG11B10Ufloat;
case filament::backend::TextureFormat::UNUSED:
return wgpu::TextureFormat::Undefined;
case filament::backend::TextureFormat::RGB10_A2:
return wgpu::TextureFormat::RGB10A2Unorm;
case filament::backend::TextureFormat::RGB16F:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA16Float;
case filament::backend::TextureFormat::RGB16UI:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA16Uint;
case filament::backend::TextureFormat::RGB16I:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA16Sint;
case filament::backend::TextureFormat::RGB32F:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA32Float;
case filament::backend::TextureFormat::RGB32UI:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA32Uint;
case filament::backend::TextureFormat::RGB32I:
// No direct mapping in wgpu without alpha.
return wgpu::TextureFormat::RGBA32Sint;
case filament::backend::TextureFormat::DXT1_RGB:
return wgpu::TextureFormat::BC1RGBAUnorm;
case filament::backend::TextureFormat::DXT1_RGBA:
return wgpu::TextureFormat::BC1RGBAUnorm;
case filament::backend::TextureFormat::DXT3_RGBA:
return wgpu::TextureFormat::BC2RGBAUnorm;
case filament::backend::TextureFormat::DXT5_RGBA:
return wgpu::TextureFormat::BC3RGBAUnorm;
case filament::backend::TextureFormat::DXT1_SRGB:
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
case filament::backend::TextureFormat::DXT1_SRGBA:
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
case filament::backend::TextureFormat::DXT3_SRGBA:
return wgpu::TextureFormat::BC2RGBAUnormSrgb;
case filament::backend::TextureFormat::DXT5_SRGBA:
return wgpu::TextureFormat::BC3RGBAUnormSrgb;
}
}
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel,
const uint8_t& levelCount) {
wgpu::TextureViewDescriptor desc;
desc.baseMipLevel = baseLevel;
desc.mipLevelCount = levelCount;
// baseArrayLayer is required, making a guess
desc.baseArrayLayer = 0;
// Have not found an analouge to aspect in other drivers, but ALL should be unrestrictive.
// TODO Can we make this better?
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);
}
}// namespace filament::backend

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/FixedCapacityVector.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <vector>
namespace filament::backend {
class WGPUProgram final : public HwProgram {
public:
WGPUProgram(wgpu::Device&, Program&);
wgpu::ShaderModule vertexShaderModule = nullptr;
wgpu::ShaderModule fragmentShaderModule = nullptr;
wgpu::ShaderModule computeShaderModule = nullptr;
std::vector<wgpu::ConstantEntry> constants;
};
struct WGPUBufferObject;
// 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
// is equal to bufferCount. Each VertexBufferLayout can contain multiple VertexAttribute. Bind index
// of vertex buffer is implicitly calculated by the position of VertexBufferLayout in an array.
class WGPUVertexBufferInfo : public HwVertexBufferInfo {
public:
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const {
return mVertexBufferLayout.data();
}
inline uint32_t getVertexBufferLayoutSize() const {
return mVertexBufferLayout.size();
}
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].data();
}
inline uint32_t getVertexAttributeSize(uint32_t index) const {
assert_invariant(index < mAttributes.size());
return mAttributes[index].size();
}
private:
// TODO: can we do better in terms on heap management.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {};
};
struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vertexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih);
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> buffers;
};
struct WGPUIndexBuffer : public HwIndexBuffer {
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
uint32_t indexCount);
wgpu::Buffer buffer;
wgpu::IndexFormat indexFormat;
};
struct WGPUBufferObject : HwBufferObject {
WGPUBufferObject(wgpu::Device const &device, BufferObjectBinding bindingType, uint32_t byteCount);
wgpu::Buffer buffer = nullptr;
const BufferObjectBinding bufferObjectBinding;
};
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
~WebGPUDescriptorSetLayout();
[[nodiscard]] const wgpu::BindGroupLayout& getLayout() const { return mLayout; }
[[nodiscard]] uint getLayoutSize() const { return mLayoutSize; }
private:
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
uint mLayoutSize;
wgpu::BindGroupLayout mLayout;
};
class WebGPUDescriptorSet final : public HwDescriptorSet {
public:
WebGPUDescriptorSet(const wgpu::BindGroupLayout& layout, uint layoutSize);
~WebGPUDescriptorSet();
wgpu::BindGroup lockAndReturn(wgpu::Device const& device);
void addEntry(uint index, wgpu::BindGroupEntry&& entry);
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
private:
// 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;
std::vector<wgpu::BindGroupEntry> entries;
wgpu::BindGroup mBindGroup;
};
class WGPUTexture : public HwTexture {
public:
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device device) noexcept;
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
const wgpu::Texture& getTexture() const { return texture; }
const wgpu::TextureView& getTexView() const { return texView; }
// Public to allow checking for support of a texture format
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
private:
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount);
// 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
// ref counted pointer) when making views. View is optional
wgpu::Texture texture = nullptr;
wgpu::TextureView texView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPURenderPrimitive() {}
void setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
WGPUVertexBuffer* vertexBuffer = nullptr;
WGPUIndexBuffer* indexBuffer = nullptr;
};
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPURenderTarget : public HwRenderTarget {
class Attachment {
public:
friend struct WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->getTexture()),
mWGPUTexture(gpuTexture) {}
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,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
WGPURenderTarget()
: HwRenderTarget(0, 0),
defaultRenderTarget(true) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
const RenderPassParams& params);
math::uint2 getAttachmentSize() noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
private:
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H

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