Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d67e1a9ee8 |
@@ -45,23 +45,18 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- 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 ../..
|
||||
- name: Upload release assets
|
||||
run: |
|
||||
xargs -L 1 sudo pip3 install < build/common/requirements.txt
|
||||
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;
|
||||
- uses: actions/github-script@v6
|
||||
python3 build/common/upload-assets.py ${TAG} out/*.tgz
|
||||
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);
|
||||
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
@@ -80,21 +75,16 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh release
|
||||
cd ../..
|
||||
- name: Upload release assets
|
||||
run: |
|
||||
xargs -L 1 sudo pip3 install < build/common/requirements.txt
|
||||
mv out/filament-release-web.tgz out/filament-${TAG}-web.tgz
|
||||
- uses: actions/github-script@v6
|
||||
python3 build/common/upload-assets.py ${TAG} out/*.tgz
|
||||
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);
|
||||
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
@@ -113,16 +103,8 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
cd build/android && printf "y" | ./build.sh release
|
||||
cd ../..
|
||||
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
|
||||
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
|
||||
mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-android.aar
|
||||
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
|
||||
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
|
||||
- name: Sign sample-gltf-viewer
|
||||
run: |
|
||||
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
|
||||
@@ -137,15 +119,18 @@ jobs:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
APK_KEYSTORE_BASE64: ${{ secrets.APK_KEYSTORE_BASE64 }}
|
||||
APK_KEYSTORE_PASS: ${{ secrets.APK_KEYSTORE_PASS }}
|
||||
- uses: actions/github-script@v6
|
||||
- name: Upload release assets
|
||||
run: |
|
||||
xargs -L 1 sudo pip3 install < build/common/requirements.txt
|
||||
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
|
||||
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
|
||||
mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-android.aar
|
||||
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
|
||||
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
|
||||
python3 build/common/upload-assets.py ${TAG} out/*.aar out/*.apk
|
||||
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/*.aar', 'out/*.apk'].join('\n'));
|
||||
await upload({ github, context }, await globber.glob(), TAG);
|
||||
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
|
||||
|
||||
build-ios:
|
||||
name: build-ios
|
||||
@@ -164,21 +149,16 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh release
|
||||
cd ../..
|
||||
- name: Upload release assets
|
||||
run: |
|
||||
xargs -L 1 sudo pip3 install < build/common/requirements.txt
|
||||
mv out/filament-release-ios.tgz out/filament-${TAG}-ios.tgz
|
||||
- uses: actions/github-script@v6
|
||||
python3 build/common/upload-assets.py ${TAG} out/*.tgz
|
||||
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);
|
||||
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
|
||||
|
||||
build-windows:
|
||||
name: build-windows
|
||||
@@ -198,19 +178,15 @@ jobs:
|
||||
with:
|
||||
ref: ${{ steps.git_ref.outputs.ref }}
|
||||
- name: Run build script
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
build\windows\build-github.bat release
|
||||
cd ..\..
|
||||
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
|
||||
shell: cmd
|
||||
- uses: actions/github-script@v6
|
||||
- name: Upload release assets
|
||||
run: |
|
||||
pip3 install PyGithub
|
||||
mv out/filament-windows.tgz out/filament-${TAG}-windows.tgz
|
||||
python build/common/upload-assets.py ${TAG} out/*.tgz
|
||||
shell: bash
|
||||
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);
|
||||
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
|
||||
|
||||
+9
-13
@@ -74,18 +74,18 @@ if (CCACHE_PROGRAM)
|
||||
configure_file(build/launch-cxx.in launch-cxx)
|
||||
|
||||
execute_process(COMMAND chmod a+rx
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/launch-c"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/launch-cxx"
|
||||
"${CMAKE_BINARY_DIR}/launch-c"
|
||||
"${CMAKE_BINARY_DIR}/launch-cxx"
|
||||
)
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx")
|
||||
else()
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-c")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-cxx")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -99,10 +99,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
# ==================================================================================================
|
||||
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT WEBGL)
|
||||
set(LINUX TRUE)
|
||||
else()
|
||||
# since cmake 3.25 LINUX is automatically set based on CMAKE_SYSTEM_NAME, which the android
|
||||
# cmake files are setting to "Linux".
|
||||
set(LINUX FALSE)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
@@ -554,7 +550,7 @@ function(list_licenses OUTPUT MODULES)
|
||||
endif()
|
||||
endforeach()
|
||||
configure_file(${FILAMENT}/build/licenses.inc.in ${OUTPUT})
|
||||
endfunction()
|
||||
endfunction(list_licenses)
|
||||
|
||||
set(COMBINE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/build/linux/combine-static-libs.sh")
|
||||
if (WIN32)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.31.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.28.2'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
```
|
||||
pod 'Filament', '~> 1.31.1'
|
||||
pod 'Filament', '~> 1.28.2'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
@@ -117,9 +117,9 @@ steps:
|
||||
- Image-based lighting
|
||||
- Physically-based camera (shutter speed, sensitivity and aperture)
|
||||
- Physical light units
|
||||
- Point lights, spot lights, and directional light
|
||||
- Point lights, spot lights and directional light
|
||||
- Specular anti-aliasing
|
||||
- Point, spot, and directional light shadows
|
||||
- Spot and directional light shadows
|
||||
- Cascaded shadows
|
||||
- EVSM, PCSS, DPCF, or PCF shadows
|
||||
- Transparent shadows
|
||||
|
||||
@@ -5,41 +5,12 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## main branch
|
||||
|
||||
## v1.31.1
|
||||
|
||||
- gltfio: expose joint inverse bind matrices via method on FilamentInstance
|
||||
- filamesh: add error when uv coords size does not match num of vertices. (#6351)
|
||||
|
||||
## v1.31.0
|
||||
|
||||
- java: fix confusing typo in Camera documentation
|
||||
- engine: the output color space can be set on `ColorGrading`. This API currently has limitations,
|
||||
please refer to the documentation for more details
|
||||
- Support for RGTC and BPTC texture compression
|
||||
- engine: fix TransformManager high precision mode when using transactions
|
||||
- web: added TypeScript definition for `Engine.destroy`
|
||||
- materials: `getNormalizedViewportCoord()` now returns the logical (i.e. user) viewport
|
||||
normalized position and keeps z reversed [⚠️ **Recompile Materials**]
|
||||
- backend: workaround Adreno shader compiler bug (#6355) [⚠️ **Recompile Materials**]
|
||||
- geometry: change computing tangent basis from normal vector to use Frisvad's method
|
||||
|
||||
## v1.30.0
|
||||
|
||||
- engine: optimize per-shadow UBO [⚠️ **Recompile Materials**]
|
||||
- engine: fix potential memory corruption with larger scenes
|
||||
- engine: VSM's MSAA setting is now per-view instead of per-light [⚠️ **API Change**]
|
||||
|
||||
## v1.29.0
|
||||
|
||||
- gltfio: calculate primitive's AABB correctly.
|
||||
- gltfio: recompute bounding boxes with morph targets
|
||||
- engine: add missing getters on `MaterialInstance`
|
||||
- WebGL: add missing `ColorGrading` JS bindings
|
||||
- engine: improvements/cleanup of Shadow mapping code [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.28.3
|
||||
|
||||
|
||||
- backend: add support for GGP platform
|
||||
- engine: primitives with `CullingMode::FRONT_AND_BACK` are now skipped.
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
|
||||
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
|
||||
jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
|
||||
jfloat maxShadowDistance, jint vsmMsaaSamples, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
|
||||
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
|
||||
LightManager::ShadowOptions shadowOptions {
|
||||
.mapSize = (uint32_t)mapSize,
|
||||
@@ -97,6 +97,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
.stepCount = uint8_t(stepCount),
|
||||
.maxShadowDistance = maxShadowDistance,
|
||||
.vsm = {
|
||||
.msaaSamples = (uint8_t) vsmMsaaSamples,
|
||||
.elvsm = (bool)elvsm,
|
||||
.blurWidth = blurWidth
|
||||
},
|
||||
|
||||
@@ -365,7 +365,7 @@ public class Camera {
|
||||
* By default, this is an identity matrix.
|
||||
* </p>
|
||||
*
|
||||
* @param inScaling diagonal of the scaling matrix to be applied after the projection matrix.
|
||||
* @param scaling diagonal of the scaling matrix to be applied after the projection matrix.
|
||||
*
|
||||
* @see Camera#setProjection
|
||||
* @see Camera#setLensProjection
|
||||
@@ -409,7 +409,7 @@ public class Camera {
|
||||
*
|
||||
* <pre>
|
||||
* engine.getTransformManager().setTransform(
|
||||
* engine.getTransformManager().getInstance(camera->getEntity()), modelMatrix);
|
||||
* engine.getTransformManager().getInstance(camera->getEntity()), viewMatrix);
|
||||
* </pre>
|
||||
*
|
||||
* @param modelMatrix The camera position and orientation provided as a <b>rigid transform</b> matrix.
|
||||
|
||||
+18
-2
@@ -349,6 +349,20 @@ public class LightManager {
|
||||
* @see View#setShadowType
|
||||
*/
|
||||
|
||||
/**
|
||||
* The number of MSAA samples to use when rendering VSM shadow maps.
|
||||
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
|
||||
* off MSAA.
|
||||
* Higher values may not be available depending on the underlying hardware.
|
||||
*
|
||||
* <p>
|
||||
* <strong>Warning: This API is still experimental and subject to change.</strong>
|
||||
* </p>
|
||||
*/
|
||||
@IntRange(from = 1)
|
||||
public int vsmMsaaSamples = 1;
|
||||
|
||||
|
||||
/**
|
||||
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
|
||||
* an improvement to the default EVSM which suffers important light leaks. Enabling
|
||||
@@ -505,7 +519,7 @@ public class LightManager {
|
||||
options.shadowFarHint, options.stable, options.lispsm,
|
||||
options.polygonOffsetConstant, options.polygonOffsetSlope,
|
||||
options.screenSpaceContactShadows,
|
||||
options.stepCount, options.maxShadowDistance,
|
||||
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
|
||||
options.elvsm, options.blurWidth, options.shadowBulbRadius);
|
||||
return this;
|
||||
}
|
||||
@@ -1004,6 +1018,8 @@ public class LightManager {
|
||||
* lightbulbs.
|
||||
*
|
||||
* @param efficiency Efficiency in percent. This depends on the type of lightbulb used.
|
||||
*
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
public void setIntensity(@EntityInstance int i, float watts, float efficiency) {
|
||||
nSetIntensity(mNativeObject, i , watts, efficiency);
|
||||
@@ -1169,7 +1185,7 @@ public class LightManager {
|
||||
boolean stable, boolean lispsm,
|
||||
float polygonOffsetConstant, float polygonOffsetSlope,
|
||||
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
|
||||
boolean elvsm, float blurWidth, float shadowBulbRadius);
|
||||
int vsmMsaaSamples, boolean elvsm, float blurWidth, float shadowBulbRadius);
|
||||
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
|
||||
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
|
||||
|
||||
@@ -124,24 +124,7 @@ public class Renderer {
|
||||
*/
|
||||
public static class ClearOptions {
|
||||
/**
|
||||
* Color (sRGB linear) to use to clear the RenderTarget (typically the SwapChain).
|
||||
*
|
||||
* The RenderTarget is cleared using this color, which won't be tone-mapped since
|
||||
* tone-mapping is part of View rendering (this is not).
|
||||
*
|
||||
* When a View is rendered, there are 3 scenarios to consider:
|
||||
* - Pixels rendered by the View replace the clear color (or blend with it in
|
||||
* `BlendMode.TRANSLUCENT` mode).
|
||||
*
|
||||
* - With blending mode set to `BlendMode.TRANSLUCENT`, Pixels untouched by the View
|
||||
* are considered fulling transparent and let the clear color show through.
|
||||
*
|
||||
* - With blending mode set to `BlendMode.OPAQUE`, Pixels untouched by the View
|
||||
* are set to the clear color. However, because it is now used in the context of a View,
|
||||
* it will go through the post-processing stage, which includes tone-mapping.
|
||||
*
|
||||
* For consistency, it is recommended to always use a Skybox to clear an opaque View's
|
||||
* background, or to use black or fully-transparent (i.e. {0,0,0,0}) as the clear color.
|
||||
* Color to use to clear the SwapChain
|
||||
*/
|
||||
@NonNull
|
||||
public float[] clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
@@ -250,19 +250,7 @@ public class Texture {
|
||||
SRGB8_ALPHA8_ASTC_10x8,
|
||||
SRGB8_ALPHA8_ASTC_10x10,
|
||||
SRGB8_ALPHA8_ASTC_12x10,
|
||||
SRGB8_ALPHA8_ASTC_12x12,
|
||||
|
||||
// RGTC formats available with a GLES extension
|
||||
RED_RGTC1, // BC4 unsigned
|
||||
SIGNED_RED_RGTC1, // BC4 signed
|
||||
RED_GREEN_RGTC2, // BC5 unsigned
|
||||
SIGNED_RED_GREEN_RGTC2, // BC5 signed
|
||||
|
||||
// BPTC formats available with a GLES extension
|
||||
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
|
||||
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
|
||||
RGBA_BPTC_UNORM, // BC7
|
||||
SRGB_ALPHA_BPTC_UNORM // BC7 sRGB
|
||||
SRGB8_ALPHA8_ASTC_12x12
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,19 +296,7 @@ public class Texture {
|
||||
SRGB8_ALPHA8_ASTC_10x8,
|
||||
SRGB8_ALPHA8_ASTC_10x10,
|
||||
SRGB8_ALPHA8_ASTC_12x10,
|
||||
SRGB8_ALPHA8_ASTC_12x12,
|
||||
|
||||
// RGTC formats available with a GLES extension
|
||||
RED_RGTC1, // BC4 unsigned
|
||||
SIGNED_RED_RGTC1, // BC4 signed
|
||||
RED_GREEN_RGTC2, // BC5 unsigned
|
||||
SIGNED_RED_GREEN_RGTC2, // BC5 signed
|
||||
|
||||
// BPTC formats available with a GLES extension
|
||||
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
|
||||
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
|
||||
RGBA_BPTC_UNORM, // BC7
|
||||
SRGB_ALPHA_BPTC_UNORM // BC7 sRGB
|
||||
SRGB8_ALPHA8_ASTC_12x12
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1817,13 +1817,6 @@ public class View {
|
||||
* Whether to generate mipmaps for all VSM shadow maps.
|
||||
*/
|
||||
public boolean mipmapping = false;
|
||||
/**
|
||||
* The number of MSAA samples to use when rendering VSM shadow maps.
|
||||
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
|
||||
* off MSAA.
|
||||
* Higher values may not be available depending on the underlying hardware.
|
||||
*/
|
||||
public int msaaSamples = 1;
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.31.1
|
||||
VERSION_NAME=1.28.2
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+15
-18
@@ -27,7 +27,6 @@ import android.view.animation.LinearInterpolator
|
||||
|
||||
import com.google.android.filament.*
|
||||
import com.google.android.filament.RenderableManager.*
|
||||
import com.google.android.filament.Renderer.ClearOptions
|
||||
import com.google.android.filament.VertexBuffer.*
|
||||
import com.google.android.filament.android.DisplayHelper
|
||||
import com.google.android.filament.android.UiHelper
|
||||
@@ -65,6 +64,7 @@ class MainActivity : Activity() {
|
||||
private lateinit var view0: View
|
||||
private lateinit var view1: View
|
||||
private lateinit var view2: View
|
||||
private lateinit var view3: View
|
||||
private lateinit var view4: View
|
||||
// We need skybox to set the background color
|
||||
private lateinit var skybox: Skybox
|
||||
@@ -119,19 +119,15 @@ class MainActivity : Activity() {
|
||||
view0 = engine.createView()
|
||||
view1 = engine.createView()
|
||||
view2 = engine.createView()
|
||||
view3 = engine.createView()
|
||||
view4 = engine.createView()
|
||||
|
||||
view0.setName("view0");
|
||||
view1.setName("view1");
|
||||
view2.setName("view2");
|
||||
view3.setName("view3");
|
||||
view4.setName("view4");
|
||||
|
||||
// Useful for debugging issues
|
||||
// view0.isPostProcessingEnabled = false
|
||||
// view1.isPostProcessingEnabled = false
|
||||
// view2.isPostProcessingEnabled = false
|
||||
// view4.isPostProcessingEnabled = false
|
||||
|
||||
view4.blendMode = View.BlendMode.TRANSLUCENT;
|
||||
|
||||
skybox = Skybox.Builder().build(engine);
|
||||
@@ -144,11 +140,13 @@ class MainActivity : Activity() {
|
||||
view0.camera = camera
|
||||
view1.camera = camera
|
||||
view2.camera = camera
|
||||
view3.camera = camera
|
||||
view4.camera = camera
|
||||
|
||||
view0.scene = scene
|
||||
view1.scene = scene
|
||||
view2.scene = scene
|
||||
view3.scene = scene
|
||||
view4.scene = scene
|
||||
}
|
||||
|
||||
@@ -381,6 +379,7 @@ class MainActivity : Activity() {
|
||||
engine.destroyView(view0)
|
||||
engine.destroyView(view1)
|
||||
engine.destroyView(view2)
|
||||
engine.destroyView(view3)
|
||||
engine.destroyView(view4)
|
||||
engine.destroySkybox(skybox)
|
||||
engine.destroyScene(scene)
|
||||
@@ -408,18 +407,19 @@ class MainActivity : Activity() {
|
||||
// If beginFrame() returns false you should skip the frame
|
||||
// This means you are sending frames too quickly to the GPU
|
||||
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
|
||||
scene.skybox = skybox
|
||||
skybox.setColor(0.35f, 0.35f, 0.35f, 1.0f);
|
||||
|
||||
skybox.setColor(0.035f, 0.035f, 0.035f, 1.0f);
|
||||
renderer.render(view0)
|
||||
|
||||
skybox.setColor(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
|
||||
skybox.setColor(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
renderer.render(view1)
|
||||
|
||||
scene.skybox = null
|
||||
|
||||
skybox.setColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
renderer.render(view2)
|
||||
|
||||
skybox.setColor(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
renderer.render(view3)
|
||||
|
||||
skybox.setColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
renderer.render(view4)
|
||||
|
||||
renderer.endFrame()
|
||||
@@ -433,10 +433,6 @@ class MainActivity : Activity() {
|
||||
swapChain?.let { engine.destroySwapChain(it) }
|
||||
swapChain = engine.createSwapChain(surface)
|
||||
renderer.setDisplayInfo(DisplayHelper.getDisplayInfo(surfaceView.display, Renderer.DisplayInfo()))
|
||||
renderer.clearOptions = renderer.clearOptions.apply {
|
||||
clear = true
|
||||
clearColor = floatArrayOf( 0.0f, 0.0f, 1.0f, 0.0f )
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromSurface() {
|
||||
@@ -457,6 +453,7 @@ class MainActivity : Activity() {
|
||||
view0.viewport = Viewport(0, 0, width / 2, height / 2)
|
||||
view1.viewport = Viewport(width / 2, 0, width / 2, height / 2)
|
||||
view2.viewport = Viewport(0, height / 2, width / 2, height / 2)
|
||||
view3.viewport = Viewport(width / 2, height / 2, width / 2, height / 2)
|
||||
view4.viewport = Viewport(width / 4, height / 4, width / 2, height / 2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
setuptools==40.6.2
|
||||
wheel==0.37.1
|
||||
certifi==2021.10.8
|
||||
cffi==1.15.0
|
||||
charset-normalizer==2.0.12
|
||||
Deprecated==1.2.13
|
||||
idna==3.3
|
||||
pycparser==2.21
|
||||
PyGithub==1.55
|
||||
PyJWT==2.4.0
|
||||
PyNaCl==1.5.0
|
||||
requests==2.27.1
|
||||
urllib3==1.26.9
|
||||
wrapt==1.14.0
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
# Copyright (C) 2019 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.
|
||||
|
||||
|
||||
# requirements.txt created via:
|
||||
#
|
||||
# pip3 install virtualenv
|
||||
# python3 -m venv env
|
||||
# pip3 install PyGithub
|
||||
# pip3 install wheel
|
||||
# --all includes wheel and setuputils, which is needed for GitHub Actions
|
||||
# pip3 freeze --all > requirements.txt
|
||||
# Manually move 'setuptools' and 'wheel' lines to the beginning of the file.
|
||||
# Manually remove the 'pip' line.
|
||||
#
|
||||
# Requirements are installed in GitHub Actions via:
|
||||
# xargs -L 1 sudo pip3 install < build/common/requirements.txt
|
||||
# xargs must be used because the ordering of the dependencies matters (namely, setuptools).
|
||||
# sudo must be used to uninstall non-matching versions of packages.
|
||||
# pip is removed to avoid conflicts with the pre-installed pip version on Actions.
|
||||
# This technique does not work with Windows, unfortunately.
|
||||
|
||||
from github import Github
|
||||
import os, sys
|
||||
|
||||
def print_usage():
|
||||
print('Upload assets to a Filament GitHub release.')
|
||||
print()
|
||||
print('Usage:')
|
||||
print(' upload-assets.py <tag> <asset>...')
|
||||
print()
|
||||
print('Notes:')
|
||||
print(' The GitHub release must already be created prior to running this script. This is typically done')
|
||||
print(' through the GitHub web UI.')
|
||||
print()
|
||||
print(' <tag> is the Git tag for the desired release to attach assets to, for example, "v1.4.2".')
|
||||
print()
|
||||
print(' <asset> is a path to the asset file to upload. The file name will be used as the name of the')
|
||||
print(' asset.')
|
||||
print()
|
||||
print(' The GITHUB_API_KEY environment variable must be set to a valid authentication token for a')
|
||||
print(' collaborator account of the Filament repository.')
|
||||
|
||||
# The first argument is the path to this script.
|
||||
if len(sys.argv) < 3:
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
tag = sys.argv[1]
|
||||
assets = sys.argv[2:]
|
||||
|
||||
authentication_token = os.environ.get('GITHUB_API_KEY')
|
||||
if not authentication_token:
|
||||
sys.stderr.write('Error: the GITHUB_API_KEY is not set.\n')
|
||||
sys.exit(1)
|
||||
|
||||
g = Github(authentication_token)
|
||||
|
||||
FILAMENT_REPO = "google/filament"
|
||||
filament = g.get_repo(FILAMENT_REPO)
|
||||
|
||||
def find_release_from_tag(repo, tag):
|
||||
""" Find a release in the repo for the given Git tag string. """
|
||||
releases = repo.get_releases()
|
||||
for r in releases:
|
||||
if r.tag_name == tag:
|
||||
return r
|
||||
return None
|
||||
|
||||
release = find_release_from_tag(filament, tag)
|
||||
if not release:
|
||||
sys.stderr.write(f"Error: Could not find release with tag '{tag}'.\n")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Found release with tag '{tag}'.")
|
||||
|
||||
for asset_path in assets:
|
||||
sys.stdout.write(f'Uploding asset: {asset_path}... ')
|
||||
asset_name = os.path.basename(asset_path)
|
||||
asset = release.upload_asset(asset_path, name=asset_name)
|
||||
if asset:
|
||||
sys.stdout.write('Success!\n')
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
const uploadReleaseAssets = async ({github, context}, assetPathsToUpload, releaseTag) => {
|
||||
const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
|
||||
const filesToUpload = assetPathsToUpload.map(assetPath => ({
|
||||
buffer: fs.readFile(assetPath),
|
||||
assetName: path.basename(assetPath)
|
||||
}));
|
||||
|
||||
const findReleaseMatchingTag = async tag => {
|
||||
const { data: releases } = await github.rest.repos.listReleases(context.repo);
|
||||
const release = releases.find(release => release.tag_name === releaseTag);
|
||||
if (!release) {
|
||||
throw new Error(`Could not locate release with tag '${releaseTag}'`);
|
||||
}
|
||||
return release;
|
||||
};
|
||||
|
||||
const release = await findReleaseMatchingTag(releaseTag);
|
||||
|
||||
console.log(`Found release named '${release.name}' matching tag '${release.tag_name}'.`);
|
||||
|
||||
const uploadPromises = [];
|
||||
for (const file of filesToUpload) {
|
||||
console.log(`Uploading asset ${file.assetName}.`);
|
||||
const p = github.rest.repos.uploadReleaseAsset({
|
||||
...context.repo,
|
||||
release_id: release.id,
|
||||
name: file.assetName,
|
||||
data: await file.buffer
|
||||
});
|
||||
uploadPromises.push(p);
|
||||
}
|
||||
|
||||
await Promise.all(uploadPromises);
|
||||
console.log("Done!");
|
||||
}
|
||||
|
||||
module.exports = uploadReleaseAssets;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// To test this script locally, uncomment the code below and run:
|
||||
//
|
||||
// npm install
|
||||
// node index.js
|
||||
|
||||
// const { Octokit } = require("@octokit/rest");
|
||||
// const glob = require("@actions/glob");
|
||||
// (async () => {
|
||||
// const github = new Octokit({
|
||||
// auth: "" // <-- paste GitHub auth token here
|
||||
// });
|
||||
// const context = {
|
||||
// repo: {
|
||||
// owner: "bejado",
|
||||
// repo: "filament",
|
||||
// },
|
||||
// };
|
||||
// const globber = await glob.create('*.txt');
|
||||
// await uploadReleaseAssets({ github, context }, await globber.glob(), 'vtest-28');
|
||||
// })();
|
||||
-546
@@ -1,546 +0,0 @@
|
||||
{
|
||||
"name": "upload-release-assets",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@actions/glob": "^0.3.0",
|
||||
"@octokit/rest": "^19.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/glob": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.3.0.tgz",
|
||||
"integrity": "sha512-tJP1ZhF87fd6LBnaXWlahkyvdgvsLl7WnreW1EZaC8JWjpMXmzqWzQVe/IEYslrkT9ymibVrKyJN4UMD7uQM2w==",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz",
|
||||
"integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz",
|
||||
"integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^3.0.0",
|
||||
"@octokit/graphql": "^5.0.0",
|
||||
"@octokit/request": "^6.0.0",
|
||||
"@octokit/request-error": "^3.0.0",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz",
|
||||
"integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz",
|
||||
"integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^6.0.0",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
|
||||
"integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
|
||||
"integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-request-log": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
|
||||
"integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz",
|
||||
"integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^8.0.0",
|
||||
"deprecation": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
|
||||
"integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^7.0.0",
|
||||
"@octokit/request-error": "^3.0.0",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz",
|
||||
"integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^8.0.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "19.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz",
|
||||
"integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^4.1.0",
|
||||
"@octokit/plugin-paginate-rest": "^5.0.0",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^6.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz",
|
||||
"integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"@actions/glob": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.3.0.tgz",
|
||||
"integrity": "sha512-tJP1ZhF87fd6LBnaXWlahkyvdgvsLl7WnreW1EZaC8JWjpMXmzqWzQVe/IEYslrkT9ymibVrKyJN4UMD7uQM2w==",
|
||||
"requires": {
|
||||
"@actions/core": "^1.2.6",
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
||||
"requires": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"@octokit/auth-token": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz",
|
||||
"integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==",
|
||||
"requires": {
|
||||
"@octokit/types": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz",
|
||||
"integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^3.0.0",
|
||||
"@octokit/graphql": "^5.0.0",
|
||||
"@octokit/request": "^6.0.0",
|
||||
"@octokit/request-error": "^3.0.0",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/endpoint": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz",
|
||||
"integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==",
|
||||
"requires": {
|
||||
"@octokit/types": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/graphql": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz",
|
||||
"integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==",
|
||||
"requires": {
|
||||
"@octokit/request": "^6.0.0",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
|
||||
"integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
|
||||
"integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
|
||||
"requires": {
|
||||
"@octokit/types": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-request-log": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
|
||||
"integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz",
|
||||
"integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==",
|
||||
"requires": {
|
||||
"@octokit/types": "^8.0.0",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
|
||||
"integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^7.0.0",
|
||||
"@octokit/request-error": "^3.0.0",
|
||||
"@octokit/types": "^8.0.0",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/request-error": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz",
|
||||
"integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^8.0.0",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/rest": {
|
||||
"version": "19.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz",
|
||||
"integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==",
|
||||
"requires": {
|
||||
"@octokit/core": "^4.1.0",
|
||||
"@octokit/plugin-paginate-rest": "^5.0.0",
|
||||
"@octokit/plugin-request-log": "^1.0.4",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^6.7.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz",
|
||||
"integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^14.0.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "upload-release-assets",
|
||||
"version": "1.0.0",
|
||||
"description": "Upload Filament release assets",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Google",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@actions/glob": "^0.3.0",
|
||||
"@octokit/rest": "^19.0.5"
|
||||
}
|
||||
}
|
||||
@@ -2263,7 +2263,7 @@ type aliases:
|
||||
|
||||
Name | Type | Description
|
||||
:-----------------------------------|:--------:|:------------------------------------
|
||||
**getResolution()** | float4 | Dimensions of the view's effective (physical) viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands.
|
||||
**getResolution()** | float4 | Dimensions of the view's effective viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands. This can be used in conjunction with `getNormalizedViewportCoord()` to generate pixel coordinates.
|
||||
**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space
|
||||
**getWorldOffset()** | float3 | The shift required to obtain API-level world space
|
||||
**getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1
|
||||
@@ -2301,7 +2301,7 @@ The following APIs are only available from the fragment block:
|
||||
**getWorldNormalVector()** | float3 | Normalized normal in world space, after bump mapping (must be used after `prepareMaterial()`)
|
||||
**getWorldGeometricNormalVector()** | float3 | Normalized normal in world space, before bump mapping (can be used before `prepareMaterial()`)
|
||||
**getWorldReflectedVector()** | float3 | Reflection of the view vector about the normal (must be used after `prepareMaterial()`)
|
||||
**getNormalizedViewportCoord()** | float3 | Normalized user viewport position (i.e. NDC coordinates normalized to [0, 1] for the position, [1, 0] for the depth), can be used before `prepareMaterial()`). Because the user viewport is smaller than the actual physical viewport, these coordinates can be negative or superior to 1 in the non-visible area of the physical viewport.
|
||||
**getNormalizedViewportCoord()** | float3 | Normalized viewport position (i.e. NDC coordinates normalized to [0, 1], can be used before `prepareMaterial()`)
|
||||
**getNdotV()** | float | The result of `dot(normal, view)`, always strictly greater than 0 (must be used after `prepareMaterial()`)
|
||||
**getColor()** | float4 | Interpolated color of the fragment, if the color attribute is required
|
||||
**getUV0()** | float2 | First interpolated set of UV coordinates, only available if the uv0 attribute is required
|
||||
|
||||
+157
-343
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -261,7 +261,7 @@ class App {
|
||||
this.renderer.setClearOptions({clearColor: kBackgroundColor, clear: true});
|
||||
|
||||
this.render = this.render.bind(this);
|
||||
this.simpleViewer = new Filament.ViewerGui(engine, scene, view, kInitialSidebarWidth);
|
||||
this.simpleViewer = new Filament.SimpleViewer(engine, scene, view, kInitialSidebarWidth);
|
||||
|
||||
this.mouseX = -1;
|
||||
this.mouseY = -1;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+153
-330
File diff suppressed because one or more lines are too long
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
+20
-21
@@ -10,7 +10,7 @@ html, body { height: 100%; }
|
||||
body { margin: 0; overflow: hidden; }
|
||||
#container { position: relative; height: 100%; }
|
||||
canvas { position: absolute; width: 100%; height: 100%; }
|
||||
#messages { position: absolute; width: 100%; height: 100%; padding-left: 10px; color:blue; pointer-events: none; }
|
||||
#messages { position: absolute; width: 100%; height: 100%; padding-left: 10px; color:white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -20,7 +20,7 @@ canvas { position: absolute; width: 100%; height: 100%; }
|
||||
</div>
|
||||
<script src="filament.js"></script>
|
||||
<script src="gl-matrix-min.js"></script>
|
||||
<script src="gltumble.min.js"></script>
|
||||
<script src="https://unpkg.com/gltumble"></script>
|
||||
<script>
|
||||
|
||||
const env = 'default_env';
|
||||
@@ -43,19 +43,7 @@ class App {
|
||||
const scene = this.scene = engine.createScene();
|
||||
this.trackball = new Trackball(canvas, {startSpin: 0.035});
|
||||
|
||||
const messages = document.getElementById('messages');
|
||||
|
||||
canvas.addEventListener('pointerdown', evt => {
|
||||
const x = evt.clientX;
|
||||
const y = this.canvas.getBoundingClientRect().height - 1 - evt.clientY;
|
||||
const dpr = window.devicePixelRatio;
|
||||
this.view.pick(x * dpr, y * dpr, (results) => {
|
||||
const name = this.asset.getName(results.renderable);
|
||||
messages.innerText = name ? ('Picked ' + name) : '';
|
||||
});
|
||||
});
|
||||
|
||||
const indirectLight = this.ibl = engine.createIblFromKtx1(ibl_url);
|
||||
const indirectLight = this.ibl = engine.createIblFromKtx(ibl_url);
|
||||
this.scene.setIndirectLight(indirectLight);
|
||||
|
||||
const iblDirection = IndirectLight.getDirectionEstimate(indirectLight.shfloats);
|
||||
@@ -70,7 +58,7 @@ class App {
|
||||
mat3.fromRotation(mat, radians, [0, 1, 0]);
|
||||
indirectLight.setRotation(mat);
|
||||
|
||||
const skybox = engine.createSkyFromKtx1(sky_url);
|
||||
const skybox = engine.createSkyFromKtx(sky_url);
|
||||
this.scene.setSkybox(skybox);
|
||||
|
||||
const sunlight = Filament.EntityManager.get().create();
|
||||
@@ -88,17 +76,28 @@ class App {
|
||||
const loader = this.loader = engine.createAssetLoader();
|
||||
|
||||
this.allowRefresh = false;
|
||||
const asset = this.asset = loader.createAsset(mesh_url);
|
||||
const asset = this.asset = loader.createAssetFromJson(mesh_url);
|
||||
this.assetRoot = this.asset.getRoot();
|
||||
|
||||
const messages = document.getElementById('messages');
|
||||
|
||||
// Crudely indicate progress by printing the URI of each resource as it is loaded.
|
||||
const onFetched = (uri) => messages.innerText += `Downloaded ${uri}\n`;
|
||||
|
||||
const onDone = () => {
|
||||
this.allowRefresh = true;
|
||||
|
||||
// Clear the progress indication messages.
|
||||
messages.innerText = "";
|
||||
// Enable shadows on every renderable.
|
||||
const entities = asset.getEntities();
|
||||
const rm = engine.getRenderableManager();
|
||||
for (const entity of entities) {
|
||||
const instance = rm.getInstance(entity);
|
||||
rm.setCastShadows(instance, true);
|
||||
instance.delete();
|
||||
}
|
||||
|
||||
// Hide the HUD.
|
||||
messages.remove();
|
||||
};
|
||||
asset.loadResources(onDone, onFetched);
|
||||
|
||||
@@ -138,7 +137,7 @@ class App {
|
||||
this.allowRefresh = false;
|
||||
this.scene.removeEntities(this.asset.getEntities());
|
||||
this.loader.destroyAsset(this.asset);
|
||||
this.asset = this.loader.createAsset(mesh_url);
|
||||
this.asset = this.loader.createAssetFromJson(mesh_url);
|
||||
const onDone = () => { this.allowRefresh = true; }
|
||||
this.asset.loadResources(onDone);
|
||||
}
|
||||
@@ -150,7 +149,7 @@ class App {
|
||||
tcm.setTransform(inst, this.trackball.getMatrix());
|
||||
inst.delete();
|
||||
|
||||
// Gradually add renderables to the scene as their textures become ready.
|
||||
// Add renderable entities to the scene as they become ready.
|
||||
let entity;
|
||||
const popRenderable = () => {
|
||||
entity = this.asset.popRenderable();
|
||||
|
||||
Binary file not shown.
@@ -54,14 +54,14 @@ class App {
|
||||
.build(engine, sunlight);
|
||||
this.scene.addEntity(sunlight);
|
||||
|
||||
const indirectLight = this.ibl = engine.createIblFromKtx1(iblfile);
|
||||
const indirectLight = this.ibl = engine.createIblFromKtx(iblfile);
|
||||
this.scene.setIndirectLight(indirectLight);
|
||||
|
||||
const radians = 1.0;
|
||||
indirectLight.setRotation(mat3.fromRotation(mat3.create(), radians, [0, 1, 0]))
|
||||
indirectLight.setIntensity(10000);
|
||||
|
||||
const skybox = engine.createSkyFromKtx1(skyfile);
|
||||
const skybox = engine.createSkyFromKtx(skyfile);
|
||||
this.scene.setSkybox(skybox);
|
||||
|
||||
const material = engine.createMaterial('parquet.filamat');
|
||||
|
||||
Binary file not shown.
+128
-137
@@ -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'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>
|
||||
@@ -69,7 +69,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#KtxInfo">KtxInfo</a></td>
|
||||
<td align="left">Property accessor for KTX1 header.</td>
|
||||
<td align="left">Property accessor for KTX header.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#LightManager%24Instance">LightManager$Instance</a></td>
|
||||
@@ -89,7 +89,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#Renderer">Renderer</a></td>
|
||||
<td align="left">Represents the platform'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'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>
|
||||
@@ -190,10 +190,6 @@
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#ColorGrading%24LutFormat">ColorGrading$LutFormat</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#ColorGrading%24QualityLevel">ColorGrading$QualityLevel</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
@@ -218,10 +214,6 @@
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#FeatureLevel">FeatureLevel</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#Frustum%24Plane">Frustum$Plane</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
@@ -230,14 +222,6 @@
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#Ktx2Reader%24Result">Ktx2Reader$Result</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#Ktx2Reader%24TransferFunction">Ktx2Reader$TransferFunction</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#LightManager%24Type">LightManager$Type</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
@@ -274,14 +258,6 @@
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#StencilFace">StencilFace</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#StencilOperation">StencilOperation</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#Texture%24CubemapFace">Texture$CubemapFace</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
@@ -298,10 +274,6 @@
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#TransparencyMode">TransparencyMode</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#VertexAttribute">VertexAttribute</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
@@ -314,6 +286,26 @@
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#View%24AntiAliasing">View$AntiAliasing</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#View%24BlendMode">View$BlendMode</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#View%24BloomOptions%24BlendMode">View$BloomOptions$BlendMode</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#View%24DepthOfFieldOptions%24Filter">View$DepthOfFieldOptions$Filter</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#View%24QualityLevel">View$QualityLevel</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"><a href="#WrapMode">WrapMode</a></td>
|
||||
<td align="left"></td>
|
||||
</tr>
|
||||
@@ -379,7 +371,7 @@
|
||||
<li><em>returns</em> an instance of <a href="#Camera">Camera</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.createIblFromKtx1(buffer, options)</strong>
|
||||
<li><strong>engine.createIblFromKtx(buffer, options)</strong>
|
||||
<ul>
|
||||
<li>Utility that creates an [IndirectLight] from a KTX file.</li>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX file contents</li>
|
||||
@@ -403,7 +395,7 @@
|
||||
<li><em>returns</em> an instance of <a href="#Scene">Scene</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.createSkyFromKtx1(buffer, options)</strong>
|
||||
<li><strong>engine.createSkyFromKtx(buffer, options)</strong>
|
||||
<ul>
|
||||
<li>Utility function that creates a [Skybox] from a KTX file.</li>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX file contents</li>
|
||||
@@ -424,18 +416,10 @@
|
||||
<li><em>returns</em> <a href="#Texture">Texture</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.createTextureFromKtx1(buffer, options)</strong>
|
||||
<li><strong>engine.createTextureFromKtx(buffer, options)</strong>
|
||||
<ul>
|
||||
<li>Utility function that creates a [Texture] from a KTX1 file.</li>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX1 file contents</li>
|
||||
<li><em>options</em> Options dictionary.</li>
|
||||
<li><em>returns</em> <a href="#Texture">Texture</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.createTextureFromKtx2(buffer, options)</strong>
|
||||
<ul>
|
||||
<li>Utility function that creates a [Texture] from a KTX2 file.</li>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX2 file contents</li>
|
||||
<li>Utility function that creates a [Texture] from a KTX file.</li>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX file contents</li>
|
||||
<li><em>options</em> Options dictionary.</li>
|
||||
<li><em>returns</em> <a href="#Texture">Texture</a></li>
|
||||
</ul>
|
||||
@@ -569,26 +553,57 @@
|
||||
<li><em>entities</em> array of entities</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setAmbientOcclusionOptions()</strong></li>
|
||||
<li><strong>engine.setBloomOptions()</strong></li>
|
||||
<li><strong>engine.setAmbientOcclusionOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: radius, power, bias, resolution, intensity, quality.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setBloomOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: enabled, strength, resolution, anomorphism, levels, blendMode, threshold, highlight.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setClearOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: clearColor, clear, discard.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setDepthOfFieldOptions()</strong></li>
|
||||
<li><strong>engine.setFogOptions()</strong></li>
|
||||
<li><strong>engine.setGuardBandOptions()</strong></li>
|
||||
<li><strong>engine.setMultiSampleAntiAliasingOptions()</strong></li>
|
||||
<li><strong>engine.setScreenSpaceReflectionsOptions()</strong></li>
|
||||
<li><strong>engine.setDepthOfFieldOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: cocScale, maxApertureDiameter, enabled.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setFogOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: distance, maximumOpacity, height, heightFalloff, color, density, inScatteringStart,</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setMultiSampleAntiAliasingOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: enabled, sampleCount, customResolve.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setScreenSpaceReflectionsOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: thickness, bias, maxDistance, stride, enabled.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setShadowOptions(instance, overrides)</strong>
|
||||
<ul>
|
||||
<li><em>instance</em> Instance of a light component obtained from <code>getInstance</code>.</li>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
|
||||
<li><strong>engine.setVignetteOptions()</strong></li>
|
||||
<li><strong>engine.setTemporalAntiAliasingOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: filterWidth, feedback, enabled.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setVignetteOptions(overrides)</strong>
|
||||
<ul>
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: midPoint, roundness, feather, color, enabled.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='classdoc'>
|
||||
@@ -603,11 +618,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'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's ECS.</p>
|
||||
<p>Singleton used for constructing entities in Filament's ECS.</p>
|
||||
<ul>
|
||||
<li><strong>entityManager.create()</strong>
|
||||
<ul>
|
||||
@@ -712,9 +727,15 @@ wrapper rather than interacting with <code>Ktx1Bundle</code> directly.</p>
|
||||
</div>
|
||||
<div class='classdoc'>
|
||||
<h2>class <a id='KtxInfo' href='#KtxInfo'>KtxInfo</a></h2>
|
||||
<p>Property accessor for KTX1 header.</p>
|
||||
<p>Property accessor for KTX header.</p>
|
||||
<ul>
|
||||
<li><strong>ktxInfo.createAsset(buffer)</strong>
|
||||
<li><strong>ktxInfo.createAssetFromBinary(buffer)</strong>
|
||||
<ul>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a></li>
|
||||
<li><em>returns</em> an instance of <a href="#FilamentAsset">FilamentAsset</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>ktxInfo.createAssetFromJson(buffer)</strong>
|
||||
<ul>
|
||||
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a></li>
|
||||
<li><em>returns</em> an instance of <a href="#FilamentAsset">FilamentAsset</a></li>
|
||||
@@ -741,7 +762,7 @@ properties.</p>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Be sure to call the instance's <code>delete</code> method when you'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>
|
||||
@@ -779,11 +800,11 @@ properties.</p>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Be sure to call the instance's <code>delete</code> method when you'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's native window.</p>
|
||||
<p>Represents the platform's native window.</p>
|
||||
<ul>
|
||||
<li><strong>renderer.render(swapChain, view)</strong>
|
||||
<ul>
|
||||
@@ -802,7 +823,7 @@ properties.</p>
|
||||
</div>
|
||||
<div class='classdoc'>
|
||||
<h2>class <a id='SwapChain' href='#SwapChain'>SwapChain</a></h2>
|
||||
<p>Represents the platform'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'>
|
||||
@@ -846,7 +867,7 @@ properties.</p>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Be sure to call the instance's <code>delete</code> method when you'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>
|
||||
@@ -943,7 +964,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's of required assets.</li>
|
||||
<li>Array of strings containing URL's of required assets.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>onDone</strong>
|
||||
@@ -953,7 +974,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's invoked after each asset is downloaded.</li>
|
||||
<li>optional callback that's invoked after each asset is downloaded.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -1000,7 +1021,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's of required assets.</li>
|
||||
<li>Array of strings containing URL's of required assets.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>onready</strong>
|
||||
@@ -1009,12 +1030,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'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'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'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>
|
||||
@@ -1064,13 +1085,6 @@ This defines the following functions:</p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='ColorGrading$LutFormat' href='#ColorGrading$LutFormat'>ColorGrading$LutFormat</a></h2>
|
||||
<ul>
|
||||
<li>INTEGER</li>
|
||||
<li>FLOAT</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='ColorGrading$QualityLevel' href='#ColorGrading$QualityLevel'>ColorGrading$QualityLevel</a></h2>
|
||||
<ul>
|
||||
<li>LOW</li>
|
||||
@@ -1126,10 +1140,6 @@ This defines the following functions:</p>
|
||||
<li>DXT1_RGBA</li>
|
||||
<li>DXT3_RGBA</li>
|
||||
<li>DXT5_RGBA</li>
|
||||
<li>DXT1_SRGB</li>
|
||||
<li>DXT1_SRGBA</li>
|
||||
<li>DXT3_SRGBA</li>
|
||||
<li>DXT5_SRGBA</li>
|
||||
<li>RGBA_ASTC_4x4</li>
|
||||
<li>RGBA_ASTC_5x4</li>
|
||||
<li>RGBA_ASTC_5x5</li>
|
||||
@@ -1170,13 +1180,6 @@ This defines the following functions:</p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='FeatureLevel' href='#FeatureLevel'>FeatureLevel</a></h2>
|
||||
<ul>
|
||||
<li>FEATURE_LEVEL_1</li>
|
||||
<li>FEATURE_LEVEL_2</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='Frustum$Plane' href='#Frustum$Plane'>Frustum$Plane</a></h2>
|
||||
<ul>
|
||||
<li>LEFT</li>
|
||||
@@ -1195,23 +1198,6 @@ This defines the following functions:</p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='Ktx2Reader$Result' href='#Ktx2Reader$Result'>Ktx2Reader$Result</a></h2>
|
||||
<ul>
|
||||
<li>SUCCESS</li>
|
||||
<li>COMPRESSED_TRANSCODE_FAILURE</li>
|
||||
<li>UNCOMPRESSED_TRANSCODE_FAILURE</li>
|
||||
<li>FORMAT_UNSUPPORTED</li>
|
||||
<li>FORMAT_ALREADY_REQUESTED</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='Ktx2Reader$TransferFunction' href='#Ktx2Reader$TransferFunction'>Ktx2Reader$TransferFunction</a></h2>
|
||||
<ul>
|
||||
<li>LINEAR</li>
|
||||
<li>sRGB</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='LightManager$Type' href='#LightManager$Type'>LightManager$Type</a></h2>
|
||||
<ul>
|
||||
<li>SUN</li>
|
||||
@@ -1289,6 +1275,7 @@ This defines the following functions:</p>
|
||||
<li>LINE_STRIP</li>
|
||||
<li>TRIANGLES</li>
|
||||
<li>TRIANGLE_STRIP</li>
|
||||
<li>NONE</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
@@ -1308,27 +1295,6 @@ This defines the following functions:</p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='StencilFace' href='#StencilFace'>StencilFace</a></h2>
|
||||
<ul>
|
||||
<li>FRONT</li>
|
||||
<li>BACK</li>
|
||||
<li>FRONT_AND_BACK</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='StencilOperation' href='#StencilOperation'>StencilOperation</a></h2>
|
||||
<ul>
|
||||
<li>KEEP</li>
|
||||
<li>ZERO</li>
|
||||
<li>REPLACE</li>
|
||||
<li>INCR_CLAMP</li>
|
||||
<li>INCR_WRAP</li>
|
||||
<li>DECR_CLAMP</li>
|
||||
<li>DECR_WRAP</li>
|
||||
<li>INVERT</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='Texture$CubemapFace' href='#Texture$CubemapFace'>Texture$CubemapFace</a></h2>
|
||||
<ul>
|
||||
<li>POSITIVE_X</li>
|
||||
@@ -1411,10 +1377,6 @@ This defines the following functions:</p>
|
||||
<li>DXT1_RGBA</li>
|
||||
<li>DXT3_RGBA</li>
|
||||
<li>DXT5_RGBA</li>
|
||||
<li>DXT1_SRGB</li>
|
||||
<li>DXT1_SRGBA</li>
|
||||
<li>DXT3_SRGBA</li>
|
||||
<li>DXT5_SRGBA</li>
|
||||
<li>RGBA_ASTC_4x4</li>
|
||||
<li>RGBA_ASTC_5x4</li>
|
||||
<li>RGBA_ASTC_5x5</li>
|
||||
@@ -1466,14 +1428,6 @@ This defines the following functions:</p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='TransparencyMode' href='#TransparencyMode'>TransparencyMode</a></h2>
|
||||
<ul>
|
||||
<li>DEFAULT</li>
|
||||
<li>TWO_PASSES_ONE_SIDE</li>
|
||||
<li>TWO_PASSES_TWO_SIDES</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='VertexAttribute' href='#VertexAttribute'>VertexAttribute</a></h2>
|
||||
<ul>
|
||||
<li>POSITION</li>
|
||||
@@ -1540,6 +1494,43 @@ This defines the following functions:</p>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='View$AntiAliasing' href='#View$AntiAliasing'>View$AntiAliasing</a></h2>
|
||||
<ul>
|
||||
<li>NONE</li>
|
||||
<li>FXAA</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='View$BlendMode' href='#View$BlendMode'>View$BlendMode</a></h2>
|
||||
<ul>
|
||||
<li>OPAQUE</li>
|
||||
<li>TRANSLUCENT</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='View$BloomOptions$BlendMode' href='#View$BloomOptions$BlendMode'>View$BloomOptions$BlendMode</a></h2>
|
||||
<ul>
|
||||
<li>ADD</li>
|
||||
<li>INTERPOLATE</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='View$DepthOfFieldOptions$Filter' href='#View$DepthOfFieldOptions$Filter'>View$DepthOfFieldOptions$Filter</a></h2>
|
||||
<ul>
|
||||
<li>NONE</li>
|
||||
<li>MEDIAN</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='View$QualityLevel' href='#View$QualityLevel'>View$QualityLevel</a></h2>
|
||||
<ul>
|
||||
<li>LOW</li>
|
||||
<li>MEDIUM</li>
|
||||
<li>HIGH</li>
|
||||
<li>ULTRA</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class='enumdoc'>
|
||||
<h2>enum <a id='WrapMode' href='#WrapMode'>WrapMode</a></h2>
|
||||
<ul>
|
||||
<li>CLAMP_TO_EDGE</li>
|
||||
|
||||
Binary file not shown.
+20
-27
@@ -16,14 +16,17 @@ canvas { touch-action: none; width: 100%; height: 100%; }
|
||||
<script src="gl-matrix-min.js"></script>
|
||||
<script>
|
||||
|
||||
const albedo_suffix = Filament.getSupportedFormatSuffix('astc s3tc_srgb');
|
||||
const texture_suffix = Filament.getSupportedFormatSuffix('etc');
|
||||
|
||||
const env = 'default_env'
|
||||
const ibl_url = `${env}/${env}_ibl.ktx`;
|
||||
const sky_url = `${env}/${env}_skybox.ktx`;
|
||||
const albedo_url = `albedo.ktx2`;
|
||||
const ao_url = `ao.ktx2`;
|
||||
const metallic_url = `metallic.ktx2`;
|
||||
const normal_url = `normal.ktx2`;
|
||||
const roughness_url = `roughness.ktx2`;
|
||||
const albedo_url = `albedo${albedo_suffix}.ktx`;
|
||||
const ao_url = `ao${texture_suffix}.ktx`;
|
||||
const metallic_url = `metallic${texture_suffix}.ktx`;
|
||||
const normal_url = `normal${texture_suffix}.ktx`;
|
||||
const roughness_url = `roughness${texture_suffix}.ktx`;
|
||||
const filamat_url = 'textured.filamat';
|
||||
const filamesh_url = 'suzanne.filamesh';
|
||||
|
||||
@@ -38,13 +41,12 @@ Filament.init([
|
||||
ibl_url,
|
||||
sky_url
|
||||
], () => {
|
||||
window.AttributeType = Filament.VertexBuffer$AttributeType;
|
||||
window.Format = Filament.Texture$InternalFormat;
|
||||
window.Fov = Filament.Camera$Fov;
|
||||
window.IndexType = Filament.IndexBuffer$IndexType;
|
||||
window.LightType = Filament.LightManager$Type;
|
||||
window.PrimitiveType = Filament.RenderableManager$PrimitiveType;
|
||||
window.VertexAttribute = Filament.VertexAttribute;
|
||||
window.AttributeType = Filament.VertexBuffer$AttributeType;
|
||||
window.PrimitiveType = Filament.RenderableManager$PrimitiveType;
|
||||
window.IndexType = Filament.IndexBuffer$IndexType;
|
||||
window.Fov = Filament.Camera$Fov;
|
||||
window.LightType = Filament.LightManager$Type;
|
||||
window.app = new App(document.getElementsByTagName('canvas')[0]);
|
||||
});
|
||||
|
||||
@@ -66,11 +68,11 @@ class App {
|
||||
.build(engine, sunlight);
|
||||
this.scene.addEntity(sunlight);
|
||||
|
||||
const indirectLight = this.ibl = engine.createIblFromKtx1(ibl_url);
|
||||
const indirectLight = this.ibl = engine.createIblFromKtx(ibl_url);
|
||||
this.scene.setIndirectLight(indirectLight);
|
||||
indirectLight.setIntensity(80000);
|
||||
|
||||
const skybox = engine.createSkyFromKtx1(sky_url);
|
||||
const skybox = engine.createSkyFromKtx(sky_url);
|
||||
this.scene.setSkybox(skybox);
|
||||
|
||||
const material = engine.createMaterial(filamat_url);
|
||||
@@ -81,20 +83,11 @@ class App {
|
||||
Filament.MagFilter.LINEAR,
|
||||
Filament.WrapMode.CLAMP_TO_EDGE);
|
||||
|
||||
// Filament requests support for the following extensions, but none are guaranteed.
|
||||
// WEBGL_compressed_texture_s3tc, WEBGL_compressed_texture_s3tc_srgb
|
||||
// WEBGL_compressed_texture_astc, WEBGL_compressed_texture_etc
|
||||
const albedo = engine.createTextureFromKtx2(albedo_url, {
|
||||
srgb: true,
|
||||
formats: [ Format.DXT5_RGBA, Format.DXT5_SRGBA, Format.ETC2_EAC_SRGBA8 ]
|
||||
});
|
||||
|
||||
console.assert(albedo, "Unable to create albedo texture.")
|
||||
|
||||
const roughness = engine.createTextureFromKtx2(roughness_url);
|
||||
const metallic = engine.createTextureFromKtx2(metallic_url);
|
||||
const ao = engine.createTextureFromKtx2(ao_url);
|
||||
const normal = engine.createTextureFromKtx2(normal_url);
|
||||
const albedo = engine.createTextureFromKtx(albedo_url, {srgb: true});
|
||||
const roughness = engine.createTextureFromKtx(roughness_url);
|
||||
const metallic = engine.createTextureFromKtx(metallic_url);
|
||||
const normal = engine.createTextureFromKtx(normal_url);
|
||||
const ao = engine.createTextureFromKtx(ao_url);
|
||||
|
||||
matinstance.setTextureParameter('albedo', albedo, sampler)
|
||||
matinstance.setTextureParameter('roughness', roughness, sampler)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+155
-155
@@ -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'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'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,
|
||||
@@ -47,10 +47,10 @@ fragment {
|
||||
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc -a opengl -p mobile -o plastic.filamat plastic.mat
|
||||
</pre></div>
|
||||
|
||||
<p>You should now have a material archive in your working directory, which we'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'll use Filament'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
|
||||
@@ -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">'pillars_2k'</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">'plastic.filamat'</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> environ <span style="color: #666666">=</span> <span style="color: #BA2121">'pillars_2k'</span>;
|
||||
<span style="color: #008000; font-weight: bold">const</span> ibl_url <span style="color: #666666">=</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> sky_url <span style="color: #666666">=</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> filamat_url <span style="color: #666666">=</span> <span style="color: #BA2121">'plastic.filamat'</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>=><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([ filamat_url, ibl_url, sky_url ], () => {
|
||||
<span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span>
|
||||
<span style="color: #008000">window</span>.VertexAttribute <span style="color: #666666">=</span> Filament.VertexAttribute;
|
||||
<span style="color: #008000">window</span>.AttributeType <span style="color: #666666">=</span> Filament.VertexBuffer$AttributeType;
|
||||
<span style="color: #008000">window</span>.PrimitiveType <span style="color: #666666">=</span> Filament.RenderableManager$PrimitiveType;
|
||||
<span style="color: #008000">window</span>.IndexType <span style="color: #666666">=</span> Filament.IndexBuffer$IndexType;
|
||||
<span style="color: #008000">window</span>.Fov <span style="color: #666666">=</span> Filament.Camera$Fov;
|
||||
<span style="color: #008000">window</span>.LightType <span style="color: #666666">=</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">'canvas'</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: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span>
|
||||
<span style="color: #008000; font-weight: bold">const</span> canvas <span style="color: #666666">=</span> <span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">'canvas'</span>)[<span style="color: #666666">0</span>];
|
||||
<span style="color: #008000">window</span>.app <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> App(canvas);
|
||||
} );
|
||||
|
||||
<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> App {
|
||||
<span style="color: #008000; font-weight: bold">constructor</span>(canvas) {
|
||||
<span style="color: #008000; font-weight: bold">this</span>.canvas <span style="color: #666666">=</span> canvas;
|
||||
<span style="color: #008000; font-weight: bold">const</span> engine <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine <span style="color: #666666">=</span> Filament.Engine.create(canvas);
|
||||
<span style="color: #008000; font-weight: bold">const</span> scene <span style="color: #666666">=</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: #3D7B7B; font-style: italic">// TODO: create material</span>
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span>
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span>
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</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">'resize'</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: #008000; font-weight: bold">this</span>.swapChain <span style="color: #666666">=</span> engine.createSwapChain();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.renderer <span style="color: #666666">=</span> engine.createRenderer();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera <span style="color: #666666">=</span> engine.createCamera(Filament.EntityManager.get().create());
|
||||
<span style="color: #008000; font-weight: bold">this</span>.view <span style="color: #666666">=</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(scene);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.resize();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.render <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.resize <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
|
||||
<span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">'resize'</span>, <span style="color: #008000; font-weight: bold">this</span>.resize);
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
|
||||
<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>
|
||||
render() {
|
||||
<span style="color: #008000; font-weight: bold">const</span> eye <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">4</span>], center <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>], up <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">1</span>, <span style="color: #666666">0</span>];
|
||||
<span style="color: #008000; font-weight: bold">const</span> radians <span style="color: #666666">=</span> <span style="color: #008000">Date</span>.now() <span style="color: #666666">/</span> <span style="color: #666666">10000</span>;
|
||||
vec3.rotateY(eye, eye, center, radians);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye, center, up);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain, <span style="color: #008000; font-weight: bold">this</span>.view);
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
|
||||
<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>
|
||||
resize() {
|
||||
<span style="color: #008000; font-weight: bold">const</span> dpr <span style="color: #666666">=</span> <span style="color: #008000">window</span>.devicePixelRatio;
|
||||
<span style="color: #008000; font-weight: bold">const</span> width <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.width <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerWidth <span style="color: #666666">*</span> dpr;
|
||||
<span style="color: #008000; font-weight: bold">const</span> height <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.height <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerHeight <span style="color: #666666">*</span> dpr;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, width, height]);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>, width <span style="color: #666666">/</span> height, <span style="color: #666666">1.0</span>, <span style="color: #666666">10.0</span>, Fov.VERTICAL);
|
||||
}
|
||||
}
|
||||
</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'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> material <span style="color: #666666">=</span> engine.createMaterial(filamat_url);
|
||||
<span style="color: #008000; font-weight: bold">const</span> matinstance <span style="color: #666666">=</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">'baseColor'</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">'roughness'</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.5</span>);<span style="color: #bbbbbb"></span>
|
||||
matinstance.setFloatParameter(<span style="color: #BA2121">'clearCoat'</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>);<span style="color: #bbbbbb"></span>
|
||||
matinstance.setFloatParameter(<span style="color: #BA2121">'clearCoatRoughness'</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> red <span style="color: #666666">=</span> [<span style="color: #666666">0.8</span>, <span style="color: #666666">0.0</span>, <span style="color: #666666">0.0</span>];
|
||||
matinstance.setColor3Parameter(<span style="color: #BA2121">'baseColor'</span>, Filament.RgbType.sRGB, red);
|
||||
matinstance.setFloatParameter(<span style="color: #BA2121">'roughness'</span>, <span style="color: #666666">0.5</span>);
|
||||
matinstance.setFloatParameter(<span style="color: #BA2121">'clearCoat'</span>, <span style="color: #666666">1.0</span>);
|
||||
matinstance.setFloatParameter(<span style="color: #BA2121">'clearCoatRoughness'</span>, <span style="color: #666666">0.3</span>);
|
||||
</pre></div>
|
||||
|
||||
<p>The next step is to create a renderable for the sphere. To help with this, we'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'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> renderable <span style="color: #666666">=</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> icosphere <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.IcoSphere(<span style="color: #666666">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> vb <span style="color: #666666">=</span> Filament.VertexBuffer.Builder()
|
||||
.vertexCount(icosphere.vertices.length <span style="color: #666666">/</span> <span style="color: #666666">3</span>)
|
||||
.bufferCount(<span style="color: #666666">2</span>)
|
||||
.attribute(VertexAttribute.POSITION, <span style="color: #666666">0</span>, AttributeType.FLOAT3, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>)
|
||||
.attribute(VertexAttribute.TANGENTS, <span style="color: #666666">1</span>, AttributeType.SHORT4, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>)
|
||||
.normalized(VertexAttribute.TANGENTS)
|
||||
.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> ib <span style="color: #666666">=</span> Filament.IndexBuffer.Builder()
|
||||
.indexCount(icosphere.triangles.length)
|
||||
.bufferType(IndexType.USHORT)
|
||||
.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: #666666">0</span>, icosphere.vertices);
|
||||
vb.setBufferAt(engine, <span style="color: #666666">1</span>, icosphere.tangents);
|
||||
ib.setBuffer(engine, 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: #666666">1</span>)
|
||||
.boundingBox({ center<span style="color: #666666">:</span> [<span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>], halfExtent<span style="color: #666666">:</span> [<span style="color: #666666">1</span>, <span style="color: #666666">1</span>, <span style="color: #666666">1</span>] })
|
||||
.material(<span style="color: #666666">0</span>, matinstance)
|
||||
.geometry(<span style="color: #666666">0</span>, PrimitiveType.TRIANGLES, vb, ib)
|
||||
.build(engine, renderable);
|
||||
</pre></div>
|
||||
|
||||
<p>At this point, the app is rendering a sphere, but it is black so it doesn'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> sunlight <span style="color: #666666">=</span> Filament.EntityManager.get().create();
|
||||
scene.addEntity(sunlight);
|
||||
Filament.LightManager.Builder(LightType.SUN)
|
||||
.color([<span style="color: #666666">0.98</span>, <span style="color: #666666">0.92</span>, <span style="color: #666666">0.89</span>])
|
||||
.intensity(<span style="color: #666666">110000.0</span>)
|
||||
.direction([<span style="color: #666666">0.6</span>, <span style="color: #666666">-1.0</span>, <span style="color: #666666">-0.8</span>])
|
||||
.sunAngularRadius(<span style="color: #666666">1.9</span>)
|
||||
.sunHaloSize(<span style="color: #666666">10.0</span>)
|
||||
.sunHaloFalloff(<span style="color: #666666">80.0</span>)
|
||||
.build(engine, 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> backlight <span style="color: #666666">=</span> Filament.EntityManager.get().create();
|
||||
scene.addEntity(backlight);
|
||||
Filament.LightManager.Builder(LightType.DIRECTIONAL)
|
||||
.direction([<span style="color: #666666">-1</span>, <span style="color: #666666">0</span>, <span style="color: #666666">1</span>])
|
||||
.intensity(<span style="color: #666666">50000.0</span>)
|
||||
.build(engine, 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'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: #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> format <span style="color: #666666">=</span> Filament.PixelDataFormat.RGB;
|
||||
<span style="color: #008000; font-weight: bold">const</span> datatype <span style="color: #666666">=</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> ibl_package <span style="color: #666666">=</span> Filament.Buffer(Filament.assets[ibl_url]);
|
||||
<span style="color: #008000; font-weight: bold">const</span> iblktx <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</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> ibltex <span style="color: #666666">=</span> Filament.Texture.Builder()
|
||||
.width(iblktx.info().pixelWidth)
|
||||
.height(iblktx.info().pixelHeight)
|
||||
.levels(iblktx.getNumMipLevels())
|
||||
.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
|
||||
.format(Filament.Texture$InternalFormat.RGBA8)
|
||||
.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"><</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: #008000; font-weight: bold">let</span> level <span style="color: #666666">=</span> <span style="color: #666666">0</span>; level <span style="color: #666666"><</span> iblktx.getNumMipLevels(); <span style="color: #666666">++</span>level) {
|
||||
<span style="color: #008000; font-weight: bold">const</span> uint8array <span style="color: #666666">=</span> iblktx.getCubeBlob(level).getBytes();
|
||||
<span style="color: #008000; font-weight: bold">const</span> pixelbuffer <span style="color: #666666">=</span> Filament.PixelBuffer(uint8array, format, datatype);
|
||||
ibltex.setImageCube(engine, level, 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">'sh'</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> shstring <span style="color: #666666">=</span> iblktx.getMetadata(<span style="color: #BA2121">'sh'</span>);
|
||||
<span style="color: #008000; font-weight: bold">const</span> shfloats <span style="color: #666666">=</span> shstring.split(<span style="color: #A45A77">/\s/</span>, <span style="color: #666666">9</span> <span style="color: #666666">*</span> <span style="color: #666666">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> indirectLight <span style="color: #666666">=</span> Filament.IndirectLight.Builder()
|
||||
.reflections(ibltex)
|
||||
.irradianceSh(<span style="color: #666666">3</span>, shfloats)
|
||||
.intensity(<span style="color: #666666">50000.0</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> indirectLight <span style="color: #666666">=</span> engine.createIblFromKtx(ibl_url);
|
||||
indirectLight.setIntensity(<span style="color: #666666">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'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> sky_package <span style="color: #666666">=</span> Filament.Buffer(Filament.assets[sky_url]);
|
||||
<span style="color: #008000; font-weight: bold">const</span> skyktx <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.Ktx1Bundle(sky_package);
|
||||
<span style="color: #008000; font-weight: bold">const</span> skytex <span style="color: #666666">=</span> Filament.Texture.Builder()
|
||||
.width(skyktx.info().pixelWidth)
|
||||
.height(skyktx.info().pixelHeight)
|
||||
.levels(<span style="color: #666666">1</span>)
|
||||
.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
|
||||
.format(Filament.Texture$InternalFormat.RGBA8)
|
||||
.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> uint8array <span style="color: #666666">=</span> skyktx.getCubeBlob(<span style="color: #666666">0</span>).getBytes();
|
||||
<span style="color: #008000; font-weight: bold">const</span> pixelbuffer <span style="color: #666666">=</span> Filament.PixelBuffer(uint8array, format, datatype);
|
||||
skytex.setImageCube(engine, <span style="color: #666666">0</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> skybox <span style="color: #666666">=</span> engine.createSkyFromKtx(sky_url);
|
||||
scene.setSkybox(skybox);
|
||||
</pre></div>
|
||||
|
||||
<p>That'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'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>
|
||||
|
||||
@@ -70,10 +70,10 @@ class App {
|
||||
.direction([-1, 0, 1])
|
||||
.intensity(50000.0)
|
||||
.build(engine, backlight);
|
||||
const indirectLight = engine.createIblFromKtx1(ibl_url);
|
||||
const indirectLight = engine.createIblFromKtx(ibl_url);
|
||||
indirectLight.setIntensity(50000);
|
||||
scene.setIndirectLight(indirectLight);
|
||||
const skybox = engine.createSkyFromKtx1(sky_url);
|
||||
const skybox = engine.createSkyFromKtx(sky_url);
|
||||
scene.setSkybox(skybox);
|
||||
this.swapChain = engine.createSwapChain();
|
||||
this.renderer = engine.createRenderer();
|
||||
|
||||
+102
-102
@@ -8,18 +8,18 @@
|
||||
<div class="demo_frame"><iframe src="demo_suzanne.html"></iframe><a href="demo_suzanne.html">🔗</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'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'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'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
|
||||
</pre></div>
|
||||
|
||||
<h2>Create mipmapped textures</h2>
|
||||
<p>Next, let's create mipmapped KTX files using filament's <code>mipgen</code> tool. We'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 and two compressed variants.</span>
|
||||
@@ -45,10 +45,10 @@ mipgen --grayscale ao.png ao.ktx
|
||||
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 ao.png ao_etc.ktx
|
||||
</pre></div>
|
||||
|
||||
<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>
|
||||
<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'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> -
|
||||
@@ -60,7 +60,7 @@ cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="c
|
||||
|
||||
<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'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 {
|
||||
@@ -101,65 +101,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>=><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">'canvas'</span>)[<span style="color: #666666">0</span>]);<span style="color: #bbbbbb"></span>
|
||||
});<span style="color: #bbbbbb"></span>
|
||||
Filament.init([ filamat_url, filamesh_url, sky_small_url, ibl_url ], () => {
|
||||
<span style="color: #008000">window</span>.app <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">'canvas'</span>)[<span style="color: #666666">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> App {
|
||||
<span style="color: #008000; font-weight: bold">constructor</span>(canvas) {
|
||||
<span style="color: #008000; font-weight: bold">this</span>.canvas <span style="color: #666666">=</span> canvas;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.engine <span style="color: #666666">=</span> Filament.Engine.create(canvas);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.scene <span style="color: #666666">=</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: #008000; font-weight: bold">const</span> material <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.matinstance <span style="color: #666666">=</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: #008000; font-weight: bold">const</span> filamesh <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url, <span style="color: #008000; font-weight: bold">this</span>.matinstance);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.suzanne <span style="color: #666666">=</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: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span>
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</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">'resize'</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
|
||||
<span style="color: #008000; font-weight: bold">this</span>.swapChain <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.renderer <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());
|
||||
<span style="color: #008000; font-weight: bold">this</span>.view <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</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: #008000; font-weight: bold">this</span>.render <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.resize <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
|
||||
<span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">'resize'</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: #008000; font-weight: bold">const</span> eye <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">4</span>], center <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>], up <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">1</span>, <span style="color: #666666">0</span>];
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye, center, 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: #008000; font-weight: bold">this</span>.resize();
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
|
||||
<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>
|
||||
render() {
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</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: #008000; font-weight: bold">this</span>.view);
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
|
||||
<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>
|
||||
resize() {
|
||||
<span style="color: #008000; font-weight: bold">const</span> dpr <span style="color: #666666">=</span> <span style="color: #008000">window</span>.devicePixelRatio;
|
||||
<span style="color: #008000; font-weight: bold">const</span> width <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.width <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerWidth <span style="color: #666666">*</span> dpr;
|
||||
<span style="color: #008000; font-weight: bold">const</span> height <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.height <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerHeight <span style="color: #666666">*</span> dpr;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, width, 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"><</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: #008000; font-weight: bold">const</span> aspect <span style="color: #666666">=</span> width <span style="color: #666666">/</span> height;
|
||||
<span style="color: #008000; font-weight: bold">const</span> Fov <span style="color: #666666">=</span> Filament.Camera$Fov, fov <span style="color: #666666">=</span> aspect <span style="color: #666666"><</span> <span style="color: #666666">1</span> <span style="color: #666666">?</span> Fov.HORIZONTAL <span style="color: #666666">:</span> Fov.VERTICAL;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>, aspect, <span style="color: #666666">1.0</span>, <span style="color: #666666">10.0</span>, fov);
|
||||
}
|
||||
}
|
||||
</pre></div>
|
||||
|
||||
<p>Our app will only require a subset of assets to be present for <code>App</code> construction. We'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
|
||||
@@ -172,65 +172,65 @@ 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">'astc s3tc_srgb'</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">'etc'</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> albedo_suffix <span style="color: #666666">=</span> Filament.getSupportedFormatSuffix(<span style="color: #BA2121">'astc s3tc_srgb'</span>);
|
||||
<span style="color: #008000; font-weight: bold">const</span> texture_suffix <span style="color: #666666">=</span> Filament.getSupportedFormatSuffix(<span style="color: #BA2121">'etc'</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">'venetian_crossroads_2k'</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">'textured.filamat'</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">'suzanne.filamesh'</span>;<span style="color: #bbbbbb"></span>
|
||||
<span style="color: #008000; font-weight: bold">const</span> environ <span style="color: #666666">=</span> <span style="color: #BA2121">'venetian_crossroads_2k'</span>
|
||||
<span style="color: #008000; font-weight: bold">const</span> ibl_url <span style="color: #666666">=</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> sky_small_url <span style="color: #666666">=</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> sky_large_url <span style="color: #666666">=</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> albedo_url <span style="color: #666666">=</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> ao_url <span style="color: #666666">=</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> metallic_url <span style="color: #666666">=</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> normal_url <span style="color: #666666">=</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> roughness_url <span style="color: #666666">=</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> filamat_url <span style="color: #666666">=</span> <span style="color: #BA2121">'textured.filamat'</span>;
|
||||
<span style="color: #008000; font-weight: bold">const</span> filamesh_url <span style="color: #666666">=</span> <span style="color: #BA2121">'suzanne.filamesh'</span>;
|
||||
</pre></div>
|
||||
|
||||
<h2>Create skybox and IBL</h2>
|
||||
<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: #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: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx(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: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx(ibl_url);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666666">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'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'll make several <code>setTextureParameter</code> calls on the material instance, then we'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>=><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.createTextureFromKtx1(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.createTextureFromKtx1(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.createTextureFromKtx1(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.createTextureFromKtx1(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.createTextureFromKtx1(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, albedo_url, roughness_url, metallic_url, normal_url, ao_url], () => {
|
||||
<span style="color: #008000; font-weight: bold">const</span> albedo <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(albedo_url, {srgb<span style="color: #666666">:</span> <span style="color: #008000; font-weight: bold">true</span>});
|
||||
<span style="color: #008000; font-weight: bold">const</span> roughness <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(roughness_url);
|
||||
<span style="color: #008000; font-weight: bold">const</span> metallic <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(metallic_url);
|
||||
<span style="color: #008000; font-weight: bold">const</span> normal <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(normal_url);
|
||||
<span style="color: #008000; font-weight: bold">const</span> ao <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(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: #008000; font-weight: bold">const</span> sampler <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.TextureSampler(
|
||||
Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
|
||||
Filament.MagFilter.LINEAR,
|
||||
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">'albedo'</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">'roughness'</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">'metallic'</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">'normal'</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">'ao'</span>,<span style="color: #bbbbbb"> </span>ao,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
|
||||
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">'albedo'</span>, albedo, sampler);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">'roughness'</span>, roughness, sampler);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">'metallic'</span>, metallic, sampler);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">'normal'</span>, normal, sampler);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">'ao'</span>, ao, 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: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</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: #008000; font-weight: bold">this</span>.skybox <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx(sky_large_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: #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: #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>
|
||||
@@ -241,16 +241,16 @@ listens for drag events and computes a rotation matrix.</p>
|
||||
|
||||
<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: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Trackball(canvas, {startSpin<span style="color: #666666">:</span> <span style="color: #666666">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> tcm <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
|
||||
<span style="color: #008000; font-weight: bold">const</span> inst <span style="color: #666666">=</span> tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
|
||||
tcm.setTransform(inst, <span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());
|
||||
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();
|
||||
</pre></div>
|
||||
|
||||
<p>That'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>
|
||||
|
||||
@@ -23,9 +23,9 @@ class App {
|
||||
this.matinstance = material.createInstance();
|
||||
const filamesh = this.engine.loadFilamesh(filamesh_url, this.matinstance);
|
||||
this.suzanne = filamesh.renderable;
|
||||
this.skybox = this.engine.createSkyFromKtx1(sky_small_url);
|
||||
this.skybox = this.engine.createSkyFromKtx(sky_small_url);
|
||||
this.scene.setSkybox(this.skybox);
|
||||
this.indirectLight = this.engine.createIblFromKtx1(ibl_url);
|
||||
this.indirectLight = this.engine.createIblFromKtx(ibl_url);
|
||||
this.indirectLight.setIntensity(100000);
|
||||
this.scene.setIndirectLight(this.indirectLight);
|
||||
this.trackball = new Trackball(canvas, {
|
||||
@@ -33,13 +33,13 @@ class App {
|
||||
});
|
||||
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url],
|
||||
() => {
|
||||
const albedo = this.engine.createTextureFromKtx1(albedo_url, {
|
||||
const albedo = this.engine.createTextureFromKtx(albedo_url, {
|
||||
srgb: true
|
||||
});
|
||||
const roughness = this.engine.createTextureFromKtx1(roughness_url);
|
||||
const metallic = this.engine.createTextureFromKtx1(metallic_url);
|
||||
const normal = this.engine.createTextureFromKtx1(normal_url);
|
||||
const ao = this.engine.createTextureFromKtx1(ao_url);
|
||||
const roughness = this.engine.createTextureFromKtx(roughness_url);
|
||||
const metallic = this.engine.createTextureFromKtx(metallic_url);
|
||||
const normal = this.engine.createTextureFromKtx(normal_url);
|
||||
const ao = this.engine.createTextureFromKtx(ao_url);
|
||||
const sampler = new Filament.TextureSampler(Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
|
||||
Filament.MagFilter.LINEAR, Filament.WrapMode.CLAMP_TO_EDGE);
|
||||
this.matinstance.setTextureParameter('albedo', albedo, sampler);
|
||||
@@ -49,7 +49,7 @@ class App {
|
||||
this.matinstance.setTextureParameter('ao', ao, sampler);
|
||||
// Replace low-res skybox with high-res skybox.
|
||||
this.engine.destroySkybox(this.skybox);
|
||||
this.skybox = this.engine.createSkyFromKtx1(sky_large_url);
|
||||
this.skybox = this.engine.createSkyFromKtx(sky_large_url);
|
||||
this.scene.setSkybox(this.skybox);
|
||||
this.scene.addEntity(this.suzanne);
|
||||
});
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<div class="demo_frame"><iframe src="demo_triangle.html"></iframe><a href="demo_triangle.html">🔗</a></div>
|
||||
<h2>Literate programming</h2>
|
||||
<p>The markdown source for this tutorial is not only used to generate this
|
||||
web page, it'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'll often see
|
||||
(generating JS). In the code samples, you'll often see
|
||||
<code>// TODO: <some task></code>. These are special markers that get replaced by
|
||||
subsequent code blocks.</p>
|
||||
<h2>Start your project</h2>
|
||||
@@ -48,36 +48,36 @@ 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">'resize'</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> App {
|
||||
<span style="color: #008000; font-weight: bold">constructor</span>() {
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span>
|
||||
<span style="color: #008000; font-weight: bold">this</span>.render <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.resize <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
|
||||
<span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">'resize'</span>, <span style="color: #008000; font-weight: bold">this</span>.resize);
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
render() {
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
resize() {
|
||||
<span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span>
|
||||
}
|
||||
}
|
||||
|
||||
Filament.init([<span style="color: #BA2121">'triangle.filamat'</span>],<span style="color: #bbbbbb"> </span>()<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: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
|
||||
Filament.init([<span style="color: #BA2121">'triangle.filamat'</span>], () => { <span style="color: #008000">window</span>.app <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> App() } );
|
||||
</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'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'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>
|
||||
@@ -88,23 +88,23 @@ npx http-server -p <span style="color: #666666">8000</span> <span style="colo
|
||||
|
||||
<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'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'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">'canvas'</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: #666666">=</span> <span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">'canvas'</span>)[<span style="color: #666666">0</span>];
|
||||
<span style="color: #008000; font-weight: bold">const</span> engine <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine <span style="color: #666666">=</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'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: #666666">=</span> engine.createScene();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.triangle <span style="color: #666666">=</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'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> TRIANGLE_POSITIONS <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> <span style="color: #008000">Float32Array</span>([
|
||||
<span style="color: #666666">1</span>, <span style="color: #666666">0</span>,
|
||||
<span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">2</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>), <span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">2</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>),
|
||||
<span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">4</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>), <span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">4</span> <span style="color: #666666">/</span> <span style="color: #666666">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> TRIANGLE_COLORS <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> <span style="color: #008000">Uint32Array</span>([<span style="color: #666666">0xffff0000</span>, <span style="color: #666666">0xff00ff00</span>, <span style="color: #666666">0xff0000ff</span>]);
|
||||
</pre></div>
|
||||
|
||||
<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: #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> VertexAttribute <span style="color: #666666">=</span> Filament.VertexAttribute;
|
||||
<span style="color: #008000; font-weight: bold">const</span> AttributeType <span style="color: #666666">=</span> Filament.VertexBuffer$AttributeType;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.vb <span style="color: #666666">=</span> Filament.VertexBuffer.Builder()
|
||||
.vertexCount(<span style="color: #666666">3</span>)
|
||||
.bufferCount(<span style="color: #666666">2</span>)
|
||||
.attribute(VertexAttribute.POSITION, <span style="color: #666666">0</span>, AttributeType.FLOAT2, <span style="color: #666666">0</span>, <span style="color: #666666">8</span>)
|
||||
.attribute(VertexAttribute.COLOR, <span style="color: #666666">1</span>, AttributeType.UBYTE4, <span style="color: #666666">0</span>, <span style="color: #666666">4</span>)
|
||||
.normalized(VertexAttribute.COLOR)
|
||||
.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: #666666">0</span>, TRIANGLE_POSITIONS);
|
||||
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine, <span style="color: #666666">1</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'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: #666666">=</span> Filament.IndexBuffer.Builder()
|
||||
.indexCount(<span style="color: #666666">3</span>)
|
||||
.bufferType(Filament.IndexBuffer$IndexType.USHORT)
|
||||
.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: #AA22FF; font-weight: bold">new</span> <span style="color: #008000">Uint16Array</span>([<span style="color: #666666">0</span>, <span style="color: #666666">1</span>, <span style="color: #666666">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'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'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">'triangle.filamat'</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> mat <span style="color: #666666">=</span> engine.createMaterial(<span style="color: #BA2121">'triangle.filamat'</span>);
|
||||
<span style="color: #008000; font-weight: bold">const</span> matinst <span style="color: #666666">=</span> mat.getDefaultInstance();
|
||||
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)
|
||||
.boundingBox({ center<span style="color: #666666">:</span> [<span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>], halfExtent<span style="color: #666666">:</span> [<span style="color: #666666">1</span>, <span style="color: #666666">1</span>, <span style="color: #666666">1</span>] })
|
||||
.material(<span style="color: #666666">0</span>, matinst)
|
||||
.geometry(<span style="color: #666666">0</span>, Filament.RenderableManager$PrimitiveType.TRIANGLES, <span style="color: #008000; font-weight: bold">this</span>.vb, <span style="color: #008000; font-weight: bold">this</span>.ib)
|
||||
.build(engine, <span style="color: #008000; font-weight: bold">this</span>.triangle);
|
||||
</pre></div>
|
||||
|
||||
<p>Next let'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: #666666">=</span> engine.createSwapChain();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.renderer <span style="color: #666666">=</span> engine.createRenderer();
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera <span style="color: #666666">=</span> engine.createCamera(Filament.EntityManager.get().create());
|
||||
<span style="color: #008000; font-weight: bold">this</span>.view <span style="color: #666666">=</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: #666666">:</span> [<span style="color: #666666">0.0</span>, <span style="color: #666666">0.1</span>, <span style="color: #666666">0.2</span>, <span style="color: #666666">1.0</span>], clear<span style="color: #666666">:</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'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: #3D7B7B; font-style: italic">// TODO: render scene</span>
|
||||
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
|
||||
}
|
||||
</pre></div>
|
||||
|
||||
<p>Let'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> radians <span style="color: #666666">=</span> <span style="color: #008000">Date</span>.now() <span style="color: #666666">/</span> <span style="color: #666666">1000</span>;
|
||||
<span style="color: #008000; font-weight: bold">const</span> transform <span style="color: #666666">=</span> mat4.fromRotation(mat4.create(), radians, [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">1</span>]);
|
||||
<span style="color: #008000; font-weight: bold">const</span> tcm <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
|
||||
<span style="color: #008000; font-weight: bold">const</span> inst <span style="color: #666666">=</span> tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);
|
||||
tcm.setTransform(inst, transform);
|
||||
inst.<span style="color: #AA22FF; 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: #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> dpr <span style="color: #666666">=</span> <span style="color: #008000">window</span>.devicePixelRatio;
|
||||
<span style="color: #008000; font-weight: bold">const</span> width <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.width <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerWidth <span style="color: #666666">*</span> dpr;
|
||||
<span style="color: #008000; font-weight: bold">const</span> height <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.height <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerHeight <span style="color: #666666">*</span> dpr;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, width, 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> aspect <span style="color: #666666">=</span> width <span style="color: #666666">/</span> height;
|
||||
<span style="color: #008000; font-weight: bold">const</span> Projection <span style="color: #666666">=</span> Filament.Camera$Projection;
|
||||
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO, <span style="color: #666666">-</span>aspect, aspect, <span style="color: #666666">-1</span>, <span style="color: #666666">1</span>, <span style="color: #666666">0</span>, <span style="color: #666666">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'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>
|
||||
|
||||
@@ -17,7 +17,6 @@ set(PUBLIC_HDRS
|
||||
include/filament/Camera.h
|
||||
include/filament/Color.h
|
||||
include/filament/ColorGrading.h
|
||||
include/filament/ColorSpace.h
|
||||
include/filament/DebugRegistry.h
|
||||
include/filament/Engine.h
|
||||
include/filament/Exposure.h
|
||||
@@ -54,7 +53,7 @@ set(SRCS
|
||||
src/BufferObject.cpp
|
||||
src/Camera.cpp
|
||||
src/Color.cpp
|
||||
src/ColorSpaceUtils.cpp
|
||||
src/ColorSpace.cpp
|
||||
src/Culler.cpp
|
||||
src/DFG.cpp
|
||||
src/DebugRegistry.cpp
|
||||
@@ -136,7 +135,7 @@ set(SRCS
|
||||
set(PRIVATE_HDRS
|
||||
src/Allocators.h
|
||||
src/BufferPoolAllocator.h
|
||||
src/ColorSpaceUtils.h
|
||||
src/ColorSpace.h
|
||||
src/Culler.h
|
||||
src/DFG.h
|
||||
src/FilamentAPI-impl.h
|
||||
|
||||
@@ -405,7 +405,6 @@ if (APPLE)
|
||||
backend
|
||||
getopt
|
||||
gtest
|
||||
filamat
|
||||
SPIRV
|
||||
spirv-cross-glsl)
|
||||
|
||||
|
||||
@@ -398,18 +398,6 @@ enum class CompressedPixelDataType : uint16_t {
|
||||
SRGB8_ALPHA8_ASTC_10x10,
|
||||
SRGB8_ALPHA8_ASTC_12x10,
|
||||
SRGB8_ALPHA8_ASTC_12x12,
|
||||
|
||||
// RGTC formats available with a GLES extension
|
||||
RED_RGTC1, // BC4 unsigned
|
||||
SIGNED_RED_RGTC1, // BC4 signed
|
||||
RED_GREEN_RGTC2, // BC5 unsigned
|
||||
SIGNED_RED_GREEN_RGTC2, // BC5 signed
|
||||
|
||||
// BPTC formats available with a GLES extension
|
||||
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
|
||||
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
|
||||
RGBA_BPTC_UNORM, // BC7
|
||||
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
|
||||
};
|
||||
|
||||
/** Supported texel formats
|
||||
@@ -564,18 +552,6 @@ enum class TextureFormat : uint16_t {
|
||||
SRGB8_ALPHA8_ASTC_10x10,
|
||||
SRGB8_ALPHA8_ASTC_12x10,
|
||||
SRGB8_ALPHA8_ASTC_12x12,
|
||||
|
||||
// RGTC formats available with a GLES extension
|
||||
RED_RGTC1, // BC4 unsigned
|
||||
SIGNED_RED_RGTC1, // BC4 signed
|
||||
RED_GREEN_RGTC2, // BC5 unsigned
|
||||
SIGNED_RED_GREEN_RGTC2, // BC5 signed
|
||||
|
||||
// BPTC formats available with a GLES extension
|
||||
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
|
||||
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
|
||||
RGBA_BPTC_UNORM, // BC7
|
||||
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
|
||||
};
|
||||
|
||||
//! Bitmask describing the intended Texture Usage
|
||||
@@ -656,7 +632,7 @@ static constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
//! returns whether this format is a compressed format
|
||||
//! returns whether this format a compressed format
|
||||
static constexpr bool isCompressedFormat(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::EAC_R11;
|
||||
}
|
||||
@@ -666,7 +642,7 @@ static constexpr bool isETC2Compression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8;
|
||||
}
|
||||
|
||||
//! returns whether this format is an S3TC compressed format
|
||||
//! returns whether this format is an ETC3 compressed format
|
||||
static constexpr bool isS3TCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA;
|
||||
}
|
||||
@@ -675,16 +651,6 @@ static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA;
|
||||
}
|
||||
|
||||
//! returns whether this format is an RGTC compressed format
|
||||
static constexpr bool isRGTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2;
|
||||
}
|
||||
|
||||
//! returns whether this format is an BPTC compressed format
|
||||
static constexpr bool isBPTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM;
|
||||
}
|
||||
|
||||
static constexpr bool isASTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ bool requestsGoogleLineDirectivesExtension(std::string_view source) noexcept {
|
||||
}
|
||||
|
||||
void removeGoogleLineDirectives(char* shader, size_t length) noexcept {
|
||||
std::string_view const s{ shader, length };
|
||||
std::string_view s(shader, length);
|
||||
|
||||
size_t pos = 0;
|
||||
while (true) {
|
||||
@@ -173,20 +173,6 @@ size_t getFormatSize(TextureFormat format) noexcept {
|
||||
case TextureFormat::DXT5_SRGBA:
|
||||
return 16;
|
||||
|
||||
case TextureFormat::RED_RGTC1:
|
||||
case TextureFormat::SIGNED_RED_RGTC1:
|
||||
return 8;
|
||||
|
||||
case TextureFormat::RED_GREEN_RGTC2:
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
return 16;
|
||||
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
case TextureFormat::RGBA_BPTC_UNORM:
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
return 16;
|
||||
|
||||
// The block size for ASTC compression is always 16 bytes.
|
||||
case TextureFormat::RGBA_ASTC_4x4:
|
||||
case TextureFormat::RGBA_ASTC_5x4:
|
||||
@@ -290,22 +276,16 @@ size_t getFormatComponentCount(TextureFormat format) noexcept {
|
||||
// Compressed formats ---------------------------------------------------------------------
|
||||
case TextureFormat::EAC_R11:
|
||||
case TextureFormat::EAC_R11_SIGNED:
|
||||
case TextureFormat::RED_RGTC1:
|
||||
case TextureFormat::SIGNED_RED_RGTC1:
|
||||
return 1;
|
||||
|
||||
case TextureFormat::EAC_RG11:
|
||||
case TextureFormat::EAC_RG11_SIGNED:
|
||||
case TextureFormat::RED_GREEN_RGTC2:
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
return 2;
|
||||
|
||||
case TextureFormat::ETC2_RGB8:
|
||||
case TextureFormat::ETC2_SRGB8:
|
||||
case TextureFormat::DXT1_RGB:
|
||||
case TextureFormat::DXT1_SRGB:
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
return 3;
|
||||
|
||||
case TextureFormat::ETC2_EAC_RGBA8:
|
||||
@@ -318,8 +298,6 @@ size_t getFormatComponentCount(TextureFormat format) noexcept {
|
||||
case TextureFormat::DXT3_SRGBA:
|
||||
case TextureFormat::DXT5_RGBA:
|
||||
case TextureFormat::DXT5_SRGBA:
|
||||
case TextureFormat::RGBA_BPTC_UNORM:
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
case TextureFormat::RGBA_ASTC_4x4:
|
||||
case TextureFormat::RGBA_ASTC_5x4:
|
||||
case TextureFormat::RGBA_ASTC_5x5:
|
||||
@@ -378,18 +356,6 @@ size_t getBlockWidth(TextureFormat format) noexcept {
|
||||
case TextureFormat::DXT5_SRGBA:
|
||||
return 4;
|
||||
|
||||
case TextureFormat::RED_RGTC1:
|
||||
case TextureFormat::SIGNED_RED_RGTC1:
|
||||
case TextureFormat::RED_GREEN_RGTC2:
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
return 4;
|
||||
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
case TextureFormat::RGBA_BPTC_UNORM:
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
return 4;
|
||||
|
||||
case TextureFormat::RGBA_ASTC_4x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
return 4;
|
||||
@@ -489,8 +455,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto freeFunc = [](void* buffer,
|
||||
UTILS_UNUSED size_t size, UTILS_UNUSED void* user) { free(buffer); };
|
||||
const auto freeFunc = [](void* buffer, size_t size, void* user) { free(buffer); };
|
||||
const size_t reshapedSize = 4 * data.size / 3;
|
||||
const PixelDataFormat reshapedFormat =
|
||||
data.format == PixelDataFormat::RGB ? PixelDataFormat::RGBA : PixelDataFormat::RGBA_INTEGER;
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
namespace filament::backend {
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
PresentCallable::PresentCallable(PresentFn fn, void* user) noexcept
|
||||
: mPresentFn(fn), mUser(user) {
|
||||
@@ -34,4 +35,5 @@ void PresentCallable::operator()(bool presentFrame) noexcept {
|
||||
mPresentFn = nullptr;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -62,7 +62,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
void* vaddr = MAP_FAILED;
|
||||
void* vaddr_shadow = MAP_FAILED;
|
||||
void* vaddr_guard = MAP_FAILED;
|
||||
int const fd = ashmem_create_region("filament::CircularBuffer", size + BLOCK_SIZE);
|
||||
int fd = ashmem_create_region("filament::CircularBuffer", size + BLOCK_SIZE);
|
||||
if (fd >= 0) {
|
||||
// reserve/find enough address space
|
||||
void* reserve_vaddr = mmap(nullptr, size * 2 + BLOCK_SIZE,
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
|
||||
: mRequiredSize((requiredSize + CircularBuffer::BLOCK_MASK) & ~CircularBuffer::BLOCK_MASK),
|
||||
@@ -40,13 +41,13 @@ CommandBufferQueue::~CommandBufferQueue() {
|
||||
}
|
||||
|
||||
void CommandBufferQueue::requestExit() {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard<utils::Mutex> lock(mLock);
|
||||
mExitRequested = EXIT_REQUESTED;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
bool CommandBufferQueue::isExitRequested() const {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard<utils::Mutex> lock(mLock);
|
||||
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
|
||||
"mExitRequested is corrupted (value = 0x%08x)!", mExitRequested);
|
||||
return (bool)mExitRequested;
|
||||
@@ -72,7 +73,7 @@ void CommandBufferQueue::flush() noexcept {
|
||||
void* const tail = circularBuffer.getTail();
|
||||
|
||||
// size of this slice
|
||||
uint32_t const used = uint32_t(intptr_t(head) - intptr_t(tail));
|
||||
uint32_t used = uint32_t(intptr_t(head) - intptr_t(tail));
|
||||
|
||||
circularBuffer.circularize();
|
||||
|
||||
@@ -124,9 +125,10 @@ std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() con
|
||||
}
|
||||
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Slice const& buffer) {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard<utils::Mutex> lock(mLock);
|
||||
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
#include "private/backend/CommandStream.h"
|
||||
|
||||
#if DEBUG_COMMAND_STREAM
|
||||
#include <utils/CallStack.h>
|
||||
#endif
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/Systrace.h>
|
||||
@@ -37,7 +34,7 @@ namespace filament::backend {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// A few utility functions for debugging...
|
||||
|
||||
inline void printParameterPack(io::ostream&) { }
|
||||
inline void printParameterPack(io::ostream& out) { }
|
||||
|
||||
template<typename LAST>
|
||||
static void printParameterPack(io::ostream& out, const LAST& t) { out << t; }
|
||||
@@ -98,7 +95,7 @@ void CommandStream::execute(void* buffer) {
|
||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||
// we want to remove all this when tracing is completely disabled
|
||||
profiler.stop();
|
||||
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
|
||||
UTILS_UNUSED Profiler::Counters counters = profiler.readCounters();
|
||||
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions());
|
||||
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles());
|
||||
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10);
|
||||
|
||||
@@ -96,8 +96,6 @@ struct MetalContext {
|
||||
SamplerStateCache samplerStateCache;
|
||||
ArgumentEncoderCache argumentEncoderCache;
|
||||
|
||||
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
|
||||
|
||||
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
|
||||
|
||||
// Keeps track of sampler groups we've finalized for the current render pass.
|
||||
|
||||
@@ -1005,7 +1005,6 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
mContext->depthStencilState.invalidate();
|
||||
mContext->cullModeState.invalidate();
|
||||
mContext->windingState.invalidate();
|
||||
mContext->currentPolygonOffset = {0.0f, 0.0f};
|
||||
|
||||
mContext->finalizedSamplerGroups.clear();
|
||||
}
|
||||
@@ -1184,12 +1183,8 @@ void MetalDriver::startCapture(int) {
|
||||
if (@available(iOS 13, *)) {
|
||||
MTLCaptureDescriptor* descriptor = [MTLCaptureDescriptor new];
|
||||
descriptor.captureObject = mContext->device;
|
||||
#if defined(IOS)
|
||||
descriptor.destination = MTLCaptureDestinationDeveloperTools;
|
||||
#else
|
||||
descriptor.destination = MTLCaptureDestinationGPUTraceDocument;
|
||||
descriptor.outputURL = [[NSURL alloc] initFileURLWithPath:@"filament.gputrace"];
|
||||
#endif
|
||||
NSError* error = nil;
|
||||
[[MTLCaptureManager sharedCaptureManager] startCaptureWithDescriptor:descriptor
|
||||
error:&error];
|
||||
@@ -1591,12 +1586,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
[mContext->currentRenderPassEncoder setDepthStencilState:state];
|
||||
}
|
||||
|
||||
if (ps.polygonOffset.constant != mContext->currentPolygonOffset.constant ||
|
||||
ps.polygonOffset.slope != mContext->currentPolygonOffset.slope) {
|
||||
if (ps.polygonOffset.constant != 0.0 || ps.polygonOffset.slope != 0.0) {
|
||||
[mContext->currentRenderPassEncoder setDepthBias:ps.polygonOffset.constant
|
||||
slopeScale:ps.polygonOffset.slope
|
||||
clamp:0.0];
|
||||
mContext->currentPolygonOffset = ps.polygonOffset;
|
||||
}
|
||||
|
||||
// Set scissor-rectangle.
|
||||
|
||||
@@ -198,16 +198,6 @@ MTLPixelFormat getMetalFormat(MetalContext* context, TextureFormat format) noexc
|
||||
|
||||
case TextureFormat::DXT1_RGB: return MTLPixelFormatInvalid;
|
||||
case TextureFormat::DXT1_SRGB: return MTLPixelFormatInvalid;
|
||||
|
||||
case TextureFormat::RED_RGTC1: return MTLPixelFormatBC4_RUnorm;
|
||||
case TextureFormat::SIGNED_RED_RGTC1: return MTLPixelFormatBC4_RSnorm;
|
||||
case TextureFormat::RED_GREEN_RGTC2: return MTLPixelFormatBC5_RGUnorm;
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2: return MTLPixelFormatBC5_RGSnorm;
|
||||
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return MTLPixelFormatBC6H_RGBFloat;
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT: return MTLPixelFormatBC6H_RGBUfloat;
|
||||
case TextureFormat::RGBA_BPTC_UNORM: return MTLPixelFormatBC7_RGBAUnorm;
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return MTLPixelFormatBC7_RGBAUnorm_sRGB;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ const char* getGLError(GLenum error) noexcept {
|
||||
|
||||
UTILS_NOINLINE
|
||||
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
|
||||
GLenum const error = glGetError();
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
const char* string = getGLError(error);
|
||||
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
|
||||
@@ -68,7 +68,7 @@ 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);
|
||||
GLenum err = checkGLError(out, function, line);
|
||||
if (err != GL_NO_ERROR) {
|
||||
debug_trap();
|
||||
}
|
||||
@@ -104,7 +104,7 @@ const char* getFramebufferStatus(GLenum status) noexcept {
|
||||
|
||||
UTILS_NOINLINE
|
||||
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum const status = glCheckFramebufferStatus(target);
|
||||
GLenum status = glCheckFramebufferStatus(target);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
const char* string = getFramebufferStatus(status);
|
||||
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
|
||||
@@ -115,7 +115,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);
|
||||
GLenum status = checkFramebufferStatus(out, target, function, line);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
@@ -472,34 +472,6 @@ constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept {
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if defined(GL_EXT_texture_compression_rgtc)
|
||||
case TextureFormat::RED_RGTC1: return GL_COMPRESSED_RED_RGTC1_EXT;
|
||||
case TextureFormat::SIGNED_RED_RGTC1: return GL_COMPRESSED_SIGNED_RED_RGTC1_EXT;
|
||||
case TextureFormat::RED_GREEN_RGTC2: return GL_COMPRESSED_RED_GREEN_RGTC2_EXT;
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
|
||||
#else
|
||||
case TextureFormat::RED_RGTC1:
|
||||
case TextureFormat::SIGNED_RED_RGTC1:
|
||||
case TextureFormat::RED_GREEN_RGTC2:
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
// this should not happen
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if defined(GL_EXT_texture_compression_bptc)
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT;
|
||||
case TextureFormat::RGBA_BPTC_UNORM: return GL_COMPRESSED_RGBA_BPTC_UNORM_EXT;
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT;
|
||||
#else
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
case TextureFormat::RGBA_BPTC_UNORM:
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
// this should not happen
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if defined(GL_KHR_texture_compression_astc_hdr)
|
||||
case TextureFormat::RGBA_ASTC_4x4: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
|
||||
case TextureFormat::RGBA_ASTC_5x4: return GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
|
||||
|
||||
@@ -27,7 +27,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
|
||||
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
|
||||
char const* version = (char const*)glGetString(GL_VERSION);
|
||||
// This works on all versions of GLES
|
||||
int const n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0;
|
||||
int n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0;
|
||||
return n == 2;
|
||||
} else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
|
||||
// OpenGL version
|
||||
@@ -142,7 +142,7 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
bugs.enable_initialize_non_used_uniform_array = true;
|
||||
|
||||
int maj, min, driverMajor, driverMinor;
|
||||
int const c = sscanf(state.version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
|
||||
int c = sscanf(state.version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
|
||||
&maj, &min, &driverMajor, &driverMinor);
|
||||
if (c == 4) {
|
||||
// Workarounds based on version here.
|
||||
@@ -176,7 +176,7 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
bugs.dont_use_timer_query = true;
|
||||
|
||||
int maj, min, driverVersion, driverRevision, driverPatch;
|
||||
int const c = sscanf(state.version, "OpenGL ES %d.%d v%d.r%dp%d", // NOLINT(cert-err34-c)
|
||||
int c = sscanf(state.version, "OpenGL ES %d.%d v%d.r%dp%d", // NOLINT(cert-err34-c)
|
||||
&maj, &min, &driverVersion, &driverRevision, &driverPatch);
|
||||
if (c == 5) {
|
||||
// Workarounds based on version here.
|
||||
@@ -218,7 +218,7 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
if (strstr(state.renderer, "Adreno")) {
|
||||
// Qualcomm GPU
|
||||
// early exit condition is flattened in EASU code
|
||||
// (that should be regardless of ANGLE, but we should double-check)
|
||||
// (that should be regardless of ANGLE, but we should double check)
|
||||
bugs.split_easu = true;
|
||||
}
|
||||
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
|
||||
@@ -270,8 +270,8 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
|
||||
#if !defined(NDEBUG) && defined(GL_KHR_debug)
|
||||
if (ext.KHR_debug) {
|
||||
auto cb = +[](GLenum, GLenum type, GLuint, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void *) {
|
||||
auto cb = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void *userParam) {
|
||||
io::ostream* stream = &slog.i;
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH: stream = &slog.e; break;
|
||||
@@ -292,7 +292,7 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
case GL_DEBUG_TYPE_MARKER: level = "MARKER: "; break;
|
||||
default: break;
|
||||
}
|
||||
out << "KHR_debug " << level << std::string_view{ message, size_t(length) } << io::endl;
|
||||
out << "KHR_debug " << level << message << io::endl;
|
||||
};
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
@@ -304,7 +304,7 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
void OpenGLContext::setDefaultState() noexcept {
|
||||
// We need to make sure our internal state matches the GL state when we start.
|
||||
// (some of these calls may be unneeded as they might be the gl defaults)
|
||||
GLenum const caps[] = {
|
||||
GLenum caps[] = {
|
||||
GL_BLEND,
|
||||
GL_CULL_FACE,
|
||||
GL_SCISSOR_TEST,
|
||||
@@ -315,14 +315,16 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
GL_SAMPLE_COVERAGE,
|
||||
GL_POLYGON_OFFSET_FILL,
|
||||
};
|
||||
size_t capsCount = sizeof(caps) / sizeof(caps[0]);
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (auto const capi : caps) {
|
||||
size_t const capIndex = getIndexForCap(capi);
|
||||
for (GLenum capi = 0; capi < capsCount; ++capi) {
|
||||
size_t capIndex = getIndexForCap(caps[capi]);
|
||||
GLenum cap = caps[capi];
|
||||
if (state.enables.caps[capIndex]) {
|
||||
glEnable(capi);
|
||||
glEnable(cap);
|
||||
} else {
|
||||
glDisable(capi);
|
||||
glDisable(cap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +353,7 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
|
||||
void OpenGLContext::initExtensionsGLES() noexcept {
|
||||
const char * const extensions = (const char*)glGetString(GL_EXTENSIONS);
|
||||
GLUtils::unordered_string_set const exts = GLUtils::split(extensions);
|
||||
GLUtils::unordered_string_set exts = GLUtils::split(extensions);
|
||||
if constexpr (DEBUG_PRINT_EXTENSIONS) {
|
||||
for (auto extension: exts) {
|
||||
slog.d << "\"" << std::string_view(extension) << "\"\n";
|
||||
@@ -375,8 +377,6 @@ void OpenGLContext::initExtensionsGLES() noexcept {
|
||||
#endif
|
||||
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
|
||||
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
|
||||
ext.EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
|
||||
ext.EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
|
||||
ext.EXT_texture_cube_map_array = exts.has("GL_EXT_texture_cube_map_array"sv) || exts.has("GL_OES_texture_cube_map_array"sv);
|
||||
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
|
||||
ext.KHR_debug = exts.has("GL_KHR_debug"sv);
|
||||
@@ -420,8 +420,6 @@ void OpenGLContext::initExtensionsGL() noexcept {
|
||||
ext.EXT_texture_compression_etc2 = exts.has("GL_ARB_ES3_compatibility"sv);
|
||||
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
|
||||
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
|
||||
ext.EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
|
||||
ext.EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
|
||||
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
|
||||
ext.EXT_texture_sRGB = exts.has("GL_EXT_texture_sRGB"sv);
|
||||
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
|
||||
@@ -446,7 +444,7 @@ void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
|
||||
glBindBuffer(target, buffer);
|
||||
}
|
||||
} else {
|
||||
size_t const targetIndex = getIndexForBufferTarget(target);
|
||||
size_t targetIndex = getIndexForBufferTarget(target);
|
||||
update_state(state.buffers.genericBinding[targetIndex], buffer, [&]() {
|
||||
glBindBuffer(target, buffer);
|
||||
});
|
||||
@@ -613,7 +611,7 @@ void OpenGLContext::resetState() noexcept {
|
||||
// Reset state.buffers to its default state to avoid the complexity and error-prone
|
||||
// nature of resetting the GL state to its existing state
|
||||
state.buffers = {};
|
||||
GLenum const bufferTargets[] = {
|
||||
GLenum bufferTargets[] = {
|
||||
GL_UNIFORM_BUFFER,
|
||||
GL_TRANSFORM_FEEDBACK_BUFFER,
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
@@ -626,7 +624,9 @@ void OpenGLContext::resetState() noexcept {
|
||||
GL_PIXEL_PACK_BUFFER,
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
};
|
||||
for (auto const target : bufferTargets) {
|
||||
size_t bufferTargetCount = sizeof(bufferTargets) / sizeof(bufferTargets[0]);
|
||||
for (size_t targetArrayIndex = 0; targetArrayIndex < bufferTargetCount; ++targetArrayIndex) {
|
||||
GLenum target = bufferTargets[targetArrayIndex];
|
||||
glBindBuffer(target, 0);
|
||||
}
|
||||
|
||||
@@ -655,11 +655,13 @@ void OpenGLContext::resetState() noexcept {
|
||||
GL_TEXTURE_CUBE_MAP_ARRAY,
|
||||
#endif
|
||||
};
|
||||
const size_t textureTargetCount = sizeof(textureTargets) / sizeof(textureTargets[0]);
|
||||
for (GLint unit = 0; unit < gets.max_combined_texture_image_units; ++unit) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindSampler(unit, 0);
|
||||
|
||||
for (auto const target : textureTargets) {
|
||||
for (size_t textureTargetArrayIndex = 0; textureTargetArrayIndex < textureTargetCount; ++textureTargetArrayIndex) {
|
||||
GLuint target = textureTargets[textureTargetArrayIndex];
|
||||
glBindTexture(target, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,93 +137,91 @@ public:
|
||||
|
||||
// features supported by this version of GL or GLES
|
||||
struct {
|
||||
bool multisample_texture;
|
||||
} features = {};
|
||||
bool multisample_texture = false;
|
||||
} features;
|
||||
|
||||
// supported extensions detected at runtime
|
||||
struct {
|
||||
bool APPLE_color_buffer_packed_float;
|
||||
bool ARB_shading_language_packing;
|
||||
bool EXT_clip_control;
|
||||
bool EXT_color_buffer_float;
|
||||
bool EXT_color_buffer_half_float;
|
||||
bool EXT_debug_marker;
|
||||
bool EXT_disjoint_timer_query;
|
||||
bool EXT_multisampled_render_to_texture;
|
||||
bool EXT_multisampled_render_to_texture2;
|
||||
bool EXT_shader_framebuffer_fetch;
|
||||
bool KHR_texture_compression_astc_hdr;
|
||||
bool KHR_texture_compression_astc_ldr;
|
||||
bool EXT_texture_compression_etc2;
|
||||
bool EXT_texture_compression_s3tc;
|
||||
bool EXT_texture_compression_s3tc_srgb;
|
||||
bool EXT_texture_compression_rgtc;
|
||||
bool EXT_texture_compression_bptc;
|
||||
bool EXT_texture_cube_map_array;
|
||||
bool EXT_texture_filter_anisotropic;
|
||||
bool EXT_texture_sRGB;
|
||||
bool GOOGLE_cpp_style_line_directive;
|
||||
bool KHR_debug;
|
||||
bool OES_EGL_image_external_essl3;
|
||||
bool QCOM_tiled_rendering;
|
||||
bool WEBGL_compressed_texture_etc;
|
||||
bool WEBGL_compressed_texture_s3tc;
|
||||
bool WEBGL_compressed_texture_s3tc_srgb;
|
||||
} ext = {};
|
||||
bool APPLE_color_buffer_packed_float = false;
|
||||
bool ARB_shading_language_packing = false;
|
||||
bool EXT_clip_control = false;
|
||||
bool EXT_color_buffer_float = false;
|
||||
bool EXT_color_buffer_half_float = false;
|
||||
bool EXT_debug_marker = false;
|
||||
bool EXT_disjoint_timer_query = false;
|
||||
bool EXT_multisampled_render_to_texture = false;
|
||||
bool EXT_multisampled_render_to_texture2 = false;
|
||||
bool EXT_shader_framebuffer_fetch = false;
|
||||
bool KHR_texture_compression_astc_hdr = false;
|
||||
bool KHR_texture_compression_astc_ldr = false;
|
||||
bool EXT_texture_compression_etc2 = false;
|
||||
bool EXT_texture_compression_s3tc = false;
|
||||
bool EXT_texture_compression_s3tc_srgb = false;
|
||||
bool EXT_texture_cube_map_array = false;
|
||||
bool EXT_texture_filter_anisotropic = false;
|
||||
bool EXT_texture_sRGB = false;
|
||||
bool GOOGLE_cpp_style_line_directive = false;
|
||||
bool KHR_debug = false;
|
||||
bool OES_EGL_image_external_essl3 = false;
|
||||
bool QCOM_tiled_rendering = false;
|
||||
bool WEBGL_compressed_texture_etc = false;
|
||||
bool WEBGL_compressed_texture_s3tc = false;
|
||||
bool WEBGL_compressed_texture_s3tc_srgb = false;
|
||||
} ext;
|
||||
|
||||
struct {
|
||||
// Some drivers have issues with UBOs in the fragment shader when
|
||||
// glFlush() is called between draw calls.
|
||||
bool disable_glFlush;
|
||||
bool disable_glFlush = false;
|
||||
|
||||
// Some drivers seem to not store the GL_ELEMENT_ARRAY_BUFFER binding
|
||||
// in the VAO state.
|
||||
bool vao_doesnt_store_element_array_buffer_binding;
|
||||
bool vao_doesnt_store_element_array_buffer_binding = false;
|
||||
|
||||
// Some drivers have gl state issues when drawing from shared contexts
|
||||
bool disable_shared_context_draws;
|
||||
bool disable_shared_context_draws = false;
|
||||
|
||||
// Some drivers require the GL_TEXTURE_EXTERNAL_OES target to be bound when
|
||||
// the texture image changes, even if it's already bound to that texture
|
||||
bool texture_external_needs_rebind;
|
||||
bool texture_external_needs_rebind = false;
|
||||
|
||||
// Some web browsers seem to immediately clear the default framebuffer when calling
|
||||
// glInvalidateFramebuffer with WebGL 2.0
|
||||
bool disable_invalidate_framebuffer;
|
||||
bool disable_invalidate_framebuffer = false;
|
||||
|
||||
// Some drivers declare GL_EXT_texture_filter_anisotropic but don't support
|
||||
// calling glSamplerParameter() with GL_TEXTURE_MAX_ANISOTROPY_EXT
|
||||
bool texture_filter_anisotropic_broken_on_sampler;
|
||||
bool texture_filter_anisotropic_broken_on_sampler = false;
|
||||
|
||||
// Some drivers have issues when reading from a mip while writing to a different mip.
|
||||
// In the OpenGL ES 3.0 specification this is covered in section 4.4.3,
|
||||
// "Feedback Loops Between Textures and the Framebuffer".
|
||||
bool disable_feedback_loops;
|
||||
bool disable_feedback_loops = false;
|
||||
|
||||
// Some drivers don't implement timer queries correctly
|
||||
bool dont_use_timer_query;
|
||||
bool dont_use_timer_query = false;
|
||||
|
||||
// Some drivers can't blit from a sidecar renderbuffer into a layer of a texture array.
|
||||
// This technique is used for VSM with MSAA turned on.
|
||||
bool disable_sidecar_blit_into_texture_array;
|
||||
bool disable_sidecar_blit_into_texture_array = false;
|
||||
|
||||
// Some drivers incorrectly flatten the early exit condition in the EASU code, in which
|
||||
// case we need an alternative algorithm
|
||||
bool split_easu;
|
||||
bool split_easu = false;
|
||||
|
||||
// As of Android R some qualcomm drivers invalidate buffers for the whole render pass
|
||||
// even if glInvalidateFramebuffer() is called at the end of it.
|
||||
bool invalidate_end_only_if_invalidate_start;
|
||||
bool invalidate_end_only_if_invalidate_start = false;
|
||||
|
||||
// GLES doesn't allow feedback loops even if writes are disabled. So take we the point of
|
||||
// view that this is generally forbidden. However, this restriction is lifted on desktop
|
||||
// GL and Vulkan and probably Metal.
|
||||
bool allow_read_only_ancillary_feedback_loop;
|
||||
bool allow_read_only_ancillary_feedback_loop = false;
|
||||
|
||||
// Some Adreno drivers crash in glDrawXXX() when there's an uninitialized uniform block,
|
||||
// even when the shader doesn't access it.
|
||||
bool enable_initialize_non_used_uniform_array;
|
||||
} bugs = {};
|
||||
bool enable_initialize_non_used_uniform_array = false;
|
||||
} bugs;
|
||||
|
||||
// state getters -- as needed.
|
||||
vec4gli const& getViewport() const { return state.window.viewport; }
|
||||
|
||||
@@ -78,8 +78,8 @@ using namespace GLUtils;
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
UTILS_NOINLINE
|
||||
Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
Driver* OpenGLDriver::create(
|
||||
OpenGLPlatform* const platform, void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
assert_invariant(platform);
|
||||
OpenGLPlatform* const ec = platform;
|
||||
|
||||
@@ -120,7 +120,7 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
|
||||
// here we check we're on a supported version of GL before initializing the driver
|
||||
GLint major = 0, minor = 0;
|
||||
bool const success = OpenGLContext::queryOpenGLVersion(&major, &minor);
|
||||
bool success = OpenGLContext::queryOpenGLVersion(&major, &minor);
|
||||
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
PANIC_LOG("Can't get OpenGL version");
|
||||
@@ -142,9 +142,8 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
}
|
||||
}
|
||||
|
||||
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||
Platform::DriverConfig const validConfig {
|
||||
.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize) };
|
||||
size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||
Platform::DriverConfig validConfig { .handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize) };
|
||||
OpenGLDriver* const driver = new OpenGLDriver(ec, validConfig);
|
||||
return driver;
|
||||
}
|
||||
@@ -170,8 +169,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
|
||||
|
||||
// set a reasonable default value for our stream array
|
||||
mTexturesWithStreamsAttached.reserve(8);
|
||||
mStreamsWithPendingAquiredImage.reserve(8);
|
||||
mExternalStreams.reserve(8);
|
||||
|
||||
#ifndef NDEBUG
|
||||
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
|
||||
@@ -437,10 +435,10 @@ void OpenGLDriver::createIndexBufferR(
|
||||
DEBUG_MARKER()
|
||||
|
||||
auto& gl = mContext;
|
||||
uint8_t const elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
uint8_t elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
GLIndexBuffer* ib = construct<GLIndexBuffer>(ibh, elementSize, indexCount);
|
||||
glGenBuffers(1, &ib->gl.buffer);
|
||||
GLsizeiptr const size = elementSize * indexCount;
|
||||
GLsizeiptr size = elementSize * indexCount;
|
||||
gl.bindVertexArray(nullptr);
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, getBufferUsage(usage));
|
||||
@@ -585,7 +583,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
// getIndexForTextureTarget() is constexpr -- so all of this disappears at compile time.
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// we can't be here -- doesn't matter what we do
|
||||
// we can't be here -- doesn't mater what we do
|
||||
case SamplerType::SAMPLER_2D:
|
||||
t->gl.target = GL_TEXTURE_2D;
|
||||
t->gl.targetIndex = (uint8_t)gl.getIndexForTextureTarget(GL_TEXTURE_2D);
|
||||
@@ -1040,8 +1038,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
|
||||
auto& gl = mContext;
|
||||
if (gl.ext.EXT_multisampled_render_to_texture ||
|
||||
gl.ext.EXT_multisampled_render_to_texture2) {
|
||||
glext::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,
|
||||
samples, internalformat, width, height);
|
||||
glext::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, internalformat, width, height);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -1117,7 +1114,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
* About the size of the attachments:
|
||||
*
|
||||
* If the attachment sizes are not all identical, the results of rendering are defined only
|
||||
* within the largest area that can fit in all the attachments. This area is defined as
|
||||
* within the largest area that can fit in all of the attachments. This area is defined as
|
||||
* the intersection of rectangles having a lower left of (0, 0) and an upper right of
|
||||
* (width, height) for each attachment. Contents of attachments outside this area are
|
||||
* undefined after execution of a rendering command.
|
||||
@@ -1208,11 +1205,11 @@ void OpenGLDriver::createSyncR(Handle<HwSync> fh, int) {
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
// check the status of the sync once a frame, since we must do this from our thread
|
||||
std::weak_ptr<GLSync::State> const weak = f->result;
|
||||
std::weak_ptr<GLSync::State> weak = f->result;
|
||||
runEveryNowAndThen([sync = f->gl.sync, weak]() -> bool {
|
||||
auto result = weak.lock();
|
||||
if (result) {
|
||||
GLenum const status = glClientWaitSync(sync, 0, 0u);
|
||||
GLenum status = glClientWaitSync(sync, 0, 0u);
|
||||
result->status.store(status, std::memory_order_relaxed);
|
||||
return (status != GL_TIMEOUT_EXPIRED);
|
||||
}
|
||||
@@ -1373,23 +1370,15 @@ void OpenGLDriver::destroyStream(Handle<HwStream> sh) {
|
||||
GLStream* s = handle_cast<GLStream*>(sh);
|
||||
|
||||
// if this stream is still attached to a texture, detach it first
|
||||
auto& texturesWithStreamsAttached = mTexturesWithStreamsAttached;
|
||||
auto pos = std::find_if(
|
||||
texturesWithStreamsAttached.begin(), texturesWithStreamsAttached.end(),
|
||||
[s](GLTexture const* t) {
|
||||
return t->hwStream == s;
|
||||
});
|
||||
|
||||
if (pos != texturesWithStreamsAttached.end()) {
|
||||
auto& externalStreams = mExternalStreams;
|
||||
auto pos = std::find_if(externalStreams.begin(), externalStreams.end(),
|
||||
[s](GLTexture const* t) { return t->hwStream == s; });
|
||||
if (pos != externalStreams.end()) {
|
||||
detachStream(*pos);
|
||||
}
|
||||
|
||||
// and then destroy the stream. Only NATIVE streams have Platform::Stream associated.
|
||||
if (s->streamType == StreamType::NATIVE) {
|
||||
mPlatform.destroyStream(s->stream);
|
||||
}
|
||||
|
||||
// finally destroy the HwStream handle
|
||||
destruct(sh, s);
|
||||
}
|
||||
}
|
||||
@@ -1437,56 +1426,30 @@ Handle<HwStream> OpenGLDriver::createStreamAcquired() {
|
||||
void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer,
|
||||
CallbackHandler* handler, StreamCallback cb, void* userData) {
|
||||
GLStream* glstream = handle_cast<GLStream*>(sh);
|
||||
assert_invariant(glstream->streamType == StreamType::ACQUIRED);
|
||||
|
||||
if (UTILS_UNLIKELY(glstream->user_thread.pending.image)) {
|
||||
if (glstream->user_thread.pending.image) {
|
||||
scheduleRelease(glstream->user_thread.pending);
|
||||
slog.w << "Acquired image is set more than once per frame." << io::endl;
|
||||
}
|
||||
|
||||
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
|
||||
hwbuffer, cb, userData, handler });
|
||||
|
||||
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
|
||||
// NULL to glEGLImageTargetTexture2DOES, and there is no concept of "detaching" an
|
||||
// EGLimage from a texture.
|
||||
mStreamsWithPendingAquiredImage.push_back(glstream);
|
||||
}
|
||||
}
|
||||
|
||||
// updateStreams() and setAcquiredImage() are both called from on the application's thread
|
||||
// and therefore do not require synchronization. The former is always called immediately before
|
||||
// beginFrame, the latter is called by the user from anywhere outside beginFrame / endFrame.
|
||||
void OpenGLDriver::updateStreams(DriverApi* driver) {
|
||||
if (UTILS_UNLIKELY(!mStreamsWithPendingAquiredImage.empty())) {
|
||||
for (GLStream* s : mStreamsWithPendingAquiredImage) {
|
||||
assert_invariant(s);
|
||||
assert_invariant(s->streamType == StreamType::ACQUIRED);
|
||||
if (UTILS_UNLIKELY(!mExternalStreams.empty())) {
|
||||
for (GLTexture* t : mExternalStreams) {
|
||||
assert_invariant(t);
|
||||
|
||||
AcquiredImage const previousImage = s->user_thread.acquired;
|
||||
s->user_thread.acquired = s->user_thread.pending;
|
||||
s->user_thread.pending = { nullptr };
|
||||
GLStream* s = static_cast<GLStream*>(t->hwStream); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
if (UTILS_UNLIKELY(s == nullptr)) {
|
||||
// this can happen because we're called synchronously and the setExternalStream()
|
||||
// call may not have been processed yet.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bind the stashed EGLImage to its corresponding GL texture as soon as we start
|
||||
// making the GL calls for the upcoming frame.
|
||||
driver->queueCommand([this, s, image = s->user_thread.acquired.image, previousImage]() {
|
||||
|
||||
auto& streams = mTexturesWithStreamsAttached;
|
||||
auto pos = std::find_if(streams.begin(), streams.end(),
|
||||
[s](GLTexture const* t) {
|
||||
return t->hwStream == s;
|
||||
});
|
||||
if (pos != streams.end()) {
|
||||
setExternalTexture(*pos, image);
|
||||
}
|
||||
|
||||
if (previousImage.image) {
|
||||
scheduleRelease(AcquiredImage(previousImage));
|
||||
}
|
||||
});
|
||||
if (s->streamType == StreamType::ACQUIRED) {
|
||||
updateStreamAcquired(t, driver);
|
||||
}
|
||||
}
|
||||
mStreamsWithPendingAquiredImage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1546,12 +1509,6 @@ bool OpenGLDriver::isTextureFormatSupported(TextureFormat format) {
|
||||
return ext.EXT_texture_compression_s3tc || // this is ES specific
|
||||
ext.WEBGL_compressed_texture_s3tc; // this is WEBGL specific
|
||||
}
|
||||
if (isRGTCCompression(format)) {
|
||||
return ext.EXT_texture_compression_rgtc;
|
||||
}
|
||||
if (isBPTCCompression(format)) {
|
||||
return ext.EXT_texture_compression_bptc;
|
||||
}
|
||||
if (isASTCCompression(format)) {
|
||||
return ext.KHR_texture_compression_astc_hdr;
|
||||
}
|
||||
@@ -1852,7 +1809,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
DEBUG_MARKER()
|
||||
|
||||
#if defined(GL_EXT_texture_filter_anisotropic)
|
||||
OpenGLContext const& context = getContext();
|
||||
OpenGLContext& context = getContext();
|
||||
const bool anisotropyWorkaround =
|
||||
context.ext.EXT_texture_filter_anisotropic &&
|
||||
context.bugs.texture_filter_anisotropic_broken_on_sampler;
|
||||
@@ -1872,7 +1829,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
SamplerParams params = pSamplers[i].s;
|
||||
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
|
||||
// From OES_EGL_image_external spec:
|
||||
// "The default s and t wrap modes are CLAMP_TO_EDGE, and it is an INVALID_ENUM
|
||||
// "The default s and t wrap modes are CLAMP_TO_EDGE and it is an INVALID_ENUM
|
||||
// error to set the wrap mode to any other value."
|
||||
params.wrapS = SamplerWrapMode::CLAMP_TO_EDGE;
|
||||
params.wrapT = SamplerWrapMode::CLAMP_TO_EDGE;
|
||||
@@ -1896,7 +1853,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
// Driver claims to support anisotropic filtering, but it fails when set on
|
||||
// the sampler, we have to set it on the texture instead.
|
||||
// The texture is already bound here.
|
||||
GLfloat const anisotropy = float(1u << params.anisotropyLog2);
|
||||
GLfloat anisotropy = float(1u << params.anisotropyLog2);
|
||||
glTexParameterf(t->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
std::min(context.gets.max_anisotropy, anisotropy));
|
||||
}
|
||||
@@ -1987,8 +1944,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
return;
|
||||
}
|
||||
|
||||
GLenum const glFormat = getFormat(p.format);
|
||||
GLenum const glType = getType(p.type);
|
||||
GLenum glFormat = getFormat(p.format);
|
||||
GLenum glType = getType(p.type);
|
||||
|
||||
gl.pixelStore(GL_UNPACK_ROW_LENGTH, GLint(p.stride));
|
||||
gl.pixelStore(GL_UNPACK_ALIGNMENT, GLint(p.alignment));
|
||||
@@ -1997,7 +1954,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
switch (t->target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// if we get there, it's because the user is trying to use an external texture,
|
||||
// if we get there, it's because the user is trying to use an external texture
|
||||
// but it's not supported, so instead, we behave like a texture2d.
|
||||
// fallthrough...
|
||||
case SamplerType::SAMPLER_2D:
|
||||
@@ -2041,7 +1998,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
assert_invariant(zoffset + depth <= 6);
|
||||
UTILS_NOUNROLL
|
||||
for (size_t face = 0; face < depth; face++) {
|
||||
GLenum const target = getCubemapTarget(zoffset + face);
|
||||
GLenum target = getCubemapTarget(zoffset + face);
|
||||
glTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
|
||||
GLsizei(width), GLsizei(height), glFormat, glType,
|
||||
static_cast<uint8_t const*>(p.buffer) + faceSize * face);
|
||||
@@ -2050,7 +2007,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
}
|
||||
}
|
||||
|
||||
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// update the base/max LOD so we don't access undefined LOD. this allows the app to
|
||||
// specify levels as they become available.
|
||||
|
||||
if (int8_t(level) < t->gl.baseLevel) {
|
||||
@@ -2085,13 +2042,13 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
// TODO: maybe assert that the CompressedPixelDataType is the same as the internalFormat
|
||||
|
||||
GLsizei const imageSize = GLsizei(p.imageSize);
|
||||
GLsizei imageSize = GLsizei(p.imageSize);
|
||||
|
||||
// TODO: maybe assert the size is right (b/c we can compute it ourselves)
|
||||
|
||||
switch (t->target) {
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
// if we get there, it's because the user is trying to use an external texture,
|
||||
// if we get there, it's because the user is trying to use an external texture
|
||||
// but it's not supported, so instead, we behave like a texture2d.
|
||||
// fallthrough...
|
||||
case SamplerType::SAMPLER_2D:
|
||||
@@ -2133,7 +2090,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t face = 0; face < depth; face++) {
|
||||
GLenum const target = getCubemapTarget(zoffset + face);
|
||||
GLenum target = getCubemapTarget(zoffset + face);
|
||||
glCompressedTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
|
||||
GLsizei(width), GLsizei(height), t->gl.internalFormat,
|
||||
imageSize, static_cast<uint8_t const*>(p.buffer) + faceSize * face);
|
||||
@@ -2142,7 +2099,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
|
||||
}
|
||||
}
|
||||
|
||||
// update the base/max LOD, so we don't access undefined LOD. this allows the app to
|
||||
// update the base/max LOD so we don't access undefined LOD. this allows the app to
|
||||
// specify levels as they become available.
|
||||
|
||||
if (uint8_t(level) < t->gl.baseLevel) {
|
||||
@@ -2220,7 +2177,7 @@ void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh)
|
||||
|
||||
UTILS_NOINLINE
|
||||
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
|
||||
mTexturesWithStreamsAttached.push_back(t);
|
||||
mExternalStreams.push_back(t);
|
||||
|
||||
switch (hwStream->streamType) {
|
||||
case StreamType::NATIVE:
|
||||
@@ -2235,10 +2192,10 @@ void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
|
||||
UTILS_NOINLINE
|
||||
void OpenGLDriver::detachStream(GLTexture* t) noexcept {
|
||||
auto& gl = mContext;
|
||||
auto& texturesWithStreamsAttached = mTexturesWithStreamsAttached;
|
||||
auto pos = std::find(texturesWithStreamsAttached.begin(), texturesWithStreamsAttached.end(), t);
|
||||
if (pos != texturesWithStreamsAttached.end()) {
|
||||
texturesWithStreamsAttached.erase(pos);
|
||||
auto& streams = mExternalStreams;
|
||||
auto pos = std::find(streams.begin(), streams.end(), t);
|
||||
if (pos != streams.end()) {
|
||||
streams.erase(pos);
|
||||
}
|
||||
|
||||
GLStream* s = static_cast<GLStream*>(t->hwStream); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
@@ -2313,7 +2270,7 @@ void OpenGLDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
|
||||
bool OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
|
||||
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
|
||||
uint64_t const d = tq->elapsed.load(std::memory_order_relaxed);
|
||||
uint64_t d = tq->elapsed.load(std::memory_order_relaxed);
|
||||
if (!d) {
|
||||
return false;
|
||||
}
|
||||
@@ -2366,7 +2323,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES30) {
|
||||
if (!gl.bugs.disable_invalidate_framebuffer) {
|
||||
AttachmentArray attachments; // NOLINT
|
||||
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
|
||||
GLsizei attachmentCount = getAttachments(attachments, rt, discardFlags);
|
||||
if (attachmentCount) {
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
@@ -2403,10 +2360,8 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
mRenderPassDepthWrite = any(clearFlags & TargetBufferFlags::DEPTH);
|
||||
mRenderPassStencilWrite = any(clearFlags & TargetBufferFlags::STENCIL);
|
||||
|
||||
static_assert(sizeof(GLsizei) >= sizeof(uint32_t));
|
||||
gl.viewport(params.viewport.left, params.viewport.bottom,
|
||||
(GLsizei)std::min(uint32_t(std::numeric_limits<int32_t>::max()), params.viewport.width),
|
||||
(GLsizei)std::min(uint32_t(std::numeric_limits<int32_t>::max()), params.viewport.height));
|
||||
(GLsizei)params.viewport.width, (GLsizei)params.viewport.height);
|
||||
|
||||
gl.depthRange(params.depthRange.near, params.depthRange.far);
|
||||
|
||||
@@ -2456,7 +2411,7 @@ void OpenGLDriver::endRenderPass(int) {
|
||||
// we wouldn't have to bind the framebuffer if we had glInvalidateNamedFramebuffer()
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
AttachmentArray attachments; // NOLINT
|
||||
GLsizei const attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
|
||||
GLsizei attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
|
||||
if (attachmentCount) {
|
||||
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
@@ -2484,7 +2439,7 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
|
||||
assert_invariant(rt->gl.fbo_read);
|
||||
auto& gl = mContext;
|
||||
const TargetBufferFlags resolve = rt->gl.resolve & ~discardFlags;
|
||||
GLbitfield const mask = getAttachmentBitfield(resolve);
|
||||
GLbitfield mask = getAttachmentBitfield(resolve);
|
||||
if (UTILS_UNLIKELY(mask)) {
|
||||
|
||||
// we can only resolve COLOR0 at the moment
|
||||
@@ -2575,6 +2530,40 @@ void OpenGLDriver::setScissor(Viewport const& scissor) noexcept {
|
||||
gl.enable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
// Binds the external image stashed in the associated stream.
|
||||
//
|
||||
// updateStreamAcquired() and setAcquiredImage() are both called from on the application's thread
|
||||
// and therefore do not require synchronization. The former is always called immediately before
|
||||
// beginFrame, the latter is called by the user from anywhere outside beginFrame / endFrame.
|
||||
void OpenGLDriver::updateStreamAcquired(GLTexture* gltexture, DriverApi* driver) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
GLStream* glstream = static_cast<GLStream*>(gltexture->hwStream); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
|
||||
assert_invariant(glstream);
|
||||
assert_invariant(glstream->streamType == StreamType::ACQUIRED);
|
||||
|
||||
// If there's no pending image, do nothing. Note that GL_OES_EGL_image does not let you pass
|
||||
// NULL to glEGLImageTargetTexture2DOES, and there is no concept of "detaching" an EGLimage from
|
||||
// a texture.
|
||||
if (glstream->user_thread.pending.image == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
AcquiredImage previousImage = glstream->user_thread.acquired;
|
||||
glstream->user_thread.acquired = glstream->user_thread.pending;
|
||||
glstream->user_thread.pending = { nullptr };
|
||||
|
||||
// Bind the stashed EGLImage to its corresponding GL texture as soon as we start making the GL
|
||||
// calls for the upcoming frame.
|
||||
void* image = glstream->user_thread.acquired.image;
|
||||
driver->queueCommand([this, gltexture, image, previousImage]() {
|
||||
setExternalTexture(gltexture, image);
|
||||
if (previousImage.image) {
|
||||
scheduleRelease(AcquiredImage(previousImage));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Setting rendering state
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -2598,7 +2587,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
|
||||
|
||||
GLBufferObject* ub = handle_cast<GLBufferObject *>(ubh);
|
||||
|
||||
GLenum const target = GLUtils::getBufferBindingType(bindingType);
|
||||
GLenum target = GLUtils::getBufferBindingType(bindingType);
|
||||
|
||||
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
|
||||
ub->gl.binding == target);
|
||||
@@ -2611,7 +2600,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
|
||||
void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
GLenum const target = GLUtils::getBufferBindingType(bindingType);
|
||||
GLenum target = GLUtils::getBufferBindingType(bindingType);
|
||||
gl.bindBufferRange(target, GLuint(index), 0, 0, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
@@ -2642,7 +2631,7 @@ GLuint OpenGLDriver::getSamplerSlow(SamplerParams params) const noexcept {
|
||||
auto& gl = mContext;
|
||||
if (gl.ext.EXT_texture_filter_anisotropic &&
|
||||
!gl.bugs.texture_filter_anisotropic_broken_on_sampler) {
|
||||
GLfloat const anisotropy = float(1u << params.anisotropyLog2);
|
||||
GLfloat anisotropy = float(1u << params.anisotropyLog2);
|
||||
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
std::min(gl.gets.max_anisotropy, anisotropy));
|
||||
}
|
||||
@@ -2703,8 +2692,8 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
|
||||
GLenum const glFormat = getFormat(p.format);
|
||||
GLenum const glType = getType(p.type);
|
||||
GLenum glFormat = getFormat(p.format);
|
||||
GLenum glType = getType(p.type);
|
||||
|
||||
gl.pixelStore(GL_PACK_ROW_LENGTH, (GLint)p.stride);
|
||||
gl.pixelStore(GL_PACK_ALIGNMENT, (GLint)p.alignment);
|
||||
@@ -2719,7 +2708,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
* +--------------------+
|
||||
* | | stride alignment
|
||||
* | | ----------------------->-->
|
||||
* | | +----------------------+--+ low addresses
|
||||
* | | +----------------------+--+ low adresses
|
||||
* | | | | | |
|
||||
* | w | | | bottom | |
|
||||
* | <---------> | | V | |
|
||||
@@ -2729,7 +2718,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
* +------>| v | | +---->| | | |
|
||||
* | +.........+ | | +---------+ | |
|
||||
* | ^ | | | |
|
||||
* | y | | +----------------------+--+ high addresses
|
||||
* | y | | +----------------------+--+ high adresses
|
||||
* +------------+-------+
|
||||
* Image is "flipped" vertically
|
||||
* "bottom" is from the "top" (low addresses)
|
||||
@@ -2755,8 +2744,9 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
auto& gl = mContext;
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
void* vaddr = nullptr;
|
||||
std::unique_ptr<uint8_t> clientBuffer;
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
std::unique_ptr<uint8_t> clientBuffer = std::make_unique<uint8_t>(p.size);
|
||||
clientBuffer.reset(new uint8_t(p.size));
|
||||
glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, p.size, clientBuffer.get());
|
||||
vaddr = clientBuffer.get();
|
||||
#else
|
||||
@@ -2764,10 +2754,10 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
#endif
|
||||
if (vaddr) {
|
||||
// now we need to flip the buffer vertically to match our API
|
||||
size_t const stride = p.stride ? p.stride : width;
|
||||
size_t const bpp = PixelBufferDescriptor::computeDataSize(
|
||||
size_t stride = p.stride ? p.stride : width;
|
||||
size_t bpp = PixelBufferDescriptor::computeDataSize(
|
||||
p.format, p.type, 1, 1, 1);
|
||||
size_t const bpr = PixelBufferDescriptor::computeDataSize(
|
||||
size_t bpr = PixelBufferDescriptor::computeDataSize(
|
||||
p.format, p.type, stride, 1, p.alignment);
|
||||
char const* head = (char const*)vaddr + p.left * bpp + bpr * p.top;
|
||||
char* tail = (char*)p.buffer + p.left * bpp + bpr * (p.top + height - 1);
|
||||
@@ -2854,14 +2844,14 @@ void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
|
||||
auto& v = mGpuCommandCompleteOps;
|
||||
auto it = v.begin();
|
||||
while (it != v.end()) {
|
||||
GLenum const status = glClientWaitSync(it->first, 0, 0);
|
||||
GLenum status = glClientWaitSync(it->first, 0, 0);
|
||||
if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED) {
|
||||
it->second();
|
||||
glDeleteSync(it->first);
|
||||
it = v.erase(it);
|
||||
} else if (UTILS_UNLIKELY(status == GL_WAIT_FAILED)) {
|
||||
// This should never happen, but is very problematic if it does, as we might leak
|
||||
// some data depending on what the callback does. However, we clean up our own state.
|
||||
// some data depending on what the callback does. However, we clean-up our own state.
|
||||
glDeleteSync(it->first);
|
||||
it = v.erase(it);
|
||||
} else {
|
||||
@@ -2911,15 +2901,13 @@ void OpenGLDriver::tick(int) {
|
||||
executeEveryNowAndThenOps();
|
||||
}
|
||||
|
||||
void OpenGLDriver::beginFrame(
|
||||
UTILS_UNUSED int64_t monotonic_clock_ns,
|
||||
UTILS_UNUSED uint32_t frameId) {
|
||||
void OpenGLDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
insertEventMarker("beginFrame");
|
||||
if (UTILS_UNLIKELY(!mTexturesWithStreamsAttached.empty())) {
|
||||
if (UTILS_UNLIKELY(!mExternalStreams.empty())) {
|
||||
OpenGLPlatform& platform = mPlatform;
|
||||
for (GLTexture const* t : mTexturesWithStreamsAttached) {
|
||||
for (GLTexture const* t : mExternalStreams) {
|
||||
assert_invariant(t && t->hwStream);
|
||||
if (t->hwStream->streamType == StreamType::NATIVE) {
|
||||
assert_invariant(t->hwStream->stream);
|
||||
@@ -2947,7 +2935,7 @@ void OpenGLDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
mPlatform.setPresentationTime(monotonic_clock_ns);
|
||||
}
|
||||
|
||||
void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
|
||||
void OpenGLDriver::endFrame(uint32_t frameId) {
|
||||
DEBUG_MARKER()
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// WebGL builds are single-threaded so users might manipulate various GL state after we're
|
||||
@@ -3050,7 +3038,7 @@ void OpenGLDriver::blit(TargetBufferFlags buffers,
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
|
||||
GLbitfield const mask = getAttachmentBitfield(buffers);
|
||||
GLbitfield mask = getAttachmentBitfield(buffers);
|
||||
if (mask) {
|
||||
GLenum glFilterMode = (filter == SamplerMagFilter::NEAREST) ? GL_NEAREST : GL_LINEAR;
|
||||
if (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) {
|
||||
@@ -3063,7 +3051,7 @@ void OpenGLDriver::blit(TargetBufferFlags buffers,
|
||||
// buffer -- this should produce the same output that if we copied from the resolved
|
||||
// texture. EXT_multisampled_render_to_texture seems to allow both behaviours, and this
|
||||
// is an emulation of that. We cannot use the resolved texture easily because it's not
|
||||
// actually attached to the RenderTarget. Another implementation would be to do a
|
||||
// actually attached to the this RenderTarget. Another implementation would be to do a
|
||||
// reverse-resolve, but that wouldn't buy us anything.
|
||||
GLRenderTarget const* s = handle_cast<GLRenderTarget const*>(src);
|
||||
GLRenderTarget const* d = handle_cast<GLRenderTarget const*>(dst);
|
||||
|
||||
@@ -48,6 +48,7 @@ class PixelBufferDescriptor;
|
||||
struct TargetBufferInfo;
|
||||
|
||||
class OpenGLProgram;
|
||||
class OpenGLBlitter;
|
||||
class OpenGLTimerQueryInterface;
|
||||
|
||||
class OpenGLDriver final : public DriverBase {
|
||||
@@ -151,6 +152,8 @@ public:
|
||||
struct GLStream : public HwStream {
|
||||
using HwStream::HwStream;
|
||||
struct Info {
|
||||
// storage for the read/write textures below
|
||||
Platform::ExternalTexture* ets = nullptr;
|
||||
GLuint width = 0;
|
||||
GLuint height = 0;
|
||||
};
|
||||
@@ -344,12 +347,7 @@ private:
|
||||
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
|
||||
|
||||
mutable tsl::robin_map<uint32_t, GLuint> mSamplerMap;
|
||||
|
||||
// this must be accessed from the driver thread only
|
||||
std::vector<GLTexture*> mTexturesWithStreamsAttached;
|
||||
|
||||
// the must be accessed from the user thread only
|
||||
std::vector<GLStream*> mStreamsWithPendingAquiredImage;
|
||||
mutable std::vector<GLTexture*> mExternalStreams;
|
||||
|
||||
void attachStream(GLTexture* t, GLStream* stream) noexcept;
|
||||
void detachStream(GLTexture* t) noexcept;
|
||||
@@ -357,6 +355,7 @@ private:
|
||||
|
||||
OpenGLPlatform& mPlatform;
|
||||
|
||||
void updateStreamAcquired(GLTexture* t, DriverApi* driver) noexcept;
|
||||
void updateTextureLodRange(GLTexture* texture, int8_t targetLevel) noexcept;
|
||||
|
||||
void setExternalTexture(GLTexture* t, void* image);
|
||||
|
||||
@@ -81,7 +81,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
|
||||
}
|
||||
const GLuint program = gl.program;
|
||||
UTILS_NOUNROLL
|
||||
for (GLuint const shader: gl.shaders) {
|
||||
for (GLuint shader: gl.shaders) {
|
||||
if (shader) {
|
||||
if (program) {
|
||||
glDetachShader(program, shader);
|
||||
@@ -103,18 +103,18 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
GLuint shaderIds[Program::SHADER_TYPE_COUNT],
|
||||
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
||||
std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
|
||||
|
||||
std::string specializationConstantString;
|
||||
std::string specificationConstantString;
|
||||
for (auto const& sc : specializationConstants) {
|
||||
specializationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
|
||||
specializationConstantString += std::visit([](auto&& arg) {
|
||||
specificationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
|
||||
specificationConstantString += std::visit([](auto&& arg) {
|
||||
return std::to_string(arg);
|
||||
}, sc.value);
|
||||
specializationConstantString += '\n';
|
||||
specificationConstantString += '\n';
|
||||
}
|
||||
if (!specializationConstantString.empty()) {
|
||||
specializationConstantString += '\n';
|
||||
if (!specificationConstantString.empty()) {
|
||||
specificationConstantString += '\n';
|
||||
}
|
||||
|
||||
// build all shaders
|
||||
@@ -132,7 +132,7 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderBlob& shader = shadersSource[i];
|
||||
|
||||
// remove GOOGLE_cpp_style_line_directive
|
||||
std::string_view const source = process_GOOGLE_cpp_style_line_directive(context,
|
||||
std::string_view source = process_GOOGLE_cpp_style_line_directive(context,
|
||||
reinterpret_cast<char*>(shader.data()), shader.size());
|
||||
|
||||
// add support for ARB_shading_language_packing if needed
|
||||
@@ -143,19 +143,19 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
|
||||
|
||||
const std::array<const char*, 4> sources = {
|
||||
prolog.data(),
|
||||
specializationConstantString.c_str(),
|
||||
specificationConstantString.c_str(),
|
||||
packingFunctions.data(),
|
||||
body.data()
|
||||
};
|
||||
|
||||
const std::array<GLint, 4> lengths = {
|
||||
(GLint)prolog.length(),
|
||||
(GLint)specializationConstantString.length(),
|
||||
(GLint)specificationConstantString.length(),
|
||||
(GLint)packingFunctions.length(),
|
||||
(GLint)body.length() - 1 // null terminated
|
||||
};
|
||||
|
||||
GLuint const shaderId = glCreateShader(glShaderType);
|
||||
GLuint shaderId = glCreateShader(glShaderType);
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
glCompileShader(shaderId);
|
||||
|
||||
@@ -246,8 +246,8 @@ std::array<std::string_view, 2> OpenGLProgram::splitShaderSource(std::string_vie
|
||||
auto eol = source.find('\n', pos) + 1;
|
||||
assert_invariant(eol != std::string_view::npos);
|
||||
|
||||
std::string_view const version = source.substr(start, eol - start);
|
||||
std::string_view const body = source.substr(version.length(), source.length() - version.length());
|
||||
std::string_view version = source.substr(start, eol - start);
|
||||
std::string_view body = source.substr(version.length(), source.length() - version.length());
|
||||
return { version, body };
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ std::array<std::string_view, 2> OpenGLProgram::splitShaderSource(std::string_vie
|
||||
* program itself is valid).
|
||||
*/
|
||||
GLuint OpenGLProgram::linkProgram(const GLuint shaderIds[Program::SHADER_TYPE_COUNT]) noexcept {
|
||||
GLuint const program = glCreateProgram();
|
||||
GLuint program = glCreateProgram();
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
if (shaderIds[i]) {
|
||||
glAttachShader(program, shaderIds[i]);
|
||||
@@ -286,15 +286,13 @@ bool OpenGLProgram::checkProgramStatus(const char* name,
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
const ShaderStage type = static_cast<ShaderStage>(i);
|
||||
const GLuint shader = shaderIds[i];
|
||||
if (shader) {
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (status != GL_TRUE) {
|
||||
logCompilationError(slog.e, type, name, shader, shaderSourceCode[i]);
|
||||
}
|
||||
glDetachShader(program, shader);
|
||||
glDeleteShader(shader);
|
||||
shaderIds[i] = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (status != GL_TRUE) {
|
||||
logCompilationError(slog.e, type, name, shader, shaderSourceCode[i]);
|
||||
}
|
||||
glDetachShader(program, shader);
|
||||
glDeleteShader(shader);
|
||||
shaderIds[i] = 0;
|
||||
}
|
||||
// log the link error as well
|
||||
logProgramLinkError(slog.e, name, program);
|
||||
@@ -342,9 +340,9 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
for (GLuint binding = 0, n = lazyInitializationData.uniformBlockInfo.size(); binding < n; binding++) {
|
||||
auto const& name = lazyInitializationData.uniformBlockInfo[binding];
|
||||
if (!name.empty()) {
|
||||
GLuint const index = glGetUniformBlockIndex(program, name.c_str());
|
||||
if (index != GL_INVALID_INDEX) {
|
||||
glUniformBlockBinding(program, index, binding);
|
||||
GLint index = glGetUniformBlockIndex(program, name.c_str());
|
||||
if (index >= 0) {
|
||||
glUniformBlockBinding(program, GLuint(index), binding);
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
@@ -369,7 +367,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
UTILS_NOUNROLL
|
||||
for (const Program::Sampler& sampler: samplers) {
|
||||
// find its location and associate a TMU to it
|
||||
GLint const loc = glGetUniformLocation(program, sampler.name.c_str());
|
||||
GLint loc = glGetUniformLocation(program, sampler.name.c_str());
|
||||
if (loc >= 0) {
|
||||
// this can fail if the program doesn't use this sampler
|
||||
glUniform1i(loc, tmu);
|
||||
@@ -404,7 +402,7 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
|
||||
assert_invariant(sb);
|
||||
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
|
||||
const GLTexture* const t = sb->textureUnitEntries[j].texture;
|
||||
GLuint const s = sb->textureUnitEntries[j].sampler;
|
||||
GLuint s = sb->textureUnitEntries[j].sampler;
|
||||
if (t) { // program may not use all samplers of sampler group
|
||||
if (UTILS_UNLIKELY(t->gl.fence)) {
|
||||
glWaitSync(t->gl.fence, 0, GL_TIMEOUT_IGNORED);
|
||||
@@ -421,8 +419,7 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
|
||||
|
||||
UTILS_NOINLINE
|
||||
void logCompilationError(io::ostream& out, ShaderStage shaderType,
|
||||
const char* name, GLuint shaderId,
|
||||
UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
||||
const char* name, GLuint shaderId, CString const& sourceCode) noexcept {
|
||||
|
||||
auto to_string = [](ShaderStage type) -> const char* {
|
||||
switch (type) {
|
||||
|
||||
@@ -252,14 +252,7 @@ io::ostream& operator<<(io::ostream& out, TextureFormat format) {
|
||||
CASE(TextureFormat, DXT3_SRGBA)
|
||||
CASE(TextureFormat, DXT5_RGBA)
|
||||
CASE(TextureFormat, DXT5_SRGBA)
|
||||
CASE(TextureFormat, RED_RGTC1)
|
||||
CASE(TextureFormat, SIGNED_RED_RGTC1)
|
||||
CASE(TextureFormat, RED_GREEN_RGTC2)
|
||||
CASE(TextureFormat, SIGNED_RED_GREEN_RGTC2)
|
||||
CASE(TextureFormat, RGB_BPTC_SIGNED_FLOAT)
|
||||
CASE(TextureFormat, RGB_BPTC_UNSIGNED_FLOAT)
|
||||
CASE(TextureFormat, RGBA_BPTC_UNORM)
|
||||
CASE(TextureFormat, SRGB_ALPHA_BPTC_UNORM)
|
||||
CASE(TextureFormat, UNUSED)
|
||||
CASE(TextureFormat, RGBA_ASTC_4x4)
|
||||
CASE(TextureFormat, RGBA_ASTC_5x4)
|
||||
CASE(TextureFormat, RGBA_ASTC_5x5)
|
||||
@@ -288,7 +281,6 @@ io::ostream& operator<<(io::ostream& out, TextureFormat format) {
|
||||
CASE(TextureFormat, SRGB8_ALPHA8_ASTC_10x10)
|
||||
CASE(TextureFormat, SRGB8_ALPHA8_ASTC_12x10)
|
||||
CASE(TextureFormat, SRGB8_ALPHA8_ASTC_12x12)
|
||||
CASE(TextureFormat, UNUSED)
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -31,472 +31,6 @@ using namespace bluevk;
|
||||
|
||||
using utils::FixedCapacityVector;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
#if VK_ENABLE_VALIDATION
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
|
||||
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
|
||||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
||||
utils::slog.e << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
} else {
|
||||
utils::slog.w << "VULKAN WARNING: (" << pLayerPrefix << ") "
|
||||
<< pMessage << utils::io::endl;
|
||||
}
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
|
||||
void* pUserData) {
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
utils::slog.e << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") "
|
||||
<< cbdata->pMessage << utils::io::endl;
|
||||
} else {
|
||||
// TODO: emit best practices warnings about aggressive pipeline barriers.
|
||||
if (strstr(cbdata->pMessage, "ALL_GRAPHICS_BIT") || strstr(cbdata->pMessage, "ALL_COMMANDS_BIT")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
utils::slog.w << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") "
|
||||
<< cbdata->pMessage << utils::io::endl;
|
||||
}
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
|
||||
// Print some driver or MoltenVK information if it is available.
|
||||
if (vkGetPhysicalDeviceProperties2KHR) {
|
||||
VkPhysicalDeviceDriverProperties driverProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
|
||||
};
|
||||
VkPhysicalDeviceProperties2 physicalDeviceProperties2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &driverProperties,
|
||||
};
|
||||
vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2);
|
||||
utils::slog.i << "Vulkan device driver: "
|
||||
<< driverProperties.driverName << " "
|
||||
<< driverProperties.driverInfo << utils::io::endl;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceProperties deviceProperties;
|
||||
vkGetPhysicalDeviceProperties(device, &deviceProperties);
|
||||
|
||||
// Print out some properties of the GPU for diagnostic purposes.
|
||||
//
|
||||
// Ideally, the vendors register their vendor ID's with Khronos so that apps can make an
|
||||
// id => string mapping. However, in practice this hasn't happened. At the time of this
|
||||
// writing the gpuinfo database has the following ID's:
|
||||
//
|
||||
// 0x1002 - AMD
|
||||
// 0x1010 - ImgTec
|
||||
// 0x10DE - NVIDIA
|
||||
// 0x13B5 - ARM
|
||||
// 0x106B - APPLE
|
||||
// 0x5143 - Qualcomm
|
||||
// 0x8086 - INTEL
|
||||
//
|
||||
// Since we don't have any vendor-specific workarounds yet, there's no need to make this
|
||||
// mapping in code. The "deviceName" string informally reveals the marketing name for the
|
||||
// GPU. (e.g., Quadro)
|
||||
const uint32_t driverVersion = deviceProperties.driverVersion;
|
||||
const uint32_t vendorID = deviceProperties.vendorID;
|
||||
const uint32_t deviceID = deviceProperties.deviceID;
|
||||
const int major = VK_VERSION_MAJOR(deviceProperties.apiVersion);
|
||||
const int minor = VK_VERSION_MINOR(deviceProperties.apiVersion);
|
||||
|
||||
const FixedCapacityVector<VkPhysicalDevice> physicalDevices = filament::backend::enumerate(
|
||||
vkEnumeratePhysicalDevices, instance);
|
||||
|
||||
utils::slog.i << "Selected physical device '"
|
||||
<< deviceProperties.deviceName
|
||||
<< "' from " << physicalDevices.size() << " physical devices. "
|
||||
<< "(vendor " << utils::io::hex << vendorID << ", "
|
||||
<< "device " << deviceID << ", "
|
||||
<< "driver " << driverVersion << ", "
|
||||
<< utils::io::dec << "api " << major << "." << minor << ")"
|
||||
<< utils::io::endl;
|
||||
}
|
||||
|
||||
void printDepthFormats(VkPhysicalDevice device) {
|
||||
// For diagnostic purposes, print useful information about available depth formats.
|
||||
// Note that Vulkan is more constrained than OpenGL ES 3.1 in this area.
|
||||
if constexpr (VK_ENABLE_VALIDATION && FILAMENT_VULKAN_VERBOSE) {
|
||||
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
||||
utils::slog.i << "Sampleable depth formats: ";
|
||||
for (VkFormat format = (VkFormat) 1;;) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
||||
if ((props.optimalTilingFeatures & required) == required) {
|
||||
utils::slog.i << format << " ";
|
||||
}
|
||||
if (format == VK_FORMAT_ASTC_12x12_SRGB_BLOCK) {
|
||||
utils::slog.i << utils::io::endl;
|
||||
break;
|
||||
}
|
||||
format = (VkFormat) (1 + (int) format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool enableLayers(VkInstanceCreateInfo* instanceCreateInfo) {
|
||||
const std::string_view DESIRED_LAYERS[] = {
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
#if FILAMENT_VULKAN_DUMP_API
|
||||
"VK_LAYER_LUNARG_api_dump",
|
||||
#endif
|
||||
#if defined(ENABLE_RENDERDOC)
|
||||
"VK_LAYER_RENDERDOC_Capture",
|
||||
#endif
|
||||
};
|
||||
|
||||
constexpr size_t kMaxEnabledLayersCount = sizeof(DESIRED_LAYERS) / sizeof(DESIRED_LAYERS[0]);
|
||||
|
||||
const FixedCapacityVector<VkLayerProperties> availableLayers = filament::backend::enumerate(
|
||||
vkEnumerateInstanceLayerProperties);
|
||||
|
||||
auto enabledLayers = FixedCapacityVector<const char*>::with_capacity(kMaxEnabledLayersCount);
|
||||
for (const auto& desired : DESIRED_LAYERS) {
|
||||
for (const VkLayerProperties& layer : availableLayers) {
|
||||
const std::string_view availableLayer(layer.layerName);
|
||||
if (availableLayer == desired) {
|
||||
enabledLayers.push_back(desired.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabledLayers.empty()) {
|
||||
instanceCreateInfo->enabledLayerCount = (uint32_t) enabledLayers.size();
|
||||
instanceCreateInfo->ppEnabledLayerNames = enabledLayers.data();
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
utils::slog.d << "Validation layers are not available; did you set jniLibs in your "
|
||||
<< "gradle file?" << utils::io::endl;
|
||||
#else
|
||||
utils::slog.d << "Validation layer not available; did you install the Vulkan SDK?\n"
|
||||
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
struct InstanceExtensions {
|
||||
bool debugUtilsSupported = false;
|
||||
bool portabilityEnumerationSupported = false;
|
||||
};
|
||||
|
||||
struct DeviceExtensions {
|
||||
bool debugMarkersSupported = false;
|
||||
bool portabilitySubsetSupported = false;
|
||||
bool maintenanceSupported[3] = {};
|
||||
};
|
||||
|
||||
InstanceExtensions getInstanceExtensions() {
|
||||
InstanceExtensions exts;
|
||||
// Determine if the VK_EXT_debug_utils instance extension is available.
|
||||
const FixedCapacityVector<VkExtensionProperties> availableExts = filament::backend::enumerate(
|
||||
vkEnumerateInstanceExtensionProperties,
|
||||
static_cast<const char*>(nullptr) /* pLayerName */);
|
||||
for (const auto& extProps : availableExts) {
|
||||
if (!strcmp(extProps.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
|
||||
exts.debugUtilsSupported = true;
|
||||
} else if (!strcmp(extProps.extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
exts.portabilityEnumerationSupported = true;
|
||||
}
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
DeviceExtensions getDeviceExtensions(VkPhysicalDevice device) {
|
||||
DeviceExtensions exts;
|
||||
|
||||
// Identify supported physical device extensions
|
||||
const FixedCapacityVector<VkExtensionProperties> extensions = filament::backend::enumerate(
|
||||
vkEnumerateDeviceExtensionProperties, device,
|
||||
static_cast<const char*>(nullptr) /* pLayerName */);
|
||||
for (const auto& extension : extensions) {
|
||||
if (!strcmp(extension.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
|
||||
exts.debugMarkersSupported = true;
|
||||
} else if (!strcmp(extension.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
|
||||
exts.portabilitySubsetSupported = true;
|
||||
} else if (!strcmp(extension.extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
|
||||
exts.maintenanceSupported[0] = true;
|
||||
} else if (!strcmp(extension.extensionName, VK_KHR_MAINTENANCE2_EXTENSION_NAME)) {
|
||||
exts.maintenanceSupported[1] = true;
|
||||
} else if (!strcmp(extension.extensionName, VK_KHR_MAINTENANCE3_EXTENSION_NAME)) {
|
||||
exts.maintenanceSupported[2] = true;
|
||||
}
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
VkInstance createInstance(const char* const* ppRequiredExtensions, uint32_t requiredExtensionCount,
|
||||
const InstanceExtensions& instExtensions) {
|
||||
VkInstance instance;
|
||||
VkInstanceCreateInfo instanceCreateInfo = {};
|
||||
bool validationFeaturesSupported = false;
|
||||
|
||||
#if VK_ENABLE_VALIDATION
|
||||
if (enableLayers(&instanceCreateInfo)) {
|
||||
// If layers are supported, Check if VK_EXT_validation_features is supported.
|
||||
const FixedCapacityVector<VkExtensionProperties> availableExts = filament::backend::enumerate(
|
||||
vkEnumerateInstanceExtensionProperties, "VK_LAYER_KHRONOS_validation");
|
||||
for (const auto& extProps : availableExts) {
|
||||
if (!strcmp(extProps.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) {
|
||||
validationFeaturesSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // VK_ENABLE_VALIDATION
|
||||
|
||||
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 5
|
||||
// instance extensions here in the common code. So that's a max of 7.
|
||||
static constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 7;
|
||||
const char* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
|
||||
uint32_t enabledExtensionCount = 0;
|
||||
|
||||
// Request all cross-platform extensions.
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_KHR_SURFACE_EXTENSION_NAME;
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
|
||||
#if VK_ENABLE_VALIDATION && defined(__ANDROID__)
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
|
||||
#endif
|
||||
if (validationFeaturesSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME;
|
||||
}
|
||||
if (instExtensions.debugUtilsSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
||||
}
|
||||
if (instExtensions.portabilityEnumerationSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME;
|
||||
}
|
||||
|
||||
// Request platform-specific extensions.
|
||||
for (uint32_t i = 0; i < requiredExtensionCount; ++i) {
|
||||
assert_invariant(enabledExtensionCount < MAX_INSTANCE_EXTENSION_COUNT);
|
||||
ppEnabledExtensions[enabledExtensionCount++] = ppRequiredExtensions[i];
|
||||
}
|
||||
|
||||
// Create the Vulkan instance.
|
||||
VkApplicationInfo appInfo = {};
|
||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
appInfo.apiVersion = VK_MAKE_API_VERSION(0,
|
||||
VK_REQUIRED_VERSION_MAJOR, VK_REQUIRED_VERSION_MINOR, 0);
|
||||
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instanceCreateInfo.pApplicationInfo = &appInfo;
|
||||
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
|
||||
instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions;
|
||||
if (instExtensions.portabilityEnumerationSupported) {
|
||||
instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
|
||||
VkValidationFeaturesEXT features = {};
|
||||
VkValidationFeatureEnableEXT enables[] = {
|
||||
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
|
||||
// TODO: Enable synchronization validation.
|
||||
// VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
|
||||
};
|
||||
if (validationFeaturesSupported) {
|
||||
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
|
||||
features.enabledValidationFeatureCount = sizeof(enables) / sizeof(enables[0]);
|
||||
features.pEnabledValidationFeatures = enables;
|
||||
instanceCreateInfo.pNext = &features;
|
||||
}
|
||||
|
||||
VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &instance);
|
||||
#ifndef NDEBUG
|
||||
if (result != VK_SUCCESS) {
|
||||
utils::slog.e << "Unable to create instance: " << result << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan instance.");
|
||||
return instance;
|
||||
}
|
||||
|
||||
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceFeatures& features,
|
||||
uint32_t graphicsQueueFamilyIndex, const DeviceExtensions& deviceExtensions) {
|
||||
VkDevice device;
|
||||
VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {};
|
||||
const float queuePriority[] = {1.0f};
|
||||
VkDeviceCreateInfo deviceCreateInfo = {};
|
||||
FixedCapacityVector<const char*> deviceExtensionNames;
|
||||
deviceExtensionNames.reserve(6);
|
||||
deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
if (deviceExtensions.debugMarkersSupported) {
|
||||
deviceExtensionNames.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
}
|
||||
if (deviceExtensions.portabilitySubsetSupported) {
|
||||
deviceExtensionNames.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
|
||||
}
|
||||
if (deviceExtensions.maintenanceSupported[0]) {
|
||||
deviceExtensionNames.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
||||
}
|
||||
if (deviceExtensions.maintenanceSupported[1]) {
|
||||
deviceExtensionNames.push_back(VK_KHR_MAINTENANCE2_EXTENSION_NAME);
|
||||
}
|
||||
if (deviceExtensions.maintenanceSupported[2]) {
|
||||
deviceExtensionNames.push_back(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
|
||||
}
|
||||
deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
deviceQueueCreateInfo->queueFamilyIndex = graphicsQueueFamilyIndex;
|
||||
deviceQueueCreateInfo->queueCount = 1;
|
||||
deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0];
|
||||
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
deviceCreateInfo.queueCreateInfoCount = 1;
|
||||
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
|
||||
|
||||
// We could simply enable all supported features, but since that may have performance
|
||||
// consequences let's just enable the features we need.
|
||||
VkPhysicalDeviceFeatures enabledFeatures {
|
||||
.samplerAnisotropy = features.samplerAnisotropy,
|
||||
.textureCompressionETC2 = features.textureCompressionETC2,
|
||||
.textureCompressionBC = features.textureCompressionBC,
|
||||
};
|
||||
|
||||
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
||||
deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensionNames.size();
|
||||
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames.data();
|
||||
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.imageViewFormatSwizzle = VK_TRUE,
|
||||
.mutableComparisonSamplers = VK_TRUE,
|
||||
};
|
||||
if (deviceExtensions.portabilitySubsetSupported) {
|
||||
deviceCreateInfo.pNext = &portability;
|
||||
}
|
||||
|
||||
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error.");
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
// This method is used to enable/disable extensions based on external factors (i.e.
|
||||
// driver/device workarounds).
|
||||
void pruneExtensions(VkPhysicalDevice device, InstanceExtensions* instExtensions,
|
||||
DeviceExtensions* deviceExtensions) {
|
||||
if (vkGetPhysicalDeviceProperties2KHR) {
|
||||
char* driverInfo = nullptr;
|
||||
VkPhysicalDeviceDriverProperties driverProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
|
||||
};
|
||||
VkPhysicalDeviceProperties2 physicalDeviceProperties2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &driverProperties,
|
||||
};
|
||||
vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2);
|
||||
driverInfo = driverProperties.driverInfo;
|
||||
|
||||
if (instExtensions->debugUtilsSupported) {
|
||||
// Workaround for Mesa drivers. See issue #6192
|
||||
if (driverInfo && strstr(driverInfo, "Mesa")) {
|
||||
instExtensions->debugUtilsSupported = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// debugUtils and debugMarkers extensions are used mutually exclusively.
|
||||
if (instExtensions->debugUtilsSupported && deviceExtensions->debugMarkersSupported) {
|
||||
deviceExtensions->debugMarkersSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
FixedCapacityVector<VkQueueFamilyProperties> getPhysicalDeviceQueueFamilyPropertiesHelper(
|
||||
VkPhysicalDevice device) {
|
||||
uint32_t queueFamiliesCount;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, nullptr);
|
||||
FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties(queueFamiliesCount);
|
||||
if (queueFamiliesCount > 0) {
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount,
|
||||
queueFamiliesProperties.data());
|
||||
}
|
||||
return queueFamiliesProperties;
|
||||
}
|
||||
|
||||
VkPhysicalDevice selectPhysicalDevice(VkInstance instance) {
|
||||
const FixedCapacityVector<VkPhysicalDevice> physicalDevices = filament::backend::enumerate(
|
||||
vkEnumeratePhysicalDevices, instance);
|
||||
for (const auto& candidateDevice : physicalDevices) {
|
||||
VkPhysicalDeviceProperties targetDeviceProperties;
|
||||
vkGetPhysicalDeviceProperties(candidateDevice, &targetDeviceProperties);
|
||||
|
||||
const int major = VK_VERSION_MAJOR(targetDeviceProperties.apiVersion);
|
||||
const int minor = VK_VERSION_MINOR(targetDeviceProperties.apiVersion);
|
||||
|
||||
// Does the device support the required Vulkan level?
|
||||
if (major < VK_REQUIRED_VERSION_MAJOR) {
|
||||
continue;
|
||||
}
|
||||
if (major == VK_REQUIRED_VERSION_MAJOR && minor < VK_REQUIRED_VERSION_MINOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Does the device have any command queues that support graphics?
|
||||
// In theory, we should also ensure that the device supports presentation of our
|
||||
// particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement.
|
||||
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties =
|
||||
getPhysicalDeviceQueueFamilyPropertiesHelper(candidateDevice);
|
||||
bool foundGraphicsQueue = false;
|
||||
for (const auto& props : queueFamiliesProperties) {
|
||||
if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
foundGraphicsQueue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundGraphicsQueue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Does the device support the VK_KHR_swapchain extension?
|
||||
const FixedCapacityVector<VkExtensionProperties> extensions = filament::backend::enumerate(
|
||||
vkEnumerateDeviceExtensionProperties, candidateDevice,
|
||||
static_cast<const char*>(nullptr) /* pLayerName */);
|
||||
bool supportsSwapchain = false;
|
||||
for (const auto& extension : extensions) {
|
||||
if (!strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
|
||||
supportsSwapchain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!supportsSwapchain) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return candidateDevice;
|
||||
}
|
||||
|
||||
PANIC_POSTCONDITION("Unable to find suitable device.");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkFormat findSupportedFormat(VkPhysicalDevice device, utils::Slice<VkFormat> candidates,
|
||||
VkImageTiling tiling, VkFormatFeatureFlags features) {
|
||||
for (VkFormat format : candidates) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(device, format, &props);
|
||||
if (tiling == VK_IMAGE_TILING_LINEAR &&
|
||||
(props.linearTilingFeatures & features) == features) {
|
||||
return format;
|
||||
} else if (tiling == VK_IMAGE_TILING_OPTIMAL &&
|
||||
(props.optimalTilingFeatures & features) == features) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VkImage VulkanAttachment::getImage() const {
|
||||
@@ -521,60 +55,188 @@ VkImageView VulkanAttachment::getImageView(VkImageAspectFlags aspect) const {
|
||||
return texture->getAttachmentView(level, layer, aspect);
|
||||
}
|
||||
|
||||
void VulkanContext::initialize(const char* const* ppRequiredExtensions,
|
||||
uint32_t requiredExtensionCount) {
|
||||
InstanceExtensions instExtensions = getInstanceExtensions();
|
||||
instance = createInstance(ppRequiredExtensions, requiredExtensionCount, instExtensions);
|
||||
bluevk::bindInstance(instance);
|
||||
void VulkanContext::selectPhysicalDevice() {
|
||||
uint32_t physicalDeviceCount = 0;
|
||||
VkResult result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS && physicalDeviceCount > 0,
|
||||
"vkEnumeratePhysicalDevices count error.");
|
||||
FixedCapacityVector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
|
||||
result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount,
|
||||
physicalDevices.data());
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkEnumeratePhysicalDevices error.");
|
||||
for (uint32_t i = 0; i < physicalDeviceCount; ++i) {
|
||||
physicalDevice = physicalDevices[i];
|
||||
vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
|
||||
|
||||
physicalDevice = selectPhysicalDevice(instance);
|
||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
|
||||
afterSelectPhysicalDevice();
|
||||
printDeviceInfo(instance, physicalDevice);
|
||||
const int major = VK_VERSION_MAJOR(physicalDeviceProperties.apiVersion);
|
||||
const int minor = VK_VERSION_MINOR(physicalDeviceProperties.apiVersion);
|
||||
|
||||
DeviceExtensions deviceExtensions = getDeviceExtensions(physicalDevice);
|
||||
pruneExtensions(physicalDevice, &instExtensions, &deviceExtensions);
|
||||
|
||||
device = createLogicalDevice(physicalDevice, physicalDeviceFeatures, graphicsQueueFamilyIndex,
|
||||
deviceExtensions);
|
||||
// Initialize graphicsQueue, commandPool, timestamps, allocator,
|
||||
// and command buffer manager (commands).
|
||||
afterCreateLogicalDevice();
|
||||
|
||||
// Store the extension support in the context
|
||||
debugUtilsSupported = instExtensions.debugUtilsSupported;
|
||||
portabilityEnumerationSupported = instExtensions.portabilityEnumerationSupported;
|
||||
debugMarkersSupported = deviceExtensions.debugMarkersSupported;
|
||||
portabilitySubsetSupported = deviceExtensions.portabilitySubsetSupported;
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
maintenanceSupported[j] = deviceExtensions.maintenanceSupported[j];
|
||||
}
|
||||
|
||||
// Initialize finalDepthFormat, debugCallback, and debugMessenger
|
||||
afterCreateInstance();
|
||||
|
||||
printDepthFormats(physicalDevice);
|
||||
}
|
||||
|
||||
void VulkanContext::afterSelectPhysicalDevice() {
|
||||
vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
|
||||
|
||||
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties =
|
||||
getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice);
|
||||
graphicsQueueFamilyIndex = 0xffff;
|
||||
for (uint32_t j = 0; j < queueFamiliesProperties.size(); ++j) {
|
||||
VkQueueFamilyProperties props = queueFamiliesProperties[j];
|
||||
if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
graphicsQueueFamilyIndex = j;
|
||||
// Does the device support the required Vulkan level?
|
||||
if (major < VK_REQUIRED_VERSION_MAJOR) {
|
||||
continue;
|
||||
}
|
||||
if (major == VK_REQUIRED_VERSION_MAJOR && minor < VK_REQUIRED_VERSION_MINOR) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
vkGetPhysicalDeviceFeatures(physicalDevice, &physicalDeviceFeatures);
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
||||
// Does the device have any command queues that support graphics?
|
||||
// In theory, we should also ensure that the device supports presentation of our
|
||||
// particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement.
|
||||
uint32_t queueFamiliesCount;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, nullptr);
|
||||
if (queueFamiliesCount == 0) {
|
||||
continue;
|
||||
}
|
||||
FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties(queueFamiliesCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount,
|
||||
queueFamiliesProperties.data());
|
||||
graphicsQueueFamilyIndex = 0xffff;
|
||||
for (uint32_t j = 0; j < queueFamiliesCount; ++j) {
|
||||
VkQueueFamilyProperties props = queueFamiliesProperties[j];
|
||||
if (props.queueCount == 0) {
|
||||
continue;
|
||||
}
|
||||
if (props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
graphicsQueueFamilyIndex = j;
|
||||
}
|
||||
}
|
||||
if (graphicsQueueFamilyIndex == 0xffff) continue;
|
||||
|
||||
// Does the device support the VK_KHR_swapchain extension?
|
||||
uint32_t extensionCount;
|
||||
result = vkEnumerateDeviceExtensionProperties(physicalDevice, /*pLayerName = */ nullptr,
|
||||
&extensionCount, nullptr);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS,
|
||||
"vkEnumerateDeviceExtensionProperties count error.");
|
||||
FixedCapacityVector<VkExtensionProperties> extensions(extensionCount);
|
||||
result = vkEnumerateDeviceExtensionProperties(physicalDevice, /*pLayerName = */ nullptr,
|
||||
&extensionCount, extensions.data());
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkEnumerateDeviceExtensionProperties error.");
|
||||
bool supportsSwapchain = false;
|
||||
for (uint32_t k = 0; k < extensionCount; ++k) {
|
||||
if (!strcmp(extensions[k].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
|
||||
supportsSwapchain = true;
|
||||
} else if (!strcmp(extensions[k].extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
|
||||
debugMarkersSupported = true;
|
||||
} else if (!strcmp(extensions[k].extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
|
||||
portabilitySubsetSupported = true;
|
||||
} else if (!strcmp(extensions[k].extensionName, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
|
||||
maintenanceSupported[0] = true;
|
||||
} else if (!strcmp(extensions[k].extensionName, VK_KHR_MAINTENANCE2_EXTENSION_NAME)) {
|
||||
maintenanceSupported[1] = true;
|
||||
} else if (!strcmp(extensions[k].extensionName, VK_KHR_MAINTENANCE3_EXTENSION_NAME)) {
|
||||
maintenanceSupported[2] = true;
|
||||
}
|
||||
}
|
||||
if (!supportsSwapchain) continue;
|
||||
|
||||
// Bingo, we finally found a physical device that supports everything we need.
|
||||
vkGetPhysicalDeviceFeatures(physicalDevice, &physicalDeviceFeatures);
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
||||
|
||||
// Print some driver or MoltenVK information if it is available.
|
||||
if (vkGetPhysicalDeviceProperties2KHR) {
|
||||
VkPhysicalDeviceDriverProperties driverProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
|
||||
};
|
||||
VkPhysicalDeviceProperties2 physicalDeviceProperties2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &driverProperties,
|
||||
};
|
||||
vkGetPhysicalDeviceProperties2KHR(physicalDevice, &physicalDeviceProperties2);
|
||||
utils::slog.i << "Vulkan device driver: "
|
||||
<< driverProperties.driverName << " "
|
||||
<< driverProperties.driverInfo << utils::io::endl;
|
||||
}
|
||||
|
||||
// Print out some properties of the GPU for diagnostic purposes.
|
||||
//
|
||||
// Ideally, the vendors register their vendor ID's with Khronos so that apps can make an
|
||||
// id => string mapping. However, in practice this hasn't happened. At the time of this
|
||||
// writing the gpuinfo database has the following ID's:
|
||||
//
|
||||
// 0x1002 - AMD
|
||||
// 0x1010 - ImgTec
|
||||
// 0x10DE - NVIDIA
|
||||
// 0x13B5 - ARM
|
||||
// 0x106B - APPLE
|
||||
// 0x5143 - Qualcomm
|
||||
// 0x8086 - INTEL
|
||||
//
|
||||
// Since we don't have any vendor-specific workarounds yet, there's no need to make this
|
||||
// mapping in code. The "deviceName" string informally reveals the marketing name for the
|
||||
// GPU. (e.g., Quadro)
|
||||
const uint32_t driverVersion = physicalDeviceProperties.driverVersion;
|
||||
const uint32_t vendorID = physicalDeviceProperties.vendorID;
|
||||
const uint32_t deviceID = physicalDeviceProperties.deviceID;
|
||||
utils::slog.i << "Selected physical device '"
|
||||
<< physicalDeviceProperties.deviceName
|
||||
<< "' from " << physicalDeviceCount << " physical devices. "
|
||||
<< "(vendor " << utils::io::hex << vendorID << ", "
|
||||
<< "device " << deviceID << ", "
|
||||
<< "driver " << driverVersion << ", "
|
||||
<< utils::io::dec << "api " << major << "." << minor << ")"
|
||||
<< utils::io::endl;
|
||||
return;
|
||||
}
|
||||
PANIC_POSTCONDITION("Unable to find suitable device.");
|
||||
}
|
||||
|
||||
void VulkanContext::afterCreateLogicalDevice() {
|
||||
void VulkanContext::createLogicalDevice() {
|
||||
VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {};
|
||||
const float queuePriority[] = {1.0f};
|
||||
VkDeviceCreateInfo deviceCreateInfo = {};
|
||||
FixedCapacityVector<const char*> deviceExtensionNames;
|
||||
deviceExtensionNames.reserve(6);
|
||||
deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
if (debugMarkersSupported && !debugUtilsSupported) {
|
||||
deviceExtensionNames.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
}
|
||||
if (portabilitySubsetSupported) {
|
||||
deviceExtensionNames.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
|
||||
}
|
||||
if (maintenanceSupported[0]) {
|
||||
deviceExtensionNames.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
||||
}
|
||||
if (maintenanceSupported[1]) {
|
||||
deviceExtensionNames.push_back(VK_KHR_MAINTENANCE2_EXTENSION_NAME);
|
||||
}
|
||||
if (maintenanceSupported[2]) {
|
||||
deviceExtensionNames.push_back(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
|
||||
}
|
||||
deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
deviceQueueCreateInfo->queueFamilyIndex = graphicsQueueFamilyIndex;
|
||||
deviceQueueCreateInfo->queueCount = 1;
|
||||
deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0];
|
||||
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
deviceCreateInfo.queueCreateInfoCount = 1;
|
||||
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
|
||||
|
||||
// We could simply enable all supported features, but since that may have performance
|
||||
// consequences let's just enable the features we need.
|
||||
const auto& supportedFeatures = physicalDeviceFeatures;
|
||||
VkPhysicalDeviceFeatures enabledFeatures {
|
||||
.samplerAnisotropy = supportedFeatures.samplerAnisotropy,
|
||||
.textureCompressionETC2 = supportedFeatures.textureCompressionETC2,
|
||||
.textureCompressionBC = supportedFeatures.textureCompressionBC,
|
||||
};
|
||||
|
||||
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
|
||||
deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensionNames.size();
|
||||
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames.data();
|
||||
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
|
||||
.pNext = nullptr,
|
||||
.imageViewFormatSwizzle = VK_TRUE,
|
||||
.mutableComparisonSamplers = VK_TRUE,
|
||||
};
|
||||
if (portabilitySubsetSupported) {
|
||||
deviceCreateInfo.pNext = &portability;
|
||||
}
|
||||
|
||||
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateDevice error.");
|
||||
vkGetDeviceQueue(device, graphicsQueueFamilyIndex, 0,
|
||||
&graphicsQueue);
|
||||
|
||||
@@ -583,7 +245,7 @@ void VulkanContext::afterCreateLogicalDevice() {
|
||||
createInfo.flags =
|
||||
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
|
||||
createInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
|
||||
VkResult result = vkCreateCommandPool(device, &createInfo, VKALLOC, &commandPool);
|
||||
result = vkCreateCommandPool(device, &createInfo, VKALLOC, &commandPool);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateCommandPool error.");
|
||||
|
||||
// Create a timestamp pool large enough to hold a pair of queries for each timer.
|
||||
@@ -634,43 +296,6 @@ void VulkanContext::afterCreateLogicalDevice() {
|
||||
commands = new VulkanCommands(device, graphicsQueueFamilyIndex);
|
||||
}
|
||||
|
||||
void VulkanContext::afterCreateInstance() {
|
||||
#if VK_ENABLE_VALIDATION
|
||||
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback =
|
||||
vkCreateDebugReportCallbackEXT;
|
||||
VkResult result;
|
||||
if (debugUtilsSupported) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
.pfnUserCallback = debugUtilsCallback
|
||||
};
|
||||
result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo, VKALLOC, &debugMessenger);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
|
||||
} else if (createDebugReportCallback) {
|
||||
const VkDebugReportCallbackCreateInfoEXT cbinfo = {
|
||||
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
|
||||
nullptr,
|
||||
VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT,
|
||||
debugReportCallback,
|
||||
nullptr
|
||||
};
|
||||
result = createDebugReportCallback(instance, &cbinfo, VKALLOC, &debugCallback);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Choose a depth format that meets our requirements. Take care not to include stencil formats
|
||||
// just yet, since that would require a corollary change to the "aspect" flags for the VkImage.
|
||||
const VkFormat formats[] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_X8_D24_UNORM_PACK32 };
|
||||
finalDepthFormat = findSupportedFormat(
|
||||
physicalDevice, utils::Slice<VkFormat>(formats, formats + 2), VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
|
||||
}
|
||||
|
||||
uint32_t VulkanContext::selectMemoryType(uint32_t flags, VkFlags reqs) {
|
||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
||||
if (flags & 1) {
|
||||
@@ -684,6 +309,22 @@ uint32_t VulkanContext::selectMemoryType(uint32_t flags, VkFlags reqs) {
|
||||
return (uint32_t) ~0ul;
|
||||
}
|
||||
|
||||
VkFormat VulkanContext::findSupportedFormat(utils::Slice<VkFormat> candidates,
|
||||
VkImageTiling tiling, VkFormatFeatureFlags features) {
|
||||
for (VkFormat format : candidates) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
|
||||
if (tiling == VK_IMAGE_TILING_LINEAR &&
|
||||
(props.linearTilingFeatures & features) == features) {
|
||||
return format;
|
||||
} else if (tiling == VK_IMAGE_TILING_OPTIMAL &&
|
||||
(props.optimalTilingFeatures & features) == features) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
void VulkanContext::createEmptyTexture(VulkanStagePool& stagePool) {
|
||||
emptyTexture = new VulkanTexture(*this, SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 1, 1, 1,
|
||||
|
||||
@@ -62,17 +62,13 @@ struct VulkanRenderPass {
|
||||
// For now we only support a single-device, single-instance scenario. Our concept of "context" is a
|
||||
// bundle of state containing the Device, the Instance, and various globally-useful Vulkan objects.
|
||||
struct VulkanContext {
|
||||
public:
|
||||
void initialize(const char* const* ppRequiredExtensions, uint32_t requiredExtensionCount);
|
||||
void createEmptyTexture(VulkanStagePool& stagePool);
|
||||
void selectPhysicalDevice();
|
||||
void createLogicalDevice();
|
||||
uint32_t selectMemoryType(uint32_t flags, VkFlags reqs);
|
||||
VkFormat findSupportedFormat(utils::Slice<VkFormat> candidates, VkImageTiling tiling,
|
||||
VkFormatFeatureFlags features);
|
||||
void createEmptyTexture(VulkanStagePool& stagePool);
|
||||
|
||||
private:
|
||||
void afterSelectPhysicalDevice();
|
||||
void afterCreateLogicalDevice();
|
||||
void afterCreateInstance();
|
||||
|
||||
public:
|
||||
VkInstance instance;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkPhysicalDeviceProperties physicalDeviceProperties;
|
||||
@@ -97,9 +93,6 @@ public:
|
||||
VmaAllocator allocator;
|
||||
VulkanTexture* emptyTexture = nullptr;
|
||||
VulkanCommands* commands = nullptr;
|
||||
VkDebugReportCallbackEXT debugCallback = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
|
||||
|
||||
std::string currentDebugMarker;
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,45 @@ using utils::FixedCapacityVector;
|
||||
#pragma clang diagnostic ignored "-Wreturn-stack-address"
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
#if VK_ENABLE_VALIDATION
|
||||
|
||||
namespace {
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
|
||||
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
|
||||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
||||
utils::slog.e << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
} else {
|
||||
utils::slog.w << "VULKAN WARNING: (" << pLayerPrefix << ") "
|
||||
<< pMessage << utils::io::endl;
|
||||
}
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
|
||||
void* pUserData) {
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
utils::slog.e << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") "
|
||||
<< cbdata->pMessage << utils::io::endl;
|
||||
} else {
|
||||
// TODO: emit best practices warnings about aggressive pipeline barriers.
|
||||
if (strstr(cbdata->pMessage, "ALL_GRAPHICS_BIT") || strstr(cbdata->pMessage, "ALL_COMMANDS_BIT")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
utils::slog.w << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") "
|
||||
<< cbdata->pMessage << utils::io::endl;
|
||||
}
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* VulkanDriverFactory::create(VulkanPlatform* const platform,
|
||||
@@ -68,12 +107,209 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform,
|
||||
// Load Vulkan entry points.
|
||||
ASSERT_POSTCONDITION(bluevk::initialize(), "BlueVK is unable to load entry points.");
|
||||
|
||||
mContext.initialize(ppRequiredExtensions, requiredExtensionCount);
|
||||
// Determine if the VK_EXT_debug_utils instance extension is available.
|
||||
mContext.debugUtilsSupported = false;
|
||||
uint32_t availableExtsCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &availableExtsCount, nullptr);
|
||||
utils::FixedCapacityVector<VkExtensionProperties> availableExts(availableExtsCount);
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &availableExtsCount, availableExts.data());
|
||||
for (const auto& extProps : availableExts) {
|
||||
if (!strcmp(extProps.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
|
||||
mContext.debugUtilsSupported = true;
|
||||
} else if (!strcmp(extProps.extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
mContext.portabilityEnumerationSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
VkInstanceCreateInfo instanceCreateInfo = {};
|
||||
|
||||
bool validationFeaturesSupported = false;
|
||||
|
||||
#if VK_ENABLE_VALIDATION
|
||||
const std::string_view DESIRED_LAYERS[] = {
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
#if FILAMENT_VULKAN_DUMP_API
|
||||
"VK_LAYER_LUNARG_api_dump",
|
||||
#endif
|
||||
#if defined(ENABLE_RENDERDOC)
|
||||
"VK_LAYER_RENDERDOC_Capture",
|
||||
#endif
|
||||
};
|
||||
|
||||
constexpr size_t kMaxEnabledLayersCount = sizeof(DESIRED_LAYERS) / sizeof(DESIRED_LAYERS[0]);
|
||||
|
||||
uint32_t layerCount;
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||
FixedCapacityVector<VkLayerProperties> availableLayers(layerCount);
|
||||
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
|
||||
auto enabledLayers = FixedCapacityVector<const char*>::with_capacity(kMaxEnabledLayersCount);
|
||||
for (const auto& desired : DESIRED_LAYERS) {
|
||||
for (const VkLayerProperties& layer : availableLayers) {
|
||||
const std::string_view availableLayer(layer.layerName);
|
||||
if (availableLayer == desired) {
|
||||
enabledLayers.push_back(desired.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!enabledLayers.empty()) {
|
||||
instanceCreateInfo.enabledLayerCount = (uint32_t) enabledLayers.size();
|
||||
instanceCreateInfo.ppEnabledLayerNames = enabledLayers.data();
|
||||
|
||||
// Check if VK_EXT_validation_features is supported.
|
||||
uint32_t availableExtsCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &availableExtsCount, nullptr);
|
||||
utils::FixedCapacityVector<VkExtensionProperties> availableExts(availableExtsCount);
|
||||
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &availableExtsCount, availableExts.data());
|
||||
for (const auto& extProps : availableExts) {
|
||||
if (!strcmp(extProps.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) {
|
||||
validationFeaturesSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
#if defined(__ANDROID__)
|
||||
utils::slog.d << "Validation layers are not available; did you set jniLibs in your "
|
||||
<< "gradle file?" << utils::io::endl;
|
||||
#else
|
||||
utils::slog.d << "Validation layer not available; did you install the Vulkan SDK?\n"
|
||||
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
#endif // VK_ENABLE_VALIDATION
|
||||
|
||||
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 5
|
||||
// instance extensions here in the common code. So that's a max of 7.
|
||||
static constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 7;
|
||||
const char* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
|
||||
uint32_t enabledExtensionCount = 0;
|
||||
|
||||
// Request all cross-platform extensions.
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_KHR_SURFACE_EXTENSION_NAME;
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
|
||||
#if VK_ENABLE_VALIDATION
|
||||
#if defined(__ANDROID__)
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
|
||||
#endif
|
||||
if (validationFeaturesSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME;
|
||||
}
|
||||
#endif
|
||||
if (mContext.debugUtilsSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
||||
}
|
||||
if (mContext.portabilityEnumerationSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME;
|
||||
}
|
||||
|
||||
// Request platform-specific extensions.
|
||||
for (uint32_t i = 0; i < requiredExtensionCount; ++i) {
|
||||
assert_invariant(enabledExtensionCount < MAX_INSTANCE_EXTENSION_COUNT);
|
||||
ppEnabledExtensions[enabledExtensionCount++] = ppRequiredExtensions[i];
|
||||
}
|
||||
|
||||
// Create the Vulkan instance.
|
||||
VkApplicationInfo appInfo = {};
|
||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
appInfo.apiVersion = VK_MAKE_API_VERSION(0,
|
||||
VK_REQUIRED_VERSION_MAJOR, VK_REQUIRED_VERSION_MINOR, 0);
|
||||
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instanceCreateInfo.pApplicationInfo = &appInfo;
|
||||
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
|
||||
instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions;
|
||||
if (mContext.portabilityEnumerationSupported) {
|
||||
instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
|
||||
VkValidationFeaturesEXT features = {};
|
||||
VkValidationFeatureEnableEXT enables[] = {
|
||||
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
|
||||
// TODO: Enable synchronization validation.
|
||||
// VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
|
||||
};
|
||||
if (validationFeaturesSupported) {
|
||||
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
|
||||
features.enabledValidationFeatureCount = sizeof(enables) / sizeof(enables[0]);
|
||||
features.pEnabledValidationFeatures = enables;
|
||||
instanceCreateInfo.pNext = &features;
|
||||
}
|
||||
|
||||
VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &mContext.instance);
|
||||
#ifndef NDEBUG
|
||||
if (result != VK_SUCCESS) {
|
||||
utils::slog.e << "Unable to create instance: " << result << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan instance.");
|
||||
bluevk::bindInstance(mContext.instance);
|
||||
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback =
|
||||
vkCreateDebugReportCallbackEXT;
|
||||
|
||||
#if VK_ENABLE_VALIDATION
|
||||
if (mContext.debugUtilsSupported) {
|
||||
VkDebugUtilsMessengerCreateInfoEXT createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
.pfnUserCallback = debugUtilsCallback
|
||||
};
|
||||
result = vkCreateDebugUtilsMessengerEXT(mContext.instance, &createInfo, VKALLOC, &mDebugMessenger);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
|
||||
} else if (createDebugReportCallback) {
|
||||
const VkDebugReportCallbackCreateInfoEXT cbinfo = {
|
||||
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
|
||||
nullptr,
|
||||
VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT,
|
||||
debugReportCallback,
|
||||
nullptr
|
||||
};
|
||||
result = createDebugReportCallback(mContext.instance, &cbinfo, VKALLOC, &mDebugCallback);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize the following fields: physicalDevice, physicalDeviceProperties,
|
||||
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
|
||||
mContext.selectPhysicalDevice();
|
||||
|
||||
// Initialize device, graphicsQueue, and command buffer manager.
|
||||
mContext.createLogicalDevice();
|
||||
|
||||
mContext.createEmptyTexture(mStagePool);
|
||||
|
||||
mContext.commands->setObserver(&mPipelineCache);
|
||||
mPipelineCache.setDevice(mContext.device, mContext.allocator);
|
||||
mPipelineCache.setDummyTexture(mContext.emptyTexture->getPrimaryImageView());
|
||||
|
||||
// Choose a depth format that meets our requirements. Take care not to include stencil formats
|
||||
// just yet, since that would require a corollary change to the "aspect" flags for the VkImage.
|
||||
const VkFormat formats[] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_X8_D24_UNORM_PACK32 };
|
||||
mContext.finalDepthFormat = mContext.findSupportedFormat(
|
||||
utils::Slice<VkFormat>(formats, formats + 2),
|
||||
VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
|
||||
|
||||
// For diagnostic purposes, print useful information about available depth formats.
|
||||
// Note that Vulkan is more constrained than OpenGL ES 3.1 in this area.
|
||||
if constexpr (VK_ENABLE_VALIDATION && FILAMENT_VULKAN_VERBOSE) {
|
||||
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
||||
utils::slog.i << "Sampleable depth formats: ";
|
||||
for (VkFormat format = (VkFormat) 1;;) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(mContext.physicalDevice, format, &props);
|
||||
if ((props.optimalTilingFeatures & required) == required) {
|
||||
utils::slog.i << format << " ";
|
||||
}
|
||||
if (format == VK_FORMAT_ASTC_12x12_SRGB_BLOCK) {
|
||||
utils::slog.i << utils::io::endl;
|
||||
break;
|
||||
}
|
||||
format = (VkFormat) (1 + (int) format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VulkanDriver::~VulkanDriver() noexcept = default;
|
||||
@@ -120,11 +356,11 @@ void VulkanDriver::terminate() {
|
||||
vkDestroyQueryPool(mContext.device, mContext.timestamps.pool, VKALLOC);
|
||||
vkDestroyCommandPool(mContext.device, mContext.commandPool, VKALLOC);
|
||||
vkDestroyDevice(mContext.device, VKALLOC);
|
||||
if (mContext.debugCallback) {
|
||||
vkDestroyDebugReportCallbackEXT(mContext.instance, mContext.debugCallback, VKALLOC);
|
||||
if (mDebugCallback) {
|
||||
vkDestroyDebugReportCallbackEXT(mContext.instance, mDebugCallback, VKALLOC);
|
||||
}
|
||||
if (mContext.debugMessenger) {
|
||||
vkDestroyDebugUtilsMessengerEXT(mContext.instance, mContext.debugMessenger, VKALLOC);
|
||||
if (mDebugMessenger) {
|
||||
vkDestroyDebugUtilsMessengerEXT(mContext.instance, mDebugMessenger, VKALLOC);
|
||||
}
|
||||
vkDestroyInstance(mContext.instance, VKALLOC);
|
||||
mContext.device = nullptr;
|
||||
|
||||
@@ -151,6 +151,8 @@ private:
|
||||
VulkanSamplerCache mSamplerCache;
|
||||
VulkanBlitter mBlitter;
|
||||
VulkanSamplerGroup* mSamplerBindings[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
|
||||
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -184,16 +184,6 @@ VkFormat getVkFormat(TextureFormat format) {
|
||||
case TextureFormat::DXT5_RGBA: return VK_FORMAT_BC3_UNORM_BLOCK;
|
||||
case TextureFormat::DXT5_SRGBA: return VK_FORMAT_BC3_SRGB_BLOCK;
|
||||
|
||||
case TextureFormat::RED_RGTC1: return VK_FORMAT_BC4_UNORM_BLOCK;
|
||||
case TextureFormat::SIGNED_RED_RGTC1: return VK_FORMAT_BC4_SNORM_BLOCK;
|
||||
case TextureFormat::RED_GREEN_RGTC2: return VK_FORMAT_BC5_UNORM_BLOCK;
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2: return VK_FORMAT_BC5_SNORM_BLOCK;
|
||||
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return VK_FORMAT_BC6H_SFLOAT_BLOCK;
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT: return VK_FORMAT_BC6H_UFLOAT_BLOCK;
|
||||
case TextureFormat::RGBA_BPTC_UNORM: return VK_FORMAT_BC7_UNORM_BLOCK;
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return VK_FORMAT_BC7_SRGB_BLOCK;
|
||||
|
||||
case TextureFormat::RGBA_ASTC_4x4: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
|
||||
case TextureFormat::RGBA_ASTC_5x4: return VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
|
||||
case TextureFormat::RGBA_ASTC_5x5: return VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -68,48 +66,6 @@ bool equivalent(const VkExtent2D& a, const VkExtent2D& b);
|
||||
bool isDepthFormat(VkFormat format);
|
||||
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);
|
||||
|
||||
|
||||
// Helper function for the vkEnumerateX methods. These methods have the format of
|
||||
// VkResult vkEnumerateX(InputType1 arg1, InputTyp2 arg2, ..., uint32_t* size,
|
||||
// OutputType* output_arg)
|
||||
// Instead of macros and explicitly listing the template params, Variadic Template was also
|
||||
// considered, but because the "variadic" part of the vk methods (i.e. the inputs) are before the
|
||||
// non-variadic parts, this breaks the template type matching logic. Hence, we use a macro approach
|
||||
// here.
|
||||
#define EXPAND_ENUM(...)\
|
||||
uint32_t size = 0;\
|
||||
VkResult result = func(__VA_ARGS__, nullptr);\
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "enumerate size error");\
|
||||
utils::FixedCapacityVector<OutType> ret(size);\
|
||||
result = func(__VA_ARGS__, ret.data());\
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "enumerate error");\
|
||||
return std::move(ret);
|
||||
|
||||
#define EXPAND_ENUM_NO_ARGS() EXPAND_ENUM(&size)
|
||||
#define EXPAND_ENUM_ARGS(...) EXPAND_ENUM(__VA_ARGS__, &size)
|
||||
|
||||
template <typename OutType>
|
||||
utils::FixedCapacityVector<OutType> enumerate(VKAPI_ATTR VkResult (*func)(uint32_t*, OutType*)) {
|
||||
EXPAND_ENUM_NO_ARGS();
|
||||
}
|
||||
|
||||
template <typename InType, typename OutType>
|
||||
utils::FixedCapacityVector<OutType> enumerate(
|
||||
VKAPI_ATTR VkResult (*func)(InType, uint32_t*, OutType*), InType inData) {
|
||||
EXPAND_ENUM_ARGS(inData);
|
||||
}
|
||||
|
||||
template <typename InTypeA, typename InTypeB, typename OutType>
|
||||
utils::FixedCapacityVector<OutType> enumerate(
|
||||
VKAPI_ATTR VkResult (*func)(InTypeA, InTypeB, uint32_t*, OutType*), InTypeA inDataA,
|
||||
InTypeB inDataB) {
|
||||
EXPAND_ENUM_ARGS(inDataA, inDataB);
|
||||
}
|
||||
|
||||
#undef EXPAND_ENUM
|
||||
#undef EXPAND_ENUM_NO_ARGS
|
||||
#undef EXPAND_ENUM_ARGS
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b);
|
||||
|
||||
@@ -22,12 +22,8 @@
|
||||
#include <spirv_glsl.hpp>
|
||||
#include <spirv_msl.hpp>
|
||||
|
||||
#include "../src/GLSLPostProcessor.h"
|
||||
|
||||
#include "builtinResource.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -63,6 +59,38 @@ void SpvToGlsl(const SpirvBlob* spirv, std::string* outGlsl) {
|
||||
*outGlsl = glslCompiler.compile();
|
||||
}
|
||||
|
||||
void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl) {
|
||||
CompilerMSL mslCompiler(*spirv);
|
||||
mslCompiler.set_msl_options(CompilerMSL::Options {
|
||||
.msl_version = CompilerMSL::Options::make_msl_version(1, 1)
|
||||
});
|
||||
|
||||
auto executionModel = mslCompiler.get_execution_model();
|
||||
|
||||
auto duplicateResourceBinding = [executionModel, &mslCompiler](const auto& resource) {
|
||||
auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
|
||||
auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding);
|
||||
MSLResourceBinding newBinding;
|
||||
newBinding.stage = executionModel;
|
||||
newBinding.desc_set = set;
|
||||
newBinding.binding = binding;
|
||||
newBinding.msl_texture = binding;
|
||||
newBinding.msl_sampler = binding;
|
||||
newBinding.msl_buffer = binding;
|
||||
mslCompiler.add_msl_resource_binding(newBinding);
|
||||
};
|
||||
|
||||
auto resources = mslCompiler.get_shader_resources();
|
||||
for (const auto& resource : resources.sampled_images) {
|
||||
duplicateResourceBinding(resource);
|
||||
}
|
||||
for (const auto& resource : resources.uniform_buffers) {
|
||||
duplicateResourceBinding(resource);
|
||||
}
|
||||
|
||||
*outMsl = mslCompiler.compile();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void ShaderGenerator::init() {
|
||||
@@ -74,16 +102,15 @@ void ShaderGenerator::shutdown() {
|
||||
}
|
||||
|
||||
ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment,
|
||||
Backend backend, bool isMobile, const filament::SamplerInterfaceBlock* sib) noexcept
|
||||
Backend backend, bool isMobile) noexcept
|
||||
: mBackend(backend),
|
||||
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile, sib)),
|
||||
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile)),
|
||||
mFragmentBlob(transpileShader(ShaderStage::FRAGMENT, std::move(fragment), backend,
|
||||
isMobile, sib)) {
|
||||
isMobile)) {
|
||||
}
|
||||
|
||||
ShaderGenerator::Blob ShaderGenerator::transpileShader(
|
||||
ShaderStage stage, std::string shader, Backend backend, bool isMobile,
|
||||
const filament::SamplerInterfaceBlock* sib) noexcept {
|
||||
ShaderStage stage, std::string shader, Backend backend, bool isMobile) noexcept {
|
||||
TProgram program;
|
||||
const EShLanguage language = stage == ShaderStage::VERTEX ? EShLangVertex : EShLangFragment;
|
||||
TShader tShader(language);
|
||||
@@ -146,10 +173,7 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
|
||||
}
|
||||
return { result.c_str(), result.c_str() + result.length() + 1 };
|
||||
} else if (backend == Backend::METAL) {
|
||||
const auto sm = isMobile ? ShaderModel::MOBILE : ShaderModel::DESKTOP;
|
||||
filamat::SibVector sibs = filamat::SibVector::with_capacity(1);
|
||||
if (sib) { sibs.emplace_back(0, sib); }
|
||||
filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, sibs, nullptr);
|
||||
SpvToMsl(&spirv, &result);
|
||||
return { result.c_str(), result.c_str() + result.length() + 1 };
|
||||
} else if (backend == Backend::VULKAN) {
|
||||
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "PlatformRunner.h"
|
||||
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
#include "backend/Program.h"
|
||||
|
||||
@@ -39,8 +38,7 @@ public:
|
||||
* @param vertex The vertex shader, written in GLSL 450 core.
|
||||
* @param fragment The fragment shader, written in GLSL 450 core.
|
||||
*/
|
||||
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile,
|
||||
const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
|
||||
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile) noexcept;
|
||||
|
||||
ShaderGenerator(const ShaderGenerator& rhs) = delete;
|
||||
ShaderGenerator& operator=(const ShaderGenerator& rhs) = delete;
|
||||
@@ -52,7 +50,7 @@ private:
|
||||
|
||||
using Blob = std::vector<char>;
|
||||
static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend,
|
||||
bool isMobile, const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
|
||||
bool isMobile) noexcept;
|
||||
|
||||
Backend mBackend;
|
||||
|
||||
|
||||
@@ -133,12 +133,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
// Create a program.
|
||||
ProgramHandle program;
|
||||
{
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("backend_test_sib")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include "private/filament/SamplerInterfaceBlock.h"
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
|
||||
#include <math/half.h>
|
||||
@@ -55,7 +54,7 @@ layout(location = 0) out vec4 fragColor;
|
||||
|
||||
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
|
||||
// This parameter is ignored for other backends.
|
||||
layout(location = 0, set = 1) uniform {samplerType} test_tex;
|
||||
layout(location = 0, set = 1) uniform {samplerType} tex;
|
||||
|
||||
void main() {
|
||||
vec2 fbsize = vec2(512);
|
||||
@@ -63,7 +62,7 @@ void main() {
|
||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
fragColor = vec4(texture(test_tex, uv).rgb, 1.0f);
|
||||
fragColor = vec4(texture(tex, uv).rgb, 1.0f);
|
||||
}
|
||||
|
||||
)");
|
||||
@@ -73,7 +72,8 @@ std::string fragmentUpdateImage3DTemplate (R"(#version 450 core
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
|
||||
layout(location = 0, set = 1) uniform {samplerType} test_tex;
|
||||
// This parameter is ignored for other backends.
|
||||
layout(location = 0, set = 1) uniform {samplerType} tex;
|
||||
|
||||
float getLayer(in sampler3D s) { return 2.5f / 4.0f; }
|
||||
float getLayer(in sampler2DArray s) { return 2.0f; }
|
||||
@@ -84,7 +84,7 @@ void main() {
|
||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
fragColor = vec4(texture(test_tex, vec3(uv, getLayer(test_tex))).rgb, 1.0f);
|
||||
fragColor = vec4(texture(tex, vec3(uv, getLayer(tex))).rgb, 1.0f);
|
||||
}
|
||||
|
||||
)");
|
||||
@@ -94,7 +94,8 @@ std::string fragmentUpdateImageMip (R"(#version 450 core
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
|
||||
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
||||
// This parameter is ignored for other backends.
|
||||
layout(location = 0, set = 1) uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
vec2 fbsize = vec2(512);
|
||||
@@ -102,7 +103,7 @@ void main() {
|
||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
fragColor = vec4(textureLod(test_tex, uv, 1.0f).rgb, 1.0f);
|
||||
fragColor = vec4(textureLod(tex, uv, 1.0f).rgb, 1.0f);
|
||||
}
|
||||
|
||||
)");
|
||||
@@ -344,15 +345,10 @@ TEST_F(BackendTest, UpdateImage2D) {
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// Create a program.
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("Test")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
ProgramHandle program;
|
||||
std::string fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(t.textureFormat), fragmentTemplate);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
@@ -429,14 +425,9 @@ TEST_F(BackendTest, UpdateImageSRGB) {
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// Create a program.
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("Test")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
std::string fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
@@ -520,14 +511,9 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// Create a program.
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("Test")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
std::string fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
@@ -597,14 +583,9 @@ TEST_F(BackendTest, UpdateImage3D) {
|
||||
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
|
||||
|
||||
// Create a program.
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("Test")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
std::string fragment = stringReplace("{samplerType}",
|
||||
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
|
||||
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
|
||||
|
||||
@@ -45,7 +45,7 @@ std::string fragment (R"(#version 450 core
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0, set = 1) uniform sampler2D tex;
|
||||
layout(set = 1, binding = 6) uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
fragColor = texture(tex, uv);
|
||||
@@ -65,16 +65,11 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
auto swapChain = createSwapChain();
|
||||
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("backend_test_sib")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
|
||||
// Create a program that samples a texture.
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
Program::Sampler sampler { utils::CString("tex"), 0 };
|
||||
Program::Sampler sampler { utils::CString("tex"), 6 };
|
||||
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
|
||||
backend::Handle<HwProgram> program = getDriverApi().createProgram(std::move(p));
|
||||
|
||||
@@ -144,16 +139,11 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
|
||||
auto swapChain = createSwapChain();
|
||||
|
||||
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
|
||||
.name("backend_test_sib")
|
||||
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
|
||||
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
|
||||
.build();
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
|
||||
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
|
||||
|
||||
// Create a program that samples a texture.
|
||||
Program p = shaderGen.getProgram(getDriverApi());
|
||||
Program::Sampler sampler { utils::CString("tex"), 0 };
|
||||
Program::Sampler sampler { utils::CString("tex"), 6 };
|
||||
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
|
||||
auto program = getDriverApi().createProgram(std::move(p));
|
||||
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
# Investigating SPIRV-Cross / SPIRV-Tools issues
|
||||
|
||||
There are 4 repositories at play here:
|
||||
- [KhronosGroup/glslang](https://github.com/KhronosGroup/glslang)
|
||||
- [KhronosGroup/spirv-tools](https://github.com/KhronosGroup/SPIRV-Tools)
|
||||
- [KhronosGroup/spirv-cross](https://github.com/KhronosGroup/SPIRV-Cross)
|
||||
- [KhronosGroup/SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers)
|
||||
|
||||
Typically, the bug is present either in spirv-tools or spirv-cross.
|
||||
|
||||
## Build and install command-line tools on PATH
|
||||
|
||||
The goal is to replicate the bug outside of Filament, so we're going to use command-line versions of
|
||||
the SPIRV tools.
|
||||
|
||||
### Clone and build each repo
|
||||
|
||||
Note: Filament checks-out versions of these repositories inside `third_party/`; however, I've found
|
||||
it easiser to check out fresh copies separately so I can simply `git pull` to get the latest
|
||||
versions. Furthermore, Filament has modified some of these repositories locally for its own use
|
||||
case. Checking them out separately "proves" that the issue isn't Filament-specific.
|
||||
|
||||
```
|
||||
git clone git@github.com:KhronosGroup/SPIRV-Tools.git
|
||||
git clone git@github.com:KhronosGroup/SPIRV-Cross.git
|
||||
git clone git@github.com:KhronosGroup/glslang.git
|
||||
git clone git@github.com:KhronosGroup/SPIRV-Headers.git SPIRV-Tools/external/SPIRV-Headers
|
||||
|
||||
cd SPIRV-Tools/
|
||||
mkdir build && cmake . -G Ninja -B build
|
||||
ninja -C build
|
||||
cd ..
|
||||
|
||||
cd SPIRV-Cross/
|
||||
mkdir build && cmake . -G Ninja -B build
|
||||
ninja -C build
|
||||
cd ..
|
||||
|
||||
cd glslang/
|
||||
mkdir build && cmake . -G Ninja -B build
|
||||
ninja -C build
|
||||
cd ..
|
||||
```
|
||||
|
||||
### Add directories to PATH
|
||||
|
||||
```
|
||||
export PATH=`pwd`/SPIRV-Tools/build/tools:$PATH
|
||||
export PATH=`pwd`/glslang/build/StandAlone:$PATH
|
||||
export PATH=`pwd`/spirv-cross/build:$PATH
|
||||
```
|
||||
|
||||
Ensure the following tools now exist on your PATH:
|
||||
|
||||
1. `glslangValidator`
|
||||
2. `spiv-opt`
|
||||
3. `spirv-val`
|
||||
4. `spirv-cross`
|
||||
|
||||
## Isolate the problematic GLSL shader
|
||||
|
||||
First determine the Filament material and variant that causes the problem.
|
||||
|
||||
What we want is the "raw" GLSL version of the shader, before any optimizations / cross-compilation
|
||||
happens.
|
||||
|
||||
The easiest way I've found to do this is by adding a `std::cout` statement inside `MaterialBuilder`,
|
||||
right after `sg.createFragmentProgram` or `sg.createVertexProgram` is called. For example,
|
||||
|
||||
```
|
||||
if (v.variant.key == 8u) { std::cout << shader << std::endl; }
|
||||
```
|
||||
|
||||
Then, I invoke `matc` and redirect stdout to a .frag or .vert file.
|
||||
|
||||
Note that gltfio material "templates" first go through a build step. After building gltfio, the
|
||||
gltfio Filament materials are output to:
|
||||
|
||||
```
|
||||
out/cmake-release/libs/gltfio/*.mat
|
||||
```
|
||||
|
||||
One of these materials can be compiled with the following command:
|
||||
|
||||
```
|
||||
matc \
|
||||
-TCUSTOM_PARAMS="// no custom params" \
|
||||
-TCUSTOM_VERTEX="// no custom vertex" \
|
||||
-TCUSTOM_FRAGMENT="// no custom fragment" \
|
||||
-TDOUBLESIDED=false \
|
||||
-TTRANSPARENCY=default \
|
||||
-TSHADINGMODEL=unlit \
|
||||
-TBLENDING=opaque \
|
||||
--platform mobile --api metal -o temp.filamat \
|
||||
unlit_opaque.mat
|
||||
```
|
||||
|
||||
## Reproduce the compilation error
|
||||
|
||||
The goal is to generate a .spv file that doesn't pass validation (through the spirv-val tool).
|
||||
|
||||
Reproducing the error usually involves a few steps:
|
||||
|
||||
1. Compile the raw GLSL shader into SPIR-V.
|
||||
|
||||
```
|
||||
glslangValidator -V -o unoptimized.spv in.frag
|
||||
```
|
||||
|
||||
2. Optimize for performance.
|
||||
|
||||
```
|
||||
spirv-opt -Oconfig=optimizations.cfg unoptimized.spv -o optimized.spv
|
||||
```
|
||||
|
||||
See [optimizations.cfg](optimizations.cfg) for a template. This file should contain the same list of optimizations that
|
||||
Filament employs. This should match the same optimizations specified in `GLSLPostProcessor`, for
|
||||
example, `GLSLPostProcessor::registerPerformancePasses` or `GLSLPostProcessor::registerSizePasses`.
|
||||
|
||||
3. For shaders targeting Metal, convert relaxed ops to half.
|
||||
|
||||
```
|
||||
spirv-opt \
|
||||
--convert-relaxed-to-half \
|
||||
--simplify-instructions \
|
||||
--redundancy-elimination \
|
||||
--eliminate-dead-code-aggressive \
|
||||
optimized.spv \
|
||||
-o half.spv
|
||||
```
|
||||
|
||||
4. Finally, validate the final SPIR-V.
|
||||
|
||||
```
|
||||
spirv-val half.spv
|
||||
```
|
||||
|
||||
5. Sometimes validation will still pass, but still generate invalid shaders after cross-compiling.
|
||||
In these cases, you'll need to cross compile to the target language and manually pick out errors
|
||||
in the generated shader.
|
||||
|
||||
```
|
||||
# for OpenGL
|
||||
spirv-cross optimized.spv > optimized.frag
|
||||
|
||||
# for OpenGL ES
|
||||
spirv-cross --es optimized.spv > optimized.frag
|
||||
|
||||
# for MSL
|
||||
spirv-cross --msl optimized.spv > optimized.metal
|
||||
```
|
||||
|
||||
To invoke Apple's compiler to compile MSL, you can run:
|
||||
|
||||
```
|
||||
xcrun -sdk macosx metal -c optimized.metal -o /dev/null
|
||||
```
|
||||
|
||||
## Clean up the shader for a bug report
|
||||
|
||||
These commands will run the preprocessor only on `in.frag`, and remove any empty lines.
|
||||
|
||||
```
|
||||
glslangValidator -E in.frag > preprocessed.frag
|
||||
sed '/^$/d' preprocessed.frag > preprocessed_small.frag
|
||||
```
|
||||
|
||||
You can also run `clang-format` on the preprocessed shader to make it easier to read:
|
||||
|
||||
```
|
||||
clang-format -i preprocessed_small.frag
|
||||
```
|
||||
|
||||
I always try to "whittle down" the shader to a smaller version that still reproduces the error. This
|
||||
might make it a bit easier on the Khronos team to diagnose the issue. I typically follow these steps
|
||||
in a loop until I'm satisfied:
|
||||
|
||||
1. Delete an unnecessary part of the shader
|
||||
2. Run the steps to reproduce the error
|
||||
3. If the error still reproduces, repeat
|
||||
4. Otherwise, undo the change and make a smaller change
|
||||
|
||||
There's also a [Reducer](https://github.com/KhronosGroup/SPIRV-Tools#reducer) tool that's part of
|
||||
SPIRV-Tools which can be used to automate these steps. I haven't experimented much with this, but it
|
||||
seems promising.
|
||||
|
||||
## Submit an Issue with the relevant Khronos repository
|
||||
|
||||
See some example issues that have been filed in the past:
|
||||
|
||||
- https://github.com/KhronosGroup/SPIRV-Cross/issues/1935
|
||||
- https://github.com/KhronosGroup/SPIRV-Cross/issues/1088
|
||||
- https://github.com/KhronosGroup/SPIRV-Cross/issues/1026
|
||||
- https://github.com/KhronosGroup/SPIRV-Tools/issues/4452
|
||||
- https://github.com/KhronosGroup/SPIRV-Tools/issues/3406
|
||||
- https://github.com/KhronosGroup/SPIRV-Tools/issues/3099
|
||||
- https://github.com/KhronosGroup/SPIRV-Tools/issues/5044
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
--wrap-opkill
|
||||
--eliminate-dead-branches
|
||||
--merge-return
|
||||
--inline-entry-points-exhaustive
|
||||
--eliminate-dead-functions
|
||||
--private-to-local
|
||||
--scalar-replacement=0
|
||||
--ssa-rewrite
|
||||
--ccp
|
||||
--loop-unroll
|
||||
--eliminate-dead-branches
|
||||
--simplify-instructions
|
||||
--scalar-replacement=0
|
||||
--eliminate-local-single-store
|
||||
--if-conversion
|
||||
--simplify-instructions
|
||||
--eliminate-dead-code-aggressive
|
||||
--eliminate-dead-branches
|
||||
--merge-blocks
|
||||
--convert-local-access-chains
|
||||
--eliminate-local-single-block
|
||||
--eliminate-dead-code-aggressive
|
||||
--copy-propagate-arrays
|
||||
--vector-dce
|
||||
--eliminate-dead-inserts
|
||||
--eliminate-dead-members
|
||||
--eliminate-local-single-store
|
||||
--merge-blocks
|
||||
--ssa-rewrite
|
||||
--redundancy-elimination
|
||||
--simplify-instructions
|
||||
--eliminate-dead-code-aggressive
|
||||
--cfg-cleanup
|
||||
@@ -40,33 +40,33 @@ using sRGBColorA = math::float4;
|
||||
|
||||
//! types of RGB colors
|
||||
enum class RgbType : uint8_t {
|
||||
sRGB, //!< the color is defined in Rec.709-sRGB-D65 (sRGB) space
|
||||
LINEAR, //!< the color is defined in Rec.709-Linear-D65 ("linear sRGB") space
|
||||
sRGB, //!< the color is defined in sRGB space
|
||||
LINEAR, //!< the color is defined in linear space
|
||||
};
|
||||
|
||||
//! types of RGBA colors
|
||||
enum class RgbaType : uint8_t {
|
||||
/**
|
||||
* the color is defined in Rec.709-sRGB-D65 (sRGB) space and the RGB values
|
||||
* have not been pre-multiplied by the alpha (for instance, a 50%
|
||||
* the color is defined in sRGB space and the RGB values
|
||||
* have not been premultiplied by the alpha (for instance, a 50%
|
||||
* transparent red is <1,0,0,0.5>)
|
||||
*/
|
||||
sRGB,
|
||||
/**
|
||||
* the color is defined in Rec.709-Linear-D65 ("linear sRGB") space and the
|
||||
* RGB values have not been pre-multiplied by the alpha (for instance, a 50%
|
||||
* the color is defined in linear space and the RGB values
|
||||
* have not been premultiplied by the alpha (for instance, a 50%
|
||||
* transparent red is <1,0,0,0.5>)
|
||||
*/
|
||||
LINEAR,
|
||||
/**
|
||||
* the color is defined in Rec.709-sRGB-D65 (sRGB) space and the RGB values
|
||||
* have been pre-multiplied by the alpha (for instance, a 50%
|
||||
* the color is defined in sRGB space and the RGB values
|
||||
* have been premultiplied by the alpha (for instance, a 50%
|
||||
* transparent red is <0.5,0,0,0.5>)
|
||||
*/
|
||||
PREMULTIPLIED_sRGB,
|
||||
/**
|
||||
* the color is defined in Rec.709-Linear-D65 ("linear sRGB") space and the
|
||||
* RGB values have been pre-multiplied by the alpha (for instance, a 50%
|
||||
* the color is defined in linear space and the RGB values
|
||||
* have been premultiplied by the alpha (for instance, a 50%
|
||||
* transparent red is <0.5,0,0,0.5>)
|
||||
*/
|
||||
PREMULTIPLIED_LINEAR
|
||||
@@ -93,44 +93,40 @@ public:
|
||||
template<ColorConversion = ACCURATE>
|
||||
static LinearColor toLinear(sRGBColor const& color);
|
||||
|
||||
/**
|
||||
* Converts an RGB color in Rec.709-Linear-D65 ("linear sRGB") space to an
|
||||
* RGB color in Rec.709-sRGB-D65 (sRGB) space.
|
||||
*/
|
||||
//! converts an RGB color in linear space to an RGB color in sRGB space
|
||||
template<ColorConversion = ACCURATE>
|
||||
static sRGBColor toSRGB(LinearColor const& color);
|
||||
|
||||
/**
|
||||
* Converts an RGBA color in Rec.709-sRGB-D65 (sRGB) space to an RGBA color in
|
||||
* Rec.709-Linear-D65 ("linear sRGB") space the alpha component is left unmodified.
|
||||
* converts an RGBA color in sRGB space to an RGBA color in linear space
|
||||
* the alpha component is left unmodified
|
||||
*/
|
||||
template<ColorConversion = ACCURATE>
|
||||
static LinearColorA toLinear(sRGBColorA const& color);
|
||||
|
||||
/**
|
||||
* Converts an RGBA color in Rec.709-Linear-D65 ("linear sRGB") space to
|
||||
* an RGBA color in Rec.709-sRGB-D65 (sRGB) space the alpha component is
|
||||
* left unmodified.
|
||||
* converts an RGBA color in linear space to an RGBA color in sRGB space
|
||||
* the alpha component is left unmodified
|
||||
*/
|
||||
template<ColorConversion = ACCURATE>
|
||||
static sRGBColorA toSRGB(LinearColorA const& color);
|
||||
|
||||
/**
|
||||
* Converts a correlated color temperature to a linear RGB color in sRGB
|
||||
* converts a correlated color temperature to a linear RGB color in sRGB
|
||||
* space the temperature must be expressed in kelvin and must be in the
|
||||
* range 1,000K to 15,000K.
|
||||
* range 1,000K to 15,000K
|
||||
*/
|
||||
static LinearColor cct(float K);
|
||||
|
||||
/**
|
||||
* Converts a CIE standard illuminant series D to a linear RGB color in
|
||||
* converts a CIE standard illuminant series D to a linear RGB color in
|
||||
* sRGB space the temperature must be expressed in kelvin and must be in
|
||||
* the range 4,000K to 25,000K
|
||||
*/
|
||||
static LinearColor illuminantD(float K);
|
||||
|
||||
/**
|
||||
* Computes the Beer-Lambert absorption coefficients from the specified
|
||||
* computes the Beer-Lambert absorption coefficients from the specified
|
||||
* transmittance color and distance. The computed absorption will guarantee
|
||||
* the white light will become the specified color at the specified distance.
|
||||
* The output of this function can be used as the absorption parameter of
|
||||
|
||||
@@ -31,10 +31,6 @@ namespace filament {
|
||||
class Engine;
|
||||
class FColorGrading;
|
||||
|
||||
namespace color {
|
||||
class ColorSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* ColorGrading is used to transform (either to modify or correct) the colors of the HDR buffer
|
||||
* rendered by Filament. Color grading transforms are applied after lighting, and after any lens
|
||||
@@ -101,7 +97,6 @@ class ColorSpace;
|
||||
* - Tone mapping: ACESLegacyToneMapper
|
||||
* - Luminance scaling: false
|
||||
* - Gamut mapping: false
|
||||
* - Output color space: Rec709-sRGB-D65
|
||||
*
|
||||
* @see View
|
||||
*/
|
||||
@@ -452,19 +447,6 @@ public:
|
||||
*/
|
||||
Builder& curves(math::float3 shadowGamma, math::float3 midPoint, math::float3 highlightScale) noexcept;
|
||||
|
||||
/**
|
||||
* Sets the output color space for this ColorGrading object. After all color grading steps
|
||||
* have been applied, the final color will be converted in the desired color space.
|
||||
*
|
||||
* NOTE: Currently the output color space must be one of Rec709-sRGB-D65 or
|
||||
* Rec709-Linear-D65. Only the transfer function is taken into account.
|
||||
*
|
||||
* @param colorSpace The output color space.
|
||||
*
|
||||
* @return This Builder, for chaining calls
|
||||
*/
|
||||
Builder& outputColorSpace(const color::ColorSpace& colorSpace) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the ColorGrading object and returns a pointer to it.
|
||||
*
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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_COLOR_SPACE_H
|
||||
#define TNT_FILAMENT_COLOR_SPACE_H
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
namespace filament {
|
||||
namespace color {
|
||||
|
||||
using namespace math;
|
||||
|
||||
/**
|
||||
* Holds the chromaticities of a color space's primaries as xy coordinates
|
||||
* in xyY (Y is assumed to be 1).
|
||||
*/
|
||||
struct Primaries {
|
||||
float2 r;
|
||||
float2 g;
|
||||
float2 b;
|
||||
|
||||
bool operator==(const Primaries& rhs) const noexcept {
|
||||
return r == rhs.r && b == rhs.b && g == rhs.b;
|
||||
}
|
||||
};
|
||||
|
||||
//! Reference white for a color space, defined as the xy coordinates in the xyY space.
|
||||
using WhitePoint = float2;
|
||||
|
||||
/**
|
||||
* <p>Defines the parameters for the ICC parametric curve type 4, as
|
||||
* defined in ICC.1:2004-10, section 10.15.</p>
|
||||
*
|
||||
* <p>The EOTF is of the form:</p>
|
||||
*
|
||||
* \(\begin{equation}
|
||||
* Y = \begin{cases}c X + f & X \lt d \\\
|
||||
* \left( a X + b \right) ^{g} + e & X \ge d \end{cases}
|
||||
* \end{equation}\)
|
||||
*
|
||||
* <p>The corresponding OETF is simply the inverse function.</p>
|
||||
*
|
||||
* <p>The parameters defined by this class form a valid transfer
|
||||
* function only if all the following conditions are met:</p>
|
||||
* <ul>
|
||||
* <li>No parameter is a NaN</li>
|
||||
* <li>\(d\) is in the range \([0..1]\)</li>
|
||||
* <li>The function is not constant</li>
|
||||
* <li>The function is positive and increasing</li>
|
||||
* </ul>
|
||||
*/
|
||||
struct TransferFunction {
|
||||
/**
|
||||
* <p>Defines the parameters for the ICC parametric curve type 3, as
|
||||
* defined in ICC.1:2004-10, section 10.15.</p>
|
||||
*
|
||||
* <p>The EOTF is of the form:</p>
|
||||
*
|
||||
* \(\begin{equation}
|
||||
* Y = \begin{cases}c X & X \lt d \\\
|
||||
* \left( a X + b \right) ^{g} & X \ge d \end{cases}
|
||||
* \end{equation}\)
|
||||
*
|
||||
* <p>This constructor is equivalent to setting \(e\) and \(f\) to 0.</p>
|
||||
*
|
||||
* @param a The value of \(a\) in the equation of the EOTF described above
|
||||
* @param b The value of \(b\) in the equation of the EOTF described above
|
||||
* @param c The value of \(c\) in the equation of the EOTF described above
|
||||
* @param d The value of \(d\) in the equation of the EOTF described above
|
||||
* @param g The value of \(g\) in the equation of the EOTF described above
|
||||
*/
|
||||
constexpr TransferFunction(
|
||||
double a,
|
||||
double b,
|
||||
double c,
|
||||
double d,
|
||||
double e,
|
||||
double f,
|
||||
double g
|
||||
) : a(a),
|
||||
b(b),
|
||||
c(c),
|
||||
d(d),
|
||||
e(e),
|
||||
f(f),
|
||||
g(g) {
|
||||
}
|
||||
|
||||
constexpr TransferFunction(
|
||||
double a,
|
||||
double b,
|
||||
double c,
|
||||
double d,
|
||||
double g
|
||||
) : TransferFunction(a, b, c, d, 0.0, 0.0, g) {
|
||||
}
|
||||
|
||||
bool operator==(const TransferFunction& rhs) const noexcept {
|
||||
return
|
||||
a == rhs.a &&
|
||||
b == rhs.b &&
|
||||
c == rhs.c &&
|
||||
d == rhs.d &&
|
||||
e == rhs.e &&
|
||||
f == rhs.f &&
|
||||
g == rhs.g;
|
||||
}
|
||||
|
||||
double a;
|
||||
double b;
|
||||
double c;
|
||||
double d;
|
||||
double e;
|
||||
double f;
|
||||
double g;
|
||||
};
|
||||
|
||||
/**
|
||||
* <p>A color space in Filament is always an RGB color space. A specific RGB color space
|
||||
* is defined by the following properties:</p>
|
||||
* <ul>
|
||||
* <li>Three chromaticities of the red, green and blue primaries, which
|
||||
* define the gamut of the color space.</li>
|
||||
* <li>A white point chromaticity that defines the stimulus to which
|
||||
* color space values are normalized (also just called "white").</li>
|
||||
* <li>An opto-electronic transfer function, also called opto-electronic
|
||||
* conversion function or often, and approximately, gamma function.</li>
|
||||
* <li>An electro-optical transfer function, also called electo-optical
|
||||
* conversion function or often, and approximately, gamma function.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Primaries and white point chromaticities</h3>
|
||||
* <p>In this implementation, the chromaticity of the primaries and the white
|
||||
* point of an RGB color space is defined in the CIE xyY color space. This
|
||||
* color space separates the chromaticity of a color, the x and y components,
|
||||
* and its luminance, the Y component. Since the primaries and the white
|
||||
* point have full brightness, the Y component is assumed to be 1 and only
|
||||
* the x and y components are needed to encode them.</p>
|
||||
*
|
||||
* <h3>Transfer functions</h3>
|
||||
* <p>A transfer function is a color component conversion function, defined as
|
||||
* a single variable, monotonic mathematical function. It is applied to each
|
||||
* individual component of a color. They are used to perform the mapping
|
||||
* between linear tristimulus values and non-linear electronic signal value.</p>
|
||||
* <p>The <em>opto-electronic transfer function</em> (OETF or OECF) encodes
|
||||
* tristimulus values in a scene to a non-linear electronic signal value.</p>
|
||||
*/
|
||||
class ColorSpace {
|
||||
public:
|
||||
constexpr ColorSpace(
|
||||
const Primaries primaries,
|
||||
const TransferFunction transferFunction,
|
||||
const WhitePoint whitePoint
|
||||
) : mPrimaries(primaries),
|
||||
mTransferFunction(transferFunction),
|
||||
mWhitePoint(whitePoint) {
|
||||
}
|
||||
|
||||
bool operator==(const ColorSpace& rhs) const noexcept {
|
||||
return mPrimaries == rhs.mPrimaries &&
|
||||
mTransferFunction == rhs.mTransferFunction &&
|
||||
mWhitePoint == rhs.mWhitePoint;
|
||||
}
|
||||
|
||||
constexpr const Primaries& getPrimaries() const { return mPrimaries; }
|
||||
constexpr const TransferFunction& getTransferFunction() const { return mTransferFunction; }
|
||||
constexpr const WhitePoint& getWhitePoint() const { return mWhitePoint; }
|
||||
|
||||
private:
|
||||
Primaries mPrimaries;
|
||||
TransferFunction mTransferFunction;
|
||||
WhitePoint mWhitePoint;
|
||||
};
|
||||
|
||||
/**
|
||||
* Intermediate class used when building a color space using the "-" syntax:
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* // Declares a "linear sRGB" color space.
|
||||
* ColorSpace myColorSpace = Rec709-Linear-sRGB;
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
class PartialColorSpace {
|
||||
public:
|
||||
constexpr ColorSpace operator-(const WhitePoint whitePoint) const {
|
||||
return ColorSpace(mPrimaries, mTransferFunction, whitePoint);
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr PartialColorSpace(
|
||||
const Primaries primaries,
|
||||
const TransferFunction transferFunction
|
||||
) : mPrimaries(primaries),
|
||||
mTransferFunction(transferFunction) {
|
||||
}
|
||||
|
||||
Primaries mPrimaries;
|
||||
TransferFunction mTransferFunction;
|
||||
|
||||
friend class Gamut;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the chromaticities of the primaries for a color space. The chromaticities
|
||||
* are expressed as three pairs of xy coordinates (in xyY) for the red, green, and blue
|
||||
* chromaticities.
|
||||
*/
|
||||
class Gamut {
|
||||
public:
|
||||
constexpr Gamut(const Primaries primaries) : mPrimaries(primaries) {
|
||||
}
|
||||
|
||||
constexpr Gamut(float2 r, float2 g, float2 b) : Gamut(Primaries{r, g, b}) {
|
||||
}
|
||||
|
||||
constexpr PartialColorSpace operator-(const TransferFunction transferFunction) const {
|
||||
return PartialColorSpace(mPrimaries, transferFunction);
|
||||
}
|
||||
|
||||
constexpr const Primaries& getPrimaries() const { return mPrimaries; }
|
||||
|
||||
private:
|
||||
Primaries mPrimaries;
|
||||
};
|
||||
|
||||
//! Rec.709 color gamut, used in the sRGB and DisplayP3 color spaces.
|
||||
constexpr Gamut Rec709 = Gamut({0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f});
|
||||
|
||||
//! Linear transfer function.
|
||||
constexpr TransferFunction Linear = TransferFunction(1.0, 0.0, 0.0, 0.0, 1.0);
|
||||
//! sRGB transfer function.
|
||||
constexpr TransferFunction sRGB =
|
||||
TransferFunction(1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045, 2.4);
|
||||
|
||||
//! Standard CIE 1931 2° illuminant D65. This illuminant has a color temperature of 6504K.
|
||||
constexpr WhitePoint D65 = WhitePoint({0.31271f, 0.32902f});
|
||||
|
||||
} // namespace color
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_COLOR_SPACE_H
|
||||
@@ -340,6 +340,14 @@ public:
|
||||
* @see View::setShadowType
|
||||
*/
|
||||
struct Vsm {
|
||||
/**
|
||||
* The number of MSAA samples to use when rendering VSM shadow maps.
|
||||
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
|
||||
* off MSAA.
|
||||
* Higher values may not be available depending on the underlying hardware.
|
||||
*/
|
||||
uint8_t msaaSamples = 1;
|
||||
|
||||
/**
|
||||
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
|
||||
* an improvement to the default EVSM which suffers important light leaks. Enabling
|
||||
|
||||
@@ -390,14 +390,6 @@ struct VsmShadowOptions {
|
||||
*/
|
||||
bool mipmapping = false;
|
||||
|
||||
/**
|
||||
* The number of MSAA samples to use when rendering VSM shadow maps.
|
||||
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
|
||||
* off MSAA.
|
||||
* Higher values may not be available depending on the underlying hardware.
|
||||
*/
|
||||
uint8_t msaaSamples = 1;
|
||||
|
||||
/**
|
||||
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
|
||||
* precision is rarely needed, but it does reduces light leaks as well as "fading"
|
||||
|
||||
@@ -117,37 +117,15 @@ public:
|
||||
* ClearOptions are used at the beginning of a frame to clear or retain the SwapChain content.
|
||||
*/
|
||||
struct ClearOptions {
|
||||
/**
|
||||
* Color (sRGB linear) to use to clear the RenderTarget (typically the SwapChain).
|
||||
*
|
||||
* The RenderTarget is cleared using this color, which won't be tone-mapped since
|
||||
* tone-mapping is part of View rendering (this is not).
|
||||
*
|
||||
* When a View is rendered, there are 3 scenarios to consider:
|
||||
* - Pixels rendered by the View replace the clear color (or blend with it in
|
||||
* `BlendMode::TRANSLUCENT` mode).
|
||||
*
|
||||
* - With blending mode set to `BlendMode::TRANSLUCENT`, Pixels untouched by the View
|
||||
* are considered fulling transparent and let the clear color show through.
|
||||
*
|
||||
* - With blending mode set to `BlendMode::OPAQUE`, Pixels untouched by the View
|
||||
* are set to the clear color. However, because it is now used in the context of a View,
|
||||
* it will go through the post-processing stage, which includes tone-mapping.
|
||||
*
|
||||
* For consistency, it is recommended to always use a Skybox to clear an opaque View's
|
||||
* background, or to use black or fully-transparent (i.e. {0,0,0,0}) as the clear color.
|
||||
*/
|
||||
/** Color to use to clear the SwapChain */
|
||||
math::float4 clearColor = {};
|
||||
|
||||
/** Value to clear the stencil buffer */
|
||||
uint8_t clearStencil = 0u;
|
||||
|
||||
/**
|
||||
* Whether the SwapChain should be cleared using the clearColor. Use this if translucent
|
||||
* View will be drawn, for instance.
|
||||
*/
|
||||
bool clear = false;
|
||||
|
||||
/**
|
||||
* Whether the SwapChain content should be discarded. clear implies discard. Set this
|
||||
* to false (along with clear to false as well) if the SwapChain already has content that
|
||||
|
||||
@@ -80,7 +80,14 @@ public:
|
||||
*
|
||||
* @return The associated Skybox, or nullptr if there is none.
|
||||
*/
|
||||
Skybox* getSkybox() const noexcept;
|
||||
Skybox* getSkybox() noexcept;
|
||||
|
||||
/**
|
||||
* Returns an immutable Skybox associated with the Scene.
|
||||
*
|
||||
* @return The associated Skybox, or nullptr if there is none.
|
||||
*/
|
||||
Skybox const* getSkybox() const noexcept;
|
||||
|
||||
/**
|
||||
* Set the IndirectLight to use when rendering the Scene.
|
||||
@@ -89,17 +96,8 @@ public:
|
||||
* IndirectLight.
|
||||
*
|
||||
* @param ibl The IndirectLight to use when rendering the Scene or nullptr to unset.
|
||||
* @see getIndirectLight
|
||||
*/
|
||||
void setIndirectLight(IndirectLight* ibl) noexcept;
|
||||
|
||||
/**
|
||||
* Get the IndirectLight or nullptr if none is set.
|
||||
*
|
||||
* @return the the IndirectLight or nullptr if none is set
|
||||
* @see setIndirectLight
|
||||
*/
|
||||
IndirectLight* getIndirectLight() const noexcept;
|
||||
void setIndirectLight(IndirectLight const* ibl) noexcept;
|
||||
|
||||
/**
|
||||
* Adds an Entity to the Scene.
|
||||
|
||||
@@ -35,8 +35,8 @@ AtlasAllocator::AtlasAllocator(size_t maxTextureSize) noexcept {
|
||||
mMaxTextureSizePot = (sizeof(maxTextureSize) * 8 - 1u) - utils::clz(maxTextureSize);
|
||||
}
|
||||
|
||||
AtlasAllocator::Allocation AtlasAllocator::allocate(size_t textureSize) noexcept {
|
||||
Allocation result{};
|
||||
Viewport AtlasAllocator::allocate(size_t textureSize) noexcept {
|
||||
Viewport result{};
|
||||
const size_t powerOfTwo = (sizeof(textureSize) * 8 - 1u) - utils::clz(textureSize);
|
||||
|
||||
// asked for a texture size too large
|
||||
@@ -45,24 +45,22 @@ AtlasAllocator::Allocation AtlasAllocator::allocate(size_t textureSize) noexcept
|
||||
}
|
||||
|
||||
// asked for a texture size too small
|
||||
if (UTILS_UNLIKELY(mMaxTextureSizePot - powerOfTwo >= QUAD_TREE_DEPTH)) {
|
||||
if (UTILS_UNLIKELY(mMaxTextureSizePot - powerOfTwo >= QuadTree::height())) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const size_t layer = (mMaxTextureSizePot - powerOfTwo);
|
||||
const NodeId loc = allocateInLayer(LAYERS_DEPTH + layer);
|
||||
const size_t layer = mMaxTextureSizePot - powerOfTwo;
|
||||
const NodeId loc = allocateInLayer(layer);
|
||||
if (loc.l >= 0) {
|
||||
assert_invariant(loc.l - LAYERS_DEPTH == int8_t(layer));
|
||||
const size_t dimension = 1u << powerOfTwo;
|
||||
assert_invariant(loc.l == int8_t(layer));
|
||||
size_t dimension = 1u << powerOfTwo;
|
||||
// find the location of in the texture from the morton code (quadtree position)
|
||||
const auto [x, y] = unmorton(loc.code);
|
||||
auto [x, y] = unmorton(loc.code);
|
||||
// scale to our maximum allocation size
|
||||
const uint32_t mask = (1u << layer) - 1u;
|
||||
result.viewport.left = int32_t(x & mask) << powerOfTwo;
|
||||
result.viewport.bottom = int32_t(y & mask) << powerOfTwo;
|
||||
result.viewport.width = dimension;
|
||||
result.viewport.height = dimension;
|
||||
result.layer = loc.code >> (2 * layer);
|
||||
result.left = int32_t(x) << powerOfTwo;
|
||||
result.bottom = int32_t(y) << powerOfTwo;
|
||||
result.width = dimension;
|
||||
result.height = dimension;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -110,9 +108,9 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
|
||||
}
|
||||
|
||||
// We only want to find a fitting node that already has siblings, in order
|
||||
// to accomplish a "best fit" allocation. So if a parent (of what we're looking for)
|
||||
// has no children, we skip the whole tree below it.
|
||||
if (!node.hasChildren()) {
|
||||
// to accomplish a "best fit" allocation. So if we're a parent of a 'fitting'
|
||||
// node and don't have children, we skip the children recursion.
|
||||
if (curr.l == n - 1 && !node.hasChildren()) {
|
||||
return QuadTree::TraversalResult::SKIP_CHILDREN;
|
||||
}
|
||||
|
||||
@@ -123,8 +121,6 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
|
||||
if (candidate.l >= 0) {
|
||||
const size_t i = index(candidate.l, candidate.code);
|
||||
Node& allocation = mQuadTree[i];
|
||||
assert_invariant(!allocation.isAllocated());
|
||||
assert_invariant(!allocation.hasChildren());
|
||||
|
||||
if (candidate.l == n) {
|
||||
allocation.allocated = true;
|
||||
@@ -136,38 +132,26 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
|
||||
assert_invariant(parent.hasChildren());
|
||||
assert_invariant(!parent.hasAllChildren());
|
||||
parent.children++;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// check that all the parents are not allocated and have at least 1 child.
|
||||
NodeId ppp = candidate;
|
||||
while (ppp.l > 0) {
|
||||
const size_t pi = QuadTreeUtils::parent(ppp.l, ppp.code);
|
||||
ppp = NodeId{ int8_t(ppp.l - 1), uint8_t(ppp.code >> 2) };
|
||||
Node& node = mQuadTree[pi];
|
||||
assert_invariant(!node.isAllocated());
|
||||
assert_invariant(node.hasChildren());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (candidate.l < int8_t(QuadTree::height())) {
|
||||
// we need to create the hierarchy down to the level we need
|
||||
NodeId found{ -1, 0 };
|
||||
assert_invariant(!allocation.isAllocated());
|
||||
assert_invariant(!allocation.hasChildren());
|
||||
|
||||
NodeId found{};
|
||||
QuadTree::traverse(candidate.l, candidate.code,
|
||||
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
|
||||
size_t i = index(curr.l, curr.code);
|
||||
Node& node = mQuadTree[i];
|
||||
if (curr.l == n) {
|
||||
found = curr;
|
||||
assert_invariant(!node.hasChildren());
|
||||
node.allocated = true;
|
||||
return QuadTree::TraversalResult::EXIT;
|
||||
}
|
||||
assert_invariant(!node.hasAllChildren());
|
||||
node.children++;
|
||||
return QuadTree::TraversalResult::RECURSE;
|
||||
});
|
||||
|
||||
assert_invariant(found.l != -1);
|
||||
candidate = found;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <private/filament/EngineEnums.h>
|
||||
|
||||
class AtlasAllocator_AllocateFirstLevel_Test;
|
||||
class AtlasAllocator_AllocateSecondLevel_Test;
|
||||
class AtlasAllocator_AllocateMixed0_Test;
|
||||
@@ -55,18 +53,8 @@ class AtlasAllocator {
|
||||
uint8_t children : 3; // 0, 1, 2, 3, 4
|
||||
};
|
||||
|
||||
// this determines the number of layers we can use (3 layers == 64 quadtree entries)
|
||||
static constexpr size_t LAYERS_DEPTH = 3u;
|
||||
|
||||
// this determines how many "sub-sizes" we can have from the base size.
|
||||
// e.g. with a max texture size of 1024, we can allocate 1024, 512, 256 and 128 textures.
|
||||
static constexpr size_t QUAD_TREE_DEPTH = 4u;
|
||||
|
||||
// LAYERS_DEPTH limits the number of layers
|
||||
static_assert(CONFIG_MAX_SHADOW_LAYERS <= 1u << (LAYERS_DEPTH * 2u));
|
||||
|
||||
// QuadTreeArray is limited to a maximum depth of 7
|
||||
using QuadTree = utils::QuadTreeArray<Node, LAYERS_DEPTH + QUAD_TREE_DEPTH>;
|
||||
using QuadTree = utils::QuadTreeArray<Node, QUAD_TREE_DEPTH>;
|
||||
using NodeId = QuadTree::NodeId;
|
||||
|
||||
public:
|
||||
@@ -74,18 +62,14 @@ public:
|
||||
* Create allocator and specify the maximum texture size. Must be a power of two.
|
||||
* Allocations size allowed are the four power-of-two smaller or equal to this size.
|
||||
*/
|
||||
explicit AtlasAllocator(size_t maxTextureSize) noexcept;
|
||||
explicit AtlasAllocator(size_t maxTextureSize = 1024) noexcept;
|
||||
|
||||
/*
|
||||
* Allocates a square of size `textureSize`. Must be one of the power-of-two allowed
|
||||
* (see above).
|
||||
* Returns the location of the allocation within the maxTextureSize^2 square.
|
||||
*/
|
||||
struct Allocation {
|
||||
int32_t layer = -1;
|
||||
Viewport viewport;
|
||||
};
|
||||
Allocation allocate(size_t textureSize) noexcept;
|
||||
Viewport allocate(size_t textureSize) noexcept;
|
||||
|
||||
/*
|
||||
* Frees all allocations and reset the maximum texture size.
|
||||
|
||||
@@ -92,6 +92,7 @@ private:
|
||||
|
||||
template<size_t POOL_SIZE, size_t ALIGNMENT, typename AllocatorPolicy, typename LockingPolicy>
|
||||
BufferPoolAllocator<POOL_SIZE, ALIGNMENT, AllocatorPolicy, LockingPolicy>::~BufferPoolAllocator() noexcept {
|
||||
assert_invariant(mOutstandingBuffers == mEntries.size());
|
||||
clearInternal();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <filament/Color.h>
|
||||
|
||||
#include "ColorSpaceUtils.h"
|
||||
#include "ColorSpace.h"
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ColorSpaceUtils.h"
|
||||
#include "ColorSpace.h"
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -296,10 +296,6 @@ inline float3 linearAP1_to_ACEScct(float3 x) noexcept {
|
||||
return x;
|
||||
}
|
||||
|
||||
inline float3 OETF_Linear(float3 x) noexcept {
|
||||
return x;
|
||||
}
|
||||
|
||||
inline float3 OETF_sRGB(float3 x) noexcept {
|
||||
constexpr float a = 0.055f;
|
||||
constexpr float a1 = 1.055f;
|
||||
@@ -79,12 +79,14 @@ void PerShadowMapUniforms::prepareLodBias(Transaction const& transaction,
|
||||
}
|
||||
|
||||
void PerShadowMapUniforms::prepareViewport(Transaction const& transaction,
|
||||
backend::Viewport const& viewport) noexcept {
|
||||
float2 const dimensions{ viewport.width, viewport.height };
|
||||
const filament::Viewport& viewport,
|
||||
uint32_t xoffset, uint32_t yoffset) noexcept {
|
||||
const float w = float(viewport.width);
|
||||
const float h = float(viewport.height);
|
||||
auto& s = edit(transaction);
|
||||
s.resolution = { dimensions, 1.0f / dimensions };
|
||||
s.logicalViewportScale = 1.0f;
|
||||
s.logicalViewportOffset = 0.0f;
|
||||
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
|
||||
s.origin = float2{ viewport.left, viewport.bottom };
|
||||
s.offset = float2{ xoffset, yoffset };
|
||||
}
|
||||
|
||||
void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
|
||||
@@ -96,6 +98,45 @@ void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
|
||||
s.userTime = userTime;
|
||||
}
|
||||
|
||||
void PerShadowMapUniforms::prepareDirectionalLight(Transaction const& transaction,
|
||||
FEngine& engine,
|
||||
float exposure,
|
||||
float3 const& sceneSpaceDirection,
|
||||
PerShadowMapUniforms::LightManagerInstance directionalLight) noexcept {
|
||||
FLightManager& lcm = engine.getLightManager();
|
||||
auto& s = edit(transaction);
|
||||
|
||||
const float3 l = -sceneSpaceDirection; // guaranteed normalized
|
||||
|
||||
if (directionalLight.isValid()) {
|
||||
const float4 colorIntensity = {
|
||||
lcm.getColor(directionalLight), lcm.getIntensity(directionalLight) * exposure };
|
||||
|
||||
s.lightDirection = l;
|
||||
s.lightColorIntensity = colorIntensity;
|
||||
s.lightChannels = lcm.getLightChannels(directionalLight);
|
||||
|
||||
const bool isSun = lcm.isSunLight(directionalLight);
|
||||
// The last parameter must be < 0.0f for regular directional lights
|
||||
float4 sun{ 0.0f, 0.0f, 0.0f, -1.0f };
|
||||
if (UTILS_UNLIKELY(isSun && colorIntensity.w > 0.0f)) {
|
||||
// currently we have only a single directional light, so it's probably likely that it's
|
||||
// also the Sun. However, conceptually, most directional lights won't be sun lights.
|
||||
float radius = lcm.getSunAngularRadius(directionalLight);
|
||||
float haloSize = lcm.getSunHaloSize(directionalLight);
|
||||
float haloFalloff = lcm.getSunHaloFalloff(directionalLight);
|
||||
sun.x = std::cos(radius);
|
||||
sun.y = std::sin(radius);
|
||||
sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x);
|
||||
sun.w = haloFalloff;
|
||||
}
|
||||
s.sun = sun;
|
||||
} else {
|
||||
// Disable the sun if there's no directional light
|
||||
s.sun = float4{ 0.0f, 0.0f, 0.0f, -1.0f };
|
||||
}
|
||||
}
|
||||
|
||||
void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction,
|
||||
bool highPrecision) noexcept {
|
||||
auto& s = edit(transaction);
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
float bias) noexcept;
|
||||
|
||||
static void prepareViewport(Transaction const& transaction,
|
||||
backend::Viewport const& viewport) noexcept;
|
||||
const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
|
||||
|
||||
static void prepareTime(Transaction const& transaction,
|
||||
FEngine& engine, math::float4 const& userTime) noexcept;
|
||||
@@ -73,6 +73,10 @@ public:
|
||||
static void prepareShadowMapping(Transaction const& transaction,
|
||||
bool highPrecision) noexcept;
|
||||
|
||||
static void prepareDirectionalLight(Transaction const& transaction,
|
||||
FEngine& engine, float exposure,
|
||||
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
|
||||
|
||||
static Transaction open(backend::DriverApi& driver) noexcept;
|
||||
|
||||
// update local data into GPU UBO
|
||||
|
||||
@@ -100,17 +100,14 @@ void PerViewUniforms::prepareExposure(float ev100) noexcept {
|
||||
s.ev100 = ev100;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareViewport(
|
||||
const filament::Viewport& physicalViewport,
|
||||
const filament::Viewport& logicalViewport) noexcept {
|
||||
float4 const physical{ physicalViewport.left, physicalViewport.bottom,
|
||||
physicalViewport.width, physicalViewport.height };
|
||||
float4 const logical{ logicalViewport.left, logicalViewport.bottom,
|
||||
logicalViewport.width, logicalViewport.height };
|
||||
void PerViewUniforms::prepareViewport(const filament::Viewport& viewport,
|
||||
uint32_t xoffset, uint32_t yoffset) noexcept {
|
||||
const float w = float(viewport.width);
|
||||
const float h = float(viewport.height);
|
||||
auto& s = mUniforms.edit();
|
||||
s.resolution = { physical.zw, 1.0f / physical.zw };
|
||||
s.logicalViewportScale = physical.zw / logical.zw;
|
||||
s.logicalViewportOffset = -logical.xy / logical.zw;
|
||||
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
|
||||
s.origin = float2{ viewport.left, viewport.bottom };
|
||||
s.offset = float2{ xoffset, yoffset };
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareTime(FEngine& engine, math::float4 const& userTime) noexcept {
|
||||
@@ -293,10 +290,14 @@ void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept {
|
||||
|
||||
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
|
||||
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
|
||||
uniforms.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
|
||||
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
|
||||
uniforms.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
|
||||
uniforms.shadowBias = shadowMappingUniforms.shadowBias;
|
||||
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
|
||||
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
|
||||
uniforms.cascades = shadowMappingUniforms.cascades;
|
||||
uniforms.cascades |= uint32_t(shadowMappingUniforms.elvsm) << 31u;
|
||||
}
|
||||
|
||||
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
|
||||
|
||||
@@ -78,9 +78,7 @@ public:
|
||||
* @param yoffset vertical rendering offset *within* the viewport.
|
||||
* Non-zero when we have guard bands.
|
||||
*/
|
||||
void prepareViewport(
|
||||
const filament::Viewport& physicalViewport,
|
||||
const filament::Viewport& logicalViewport) noexcept;
|
||||
void prepareViewport(const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
|
||||
|
||||
void prepareTime(FEngine& engine, math::float4 const& userTime) noexcept;
|
||||
void prepareTemporalNoise(FEngine& engine, TemporalAntiAliasingOptions const& options) noexcept;
|
||||
|
||||
@@ -707,11 +707,6 @@ void RenderPass::Executor::overrideScissor(backend::Viewport const* scissor) noe
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noexcept {
|
||||
mScissorOverride = true;
|
||||
mScissor = scissor;
|
||||
}
|
||||
|
||||
void RenderPass::Executor::execute(FEngine& engine, const char* name) const noexcept {
|
||||
execute(engine.getDriverApi(), mCommands.begin(), mCommands.end());
|
||||
}
|
||||
@@ -755,11 +750,6 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
|
||||
continue;
|
||||
}
|
||||
|
||||
// primitiveHandle may be invalid if no geometry was set on the renderable.
|
||||
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// per-renderable uniform
|
||||
const PrimitiveInfo info = first->primitive;
|
||||
pipeline.rasterState = info.rasterState;
|
||||
|
||||
@@ -349,7 +349,6 @@ public:
|
||||
|
||||
// if non-null, overrides the material's scissor
|
||||
void overrideScissor(backend::Viewport const* scissor) noexcept;
|
||||
void overrideScissor(backend::Viewport const& scissor) noexcept;
|
||||
|
||||
void execute(FEngine& engine, const char* name) const noexcept;
|
||||
};
|
||||
|
||||
@@ -47,6 +47,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
FrameGraphId<FrameGraphTexture> ssr; // either screen-space reflections or refractions
|
||||
FrameGraphId<FrameGraphTexture> structure;
|
||||
float4 clearColor{};
|
||||
TargetBufferFlags clearFlags{};
|
||||
};
|
||||
|
||||
Blackboard& blackboard = fg.getBlackboard();
|
||||
@@ -54,8 +56,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
auto& colorPass = fg.addPass<ColorPassData>(name,
|
||||
[&](FrameGraph::Builder& builder, ColorPassData& data) {
|
||||
|
||||
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
|
||||
TargetBufferFlags clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
|
||||
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
@@ -98,7 +100,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
TargetBufferFlags::STENCIL : TargetBufferFlags::NONE;
|
||||
const char* const name = config.enabledStencilBuffer ?
|
||||
"Depth/Stencil Buffer" : "Depth Buffer";
|
||||
TextureFormat const format = config.enabledStencilBuffer ?
|
||||
TextureFormat format = config.enabledStencilBuffer ?
|
||||
TextureFormat::DEPTH32F_STENCIL8 : TextureFormat::DEPTH32F;
|
||||
data.depth = builder.createTexture(name, {
|
||||
.width = colorBufferDesc.width,
|
||||
@@ -153,11 +155,14 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
*/
|
||||
builder.declareRenderPass("Color Pass Target", {
|
||||
.attachments = { .color = { data.color, data.output },
|
||||
.depth = data.depth,
|
||||
.stencil = data.stencil },
|
||||
.clearColor = config.clearColor,
|
||||
.samples = config.msaa,
|
||||
.depth = data.depth,
|
||||
.stencil = data.stencil },
|
||||
.samples = config.msaa,
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags });
|
||||
|
||||
data.clearColor = config.clearColor;
|
||||
data.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags;
|
||||
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
[=, &view, &engine](FrameGraphResources const& resources,
|
||||
@@ -179,7 +184,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
resources.getTexture(data.structure) : engine.getOneTexture());
|
||||
|
||||
// set screen-space reflections and screen-space refractions
|
||||
TextureHandle const ssr = data.ssr ?
|
||||
TextureHandle ssr = data.ssr ?
|
||||
resources.getTexture(data.ssr) : engine.getOneTextureArray();
|
||||
|
||||
view.prepareSSR(ssr, config.ssrLodOffset,
|
||||
@@ -194,12 +199,13 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
out.params.viewport.height == resources.getDescriptor(data.color).height);
|
||||
|
||||
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport),
|
||||
config.logicalViewport);
|
||||
config.xoffset, config.yoffset);
|
||||
|
||||
view.commitUniforms(driver);
|
||||
|
||||
// TODO: this should be a parameter of FrameGraphRenderPass::Descriptor
|
||||
out.params.clearColor = data.clearColor;
|
||||
out.params.clearStencil = config.clearStencil;
|
||||
out.params.flags.clear = data.clearFlags;
|
||||
if (view.getBlendMode() == BlendMode::TRANSLUCENT) {
|
||||
if (any(out.params.flags.discardStart & TargetBufferFlags::COLOR0)) {
|
||||
// if the buffer is discarded (e.g. it's new) and we're blending,
|
||||
@@ -235,7 +241,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
return output;
|
||||
}
|
||||
|
||||
std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
|
||||
FrameGraphId<FrameGraphTexture> RendererUtils::refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
@@ -269,8 +275,8 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
|
||||
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
|
||||
// When rendering the opaques, we need to conserve the sample buffer,
|
||||
// so create config that specifies the sample count.
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height,
|
||||
.width = config.width,
|
||||
.height = config.height,
|
||||
.samples = config.msaa,
|
||||
.format = config.hdrFormat
|
||||
}, config, { .asSubpass = false },
|
||||
@@ -285,16 +291,9 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
|
||||
// automatically because these are set in the Blackboard (they were set by the opaque
|
||||
// pass). For this reason, `desc` below is only used in colorPass() for the width and
|
||||
// height.
|
||||
|
||||
// Since we're reusing the existing target we don't want to clear any of its buffer.
|
||||
// Important: if this target ended up being an imported target, then the clearFlags
|
||||
// specified here wouldn't apply (the clearFlags of the imported target take precedence),
|
||||
// and we'd end up clearing the opaque pass. This scenario never happens because it is
|
||||
// prevented in Renderer.cpp's final blit.
|
||||
config.clearFlags = TargetBufferFlags::NONE;
|
||||
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height },
|
||||
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view,
|
||||
{ .width = config.width, .height = config.height },
|
||||
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
|
||||
|
||||
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
|
||||
@@ -307,7 +306,7 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
|
||||
} else {
|
||||
output = input;
|
||||
}
|
||||
return { output, hasScreenSpaceRefraction };
|
||||
return output;
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FRenderTarget;
|
||||
@@ -42,11 +40,14 @@ class RendererUtils {
|
||||
public:
|
||||
|
||||
struct ColorPassConfig {
|
||||
// Rendering viewport (e.g. scaled down viewport from dynamic resolution)
|
||||
Viewport physicalViewport;
|
||||
// Logical viewport (e.g. left-bottom non-zero when we have guard bands), origin
|
||||
// relative to physicalViewport
|
||||
Viewport logicalViewport;
|
||||
// Rendering viewport width (e.g. scaled down viewport from dynamic resolution)
|
||||
uint32_t width;
|
||||
// Rendering viewport height (e.g. scaled down viewport from dynamic resolution)
|
||||
uint32_t height;
|
||||
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
|
||||
uint32_t xoffset;
|
||||
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
|
||||
uint32_t yoffset;
|
||||
// dynamic resolution scale
|
||||
math::float2 scale;
|
||||
// HDR format
|
||||
@@ -76,7 +77,7 @@ public:
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor const& passExecutor) noexcept;
|
||||
|
||||
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
|
||||
static FrameGraphId<FrameGraphTexture> refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
|
||||
@@ -27,16 +27,16 @@ void Scene::setSkybox(Skybox* skybox) noexcept {
|
||||
downcast(this)->setSkybox(downcast(skybox));
|
||||
}
|
||||
|
||||
Skybox* Scene::getSkybox() const noexcept {
|
||||
Skybox* Scene::getSkybox() noexcept {
|
||||
return downcast(this)->getSkybox();
|
||||
}
|
||||
|
||||
void Scene::setIndirectLight(IndirectLight* ibl) noexcept {
|
||||
downcast(this)->setIndirectLight(downcast(ibl));
|
||||
Skybox const* Scene::getSkybox() const noexcept {
|
||||
return downcast(this)->getSkybox();
|
||||
}
|
||||
|
||||
IndirectLight* Scene::getIndirectLight() const noexcept {
|
||||
return downcast(this)->getIndirectLight();
|
||||
void Scene::setIndirectLight(IndirectLight const* ibl) noexcept {
|
||||
downcast(this)->setIndirectLight(downcast(ibl));
|
||||
}
|
||||
|
||||
void Scene::addEntity(Entity entity) {
|
||||
|
||||
+158
-136
@@ -78,19 +78,7 @@ void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType,
|
||||
}
|
||||
|
||||
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
|
||||
auto z_axis = direction;
|
||||
auto norm_up = float3{ 0, 1, 0 };
|
||||
if (UTILS_UNLIKELY(std::abs(dot(z_axis, norm_up)) > 0.999f)) {
|
||||
// Fix up vector if we're degenerate (looking straight up, basically)
|
||||
norm_up = { norm_up.z, norm_up.x, norm_up.y };
|
||||
}
|
||||
auto x_axis = normalize(cross(z_axis, norm_up));
|
||||
auto y_axis = cross(x_axis, z_axis);
|
||||
const mat4f Mm{
|
||||
float4{ x_axis, 0 },
|
||||
float4{ y_axis, 0 },
|
||||
float4{ -z_axis, 0 },
|
||||
float4{ position, 1 }};
|
||||
const mat4f Mm = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
|
||||
return FCamera::rigidTransformInverse(Mm);
|
||||
}
|
||||
|
||||
@@ -380,9 +368,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
// Computes St the transform to use in the shader to access the shadow map texture
|
||||
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
|
||||
const backend::Viewport viewport = getViewport();
|
||||
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport);
|
||||
const mat4f St = math::highPrecisionMultiply(Mt, S);
|
||||
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
|
||||
const mat4f St = mat4f(MbMt * S);
|
||||
|
||||
ShadowMap::ShaderParameters shaderParameters;
|
||||
|
||||
@@ -390,7 +377,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
|
||||
// if Wp[3][1] is 0, then LISPSM was cancelled.
|
||||
if (useLispsm && Wp[3][1] != 0.0f) {
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(Mt * F),
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F),
|
||||
shadowMapInfo.shadowDimension);
|
||||
} else {
|
||||
// We know we're using an ortho projection
|
||||
@@ -403,8 +390,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
|
||||
}
|
||||
|
||||
shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
|
||||
|
||||
// We apply the constant bias in world space (as opposed to light-space) to account
|
||||
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
|
||||
// by baking it in the shadow-map itself.
|
||||
@@ -419,8 +404,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
// The model matrix below is in fact inverted to get the view matrix and passed to the
|
||||
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
|
||||
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
|
||||
mCamera->setModelMatrix(FCamera::rigidTransformInverse(math::highPrecisionMultiplyd(Mv, b)));
|
||||
mCamera->setCustomProjection(mat4(Mn * F * W * L * Mp), znear, zfar);
|
||||
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
|
||||
mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar);
|
||||
|
||||
// for the debug camera, we need to undo the world origin
|
||||
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
|
||||
@@ -428,18 +413,48 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
ShadowMap::ShaderParameters ShadowMap::updatePunctual(
|
||||
mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
|
||||
const ShadowMapInfo& shadowMapInfo, const FLightManager::ShadowParams& params) noexcept {
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
|
||||
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo,
|
||||
FScene const& scene, SceneInfo sceneInfo) noexcept {
|
||||
|
||||
assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize);
|
||||
ShaderParameters shaderParameters;
|
||||
|
||||
auto& lcm = engine.getLightManager();
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
|
||||
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
|
||||
/*
|
||||
* Compute the light model matrix.
|
||||
*/
|
||||
|
||||
// Choose a reasonable value for the near plane.
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
|
||||
// find decent near/far
|
||||
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
|
||||
|
||||
// if the scene was empty, near > far
|
||||
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
|
||||
|
||||
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
|
||||
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
|
||||
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
|
||||
|
||||
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
|
||||
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
|
||||
|
||||
// Final shadow transform
|
||||
const backend::Viewport viewport = getViewport();
|
||||
const mat4f S = math::highPrecisionMultiply(Mp, Mv);
|
||||
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport);
|
||||
const mat4f St = math::highPrecisionMultiply(Mt, S);
|
||||
const mat4f S = MpMv;
|
||||
|
||||
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
|
||||
const mat4f St = mat4f(MbMt * S);
|
||||
|
||||
// TODO: focus projection
|
||||
// 1) focus on the casters
|
||||
@@ -454,9 +469,7 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
|
||||
// = zInLightSpace * texelSizeAtOneMeter
|
||||
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
|
||||
// Note: this would not work with LISPSM, which warps the texture space.
|
||||
ShaderParameters shaderParameters;
|
||||
shaderParameters.texelSizeAtOneMeterWs =
|
||||
(2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
|
||||
shaderParameters.texelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
|
||||
shaderParameters.lightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
|
||||
|
||||
if (!shadowMapInfo.vsm) {
|
||||
@@ -465,11 +478,9 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
|
||||
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
|
||||
}
|
||||
|
||||
shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
|
||||
|
||||
const float3 direction = -transpose(Mv)[2].xyz;
|
||||
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
const mat4f Sb = S * b;
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so that
|
||||
// the cameraPosition uniform gets set correctly.
|
||||
@@ -479,47 +490,22 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
|
||||
// The model matrix below is in fact inverted to get the view matrix and passed to the
|
||||
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
|
||||
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
|
||||
mCamera->setModelMatrix(FCamera::rigidTransformInverse(math::highPrecisionMultiplyd(Mv, b)));
|
||||
mCamera->setCustomProjection(math::highPrecisionMultiplyd(Mn, Mp), nearPlane, farPlane);
|
||||
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
|
||||
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
|
||||
|
||||
// for the debug camera, we need to undo the world origin
|
||||
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
|
||||
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo,
|
||||
FScene const& scene, SceneInfo sceneInfo) noexcept {
|
||||
|
||||
auto& lcm = engine.getLightManager();
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
|
||||
// find decent near/far
|
||||
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
|
||||
|
||||
// if the scene was empty, near > far
|
||||
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
|
||||
if (!mHasVisibleShadows) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
|
||||
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
|
||||
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
|
||||
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
return updatePunctual(Mv, outerConeAngle, nearPlane, farPlane, shadowMapInfo, params);
|
||||
}
|
||||
|
||||
ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo, uint8_t face) noexcept {
|
||||
|
||||
ShaderParameters shaderParameters;
|
||||
|
||||
// check if this shadow map has anything to render
|
||||
mHasVisibleShadows = false;
|
||||
FScene::RenderableSoa const& UTILS_RESTRICT soa = scene.getRenderableData();
|
||||
@@ -532,16 +518,53 @@ ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
|
||||
}
|
||||
}
|
||||
if (!mHasVisibleShadows) {
|
||||
return {};
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
|
||||
/*
|
||||
* Compute the light model matrix.
|
||||
*/
|
||||
|
||||
const mat4f Mv = getPointLightViewMatrix(TextureCubemapFace(face), position);
|
||||
return updatePunctual(Mv, 45.0f * f::DEG_TO_RAD, 0.01f, radius, shadowMapInfo, params);
|
||||
const float3 direction = -transpose(Mv)[2].xyz;
|
||||
|
||||
// TODO: don't hardcode near plane
|
||||
// Choose a reasonable value for the near plane.
|
||||
float nearPlane = 0.01f;
|
||||
float farPlane = radius;
|
||||
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, nearPlane, farPlane);
|
||||
|
||||
// For calculating the point light normal bias, we need the texel size in world space at the
|
||||
// sample location. Using Thales's theorem, we find:
|
||||
// texelSize(zInLightSpace) = zInLightSpace * texelSizeOnTheNearPlane / near
|
||||
// = zInLightSpace * texelSizeAtOneMeter
|
||||
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
|
||||
// Note: this would not work with LISPSM, which warps the texture space.
|
||||
shaderParameters.texelSizeAtOneMeterWs =
|
||||
(2.0f * std::tan(f::PI_4) / float(shadowMapInfo.shadowDimension));
|
||||
|
||||
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so that
|
||||
// the cameraPosition uniform gets set correctly.
|
||||
// mLightSpace is used in the shader to access the shadow map texture, and has the model matrix
|
||||
// baked in.
|
||||
|
||||
// The model matrix below is in fact inverted to get the view matrix and passed to the
|
||||
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
|
||||
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
|
||||
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
|
||||
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
|
||||
|
||||
return shaderParameters;
|
||||
}
|
||||
|
||||
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
@@ -623,40 +646,50 @@ mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
|
||||
|
||||
// Apply these remapping in double to maintain a high precision for the depth axis
|
||||
ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info,
|
||||
backend::Viewport const& viewport) noexcept {
|
||||
mat4 ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info) noexcept {
|
||||
// remapping from NDC to texture coordinates (i.e. [-1,1] -> [0, 1])
|
||||
// ([1, 0] for depth mapping)
|
||||
constexpr mat4f Mt{
|
||||
mat4f::row_major_init{
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, -0.5f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
}};
|
||||
const mat4f Mt(info.clipSpaceFlipped ? mat4f::row_major_init{
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, -0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, -0.5f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
} : mat4f::row_major_init{
|
||||
0.5f, 0.0f, 0.0f, 0.5f,
|
||||
0.0f, 0.5f, 0.0f, 0.5f,
|
||||
0.0f, 0.0f, -0.5f, 0.5f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
});
|
||||
|
||||
// apply the viewport transform
|
||||
const float2 o = float2{ viewport.left, viewport.bottom } / float(info.atlasDimension);
|
||||
const float2 s = float2{ viewport.width, viewport.height } / float(info.atlasDimension);
|
||||
const mat4f Mv{
|
||||
mat4f::row_major_init{
|
||||
s.x, 0.0f, 0.0f, o.x,
|
||||
0.0f, s.y, 0.0f, o.y,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
}};
|
||||
// the shadow map texture might be larger than the shadow map dimension, so we add a scaling
|
||||
// factor
|
||||
const float v = float(info.textureDimension) / float(info.atlasDimension);
|
||||
const mat4f Mv(mat4f::row_major_init{
|
||||
v, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, v, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
});
|
||||
|
||||
// this is equivalent to call uvToRenderTargetUV() in the shader in each texture access
|
||||
const mat4f Mf = info.textureSpaceFlipped ? mat4f{
|
||||
mat4f::row_major_init{
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
}} : mat4f{};
|
||||
// apply the 1-texel border viewport transform
|
||||
const float o = 1.0f / float(info.atlasDimension);
|
||||
const float s = 1.0f - 2.0f * (1.0f / float(info.textureDimension));
|
||||
const mat4f Mb(mat4f::row_major_init{
|
||||
s, 0.0f, 0.0f, o,
|
||||
0.0f, s, 0.0f, o,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
});
|
||||
|
||||
// Compute shadow-map texture access and viewport transform
|
||||
return { Mf * (Mv * Mt), inverse(Mt) * (Mv * Mt) };
|
||||
const mat4f Mf = info.textureSpaceFlipped ? mat4f(mat4f::row_major_init{
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
}) : mat4f();
|
||||
|
||||
// Compute shadow-map texture access transform
|
||||
return mat4(Mf * Mb * Mv * Mt);
|
||||
}
|
||||
|
||||
mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
|
||||
@@ -1151,8 +1184,6 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
|
||||
void ShadowMap::initSceneInfo(ShadowMap::SceneInfo& sceneInfo,
|
||||
uint8_t visibleLayers, FScene const& scene, mat4f const& viewMatrix) {
|
||||
sceneInfo.csNearFar = { -1.0f, 1.0f };
|
||||
sceneInfo.lsNearFar = {};
|
||||
sceneInfo.visibleLayers = visibleLayers;
|
||||
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
|
||||
@@ -1212,42 +1243,26 @@ void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene,
|
||||
);
|
||||
}
|
||||
|
||||
backend::Viewport ShadowMap::getViewport() const noexcept {
|
||||
filament::Viewport ShadowMap::getViewport() const noexcept {
|
||||
// We set a viewport with a 1-texel border for when we index outside the
|
||||
// texture. This can only happen for the directional light when "focus shadow casters is used".
|
||||
// texture.
|
||||
// DON'T CHANGE this unless ShadowMap::getTextureCoordsMapping() is updated too.
|
||||
// see: ShadowMap::getTextureCoordsMapping()
|
||||
//
|
||||
// For floating-point depth textures, the 1-texel border could be set to
|
||||
// FLOAT_MAX to avoid clamping in the shadow shader (see sampleDepth inside
|
||||
// shadowing.fs). Unfortunately, the APIs don't seem let us clear depth
|
||||
// attachments to anything greater than 1.0, so we'd need a way to do this other
|
||||
// than clearing.
|
||||
const uint32_t dim = mOptions->mapSize;
|
||||
const uint16_t border = 1u;
|
||||
return { border, border, dim - 2u * border, dim - 2u * border };
|
||||
}
|
||||
|
||||
backend::Viewport ShadowMap::getScissor() const noexcept {
|
||||
// We set a viewport with a 1-texel border for when we index outside the
|
||||
// texture. This can only happen for the directional light when "focus shadow casters is used".
|
||||
const uint32_t dim = mOptions->mapSize;
|
||||
const uint16_t border = 1u;
|
||||
|
||||
switch (mShadowType) {
|
||||
case ShadowType::DIRECTIONAL:
|
||||
return { border, border, dim - 2u * border, dim - 2u * border };
|
||||
case ShadowType::SPOT:
|
||||
case ShadowType::POINT:
|
||||
default:
|
||||
return { 0, 0, dim, dim };
|
||||
if (isPointShadow()) {
|
||||
// for point-light we don't have a border
|
||||
return { 0, 0, dim, dim };
|
||||
} else {
|
||||
return { 1, 1, dim - 2, dim - 2 };
|
||||
}
|
||||
}
|
||||
|
||||
math::float4 ShadowMap::getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept {
|
||||
const auto [l, b, w, h] = getViewport();
|
||||
const float texel = 1.0f / float(shadowMapInfo.atlasDimension);
|
||||
const float4 v = float4{ l, b, l + w, b + h } * texel;
|
||||
if (shadowMapInfo.textureSpaceFlipped) {
|
||||
// this is equivalent to calling uvToRenderTargetUV() in the shader *after* clamping
|
||||
// texture coordinates to this normalized viewport.
|
||||
return { v.x, 1.0f - v.w, v.z, 1.0f - v.y };
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void ShadowMap::prepareCamera(Transaction const& transaction,
|
||||
@@ -1257,8 +1272,8 @@ void ShadowMap::prepareCamera(Transaction const& transaction,
|
||||
}
|
||||
|
||||
void ShadowMap::prepareViewport(Transaction const& transaction,
|
||||
backend::Viewport const& viewport) noexcept {
|
||||
PerShadowMapUniforms::prepareViewport(transaction, viewport);
|
||||
const filament::Viewport& viewport) noexcept {
|
||||
PerShadowMapUniforms::prepareViewport(transaction, viewport, 0, 0);
|
||||
}
|
||||
|
||||
void ShadowMap::prepareTime(Transaction const& transaction,
|
||||
@@ -1266,6 +1281,13 @@ void ShadowMap::prepareTime(Transaction const& transaction,
|
||||
PerShadowMapUniforms::prepareTime(transaction, engine, userTime);
|
||||
}
|
||||
|
||||
void ShadowMap::prepareDirectionalLight(Transaction const& transaction,
|
||||
FEngine& engine, math::float3 const& sceneSpaceDirection,
|
||||
LightManager::Instance instance) noexcept {
|
||||
PerShadowMapUniforms::prepareDirectionalLight(transaction,
|
||||
engine, 1.0f, sceneSpaceDirection, instance);
|
||||
}
|
||||
|
||||
void ShadowMap::prepareShadowMapping(Transaction const& transaction,
|
||||
bool highPrecision) noexcept {
|
||||
PerShadowMapUniforms::prepareShadowMapping(transaction, highPrecision);
|
||||
@@ -1280,7 +1302,7 @@ void ShadowMap::commit(Transaction& transaction,
|
||||
mPerShadowMapUniforms.commit(transaction, driver);
|
||||
}
|
||||
|
||||
void ShadowMap::bind(backend::DriverApi& driver) const noexcept {
|
||||
void ShadowMap::bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept {
|
||||
mPerShadowMapUniforms.bind(driver);
|
||||
}
|
||||
|
||||
|
||||
+13
-21
@@ -24,7 +24,7 @@
|
||||
#include "details/Camera.h"
|
||||
#include "details/Scene.h"
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
@@ -87,7 +87,10 @@ public:
|
||||
// e.g., for a texture dimension of 512, shadowDimension would be 510
|
||||
uint16_t shadowDimension = 0;
|
||||
|
||||
// e.g. metal and vulkan textures
|
||||
// e.g. Vulkan clip-space
|
||||
bool clipSpaceFlipped = false;
|
||||
|
||||
// e.g. metal textures
|
||||
bool textureSpaceFlipped = false;
|
||||
|
||||
// whether we're using vsm
|
||||
@@ -127,7 +130,6 @@ public:
|
||||
struct ShaderParameters {
|
||||
math::mat4f lightSpace{};
|
||||
math::float4 lightFromWorldZ{};
|
||||
math::float4 scissorNormalized{};
|
||||
float texelSizeAtOneMeterWs{};
|
||||
};
|
||||
|
||||
@@ -172,14 +174,13 @@ public:
|
||||
static void updateSceneInfoSpot(const math::mat4f& Mv, FScene const& scene,
|
||||
SceneInfo& sceneInfo);
|
||||
|
||||
filament::Viewport getViewport() const noexcept;
|
||||
|
||||
LightManager::ShadowOptions const* getShadowOptions() const noexcept { return mOptions; }
|
||||
size_t getLightIndex() const { return mLightIndex; }
|
||||
uint16_t getShadowIndex() const { return mShadowIndex; }
|
||||
void setLayer(uint8_t layer) noexcept { mLayer = layer; }
|
||||
uint8_t getLayer() const noexcept { return mLayer; }
|
||||
backend::Viewport getViewport() const noexcept;
|
||||
backend::Viewport getScissor() const noexcept;
|
||||
|
||||
bool isDirectionalShadow() const noexcept { return mShadowType == ShadowType::DIRECTIONAL; }
|
||||
bool isSpotShadow() const noexcept { return mShadowType == ShadowType::SPOT; }
|
||||
bool isPointShadow() const noexcept { return mShadowType == ShadowType::POINT; }
|
||||
@@ -191,15 +192,18 @@ public:
|
||||
static void prepareCamera(Transaction const& transaction,
|
||||
FEngine& engine, const CameraInfo& cameraInfo) noexcept;
|
||||
static void prepareViewport(Transaction const& transaction,
|
||||
backend::Viewport const& viewport) noexcept;
|
||||
const filament::Viewport& viewport) noexcept;
|
||||
static void prepareTime(Transaction const& transaction,
|
||||
FEngine& engine, math::float4 const& userTime) noexcept;
|
||||
static void prepareDirectionalLight(Transaction const& transaction,
|
||||
FEngine& engine, math::float3 const& sceneSpaceDirection,
|
||||
LightManager::Instance instance) noexcept;
|
||||
static void prepareShadowMapping(Transaction const& transaction,
|
||||
bool highPrecision) noexcept;
|
||||
static PerShadowMapUniforms::Transaction open(backend::DriverApi& driver) noexcept;
|
||||
void commit(Transaction& transaction,
|
||||
backend::DriverApi& driver) const noexcept;
|
||||
void bind(backend::DriverApi& driver) const noexcept;
|
||||
void bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept;
|
||||
|
||||
private:
|
||||
struct Segment {
|
||||
@@ -213,11 +217,6 @@ private:
|
||||
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
|
||||
using FrustumBoxIntersection = std::array<math::float3, 64>;
|
||||
|
||||
ShaderParameters updatePunctual(
|
||||
math::mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
|
||||
const ShadowMapInfo& shadowMapInfo,
|
||||
const FLightManager::ShadowParams& params) noexcept;
|
||||
|
||||
static math::mat4f applyLISPSM(math::mat4f& Wp,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
const math::mat4f& LMpMv,
|
||||
@@ -277,18 +276,11 @@ private:
|
||||
|
||||
static math::mat4f directionalLightFrustum(float n, float f) noexcept;
|
||||
|
||||
struct TextureCoordsMapping {
|
||||
math::mat4f clipToTexture;
|
||||
math::mat4f clipToNdc;
|
||||
};
|
||||
static TextureCoordsMapping getTextureCoordsMapping(ShadowMapInfo const& info,
|
||||
backend::Viewport const& viewport) noexcept;
|
||||
static math::mat4 getTextureCoordsMapping(ShadowMapInfo const& info) noexcept;
|
||||
|
||||
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
|
||||
const math::mat4f& Mv, float znear, float zfar) noexcept;
|
||||
|
||||
math::float4 getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept;
|
||||
|
||||
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) const noexcept;
|
||||
|
||||
|
||||
@@ -97,10 +97,8 @@ void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
|
||||
LightManager::ShadowOptions const* options) noexcept {
|
||||
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
|
||||
for (size_t c = 0; c < options->shadowCascades; c++) {
|
||||
const size_t i = c;
|
||||
assert_invariant(i < CONFIG_MAX_SHADOW_CASCADES);
|
||||
auto* pShadowMap = getShadowMap(i);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, i, 0, options);
|
||||
auto* pShadowMap = getCascadeShadowMap(c);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, c, 0, options);
|
||||
mCascadeShadowMaps.push_back(pShadowMap);
|
||||
}
|
||||
}
|
||||
@@ -109,19 +107,17 @@ void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
|
||||
LightManager::ShadowOptions const* options) noexcept {
|
||||
if (spotlight) {
|
||||
const size_t c = mSpotShadowMaps.size();
|
||||
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
|
||||
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
|
||||
auto* pShadowMap = getShadowMap(i);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::SPOT, i, 0, options);
|
||||
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
|
||||
auto* pShadowMap = getPointOrSpotShadowMap(c);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::SPOT, c, 0, options);
|
||||
mSpotShadowMaps.push_back(pShadowMap);
|
||||
} else {
|
||||
// point-light, generate 6 independent shadowmaps
|
||||
// point-light
|
||||
for (size_t face = 0; face < 6; face++) {
|
||||
const size_t c = mSpotShadowMaps.size();
|
||||
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
|
||||
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
|
||||
auto* pShadowMap = getShadowMap(i);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::POINT, i, face, options);
|
||||
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
|
||||
auto* pShadowMap = getPointOrSpotShadowMap(c);
|
||||
pShadowMap->initialize(lightIndex, ShadowType::POINT, c, face, options);
|
||||
mSpotShadowMaps.push_back(pShadowMap);
|
||||
}
|
||||
}
|
||||
@@ -159,11 +155,9 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
utils::FixedCapacityVector<ShadowPass> passList;
|
||||
};
|
||||
|
||||
VsmShadowOptions const& vsmShadowOptions = view.getVsmShadowOptions();
|
||||
|
||||
auto& prepareShadowPass = fg.addPass<PrepareShadowPassData>("Prepare Shadow Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
data.passList.reserve(CONFIG_MAX_SHADOWMAPS);
|
||||
data.passList.reserve(CONFIG_MAX_SHADOW_LAYERS);
|
||||
data.shadows = builder.createTexture("Shadowmap", {
|
||||
.width = textureRequirements.size, .height = textureRequirements.size,
|
||||
.depth = textureRequirements.layers,
|
||||
@@ -205,8 +199,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
// "read" from one of its resource (only writes), so the FrameGraph culls it.
|
||||
builder.sideEffect();
|
||||
},
|
||||
[this, &engine, &view, vsmShadowOptions,
|
||||
scene, mainCameraInfo, userTime, passTemplate = pass](
|
||||
[this, &engine, &view, scene, mainCameraInfo, userTime, passTemplate = pass](
|
||||
FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
|
||||
// Note: we could almost parallel_for the loop below, the problem currently is
|
||||
@@ -236,15 +229,15 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
}
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
// Note: this loop can generate a lot of commands that come out of the
|
||||
// "per frame command arena". The allocation persists until the
|
||||
// end of the frame.
|
||||
// One way to possibly mitigate this, would be to always use the
|
||||
// same command buffer for all shadow map, but then we'd generate
|
||||
// a lot of unneeded draw calls.
|
||||
// To do this efficiently, we'd need a way to cull draw calls already
|
||||
// recorded in the command buffer, per shadow map.
|
||||
// TODO: this loop can generate a lot of commands. investigate if
|
||||
// we could compact or reduce the number of commands.
|
||||
// - we could move this into the execute of each pass, but
|
||||
// then we would have to update the UBO each time.
|
||||
// - we could also always use the same commands for all
|
||||
// shadow maps, but then we'd generate a lot of
|
||||
// unneeded draw call.
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
|
||||
// cameraInfo only valid after calling update
|
||||
const CameraInfo cameraInfo{ shadowMap.getCamera() };
|
||||
@@ -254,14 +247,18 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
|
||||
ShadowMap::prepareTime(transaction, engine, userTime);
|
||||
ShadowMap::prepareShadowMapping(transaction,
|
||||
vsmShadowOptions.highPrecision);
|
||||
view.getVsmShadowOptions().highPrecision);
|
||||
if (shadowMap.getShadowType() == ShadowType::DIRECTIONAL) {
|
||||
ShadowMap::prepareDirectionalLight(transaction, engine,
|
||||
scene->getLightData().elementAt<FScene::DIRECTION>(0),
|
||||
scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(0));
|
||||
}
|
||||
shadowMap.commit(transaction, driver);
|
||||
|
||||
// updatePrimitivesLod must be run before RenderPass::appendCommands.
|
||||
view.updatePrimitivesLod(engine,
|
||||
cameraInfo, scene->getRenderableData(), entry.range);
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
RenderPass pass(passTemplate);
|
||||
pass.setCamera(cameraInfo);
|
||||
pass.setVisibilityMask(entry.visibilityMask);
|
||||
@@ -273,13 +270,22 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
entry.executor = pass.getExecutor();
|
||||
|
||||
if (!view.hasVSM()) {
|
||||
auto const* options = shadowMap.getShadowOptions();
|
||||
auto li = scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(
|
||||
entry.shadowMap->getLightIndex());
|
||||
|
||||
auto const& params = engine.getLightManager().getShadowParams(li);
|
||||
|
||||
const PolygonOffset polygonOffset = { // handle reversed Z
|
||||
.slope = -options->polygonOffsetSlope,
|
||||
.constant = -options->polygonOffsetConstant
|
||||
.slope = -params.options.polygonOffsetSlope,
|
||||
.constant = -params.options.polygonOffsetConstant
|
||||
};
|
||||
entry.executor.overridePolygonOffset(&polygonOffset);
|
||||
}
|
||||
|
||||
constexpr const backend::Viewport disabledScissor{ 0, 0,
|
||||
(uint32_t)std::numeric_limits<int32_t>::max(),
|
||||
(uint32_t)std::numeric_limits<int32_t>::max() };
|
||||
entry.executor.overrideScissor(&disabledScissor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +314,6 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
|
||||
const uint8_t layer = entry.shadowMap->getLayer();
|
||||
const auto* options = entry.shadowMap->getShadowOptions();
|
||||
const auto msaaSamples = textureRequirements.msaaSamples;
|
||||
|
||||
auto& shadowPass = fg.addPass<ShadowPassData>("Shadow Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
@@ -327,7 +332,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
// We specify the sample count here because we don't need automatic resolve.
|
||||
auto depth = builder.createTexture("Temporary VSM Depth Texture", {
|
||||
.width = textureRequirements.size, .height = textureRequirements.size,
|
||||
.samples = msaaSamples,
|
||||
.samples = options->vsm.msaaSamples,
|
||||
.format = TextureFormat::DEPTH16,
|
||||
});
|
||||
|
||||
@@ -350,7 +355,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
|
||||
// we need to clear the shadow map with the max EVSM moments
|
||||
renderTargetDesc.clearColor = vsmClearColor;
|
||||
renderTargetDesc.samples = msaaSamples;
|
||||
renderTargetDesc.samples = options->vsm.msaaSamples;
|
||||
|
||||
if (blur) {
|
||||
data.tempBlurSrc = builder.write(data.tempBlurSrc,
|
||||
@@ -361,7 +366,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
.color = { data.tempBlurSrc },
|
||||
.depth = depth },
|
||||
.clearColor = vsmClearColor,
|
||||
.samples = msaaSamples,
|
||||
.samples = options->vsm.msaaSamples,
|
||||
.clearFlags = TargetBufferFlags::COLOR
|
||||
| TargetBufferFlags::DEPTH
|
||||
});
|
||||
@@ -388,15 +393,16 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
// `entry` lives in `PrepareShadowPassData` which is guaranteed to still
|
||||
// be alive when we execute here (all passes stay alive until the FrameGraph
|
||||
// is destroyed).
|
||||
// It wouldn't work to capture by copy because entry.executor wouldn't be
|
||||
// It wouldn't work to capture by copy because entry.pass wouldn't be
|
||||
// initialized, as this happens in an `execute` block.
|
||||
|
||||
entry.shadowMap->bindPerViewUniformsAndSamplers(driver);
|
||||
|
||||
auto rt = resources.getRenderPassInfo(data.rt);
|
||||
rt.params.viewport = entry.shadowMap->getViewport();
|
||||
|
||||
engine.flush();
|
||||
driver.beginRenderPass(rt.target, rt.params);
|
||||
entry.shadowMap->bind(driver);
|
||||
entry.executor.overrideScissor(entry.shadowMap->getScissor());
|
||||
entry.executor.execute(engine, "Shadow Pass");
|
||||
driver.endRenderPass();
|
||||
});
|
||||
@@ -447,8 +453,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options.mapSize),
|
||||
.shadowDimension = uint16_t(options.mapSize - 2u),
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
|
||||
engine.getBackend() == Backend::VULKAN,
|
||||
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
@@ -458,7 +464,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
// entire camera frustum, as if we only had a single cascade.
|
||||
ShadowMap& shadowMap = *mCascadeShadowMaps[0];
|
||||
|
||||
shadowMap.updateDirectional(mEngine,
|
||||
auto shaderParameters = shadowMap.updateDirectional(mEngine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
hasVisibleShadows = shadowMap.hasVisibleShadows();
|
||||
@@ -467,6 +473,18 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
|
||||
|
||||
// Set shadowBias, using the first directional cascade.
|
||||
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
|
||||
// Texel size is constant for directional light (although that's not true when LISPSM
|
||||
// is used, but in that case we're pretending it is).
|
||||
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||
mShadowMappingUniforms.shadowBias = normalBias * wsTexelSize;
|
||||
mShadowMappingUniforms.shadowBulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
|
||||
mShadowMappingUniforms.elvsm = options.vsm.elvsm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,10 +537,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
|
||||
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
|
||||
|
||||
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
|
||||
|
||||
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
|
||||
assert_invariant(mCascadeShadowMaps[i]);
|
||||
|
||||
@@ -536,23 +550,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
assert_invariant(shadowIndex == i);
|
||||
|
||||
// Texel size is constant for directional light (although that's not true when LISPSM
|
||||
// is used, but in that case we're pretending it is).
|
||||
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
|
||||
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
|
||||
|
||||
mShadowMappingUniforms.lightFromWorldMatrix[i] = shaderParameters.lightSpace;
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
cascadeHasVisibleShadows |= 0x1u << i;
|
||||
}
|
||||
@@ -632,7 +630,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
// compute shadow map frustum for culling
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, 0.01f, radius);
|
||||
const mat4f MpMv = math::highPrecisionMultiply(Mp, Mv);
|
||||
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
|
||||
const Frustum frustum(MpMv);
|
||||
|
||||
// Cull shadow casters
|
||||
@@ -662,8 +660,8 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
|
||||
engine.getBackend() == Backend::VULKAN,
|
||||
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
@@ -680,9 +678,8 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].direction = direction;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
@@ -690,8 +687,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -743,8 +739,8 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize), // point-lights don't have a border
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
|
||||
engine.getBackend() == Backend::VULKAN,
|
||||
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
|
||||
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
|
||||
.vsm = view.hasVSM()
|
||||
};
|
||||
|
||||
@@ -753,6 +749,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
|
||||
|
||||
// and if we need to generate it, update all the UBO data
|
||||
// Note: this below is done for all six faces even if it sets identical values each time
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
@@ -762,11 +759,16 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
auto& s = mShadowUb.edit();
|
||||
const double n = shadowMap.getCamera().getNear();
|
||||
const double f = shadowMap.getCamera().getCullingFar();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = {}; // no texture matrix for point lights
|
||||
s.shadows[shadowIndex].direction = {}; // no direction of point lights
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = {
|
||||
-((n + f) / (f - n)) * 0.5f + 0.5f,
|
||||
(f * n) / (f - n),
|
||||
-n / (f - n),
|
||||
1.0f / (f - n),
|
||||
};
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
@@ -786,12 +788,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
for (auto const* pShadowMap : mSpotShadowMaps) {
|
||||
const size_t lightIndex = pShadowMap->getLightIndex();
|
||||
// gather the per-light (not per shadow map) information. For point lights we will
|
||||
// "see" 6 shadowmaps (one per face), we must use the first face one, the shader
|
||||
// knows how to find the entry for other faces (they're guaranteed to be sequential).
|
||||
|
||||
// FIXME: currently we have one slot per shadowmap in the UBO, but we now have up to
|
||||
// 6 shadowmap per light. So for now, we only write the data of the face 0,
|
||||
// and the shader will figure out where to find the other face (layer+face)
|
||||
if (pShadowMap->getFace() == 0) {
|
||||
shadowInfo[lightIndex].castsShadows = true; // FIXME: is that set correctly?
|
||||
shadowInfo[lightIndex].index = pShadowMap->getShadowIndex();
|
||||
shadowInfo[lightIndex].layer = pShadowMap->getLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -842,8 +846,6 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
const bool useMipmapping = view.hasVSM() &&
|
||||
((vsmShadowOptions.anisotropy > 0) || vsmShadowOptions.mipmapping);
|
||||
|
||||
const uint8_t msaaSamples = vsmShadowOptions.msaaSamples;
|
||||
|
||||
TextureFormat format = TextureFormat::DEPTH16;
|
||||
if (view.hasVSM()) {
|
||||
if (vsmShadowOptions.highPrecision) {
|
||||
@@ -875,7 +877,6 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
(uint16_t)maxDimension,
|
||||
layersNeeded,
|
||||
mipLevels,
|
||||
msaaSamples,
|
||||
format
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,10 +45,14 @@ class FrameGraph;
|
||||
class RenderPass;
|
||||
|
||||
struct ShadowMappingUniforms {
|
||||
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
|
||||
math::float4 cascadeSplits;
|
||||
float shadowBulbRadiusLs;
|
||||
float shadowBias;
|
||||
float ssContactShadowDistance;
|
||||
uint32_t directionalShadows;
|
||||
uint32_t cascades;
|
||||
bool elvsm;
|
||||
};
|
||||
|
||||
class ShadowMapManager {
|
||||
@@ -89,13 +93,23 @@ public:
|
||||
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg, RenderPass const& pass,
|
||||
FView& view, CameraInfo const& mainCameraInfo, math::float4 const& userTime) noexcept;
|
||||
|
||||
ShadowMap* getShadowMap(size_t index) noexcept {
|
||||
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
|
||||
return std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[index]));
|
||||
ShadowMap* getCascadeShadowMap(size_t cascade) noexcept {
|
||||
assert_invariant(cascade < CONFIG_MAX_SHADOW_CASCADES);
|
||||
return std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[cascade]));
|
||||
}
|
||||
|
||||
ShadowMap const* getShadowMap(size_t index) const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getShadowMap(index);
|
||||
ShadowMap const* getCascadeShadowMap(size_t cascade) const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getCascadeShadowMap(cascade);
|
||||
}
|
||||
|
||||
ShadowMap* getPointOrSpotShadowMap(size_t index) noexcept {
|
||||
assert_invariant(index < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
|
||||
return std::launder(reinterpret_cast<ShadowMap*>(
|
||||
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + index]));
|
||||
}
|
||||
|
||||
ShadowMap const* getPointOrSpotShadowMap(size_t spot) const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getPointOrSpotShadowMap(spot);
|
||||
}
|
||||
|
||||
// valid after calling update() above
|
||||
@@ -180,7 +194,6 @@ private:
|
||||
uint16_t size = 0;
|
||||
uint8_t layers = 0;
|
||||
uint8_t levels = 0;
|
||||
uint8_t msaaSamples = 1;
|
||||
backend::TextureFormat format = backend::TextureFormat::DEPTH16;
|
||||
} mTextureAtlasRequirements;
|
||||
|
||||
@@ -202,13 +215,13 @@ private:
|
||||
|
||||
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
|
||||
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
|
||||
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
|
||||
CONFIG_MAX_SHADOWMAP_PUNCTUAL) };
|
||||
|
||||
// inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
|
||||
// because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
|
||||
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
|
||||
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
|
||||
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
|
||||
std::array<ShadowMapStorage, CONFIG_MAX_SHADOW_LAYERS> mShadowMapCache;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <filament/ToneMapper.h>
|
||||
|
||||
#include "ColorSpaceUtils.h"
|
||||
#include "ColorSpace.h"
|
||||
|
||||
#include <math/vec3.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user