Compare commits

..
Author SHA1 Message Date
bridgewaterrobbie 4a77d03d02 Try to handle non-transient attatchment supporting GPUs 2025-05-09 13:22:19 -04:00
Powei Feng 5cb96e5732 Clean up ndk version in build.sh (#8717)
Follow up to #8663
2025-05-09 15:51:30 +00:00
Matthew Hoffman 927aa57a4e Document ASAN (with leak detection) on MacOS (#8716)
* Revert "Optional CMake flag for enabling ASAN for backend and its tests. (#8696)"

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

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

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

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

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

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

* Addressing PR comments

---------

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

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

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


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

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

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

also factorize in one place the filtering of sampler list by layouts.
2025-05-06 11:10:06 -07:00
Mathias Agopian 6d413a4faf add missing depth formats to getMetalFormat()
this prevented to upload depth data into a depth texture
2025-05-06 11:08:34 -07:00
Powei Feng af09c517b6 Revert "Use the Perfetto SDK instead of ATRACE" (#8701)
This reverts commit ca3ff7e08e.
2025-05-06 17:41:21 +00:00
Konrad Piascik 67b9c72442 Fix windows compile errors 2025-05-06 12:24:42 -04:00
Andy Hovingh c1a8d73384 webgpu: legacy draw call should call draw2 2025-05-06 12:24:42 -04:00
Andy Hovingh 2d98ac878e webgpu: sets bind groups in the render pass 2025-05-06 12:24:42 -04:00
Andy Hovingh 35ab5f768d webgpu: depth texture and attachment 2025-05-06 12:24:42 -04:00
bridgewaterrobbieandJuan Caldas 705b3c39f4 WebGPU: Add Draw call, with temporary hack to achieve spinning triangle
Co-authored-by: Juan Caldas <juancaldas@google.com>
2025-05-06 12:24:42 -04:00
56231db326 WebGPU: Set spec constants via string mapping
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
Co-authored-by: Juan Caldas <juancaldas@google.com>
2025-05-06 12:24:42 -04:00
e97f7ef628 WebGPU: Render Target Implementation
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
Co-authored-by: Syed Idris Shah <idrisshah@google.com>
2025-05-06 12:24:42 -04:00
bridgewaterrobbieandAndy Hovingh 4570c2e710 WebGPU: Sampler, Texture and Descriptor Set setup
Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
2025-05-06 12:24:42 -04:00
bridgewaterrobbie 4759237e66 Separate out required and optional features for webgpu. Panic if any required features are not available 2025-05-06 12:24:42 -04:00
Matthew Hoffman 543b93939a Optional CMake flag for enabling ASAN for backend and its tests. (#8696) 2025-05-05 12:35:21 -05:00
Benjamin Doherty 9b356926b8 Release Filament 1.59.4 2025-05-01 17:33:19 -07:00
Matthew Hoffman 7179ab5b9d Prevent all backend vulkan tests from crashing. (#8682)
Many still fail that shouldn't though.
2025-05-01 21:50:56 +00:00
Sungun Park 0a6bbdb35c Add flag to disable depth precaching (#8686)
Depth precaching can cause initial startup stalls, particularly on
platforms such as WebGL on FireFox/Mac.

This option allows us to disable depth precaching, which can be useful
for eliminating the initial startup hiccups or stalls due to the upfront
precaching of depth variants.

BUG=[392917621]
2025-05-01 20:41:04 +00:00
Mathias Agopian 29919e64cd update remote ui, web samples and documentation 2025-05-01 10:21:27 -07:00
Andy Hovingh a7660e40f8 webgpu: fix command buffer (+encoder) life-cycle (avoids breaks on multiple render passes) 2025-05-01 07:31:58 -05:00
Mathias Agopian 22e1f9b930 better comments regarding Variant::VSM 2025-04-30 11:59:37 -07:00
Juan Caldas e205611128 BUGS=397448737
Add WebGPU Support to matdbg
2025-04-30 17:43:28 +00: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
262 changed files with 259450 additions and 3251 deletions
+7
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
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
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
+16
View File
@@ -0,0 +1,16 @@
name: 'Web Preqrequisites'
runs:
using: "composite"
steps:
- uses: ./.github/actions/dep-versions
- name: Cache EMSDK
id: emsdk-cache
uses: actions/cache@v4 # Use a specific version
with:
path: emsdk
key: ${{ runner.os }}-emsdk-${{ env.GITHUB_EMSDK_VERSION }}
- name: Install Web Prerequisites
shell: bash
run: |
bash ./build/common/get-emscripten.sh
echo "EMSDK=$PWD/emsdk" >> $GITHUB_ENV
+3
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:
+3
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
+3
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
+3
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
+52 -35
View File
@@ -9,22 +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
@@ -35,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
@@ -48,6 +60,7 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
@@ -66,6 +79,7 @@ jobs:
- 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
@@ -79,6 +93,10 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh presubmit
@@ -101,18 +119,22 @@ 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
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- name: Cache Mesa and deps
id: mesa-cache
uses: actions/cache@v4
with:
path: mesa
key: ${{ runner.os }}-mesa-deps-2-${{ vars.MESA_VERSION }}
- name: Prerequisites
id: prereqs
run: |
bash test/renderdiff/test.sh
bash test/utils/get_mesa.sh
pip install tifffile numpy
- name: Run Test
run: bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
@@ -121,38 +143,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:
code-correctness:
name: code-correctness
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- 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 prerequisites
- uses: ./.github/actions/mac-prereq
- name: Install clang-tidy and deps
run: |
pip install pyyaml
brew install llvm
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
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
run: bash test/code-correctness/test.sh
+44 -11
View File
@@ -29,15 +29,11 @@ on:
types: [created]
jobs:
build-desktop:
name: build-desktop
runs-on: ${{ matrix.os }}
build-linux:
name: build-linux
runs-on: ubuntu-22.04-32core
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
strategy:
matrix:
os: [macos-14-xlarge, ubuntu-22.04-32core]
steps:
- name: Decide Git ref
id: git_ref
@@ -49,15 +45,48 @@ 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 }}
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh release
cd build/linux && 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-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: macos-14-xlarge
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/mac-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/mac && printf "y" | ./build.sh release
cd ../..
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 +113,8 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
ref: ${{ steps.git_ref.outputs.ref }}
- uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
@@ -121,6 +152,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 +203,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 }}
+4
View File
@@ -14,6 +14,10 @@ jobs:
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- uses: ./.github/actions/web-prereq
- name: Run build script
run: |
cd build/web && printf "y" | ./build.sh continuous
+2
View File
@@ -363,6 +363,8 @@ python ./emsdk.py activate latest
source ./emsdk_env.sh
```
Alternatively, you can try running the script `build/common/get-emscripten.sh`.
After this you can invoke the [easy build](#easy-build) script as follows:
```shell
+1
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)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.59.2'
implementation 'com.google.android.filament:filament-android:1.59.5'
}
```
@@ -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.2'
pod 'Filament', '~> 1.59.5'
```
## Documentation
+14
View File
@@ -7,6 +7,20 @@ 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.60.0
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]
- `ColorGrading::Builder::toneMapper` now takes a `shared_ptr<ToneMapper>`
## v1.59.5
## v1.59.4
## v1.59.3
## v1.59.2
- Fix build/compile errors when upgrading to MacOS 15.4
+6
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>
+6
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:
@@ -18,6 +18,7 @@
#include <filament/ColorGrading.h>
#include <filament/ToneMapper.h>
#include <memory>
#include <math/vec3.h>
#include <math/vec4.h>
@@ -67,8 +68,9 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_ColorGrading_nBuilderToneMapper(JNIEnv*, jclass,
jlong nativeBuilder, jlong toneMapper_) {
ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder;
const ToneMapper* toneMapper = (const ToneMapper*) toneMapper_;
builder->toneMapper(toneMapper);
ToneMapper* toneMapper = reinterpret_cast<ToneMapper*>(toneMapper_);
std::shared_ptr<ToneMapper> toneMapperCopy(toneMapper->clone());
builder->toneMapper(toneMapperCopy);
}
#pragma clang diagnostic push
@@ -214,9 +214,9 @@ public class ColorGrading {
*
* The default tone mapping operator is {@link ToneMapper.ACESLegacy}.
*
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
* this builder. Since the build(Engine&) method is synchronous, it is safe to
* delete the tone mapper object after that finishes executing.
* The copy of the specified tone mapper is set to this builder. It is safe to delete
* the original tone mapper object while the copied one is held by the built ColorGrading
* object.
*
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
*
@@ -1980,7 +1980,7 @@ public class View {
}
/**
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
* reconstruction filter width typically between 1 (sharper) and 2 (smoother)
*/
public float filterWidth = 1.0f;
/**
+6 -1
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)
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.59.2
VERSION_NAME=1.59.5
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -151,7 +151,7 @@ function print_fgviewer_help {
}
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/android/ndk.version | cut -f 1 -d ".")}
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | cut -f 1 -d ".")}
# Requirements
CMAKE_MAJOR=3
+7 -37
View File
@@ -1,28 +1,6 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
@@ -30,30 +8,22 @@ 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
-1
View File
@@ -1 +0,0 @@
27.0.11718014
+15
View File
@@ -1,5 +1,20 @@
#!/bin/bash
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
# Typically a build script (build.sh) would source this script. For example,
# source `dirname $0`/../common/build-common.sh
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
if [[ ! "$TARGET" ]]; then
if [[ "$1" ]]; then
TARGET=$1
+8 -2
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!"
+19
View File
@@ -0,0 +1,19 @@
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
fi
-6
View File
@@ -1,6 +0,0 @@
#!/bin/bash
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "Running workflow $GITHUB_WORKFLOW (event: $GITHUB_EVENT_NAME, action: $GITHUB_ACTION)"
CONTINUOUS_INTEGRATION=true
fi
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
if [ -d "./emsdk" ]; then
echo "emsdk folder found. Assume emsdk has been installed."
cd emsdk
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
exit 0
fi
# Install emscripten.
EMSDK_VERSION=${GITHUB_EMSDK_VERSION-3.1.60}
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/${EMSDK_VERSION}.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
+18
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
+7
View File
@@ -0,0 +1,7 @@
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
GITHUB_EMSDK_VERSION=3.1.60
+1 -27
View File
@@ -1,36 +1,11 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
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
# If we're generating an archive for release or continuous builds, then we'll also build for the
@@ -41,4 +16,3 @@ if [[ "${GENERATE_ARCHIVES}" ]]; then
fi
./build.sh -i -p ios -c $BUILD_SIMULATOR $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
-6
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"
+2 -30
View File
@@ -1,39 +1,11 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
set -e
set -x
# build-common.sh will generate the following variables:
# $GENERATE_ARCHIVES
# $BUILD_DEBUG
# $BUILD_RELEASE
source `dirname $0`/../common/ci-common.sh
source `dirname $0`/ci-common.sh
source `dirname $0`/../common/build-common.sh
pushd `dirname $0`/../.. > /dev/null
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
-38
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
+2 -26
View File
@@ -1,35 +1,11 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
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
./build.sh -c $RUN_TESTS $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
-8
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"
+8 -2
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)
+8 -2
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)
+8 -2
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)
+8 -2
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)
+1 -25
View File
@@ -1,34 +1,10 @@
#!/bin/bash
# Usage: the first argument selects the build type:
# - release, to build release only
# - debug, to build debug only
# - continuous, to build release and debug
# - presubmit, for presubmit builds
#
# The default is release
echo "This script is intended to run in a CI environment and may modify your current environment."
echo "Please refer to BUILDING.md for more information."
read -r -p "Do you wish to proceed (y/n)? " choice
case "${choice}" in
y|Y)
echo "Build will proceed..."
;;
n|N)
exit 0
;;
*)
exit 0
;;
esac
source `dirname $0`/../common/ci-check.sh
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
-23
View File
@@ -1,23 +0,0 @@
#!/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
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
export EMSDK="$PWD"
cd ..
+1 -1
View File
File diff suppressed because one or more lines are too long
+36 -19
View File
@@ -166,7 +166,7 @@ This document is part of the <a href="https://github.com/google/filament">Filame
</p><ul>
<li class="minus"><a href="https://github.com/romainguy">Romain Guy</a>, <a href="https://twitter.com/romainguy">@romainguy</a>
</li>
<li class="minus"><a href="https://github.com/pixelflinger">Mathias Agopian</a>, <a href="https://twitter.com/darthmoosious">@darthmoosious</a></li></ul>
<li class="minus"><a href="https://github.com/pixelflinger">Mathias Agopian</a>, <a href="https://bsky.app/profile/pixelflinger.bsky.social">@pixelflinger</a></li></ul>
<p></p>
<a class="target" name="overview">&nbsp;</a><a class="target" name="overview">&nbsp;</a><a class="target" name="toc2">&nbsp;</a><h1>Overview</h1>
@@ -259,26 +259,27 @@ in <a href="#table_standardproperties">table&nbsp;1</a>.
</p><div class="table">
<table class="table"><tbody><tr><th style="text-align:right"> Property </th><th style="text-align:left"> Definition </th></tr>
<tr><td style="text-align:right"> <strong class="asterisk">baseColor</strong> </td><td style="text-align:left"> Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">metallic</strong> </td><td style="text-align:left"> Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1) </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">roughness</strong> </td><td style="text-align:left"> Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">metallic</strong> </td><td style="text-align:left"> Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1) </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">reflectance</strong> </td><td style="text-align:left"> Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">sheenColor</strong> </td><td style="text-align:left"> Strength of the sheen layer </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">sheenRoughness</strong> </td><td style="text-align:left"> Perceived smoothness or roughness of the sheen layer </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">ambientOcclusion</strong> </td><td style="text-align:left"> Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0 </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">clearCoat</strong> </td><td style="text-align:left"> Strength of the clear coat layer </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">clearCoatRoughness</strong> </td><td style="text-align:left"> Perceived smoothness or roughness of the clear coat layer </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">clearCoatNormal</strong> </td><td style="text-align:left"> A detail normal used to perturb the clear coat layer using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">anisotropy</strong> </td><td style="text-align:left"> Amount of anisotropy in either the tangent or bitangent direction </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">anisotropyDirection</strong> </td><td style="text-align:left"> Local surface direction in tangent space </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">ambientOcclusion</strong> </td><td style="text-align:left"> Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0 </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">normal</strong> </td><td style="text-align:left"> A detail normal used to perturb the surface using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">bentNormal</strong> </td><td style="text-align:left"> A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">clearCoatNormal</strong> </td><td style="text-align:left"> A detail normal used to perturb the clear coat layer using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">emissive</strong> </td><td style="text-align:left"> Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">postLightingColor</strong> </td><td style="text-align:left"> Additional color that can be blended with the result of the lighting computations. See <code>postLightingBlending</code> </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">ior</strong> </td><td style="text-align:left"> Index of refraction, either for refractive objects or as an alternative to reflectance </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">transmission</strong> </td><td style="text-align:left"> Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">absorption</strong> </td><td style="text-align:left"> Absorption factor for refractive objects </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">microThickness</strong> </td><td style="text-align:left"> Thickness of the thin layer of refractive objects </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">thickness</strong> </td><td style="text-align:left"> Thickness of the solid volume of refractive objects </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">sheenColor</strong> </td><td style="text-align:left"> Strength of the sheen layer </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">sheenRoughness</strong> </td><td style="text-align:left"> Perceived smoothness or roughness of the sheen layer </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">emissive</strong> </td><td style="text-align:left"> Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">normal</strong> </td><td style="text-align:left"> A detail normal used to perturb the surface using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">postLightingColor</strong> </td><td style="text-align:left"> Additional color that can be blended with the result of the lighting computations. See <code>postLightingBlending</code> </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">absorption</strong> </td><td style="text-align:left"> Absorption factor for refractive objects </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">transmission</strong> </td><td style="text-align:left"> Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">ior</strong> </td><td style="text-align:left"> Index of refraction, either for refractive objects or as an alternative to reflectance </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">microThickness</strong> </td><td style="text-align:left"> Thickness of the thin layer of refractive objects </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">bentNormal</strong> </td><td style="text-align:left"> A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">shadowStrength</strong> </td><td style="text-align:left"> Strength factor between 0 and 1 for all shadows received by this material </td></tr>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_standardproperties">&nbsp;</a><b style="font-style:normal;">Table&nbsp;1:</b> Properties of the standard model</div></center></div>
<p></p><p>
@@ -1486,10 +1487,13 @@ non-shader data.
</p><dl><dt>Type</dt><dd><p> array of parameter objects
</p></dd><dt>Value</dt><dd><p> Each entry is an object with the properties <code>name</code> and <code>type</code>, both of <code>string</code> type. The
name must be a valid GLSL identifier. Entries also have an optional <code>precision</code>, which can be
name must be a valid GLSL identifier. Entries have an optional <code>precision</code>, which can be
one of <code>default</code> (best precision for the platform, typically <code>high</code> on desktop, <code>medium</code> on
mobile), <code>low</code>, <code>medium</code>, <code>high</code>. The type must be one of the types described in
<a href="#table_materialparamstypes">table&nbsp;14</a>.
<a href="#table_materialparamstypes">table&nbsp;14</a>. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
</p></dd></dl><div class="table">
<table class="table"><tbody><tr><th style="text-align:left"> Type </th><th style="text-align:left"> Description </th></tr>
@@ -1752,7 +1756,13 @@ non-shader data.
when selecting any shading model that is not <code>unlit</code>. See the shader sections of this document
for more information on how to access these attributes from the shaders.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
</p></dd></dl><div class="admonition note"><div class="admonition-title"> Interaction with custom variables</div>
<p></p><p>
When the <code>color</code> attribute is specified, only four custom variables are available instead of five.</p></div>
<p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> parameters : [</span>
<span class="line"> {</span>
<span class="line"> type : sampler2d,</span>
@@ -1779,7 +1789,7 @@ non-shader data.
</p><dl><dt>Type</dt><dd><p> array of <code>string</code>
</p></dd><dt>Value</dt><dd><p> Up to 4 strings, each must be a valid GLSL identifier.
</p></dd><dt>Value</dt><dd><p> Up to 5 strings, each must be a valid GLSL identifier.
</p></dd><dt>Description</dt><dd><p> Defines custom interpolants (or variables) that are output by the material's vertex shader.
Each entry of the array defines the name of an interpolant. The full name in the fragment
@@ -1794,7 +1804,14 @@ non-shader data.
particular if <code>default</code> is specified the default precision is used is the fragment shader
(<code>mediump</code>) and in the vertex shader (<code>highp</code>).
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
</p></dd></dl><div class="admonition warning"><div class="admonition-title"> Interaction with required attributes</div>
<p></p><p>
If the <code>color</code> attribute is specified in the <code>required</code> list, then only four variables can be used
instead of five.</p></div>
<p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> name : Skybox,</span>
<span class="line"> parameters : [</span>
<span class="line"> {</span>
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
+16 -16
View File
@@ -49,7 +49,7 @@
</tr>
<tr>
<td align="left"><a href="#EntityManager">EntityManager</a></td>
<td align="left">Singleton used for constructing entities in Filament&#x27;s ECS.</td>
<td align="left">Singleton used for constructing entities in Filament's ECS.</td>
</tr>
<tr>
<td align="left"><a href="#Frustum">Frustum</a></td>
@@ -89,7 +89,7 @@
</tr>
<tr>
<td align="left"><a href="#Renderer">Renderer</a></td>
<td align="left">Represents the platform&#x27;s native window.</td>
<td align="left">Represents the platform's native window.</td>
</tr>
<tr>
<td align="left"><a href="#Scene">Scene</a></td>
@@ -97,7 +97,7 @@
</tr>
<tr>
<td align="left"><a href="#SwapChain">SwapChain</a></td>
<td align="left">Represents the platform&#x27;s native rendering surface.</td>
<td align="left">Represents the platform's native rendering surface.</td>
</tr>
<tr>
<td align="left"><a href="#Texture">Texture</a></td>
@@ -604,11 +604,11 @@
</ul>
<p>To create an entity with no components, use <a href="#EntityManager">EntityManager</a>.
TODO: It would be better to expose these as JS numbers rather than as JS objects.
This would also be more consistent with Filament&#x27;s Java bindings.</p>
This would also be more consistent with Filament's Java bindings.</p>
</div>
<div class='classdoc'>
<h2>class <a id='EntityManager' href='#EntityManager'>EntityManager</a></h2>
<p>Singleton used for constructing entities in Filament&#x27;s ECS.</p>
<p>Singleton used for constructing entities in Filament's ECS.</p>
<ul>
<li><strong>entityManager.create()</strong>
<ul>
@@ -742,7 +742,7 @@ properties.</p>
</ul>
</li>
</ul>
<p>Be sure to call the instance&#x27;s <code>delete</code> method when you&#x27;re done with it.</p>
<p>Be sure to call the instance's <code>delete</code> method when you're done with it.</p>
</div>
<div class='classdoc'>
<h2>class <a id='PixelBufferDescriptor' href='#PixelBufferDescriptor'>PixelBufferDescriptor</a></h2>
@@ -780,11 +780,11 @@ properties.</p>
</ul>
</li>
</ul>
<p>Be sure to call the instance&#x27;s <code>delete</code> method when you&#x27;re done with it.</p>
<p>Be sure to call the instance's <code>delete</code> method when you're done with it.</p>
</div>
<div class='classdoc'>
<h2>class <a id='Renderer' href='#Renderer'>Renderer</a></h2>
<p>Represents the platform&#x27;s native window.</p>
<p>Represents the platform's native window.</p>
<ul>
<li><strong>renderer.render(swapChain, view)</strong>
<ul>
@@ -803,7 +803,7 @@ properties.</p>
</div>
<div class='classdoc'>
<h2>class <a id='SwapChain' href='#SwapChain'>SwapChain</a></h2>
<p>Represents the platform&#x27;s native rendering surface.</p>
<p>Represents the platform's native rendering surface.</p>
<p>See also the <a href="#Engine">Engine</a> methods <code>createSwapChain</code> and <code>destroySwapChain</code>.</p>
</div>
<div class='classdoc'>
@@ -847,7 +847,7 @@ properties.</p>
</ul>
</li>
</ul>
<p>Be sure to call the instance&#x27;s <code>delete</code> method when you&#x27;re done with it.</p>
<p>Be sure to call the instance's <code>delete</code> method when you're done with it.</p>
</div>
<div class='classdoc'>
<h2>class <a id='VertexBuffer' href='#VertexBuffer'>VertexBuffer</a></h2>
@@ -944,7 +944,7 @@ See also the <a href="#Engine">Engine</a> methods <code>createView</code> and <c
<ul>
<li><strong>assets</strong>
<ul>
<li>Array of strings containing URL&#x27;s of required assets.</li>
<li>Array of strings containing URL's of required assets.</li>
</ul>
</li>
<li><strong>onDone</strong>
@@ -954,7 +954,7 @@ See also the <a href="#Engine">Engine</a> methods <code>createView</code> and <c
</li>
<li><strong>onFetched</strong>
<ul>
<li>optional callback that&#x27;s invoked after each asset is downloaded.</li>
<li>optional callback that's invoked after each asset is downloaded.</li>
</ul>
</li>
</ul>
@@ -1001,7 +1001,7 @@ useful for compressed textures. For example, some platforms accept ETC and other
<ul>
<li><strong>assets</strong>
<ul>
<li>Array of strings containing URL&#x27;s of required assets.</li>
<li>Array of strings containing URL's of required assets.</li>
</ul>
</li>
<li><strong>onready</strong>
@@ -1010,12 +1010,12 @@ useful for compressed textures. For example, some platforms accept ETC and other
</ul>
</li>
</ul>
<p>All JavaScript clients must call the init function, passing in a list of asset URL&#x27;s and a
<p>All JavaScript clients must call the init function, passing in a list of asset URL's and a
callback. This callback gets invoked only after all assets have been downloaded and the Filament
WebAssembly module has been loaded. Clients should only pass asset URL&#x27;s that absolutely must
WebAssembly module has been loaded. Clients should only pass asset URL's that absolutely must
be ready at initialization time.
When the callback is called, each downloaded asset is available in the <code>Filament.assets</code> global
object, which contains a mapping from URL&#x27;s to Uint8Array objects.</p>
object, which contains a mapping from URL's to Uint8Array objects.</p>
</div>
<div class='funcdoc'>
<h2>function <a id='loadMathExtensions' href='#loadMathExtensions'>loadMathExtensions</a>()</h2>
Binary file not shown.
Binary file not shown.
+157 -157
View File
@@ -10,7 +10,7 @@
textures.</p>
<p>For starters, create a text file called <code>redball.html</code> and copy over the HTML that we used in the
<a href="tutorial_triangle.html">previous tutorial</a>. Change the last script tag from <code>triangle.js</code> to <code>redball.js</code>.</p>
<p>Next you&#x27;ll need to get a couple command-line tools: <code>matc</code> and <code>cmgen</code>. You can find these in the
<p>Next you'll need to get a couple command-line tools: <code>matc</code> and <code>cmgen</code>. You can find these in the
appropriate <a href="//github.com/google/filament/releases">Filament release</a>. You should choose the
archive that corresponds to your development machine rather than the one for web, and the version
that matches the <code>unpkg.com/filament@x.x.x</code> url in the script tag of <code>redball.html</code> (you may check
@@ -19,7 +19,7 @@ out the last available release of <a href="https://www.npmjs.com/package/filamen
<p>The <code>matc</code> tool consumes a text file containing a high-level description of a PBR material, and
produces a binary material package that contains shader code and associated metadata. For more
information, see the official document describing the <a href="https://google.github.io/filament/Materials.md.html">Filament Material System</a>.</p>
<p>Let&#x27;s try out <code>matc</code>. Create the following file in your favorite text editor and call it
<p>Let's try out <code>matc</code>. Create the following file in your favorite text editor and call it
<code>plastic.mat</code>.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>material {
name : Lit,
@@ -44,16 +44,16 @@ fragment {
</pre></div>
<p>Next, invoke <code>matc</code> as follows.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc -a opengl -p mobile -o plastic.filamat plastic.mat
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc<span style="color: #BBB"> </span>-a<span style="color: #BBB"> </span>opengl<span style="color: #BBB"> </span>-p<span style="color: #BBB"> </span>mobile<span style="color: #BBB"> </span>-o<span style="color: #BBB"> </span>plastic.filamat<span style="color: #BBB"> </span>plastic.mat
</pre></div>
<p>You should now have a material archive in your working directory, which we&#x27;ll use later in the
<p>You should now have a material archive in your working directory, which we'll use later in the
tutorial.</p>
<h2>Bake environment map</h2>
<p>Next we&#x27;ll use Filament&#x27;s <code>cmgen</code> tool to consume a HDR environment map in latlong format, and
<p>Next we'll use Filament's <code>cmgen</code> tool to consume a HDR environment map in latlong format, and
produce two cubemap files: a mipmapped IBL and a blurry skybox.</p>
<p>Download <a href="//github.com/google/filament/blob/main/third_party/environments/pillars_2k.hdr">pillars_2k.hdr</a>, then invoke the following command in your terminal.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen -x pillars_2k --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 pillars_2k.hdr
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen<span style="color: #BBB"> </span>-x<span style="color: #BBB"> </span>pillars_2k<span style="color: #BBB"> </span>--format<span style="color: #666">=</span>ktx<span style="color: #BBB"> </span>--size<span style="color: #666">=256</span><span style="color: #BBB"> </span>--extract-blur<span style="color: #666">=0</span>.1<span style="color: #BBB"> </span>pillars_2k.hdr
</pre></div>
<p>You should now have a <code>pillars_2k</code> folder containing a couple KTX files for the IBL and skybox, as
@@ -61,84 +61,84 @@ well as a text file with spherical harmonics coefficients. You can discard the t
IBL KTX contains these coefficients in its metadata.</p>
<h2>Create JavaScript</h2>
<p>Next, create <code>redball.js</code> with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;pillars_2k&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;plastic.filamat&#39;</span><span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>environ<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">&#39;pillars_2k&#39;</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ibl_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>sky_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>filamat_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">&#39;plastic.filamat&#39;</span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>ibl_url,<span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.PrimitiveType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.IndexType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer$IndexType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.LightType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.LightManager$Type;<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #BBB"> </span>filamat_url,<span style="color: #BBB"> </span>ibl_url,<span style="color: #BBB"> </span>sky_url<span style="color: #BBB"> </span>],<span style="color: #BBB"> </span>()<span style="color: #BBB"> </span>=&gt;<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span>
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.VertexAttribute<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.VertexAttribute;
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.AttributeType<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.VertexBuffer$AttributeType;
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.PrimitiveType<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.RenderableManager$PrimitiveType;
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.IndexType<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.IndexBuffer$IndexType;
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.Fov<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Camera$Fov;
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.LightType<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.LightManager$Type;
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(canvas);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>canvas<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666">0</span>];
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.app<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>App(canvas);
}<span style="color: #BBB"> </span>);
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">class</span><span style="color: #BBB"> </span>App<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>canvas;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>engine<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Engine.create(canvas);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>scene<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createScene();
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create material</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create skybox</span><span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create material</span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create skybox</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(scene);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createSwapChain();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createRenderer();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createView();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(scene);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #BBB"> </span>}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10000</span>;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>vec3.rotateY(eye,<span style="color: #bbbbbb"> </span>eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>radians);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span>render()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>eye<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">4</span>],<span style="color: #BBB"> </span>center<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>],<span style="color: #BBB"> </span>up<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>];
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>radians<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">Date</span>.now()<span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">10000</span>;
<span style="color: #BBB"> </span>vec3.rotateY(eye,<span style="color: #BBB"> </span>eye,<span style="color: #BBB"> </span>center,<span style="color: #BBB"> </span>radians);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #BBB"> </span>center,<span style="color: #BBB"> </span>up);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #BBB"> </span>}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>Fov.VERTICAL);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span>resize()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>dpr<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>width<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span>dpr;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>height<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span>dpr;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>width,<span style="color: #BBB"> </span>height]);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666">45</span>,<span style="color: #BBB"> </span>width<span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span>height,<span style="color: #BBB"> </span><span style="color: #666">1.0</span>,<span style="color: #BBB"> </span><span style="color: #666">10.0</span>,<span style="color: #BBB"> </span>Fov.VERTICAL);
<span style="color: #BBB"> </span>}
}
</pre></div>
<p>The above boilerplate should be familiar to you from the previous tutorial, although it loads in a
new set of assets. We also added some animation to the camera.</p>
<p>Next let&#x27;s create a material instance from the package that we built at the beginning the tutorial.
<p>Next let's create a material instance from the package that we built at the beginning the tutorial.
Replace the <strong>create material</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(filamat_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>material<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createMaterial(filamat_url);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>matinstance<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>material.createInstance();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>red<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.8</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>];<span style="color: #bbbbbb"></span>
matinstance.setColor3Parameter(<span style="color: #BA2121">&#39;baseColor&#39;</span>,<span style="color: #bbbbbb"> </span>Filament.RgbType.sRGB,<span style="color: #bbbbbb"> </span>red);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.5</span>);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoat&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoatRoughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.3</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>red<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0.8</span>,<span style="color: #BBB"> </span><span style="color: #666">0.0</span>,<span style="color: #BBB"> </span><span style="color: #666">0.0</span>];
matinstance.setColor3Parameter(<span style="color: #BA2121">&#39;baseColor&#39;</span>,<span style="color: #BBB"> </span>Filament.RgbType.sRGB,<span style="color: #BBB"> </span>red);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #BBB"> </span><span style="color: #666">0.5</span>);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoat&#39;</span>,<span style="color: #BBB"> </span><span style="color: #666">1.0</span>);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoatRoughness&#39;</span>,<span style="color: #BBB"> </span><span style="color: #666">0.3</span>);
</pre></div>
<p>The next step is to create a renderable for the sphere. To help with this, we&#x27;ll use the <code>IcoSphere</code>
<p>The next step is to create a renderable for the sphere. To help with this, we'll use the <code>IcoSphere</code>
utility class, whose constructor takes a LOD. Its job is to subdivide an icosadedron, producing
three arrays:</p>
<ul>
@@ -147,137 +147,137 @@ three arrays:</p>
as quaternions.</li>
<li><code>icosphere.triangles</code> Uint16Array with triangle indices.</li>
</ul>
<p>Let&#x27;s go ahead use these arrays to build the vertex buffer and index buffer. Replace <strong>create
<p>Let's go ahead use these arrays to build the vertex buffer and index buffer. Replace <strong>create
sphere</strong> with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>renderable<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(renderable);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>renderable<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.EntityManager.get().create();
scene.addEntity(renderable);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>icosphere<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.IcoSphere(<span style="color: #666666">5</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>icosphere<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>Filament.IcoSphere(<span style="color: #666">5</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.vertexCount(icosphere.vertices.length<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT3,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.TANGENTS,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.SHORT4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.TANGENTS)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>vb<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.VertexBuffer.Builder()
<span style="color: #BBB"> </span>.vertexCount(icosphere.vertices.length<span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">3</span>)
<span style="color: #BBB"> </span>.bufferCount(<span style="color: #666">2</span>)
<span style="color: #BBB"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>AttributeType.FLOAT3,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>)
<span style="color: #BBB"> </span>.attribute(VertexAttribute.TANGENTS,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span>AttributeType.SHORT4,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>)
<span style="color: #BBB"> </span>.normalized(VertexAttribute.TANGENTS)
<span style="color: #BBB"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.indexCount(icosphere.triangles.length)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferType(IndexType.USHORT)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ib<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.IndexBuffer.Builder()
<span style="color: #BBB"> </span>.indexCount(icosphere.triangles.length)
<span style="color: #BBB"> </span>.bufferType(IndexType.USHORT)
<span style="color: #BBB"> </span>.build(engine);
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>icosphere.vertices);<span style="color: #bbbbbb"></span>
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>icosphere.tangents);<span style="color: #bbbbbb"></span>
ib.setBuffer(engine,<span style="color: #bbbbbb"> </span>icosphere.triangles);<span style="color: #bbbbbb"></span>
vb.setBufferAt(engine,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>icosphere.vertices);
vb.setBufferAt(engine,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span>icosphere.tangents);
ib.setBuffer(engine,<span style="color: #BBB"> </span>icosphere.triangles);
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinstance)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span>vb,<span style="color: #bbbbbb"> </span>ib)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>renderable);<span style="color: #bbbbbb"></span>
Filament.RenderableManager.Builder(<span style="color: #666">1</span>)
<span style="color: #BBB"> </span>.boundingBox({<span style="color: #BBB"> </span>center<span style="color: #666">:</span><span style="color: #BBB"> </span>[<span style="color: #666">-1</span>,<span style="color: #BBB"> </span><span style="color: #666">-1</span>,<span style="color: #BBB"> </span><span style="color: #666">-1</span>],<span style="color: #BBB"> </span>halfExtent<span style="color: #666">:</span><span style="color: #BBB"> </span>[<span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>]<span style="color: #BBB"> </span>})
<span style="color: #BBB"> </span>.material(<span style="color: #666">0</span>,<span style="color: #BBB"> </span>matinstance)
<span style="color: #BBB"> </span>.geometry(<span style="color: #666">0</span>,<span style="color: #BBB"> </span>PrimitiveType.TRIANGLES,<span style="color: #BBB"> </span>vb,<span style="color: #BBB"> </span>ib)
<span style="color: #BBB"> </span>.build(engine,<span style="color: #BBB"> </span>renderable);
</pre></div>
<p>At this point, the app is rendering a sphere, but it is black so it doesn&#x27;t show up. To prove that
<p>At this point, the app is rendering a sphere, but it is black so it doesn't show up. To prove that
the sphere is there, you can try changing the background color to blue via <code>setClearColor</code>, like we
did in the first tutorial.</p>
<h2>Add lighting</h2>
<p>In this section we will create some directional light sources, as well as an image-based light (IBL)
defined by one of the KTX files we built at the start of the demo. First, replace the <strong>create
lights</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sunlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(sunlight);<span style="color: #bbbbbb"></span>
Filament.LightManager.Builder(LightType.SUN)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.color([<span style="color: #666666">0.98</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.92</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.89</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">110000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">0.6</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-0.8</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunAngularRadius(<span style="color: #666666">1.9</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunHaloSize(<span style="color: #666666">10.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunHaloFalloff(<span style="color: #666666">80.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>sunlight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>sunlight<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.EntityManager.get().create();
scene.addEntity(sunlight);
Filament.LightManager.Builder(LightType.SUN)
<span style="color: #BBB"> </span>.color([<span style="color: #666">0.98</span>,<span style="color: #BBB"> </span><span style="color: #666">0.92</span>,<span style="color: #BBB"> </span><span style="color: #666">0.89</span>])
<span style="color: #BBB"> </span>.intensity(<span style="color: #666">110000.0</span>)
<span style="color: #BBB"> </span>.direction([<span style="color: #666">0.6</span>,<span style="color: #BBB"> </span><span style="color: #666">-1.0</span>,<span style="color: #BBB"> </span><span style="color: #666">-0.8</span>])
<span style="color: #BBB"> </span>.sunAngularRadius(<span style="color: #666">1.9</span>)
<span style="color: #BBB"> </span>.sunHaloSize(<span style="color: #666">10.0</span>)
<span style="color: #BBB"> </span>.sunHaloFalloff(<span style="color: #666">80.0</span>)
<span style="color: #BBB"> </span>.build(engine,<span style="color: #BBB"> </span>sunlight);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>backlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(backlight);<span style="color: #bbbbbb"></span>
Filament.LightManager.Builder(LightType.DIRECTIONAL)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>backlight);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>backlight<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.EntityManager.get().create();
scene.addEntity(backlight);
Filament.LightManager.Builder(LightType.DIRECTIONAL)
<span style="color: #BBB"> </span>.direction([<span style="color: #666">-1</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>])
<span style="color: #BBB"> </span>.intensity(<span style="color: #666">50000.0</span>)
<span style="color: #BBB"> </span>.build(engine,<span style="color: #BBB"> </span>backlight);
</pre></div>
<p>The <code>SUN</code> light source is similar to the <code>DIRECTIONAL</code> light source, but has some extra
parameters because Filament will automatically draw a disk into the skybox.</p>
<p>Next we need to create an <code>IndirectLight</code> object from the KTX IBL. One way of doing this is the
following (don&#x27;t type this out, there&#x27;s an easier way).</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>format<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataFormat.RGB;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>datatype<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataType.UINT_10F_11F_11F_REV;<span style="color: #bbbbbb"></span>
following (don't type this out, there's an easier way).</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>format<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.PixelDataFormat.RGB;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>datatype<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.PixelDataType.UINT_10F_11F_11F_REV;
<span style="color: #3D7B7B; font-style: italic">// Create a Texture object for the mipmapped cubemap.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[ibl_url]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>iblktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(ibl_package);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Create a Texture object for the mipmapped cubemap.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ibl_package<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Buffer(Filament.assets[ibl_url]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>iblktx<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>Filament.Ktx1Bundle(ibl_package);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibltex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.width(iblktx.info().pixelWidth)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.height(iblktx.info().pixelHeight)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.levels(iblktx.getNumMipLevels())<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ibltex<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Texture.Builder()
<span style="color: #BBB"> </span>.width(iblktx.info().pixelWidth)
<span style="color: #BBB"> </span>.height(iblktx.info().pixelHeight)
<span style="color: #BBB"> </span>.levels(iblktx.getNumMipLevels())
<span style="color: #BBB"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
<span style="color: #BBB"> </span>.format(Filament.Texture$InternalFormat.RGBA8)
<span style="color: #BBB"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">for</span><span style="color: #bbbbbb"> </span>(<span style="color: #008000; font-weight: bold">let</span><span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span>iblktx.getNumMipLevels();<span style="color: #bbbbbb"> </span><span style="color: #666666">++</span>level)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getCubeBlob(level).getBytes();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>ibltex.setImageCube(engine,<span style="color: #bbbbbb"> </span>level,<span style="color: #bbbbbb"> </span>pixelbuffer);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">for</span><span style="color: #BBB"> </span>(<span style="color: #008000; font-weight: bold">let</span><span style="color: #BBB"> </span>level<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #666">0</span>;<span style="color: #BBB"> </span>level<span style="color: #BBB"> </span><span style="color: #666">&lt;</span><span style="color: #BBB"> </span>iblktx.getNumMipLevels();<span style="color: #BBB"> </span><span style="color: #666">++</span>level)<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>uint8array<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>iblktx.getCubeBlob(level).getBytes();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>pixelbuffer<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.PixelBuffer(uint8array,<span style="color: #BBB"> </span>format,<span style="color: #BBB"> </span>datatype);
<span style="color: #BBB"> </span>ibltex.setImageCube(engine,<span style="color: #BBB"> </span>level,<span style="color: #BBB"> </span>pixelbuffer);
}
<span style="color: #3D7B7B; font-style: italic">// Parse the spherical harmonics metadata.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shstring<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getMetadata(<span style="color: #BA2121">&#39;sh&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shfloats<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>shstring.split(<span style="color: #A45A77">/\s/</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">9</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>).map(<span style="color: #008000">parseFloat</span>);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Parse the spherical harmonics metadata.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>shstring<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>iblktx.getMetadata(<span style="color: #BA2121">&#39;sh&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>shfloats<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>shstring.split(<span style="color: #A45A77">/\s/</span>,<span style="color: #BBB"> </span><span style="color: #666">9</span><span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span><span style="color: #666">3</span>).map(<span style="color: #008000">parseFloat</span>);
<span style="color: #3D7B7B; font-style: italic">// Build the IBL object and insert it into the scene.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndirectLight.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.reflections(ibltex)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.irradianceSh(<span style="color: #666666">3</span>,<span style="color: #bbbbbb"> </span>shfloats)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Build the IBL object and insert it into the scene.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>indirectLight<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.IndirectLight.Builder()
<span style="color: #BBB"> </span>.reflections(ibltex)
<span style="color: #BBB"> </span>.irradianceSh(<span style="color: #666">3</span>,<span style="color: #BBB"> </span>shfloats)
<span style="color: #BBB"> </span>.intensity(<span style="color: #666">50000.0</span>)
<span style="color: #BBB"> </span>.build(engine);
scene.setIndirectLight(indirectLight);<span style="color: #bbbbbb"></span>
scene.setIndirectLight(indirectLight);
</pre></div>
<p>Filament provides a JavaScript utility to make this simpler,
simply replace the <strong>create IBL</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createIblFromKtx1(ibl_url);<span style="color: #bbbbbb"></span>
indirectLight.setIntensity(<span style="color: #666666">50000</span>);<span style="color: #bbbbbb"></span>
scene.setIndirectLight(indirectLight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>indirectLight<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createIblFromKtx1(ibl_url);
indirectLight.setIntensity(<span style="color: #666">50000</span>);
scene.setIndirectLight(indirectLight);
</pre></div>
<h2>Add background</h2>
<p>At this point you can run the demo and you should see a red plastic ball against a black background.
Without a skybox, the reflections on the ball are not representative of its surroundings.
Here&#x27;s one way to create a texture for the skybox:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[sky_url]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skyktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(sky_package);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skytex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.width(skyktx.info().pixelWidth)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.height(skyktx.info().pixelHeight)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.levels(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
Here's one way to create a texture for the skybox:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>sky_package<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Buffer(Filament.assets[sky_url]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>skyktx<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>Filament.Ktx1Bundle(sky_package);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>skytex<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Texture.Builder()
<span style="color: #BBB"> </span>.width(skyktx.info().pixelWidth)
<span style="color: #BBB"> </span>.height(skyktx.info().pixelHeight)
<span style="color: #BBB"> </span>.levels(<span style="color: #666">1</span>)
<span style="color: #BBB"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
<span style="color: #BBB"> </span>.format(Filament.Texture$InternalFormat.RGBA8)
<span style="color: #BBB"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>skyktx.getCubeBlob(<span style="color: #666666">0</span>).getBytes();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);<span style="color: #bbbbbb"></span>
skytex.setImageCube(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>pixelbuffer);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>uint8array<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>skyktx.getCubeBlob(<span style="color: #666">0</span>).getBytes();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>pixelbuffer<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.PixelBuffer(uint8array,<span style="color: #BBB"> </span>format,<span style="color: #BBB"> </span>datatype);
skytex.setImageCube(engine,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>pixelbuffer);
</pre></div>
<p>Filament provides a Javascript utility to make this easier.
Replace <strong>create skybox</strong> with the following.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSkyFromKtx1(sky_url);<span style="color: #bbbbbb"></span>
scene.setSkybox(skybox);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>skybox<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createSkyFromKtx1(sky_url);
scene.setSkybox(skybox);
</pre></div>
<p>That&#x27;s it, we now have a shiny red ball floating in an environment! The complete JavaScript file is
<p>That's it, we now have a shiny red ball floating in an environment! The complete JavaScript file is
available <a href="tutorial_redball.js">here</a>.</p>
<p>In the <a href="tutorial_suzanne.html">next tutorial</a>, we&#x27;ll take a closer look at textures and interaction.</p>
<p>In the <a href="tutorial_suzanne.html">next tutorial</a>, we'll take a closer look at textures and interaction.</p>
</body>
</html>
+121 -121
View File
@@ -8,58 +8,58 @@
<div class="demo_frame"><iframe src="demo_suzanne.html"></iframe><a href="demo_suzanne.html">&#x1F517;</a></div>
<p>This tutorial will describe how to create the <strong>suzanne</strong> demo, introducing you to compressed
textures, mipmap generation, asynchronous texture loading, and trackball rotation.</p>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a>, you&#x27;ll need to use the command-line tools that can be found in
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a>, you'll need to use the command-line tools that can be found in
the appropriate <a href="//github.com/google/filament/releases">Filament release</a> for your development machine. In addition to <code>matc</code> and <code>cmgen</code>,
we&#x27;ll also be using <code>filamesh</code> and <code>mipgen</code>.</p>
we'll also be using <code>filamesh</code> and <code>mipgen</code>.</p>
<h2>Create filamesh file</h2>
<p>Filament does not have an asset loading system, but it does provide a binary mesh format
called <code>filamesh</code> for simple use cases. Let&#x27;s create a compressed filamesh file for suzanne by
called <code>filamesh</code> for simple use cases. Let's create a compressed filamesh file for suzanne by
converting <a href="https://github.com/google/filament/blob/main/assets/models/monkey/monkey.obj">this OBJ file</a>:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>filamesh --compress monkey.obj suzanne.filamesh
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>filamesh<span style="color: #BBB"> </span>--compress<span style="color: #BBB"> </span>monkey.obj<span style="color: #BBB"> </span>suzanne.filamesh
</pre></div>
<h2>Create mipmapped textures</h2>
<p>Next, let&#x27;s create mipmapped KTX files using filament&#x27;s <code>mipgen</code> tool. We&#x27;ll create compressed and
<p>Next, let's create mipmapped KTX files using filament's <code>mipgen</code> tool. We'll create compressed and
non-compressed variants for each texture, since not all platforms support the same compression
formats. First copy over the PNG files from the <a href="https://github.com/google/filament/blob/main/assets/models/monkey">monkey folder</a>, then do:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># Create mipmaps for base color</span>
mipgen albedo.png albedo.ktx2
mipgen --compression<span style="color: #666666">=</span>uastc albedo.png albedo.ktx2
mipgen<span style="color: #BBB"> </span>albedo.png<span style="color: #BBB"> </span>albedo.ktx2
mipgen<span style="color: #BBB"> </span>--compression<span style="color: #666">=</span>uastc<span style="color: #BBB"> </span>albedo.png<span style="color: #BBB"> </span>albedo.ktx2
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the normal map and a compressed variant.</span>
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear normal.png normal.ktx
mipgen --strip-alpha --kernel<span style="color: #666666">=</span>NORMALS --linear --compression<span style="color: #666666">=</span>uastc_normals <span style="color: #AA5D1F; font-weight: bold">\</span>
normal.png normal.ktx2
mipgen<span style="color: #BBB"> </span>--strip-alpha<span style="color: #BBB"> </span>--kernel<span style="color: #666">=</span>NORMALS<span style="color: #BBB"> </span>--linear<span style="color: #BBB"> </span>normal.png<span style="color: #BBB"> </span>normal.ktx
mipgen<span style="color: #BBB"> </span>--strip-alpha<span style="color: #BBB"> </span>--kernel<span style="color: #666">=</span>NORMALS<span style="color: #BBB"> </span>--linear<span style="color: #BBB"> </span>--compression<span style="color: #666">=</span>uastc_normals<span style="color: #BBB"> </span><span style="color: #AA5D1F; font-weight: bold">\</span>
<span style="color: #BBB"> </span>normal.png<span style="color: #BBB"> </span>normal.ktx2
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component roughness map and a compressed variant.</span>
mipgen --grayscale roughness.png roughness.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>uastc roughness.png roughness.ktx2
mipgen<span style="color: #BBB"> </span>--grayscale<span style="color: #BBB"> </span>roughness.png<span style="color: #BBB"> </span>roughness.ktx
mipgen<span style="color: #BBB"> </span>--grayscale<span style="color: #BBB"> </span>--compression<span style="color: #666">=</span>uastc<span style="color: #BBB"> </span>roughness.png<span style="color: #BBB"> </span>roughness.ktx2
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component metallic map and a compressed variant.</span>
mipgen --grayscale metallic.png metallic.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>uastc metallic.png metallic.ktx2
mipgen<span style="color: #BBB"> </span>--grayscale<span style="color: #BBB"> </span>metallic.png<span style="color: #BBB"> </span>metallic.ktx
mipgen<span style="color: #BBB"> </span>--grayscale<span style="color: #BBB"> </span>--compression<span style="color: #666">=</span>uastc<span style="color: #BBB"> </span>metallic.png<span style="color: #BBB"> </span>metallic.ktx2
<span style="color: #3D7B7B; font-style: italic"># Create mipmaps for the single-component occlusion map and a compressed variant.</span>
mipgen --grayscale ao.png ao.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>uastc ao.png ao.ktx2
mipgen<span style="color: #BBB"> </span>--grayscale<span style="color: #BBB"> </span>ao.png<span style="color: #BBB"> </span>ao.ktx
mipgen<span style="color: #BBB"> </span>--grayscale<span style="color: #BBB"> </span>--compression<span style="color: #666">=</span>uastc<span style="color: #BBB"> </span>ao.png<span style="color: #BBB"> </span>ao.ktx2
</pre></div>
<p>For more information on mipgen&#x27;s arguments and supported formats, do <code>mipgen --help</code>.</p>
<p>In a production setting, you&#x27;d want to invoke these commands with a script or build system.</p>
<p>For more information on mipgen's arguments and supported formats, do <code>mipgen --help</code>.</p>
<p>In a production setting, you'd want to invoke these commands with a script or build system.</p>
<h2>Bake environment map</h2>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a> we need to use Filament&#x27;s <code>cmgen</code> tool to produce cubemap files.</p>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a> we need to use Filament's <code>cmgen</code> tool to produce cubemap files.</p>
<p>Download <a href="//github.com/google/filament/blob/main/third_party/environments/venetian_crossroads_2k.hdr">venetian_crossroads_2k.hdr</a>, then invoke the following commands in your terminal.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=64</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
<span style="color: #008000">cd</span> venetian* ; mv venetian*_ibl.ktx venetian_crossroads_2k_skybox_tiny.ktx ; <span style="color: #008000">cd</span> -
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen<span style="color: #BBB"> </span>-x<span style="color: #BBB"> </span>.<span style="color: #BBB"> </span>--format<span style="color: #666">=</span>ktx<span style="color: #BBB"> </span>--size<span style="color: #666">=64</span><span style="color: #BBB"> </span>--extract-blur<span style="color: #666">=0</span>.1<span style="color: #BBB"> </span>venetian_crossroads_2k.hdr
<span style="color: #008000">cd</span><span style="color: #BBB"> </span>venetian*<span style="color: #BBB"> </span>;<span style="color: #BBB"> </span>mv<span style="color: #BBB"> </span>venetian*_ibl.ktx<span style="color: #BBB"> </span>venetian_crossroads_2k_skybox_tiny.ktx<span style="color: #BBB"> </span>;<span style="color: #BBB"> </span><span style="color: #008000">cd</span><span style="color: #BBB"> </span>-
cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
cmgen<span style="color: #BBB"> </span>-x<span style="color: #BBB"> </span>.<span style="color: #BBB"> </span>--format<span style="color: #666">=</span>ktx<span style="color: #BBB"> </span>--size<span style="color: #666">=256</span><span style="color: #BBB"> </span>--extract-blur<span style="color: #666">=0</span>.1<span style="color: #BBB"> </span>venetian_crossroads_2k.hdr
cmgen<span style="color: #BBB"> </span>-x<span style="color: #BBB"> </span>.<span style="color: #BBB"> </span>--format<span style="color: #666">=</span>ktx<span style="color: #BBB"> </span>--size<span style="color: #666">=256</span><span style="color: #BBB"> </span>--extract-blur<span style="color: #666">=0</span>.1<span style="color: #BBB"> </span>venetian_crossroads_2k.hdr
cmgen<span style="color: #BBB"> </span>-x<span style="color: #BBB"> </span>.<span style="color: #BBB"> </span>--format<span style="color: #666">=</span>ktx<span style="color: #BBB"> </span>--size<span style="color: #666">=256</span><span style="color: #BBB"> </span>--extract-blur<span style="color: #666">=0</span>.1<span style="color: #BBB"> </span>venetian_crossroads_2k.hdr
</pre></div>
<h2>Define textured material</h2>
<p>You might recall the <code>filamat</code> file we generated in the previous tutorial for red plastic. For this
demo, we&#x27;ll create a material that uses textures for several parameters.</p>
demo, we'll create a material that uses textures for several parameters.</p>
<p>Create the following text file and call it <code>textured.mat</code>. Note that our material definition now
requires a <code>uv0</code> attribute.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>material {
@@ -90,7 +90,7 @@ fragment {
</pre></div>
<p>Next, invoke <code>matc</code> as follows.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc -a opengl -p mobile -o textured.filamat textured.mat
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc<span style="color: #BBB"> </span>-a<span style="color: #BBB"> </span>opengl<span style="color: #BBB"> </span>-p<span style="color: #BBB"> </span>mobile<span style="color: #BBB"> </span>-o<span style="color: #BBB"> </span>textured.filamat<span style="color: #BBB"> </span>textured.mat
</pre></div>
<p>You should now have a material archive in your working directory. For the suzanne asset, the normal
@@ -100,65 +100,65 @@ materials, consult the official document describing the <a href="https://google.
<p>Create a text file called <code>suzanne.html</code> and copy over the HTML that we used in the <a href="tutorial_redball.html">previous
tutorial</a>. Change the last script tag from <code>redball.js</code> to <code>suzanne.js</code>. Next, create <code>suzanne.js</code>
with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// TODO: declare asset URLs</span><span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// TODO: declare asset URLs</span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>filamesh_url,<span style="color: #bbbbbb"> </span>sky_small_url,<span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>]);<span style="color: #bbbbbb"></span>
});<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #BBB"> </span>filamat_url,<span style="color: #BBB"> </span>filamesh_url,<span style="color: #BBB"> </span>sky_small_url,<span style="color: #BBB"> </span>ibl_url<span style="color: #BBB"> </span>],<span style="color: #BBB"> </span>()<span style="color: #BBB"> </span>=&gt;<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.app<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666">0</span>]);
});
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">class</span><span style="color: #BBB"> </span>App<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>canvas;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Engine.create(canvas);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createScene();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>material<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>material.createInstance();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.suzanne<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>filamesh.renderable;<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>filamesh<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.suzanne<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>filamesh.renderable;
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: fetch larger assets</span><span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: fetch larger assets</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createView();
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>eye<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">4</span>],<span style="color: #BBB"> </span>center<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>],<span style="color: #BBB"> </span>up<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>];
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #BBB"> </span>center,<span style="color: #BBB"> </span>up);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #BBB"> </span>}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span>render()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #BBB"> </span>}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span>resize()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>dpr<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>width<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span>dpr;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>height<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span>dpr;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>width,<span style="color: #BBB"> </span>height]);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov,<span style="color: #bbbbbb"> </span>fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1</span><span style="color: #bbbbbb"> </span><span style="color: #666666">?</span><span style="color: #bbbbbb"> </span>Fov.HORIZONTAL<span style="color: #bbbbbb"> </span><span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>Fov.VERTICAL;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>fov);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>aspect<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>width<span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span>height;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>Fov<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Camera$Fov,<span style="color: #BBB"> </span>fov<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>aspect<span style="color: #BBB"> </span><span style="color: #666">&lt;</span><span style="color: #BBB"> </span><span style="color: #666">1</span><span style="color: #BBB"> </span><span style="color: #666">?</span><span style="color: #BBB"> </span>Fov.HORIZONTAL<span style="color: #BBB"> </span><span style="color: #666">:</span><span style="color: #BBB"> </span>Fov.VERTICAL;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666">45</span>,<span style="color: #BBB"> </span>aspect,<span style="color: #BBB"> </span><span style="color: #666">1.0</span>,<span style="color: #BBB"> </span><span style="color: #666">10.0</span>,<span style="color: #BBB"> </span>fov);
<span style="color: #BBB"> </span>}
}
</pre></div>
<p>Our app will only require a subset of assets to be present for <code>App</code> construction. We&#x27;ll download
<p>Our app will only require a subset of assets to be present for <code>App</code> construction. We'll download
the other assets after construction. By using a progressive loading strategy, we can reduce the
perceived load time.</p>
<p>Next we need to supply the URLs for various assets. This is actually a bit tricky, because different
@@ -171,85 +171,85 @@ string -- which might be empty.</p>
<p>In our case, we know that our web server will have <code>astc</code> and <code>s3tc</code> variants for albedo, and <code>etc</code>
variants for the other textures. The uncompressed variants (empty string) are always available as a
last resort. Go ahead and replace the <strong>declare asset URLs</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;astc s3tc_srgb&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>texture_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;etc&#39;</span>);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>albedo_suffix<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;astc s3tc_srgb&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>texture_suffix<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;etc&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;venetian_crossroads_2k&#39;</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_small_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox_tiny.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_large_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`albedo</span><span style="color: #A45A77; font-weight: bold">${</span>albedo_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`ao</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`metallic</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`normal</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`roughness</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;textured.filamat&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;suzanne.filamesh&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>environ<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">&#39;venetian_crossroads_2k&#39;</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ibl_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>sky_small_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox_tiny.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>sky_large_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>albedo_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`albedo</span><span style="color: #A45A77; font-weight: bold">${</span>albedo_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ao_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`ao</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>metallic_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`metallic</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>normal_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`normal</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>roughness_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">`roughness</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>filamat_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">&#39;textured.filamat&#39;</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>filamesh_url<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #BA2121">&#39;suzanne.filamesh&#39;</span>;
</pre></div>
<h2>Create skybox and IBL</h2>
<p>Next, let&#x27;s create the low-resolution skybox and IBL in the <code>App</code> constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_small_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx1(ibl_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666666">100000</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.setIndirectLight(<span style="color: #008000; font-weight: bold">this</span>.indirectLight);<span style="color: #bbbbbb"></span>
<p>Next, let's create the low-resolution skybox and IBL in the <code>App</code> constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_small_url);
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #008000; font-weight: bold">this</span>.indirectLight<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx1(ibl_url);
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666">100000</span>);
<span style="color: #008000; font-weight: bold">this</span>.scene.setIndirectLight(<span style="color: #008000; font-weight: bold">this</span>.indirectLight);
</pre></div>
<p>This allows users to see a reasonable background fairly quickly, before larger assets have finished
loading in.</p>
<h2>Fetch assets asychronously</h2>
<p>Next we&#x27;ll invoke the <code>Filament.fetch</code> function from within the app constructor. This function is
<p>Next we'll invoke the <code>Filament.fetch</code> function from within the app constructor. This function is
very similar to <code>Filament.init</code>. It takes a list of asset URLs and a callback function that triggers
when the assets have finished downloading.</p>
<p>In our callback, we&#x27;ll make several <code>setTextureParameter</code> calls on the material instance, then we&#x27;ll
<p>In our callback, we'll make several <code>setTextureParameter</code> calls on the material instance, then we'll
recreate the skybox using a higher-resolution texture. As a last step we unhide the renderable that
was created in the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url,<span style="color: #bbbbbb"> </span>albedo_url,<span style="color: #bbbbbb"> </span>roughness_url,<span style="color: #bbbbbb"> </span>metallic_url,<span style="color: #bbbbbb"> </span>normal_url,<span style="color: #bbbbbb"> </span>ao_url],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(albedo_url,<span style="color: #bbbbbb"> </span>{srgb<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(roughness_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(metallic_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(normal_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(ao_url);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url,<span style="color: #BBB"> </span>albedo_url,<span style="color: #BBB"> </span>roughness_url,<span style="color: #BBB"> </span>metallic_url,<span style="color: #BBB"> </span>normal_url,<span style="color: #BBB"> </span>ao_url],<span style="color: #BBB"> </span>()<span style="color: #BBB"> </span>=&gt;<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>albedo<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(albedo_url,<span style="color: #BBB"> </span>{srgb<span style="color: #666">:</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">true</span>});
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>roughness<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(roughness_url);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>metallic<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(metallic_url);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>normal<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(normal_url);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>ao<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx2(ao_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sampler<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.TextureSampler(<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MinFilter.LINEAR_MIPMAP_LINEAR,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MagFilter.LINEAR,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.WrapMode.CLAMP_TO_EDGE);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>sampler<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>Filament.TextureSampler(
<span style="color: #BBB"> </span>Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
<span style="color: #BBB"> </span>Filament.MagFilter.LINEAR,
<span style="color: #BBB"> </span>Filament.WrapMode.CLAMP_TO_EDGE);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;albedo&#39;</span>,<span style="color: #bbbbbb"> </span>albedo,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span>roughness,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;metallic&#39;</span>,<span style="color: #bbbbbb"> </span>metallic,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;normal&#39;</span>,<span style="color: #bbbbbb"> </span>normal,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;ao&#39;</span>,<span style="color: #bbbbbb"> </span>ao,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;albedo&#39;</span>,<span style="color: #BBB"> </span>albedo,<span style="color: #BBB"> </span>sampler);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #BBB"> </span>roughness,<span style="color: #BBB"> </span>sampler);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;metallic&#39;</span>,<span style="color: #BBB"> </span>metallic,<span style="color: #BBB"> </span>sampler);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;normal&#39;</span>,<span style="color: #BBB"> </span>normal,<span style="color: #BBB"> </span>sampler);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;ao&#39;</span>,<span style="color: #BBB"> </span>ao,<span style="color: #BBB"> </span>sampler);
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.destroySkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_large_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.destroySkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_large_url);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.suzanne);<span style="color: #bbbbbb"></span>
});<span style="color: #bbbbbb"></span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
});
</pre></div>
<h2>Introduce trackball rotation</h2>
<p>Add the following script tag to your HTML file. This imports a small third-party library that
listens for drag events and computes a rotation matrix.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;//unpkg.com/gltumble&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;//unpkg.com/gltumble&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
</pre></div>
<p>Next, replace the <strong>initialize gltumble</strong> and <strong>apply gltumble matrix</strong> comments with the following
two code snippets.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.trackball<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Trackball(canvas,<span style="color: #bbbbbb"> </span>{startSpin<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.035</span>});<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.trackball<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>Trackball(canvas,<span style="color: #BBB"> </span>{startSpin<span style="color: #666">:</span><span style="color: #BBB"> </span><span style="color: #666">0.035</span>});
</pre></div>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);<span style="color: #bbbbbb"></span>
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());<span style="color: #bbbbbb"></span>
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>tcm<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>inst<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
tcm.setTransform(inst,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());
inst.<span style="color: #A2F; font-weight: bold">delete</span>();
</pre></div>
<p>That&#x27;s it, we now have a fast-loading interactive demo. The complete JavaScript file is available
<p>That's it, we now have a fast-loading interactive demo. The complete JavaScript file is available
<a href="tutorial_suzanne.js">here</a>.</p>
</body>
+110 -110
View File
@@ -8,30 +8,30 @@
<div class="demo_frame"><iframe src="demo_triangle.html"></iframe><a href="demo_triangle.html">&#x1F517;</a></div>
<h2>Literate programming</h2>
<p>The markdown source for this tutorial is not only used to generate this
web page, it&#x27;s also used to generate the JavaScript for the above demo.
web page, it's also used to generate the JavaScript for the above demo.
We use a small Python script for weaving (generating HTML) and tangling
(generating JS). In the code samples, you&#x27;ll often see
(generating JS). In the code samples, you'll often see
<code>// TODO: &lt;some task&gt;</code>. These are special markers that get replaced by
subsequent code blocks.</p>
<h2>Start your project</h2>
<p>First, create a text file called <code>triangle.html</code> and fill it with the following HTML. This creates
a mobile-friendly page with a full-screen canvas.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #9C6500">&lt;!DOCTYPE html&gt;</span>
&lt;<span style="color: #008000; font-weight: bold">html</span> <span style="color: #687822">lang</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;en&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">html</span> <span style="color: #687822">lang</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;en&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">head</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">title</span>&gt;Filament Tutorial&lt;/<span style="color: #008000; font-weight: bold">title</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #687822">charset</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;utf-8&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #687822">name</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;viewport&quot;</span> <span style="color: #687822">content</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;width=device-width,user-scalable=no,initial-scale=1&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">style</span>&gt;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">body</span><span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">margin</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">overflow</span>:<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">hidden</span>;<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">canvas</span><span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span>touch-action:<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">none</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">width</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">100</span><span style="color: #B00040">%</span>;<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">height</span>:<span style="color: #bbbbbb"> </span><span style="color: #666666">100</span><span style="color: #B00040">%</span>;<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>&lt;/<span style="color: #008000; font-weight: bold">style</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #687822">charset</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;utf-8&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">meta</span> <span style="color: #687822">name</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;viewport&quot;</span> <span style="color: #687822">content</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;width=device-width,user-scalable=no,initial-scale=1&quot;</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">style</span>&gt;
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">body</span><span style="color: #BBB"> </span>{<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">margin</span>:<span style="color: #BBB"> </span><span style="color: #666">0</span>;<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">overflow</span>:<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">hidden</span>;<span style="color: #BBB"> </span>}
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">canvas</span><span style="color: #BBB"> </span>{<span style="color: #BBB"> </span>touch-action:<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">none</span>;<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">width</span>:<span style="color: #BBB"> </span><span style="color: #666">100</span><span style="color: #B00040">%</span>;<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">height</span>:<span style="color: #BBB"> </span><span style="color: #666">100</span><span style="color: #B00040">%</span>;<span style="color: #BBB"> </span>}
<span style="color: #BBB"> </span>&lt;/<span style="color: #008000; font-weight: bold">style</span>&gt;
&lt;/<span style="color: #008000; font-weight: bold">head</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">body</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">canvas</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">canvas</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;filament.js&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;//unpkg.com/gl-matrix@2.8.1&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666666">=</span><span style="color: #BA2121">&quot;triangle.js&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;filament.js&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;//unpkg.com/gl-matrix@2.8.1&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
&lt;<span style="color: #008000; font-weight: bold">script</span> <span style="color: #687822">src</span><span style="color: #666">=</span><span style="color: #BA2121">&quot;triangle.js&quot;</span>&gt;&lt;/<span style="color: #008000; font-weight: bold">script</span>&gt;
&lt;/<span style="color: #008000; font-weight: bold">body</span>&gt;
&lt;/<span style="color: #008000; font-weight: bold">html</span>&gt;
</pre></div>
@@ -48,63 +48,63 @@ a mobile-friendly page with a full-screen canvas.</p>
<li><code>triangle.js</code> will contain your application code.</li>
</ul>
<p>Go ahead and create <code>triangle.js</code> with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">class</span><span style="color: #BBB"> </span>App<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">constructor</span>()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span>
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #BBB"> </span>}
<span style="color: #BBB"> </span>render()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
<span style="color: #BBB"> </span>}
<span style="color: #BBB"> </span>resize()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span>
<span style="color: #BBB"> </span>}
}
Filament.init([<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App()<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>],<span style="color: #BBB"> </span>()<span style="color: #BBB"> </span>=&gt;<span style="color: #BBB"> </span>{<span style="color: #BBB"> </span><span style="color: #008000">window</span>.app<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span>App()<span style="color: #BBB"> </span>}<span style="color: #BBB"> </span>);
</pre></div>
<p>The two calls to <code>bind()</code> allow us to pass instance methods as callbacks for animation and resize
events.</p>
<p><code>Filament.init()</code> consumes two things: a list of asset URLs and a callback.</p>
<p>The callback will be triggered only after all assets finish downloading and the Filament module has
become ready. In our callback, we simply instantiated the <code>App</code> object, since we&#x27;ll do most of the
become ready. In our callback, we simply instantiated the <code>App</code> object, since we'll do most of the
work in its constructor. We also set the app instance into a <code>Window</code> property to make it accessible
from the developer console.</p>
<p>Go ahead and download <a href="triangle.filamat">triangle.filamat</a> and place it in your project folder.
This is a <em>material package</em>, which is a binary file that contains shaders and other bits of data
that define a PBR material. We&#x27;ll learn more about material packages in the next tutorial.</p>
that define a PBR material. We'll learn more about material packages in the next tutorial.</p>
<h2>Spawn a local server</h2>
<p>Because of CORS restrictions, your web app cannot fetch the material package directly from the
file system. One way around this is to create a temporary server using Python or node:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>python3 -m http.server <span style="color: #3D7B7B; font-style: italic"># Python 3</span>
python -m SimpleHTTPServer <span style="color: #3D7B7B; font-style: italic"># Python 2.7</span>
npx http-server -p <span style="color: #666666">8000</span> <span style="color: #3D7B7B; font-style: italic"># nodejs</span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>python3<span style="color: #BBB"> </span>-m<span style="color: #BBB"> </span>http.server<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic"># Python 3</span>
python<span style="color: #BBB"> </span>-m<span style="color: #BBB"> </span>SimpleHTTPServer<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic"># Python 2.7</span>
npx<span style="color: #BBB"> </span>http-server<span style="color: #BBB"> </span>-p<span style="color: #BBB"> </span><span style="color: #666">8000</span><span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic"># nodejs</span>
</pre></div>
<p>To see if this works, navigate to <a href="http://localhost:8000">http://localhost:8000</a> and check if you
can load the page without any errors appearing in the developer console.</p>
<p>Take care not to use Python&#x27;s simple server in production since it does not serve WebAssembly files
<p>Take care not to use Python's simple server in production since it does not serve WebAssembly files
with the correct MIME type.</p>
<h2>Create the Engine and Scene</h2>
<p>We now have a basic skeleton that can respond to paint and resize events. Let&#x27;s start adding
<p>We now have a basic skeleton that can respond to paint and resize events. Let's start adding
Filament objects to the app. Insert the following code into the top of the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(<span style="color: #008000; font-weight: bold">this</span>.canvas);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666">0</span>];
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>engine<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Engine.create(<span style="color: #008000; font-weight: bold">this</span>.canvas);
</pre></div>
<p>The above snippet creates the <code>Engine</code> by passing it a canvas DOM object. The engine needs the
canvas in order to create a WebGL 2.0 context in its contructor.</p>
<p>The engine is a factory for many Filament entities, including <code>Scene</code>, which is a flat container of
entities. Let&#x27;s go ahead and create a scene, then add a blank entity called <code>triangle</code> into the
entities. Let's go ahead and create a scene, then add a blank entity called <code>triangle</code> into the
scene.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.triangle<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createScene();
<span style="color: #008000; font-weight: bold">this</span>.triangle<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.EntityManager.get().create();
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.triangle);
</pre></div>
<p>Filament uses an <a href="//en.wikipedia.org/wiki/Entity-component-system">Entity-Component System</a>.
@@ -112,30 +112,30 @@ The triangle entity in the above snippet does not yet have an associated compone
tutorial we will make it into a <em>renderable</em>. Renderables are entities that have associated draw
calls.</p>
<h2>Construct typed arrays</h2>
<p>Next we&#x27;ll create two typed arrays: a positions array with XY coordinates for each vertex, and a
<p>Next we'll create two typed arrays: a positions array with XY coordinates for each vertex, and a
colors array with a 32-bit word for each vertex.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Float32Array</span>([<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"></span>
]);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>TRIANGLE_POSITIONS<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span><span style="color: #008000">Float32Array</span>([
<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,
<span style="color: #BBB"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span><span style="color: #666">2</span><span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">3</span>),<span style="color: #BBB"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span><span style="color: #666">2</span><span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">3</span>),
<span style="color: #BBB"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span><span style="color: #666">4</span><span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">3</span>),<span style="color: #BBB"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span><span style="color: #666">4</span><span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">3</span>),
]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_COLORS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint32Array</span>([<span style="color: #666666">0xffff0000</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff00ff00</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff0000ff</span>]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>TRIANGLE_COLORS<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span><span style="color: #008000">Uint32Array</span>([<span style="color: #666">0xffff0000</span>,<span style="color: #BBB"> </span><span style="color: #666">0xff00ff00</span>,<span style="color: #BBB"> </span><span style="color: #666">0xff0000ff</span>]);
</pre></div>
<p>Next we&#x27;ll use the positions and colors buffers to create a single <code>VertexBuffer</code> object.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.vertexCount(<span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT2,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">8</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.COLOR,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.UBYTE4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.COLOR)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<p>Next we'll use the positions and colors buffers to create a single <code>VertexBuffer</code> object.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>VertexAttribute<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.VertexAttribute;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>AttributeType<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.VertexBuffer$AttributeType;
<span style="color: #008000; font-weight: bold">this</span>.vb<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.VertexBuffer.Builder()
<span style="color: #BBB"> </span>.vertexCount(<span style="color: #666">3</span>)
<span style="color: #BBB"> </span>.bufferCount(<span style="color: #666">2</span>)
<span style="color: #BBB"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>AttributeType.FLOAT2,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">8</span>)
<span style="color: #BBB"> </span>.attribute(VertexAttribute.COLOR,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span>AttributeType.UBYTE4,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">4</span>)
<span style="color: #BBB"> </span>.normalized(VertexAttribute.COLOR)
<span style="color: #BBB"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_COLORS);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>TRIANGLE_POSITIONS);
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span>TRIANGLE_COLORS);
</pre></div>
<p>The above snippet first creates aliases for two enum types, then constructs the vertex buffer using
@@ -147,74 +147,74 @@ self-documenting.</p>
<p>Our app sets up two buffer slots in the vertex buffer, and each slot is associated with a single
attribute. Alternatively, we could have interleaved or concatenated these attributes into a single
buffer slot.</p>
<p>Next we&#x27;ll construct an index buffer. The index buffer for our triangle is trivial: it simply holds
<p>Next we'll construct an index buffer. The index buffer for our triangle is trivial: it simply holds
the integers 0,1,2.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.indexCount(<span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferType(Filament.IndexBuffer$IndexType.USHORT)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.ib<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.IndexBuffer.Builder()
<span style="color: #BBB"> </span>.indexCount(<span style="color: #666">3</span>)
<span style="color: #BBB"> </span>.bufferType(Filament.IndexBuffer$IndexType.USHORT)
<span style="color: #BBB"> </span>.build(engine);
<span style="color: #008000; font-weight: bold">this</span>.ib.setBuffer(engine,<span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint16Array</span>([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">2</span>]));<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.ib.setBuffer(engine,<span style="color: #BBB"> </span><span style="color: #A2F; font-weight: bold">new</span><span style="color: #BBB"> </span><span style="color: #008000">Uint16Array</span>([<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">2</span>]));
</pre></div>
<p>Note that constructing an index buffer is similar to constructing a vertex buffer, but it only has
one buffer slot, and it can only contain two types of data (USHORT or UINT).</p>
<h2>Finish up initialization</h2>
<p>Next let&#x27;s construct an actual <code>Material</code> from the material package that was downloaded (the
<p>Next let's construct an actual <code>Material</code> from the material package that was downloaded (the
material is an object; the package is just a binary blob), then extract the default
<code>MaterialInstance</code> from the material object. Material instances have concrete values for their
parameters, and they can be bound to renderables. We&#x27;ll learn more about material instances in the
parameters, and they can be bound to renderables. We'll learn more about material instances in the
next tutorial.</p>
<p>After extracting the material instance, we can finally create a renderable component for the
triangle by setting up a bounding box and passing in the vertex and index buffers.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>mat<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat.getDefaultInstance();<span style="color: #bbbbbb"></span>
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinst)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.vb,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.ib)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>mat<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createMaterial(<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>matinst<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>mat.getDefaultInstance();
Filament.RenderableManager.Builder(<span style="color: #666">1</span>)
<span style="color: #BBB"> </span>.boundingBox({<span style="color: #BBB"> </span>center<span style="color: #666">:</span><span style="color: #BBB"> </span>[<span style="color: #666">-1</span>,<span style="color: #BBB"> </span><span style="color: #666">-1</span>,<span style="color: #BBB"> </span><span style="color: #666">-1</span>],<span style="color: #BBB"> </span>halfExtent<span style="color: #666">:</span><span style="color: #BBB"> </span>[<span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>]<span style="color: #BBB"> </span>})
<span style="color: #BBB"> </span>.material(<span style="color: #666">0</span>,<span style="color: #BBB"> </span>matinst)
<span style="color: #BBB"> </span>.geometry(<span style="color: #666">0</span>,<span style="color: #BBB"> </span>Filament.RenderableManager$PrimitiveType.TRIANGLES,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.vb,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.ib)
<span style="color: #BBB"> </span>.build(engine,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.triangle);
</pre></div>
<p>Next let&#x27;s wrap up the initialization routine by creating the swap chain, renderer, camera, and
<p>Next let's wrap up the initialization routine by creating the swap chain, renderer, camera, and
view.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createSwapChain();
<span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createRenderer();
<span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>engine.createView();
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);
<span style="color: #3D7B7B; font-style: italic">// Set up a blue-green background:</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.setClearOptions({clearColor<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.2</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>],<span style="color: #bbbbbb"> </span>clear<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Set up a blue-green background:</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.setClearOptions({clearColor<span style="color: #666">:</span><span style="color: #BBB"> </span>[<span style="color: #666">0.0</span>,<span style="color: #BBB"> </span><span style="color: #666">0.1</span>,<span style="color: #BBB"> </span><span style="color: #666">0.2</span>,<span style="color: #BBB"> </span><span style="color: #666">1.0</span>],<span style="color: #BBB"> </span>clear<span style="color: #666">:</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">true</span>});
<span style="color: #3D7B7B; font-style: italic">// Adjust the initial viewport:</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Adjust the initial viewport:</span>
<span style="color: #008000; font-weight: bold">this</span>.resize();
</pre></div>
<p>At this point, we&#x27;re done creating all Filament entities, and the code should run without errors.
<p>At this point, we're done creating all Filament entities, and the code should run without errors.
However the canvas is still blank!</p>
<h2>Render and resize handlers</h2>
<p>Recall that our App class has a skeletal render method, which the browser calls every time it needs
to repaint. Often this is 60 times a second.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>render()<span style="color: #BBB"> </span>{
<span style="color: #BBB"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
<span style="color: #BBB"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
</pre></div>
<p>Let&#x27;s flesh this out by rotating the triangle and invoking the Filament renderer. Add the following
<p>Let's flesh this out by rotating the triangle and invoking the Filament renderer. Add the following
code to the top of the render method.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// Rotate the triangle.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1000</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>transform<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat4.fromRotation(mat4.create(),<span style="color: #bbbbbb"> </span>radians,<span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span>transform);<span style="color: #bbbbbb"></span>
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// Rotate the triangle.</span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>radians<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">Date</span>.now()<span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span><span style="color: #666">1000</span>;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>transform<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>mat4.fromRotation(mat4.create(),<span style="color: #BBB"> </span>radians,<span style="color: #BBB"> </span>[<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>tcm<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>inst<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);
tcm.setTransform(inst,<span style="color: #BBB"> </span>transform);
inst.<span style="color: #A2F; font-weight: bold">delete</span>();
<span style="color: #3D7B7B; font-style: italic">// Render the frame.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Render the frame.</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.view);
</pre></div>
<p>The first half of our render method obtains the transform component of the triangle entity and uses
@@ -225,19 +225,19 @@ that it wants to skip a frame, hence the <code>if</code> statement.</p>
<p>One last step. Add the following code to the resize method. This adjusts the resolution of the
rendering surface when the window size changes, taking <code>devicePixelRatio</code> into account for high-DPI
displays. It also adjusts the camera frustum accordingly.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>dpr<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>width<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span>dpr;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>height<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #BBB"> </span><span style="color: #666">*</span><span style="color: #BBB"> </span>dpr;
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span>width,<span style="color: #BBB"> </span>height]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Projection<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Projection;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO,<span style="color: #bbbbbb"> </span><span style="color: #666666">-</span>aspect,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>aspect<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>width<span style="color: #BBB"> </span><span style="color: #666">/</span><span style="color: #BBB"> </span>height;
<span style="color: #008000; font-weight: bold">const</span><span style="color: #BBB"> </span>Projection<span style="color: #BBB"> </span><span style="color: #666">=</span><span style="color: #BBB"> </span>Filament.Camera$Projection;
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO,<span style="color: #BBB"> </span><span style="color: #666">-</span>aspect,<span style="color: #BBB"> </span>aspect,<span style="color: #BBB"> </span><span style="color: #666">-1</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>,<span style="color: #BBB"> </span><span style="color: #666">0</span>,<span style="color: #BBB"> </span><span style="color: #666">1</span>);
</pre></div>
<p>You should now have a spinning triangle! The completed JavaScript is available
<a href="tutorial_triangle.js">here</a>.</p>
<p>In the <a href="tutorial_redball.html">next tutorial</a>, we&#x27;ll take a closer look at Filament materials and 3D rendering.</p>
<p>In the <a href="tutorial_redball.html">next tutorial</a>, we'll take a closer look at Filament materials and 3D rendering.</p>
</body>
</html>
+1
View File
@@ -18,6 +18,7 @@
- [Metal](./notes/metal_debugging.md)
- [Vulkan](./notes/vulkan_debugging.md)
- [SPIR-V](./notes/spirv_debugging.md)
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
- [bluevk](./dup/bluevk.md)
+41
View File
@@ -0,0 +1,41 @@
# Running with ASAN/UBSAN
## Enabling
When building though build.sh, pass the `-b` flag. This sets the cmake variable
`FILAMENT_ENABLE_ASAN_UBSAN=ON` which eventually passes `"-fsanitize=address -fsanitize=undefined"`
to all compile and link operations.
If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
`-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to cmake in order to get the same result.
## Getting memory leak detection on Mac
Memory leak detection isn't enabled by default on MacOS. There are two issues to address, first is
using a version of clang that supports memory leak detection and second is enabling it at runtime.
The version of clang distributed by Apple (with a version like "Apple clang version 16.0.0") doesn't
currently support leak detection at all. Instead you will need to get or build a different LLVM,
such as the one distributed through homebrew and get CMake to use that instead.
Then during runtime you'll need to have the environment variable `ASAN_OPTIONS` include the option
`detect_leaks=1`. Multiple `ASAN_OPTIONS` values are concatenated with `:`.
## Getting memory leak output in CLion
### Setting variables
Under `Settings | Build, Execution, Deployment | Dynamic Analysis Tools | Sanitizers` there is an
ASAN Settings field that overrides whatever other `ASAN_OPTIONS` you might set elsewhere, so you
must use that instead of setting it through your Run/Debug Configuration.
To pass `-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to CMake you'll want to create a new CMake Profile and
pass it as a CMake argument.
### Avoiding losing output
CMake will consume ASAN output and display it through a separate "Sanitizers" tab. Unfortunately
certain leak detection errors that interrupt the executable seem to not show up in this tab, but are
still removed from the user-visible console output. If this is happening and you need to see the
unfiltered console output you'll need to go to `Settings | Build, Execution, Deployment | Dynamic
Analysis Tools | Sanitizers` and uncheck "Use visual representation for Sanitizer's output".
+10
View File
@@ -194,6 +194,8 @@ if (FILAMENT_SUPPORTS_VULKAN)
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
@@ -257,8 +259,11 @@ if (FILAMENT_SUPPORTS_WEBGPU)
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)
@@ -507,8 +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
@@ -532,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
@@ -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
@@ -30,6 +30,7 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/StaticString.h>
#include <math/vec4.h>
@@ -1139,6 +1140,7 @@ struct ExternalSamplerDatum {
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
std::variant<utils::StaticString, utils::CString, std::monostate> label;
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed
@@ -1455,6 +1457,11 @@ enum class Workaround : uint16_t {
DISABLE_BLIT_INTO_TEXTURE_ARRAY,
// Multiple workarounds needed for PowerVR GPUs
POWER_VR_SHADER_WORKAROUNDS,
// Some browsers, such as Firefox on Mac, struggle with slow shader compile/link times when
// creating programs for the default material, leading to startup stutters. This workaround
// prevents these stutters by not precaching depth variants of the default material for those
// particular browsers.
DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL,
};
using StereoscopicType = backend::Platform::StereoscopicType;
@@ -149,13 +149,6 @@ public:
* - PlatformEGLAndroid
*/
bool assertNativeWindowIsValid = false;
/**
* The action to take if a Drawable cannot be acquired. If true, the
* frame is aborted instead of panic. This is only supported for:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
};
Platform() noexcept;
+2 -2
View File
@@ -44,8 +44,8 @@ public:
struct Descriptor {
utils::CString name;
backend::DescriptorType type;
backend::descriptor_binding_t binding;
DescriptorType type;
descriptor_binding_t binding;
};
struct SpecializationConstant {
@@ -294,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
*/
@@ -304,11 +314,6 @@ public:
*/
uint32_t height;
/**
* The layerCount of the external image
*/
uint32_t layerCount;
/**
* The layer count of the external image
*/
@@ -324,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.
*/
@@ -348,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;
@@ -374,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;
@@ -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
@@ -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
+2 -2
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;
}
+1
View File
@@ -185,6 +185,7 @@ inline MTLPixelFormat getMetalFormat(PixelDataFormat format, PixelDataType type)
CONVERT(RGBA_INTEGER, UINT, RGBA32Uint);
CONVERT(RGBA_INTEGER, INT, RGBA32Sint);
CONVERT(RGBA, FLOAT, RGBA32Float);
CONVERT(DEPTH_COMPONENT, FLOAT, Depth32Float);
#undef CONVERT
return MTLPixelFormatInvalid;
@@ -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);
@@ -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));
@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
return MetalDriverFactory::create(this, driverConfig);
}
+29 -37
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();
}
}
+61 -36
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
+23 -17
View File
@@ -340,7 +340,7 @@ void OpenGLContext::setDefaultState() noexcept {
GL_DITHER,
GL_SAMPLE_ALPHA_TO_COVERAGE,
GL_SAMPLE_COVERAGE,
GL_POLYGON_OFFSET_FILL,
GL_POLYGON_OFFSET_FILL,
};
UTILS_NOUNROLL
@@ -600,6 +600,12 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
// ANGLE or not. (b/376125497)
bugs->rebind_buffer_after_deletion = true;
// We disable depth precache for the default material on Mozilla FireFox. It struggles with
// slow shader compile/link times if the shader contains large arrays of uniform. Some depth
// program variants have skinning-related data, which incurs this slowness and end up
// causing an initial startup stalls. (b/392917621)
bugs->disable_depth_precache_for_default_material = true;
}
#ifdef BACKEND_OPENGL_VERSION_GLES
@@ -1069,41 +1075,41 @@ void OpenGLContext::resetState() noexcept {
glCullFace(state.raster.cullFace);
glBlendEquationSeparate(state.raster.blendEquationRGB, state.raster.blendEquationA);
glBlendFuncSeparate(
state.raster.blendFunctionSrcRGB,
state.raster.blendFunctionSrcRGB,
state.raster.blendFunctionDstRGB,
state.raster.blendFunctionSrcA,
state.raster.blendFunctionDstA
);
glColorMask(
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask
);
glDepthMask(state.raster.depthMask);
glDepthFunc(state.raster.depthFunc);
// state.stencil
glStencilFuncSeparate(
GL_FRONT,
state.stencil.front.func.func,
state.stencil.front.func.ref,
GL_FRONT,
state.stencil.front.func.func,
state.stencil.front.func.ref,
state.stencil.front.func.mask
);
glStencilFuncSeparate(
GL_BACK,
state.stencil.back.func.func,
state.stencil.back.func.ref,
GL_BACK,
state.stencil.back.func.func,
state.stencil.back.func.ref,
state.stencil.back.func.mask
);
glStencilOpSeparate(
GL_FRONT,
GL_FRONT,
state.stencil.front.op.sfail,
state.stencil.front.op.dpfail,
state.stencil.front.op.dppass
);
glStencilOpSeparate(
GL_BACK,
GL_BACK,
state.stencil.back.op.sfail,
state.stencil.back.op.dpfail,
state.stencil.back.op.dppass
@@ -1206,9 +1212,9 @@ void OpenGLContext::resetState() noexcept {
// state.window
glScissor(
state.window.scissor.x,
state.window.scissor.y,
state.window.scissor.z,
state.window.scissor.x,
state.window.scissor.y,
state.window.scissor.z,
state.window.scissor.w
);
glViewport(
@@ -328,6 +328,11 @@ public:
// bugs or performance issues.
bool force_feature_level0;
// Some browsers, such as Firefox on Mac, struggle with slow shader compile/link times when
// creating programs for the default material, leading to startup stutters. This workaround
// prevents these stutters by not precaching depth variants of the default material for
// those particular browsers.
bool disable_depth_precache_for_default_material;
} bugs = {};
@@ -569,6 +574,9 @@ private:
{ bugs.force_feature_level0,
"force_feature_level0",
""},
{ bugs.disable_depth_precache_for_default_material,
"disable_depth_precache_for_default_material",
""},
}};
// this is chosen to minimize code size
@@ -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
@@ -2434,6 +2435,8 @@ bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
return mContext.bugs.disable_blit_into_texture_array;
case Workaround::POWER_VR_SHADER_WORKAROUNDS:
return mContext.bugs.powervr_shader_workarounds;
case Workaround::DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL:
return mContext.bugs.disable_depth_precache_for_default_material;
default:
return false;
}
@@ -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
@@ -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
@@ -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
@@ -28,6 +28,7 @@
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <utils/Log.h>
namespace filament::backend {
+5
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
@@ -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();
}
@@ -142,6 +142,10 @@ public:
return mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
}
inline bool isUnifiedMemoryArchitecture() const noexcept {
return mIsUnifiedMemoryArchitecture;
}
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
@@ -164,6 +168,7 @@ private:
bool mDebugUtilsSupported = false;
bool mLazilyAllocatedMemorySupported = false;
bool mProtectedMemorySupported = false;
bool mIsUnifiedMemoryArchitecture = false;
fvkutils::VkFormatList mDepthStencilFormats;
fvkutils::VkFormatList mBlittableDepthStencilFormats;
@@ -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,39 +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;
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
if (pipelineLayoutIsSame) {
auto& lastBoundSets = mLastBoundInfo.boundSets;
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
curMask.unset(index);
}
});
if (curMask.none() &&
mLastBoundInfo.setMask == setMask && mLastBoundInfo.boundSets == updateSets) {
return;
auto const& updateSets = mStashedSets;
curMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
curMask.unset(index);
}
} else {
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
});
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->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data());
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set);
});
@@ -334,7 +329,7 @@ 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->getVkSet(),
.dstBinding = binding,
@@ -343,12 +338,17 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
.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 {
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) &&
@@ -364,16 +364,29 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
};
VkWriteDescriptorSet const descriptorWrite = {
VkWriteDescriptorSet descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->getVkSet(),
.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);
}
@@ -383,32 +396,32 @@ void VulkanDescriptorSetCache::updateInputAttachment(
// TOOD: fill this in.
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = getVkSet(layout);
auto const vkSet = mDescriptorPool->obtainSet(layout->count, layout->getVkLayout());
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle,
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);
}
});
set->setVkSet(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;
}
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
return mDescriptorPool->obtainSet(layout);
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(DescriptorCount const& count,
VkDescriptorSetLayout vklayout) {
return mDescriptorPool->obtainSet(count, vklayout);
}
void VulkanDescriptorSetCache::manualRecyle(VulkanDescriptorSetLayout::Count const& count,
void VulkanDescriptorSetCache::manualRecycle(VulkanDescriptorSetLayout::Count const& count,
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
mDescriptorPool->recycle(count, vklayout, 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 = {}; }
@@ -43,6 +43,7 @@ public:
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();
@@ -56,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;
@@ -65,17 +70,17 @@ 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 only meant to be used with external samplers (or internally within this
// class).
VkDescriptorSet getVkSet(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 only meant to be used with external samplers.
void manualRecyle(VulkanDescriptorSetLayout::Count 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; }
@@ -83,6 +88,9 @@ public:
void gc();
private:
void updateSamplerImpl(VkDescriptorSet set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
class DescriptorInfinitePool;
VkDevice mDevice;
@@ -127,6 +127,7 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
@@ -141,7 +142,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
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, bitmasks.externalSampler,
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, externalSamplers,
immutableSamplers);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
@@ -160,9 +161,9 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
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);
layout->setVkLayout(getVkLayout(layout->bitmask));
std::move(info), getVkLayout(maskGroup, maskGroup.externalSampler));
return layout;
}
@@ -46,6 +46,7 @@ public:
// This method is meant to be used with external samplers
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
fvkutils::SamplerBitmask externalSamplers,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
private:
+179 -77
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);
}
+35 -14
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;
@@ -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
@@ -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
+23 -9
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();
}
+4 -1
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;
+10 -3
View File
@@ -149,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);
+40 -13
View File
@@ -71,7 +71,7 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
fvkutils::SamplerBitmask sampler; // 8 bytes
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
// This is a subset of the bitmask.sampler field.
// This is a subset of the sampler field.
fvkutils::SamplerBitmask externalSampler; // 8 bytes
bool operator==(Bitmask const& right) const {
@@ -79,6 +79,8 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
inputAttachment == right.inputAttachment &&
externalSampler == right.externalSampler;
}
static Bitmask fromLayoutDescription(DescriptorSetLayout const& layout);
};
static_assert(sizeof(Bitmask) == 40);
@@ -120,16 +122,20 @@ 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 const& getVkLayout() const noexcept { return mVkLayout; }
VkDescriptorSetLayout getVkLayout() const noexcept { return mVkLayout; }
// It is possible to have the layout switch out due to AHardwarebuffer (external image) format
// changes.
void setVkLayout(VkDescriptorSetLayout vklayout) noexcept { mVkLayout = vklayout; }
VkDescriptorSetLayout getExternalSamplerVkLayout() const noexcept {
return mExternalSamplerVkLayout;
}
void setExternalSamplerVkLayout(VkDescriptorSetLayout vklayout) noexcept {
mExternalSamplerVkLayout = vklayout;
}
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
@@ -137,7 +143,11 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
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 {
@@ -149,23 +159,37 @@ public:
VulkanDescriptorSet(
fvkutils::UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn)
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 const& getVkSet() const noexcept {
VkDescriptorSet getVkSet() const noexcept {
return mVkSet;
}
// Note that the only case where you'd set it more than once is with external images/samplers.
void setVkSet(VkDescriptorSet vkset) noexcept { mVkSet = vkset; }
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 {
mOffsets = std::move(offsets);
@@ -182,17 +206,20 @@ public:
uint8_t const uniqueDynamicUboCount;
private:
VkDescriptorSet mVkSet = VK_NULL_HANDLE;
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; }
@@ -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 {
@@ -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 = {};

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