Merge branch 'rc/1.23.2' into release
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
FROM python:3.10.4
|
||||
|
||||
RUN pip3 install pygithub==1.55
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
COPY verify_release_notes.py /verify_release_notes.py
|
||||
|
||||
RUN chmod +x /verify_release_notes.py
|
||||
ENTRYPOINT [ "/verify_release_notes.py" ]
|
||||
@@ -0,0 +1,35 @@
|
||||
name: 'Verify Release Notes'
|
||||
description: 'Verifies that a RELEASE_NOTES file has been modified'
|
||||
inputs:
|
||||
repo-token:
|
||||
description: 'The GitHub token'
|
||||
required: true
|
||||
pull-request-number:
|
||||
description: 'The Pull Request number'
|
||||
required: true
|
||||
bypass-label-name:
|
||||
description: 'The Label used to bypass this check'
|
||||
required: false
|
||||
default: 'internal'
|
||||
release-notes-file:
|
||||
description: 'The path to the RELEASE_NOTES file'
|
||||
required: false
|
||||
default: 'RELEASE_NOTES.md'
|
||||
pull-request-repo-full-name:
|
||||
description: 'The full name of the Pull Request repo'
|
||||
required: false
|
||||
default: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
pull-request-head-ref:
|
||||
description: 'The HEAD ref of the Pull Request'
|
||||
required: false
|
||||
default: ${{ github.event.pull_request.head.ref }}
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- ${{ inputs.repo-token }}
|
||||
- ${{ inputs.pull-request-number }}
|
||||
- ${{ inputs.bypass-label-name }}
|
||||
- ${{ inputs.release-notes-file }}
|
||||
- ${{ inputs.pull-request-repo-full-name }}
|
||||
- ${{ inputs.pull-request-head-ref }}
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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.
|
||||
|
||||
from github import Github
|
||||
import sys, os
|
||||
|
||||
def print_usage():
|
||||
print('Verify that a GitHub pull request modifies a RELEASE_NOTES file')
|
||||
print()
|
||||
print('Usage:')
|
||||
print(' verify_release_notes.py [arguments]')
|
||||
print()
|
||||
print('Arguments:')
|
||||
print('1. github token')
|
||||
print('2. pull request number')
|
||||
print('3. bypass label name')
|
||||
print('4. release notes file path')
|
||||
print('5. pull request repo full name')
|
||||
print('6. pull request head ref')
|
||||
print()
|
||||
print('The GITHUB_REPOSITORY environment variable must be set (e.g., google/filament).')
|
||||
|
||||
def leave_single_comment(pull_request, comment_body):
|
||||
""" Leaves a comment on a PR once, without leaving a duplicate comment. """
|
||||
# To avoid spamming the PR author, we'll use this comment tag (which will render invisibly on
|
||||
# GitHub) to check if we've already left a comment on this PR.
|
||||
COMMENT_TAG = '<!-- verify_release_notes -->\n'
|
||||
comments = pull_request.get_issue_comments()
|
||||
for comment in comments:
|
||||
if comment.body.find(COMMENT_TAG) != -1:
|
||||
return
|
||||
# The GitHub token may not have WRITE permissions to leave a comment (for example, for 3P
|
||||
# contributors). In that case, we simply won't leave a comment.
|
||||
try:
|
||||
pull_request.create_issue_comment(f'{COMMENT_TAG}{comment_body}')
|
||||
except:
|
||||
print("Unable to leave comment. Continuing.")
|
||||
|
||||
# The first argument is the path to this script.
|
||||
if len(sys.argv) != 7:
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
authentication_token = sys.argv[1]
|
||||
pull_number = sys.argv[2]
|
||||
bypass_label_name = sys.argv[3]
|
||||
release_notes_file = sys.argv[4]
|
||||
pr_repo_full_name = sys.argv[5]
|
||||
pr_head_ref = sys.argv[6]
|
||||
|
||||
g = Github(authentication_token)
|
||||
|
||||
repo_name = os.environ.get('GITHUB_REPOSITORY')
|
||||
if repo_name is None:
|
||||
print("The GITHUB_REPOSITORY environment variable must be set.")
|
||||
sys.exit(1)
|
||||
|
||||
repo = g.get_repo(repo_name)
|
||||
|
||||
pull_request = repo.get_pull(int(pull_number))
|
||||
|
||||
# First check if the PR has the "bypass" label. This label is used for PRs that don't need to update
|
||||
# RELEASE_NOTES. If so, we can exit immediately.
|
||||
labels = [l.name for l in pull_request.labels]
|
||||
if bypass_label_name in labels:
|
||||
print(f"PR number {pull_number} in repo {repo_name} contains the '{bypass_label_name}' label.")
|
||||
print("Exiting with success.")
|
||||
sys.exit(0)
|
||||
|
||||
# Next, check if the release notes file (RELEASE_NOTES.md or similar) has been modified.
|
||||
files = pull_request.get_files()
|
||||
for file in files:
|
||||
if file.filename == release_notes_file:
|
||||
print(f"PR number {pull_number} in repo {repo_name} modifies '{release_notes_file}'.")
|
||||
print("Exiting with success.")
|
||||
sys.exit(0)
|
||||
|
||||
# At this point, we issue a warning to the PR author to remember to modify the release notes, and
|
||||
# exit with failure.
|
||||
edit_url = f"https://github.com/{pr_repo_full_name}/edit/{pr_head_ref}/{release_notes_file}"
|
||||
comment = (f"Please add a release note line to [{release_notes_file}]({edit_url}). "
|
||||
f"If this PR does not warrant a release note, add the '{bypass_label_name}' label "
|
||||
f"to this PR.")
|
||||
print(comment)
|
||||
leave_single_comment(pull_request, comment)
|
||||
|
||||
sys.exit(1)
|
||||
@@ -33,10 +33,6 @@ jobs:
|
||||
with:
|
||||
name: gltfio-android-release
|
||||
path: out/gltfio-android-release.aar
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: gltfio-android-lite-release
|
||||
path: out/gltfio-android-lite-release.aar
|
||||
- uses: actions/upload-artifact@v1.0.0
|
||||
with:
|
||||
name: filament-utils-android-release
|
||||
|
||||
@@ -126,7 +126,6 @@ jobs:
|
||||
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/gltfio-android-lite-release.aar out/gltfio-${TAG}-lite-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:
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
name: Verify Release Notes
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
verify-release-notes:
|
||||
name: Verify Release Notes
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# We *only* need to check out the .github/ directory.
|
||||
# The verify RELEASE_NOTES script uses the GitHub API to verify that RELEASE_NOTES was
|
||||
# modified.
|
||||
- name: Check out action
|
||||
uses: Bhacaz/checkout-files@73e17cfbe8d7e0c6b2672b20cb05a718e20d18d4
|
||||
with:
|
||||
files: .github
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Verify release notes
|
||||
uses: ./.github/actions/verify-release-notes
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pull-request-number: ${{ github.event.pull_request.number }}
|
||||
+3
-1
@@ -650,6 +650,7 @@ add_subdirectory(${LIBRARIES}/image)
|
||||
add_subdirectory(${LIBRARIES}/ktxreader)
|
||||
add_subdirectory(${LIBRARIES}/math)
|
||||
add_subdirectory(${LIBRARIES}/mathio)
|
||||
add_subdirectory(${LIBRARIES}/uberz)
|
||||
add_subdirectory(${LIBRARIES}/utils)
|
||||
add_subdirectory(${LIBRARIES}/viewer)
|
||||
add_subdirectory(${FILAMENT}/filament)
|
||||
@@ -721,9 +722,10 @@ if (IS_HOST_PLATFORM)
|
||||
add_subdirectory(${TOOLS}/rgb-to-lmsr)
|
||||
add_subdirectory(${TOOLS}/roughness-prefilter)
|
||||
add_subdirectory(${TOOLS}/specular-color)
|
||||
add_subdirectory(${TOOLS}/uberz)
|
||||
endif()
|
||||
|
||||
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
export(TARGETS matc cmgen filamesh mipgen resgen glslminifier FILE ${IMPORT_EXECUTABLES})
|
||||
export(TARGETS matc cmgen filamesh mipgen resgen uberz glslminifier FILE ${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.23.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.23.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.23.1'
|
||||
pod 'Filament', '~> 1.23.2'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -5,6 +5,18 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## main branch
|
||||
|
||||
## v1.23.2
|
||||
|
||||
- gltfio: Fix morphing for un-packed accessors.
|
||||
- gltfio: Ubershaders are now packaged into flexible archives.
|
||||
- gltfio: Remove poorly maintained lite flavor.
|
||||
- engine: Disable user scissor while rendering the Shadow Maps.
|
||||
- engine: Merge identical backend `RenderPrimitives` together.
|
||||
- engine: Improve `ResourceAllocator` performance a bit by reserving 128 cache entries.
|
||||
- utils: Remove `std::hash<T>` definitions for `libutils` types. Use `T::Hasher` explicitly instead. [⚠️ **API Change**]
|
||||
- backend: Fix WGL context attributes.
|
||||
- Metal: Fix potential invalid shaders when using gltfio in Ubershader mode. [⚠️ **Recompile Materials to get the fix**]
|
||||
|
||||
## v1.23.1
|
||||
|
||||
- gltfio: support skinning with bones that do not belong to any scene.
|
||||
|
||||
@@ -31,14 +31,14 @@ add_library(utils STATIC IMPORTED)
|
||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||
|
||||
add_library(uberzlib STATIC IMPORTED)
|
||||
set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a)
|
||||
|
||||
add_library(gltfio_resources STATIC IMPORTED)
|
||||
set_target_properties(gltfio_resources PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_resources.a)
|
||||
|
||||
add_library(gltfio_resources_lite STATIC IMPORTED)
|
||||
set_target_properties(gltfio_resources_lite PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_resources_lite.a)
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map")
|
||||
|
||||
set(GLTFIO_SRCS
|
||||
@@ -85,7 +85,6 @@ set(GLTFIO_SRCS
|
||||
src/main/cpp/UbershaderLoader.cpp
|
||||
src/main/cpp/ResourceLoader.cpp
|
||||
|
||||
${FILAMENT_DIR}/include/gltfio/resources/gltfresources_lite.h
|
||||
${FILAMENT_DIR}/include/gltfio/resources/gltfresources.h
|
||||
|
||||
../common/NioUtils.cpp
|
||||
@@ -109,16 +108,8 @@ add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
|
||||
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
|
||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
|
||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
|
||||
|
||||
if(GLTFIO_LITE)
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_LITE=1)
|
||||
target_link_libraries(gltfio-jni filament-jni utils log stb ktxreader basis_transcoder zstd gltfio_resources_lite)
|
||||
else()
|
||||
target_link_libraries(gltfio-jni filament-jni utils log stb ktxreader basis_transcoder zstd gltfio_resources)
|
||||
|
||||
# Enable Draco in the non-lite variant of gltfio.
|
||||
target_link_libraries(gltfio-jni dracodec)
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
|
||||
endif()
|
||||
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd gltfio_resources)
|
||||
target_link_libraries(gltfio-jni dracodec)
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
|
||||
|
||||
@@ -6,16 +6,6 @@ android {
|
||||
full {
|
||||
dimension "functionality"
|
||||
}
|
||||
|
||||
lite {
|
||||
dimension "functionality"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments.add("-DGLTFIO_LITE=ON")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
// No need to package up the following shared libs, which arise as a side effect of our
|
||||
@@ -43,11 +33,6 @@ afterEvaluate { project ->
|
||||
artifactId = POM_ARTIFACT_ID_FULL
|
||||
from components.fullRelease
|
||||
}
|
||||
|
||||
liteRelease(MavenPublication) {
|
||||
artifactId = POM_ARTIFACT_ID_LITE
|
||||
from components.liteRelease
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.23.1
|
||||
VERSION_NAME=1.23.2
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
# Host tools required by Android, WebGL, and iOS builds
|
||||
MOBILE_HOST_TOOLS="matc resgen cmgen filamesh"
|
||||
MOBILE_HOST_TOOLS="matc resgen cmgen filamesh uberz"
|
||||
WEB_HOST_TOOLS="${MOBILE_HOST_TOOLS} mipgen filamesh"
|
||||
|
||||
function print_help {
|
||||
@@ -478,14 +478,12 @@ function build_android {
|
||||
|
||||
if [[ "${INSTALL_COMMAND}" ]]; then
|
||||
echo "Installing out/filamat-android-debug.aar..."
|
||||
cp filamat-android/build/outputs/aar/filamat-android-lite-debug.aar ../out/
|
||||
cp filamat-android/build/outputs/aar/filamat-android-full-debug.aar ../out/filamat-android-debug.aar
|
||||
|
||||
echo "Installing out/filament-android-debug.aar..."
|
||||
cp filament-android/build/outputs/aar/filament-android-debug.aar ../out/
|
||||
|
||||
echo "Installing out/gltfio-android-debug.aar..."
|
||||
cp gltfio-android/build/outputs/aar/gltfio-android-lite-debug.aar ../out/
|
||||
cp gltfio-android/build/outputs/aar/gltfio-android-full-debug.aar ../out/gltfio-android-debug.aar
|
||||
|
||||
echo "Installing out/filament-utils-android-debug.aar..."
|
||||
|
||||
@@ -120,7 +120,6 @@ cmake ..\.. ^
|
||||
@echo on
|
||||
cmake --build . --target filagui --config %config% || exit /b
|
||||
cmake --build . --target gltfio_resources --config %config% || exit /b
|
||||
cmake --build . --target gltfio_resources_lite --config %config% || exit /b
|
||||
cmake --build . --target gltf-resources --config %config% || exit /b
|
||||
cmake --build . --target filamentapp-resources --config %config% || exit /b
|
||||
cmake --build . --target sample-resources --config %config% || exit /b
|
||||
|
||||
@@ -63,6 +63,7 @@ set(SRCS
|
||||
src/FrameSkipper.cpp
|
||||
src/Froxelizer.cpp
|
||||
src/Frustum.cpp
|
||||
src/HwRenderPrimitiveFactory.cpp
|
||||
src/IndexBuffer.cpp
|
||||
src/IndirectLight.cpp
|
||||
src/LightManager.cpp
|
||||
@@ -140,6 +141,7 @@ set(PRIVATE_HDRS
|
||||
src/FrameInfo.h
|
||||
src/FrameSkipper.h
|
||||
src/Froxelizer.h
|
||||
src/HwRenderPrimitiveFactory.h
|
||||
src/Intersections.h
|
||||
src/MaterialParser.h
|
||||
src/PerViewUniforms.h
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Command debugging off. debugging virtuals are not called.
|
||||
|
||||
@@ -17,17 +17,18 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
|
||||
#include <array>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class SamplerGroup {
|
||||
public:
|
||||
@@ -151,7 +152,6 @@ private:
|
||||
mutable utils::bitset32 mDirty;
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
|
||||
@@ -102,7 +102,7 @@ private:
|
||||
// These two fields store a callback and user data to notify the client that a frame is ready
|
||||
// for presentation.
|
||||
// If frameScheduledCallback is nullptr, then the Metal backend automatically calls
|
||||
// presentDrawable when the frame is commited.
|
||||
// presentDrawable when the frame is committed.
|
||||
// Otherwise, the Metal backend will not automatically present the frame. Instead, clients bear
|
||||
// the responsibility of presenting the frame by calling the PresentCallable object.
|
||||
FrameScheduledCallback frameScheduledCallback = nullptr;
|
||||
|
||||
@@ -35,6 +35,8 @@ public:
|
||||
private:
|
||||
ShaderModel getShaderModel() const noexcept final;
|
||||
|
||||
uint64_t nextFakeHandle = 1;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
@@ -50,7 +52,7 @@ private:
|
||||
|
||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName##S() noexcept override { \
|
||||
return RetType((RetType::HandleId)0xDEAD0000); } \
|
||||
return RetType((RetType::HandleId)nextFakeHandle++); } \
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl) { }
|
||||
|
||||
#include "private/backend/DriverAPI.inc"
|
||||
|
||||
@@ -145,7 +145,6 @@ Driver* PlatformCocoaGL::createDriver(void* sharedContext) noexcept {
|
||||
NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute) 24,
|
||||
NSOpenGLPFADoubleBuffer, (NSOpenGLPixelFormatAttribute) true,
|
||||
NSOpenGLPFAAccelerated, (NSOpenGLPixelFormatAttribute) true,
|
||||
NSOpenGLPFANoRecovery, (NSOpenGLPixelFormatAttribute) true,
|
||||
0, 0,
|
||||
};
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ using namespace backend;
|
||||
struct PlatformCocoaTouchGLImpl {
|
||||
EAGLContext* mGLContext = nullptr;
|
||||
CAEAGLLayer* mCurrentGlLayer = nullptr;
|
||||
std::vector<CAEAGLLayer*> mHeadlessGlLayers;
|
||||
CGRect mCurrentGlLayerRect;
|
||||
GLuint mDefaultFramebuffer = 0;
|
||||
GLuint mDefaultColorbuffer = 0;
|
||||
@@ -108,11 +109,28 @@ Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(void* nativewindow, u
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
|
||||
// TODO: implement headless SwapChain
|
||||
return nullptr;
|
||||
CAEAGLLayer* glLayer = [CAEAGLLayer layer];
|
||||
glLayer.frame = CGRectMake(0, 0, width, height);
|
||||
|
||||
// adding the pointer to the array retains the CAEAGLLayer
|
||||
pImpl->mHeadlessGlLayers.push_back(glLayer);
|
||||
|
||||
return (__bridge SwapChain*) glLayer;
|
||||
}
|
||||
|
||||
void PlatformCocoaTouchGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
CAEAGLLayer* glLayer = (__bridge CAEAGLLayer*) swapChain;
|
||||
|
||||
if (pImpl->mCurrentGlLayer == glLayer) {
|
||||
pImpl->mCurrentGlLayer = nullptr;
|
||||
}
|
||||
|
||||
auto& v = pImpl->mHeadlessGlLayers;
|
||||
auto it = std::find(v.begin(), v.end(), glLayer);
|
||||
if(it != v.end()) {
|
||||
// removing the pointer from the array releases the CAEAGLLayer
|
||||
v.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformCocoaTouchGL::createDefaultRenderTarget(uint32_t& framebuffer, uint32_t& colorbuffer,
|
||||
|
||||
@@ -103,7 +103,6 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext) noexcept {
|
||||
int attribs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_PROFILE_MASK_ARB,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
## Debugging Vulkan on macOS
|
||||
|
||||
See "Enabling Validation" in `third_party/moltenvk/README.md`.
|
||||
Install the LunarG SDK and allow the installer to include "System Global Components", which should
|
||||
be enabled by default.
|
||||
|
||||
## Debugging Vulkan on Linux
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMM
|
||||
using HeapAllocatorArena = utils::Arena<
|
||||
utils::HeapAllocator,
|
||||
utils::LockingPolicy::Mutex,
|
||||
utils::TrackingPolicy::DebugAndHighWatermark>;
|
||||
utils::TrackingPolicy::DebugAndHighWatermark,
|
||||
utils::AreaPolicy::NullArea>;
|
||||
|
||||
using LinearAllocatorArena = utils::Arena<
|
||||
utils::LinearAllocator,
|
||||
@@ -62,7 +63,9 @@ using LinearAllocatorArena = utils::Arena<
|
||||
// intrinsically synchronized as it relies on heap allocations (i.e.: malloc/free)
|
||||
using HeapAllocatorArena = utils::Arena<
|
||||
utils::HeapAllocator,
|
||||
utils::LockingPolicy::NoLock>;
|
||||
utils::LockingPolicy::NoLock,
|
||||
utils::TrackingPolicy::Untracked,
|
||||
utils::AreaPolicy::NullArea>;
|
||||
|
||||
using LinearAllocatorArena = utils::Arena<
|
||||
utils::LinearAllocator,
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "HwRenderPrimitiveFactory.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
bool operator<(HwRenderPrimitiveFactory::Key const& lhs,
|
||||
HwRenderPrimitiveFactory::Key const& rhs) noexcept {
|
||||
if (lhs.vbh == rhs.vbh) {
|
||||
if (lhs.ibh == rhs.ibh) {
|
||||
if (lhs.offset == rhs.offset) {
|
||||
if (lhs.count == rhs.count) {
|
||||
return lhs.type < rhs.type;
|
||||
} else {
|
||||
return lhs.count < rhs.count;
|
||||
}
|
||||
} else {
|
||||
return lhs.offset < rhs.offset;
|
||||
}
|
||||
} else {
|
||||
return lhs.ibh < rhs.ibh;
|
||||
}
|
||||
} else {
|
||||
return lhs.vbh < rhs.vbh;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator<(HwRenderPrimitiveFactory::Entry const& lhs,
|
||||
HwRenderPrimitiveFactory::Entry const& rhs) noexcept {
|
||||
return lhs.key < rhs.key;
|
||||
}
|
||||
|
||||
inline bool operator<(HwRenderPrimitiveFactory::Key const& lhs,
|
||||
HwRenderPrimitiveFactory::Entry const& rhs) noexcept {
|
||||
return lhs < rhs.key;
|
||||
}
|
||||
|
||||
inline bool operator<(HwRenderPrimitiveFactory::Entry const& lhs,
|
||||
HwRenderPrimitiveFactory::Key const& rhs) noexcept {
|
||||
return lhs.key < rhs;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
HwRenderPrimitiveFactory::HwRenderPrimitiveFactory()
|
||||
: mArena("HwRenderPrimitiveFactory::mArena", SET_ARENA_SIZE),
|
||||
mSet(mArena) {
|
||||
mMap.reserve(256);
|
||||
}
|
||||
|
||||
HwRenderPrimitiveFactory::~HwRenderPrimitiveFactory() noexcept = default;
|
||||
|
||||
void HwRenderPrimitiveFactory::terminate(DriverApi& driver) noexcept {
|
||||
assert_invariant(mMap.empty());
|
||||
assert_invariant(mSet.empty());
|
||||
}
|
||||
|
||||
RenderPrimitiveHandle HwRenderPrimitiveFactory::create(DriverApi& driver,
|
||||
VertexBufferHandle vbh, IndexBufferHandle ibh,
|
||||
PrimitiveType type, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
|
||||
uint32_t count) noexcept {
|
||||
|
||||
const Key key = { vbh, ibh, offset, count, type };
|
||||
|
||||
// see if we already have seen this RenderPrimitive
|
||||
auto pos = mSet.find(key);
|
||||
|
||||
// the common case is that we've never seen it (i.e.: no reuse)
|
||||
if (UTILS_LIKELY(pos == mSet.end())) {
|
||||
// create the backend object
|
||||
auto handle = driver.createRenderPrimitive(vbh, ibh,
|
||||
type, offset, minIndex, maxIndex, count);
|
||||
// insert key/handle in our set with a refcount of 1
|
||||
// IMPORTANT: std::set<> doesn't invalidate iterators in insert/erase
|
||||
auto [ipos, _] = mSet.insert({ key, handle, 1 });
|
||||
// map the handle back to the key/payload
|
||||
mMap.insert({ handle.getId(), ipos });
|
||||
return handle;
|
||||
}
|
||||
pos->refs++;
|
||||
return pos->handle;
|
||||
}
|
||||
|
||||
void HwRenderPrimitiveFactory::destroy(DriverApi& driver, RenderPrimitiveHandle rph) noexcept {
|
||||
// look for this handle in our map
|
||||
auto pos = mMap.find(rph.getId());
|
||||
|
||||
// it must be there
|
||||
assert_invariant(pos != mMap.end());
|
||||
|
||||
// check the refcount and destroy if needed
|
||||
auto ipos = pos->second;
|
||||
if (--ipos->refs == 0) {
|
||||
mSet.erase(ipos);
|
||||
mMap.erase(pos);
|
||||
driver.destroyRenderPrimitive(rph);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
|
||||
#define TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <private/backend/DriverApi.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FEngine;
|
||||
|
||||
class HwRenderPrimitiveFactory {
|
||||
public:
|
||||
|
||||
HwRenderPrimitiveFactory();
|
||||
~HwRenderPrimitiveFactory() noexcept;
|
||||
|
||||
HwRenderPrimitiveFactory(HwRenderPrimitiveFactory const& rhs) = delete;
|
||||
HwRenderPrimitiveFactory(HwRenderPrimitiveFactory&& rhs) noexcept = delete;
|
||||
HwRenderPrimitiveFactory& operator=(HwRenderPrimitiveFactory const& rhs) = delete;
|
||||
HwRenderPrimitiveFactory& operator=(HwRenderPrimitiveFactory&& rhs) noexcept = delete;
|
||||
|
||||
void terminate(backend::DriverApi& driver) noexcept;
|
||||
|
||||
backend::RenderPrimitiveHandle create(backend::DriverApi& driver,
|
||||
backend::VertexBufferHandle vbh,
|
||||
backend::IndexBufferHandle ibh,
|
||||
backend::PrimitiveType type,
|
||||
uint32_t offset,
|
||||
uint32_t minIndex,
|
||||
uint32_t maxIndex,
|
||||
uint32_t count) noexcept;
|
||||
|
||||
void destroy(backend::DriverApi& driver,
|
||||
backend::RenderPrimitiveHandle rph) noexcept;
|
||||
|
||||
private:
|
||||
struct Key { // 20 bytes
|
||||
backend::VertexBufferHandle vbh; // 4
|
||||
backend::IndexBufferHandle ibh; // 4
|
||||
uint32_t offset; // 4
|
||||
uint32_t count; // 4
|
||||
backend::PrimitiveType type; // 4
|
||||
};
|
||||
|
||||
struct Entry { // 28 bytes
|
||||
Key key; // 20
|
||||
backend::RenderPrimitiveHandle handle; // 4
|
||||
mutable uint32_t refs; // 4
|
||||
};
|
||||
|
||||
|
||||
// the std::set<T> node size is sizeof(std::set<T>::node_type) + sizeof(T), aligned.
|
||||
// we need this value for our pool allocator. On clang 64 bits this is 56 bytes.
|
||||
using DummySetAllocator = utils::STLAllocator<Entry, utils::Arena<
|
||||
utils::PoolAllocator<8>, utils::LockingPolicy::NoLock>>;
|
||||
using DummySetNode = std::pair<std::set<Entry, std::less<>, DummySetAllocator>::node_type, Entry>;
|
||||
|
||||
// Size of the arena used for the "set" part of the bimap
|
||||
static constexpr size_t SET_ARENA_SIZE = 4 * 1024 * 1024;
|
||||
|
||||
// Arena for the set<>, using a pool allocator inside a heap area.
|
||||
using Arena = utils::Arena<
|
||||
utils::PoolAllocator<sizeof(DummySetNode)>,
|
||||
utils::LockingPolicy::NoLock,
|
||||
#ifndef NDEBUG
|
||||
utils::TrackingPolicy::HighWatermark,
|
||||
#else
|
||||
utils::TrackingPolicy::Untracked,
|
||||
#endif
|
||||
utils::AreaPolicy::HeapArea>;
|
||||
|
||||
using Set = std::set<
|
||||
Entry,
|
||||
std::less<>,
|
||||
utils::STLAllocator<Entry, Arena>>;
|
||||
|
||||
using Map = tsl::robin_map<
|
||||
backend::RenderPrimitiveHandle::HandleId,
|
||||
Set::const_iterator>;
|
||||
|
||||
// Arena where the set memory is allocated
|
||||
Arena mArena;
|
||||
|
||||
// set of HwRenderPrimitive data
|
||||
Set mSet;
|
||||
|
||||
// map of RenderPrimitiveHandle to Set Entry
|
||||
Map mMap;
|
||||
|
||||
friend bool operator<(Key const& lhs, Key const& rhs) noexcept;
|
||||
friend bool operator<(Key const& lhs, Entry const& rhs) noexcept;
|
||||
friend bool operator<(Entry const& lhs, Key const& rhs) noexcept;
|
||||
friend bool operator<(Entry const& lhs, Entry const& rhs) noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
|
||||
@@ -316,7 +316,12 @@ private:
|
||||
mutable bool mHasMaterial{};
|
||||
};
|
||||
|
||||
tsl::robin_map<utils::StaticString, PostProcessMaterial> mMaterialRegistry;
|
||||
using MaterialRegistryMap = tsl::robin_map<
|
||||
utils::StaticString,
|
||||
PostProcessMaterial,
|
||||
utils::StaticString::Hasher>;
|
||||
|
||||
MaterialRegistryMap mMaterialRegistry;
|
||||
|
||||
void registerPostProcessMaterial(utils::StaticString name, uint8_t const* data, int size);
|
||||
PostProcessMaterial& getPostProcessMaterial(utils::StaticString name) noexcept;
|
||||
|
||||
@@ -81,12 +81,18 @@ void RenderPass::setCamera(const CameraInfo& camera) noexcept {
|
||||
mCameraForwardVector = camera.getForwardVector();
|
||||
}
|
||||
|
||||
void RenderPass::overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept {
|
||||
void RenderPass::overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept {
|
||||
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) {
|
||||
mPolygonOffset = *polygonOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::overrideScissor(backend::Viewport const* scissor) noexcept {
|
||||
if ((mScissorOverride = (scissor != nullptr))) {
|
||||
mScissor = *scissor;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
@@ -580,10 +586,16 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
|
||||
if (first != last) {
|
||||
SYSTRACE_VALUE32("commandCount", last - first);
|
||||
|
||||
PolygonOffset dummyPolyOffset;
|
||||
PipelineState pipeline{ .polygonOffset = mPolygonOffset };
|
||||
PolygonOffset* const pPipelinePolygonOffset =
|
||||
mPolygonOffsetOverride ? &dummyPolyOffset : &pipeline.polygonOffset;
|
||||
PipelineState pipeline{
|
||||
.polygonOffset = mPolygonOffset,
|
||||
.scissor = mScissor
|
||||
}, dummyPipeline;
|
||||
|
||||
auto* const pPipelinePolygonOffset =
|
||||
mPolygonOffsetOverride ? &dummyPipeline.polygonOffset : &pipeline.polygonOffset;
|
||||
|
||||
auto* const pScissor =
|
||||
mScissorOverride ? &dummyPipeline.scissor : &pipeline.scissor;
|
||||
|
||||
Handle<HwBufferObject> uboHandle = mUboHandle;
|
||||
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
|
||||
@@ -616,7 +628,7 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
|
||||
// this is always taken the first time
|
||||
mi = info.mi;
|
||||
ma = mi->getMaterial();
|
||||
pipeline.scissor = mi->getScissor();
|
||||
*pScissor = mi->getScissor();
|
||||
*pPipelinePolygonOffset = mi->getPolygonOffset();
|
||||
mi->use(driver);
|
||||
}
|
||||
@@ -657,7 +669,9 @@ RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command
|
||||
: mEngine(pass->mEngine), mBegin(b), mEnd(e),
|
||||
mCustomCommands(pass->mCustomCommands), mUboHandle(pass->mUboHandle),
|
||||
mPolygonOffset(pass->mPolygonOffset),
|
||||
mPolygonOffsetOverride(pass->mPolygonOffsetOverride) {
|
||||
mScissor(pass->mScissor),
|
||||
mPolygonOffsetOverride(pass->mPolygonOffsetOverride),
|
||||
mScissorOverride(pass->mScissorOverride) {
|
||||
assert_invariant(b >= pass->begin());
|
||||
assert_invariant(e <= pass->end());
|
||||
}
|
||||
|
||||
@@ -276,7 +276,10 @@ public:
|
||||
~RenderPass() noexcept;
|
||||
|
||||
// if non-null, overrides the material's polygon offset
|
||||
void overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept;
|
||||
void overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept;
|
||||
|
||||
// if non-null, overrides the material's scissor
|
||||
void overrideScissor(backend::Viewport const* scissor) noexcept;
|
||||
|
||||
// specifies the geometry to generate commands for
|
||||
void setGeometry(FScene::RenderableSoa const& soa, utils::Range<uint32_t> vr,
|
||||
@@ -330,7 +333,9 @@ public:
|
||||
const CustomCommandVector mCustomCommands;
|
||||
const backend::Handle<backend::HwBufferObject> mUboHandle;
|
||||
const backend::PolygonOffset mPolygonOffset;
|
||||
const bool mPolygonOffsetOverride;
|
||||
const backend::Viewport mScissor;
|
||||
const bool mPolygonOffsetOverride : 1;
|
||||
const bool mScissorOverride : 1;
|
||||
|
||||
Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept;
|
||||
|
||||
@@ -430,9 +435,15 @@ private:
|
||||
// whether to override the polygon offset setting
|
||||
bool mPolygonOffsetOverride = false;
|
||||
|
||||
// whether to override the polygon offset setting
|
||||
bool mScissorOverride = false;
|
||||
|
||||
// value of the override
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
|
||||
// value of scissor override
|
||||
backend::Viewport mScissor{};
|
||||
|
||||
// a vector for our custom commands
|
||||
mutable Executor::CustomCommandVector mCustomCommands;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
void FRenderPrimitive::init(backend::DriverApi& driver,
|
||||
void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
const RenderableManager::Builder::Entry& entry) noexcept {
|
||||
|
||||
assert_invariant(entry.materialInstance);
|
||||
@@ -42,7 +42,7 @@ void FRenderPrimitive::init(backend::DriverApi& driver,
|
||||
auto const& ebh = vertexBuffer->getHwHandle();
|
||||
auto const& ibh = indexBuffer->getHwHandle();
|
||||
|
||||
mHandle = driver.createRenderPrimitive(ebh, ibh, entry.type, (uint32_t)entry.offset,
|
||||
mHandle = factory.create(driver, ebh, ibh, entry.type, (uint32_t)entry.offset,
|
||||
(uint32_t)entry.minIndex, (uint32_t)entry.maxIndex, (uint32_t)entry.count);
|
||||
|
||||
mPrimitiveType = entry.type;
|
||||
@@ -50,14 +50,14 @@ void FRenderPrimitive::init(backend::DriverApi& driver,
|
||||
}
|
||||
}
|
||||
|
||||
void FRenderPrimitive::terminate(FEngine& engine) {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
void FRenderPrimitive::terminate(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver) {
|
||||
if (mHandle) {
|
||||
driver.destroyRenderPrimitive(mHandle);
|
||||
factory.destroy(driver, mHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void FRenderPrimitive::set(FEngine& engine, RenderableManager::PrimitiveType type,
|
||||
void FRenderPrimitive::set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
RenderableManager::PrimitiveType type,
|
||||
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
|
||||
size_t minIndex, size_t maxIndex, size_t count) noexcept {
|
||||
|
||||
@@ -66,13 +66,11 @@ void FRenderPrimitive::set(FEngine& engine, RenderableManager::PrimitiveType typ
|
||||
auto const& ebh = vertices->getHwHandle();
|
||||
auto const& ibh = indices->getHwHandle();
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
if (mHandle) {
|
||||
driver.destroyRenderPrimitive(mHandle);
|
||||
factory.destroy(driver, mHandle);
|
||||
}
|
||||
|
||||
mHandle = driver.createRenderPrimitive(ebh, ibh, type,
|
||||
mHandle = factory.create(driver, ebh, ibh, type,
|
||||
(uint32_t)offset, (uint32_t)minIndex, (uint32_t)maxIndex, (uint32_t)count);
|
||||
|
||||
mPrimitiveType = type;
|
||||
|
||||
@@ -31,19 +31,22 @@ class FEngine;
|
||||
class FVertexBuffer;
|
||||
class FIndexBuffer;
|
||||
class FRenderer;
|
||||
class HwRenderPrimitiveFactory;
|
||||
|
||||
class FRenderPrimitive {
|
||||
public:
|
||||
FRenderPrimitive() noexcept = default;
|
||||
|
||||
void init(backend::DriverApi& driver, const RenderableManager::Builder::Entry& entry) noexcept;
|
||||
void init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
const RenderableManager::Builder::Entry& entry) noexcept;
|
||||
|
||||
void set(FEngine& engine, RenderableManager::PrimitiveType type,
|
||||
void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
RenderableManager::PrimitiveType type,
|
||||
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
|
||||
size_t minIndex, size_t maxIndex, size_t count) noexcept;
|
||||
|
||||
// frees driver resources, object becomes invalid
|
||||
void terminate(FEngine& engine);
|
||||
void terminate(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver);
|
||||
|
||||
const FMaterialInstance* getMaterialInstance() const noexcept { return mMaterialInstance; }
|
||||
backend::Handle<backend::HwRenderPrimitive> getHwHandle() const noexcept { return mHandle; }
|
||||
|
||||
@@ -34,6 +34,17 @@ using namespace backend;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::AssociativeContainer() {
|
||||
mContainer.reserve(128);
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::~AssociativeContainer() noexcept {
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
|
||||
|
||||
@@ -153,8 +153,7 @@ private:
|
||||
template<typename T>
|
||||
struct Hasher<backend::Handle<T>> {
|
||||
std::size_t operator()(backend::Handle<T> const& s) const noexcept {
|
||||
std::hash<typename backend::Handle<T>::HandleId> hash{};
|
||||
return hash(s.getId());
|
||||
return s.getId();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -163,11 +162,14 @@ private:
|
||||
template<typename Key, typename Value, typename Hasher = Hasher<Key>>
|
||||
class AssociativeContainer {
|
||||
// We use a std::vector instead of a std::multimap because we don't expect many items
|
||||
// in the cache and std::multimap generates tons of code. Even with more items, we
|
||||
// could improve this trivially by using a sorted std::vector.
|
||||
// in the cache and std::multimap generates tons of code. std::multimap starts getting
|
||||
// significantly better around 1000 items.
|
||||
using Container = std::vector<std::pair<Key, Value>>;
|
||||
Container mContainer;
|
||||
|
||||
public:
|
||||
AssociativeContainer();
|
||||
~AssociativeContainer() noexcept;
|
||||
using iterator = typename Container::iterator;
|
||||
using const_iterator = typename Container::const_iterator;
|
||||
using key_type = typename Container::value_type::first_type;
|
||||
@@ -186,12 +188,13 @@ private:
|
||||
};
|
||||
|
||||
using CacheContainer = AssociativeContainer<TextureKey, TextureCachePayload>;
|
||||
using InUseContainer = AssociativeContainer<backend::TextureHandle, TextureKey>;
|
||||
|
||||
CacheContainer::iterator purge(CacheContainer::iterator const& pos);
|
||||
|
||||
backend::DriverApi& mBackend;
|
||||
CacheContainer mTextureCache;
|
||||
AssociativeContainer<backend::TextureHandle, TextureKey> mInUseTextures;
|
||||
InUseContainer mInUseTextures;
|
||||
size_t mAge = 0;
|
||||
uint32_t mCacheSize = 0;
|
||||
static constexpr bool mEnabled = true;
|
||||
|
||||
@@ -74,9 +74,13 @@ ShadowMap::~ShadowMap() {
|
||||
|
||||
void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
FScene::VisibleMaskType visibilityMask, RenderPass* const pass) noexcept {
|
||||
const backend::Viewport disabledScissor{ 0, 0,
|
||||
(uint32_t)std::numeric_limits<int32_t>::max(),
|
||||
(uint32_t)std::numeric_limits<int32_t>::max() };
|
||||
pass->setVisibilityMask(visibilityMask);
|
||||
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
|
||||
pass->overrideScissor(&disabledScissor);
|
||||
pass->appendCommands(RenderPass::SHADOW);
|
||||
pass->sortCommands();
|
||||
}
|
||||
|
||||
@@ -334,8 +334,9 @@ void FRenderableManager::create(
|
||||
Builder::Entry const * const entries = builder->mEntries.data();
|
||||
const size_t entryCount = builder->mEntries.size();
|
||||
FRenderPrimitive* rp = new FRenderPrimitive[entryCount];
|
||||
auto& factory = mHwRenderPrimitiveFactory;
|
||||
for (size_t i = 0; i < entryCount; ++i) {
|
||||
rp[i].init(driver, entries[i]);
|
||||
rp[i].init(factory, driver, entries[i]);
|
||||
}
|
||||
setPrimitives(ci, { rp, size_type(entryCount) });
|
||||
|
||||
@@ -463,6 +464,7 @@ void FRenderableManager::terminate() noexcept {
|
||||
manager.removeComponent(manager.getEntity(ci));
|
||||
}
|
||||
}
|
||||
mHwRenderPrimitiveFactory.terminate(mEngine.getDriverApi());
|
||||
}
|
||||
|
||||
void FRenderableManager::gc(utils::EntityManager& em) noexcept {
|
||||
@@ -477,7 +479,7 @@ void FRenderableManager::destroyComponent(Instance ci) noexcept {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// See create(RenderableManager::Builder&, Entity)
|
||||
destroyComponentPrimitives(engine, manager[ci].primitives);
|
||||
destroyComponentPrimitives(mHwRenderPrimitiveFactory, driver, manager[ci].primitives);
|
||||
destroyComponentMorphTargets(engine, manager[ci].morphTargets);
|
||||
|
||||
// destroy the bones structures if any
|
||||
@@ -494,9 +496,10 @@ void FRenderableManager::destroyComponent(Instance ci) noexcept {
|
||||
}
|
||||
|
||||
void FRenderableManager::destroyComponentPrimitives(
|
||||
FEngine& engine, Slice<FRenderPrimitive>& primitives) noexcept {
|
||||
HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
Slice<FRenderPrimitive>& primitives) noexcept {
|
||||
for (auto& primitive : primitives) {
|
||||
primitive.terminate(engine);
|
||||
primitive.terminate(factory, driver);
|
||||
}
|
||||
delete[] primitives.data();
|
||||
}
|
||||
@@ -573,8 +576,8 @@ void FRenderableManager::setGeometryAt(Instance instance, uint8_t level, size_t
|
||||
if (instance) {
|
||||
Slice<FRenderPrimitive>& primitives = getRenderPrimitives(instance, level);
|
||||
if (primitiveIndex < primitives.size()) {
|
||||
primitives[primitiveIndex].set(mEngine, type, vertices, indices, offset,
|
||||
0, vertices->getVertexCount() - 1, count);
|
||||
primitives[primitiveIndex].set(mHwRenderPrimitiveFactory, mEngine.getDriverApi(),
|
||||
type, vertices, indices, offset, 0, vertices->getVertexCount() - 1, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "upcast.h"
|
||||
|
||||
#include "HwRenderPrimitiveFactory.h"
|
||||
#include "UniformBuffer.h"
|
||||
|
||||
#include "private/backend/DriverApiForward.h"
|
||||
@@ -175,7 +176,8 @@ public:
|
||||
|
||||
private:
|
||||
void destroyComponent(Instance ci) noexcept;
|
||||
static void destroyComponentPrimitives(FEngine& engine,
|
||||
static void destroyComponentPrimitives(
|
||||
HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
utils::Slice<FRenderPrimitive>& primitives) noexcept;
|
||||
static void destroyComponentMorphTargets(FEngine& engine,
|
||||
utils::Slice<MorphTargets>& morphTargets) noexcept;
|
||||
@@ -252,6 +254,7 @@ private:
|
||||
|
||||
Sim mManager;
|
||||
FEngine& mEngine;
|
||||
HwRenderPrimitiveFactory mHwRenderPrimitiveFactory;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(RenderableManager)
|
||||
|
||||
@@ -72,8 +72,8 @@ private:
|
||||
bool hasProperty(const char* name) const noexcept;
|
||||
void* getPropertyAddress(const char* name) noexcept;
|
||||
DataSource getDataSource(const char* name) const noexcept;
|
||||
std::unordered_map<utils::StaticString, void*> mPropertyMap;
|
||||
std::unordered_map<utils::StaticString, DataSource> mDataSourceMap;
|
||||
std::unordered_map<utils::StaticString, void*, utils::StaticString::Hasher> mPropertyMap;
|
||||
std::unordered_map<utils::StaticString, DataSource, utils::StaticString::Hasher> mDataSourceMap;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(DebugRegistry)
|
||||
|
||||
@@ -178,7 +178,8 @@ FEngine::FEngine(Backend backend, Platform* platform, void* sharedGLContext) :
|
||||
mLightManager(*this),
|
||||
mCameraManager(*this),
|
||||
mCommandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE),
|
||||
mPerRenderPassAllocator("per-renderpass allocator", CONFIG_PER_RENDER_PASS_ARENA_SIZE),
|
||||
mPerRenderPassAllocator("FEngine::mPerRenderPassAllocator", CONFIG_PER_RENDER_PASS_ARENA_SIZE),
|
||||
mHeapAllocator("FEngine::mHeapAllocator", AreaPolicy::NullArea{}),
|
||||
mJobSystem(getJobSystemThreadPoolSize()),
|
||||
mEngineEpoch(std::chrono::steady_clock::now()),
|
||||
mDriverBarrier(1),
|
||||
|
||||
@@ -208,7 +208,7 @@ private:
|
||||
* (a vector<> could work, but removes would be O(n)). robin_set<> iterates almost as
|
||||
* nicely as vector<>, which is a good compromise.
|
||||
*/
|
||||
tsl::robin_set<utils::Entity> mEntities;
|
||||
tsl::robin_set<utils::Entity, utils::Entity::Hasher> mEntities;
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
namespace filament {
|
||||
|
||||
class Blackboard {
|
||||
using Container = std::unordered_map<utils::StaticString, FrameGraphHandle>;
|
||||
using Container = std::unordered_map<
|
||||
utils::StaticString,
|
||||
FrameGraphHandle,
|
||||
utils::StaticString::Hasher>;
|
||||
|
||||
public:
|
||||
Blackboard() noexcept;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.23.1"
|
||||
spec.version = "1.23.2"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.23.1/filament-v1.23.1-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.23.2/filament-v1.23.2-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -432,6 +432,7 @@
|
||||
"-lviewer",
|
||||
"-lcivetweb",
|
||||
"-lktxreader",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
@@ -487,6 +488,7 @@
|
||||
"-lviewer",
|
||||
"-lcivetweb",
|
||||
"-lktxreader",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
@@ -604,6 +606,7 @@
|
||||
"-lviewer",
|
||||
"-lcivetweb",
|
||||
"-lktxreader",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
@@ -659,6 +662,7 @@
|
||||
"-lviewer",
|
||||
"-lcivetweb",
|
||||
"-lktxreader",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.gltf-viewer";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
|
||||
@@ -20,7 +20,7 @@ targets:
|
||||
settings:
|
||||
base:
|
||||
OTHER_LDFLAGS: ["-lgltfio_core", "-lgltfio_resources", "-limage", "-lgeometry", "-lstb", "-lbasis_transcoder",
|
||||
"-lzstd", "-lcamutils", "-ldracodec", "-lviewer", "-lcivetweb", "-lktxreader"]
|
||||
"-lzstd", "-lcamutils", "-ldracodec", "-lviewer", "-lcivetweb", "-lktxreader", "-luberzlib"]
|
||||
preBuildScripts:
|
||||
- path: build-resources.sh
|
||||
name: Build Resources
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
};
|
||||
};
|
||||
buildConfigurationList = C7514DDCFDA3C5F803E239F9 /* Build configuration list for PBXProject "hello-gltf" */;
|
||||
compatibilityVersion = "Xcode 11.0";
|
||||
compatibilityVersion = "Xcode 10.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -330,6 +330,8 @@
|
||||
"-ldracodec",
|
||||
"-lktxreader",
|
||||
"-lstb",
|
||||
"-lzstd",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
@@ -380,6 +382,8 @@
|
||||
"-ldracodec",
|
||||
"-lktxreader",
|
||||
"-lstb",
|
||||
"-lzstd",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
@@ -602,6 +606,8 @@
|
||||
"-ldracodec",
|
||||
"-lktxreader",
|
||||
"-lstb",
|
||||
"-lzstd",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
@@ -652,6 +658,8 @@
|
||||
"-ldracodec",
|
||||
"-lktxreader",
|
||||
"-lstb",
|
||||
"-lzstd",
|
||||
"-luberzlib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.hello-gltf";
|
||||
SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}";
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.7">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
|
||||
@@ -12,7 +12,7 @@ targets:
|
||||
- FilamentApp
|
||||
settings:
|
||||
base:
|
||||
OTHER_LDFLAGS: ["-lgltfio_core", "-lgltfio_resources", "-limage", "-lgeometry", "-ldracodec", "-lktxreader", "-lstb"]
|
||||
OTHER_LDFLAGS: ["-lgltfio_core", "-lgltfio_resources", "-limage", "-lgeometry", "-ldracodec", "-lktxreader", "-lstb", "-lzstd", "-luberzlib"]
|
||||
preBuildScripts:
|
||||
- path: build-resources.sh
|
||||
name: Build Resources
|
||||
|
||||
@@ -34,8 +34,6 @@ static void* module = nullptr;
|
||||
bool loadLibrary() {
|
||||
|
||||
#ifndef FILAMENT_VKLIBRARY_PATH
|
||||
// Rather than looking in the working directory, look for the dylib in the same folder that the
|
||||
// executable lives in. This allows MacOS users to run Vulkan-based Filament apps from anywhere.
|
||||
const Path dylibPath = VKLIBRARY_PATH;
|
||||
|
||||
// Provide a value for VK_ICD_FILENAMES only if it has not already been set.
|
||||
|
||||
@@ -469,8 +469,10 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
|
||||
.RegisterPass(CreateWrapOpKillPass())
|
||||
.RegisterPass(CreateDeadBranchElimPass());
|
||||
|
||||
if (config.shaderModel != ShaderModel::GL_CORE_41) {
|
||||
// this triggers a segfault with AMD drivers on MacOS
|
||||
if (config.shaderModel != ShaderModel::GL_CORE_41 ||
|
||||
config.targetApi != MaterialBuilder::TargetApi::OPENGL) {
|
||||
// this triggers a segfault with AMD OpenGL drivers on MacOS
|
||||
// note that Metal also requires this pass in order to correctly generate half-precision MSL
|
||||
optimizer.RegisterPass(CreateMergeReturnPass());
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
void unregisterAll();
|
||||
|
||||
std::size_t numRegistered() const noexcept;
|
||||
size_t numRegistered() const noexcept;
|
||||
|
||||
void getRegisteredMaterials(filament::MaterialInstance** materialList,
|
||||
utils::CString* materialNameList) const;
|
||||
|
||||
@@ -31,6 +31,7 @@ enum class ComponentType {
|
||||
SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1]
|
||||
USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1]
|
||||
HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits.
|
||||
FLOAT, //!< Standard 32-bit float
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,6 +188,20 @@ size_t Transcoder::operator()(float* UTILS_RESTRICT target, void const* UTILS_RE
|
||||
}
|
||||
return required;
|
||||
}
|
||||
case ComponentType::FLOAT: {
|
||||
const uint32_t srcStride =
|
||||
mConfig.inputStrideBytes ? mConfig.inputStrideBytes : (4 * comp);
|
||||
uint8_t const* srcBytes = (uint8_t const*) source;
|
||||
for (size_t i = 0; i < count; ++i, target += comp, srcBytes += srcStride) {
|
||||
// This will never break alignment rules because the glTF spec stipulates that the
|
||||
// stride must be a multiple of the component size.
|
||||
float const* src = (float const*) srcBytes;
|
||||
for (int n = 0; n < comp; ++n) {
|
||||
target[n] = src[n];
|
||||
}
|
||||
}
|
||||
return required;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+58
-85
@@ -55,42 +55,68 @@ if (CMAKE_CROSSCOMPILING)
|
||||
include(${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
|
||||
function(generate_mat TEMPLATE SHADINGMODEL BLENDING)
|
||||
set(DOUBLESIDED false)
|
||||
set(TRANSPARENCY default)
|
||||
set(GENERATED_MAT "${RESOURCE_DIR}/${SHADINGMODEL}_${BLENDING}.mat")
|
||||
configure_file(materials/${TEMPLATE}.mat.in ${GENERATED_MAT})
|
||||
set(MATERIAL_SRCS ${MATERIAL_SRCS} ${GENERATED_MAT} PARENT_SCOPE)
|
||||
set(DOUBLESIDED false)
|
||||
set(TRANSPARENCY default)
|
||||
|
||||
function(generate_ubershader_base SHADINGMODEL BLENDING)
|
||||
configure_file(materials/base.mat.in "${RESOURCE_DIR}/${SHADINGMODEL}_${BLENDING}.mat")
|
||||
configure_file(materials/base.spec.in "${RESOURCE_DIR}/${SHADINGMODEL}_${BLENDING}.spec")
|
||||
set(MATERIAL_NAMES ${MATERIAL_NAMES} "${SHADINGMODEL}_${BLENDING}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(MATERIAL_SRCS)
|
||||
generate_mat(ubershader lit fade)
|
||||
generate_mat(ubershader lit opaque)
|
||||
generate_mat(ubershader lit masked)
|
||||
generate_mat(ubershader specularGlossiness fade)
|
||||
generate_mat(ubershader specularGlossiness opaque)
|
||||
generate_mat(ubershader specularGlossiness masked)
|
||||
generate_mat(ubershader unlit fade)
|
||||
generate_mat(ubershader unlit opaque)
|
||||
generate_mat(ubershader unlit masked)
|
||||
generate_mat(volume lit volume)
|
||||
generate_mat(transmission lit transmission)
|
||||
generate_mat(sheen lit sheen)
|
||||
function(generate_ubershader_custom NAME)
|
||||
configure_file(materials/${NAME}.mat.in "${RESOURCE_DIR}/${NAME}.mat")
|
||||
configure_file(materials/${NAME}.spec.in "${RESOURCE_DIR}/${NAME}.spec")
|
||||
set(MATERIAL_NAMES ${MATERIAL_NAMES} ${NAME} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(RESOURCE_BINS)
|
||||
foreach (input_path ${MATERIAL_SRCS})
|
||||
get_filename_component(basename "${input_path}" NAME_WE)
|
||||
set(output_path "${RESOURCE_DIR}/${basename}.filamat")
|
||||
# Generate or copy mat / spec files and build a list of material names.
|
||||
# The first material becomes the default material in the ubershader archive.
|
||||
|
||||
set(MATERIAL_NAMES)
|
||||
generate_ubershader_base(lit opaque)
|
||||
generate_ubershader_base(lit fade)
|
||||
generate_ubershader_base(lit masked)
|
||||
generate_ubershader_base(specularGlossiness fade)
|
||||
generate_ubershader_base(specularGlossiness opaque)
|
||||
generate_ubershader_base(specularGlossiness masked)
|
||||
generate_ubershader_base(unlit fade)
|
||||
generate_ubershader_base(unlit opaque)
|
||||
generate_ubershader_base(unlit masked)
|
||||
generate_ubershader_custom(volume)
|
||||
generate_ubershader_custom(transmission)
|
||||
generate_ubershader_custom(sheen)
|
||||
|
||||
# Iterate over MATERIAL_NAMES and call matc for each material.
|
||||
|
||||
set(UBERZ_INPUTS)
|
||||
foreach (NAME ${MATERIAL_NAMES})
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${input_path}
|
||||
MAIN_DEPENDENCY ${input_path}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${input_path} to ${output_path}"
|
||||
OUTPUT "${NAME}.filamat"
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o "${NAME}.filamat" "${NAME}.mat"
|
||||
DEPENDS matc "${RESOURCE_DIR}/${NAME}.mat"
|
||||
WORKING_DIRECTORY ${RESOURCE_DIR}
|
||||
COMMENT "Compiling material ${NAME}"
|
||||
)
|
||||
list(APPEND RESOURCE_BINS ${output_path})
|
||||
endforeach()
|
||||
|
||||
# Create the ubershader archive by invoking the uberz tool.
|
||||
|
||||
set(UBERZ_INPUTS)
|
||||
foreach (NAME ${MATERIAL_NAMES})
|
||||
list(APPEND UBERZ_INPUTS "${RESOURCE_DIR}/${NAME}.filamat")
|
||||
list(APPEND UBERZ_INPUTS "${RESOURCE_DIR}/${NAME}.spec")
|
||||
endforeach()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "full.uberz"
|
||||
COMMAND uberz -o "full.uberz" ${MATERIAL_NAMES}
|
||||
DEPENDS uberz ${UBERZ_INPUTS}
|
||||
WORKING_DIRECTORY ${RESOURCE_DIR}
|
||||
COMMENT "Building ubershader archive")
|
||||
|
||||
set(RESOURCE_BINS "${RESOURCE_DIR}/full.uberz")
|
||||
|
||||
# ==================================================================================================
|
||||
# Build resources library
|
||||
# ==================================================================================================
|
||||
@@ -112,64 +138,12 @@ add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC})
|
||||
|
||||
add_library(gltfio_resources ${DUMMY_SRC} ${RESGEN_SOURCE})
|
||||
|
||||
# ==================================================================================================
|
||||
# Build "lite" resources library, which contains only a few materials
|
||||
# ==================================================================================================
|
||||
|
||||
set(LITE_DIR ${CMAKE_CURRENT_BINARY_DIR}/lite)
|
||||
|
||||
function(generate_lite_mat BLENDING)
|
||||
set(DOUBLESIDED false)
|
||||
set(TRANSPARENCY default)
|
||||
set(SHADINGMODEL lit)
|
||||
set(GENERATED_MAT "${LITE_DIR}/${SHADINGMODEL}_${BLENDING}.mat")
|
||||
configure_file(materials/gltflite.mat.in ${GENERATED_MAT})
|
||||
set(MATERIAL_SRCS ${MATERIAL_SRCS} ${GENERATED_MAT} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(MATERIAL_SRCS)
|
||||
generate_lite_mat(fade)
|
||||
generate_lite_mat(opaque)
|
||||
|
||||
foreach (input_path ${MATERIAL_SRCS})
|
||||
get_filename_component(basename "${input_path}" NAME_WE)
|
||||
set(output_path "${LITE_DIR}/${basename}.filamat")
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${input_path}
|
||||
MAIN_DEPENDENCY ${input_path}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${input_path} to ${output_path}"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set(RESOURCE_BINS
|
||||
"${LITE_DIR}/lit_opaque.filamat"
|
||||
"${LITE_DIR}/lit_fade.filamat")
|
||||
|
||||
get_resgen_vars(${LITE_DIR} gltfresources_lite)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen ${RESGEN_FLAGS} ${RESOURCE_BINS}
|
||||
DEPENDS resgen ${RESOURCE_BINS}
|
||||
)
|
||||
|
||||
if (DEFINED RESGEN_SOURCE_FLAGS)
|
||||
set_source_files_properties(${RESGEN_SOURCE} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
|
||||
endif()
|
||||
|
||||
set(DUMMY_SRC "${LITE_DIR}/dummy.c")
|
||||
add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC})
|
||||
|
||||
add_library(gltfio_resources_lite ${DUMMY_SRC} ${RESGEN_SOURCE})
|
||||
|
||||
# ==================================================================================================
|
||||
# Build the "core" library (no filamat)
|
||||
# ==================================================================================================
|
||||
|
||||
include_directories(${PUBLIC_HDR_DIR} ${RESOURCE_DIR})
|
||||
link_libraries(math utils filament cgltf stb ktxreader geometry gltfio_resources tsl trie)
|
||||
link_libraries(math utils filament cgltf stb ktxreader geometry gltfio_resources tsl trie uberzlib)
|
||||
|
||||
add_library(gltfio_core STATIC ${PUBLIC_HDRS} ${SRCS})
|
||||
|
||||
@@ -200,16 +174,15 @@ if (NOT WEBGL AND NOT ANDROID AND NOT IOS)
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
install(TARGETS ${TARGET} gltfio_core gltfio_resources gltfio_resources_lite ARCHIVE DESTINATION lib/${DIST_DIR})
|
||||
install(TARGETS ${TARGET} gltfio_core gltfio_resources ARCHIVE DESTINATION lib/${DIST_DIR})
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/gltfio DESTINATION include)
|
||||
else()
|
||||
|
||||
install(TARGETS gltfio_core gltfio_resources gltfio_resources_lite ARCHIVE DESTINATION lib/${DIST_DIR})
|
||||
install(TARGETS gltfio_core gltfio_resources ARCHIVE DESTINATION lib/${DIST_DIR})
|
||||
# The Image.h header is only needed within Filament and does not need to be distributed.
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/gltfio
|
||||
DESTINATION include
|
||||
PATTERN "Image.h" EXCLUDE)
|
||||
install(FILES ${RESOURCE_DIR}/gltfresources.h DESTINATION include/gltfio/resources)
|
||||
install(FILES ${LITE_DIR}/gltfresources_lite.h DESTINATION include/gltfio/resources)
|
||||
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Description
|
||||
|
||||
`gltfio` is a loader library that consumes `gltf` or `glb` content and produces Filament
|
||||
objects. For usage details, see the docstring for `AssetLoader`.
|
||||
|
||||
gltfio has two plug-in interfaces, `TextureProvider` and `MaterialProvider`. Filament ships with
|
||||
several ready-to-go implementations described below.
|
||||
|
||||
- `MaterialProvider` creates Filament materials in response to certain glTF requirements.
|
||||
- [UbershaderLoader](#ubershaderloader) loads pre-built materials.
|
||||
- `MaterialGenerator` builds materials at run time using the `filamat` library.
|
||||
- `TextureProvider` creates and populates Filament `Texture` objects.
|
||||
- `StbProvider` uses the STB library to read PNG and JPEG files.
|
||||
- `Ktx2Provider` uses the BasisU library to read KTX2 files.
|
||||
|
||||
# UbershaderLoader
|
||||
|
||||
`UbershaderLoader` is a ready-to-go implementation of the `MaterialProvider` interface that should
|
||||
be used in applications that need fast startup times. There is no material compilation that
|
||||
occurs at run time, but the shaders might be relatively large and complex.
|
||||
|
||||
At load time, the ubershader loader consumes an *ubershader archive* which is a precompiled set of
|
||||
materials bundled with formal descriptions of the glTF features that they support.
|
||||
|
||||
The `uberz` command line tool consumes a list of `.spec` and `.filamat` files and produces a single
|
||||
`.uberz` file. For details on these two file formats, see the README in `libs/uberz`.
|
||||
@@ -1,5 +1,5 @@
|
||||
material {
|
||||
name : ubershader_${SHADINGMODEL}_${BLENDING},
|
||||
name : base_${SHADINGMODEL}_${BLENDING},
|
||||
requires : [ uv0, uv1, color ],
|
||||
shadingModel : ${SHADINGMODEL},
|
||||
blending : ${BLENDING},
|
||||
@@ -63,9 +63,17 @@ material {
|
||||
|
||||
// Reflectance
|
||||
{ type : float, name : reflectance }
|
||||
|
||||
${CUSTOM_PARAMS}
|
||||
],
|
||||
}
|
||||
|
||||
vertex {
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
${CUSTOM_VERTEX}
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
highp float2 uvs[2];
|
||||
@@ -164,5 +172,7 @@ fragment {
|
||||
material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb;
|
||||
}
|
||||
#endif
|
||||
|
||||
${CUSTOM_FRAGMENT}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Required Aspects
|
||||
ShadingModel = ${SHADINGMODEL}
|
||||
BlendingMode = ${BLENDING}
|
||||
|
||||
# Core Features
|
||||
VertexColors = optional
|
||||
BaseColorTexture = optional
|
||||
NormalTexture = optional
|
||||
OcclusionTexture = optional
|
||||
EmissiveTexture = optional
|
||||
MetallicRoughnessTexture = optional
|
||||
TextureTransforms = optional
|
||||
|
||||
# ClearCoat Features
|
||||
ClearCoat = optional
|
||||
ClearCoatTexture = optional
|
||||
ClearCoatRoughnessTexture = optional
|
||||
ClearCoatNormalTexture = optional
|
||||
|
||||
# Sheen Features
|
||||
Sheen = unsupported
|
||||
SheenColorTexture = unsupported
|
||||
SheenRoughnessTexture = unsupported
|
||||
|
||||
# Transmission and IOR Features
|
||||
Transmission = unsupported
|
||||
Ior = unsupported
|
||||
TransmissionTexture = unsupported
|
||||
|
||||
# Volume Features
|
||||
Volume = unsupported
|
||||
VolumeThicknessTexture = unsupported
|
||||
@@ -1,101 +0,0 @@
|
||||
material {
|
||||
name : gltflite_${SHADINGMODEL}_${BLENDING},
|
||||
requires : [ uv0, uv1, color ],
|
||||
shadingModel : ${SHADINGMODEL},
|
||||
blending : ${BLENDING},
|
||||
doubleSided : ${DOUBLESIDED},
|
||||
transparency : ${TRANSPARENCY},
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
reflections : screenspace,
|
||||
parameters : [
|
||||
|
||||
// Base Color
|
||||
{ type : int, name : baseColorIndex },
|
||||
{ type : float4, name : baseColorFactor },
|
||||
{ type : sampler2d, name : baseColorMap },
|
||||
|
||||
// Metallic-Roughness Map
|
||||
{ type : int, name : metallicRoughnessIndex },
|
||||
{ type : float, name : metallicFactor },
|
||||
{ type : float, name : roughnessFactor },
|
||||
{ type : sampler2d, name : metallicRoughnessMap },
|
||||
|
||||
// Normal Map
|
||||
{ type : int, name : normalIndex },
|
||||
{ type : float, name : normalScale },
|
||||
{ type : sampler2d, name : normalMap },
|
||||
|
||||
// Ambient Occlusion
|
||||
{ type : int, name : aoIndex },
|
||||
{ type : float, name : aoStrength },
|
||||
{ type : sampler2d, name : occlusionMap },
|
||||
|
||||
// Emissive Map
|
||||
{ type : int, name : emissiveIndex },
|
||||
{ type : float3, name : emissiveFactor },
|
||||
{ type : sampler2d, name : emissiveMap },
|
||||
|
||||
// Reflectance
|
||||
{ type : float, name : reflectance }
|
||||
],
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
float2 uvs[2];
|
||||
uvs[0] = getUV0();
|
||||
uvs[1] = getUV1();
|
||||
|
||||
#if !defined(SHADING_MODEL_UNLIT)
|
||||
if (materialParams.normalIndex > -1) {
|
||||
float2 uv = uvs[materialParams.normalIndex];
|
||||
material.normal = texture(materialParams_normalMap, uv).xyz * 2.0 - 1.0;
|
||||
material.normal.xy *= materialParams.normalScale;
|
||||
}
|
||||
#endif
|
||||
|
||||
prepareMaterial(material);
|
||||
material.baseColor = materialParams.baseColorFactor;
|
||||
|
||||
if (materialParams.baseColorIndex > -1) {
|
||||
float2 uv = uvs[materialParams.baseColorIndex];
|
||||
material.baseColor *= texture(materialParams_baseColorMap, uv);
|
||||
}
|
||||
|
||||
#if defined(BLEND_MODE_TRANSPARENT)
|
||||
material.baseColor.rgb *= material.baseColor.a;
|
||||
#endif
|
||||
|
||||
material.baseColor *= getColor();
|
||||
|
||||
#if !defined(SHADING_MODEL_UNLIT)
|
||||
#if defined(SHADING_MODEL_LIT)
|
||||
material.roughness = materialParams.roughnessFactor;
|
||||
material.metallic = materialParams.metallicFactor;
|
||||
material.reflectance = materialParams.reflectance;
|
||||
#endif
|
||||
|
||||
material.emissive = vec4(materialParams.emissiveFactor.rgb, 0.0);
|
||||
|
||||
if (materialParams.metallicRoughnessIndex > -1) {
|
||||
float2 uv = uvs[materialParams.metallicRoughnessIndex];
|
||||
|
||||
vec4 mr = texture(materialParams_metallicRoughnessMap, uv);
|
||||
material.roughness *= mr.g;
|
||||
material.metallic *= mr.b;
|
||||
}
|
||||
|
||||
if (materialParams.aoIndex > -1) {
|
||||
float2 uv = uvs[materialParams.aoIndex];
|
||||
material.ambientOcclusion = texture(materialParams_occlusionMap, uv).r *
|
||||
materialParams.aoStrength;
|
||||
}
|
||||
if (materialParams.emissiveIndex > -1) {
|
||||
float2 uv = uvs[materialParams.emissiveIndex];
|
||||
material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
material {
|
||||
name : sheen_${SHADINGMODEL}_${BLENDING},
|
||||
name : sheen_ubershader,
|
||||
requires : [ uv0, uv1, color ],
|
||||
shadingModel : ${SHADINGMODEL},
|
||||
shadingModel : lit,
|
||||
blending : opaque,
|
||||
depthWrite : true,
|
||||
doubleSided : ${DOUBLESIDED},
|
||||
transparency : ${TRANSPARENCY},
|
||||
doubleSided : false,
|
||||
transparency : default,
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
@@ -56,9 +56,17 @@ material {
|
||||
|
||||
// Reflectance
|
||||
{ type : float, name : reflectance }
|
||||
|
||||
${CUSTOM_PARAMS}
|
||||
],
|
||||
}
|
||||
|
||||
vertex {
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
${CUSTOM_VERTEX}
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
highp float2 uvs[2];
|
||||
@@ -127,5 +135,7 @@ fragment {
|
||||
uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy;
|
||||
material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb;
|
||||
}
|
||||
|
||||
${CUSTOM_FRAGMENT}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
ShadingModel=lit
|
||||
BlendingMode=opaque
|
||||
|
||||
Transmission=unsupported
|
||||
Volume=unsupported
|
||||
Ior=unsupported
|
||||
ClearCoat=unsupported
|
||||
|
||||
VertexColors=optional
|
||||
BaseColorTexture=optional
|
||||
NormalTexture=optional
|
||||
OcclusionTexture=optional
|
||||
EmissiveTexture=optional
|
||||
MetallicRoughnessTexture=optional
|
||||
TextureTransforms=optional
|
||||
|
||||
Sheen=optional
|
||||
SheenColorTexture=optional
|
||||
SheenRoughnessTexture=optional
|
||||
@@ -1,10 +1,10 @@
|
||||
material {
|
||||
name : transmission_${SHADINGMODEL}_${BLENDING},
|
||||
name : transmission_ubershader,
|
||||
requires : [ uv0, uv1, color ],
|
||||
shadingModel : ${SHADINGMODEL},
|
||||
shadingModel : lit,
|
||||
blending : masked,
|
||||
doubleSided : ${DOUBLESIDED},
|
||||
transparency : ${TRANSPARENCY},
|
||||
doubleSided : false,
|
||||
transparency : default,
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
@@ -53,9 +53,17 @@ material {
|
||||
|
||||
// IOR
|
||||
{ type : float, name : ior }
|
||||
|
||||
${CUSTOM_PARAMS}
|
||||
],
|
||||
}
|
||||
|
||||
vertex {
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
${CUSTOM_VERTEX}
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
highp float2 uvs[2];
|
||||
@@ -117,5 +125,7 @@ fragment {
|
||||
uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy;
|
||||
material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb;
|
||||
}
|
||||
|
||||
${CUSTOM_FRAGMENT}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
ShadingModel=lit
|
||||
|
||||
# Even though the transmission material actually has its
|
||||
# blend mode set to "masked", we want the glTF loader to
|
||||
# think that it is "opaque".
|
||||
BlendingMode=opaque
|
||||
|
||||
Sheen=unsupported
|
||||
Volume=unsupported
|
||||
ClearCoat=unsupported
|
||||
|
||||
VertexColors=optional
|
||||
BaseColorTexture=optional
|
||||
NormalTexture=optional
|
||||
OcclusionTexture=optional
|
||||
EmissiveTexture=optional
|
||||
MetallicRoughnessTexture=optional
|
||||
TextureTransforms=optional
|
||||
|
||||
Transmission=optional
|
||||
Ior=optional
|
||||
TransmissionTexture=optional
|
||||
@@ -1,10 +1,10 @@
|
||||
material {
|
||||
name : volume_${SHADINGMODEL}_${BLENDING},
|
||||
name : volume_ubershader,
|
||||
requires : [ uv0, uv1, color ],
|
||||
shadingModel : ${SHADINGMODEL},
|
||||
shadingModel : lit,
|
||||
blending : masked,
|
||||
doubleSided : ${DOUBLESIDED},
|
||||
transparency : ${TRANSPARENCY},
|
||||
doubleSided : false,
|
||||
transparency : default,
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
@@ -60,9 +60,18 @@ material {
|
||||
|
||||
// IOR
|
||||
{ type : float, name : ior }
|
||||
|
||||
|
||||
${CUSTOM_PARAMS}
|
||||
],
|
||||
}
|
||||
|
||||
vertex {
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
${CUSTOM_VERTEX}
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
highp float2 uvs[2];
|
||||
@@ -132,5 +141,7 @@ fragment {
|
||||
uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy;
|
||||
material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb;
|
||||
}
|
||||
|
||||
${CUSTOM_FRAGMENT}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
ShadingModel=lit
|
||||
|
||||
# NOTE: this intentionally omits the blending mode spec because
|
||||
# Filament requires MASKED blending for KHR_materials_volume.
|
||||
|
||||
# Core Features
|
||||
VertexColors=optional
|
||||
BaseColorTexture=optional
|
||||
NormalTexture=optional
|
||||
OcclusionTexture=optional
|
||||
EmissiveTexture=optional
|
||||
MetallicRoughnessTexture=optional
|
||||
TextureTransforms=optional
|
||||
|
||||
# ClearCoat Features
|
||||
ClearCoat=unsupported
|
||||
|
||||
# Sheen Features
|
||||
Sheen=unsupported
|
||||
SheenColorTexture=unsupported
|
||||
SheenRoughnessTexture=unsupported
|
||||
|
||||
# Transmission and IOR Features
|
||||
Transmission=optional
|
||||
Ior=optional
|
||||
TransmissionTexture=optional
|
||||
|
||||
# Volume Features
|
||||
Volume=optional
|
||||
VolumeThicknessTexture=optional
|
||||
@@ -118,8 +118,8 @@ private:
|
||||
TextureNode* getStatus(filament::Texture* texture);
|
||||
|
||||
// The following maps contain the directed edges in the graph.
|
||||
tsl::robin_map<Entity, EntityNode> mEntityToMaterial;
|
||||
tsl::robin_map<Material*, tsl::robin_set<Entity>> mMaterialToEntity;
|
||||
tsl::robin_map<Entity, EntityNode, Entity::Hasher> mEntityToMaterial;
|
||||
tsl::robin_map<Material*, tsl::robin_set<Entity, Entity::Hasher>> mMaterialToEntity;
|
||||
tsl::robin_map<Material*, MaterialNode> mMaterialToTexture;
|
||||
tsl::robin_map<filament::Texture*, tsl::robin_set<Material*>> mTextureToMaterial;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ struct Skin {
|
||||
|
||||
// The set of all nodes that are influenced by this skin.
|
||||
// This is initially gleaned from the glTF file using the "skin" attribute of each node.
|
||||
tsl::robin_set<utils::Entity> targets;
|
||||
tsl::robin_set<utils::Entity, utils::Entity::Hasher> targets;
|
||||
};
|
||||
|
||||
struct VariantMapping {
|
||||
|
||||
@@ -263,11 +263,4 @@ inline bool getElementType(cgltf_type type, cgltf_component_type ctype,
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool requiresConversion(cgltf_type type, cgltf_component_type ctype) {
|
||||
filament::VertexBuffer::AttributeType permitted;
|
||||
filament::VertexBuffer::AttributeType actual;
|
||||
bool supported = getElementType(type, ctype, &permitted, &actual);
|
||||
return supported && permitted != actual;
|
||||
}
|
||||
|
||||
#endif // GLTFIO_GLTFENUMS_H
|
||||
|
||||
@@ -176,8 +176,9 @@ static ComponentType getComponentType(const cgltf_accessor* accessor) {
|
||||
case cgltf_component_type_r_8u: return ComponentType::UBYTE;
|
||||
case cgltf_component_type_r_16: return ComponentType::SHORT;
|
||||
case cgltf_component_type_r_16u: return ComponentType::USHORT;
|
||||
case cgltf_component_type_r_32f: return ComponentType::FLOAT;
|
||||
case cgltf_component_type_r_32u:
|
||||
default:
|
||||
// This should be unreachable because other types do not require conversion.
|
||||
assert_invariant(false);
|
||||
return {};
|
||||
}
|
||||
@@ -197,6 +198,34 @@ static void convertToFloats(float* dest, const cgltf_accessor* accessor) {
|
||||
transcode(dest, source, accessor->count);
|
||||
}
|
||||
|
||||
static bool requiresConversion(cgltf_type type, cgltf_component_type ctype) {
|
||||
filament::VertexBuffer::AttributeType permitted;
|
||||
filament::VertexBuffer::AttributeType actual;
|
||||
bool supported = getElementType(type, ctype, &permitted, &actual);
|
||||
return supported && permitted != actual;
|
||||
}
|
||||
|
||||
static bool requiresPacking(const cgltf_accessor* accessor) {
|
||||
if (requiresConversion(accessor->type, accessor->component_type)) {
|
||||
return true;
|
||||
}
|
||||
const size_t dim = cgltf_num_components(accessor->type);
|
||||
switch (accessor->component_type) {
|
||||
case cgltf_component_type_r_8:
|
||||
case cgltf_component_type_r_8u:
|
||||
return accessor->stride != dim;
|
||||
case cgltf_component_type_r_16:
|
||||
case cgltf_component_type_r_16u:
|
||||
return accessor->stride != dim * 2;
|
||||
case cgltf_component_type_r_32u:
|
||||
case cgltf_component_type_r_32f:
|
||||
return accessor->stride != dim * 4;
|
||||
default:
|
||||
assert_invariant(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void decodeDracoMeshes(FFilamentAsset* asset) {
|
||||
DracoCache* dracoCache = &asset->mSourceAsset->dracoCache;
|
||||
|
||||
@@ -492,7 +521,27 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
|
||||
slot.indexBuffer->setBuffer(engine, std::move(bd));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the buffer slot does not have an associated VertexBuffer or IndexBuffer, then this
|
||||
// must be a morph target.
|
||||
assert(slot.morphTargetBuffer);
|
||||
|
||||
if (requiresPacking(accessor)) {
|
||||
const size_t dim = cgltf_num_components(accessor->type);
|
||||
const size_t floatsSize = accessor->count * sizeof(float) * dim;
|
||||
float* floatsData = (float*) malloc(floatsSize);
|
||||
convertToFloats(floatsData, accessor);
|
||||
if (accessor->type == cgltf_type_vec3) {
|
||||
slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex,
|
||||
(const float3*) floatsData, slot.morphTargetBuffer->getVertexCount());
|
||||
} else {
|
||||
slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex,
|
||||
(const float4*) data, slot.morphTargetBuffer->getVertexCount());
|
||||
}
|
||||
free(floatsData);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accessor->type == cgltf_type_vec3) {
|
||||
slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex,
|
||||
(const float3*) data, slot.morphTargetBuffer->getVertexCount());
|
||||
|
||||
@@ -24,17 +24,20 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#if GLTFIO_LITE
|
||||
#include "gltfresources_lite.h"
|
||||
#else
|
||||
#include <uberz/ArchiveCache.h>
|
||||
|
||||
#include "gltfresources.h"
|
||||
#endif
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace filament::uberz;
|
||||
using namespace gltfio;
|
||||
using namespace utils;
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::uberz::ArchiveRequirements& reqs);
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using CullingMode = MaterialInstance::CullingMode;
|
||||
@@ -64,35 +67,13 @@ public:
|
||||
|
||||
Material* getMaterial(const MaterialKey& config) const;
|
||||
|
||||
enum ShadingMode {
|
||||
UNLIT = 0,
|
||||
LIT = 1,
|
||||
SPECULAR_GLOSSINESS = 2,
|
||||
};
|
||||
|
||||
mutable Material* mMaterials[12] = {};
|
||||
mutable ArchiveCache mMaterials;
|
||||
Texture* mDummyTexture = nullptr;
|
||||
|
||||
Engine* mEngine;
|
||||
};
|
||||
|
||||
#if GLTFIO_LITE
|
||||
|
||||
#define CREATE_MATERIAL(name) Material::Builder() \
|
||||
.package(GLTFRESOURCES_LITE_ ## name ## _DATA, GLTFRESOURCES_LITE_ ## name ## _SIZE) \
|
||||
.build(*mEngine);
|
||||
|
||||
#else
|
||||
|
||||
#define CREATE_MATERIAL(name) Material::Builder() \
|
||||
.package(GLTFRESOURCES_ ## name ## _DATA, GLTFRESOURCES_ ## name ## _SIZE) \
|
||||
.build(*mEngine);
|
||||
|
||||
#endif
|
||||
|
||||
#define MATINDEX(shading, alpha, sheen, transmit, volume) (volume ? 11 : (transmit ? 10 : (sheen ? 9 : (int(shading) + 3 * int(alpha)))))
|
||||
|
||||
UbershaderLoader::UbershaderLoader(Engine* engine) : mEngine(engine) {
|
||||
UbershaderLoader::UbershaderLoader(Engine* engine) : mEngine(engine), mMaterials(*engine) {
|
||||
unsigned char texels[4] = {};
|
||||
mDummyTexture = Texture::Builder()
|
||||
.width(1).height(1)
|
||||
@@ -101,65 +82,63 @@ UbershaderLoader::UbershaderLoader(Engine* engine) : mEngine(engine) {
|
||||
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
|
||||
Texture::Type::UBYTE);
|
||||
mDummyTexture->setImage(*mEngine, 0, std::move(pbd));
|
||||
mMaterials.load((void*) GLTFRESOURCES_FULL_DATA, GLTFRESOURCES_FULL_SIZE);
|
||||
}
|
||||
|
||||
size_t UbershaderLoader::getMaterialsCount() const noexcept {
|
||||
return sizeof(mMaterials) / sizeof(mMaterials[0]);
|
||||
return mMaterials.getMaterialsCount();
|
||||
}
|
||||
|
||||
const Material* const* UbershaderLoader::getMaterials() const noexcept {
|
||||
return &mMaterials[0];
|
||||
return mMaterials.getMaterials();
|
||||
}
|
||||
|
||||
void UbershaderLoader::destroyMaterials() {
|
||||
for (auto& material : mMaterials) {
|
||||
mEngine->destroy(material);
|
||||
material = nullptr;
|
||||
}
|
||||
mMaterials.destroyMaterials();
|
||||
mEngine->destroy(mDummyTexture);
|
||||
}
|
||||
|
||||
Material* UbershaderLoader::getMaterial(const MaterialKey& config) const {
|
||||
const ShadingMode shading = config.unlit ? UNLIT :
|
||||
(config.useSpecularGlossiness ? SPECULAR_GLOSSINESS : LIT);
|
||||
const int matindex = MATINDEX(shading, config.alphaMode, config.hasSheen, config.hasTransmission, config.hasVolume);
|
||||
if (mMaterials[matindex] != nullptr) {
|
||||
return mMaterials[matindex];
|
||||
}
|
||||
switch (matindex) {
|
||||
#if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_OPAQUE_DATA)
|
||||
case MATINDEX(LIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_OPAQUE); break;
|
||||
#endif
|
||||
const Shading shading = config.unlit ? Shading::UNLIT :
|
||||
(config.useSpecularGlossiness ? Shading::SPECULAR_GLOSSINESS : Shading::LIT);
|
||||
|
||||
#if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_FADE_DATA)
|
||||
case MATINDEX(LIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_FADE); break;
|
||||
#endif
|
||||
BlendingMode blending;
|
||||
switch (config.alphaMode) {
|
||||
case AlphaMode::OPAQUE: blending = BlendingMode::OPAQUE; break;
|
||||
case AlphaMode::MASK: blending = BlendingMode::MASKED; break;
|
||||
case AlphaMode::BLEND: blending = BlendingMode::FADE; break;
|
||||
}
|
||||
|
||||
#if !GLTFIO_LITE
|
||||
case MATINDEX(LIT, AlphaMode::MASK, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_MASKED); break;
|
||||
case MATINDEX(UNLIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(UNLIT_OPAQUE); break;
|
||||
case MATINDEX(UNLIT, AlphaMode::MASK, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(UNLIT_MASKED); break;
|
||||
case MATINDEX(UNLIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(UNLIT_FADE); break;
|
||||
case MATINDEX(SPECULAR_GLOSSINESS, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(SPECULARGLOSSINESS_OPAQUE); break;
|
||||
case MATINDEX(SPECULAR_GLOSSINESS, AlphaMode::MASK, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(SPECULARGLOSSINESS_MASKED); break;
|
||||
case MATINDEX(SPECULAR_GLOSSINESS, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(SPECULARGLOSSINESS_FADE); break;
|
||||
case MATINDEX(0, 0, false, true, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_TRANSMISSION); break;
|
||||
case MATINDEX(0, 0, true, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_SHEEN); break;
|
||||
case MATINDEX(0, 0, false, false, true): mMaterials[matindex] = CREATE_MATERIAL(LIT_VOLUME); break;
|
||||
#endif
|
||||
ArchiveRequirements requirements = { shading, blending };
|
||||
requirements.features["Sheen"] = config.hasSheen;
|
||||
requirements.features["Transmission"] = config.hasTransmission;
|
||||
requirements.features["Volume"] = config.hasVolume;
|
||||
requirements.features["Ior"] = config.hasIOR;
|
||||
requirements.features["VertexColors"] = config.hasVertexColors;
|
||||
requirements.features["BaseColorTexture"] = config.hasBaseColorTexture;
|
||||
requirements.features["NormalTexture"] = config.hasNormalTexture;
|
||||
requirements.features["OcclusionTexture"] = config.hasOcclusionTexture;
|
||||
requirements.features["EmissiveTexture"] = config.hasEmissiveTexture;
|
||||
requirements.features["MetallicRoughnessTexture"] = config.hasMetallicRoughnessTexture;
|
||||
requirements.features["ClearCoatTexture"] = config.hasClearCoatTexture;
|
||||
requirements.features["ClearCoatRoughnessTexture"] = config.hasClearCoatRoughnessTexture;
|
||||
requirements.features["ClearCoatNormalTexture"] = config.hasClearCoatNormalTexture;
|
||||
requirements.features["ClearCoat"] = config.hasClearCoat;
|
||||
requirements.features["TextureTransforms"] = config.hasTextureTransforms;
|
||||
requirements.features["TransmissionTexture"] = config.hasTransmissionTexture;
|
||||
requirements.features["SheenColorTexture"] = config.hasSheenColorTexture;
|
||||
requirements.features["SheenRoughnessTexture"] = config.hasSheenRoughnessTexture;
|
||||
requirements.features["VolumeThicknessTexture"] = config.hasVolumeThicknessTexture;
|
||||
|
||||
if (Material* mat = mMaterials.getMaterial(requirements); mat != nullptr) {
|
||||
return mat;
|
||||
}
|
||||
if (mMaterials[matindex] == nullptr) {
|
||||
slog.w << "Unsupported glTF material configuration; falling back to LIT_OPAQUE." << io::endl;
|
||||
MaterialKey litOpaque = config;
|
||||
litOpaque.alphaMode = AlphaMode::OPAQUE;
|
||||
litOpaque.hasTransmission = false;
|
||||
litOpaque.hasVolume = false;
|
||||
litOpaque.hasSheen = false;
|
||||
litOpaque.useSpecularGlossiness = false;
|
||||
litOpaque.unlit = false;
|
||||
return getMaterial(litOpaque);
|
||||
}
|
||||
return mMaterials[matindex];
|
||||
|
||||
#ifndef NDEBUG
|
||||
slog.w << "Failed to find material with features:\n" << requirements << io::endl;
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
|
||||
@@ -197,6 +176,14 @@ MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config,
|
||||
return hasTexture ? int(uvmap->at(srcIndex)) - 1 : -1;
|
||||
};
|
||||
Material* material = getMaterial(*config);
|
||||
|
||||
if (material == nullptr) {
|
||||
#ifndef NDEBUG
|
||||
slog.w << "Using fallback material for " << label << "." << io::endl;
|
||||
#endif
|
||||
material = mMaterials.getDefaultMaterial();
|
||||
}
|
||||
|
||||
MaterialInstance* mi = material->createInstance(label);
|
||||
mi->setParameter("baseColorIndex",
|
||||
getUvIndex(config->baseColorUV, config->hasBaseColorTexture));
|
||||
@@ -212,8 +199,6 @@ MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config,
|
||||
MaterialInstance::TransparencyMode::TWO_PASSES_TWO_SIDES :
|
||||
MaterialInstance::TransparencyMode::DEFAULT);
|
||||
|
||||
#if !GLTFIO_LITE
|
||||
|
||||
// Initially, assume that the clear coat texture can be honored. This is changed to false when
|
||||
// running into a sampler count limitation. TODO: check if these constraints can now be relaxed.
|
||||
bool clearCoatNeedsTexture = true;
|
||||
@@ -262,17 +247,6 @@ MaterialInstance* UbershaderLoader::createMaterialInstance(MaterialKey* config,
|
||||
getUvIndex(config->transmissionUV, config->hasTransmissionTexture));
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
// In the GLTFIO_LITE configuration we do not support UV matrices, clear coat, sheen, specular
|
||||
// glossiness, or transmission. For more details, see `gltflite.mat.in`. To configure a custom
|
||||
// set of features, create your own MaterialProvider class, perhaps using UbershaderLoader as a
|
||||
// starting point.
|
||||
const bool clearCoatNeedsTexture = false;
|
||||
|
||||
const bool volumeThicknessNeedsTexture = false;
|
||||
|
||||
#endif
|
||||
|
||||
TextureSampler sampler;
|
||||
mi->setParameter("normalMap", mDummyTexture, sampler);
|
||||
@@ -317,3 +291,41 @@ MaterialProvider* createUbershaderLoader(filament::Engine* engine) {
|
||||
}
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
|
||||
inline
|
||||
const char* toString(Shading shadingModel) noexcept {
|
||||
switch (shadingModel) {
|
||||
case Shading::UNLIT: return "unlit";
|
||||
case Shading::LIT: return "lit";
|
||||
case Shading::SUBSURFACE: return "subsurface";
|
||||
case Shading::CLOTH: return "cloth";
|
||||
case Shading::SPECULAR_GLOSSINESS: return "specularGlossiness";
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
const char* toString(BlendingMode blendingMode) noexcept {
|
||||
switch (blendingMode) {
|
||||
case BlendingMode::OPAQUE: return "opaque";
|
||||
case BlendingMode::TRANSPARENT: return "transparent";
|
||||
case BlendingMode::ADD: return "add";
|
||||
case BlendingMode::MASKED: return "masked";
|
||||
case BlendingMode::FADE: return "fade";
|
||||
case BlendingMode::MULTIPLY: return "multiply";
|
||||
case BlendingMode::SCREEN: return "screen";
|
||||
}
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const ArchiveRequirements& reqs) {
|
||||
out << " ShadingModel = " << toString(reqs.shadingModel) << '\n'
|
||||
<< " BlendingMode = " << toString(reqs.blendingMode) << '\n';
|
||||
for (const auto& pair : reqs.features) {
|
||||
out << " " << pair.first.c_str() << " = " << (pair.second ? "true" : "false") << '\n';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -364,7 +364,7 @@ uint32_t getMipmapCount(const LinearImage& source) {
|
||||
Filter filterFromString(const char* rawname) {
|
||||
using namespace utils;
|
||||
using std::unordered_map;
|
||||
static const unordered_map<StaticString, Filter> map = {
|
||||
static const unordered_map<StaticString, Filter, StaticString::Hasher> map = {
|
||||
{ "BOX", Filter::BOX},
|
||||
{ "NEAREST", Filter::NEAREST},
|
||||
{ "HERMITE", Filter::HERMITE},
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(uberzlib)
|
||||
|
||||
set(TARGET uberzlib)
|
||||
set(PUBLIC_HDR_DIR include)
|
||||
|
||||
# ==================================================================================================
|
||||
# Sources and headers
|
||||
# ==================================================================================================
|
||||
set(PUBLIC_HDRS
|
||||
include/uberz/ArchiveCache.h
|
||||
include/uberz/ArchiveEnums.h
|
||||
include/uberz/ReadableArchive.h
|
||||
include/uberz/WritableArchive.h
|
||||
)
|
||||
|
||||
set(SRCS
|
||||
src/ArchiveCache.cpp
|
||||
src/ReadableArchive.cpp
|
||||
src/WritableArchive.cpp
|
||||
)
|
||||
|
||||
# ==================================================================================================
|
||||
# Include and target definitions
|
||||
# ==================================================================================================
|
||||
include_directories(${PUBLIC_HDR_DIR})
|
||||
|
||||
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
|
||||
|
||||
target_link_libraries(${TARGET} PUBLIC math utils filabridge filament zstd)
|
||||
|
||||
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
|
||||
# ==================================================================================================
|
||||
# Compiler flags
|
||||
# ==================================================================================================
|
||||
if (MSVC)
|
||||
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:/fp:fast>)
|
||||
else()
|
||||
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:-ffast-math>)
|
||||
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR})
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/uberz DESTINATION include)
|
||||
@@ -0,0 +1,87 @@
|
||||
- [Ubershader Archive Files](#ubershader-archive-files)
|
||||
- [Ubershader Spec Files](#ubershader-spec-files)
|
||||
|
||||
# Ubershader Archive Files
|
||||
|
||||
An ubershader archive provides a way to bundle up a set of `filamat` files along with some metadata
|
||||
that conveys which glTF features each material can handle. It is a file that has been compressed
|
||||
with `zstd` and has an `.uberz` file extension. In uncompressed form, it has the following layout
|
||||
(little endian is assumed).
|
||||
|
||||
```
|
||||
[u32] magic identifier: UBER
|
||||
[u32] simple (unpartitioned) version number for the archive format
|
||||
[u64] number of specs
|
||||
[u64] byte offset to SPECS
|
||||
SPECS:
|
||||
foreach spec {
|
||||
[u8] shading model
|
||||
[u8] blending model
|
||||
[u16] number of flags
|
||||
[u32] size in bytes of the filamat blob
|
||||
[u64] byte offset to FLAGLIST for this spec
|
||||
[u64] byte offset to FILAMAT for this spec
|
||||
}
|
||||
foreach spec {
|
||||
FLAGLIST:
|
||||
foreach flag {
|
||||
[u64] byte offset to FLAGNAME for this spec/flag pair
|
||||
[u64] flag value: 0 = unsupported, 1 = optional, or 2 = required
|
||||
}
|
||||
}
|
||||
foreach spec {
|
||||
foreach flag {
|
||||
FLAGNAME:
|
||||
[u8...] flag name, including null terminator
|
||||
}
|
||||
}
|
||||
foreach spec {
|
||||
FILAMAT:
|
||||
[u8...] filamat blob
|
||||
}
|
||||
```
|
||||
|
||||
In the above specification, each "offset" is a number of bytes between the top of the file to the
|
||||
given label. These offsets are 64 bits so that they can be replaced with pointers in a C struct,
|
||||
which allows the file to be consumed without any parsing. On 32-bit architectures, this still works
|
||||
because we can simply ignore the unused padding after every pointer.
|
||||
|
||||
# Ubershader Spec Files
|
||||
|
||||
An ubershader spec file is a simple text file with a `.spec` extension. It contains a list of
|
||||
key-value pairs conforming to the following grammar. Each key-value pair is either a *feature flag*
|
||||
or a *fundamental aspect*.
|
||||
|
||||
- Each feature flag can be **unsupported**, **required**, or **optional**.
|
||||
- The fundamental aspect of the material cannot be changed, such as the blend mode.
|
||||
|
||||
```eBNF
|
||||
spec = { [ comment | key_value_pair ] , "\n" } ;
|
||||
comment = "#" , { any } ;
|
||||
key_value_pair = ( fundamental_aspect | feature_flag ) ;
|
||||
fundamental_aspect = ( blending | shading ) ;
|
||||
feature_flag = identifier , equals , ("unsupported" | "required" | "optional") ;
|
||||
blending = "BlendingMode" , equals ,
|
||||
( "opaque" | "transparent" | "fade" | "add" | "masked" | "multiply" | "screen" ) ;
|
||||
shading = "ShadingModel" , equals ,
|
||||
( "lit" | "subsurface" | "cloth" | "unlit" | "specularGlossiness") ;
|
||||
equals = [ whitespace ] , "=" , [ whitespace ] ;
|
||||
any = ? any character other than newline ? ;
|
||||
whitespace = ? sequence of tabs and spaces ? ;
|
||||
identifier = ? sequence of alphanumeric characters ? ;
|
||||
```
|
||||
|
||||
If a fundamental aspect is missing from the spec, then the loader will assume that the spec can
|
||||
handle all possible values for that aspect. For example, we may wish to override the glTF
|
||||
blending mode in certain ubershader materials (e.g. materials that support `KHR_materials_volume`).
|
||||
These materials should simply omit the `BlendingMode` line from the spec.
|
||||
|
||||
If any feature flag is missing from the spec, it implicitly has the value of `unsupported`. For an
|
||||
up-to-date list of recognized feature flags, look at the source for `UbershaderLoader::getMaterial`.
|
||||
|
||||
If a particular feature flag is set to `required` for a particular material, then the glTF loader
|
||||
will bind that material to a given glTF mesh only if that feature is enabled in the mesh.
|
||||
|
||||
Usually, features are either `unsupported` or `optional`. For example, if the ubershader user can
|
||||
set `normalIndex` in the material to `-1` to signal that they do not have a normal map, then normal
|
||||
mapping should be specified as an `optional` feature of the ubershader.
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UBERZ_ARCHIVE_CACHE_H
|
||||
#define UBERZ_ARCHIVE_CACHE_H
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Material.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <uberz/ReadableArchive.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
struct ArchiveRequirements;
|
||||
|
||||
// Stores a set of Filament materials and knows how to choose a suitable material when given a
|
||||
// set of requirements. Used by gltfio; users do not need to access this class directly.
|
||||
class ArchiveCache {
|
||||
public:
|
||||
ArchiveCache(filament::Engine& engine) : mEngine(engine) {}
|
||||
~ArchiveCache();
|
||||
|
||||
void load(const void* archiveData, uint64_t archiveByteCount);
|
||||
filament::Material* getMaterial(const ArchiveRequirements& requirements);
|
||||
filament::Material* getDefaultMaterial();
|
||||
const filament::Material* const* getMaterials() const noexcept { return mMaterials.data(); }
|
||||
size_t getMaterialsCount() const noexcept { return mMaterials.size(); }
|
||||
void destroyMaterials();
|
||||
|
||||
private:
|
||||
filament::Engine& mEngine;
|
||||
utils::FixedCapacityVector<filament::Material*> mMaterials;
|
||||
struct ReadableArchive* mArchive = nullptr;
|
||||
};
|
||||
|
||||
struct ArchiveRequirements {
|
||||
filament::Shading shadingModel;
|
||||
filament::BlendingMode blendingMode;
|
||||
tsl::robin_map<utils::CString, bool, utils::CString::Hasher> features;
|
||||
};
|
||||
|
||||
} // namespace filament::uberz
|
||||
|
||||
#endif // UBERZ_ARCHIVE_CACHE_H
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UBERZ_ARCHIVE_ENUMS_H
|
||||
#define UBERZ_ARCHIVE_ENUMS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
enum class ArchiveFeature : uint64_t {
|
||||
UNSUPPORTED,
|
||||
OPTIONAL,
|
||||
REQUIRED,
|
||||
};
|
||||
|
||||
} // namespace filament::uberz
|
||||
|
||||
#endif // UBERZ_ARCHIVE_ENUMS_H
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UBERZ_READABLE_ARCHIVE_H
|
||||
#define UBERZ_READABLE_ARCHIVE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <uberz/ArchiveEnums.h>
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
// ArchiveSpec is a parse-free binary format. The client simply casts a word-aligned content blob
|
||||
// into a ReadableArchive struct pointer, then calls the following function to convert all the
|
||||
// offset fields into pointers.
|
||||
void convertOffsetsToPointers(struct ReadableArchive* archive);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic warning "-Wpadded"
|
||||
|
||||
// Precompiled set of materials bundled with a list of features flags that each material supports.
|
||||
// This is the readable counterpart to WriteableArchive.
|
||||
// Used by gltfio; users do not need to access this class directly.
|
||||
struct ReadableArchive {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint64_t specsCount;
|
||||
union {
|
||||
struct ArchiveSpec* specs;
|
||||
uint64_t specsOffset;
|
||||
};
|
||||
};
|
||||
|
||||
static constexpr filament::Shading INVALID_SHADING_MODEL = (filament::Shading) 0xff;
|
||||
static constexpr filament::BlendingMode INVALID_BLENDING = (filament::BlendingMode) 0xff;
|
||||
|
||||
struct ArchiveSpec {
|
||||
filament::Shading shadingModel;
|
||||
filament::BlendingMode blendingMode;
|
||||
uint16_t flagsCount;
|
||||
uint32_t packageByteCount;
|
||||
union {
|
||||
struct ArchiveFlag* flags;
|
||||
uint64_t flagsOffset;
|
||||
};
|
||||
union {
|
||||
uint8_t* package;
|
||||
uint64_t packageOffset;
|
||||
};
|
||||
};
|
||||
|
||||
struct ArchiveFlag {
|
||||
union {
|
||||
const char* name;
|
||||
uint64_t nameOffset;
|
||||
};
|
||||
ArchiveFeature value;
|
||||
};
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace filament::uberz
|
||||
|
||||
#endif // UBERZ_READABLE_ARCHIVE_H
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef UBERZ_WRITABLE_ARCHIVE_H
|
||||
#define UBERZ_WRITABLE_ARCHIVE_H
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <uberz/ArchiveEnums.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
// Precompiled set of materials bundled with a list of features flags that each material supports.
|
||||
// This is the writeable counterpart to ReadableArchive.
|
||||
// Used by gltfio; users do not need to access this class directly.
|
||||
class WritableArchive {
|
||||
public:
|
||||
WritableArchive(size_t materialCount) : mMaterials(materialCount) {}
|
||||
void addMaterial(const char* name, const uint8_t* package, size_t packageSize);
|
||||
void addSpecLine(std::string_view line);
|
||||
utils::FixedCapacityVector<uint8_t> serialize() const;
|
||||
|
||||
private:
|
||||
size_t mLineNumber = 1;
|
||||
ssize_t mMaterialIndex = -1;
|
||||
|
||||
struct Material {
|
||||
utils::CString name;
|
||||
utils::FixedCapacityVector<uint8_t> package;
|
||||
filament::Shading shadingModel;
|
||||
filament::BlendingMode blendingMode;
|
||||
tsl::robin_map<utils::CString, ArchiveFeature, utils::CString::Hasher> flags;
|
||||
};
|
||||
|
||||
utils::FixedCapacityVector<Material> mMaterials;
|
||||
};
|
||||
|
||||
} // namespace filament::uberz
|
||||
|
||||
#endif // UBERZ_WRITABLE_ARCHIVE_H
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <uberz/ArchiveCache.h>
|
||||
|
||||
#include <utils/memalign.h>
|
||||
|
||||
#include <zstd.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
|
||||
// Set this to a certain spec index to find out why it was deemed unsuitable.
|
||||
// To find the spec index of interest, try invoking uberz with the verbose flag.
|
||||
constexpr static int DEBUG_SPEC_INDEX = -1;
|
||||
|
||||
constexpr static void debugSuitability(int index, const char* msg) {
|
||||
if constexpr(DEBUG_SPEC_INDEX > 0) {
|
||||
if (index == DEBUG_SPEC_INDEX) {
|
||||
slog.d << "Spec " << DEBUG_SPEC_INDEX
|
||||
<< " is unsuitable due to " << msg << io::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool strIsEqual(const CString& a, const char* b) {
|
||||
return strncmp(a.c_str(), b, a.size()) == 0;
|
||||
}
|
||||
|
||||
void ArchiveCache::load(const void* archiveData, uint64_t archiveByteCount) {
|
||||
assert_invariant(mArchive == nullptr && "Do not call load() twice");
|
||||
const uint64_t decompSize = ZSTD_getFrameContentSize(archiveData, archiveByteCount);
|
||||
if (decompSize == ZSTD_CONTENTSIZE_UNKNOWN || decompSize == ZSTD_CONTENTSIZE_ERROR) {
|
||||
PANIC_POSTCONDITION("Decompression error.");
|
||||
}
|
||||
uint64_t* basePointer = (uint64_t*) utils::aligned_alloc(decompSize, 8);
|
||||
ZSTD_decompress(basePointer, decompSize, archiveData, archiveByteCount);
|
||||
mArchive = (ReadableArchive*) basePointer;
|
||||
convertOffsetsToPointers(mArchive);
|
||||
mMaterials = FixedCapacityVector<Material*>(mArchive->specsCount, nullptr);
|
||||
}
|
||||
|
||||
// This loops though all ubershaders and returns the first one that meets the given requirements.
|
||||
Material* ArchiveCache::getMaterial(const ArchiveRequirements& reqs) {
|
||||
assert_invariant(mArchive && "Please call load() before requesting any materials.");
|
||||
|
||||
for (uint64_t i = 0; i < mArchive->specsCount; ++i) {
|
||||
const ArchiveSpec& spec = mArchive->specs[i];
|
||||
if (spec.blendingMode != INVALID_BLENDING && spec.blendingMode != reqs.blendingMode) {
|
||||
debugSuitability(i, "blend mode mismatch.");
|
||||
continue;
|
||||
}
|
||||
if (spec.shadingModel != INVALID_SHADING_MODEL && spec.shadingModel != reqs.shadingModel) {
|
||||
debugSuitability(i, "material model.");
|
||||
continue;
|
||||
}
|
||||
bool specIsSuitable = true;
|
||||
|
||||
// For each feature required by the mesh, this ubershader is suitable only if it includes a
|
||||
// feature flag for it and the feature flag is either OPTIONAL or REQUIRED.
|
||||
for (const auto& req : reqs.features) {
|
||||
const CString& meshRequirement = req.first;
|
||||
if (req.second == false) {
|
||||
continue;
|
||||
}
|
||||
bool found = false;
|
||||
for (uint64_t j = 0; j < spec.flagsCount && !found; ++j) {
|
||||
const ArchiveFlag& flag = spec.flags[j];
|
||||
if (strIsEqual(meshRequirement, flag.name)) {
|
||||
if (flag.value != ArchiveFeature::UNSUPPORTED) {
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
debugSuitability(i, meshRequirement.c_str());
|
||||
specIsSuitable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If this ubershader requires a certain feature to be enabled in the glTF, but the glTF
|
||||
// mesh doesn't have it, then this ubershader is not suitable. This occurs very rarely, so
|
||||
// it intentionally comes after the other suitability check.
|
||||
for (uint64_t j = 0; j < spec.flagsCount && specIsSuitable; ++j) {
|
||||
ArchiveFlag& flag = spec.flags[j];
|
||||
if (UTILS_UNLIKELY(flag.value == ArchiveFeature::REQUIRED)) {
|
||||
// This allocates a new CString just to make a robin_map lookup, but this is rare
|
||||
// because almost none of our feature flags are REQUIRED.
|
||||
auto iter = reqs.features.find(CString(flag.name));
|
||||
if (iter == reqs.features.end() || iter.value() == false) {
|
||||
debugSuitability(i, flag.name);
|
||||
specIsSuitable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (specIsSuitable) {
|
||||
if (mMaterials[i] == nullptr) {
|
||||
mMaterials[i] = Material::Builder()
|
||||
.package(spec.package, spec.packageByteCount)
|
||||
.build(mEngine);
|
||||
}
|
||||
return mMaterials[i];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Material* ArchiveCache::getDefaultMaterial() {
|
||||
assert_invariant(mArchive && "Please call load() before requesting any materials.");
|
||||
assert_invariant(!mMaterials.empty() && "Archive must have at least one material.");
|
||||
if (mMaterials[0] == nullptr) {
|
||||
mMaterials[0] = Material::Builder()
|
||||
.package(mArchive->specs[0].package, mArchive->specs[0].packageByteCount)
|
||||
.build(mEngine);
|
||||
}
|
||||
return mMaterials[0];
|
||||
}
|
||||
|
||||
void ArchiveCache::destroyMaterials() {
|
||||
for (auto mat : mMaterials) mEngine.destroy(mat);
|
||||
mMaterials.clear();
|
||||
}
|
||||
|
||||
ArchiveCache::~ArchiveCache() {
|
||||
assert_invariant(mMaterials.size() == 0 &&
|
||||
"Please call destroyMaterials explicitly to ensure correct destruction order");
|
||||
free(mArchive);
|
||||
}
|
||||
|
||||
} // namespace filament::uberz
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <uberz/ReadableArchive.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
static_assert(sizeof(ReadableArchive) == 4 + 4 + 8 + 8);
|
||||
static_assert(sizeof(ArchiveSpec) == 1 + 1 + 2 + 4 + 8 + 8);
|
||||
static_assert(sizeof(ArchiveFlag) == 8 + 8);
|
||||
|
||||
void convertOffsetsToPointers(ReadableArchive* archive) {
|
||||
constexpr size_t wordSize = sizeof(uint64_t);
|
||||
assert_invariant(archive->specsOffset % wordSize == 0);
|
||||
uint64_t* basePointer = (uint64_t*) archive;
|
||||
archive->specs = (ArchiveSpec*) (basePointer + archive->specsOffset / wordSize);
|
||||
for (uint64_t i = 0; i < archive->specsCount; ++i) {
|
||||
ArchiveSpec& spec = archive->specs[i];
|
||||
assert_invariant(spec.flagsOffset % wordSize == 0);
|
||||
spec.flags = (ArchiveFlag*) (basePointer + (spec.flagsOffset / wordSize));
|
||||
spec.package = ((uint8_t*) basePointer) + spec.packageOffset;
|
||||
for (uint64_t j = 0; j < spec.flagsCount; ++j) {
|
||||
ArchiveFlag& flag = spec.flags[j];
|
||||
flag.name = ((const char*) basePointer) + flag.nameOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::uberz
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <uberz/WritableArchive.h>
|
||||
#include <uberz/ReadableArchive.h>
|
||||
|
||||
#include <zstd.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace utils;
|
||||
using namespace std::literals;
|
||||
|
||||
using std::string_view;
|
||||
|
||||
namespace filament::uberz {
|
||||
|
||||
static bool isAlphaNumeric(char c) {
|
||||
return (c >= '0' && c <= '9') || c == '_'
|
||||
|| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
static bool isWhitespace(char c) {
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
// Returns 0 if stringToScan does not start with prefix, otherwise returns length of prefix.
|
||||
static size_t readPrefix(string_view prefix, string_view stringToScan) {
|
||||
const size_t prefixSize = prefix.size();
|
||||
if (UTILS_UNLIKELY(stringToScan.size() < prefixSize)) {
|
||||
return 0;
|
||||
}
|
||||
return prefix == string_view { stringToScan.data(), prefixSize } ? prefixSize : 0;
|
||||
}
|
||||
|
||||
static string_view readArchiveFeature(string_view cursor, ArchiveFeature* feature) {
|
||||
size_t sz;
|
||||
if (sz = readPrefix("unsupported"sv, cursor); sz > 0) {
|
||||
*feature = ArchiveFeature::UNSUPPORTED;
|
||||
} else if (sz = readPrefix("required"sv, cursor); sz > 0) {
|
||||
*feature = ArchiveFeature::REQUIRED;
|
||||
} else if (sz = readPrefix("optional"sv, cursor); sz > 0) {
|
||||
*feature = ArchiveFeature::OPTIONAL;
|
||||
}
|
||||
return { cursor.data(), sz };
|
||||
}
|
||||
|
||||
static string_view readBlendingMode(string_view cursor, BlendingMode* blending) {
|
||||
size_t sz;
|
||||
if (sz = readPrefix("opaque"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::OPAQUE;
|
||||
} else if (sz = readPrefix("transparent"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::TRANSPARENT;
|
||||
} else if (sz = readPrefix("add"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::ADD;
|
||||
} else if (sz = readPrefix("masked"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::MASKED;
|
||||
} else if (sz = readPrefix("fade"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::FADE;
|
||||
} else if (sz = readPrefix("multiply"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::MULTIPLY;
|
||||
} else if (sz = readPrefix("screen"sv, cursor); sz > 0) {
|
||||
*blending = BlendingMode::SCREEN;
|
||||
}
|
||||
return { cursor.data(), sz };
|
||||
}
|
||||
|
||||
static string_view readShadingModel(string_view cursor, Shading* shading) {
|
||||
size_t sz;
|
||||
if (sz = readPrefix("unlit"sv, cursor); sz > 0) {
|
||||
*shading = Shading::UNLIT;
|
||||
} else if (sz = readPrefix("lit"sv, cursor); sz > 0) {
|
||||
*shading = Shading::LIT;
|
||||
} else if (sz = readPrefix("subsurface"sv, cursor); sz > 0) {
|
||||
*shading = Shading::SUBSURFACE;
|
||||
} else if (sz = readPrefix("cloth"sv, cursor); sz > 0) {
|
||||
*shading = Shading::CLOTH;
|
||||
} else if (sz = readPrefix("specularGlossiness"sv, cursor); sz > 0) {
|
||||
*shading = Shading::SPECULAR_GLOSSINESS;
|
||||
}
|
||||
return { cursor.data(), sz };
|
||||
}
|
||||
|
||||
static string_view readSymbol(string_view cursor, char symbol) {
|
||||
return { cursor.data(), cursor[0] == symbol ? 1ul : 0ul };
|
||||
}
|
||||
|
||||
static string_view readIdentifier(string_view cursor) {
|
||||
size_t i = 0;
|
||||
while (isAlphaNumeric(cursor[i])) i++;
|
||||
return { cursor.data(), i };
|
||||
}
|
||||
|
||||
static string_view readWhitespace(string_view cursor) {
|
||||
size_t i = 0;
|
||||
while (isWhitespace(cursor[i])) i++;
|
||||
return { cursor.data(), i };
|
||||
}
|
||||
|
||||
void WritableArchive::addMaterial(const char* name, const uint8_t* package, size_t packageSize) {
|
||||
Material& material = mMaterials[++mMaterialIndex];
|
||||
|
||||
material = {
|
||||
CString(name),
|
||||
FixedCapacityVector<uint8_t>(packageSize)
|
||||
};
|
||||
memcpy(material.package.data(), package, packageSize);
|
||||
|
||||
// We use invalid values to denote "not set". e.g. if the spec file
|
||||
// does not set the blend mode, then it can be used for any blend mode.
|
||||
material.blendingMode = INVALID_BLENDING;
|
||||
material.shadingModel = INVALID_SHADING_MODEL;
|
||||
|
||||
mLineNumber = 1;
|
||||
}
|
||||
|
||||
void WritableArchive::addSpecLine(string_view line) {
|
||||
assert_invariant(mMaterialIndex > -1);
|
||||
Material& material = mMaterials[mMaterialIndex];
|
||||
|
||||
string_view cursor = line;
|
||||
string_view token;
|
||||
|
||||
// Don't try to recover, just emit the error message and quit.
|
||||
auto emitSyntaxError = [&](const char* msg) {
|
||||
const int column = 1 + line.size() - cursor.size();
|
||||
PANIC_POSTCONDITION("%s.spec(%d,%d): %s", material.name.c_str(), mLineNumber, column, msg);
|
||||
};
|
||||
|
||||
// Advance the cursor forward each time a token is consumed.
|
||||
auto advanceCursorByToken = [&]() {
|
||||
cursor = { cursor.data() + token.size(), cursor.size() - token.size() };
|
||||
};
|
||||
|
||||
// Sometimes we need to advance the cursor forward by a fixed amount.
|
||||
auto advanceCursorByCount = [&](size_t count) {
|
||||
cursor = { cursor.data() + count, cursor.size() - count };
|
||||
};
|
||||
|
||||
auto parseFeatureFlag = [&]() {
|
||||
if (token = readIdentifier(cursor); token.empty()) {
|
||||
emitSyntaxError("expected identifier");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
CString id(token.data(), token.size());
|
||||
token = readWhitespace(cursor);
|
||||
advanceCursorByToken();
|
||||
if (token = readSymbol(cursor, '='); token.empty()) {
|
||||
emitSyntaxError("expected equal sign");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
token = readWhitespace(cursor);
|
||||
advanceCursorByToken();
|
||||
if (token = readArchiveFeature(cursor, &material.flags[id]); token.empty()) {
|
||||
emitSyntaxError("expected unsupported / optional / required");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
};
|
||||
|
||||
auto parseBlendingMode = [&]() {
|
||||
token = readWhitespace(cursor);
|
||||
advanceCursorByToken();
|
||||
if (token = readSymbol(cursor, '='); token.empty()) {
|
||||
emitSyntaxError("expected equal sign");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
token = readWhitespace(cursor);
|
||||
advanceCursorByToken();
|
||||
if (token = readBlendingMode(cursor, &material.blendingMode); token.empty()) {
|
||||
emitSyntaxError("expected lowercase blending mode enum");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
};
|
||||
|
||||
auto parseShadingModel = [&]() {
|
||||
token = readWhitespace(cursor);
|
||||
advanceCursorByToken();
|
||||
if (token = readSymbol(cursor, '='); token.empty()) {
|
||||
emitSyntaxError("expected equal sign");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
token = readWhitespace(cursor);
|
||||
advanceCursorByToken();
|
||||
if (token = readShadingModel(cursor, &material.shadingModel); token.empty()) {
|
||||
emitSyntaxError("expected lowercase shading enum");
|
||||
}
|
||||
advanceCursorByToken();
|
||||
};
|
||||
|
||||
if (cursor[0] == 0 || cursor[0] == '#') {
|
||||
++mLineNumber;
|
||||
return;
|
||||
}
|
||||
|
||||
if (size_t sz = readPrefix("BlendingMode"sv, cursor); sz > 0) {
|
||||
advanceCursorByCount(sz);
|
||||
parseBlendingMode();
|
||||
} else if (size_t sz = readPrefix("ShadingModel"sv, cursor); sz > 0) {
|
||||
advanceCursorByCount(sz);
|
||||
parseShadingModel();
|
||||
} else {
|
||||
parseFeatureFlag();
|
||||
}
|
||||
|
||||
if (cursor[0] != 0) {
|
||||
emitSyntaxError("unexpected trailing character");
|
||||
}
|
||||
|
||||
++mLineNumber;
|
||||
}
|
||||
|
||||
FixedCapacityVector<uint8_t> WritableArchive::serialize() const {
|
||||
size_t byteCount = sizeof(ReadableArchive);
|
||||
for (const auto& mat : mMaterials) {
|
||||
byteCount += sizeof(ArchiveSpec);
|
||||
}
|
||||
size_t flaglistOffset = byteCount;
|
||||
for (const auto& mat : mMaterials) {
|
||||
for (const auto& pair : mat.flags) {
|
||||
byteCount += sizeof(ArchiveFlag);
|
||||
}
|
||||
}
|
||||
size_t nameOffset = byteCount;
|
||||
for (const auto& mat : mMaterials) {
|
||||
for (const auto& pair : mat.flags) {
|
||||
byteCount += pair.first.size() + 1;
|
||||
}
|
||||
}
|
||||
size_t filamatOffset = byteCount;
|
||||
for (const auto& mat : mMaterials) {
|
||||
byteCount += mat.package.size();
|
||||
}
|
||||
|
||||
ReadableArchive archive;
|
||||
archive.magic = 'UBER';
|
||||
archive.version = 0;
|
||||
archive.specsCount = mMaterials.size();
|
||||
archive.specsOffset = sizeof(ReadableArchive);
|
||||
|
||||
auto specs = FixedCapacityVector<ArchiveSpec>::with_capacity(mMaterials.size());
|
||||
size_t flagCount = 0;
|
||||
for (const auto& mat : mMaterials) {
|
||||
ArchiveSpec spec = {};
|
||||
spec.shadingModel = mat.shadingModel;
|
||||
spec.blendingMode = mat.blendingMode;
|
||||
spec.flagsCount = mat.flags.size();
|
||||
spec.flagsOffset = flaglistOffset + flagCount * sizeof(ArchiveFlag);
|
||||
spec.packageByteCount = mat.package.size();
|
||||
spec.packageOffset = filamatOffset;
|
||||
specs.push_back(spec);
|
||||
filamatOffset += mat.package.size();
|
||||
flagCount += mat.flags.size();
|
||||
}
|
||||
|
||||
auto flags = FixedCapacityVector<ArchiveFlag>::with_capacity(flagCount);
|
||||
size_t charCount = 0;
|
||||
for (const auto& mat : mMaterials) {
|
||||
for (const auto& pair : mat.flags) {
|
||||
ArchiveFlag flag;
|
||||
flag.nameOffset = nameOffset + charCount;
|
||||
flag.value = pair.second;
|
||||
charCount += pair.first.size() + 1;
|
||||
flags.push_back(flag);
|
||||
}
|
||||
}
|
||||
|
||||
std::string flagNames(charCount, ' ');
|
||||
char* flagNamesPtr = flagNames.data();
|
||||
for (const auto& mat : mMaterials) {
|
||||
for (const auto& pair : mat.flags) {
|
||||
memcpy(flagNamesPtr, pair.first.data(), pair.first.size() + 1);
|
||||
flagNamesPtr += pair.first.size() + 1;
|
||||
}
|
||||
}
|
||||
assert(flagNamesPtr - flagNames.data() == flagNames.size());
|
||||
|
||||
FixedCapacityVector<uint8_t> outputBuf(byteCount);
|
||||
uint8_t* writeCursor = outputBuf.data();
|
||||
memcpy(writeCursor, &archive, sizeof(archive));
|
||||
writeCursor += sizeof(archive);
|
||||
memcpy(writeCursor, specs.data(), sizeof(ArchiveSpec) * specs.size());
|
||||
writeCursor += sizeof(ArchiveSpec) * specs.size();
|
||||
memcpy(writeCursor, flags.data(), sizeof(ArchiveFlag) * flags.size());
|
||||
writeCursor += sizeof(ArchiveFlag) * flags.size();
|
||||
memcpy(writeCursor, flagNames.data(), charCount);
|
||||
writeCursor += charCount;
|
||||
for (const auto& mat : mMaterials) {
|
||||
memcpy(writeCursor, mat.package.data(), mat.package.size());
|
||||
writeCursor += mat.package.size();
|
||||
}
|
||||
assert_invariant(writeCursor - outputBuf.data() == outputBuf.size());
|
||||
|
||||
FixedCapacityVector<uint8_t> compressedBuf(ZSTD_compressBound(outputBuf.size()));
|
||||
size_t zstdResult = ZSTD_compress(compressedBuf.data(), compressedBuf.size(), outputBuf.data(),
|
||||
outputBuf.size(), ZSTD_maxCLevel());
|
||||
if (ZSTD_isError(zstdResult)) {
|
||||
PANIC_POSTCONDITION("Error during archive compression: %s", ZSTD_getErrorName(zstdResult));
|
||||
}
|
||||
|
||||
compressedBuf.resize(zstdResult);
|
||||
return compressedBuf;
|
||||
}
|
||||
|
||||
} // namespace filament::uberz
|
||||
@@ -16,14 +16,12 @@
|
||||
|
||||
#include "PerformanceCounters.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <random>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -95,70 +93,5 @@ BENCHMARK_DEFINE_F(BinarySearch, stdLowerBound)(benchmark::State& state) {
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(BinarySearch, utilsLowerBound)(benchmark::State& state) {
|
||||
auto first = data.begin();
|
||||
auto last = data.begin() + state.range(0);
|
||||
std::vector<value_type> indices = prepareItems(state.range(0));
|
||||
value_type const* ip = indices.data();
|
||||
size_t i = 0;
|
||||
|
||||
{
|
||||
PerformanceCounters pc(state);
|
||||
for (auto _ : state) {
|
||||
auto item = ip[i++ % state.range(0)];
|
||||
auto const& pos = utils::lower_bound(first, last, item);
|
||||
benchmark::DoNotOptimize(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(BinarySearch, utilsLowerBoundPOT)(benchmark::State& state) {
|
||||
auto first = data.begin();
|
||||
auto last = data.begin() + state.range(0);
|
||||
std::vector<value_type> indices = prepareItems(state.range(0));
|
||||
value_type const* ip = indices.data();
|
||||
size_t i = 0;
|
||||
|
||||
{
|
||||
PerformanceCounters pc(state);
|
||||
for (auto _ : state) {
|
||||
auto item = ip[i++ % state.range(0)];
|
||||
auto const& pos = utils::lower_bound(first, last, item, {}, true);
|
||||
benchmark::DoNotOptimize(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(BinarySearch, utilsLowerBound4096)(benchmark::State& state) {
|
||||
auto first = data.begin();
|
||||
auto last = data.begin() + 4096;
|
||||
std::vector<value_type> indices = prepareItems(4096);
|
||||
value_type const* ip = indices.data();
|
||||
size_t i = 0;
|
||||
|
||||
{
|
||||
PerformanceCounters pc(state);
|
||||
for (auto _ : state) {
|
||||
auto item = ip[i++ % 4096];
|
||||
auto const& pos = utils::lower_bound(first, last, item, {}, true);
|
||||
benchmark::DoNotOptimize(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(BinarySearch, linearSearch)->Range(2, 1<<20);
|
||||
BENCHMARK_REGISTER_F(BinarySearch, stdLowerBound)->Range(2, 1<<20);
|
||||
BENCHMARK_REGISTER_F(BinarySearch, utilsLowerBound)->Range(2, 1<<20);
|
||||
BENCHMARK_REGISTER_F(BinarySearch, utilsLowerBoundPOT)
|
||||
->Arg(2)
|
||||
->Arg(4)
|
||||
->Arg(16)
|
||||
->Arg(32)
|
||||
->Arg(64)
|
||||
->Arg(256)
|
||||
->Arg(4096)
|
||||
->Arg(32768)
|
||||
->Arg(262144)
|
||||
->Arg(1048576);
|
||||
|
||||
BENCHMARK_REGISTER_F(BinarySearch, utilsLowerBound4096);
|
||||
|
||||
@@ -457,6 +457,12 @@ private:
|
||||
void* mEnd = nullptr;
|
||||
};
|
||||
|
||||
class NullArea {
|
||||
public:
|
||||
void* data() const noexcept { return nullptr; }
|
||||
size_t size() const noexcept { return 0; }
|
||||
};
|
||||
|
||||
} // namespace AreaPolicy
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
using const_pointer = const value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
|
||||
constexpr StaticString() noexcept = default;
|
||||
constexpr StaticString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
|
||||
|
||||
// initialization from a string literal
|
||||
template<size_t N>
|
||||
@@ -149,6 +149,14 @@ public:
|
||||
|
||||
size_type getHash() const noexcept { return mHash; }
|
||||
|
||||
struct Hasher {
|
||||
typedef StaticString argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(const argument_type& s) const noexcept {
|
||||
return s.getHash();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
const_pointer mString = nullptr;
|
||||
size_type mLength = 0;
|
||||
@@ -200,7 +208,7 @@ public:
|
||||
using iterator = value_type*;
|
||||
using const_iterator = const value_type*;
|
||||
|
||||
CString() noexcept = default;
|
||||
CString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
|
||||
|
||||
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
|
||||
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
|
||||
@@ -220,7 +228,7 @@ public:
|
||||
: CString(other, N - 1) {
|
||||
}
|
||||
|
||||
CString(StaticString const& s) : CString(s.c_str(), s.size()) {}
|
||||
CString(StaticString const& s) : CString(s.c_str(), s.size()) {} // NOLINT(google-explicit-constructor)
|
||||
|
||||
CString(const CString& rhs);
|
||||
|
||||
@@ -309,11 +317,19 @@ public:
|
||||
}
|
||||
|
||||
// placement new declared as "throw" to avoid the compiler's null-check
|
||||
inline void* operator new(size_t size, void* ptr) {
|
||||
inline void* operator new(size_t, void* ptr) {
|
||||
assert(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct Hasher : private hashCStrings {
|
||||
typedef CString argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(const argument_type& s) const noexcept {
|
||||
return hashCStrings::operator()(s.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
size_type length;
|
||||
@@ -365,34 +381,4 @@ CString to_string(T value) noexcept;
|
||||
|
||||
} // namespace utils
|
||||
|
||||
// FIXME: how could we not include this one?
|
||||
// needed for std::hash, since implementation is inline, this would not cause
|
||||
// binaries incompatibilities if another STL version was used.
|
||||
#include <functional>
|
||||
|
||||
namespace std {
|
||||
|
||||
//! \privatesection
|
||||
template<>
|
||||
struct hash<utils::CString> {
|
||||
typedef utils::CString argument_type;
|
||||
typedef size_t result_type;
|
||||
utils::hashCStrings hasher;
|
||||
size_t operator()(const utils::CString& s) const noexcept {
|
||||
return hasher(s.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
//! \privatesection
|
||||
template<>
|
||||
struct hash<utils::StaticString> {
|
||||
typedef utils::StaticString argument_type;
|
||||
typedef size_t result_type;
|
||||
size_t operator()(const utils::StaticString& s) const noexcept {
|
||||
return s.getHash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // TNT_UTILS_CSTRING_H
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
// FIXME: could we get rid of <functional>
|
||||
#include <functional> // for std::hash
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -30,7 +27,7 @@ namespace utils {
|
||||
class UTILS_PUBLIC Entity {
|
||||
public:
|
||||
// this can be used to create an array of to-be-filled entities (see create())
|
||||
Entity() noexcept = default;
|
||||
Entity() noexcept { } // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
|
||||
|
||||
// Entities can be copied
|
||||
Entity(const Entity& e) noexcept = default;
|
||||
@@ -68,10 +65,17 @@ public:
|
||||
return Entity{ Type(identity) };
|
||||
}
|
||||
|
||||
struct Hasher {
|
||||
typedef Entity argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(argument_type const& e) const {
|
||||
return e.getId();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
friend class EntityManager;
|
||||
friend class EntityManagerImpl;
|
||||
friend struct std::hash<Entity>;
|
||||
using Type = uint32_t;
|
||||
|
||||
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
|
||||
@@ -81,18 +85,4 @@ private:
|
||||
|
||||
} // namespace utils
|
||||
|
||||
|
||||
namespace std {
|
||||
|
||||
template<>
|
||||
struct hash<utils::Entity> {
|
||||
typedef utils::Entity argument_type;
|
||||
typedef size_t result_type;
|
||||
result_type operator()(argument_type const& e) const {
|
||||
return e.getId();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // TNT_UTILS_ENTITY_H
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
namespace hash {
|
||||
namespace utils::hash {
|
||||
|
||||
// Hash function that takes an arbitrary swath of word-aligned data.
|
||||
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
|
||||
@@ -99,7 +98,6 @@ inline void combine_fast(size_t& seed, const T& v) noexcept {
|
||||
seed ^= hasher(v) << 1u;
|
||||
}
|
||||
|
||||
} // namespace hash
|
||||
} // namespace utils
|
||||
} // namespace utils::hash
|
||||
|
||||
#endif // TNT_UTILS_HASH_H
|
||||
|
||||
@@ -17,40 +17,19 @@
|
||||
#ifndef TNT_UTILS_NAMECOMPONENTMANAGER_H
|
||||
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityInstance.h>
|
||||
#include <utils/SingleInstanceComponentManager.h>
|
||||
|
||||
#include <functional>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
class EntityManager;
|
||||
|
||||
namespace details {
|
||||
class SafeString {
|
||||
public:
|
||||
SafeString() noexcept = default;
|
||||
explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { }
|
||||
SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; }
|
||||
SafeString& operator=(SafeString&& rhs) noexcept {
|
||||
std::swap(mCStr, rhs.mCStr);
|
||||
return *this;
|
||||
}
|
||||
~SafeString() { free((void*)mCStr); }
|
||||
const char* c_str() const noexcept { return mCStr; }
|
||||
|
||||
private:
|
||||
char const* mCStr = nullptr;
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
|
||||
/**
|
||||
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
|
||||
* \brief Allows clients to associate string labels with entities.
|
||||
@@ -69,7 +48,7 @@ private:
|
||||
* printf("%s\n", names->getName(names->getInstance(myEntity));
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<details::SafeString> {
|
||||
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<utils::CString> {
|
||||
public:
|
||||
using Instance = EntityInstance<NameComponentManager>;
|
||||
|
||||
@@ -93,7 +72,7 @@ public:
|
||||
* @return Non-zero handle if the entity has a name component, 0 otherwise.
|
||||
*/
|
||||
Instance getInstance(Entity e) const noexcept {
|
||||
return Instance(SingleInstanceComponentManager::getInstance(e));
|
||||
return { SingleInstanceComponentManager::getInstance(e) };
|
||||
}
|
||||
|
||||
/*! \cond PRIVATE */
|
||||
|
||||
@@ -256,7 +256,7 @@ protected:
|
||||
|
||||
private:
|
||||
// maps an entity to an instance index
|
||||
tsl::robin_map<Entity, Instance> mInstanceMap;
|
||||
tsl::robin_map<Entity, Instance, Entity::Hasher> mInstanceMap;
|
||||
default_random_engine mRng;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_STOPWATCH_H
|
||||
#define TNT_UTILS_STOPWATCH_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
/*
|
||||
* A very basic Stopwatch class
|
||||
*/
|
||||
|
||||
template<typename Clock = std::chrono::steady_clock>
|
||||
class Stopwatch {
|
||||
public:
|
||||
using duration = typename Clock::duration;
|
||||
using time_point = typename Clock::time_point;
|
||||
|
||||
private:
|
||||
time_point mStart;
|
||||
duration mMinLap = std::numeric_limits<duration>::max();
|
||||
duration mMaxLap{};
|
||||
std::chrono::duration<double, std::nano> mAvgLap{};
|
||||
size_t mCount = 0;
|
||||
const char* mName = nullptr;
|
||||
|
||||
public:
|
||||
// Create a Stopwatch with a name and clock
|
||||
explicit Stopwatch(const char* name) noexcept: mName(name) {}
|
||||
|
||||
// Logs min/avg/max lap time
|
||||
~Stopwatch() noexcept;
|
||||
|
||||
// start the stopwatch
|
||||
inline void start() noexcept {
|
||||
mStart = Clock::now();
|
||||
}
|
||||
|
||||
// stop the stopwatch
|
||||
inline void stop() noexcept {
|
||||
auto d = Clock::now() - mStart;
|
||||
mMinLap = std::min(mMinLap, d);
|
||||
mMaxLap = std::max(mMaxLap, d);
|
||||
mAvgLap = (mAvgLap * mCount + d) / (mCount + 1);
|
||||
mCount++;
|
||||
}
|
||||
|
||||
// get the minimum lap time recorded
|
||||
duration getMinLapTime() const noexcept { return mMinLap; }
|
||||
|
||||
// get the maximum lap time recorded
|
||||
duration getMaxLapTime() const noexcept { return mMaxLap; }
|
||||
|
||||
// get the average lap time
|
||||
duration getAverageLapTime() const noexcept { return mAvgLap; }
|
||||
};
|
||||
|
||||
template<typename Clock>
|
||||
Stopwatch<Clock>::~Stopwatch() noexcept {
|
||||
slog.d << "Stopwatch \"" << mName << "\" : ["
|
||||
<< mMinLap.count() << ", "
|
||||
<< std::chrono::duration_cast<duration>(mAvgLap).count() << ", "
|
||||
<< mMaxLap.count() << "] ns" << io::endl;
|
||||
}
|
||||
|
||||
/*
|
||||
* AutoStopwatch can be used to start and stop a Stopwatch automatically
|
||||
* when entering and exiting a scope.
|
||||
*/
|
||||
template<typename Stopwatch>
|
||||
class AutoStopwatch {
|
||||
Stopwatch& stopwatch;
|
||||
public:
|
||||
inline explicit AutoStopwatch(Stopwatch& stopwatch) noexcept: stopwatch(stopwatch) {
|
||||
stopwatch.start();
|
||||
}
|
||||
|
||||
inline ~AutoStopwatch() noexcept { stopwatch.stop(); }
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_STOPWATCH_H
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <array> // note: this is safe, see how std::array is used below (inline / private)
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <functional> // for std::less
|
||||
#include <type_traits> // for std::enable_if
|
||||
|
||||
#include <limits.h>
|
||||
@@ -168,74 +167,6 @@ T log2i(T x) noexcept {
|
||||
return (sizeof(x) * 8 - 1u) - clz(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* branch-less version of std::lower_bound and std::upper_bound.
|
||||
* These versions are intended to be fully inlined, which only happens when the size
|
||||
* of the array is known at compile time. This code also performs better if the
|
||||
* array is a power-of-two in size.
|
||||
*
|
||||
* These code works even if the conditions above are not met, and becomes a less-branches
|
||||
* algorithm instead of a branch-less one!
|
||||
*/
|
||||
|
||||
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
|
||||
inline UTILS_PUBLIC
|
||||
RandomAccessIterator lower_bound(
|
||||
RandomAccessIterator first, RandomAccessIterator last, const T& value,
|
||||
COMPARE comp = std::less<T>(),
|
||||
bool assume_power_of_two = false) {
|
||||
size_t len = last - first;
|
||||
|
||||
if (!assume_power_of_two) {
|
||||
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
|
||||
// and gets optimized out if the size is known at compile time.
|
||||
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
|
||||
size_t difference = (last - first) - len;
|
||||
// If len was already a POT, then difference will be 0.
|
||||
// We need to explicitly check this case to avoid dereferencing past the end of the array
|
||||
first += !difference || comp(first[len], value) ? difference : 0;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
// The number of repetitions here doesn't affect the result. We manually unroll the loop
|
||||
// twice, to guarantee we have at least two iterations without branches (for the case
|
||||
// where the size is not known at compile time
|
||||
first += comp(first[len >>= 1u], value) ? len : 0;
|
||||
first += comp(first[len >>= 1u], value) ? len : 0;
|
||||
}
|
||||
first += comp(*first, value);
|
||||
return first;
|
||||
}
|
||||
|
||||
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
|
||||
inline UTILS_PUBLIC
|
||||
RandomAccessIterator upper_bound(
|
||||
RandomAccessIterator first, RandomAccessIterator last,
|
||||
const T& value, COMPARE comp = std::less<T>(),
|
||||
bool assume_power_of_two = false) {
|
||||
size_t len = last - first;
|
||||
|
||||
if (!assume_power_of_two) {
|
||||
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
|
||||
// and gets optimized out if the size is known at compile time.
|
||||
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
|
||||
size_t difference = (last - first) - len;
|
||||
// If len was already a POT, then difference will be 0.
|
||||
// We need to explicitly check this case to avoid dereferencing past the end of the array
|
||||
first += !difference || comp(value, first[len]) ? 0 : difference;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
// The number of repetitions here doesn't affect the result. We manually unroll the loop
|
||||
// twice, to guarantee we have at least two iterations without branches (for the case
|
||||
// where the size is not known at compile time
|
||||
first += !comp(value, first[len >>= 1u]) ? len : 0;
|
||||
first += !comp(value, first[len >>= 1u]) ? len : 0;
|
||||
}
|
||||
first += !comp(value, *first);
|
||||
return first;
|
||||
}
|
||||
|
||||
template<typename RandomAccessIterator, typename COMPARE>
|
||||
inline UTILS_PUBLIC
|
||||
RandomAccessIterator partition_point(
|
||||
|
||||
@@ -135,13 +135,15 @@ void TrackingPolicy::HighWatermark::onAlloc(
|
||||
}
|
||||
|
||||
TrackingPolicy::HighWatermark::~HighWatermark() noexcept {
|
||||
const size_t wm = mHighWaterMark;
|
||||
if (mSize > 0) {
|
||||
size_t wm = mHighWaterMark;
|
||||
size_t wmpct = wm / (mSize / 100);
|
||||
if (wmpct > 80) {
|
||||
slog.d << mName << " arena: High watermark "
|
||||
<< wm / 1024 << " KiB (" << wmpct << "%)" << io::endl;
|
||||
}
|
||||
} else {
|
||||
slog.d << mName << " arena: High watermark " << wm / 1024 << " KiB" << io::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ private:
|
||||
tsl::robin_set<Listener*> mListeners;
|
||||
|
||||
#if FILAMENT_UTILS_TRACK_ENTITIES
|
||||
tsl::robin_map<Entity, CallStack> mDebugActiveEntities;
|
||||
tsl::robin_map<Entity, CallStack, Entity::Hasher> mDebugActiveEntities;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ NameComponentManager::~NameComponentManager() = default;
|
||||
|
||||
void NameComponentManager::setName(Instance instance, const char* name) noexcept {
|
||||
if (instance) {
|
||||
elementAt<NAME>(instance) = details::SafeString{ name };
|
||||
elementAt<NAME>(instance) = CString{ name };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ TEST(StaticString, hash) {
|
||||
EXPECT_NE(a.getHash(), c.getHash());
|
||||
EXPECT_NE(b.getHash(), c.getHash());
|
||||
|
||||
std::hash<StaticString> ha;
|
||||
StaticString::Hasher ha;
|
||||
EXPECT_EQ(ha(a), a.getHash());
|
||||
}
|
||||
|
||||
|
||||
@@ -138,91 +138,6 @@ TEST(AlgorithmTest, popcount) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlgorithmTest, UpperBounds) {
|
||||
int* r;
|
||||
int array[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
r = utils::upper_bound(std::begin(array), std::end(array), array[i]);
|
||||
EXPECT_EQ(i + 1, r - std::begin(array));
|
||||
}
|
||||
|
||||
r = utils::upper_bound(std::begin(array), std::end(array), 100);
|
||||
EXPECT_EQ(std::end(array), r);
|
||||
|
||||
r = utils::upper_bound(std::begin(array), std::end(array), -1);
|
||||
EXPECT_EQ(std::begin(array), r);
|
||||
|
||||
|
||||
int array2[8] = { 0, 0, 0, 1, 1, 1, 2, 2 };
|
||||
r = utils::upper_bound(std::begin(array2), std::end(array2), 0);
|
||||
EXPECT_EQ(std::begin(array2) + 3, r);
|
||||
|
||||
r = utils::upper_bound(std::begin(array2), std::end(array2), 1);
|
||||
EXPECT_EQ(std::begin(array2) + 6, r);
|
||||
|
||||
r = utils::upper_bound(std::begin(array2), std::end(array2), 2);
|
||||
EXPECT_EQ(std::end(array2), r);
|
||||
|
||||
|
||||
int arrayNPotEven[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
for (int i = 0; i < 10; i++) {
|
||||
r = utils::upper_bound(std::begin(arrayNPotEven), std::end(arrayNPotEven), arrayNPotEven[i]);
|
||||
EXPECT_EQ(i + 1, r - std::begin(arrayNPotEven));
|
||||
}
|
||||
|
||||
int arrayNPotOdd[15] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
|
||||
for (int i = 0; i < 15; i++) {
|
||||
r = utils::upper_bound(std::begin(arrayNPotOdd), std::end(arrayNPotOdd), arrayNPotOdd[i]);
|
||||
EXPECT_EQ(i + 1, r - std::begin(arrayNPotOdd));
|
||||
}
|
||||
|
||||
std::array<int, 4> arr = {{1, 10, 15, 50}};
|
||||
EXPECT_EQ(50, *utils::upper_bound(arr.begin(), arr.end(), 15));
|
||||
}
|
||||
|
||||
TEST(AlgorithmTest, LowerBounds) {
|
||||
int* r;
|
||||
int array[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
r = utils::lower_bound(std::begin(array), std::end(array), array[i]);
|
||||
EXPECT_EQ(i, r - std::begin(array));
|
||||
}
|
||||
|
||||
r = utils::lower_bound(std::begin(array), std::end(array), 100);
|
||||
EXPECT_EQ(std::end(array), r);
|
||||
|
||||
r = utils::lower_bound(std::begin(array), std::end(array), -1);
|
||||
EXPECT_EQ(std::begin(array), r);
|
||||
|
||||
|
||||
int array2[8] = { 0, 0, 0, 1, 1, 1, 2, 2 };
|
||||
r = utils::lower_bound(std::begin(array2), std::end(array2), 0);
|
||||
EXPECT_EQ(std::begin(array2), r);
|
||||
|
||||
r = utils::lower_bound(std::begin(array2), std::end(array2), 1);
|
||||
EXPECT_EQ(std::begin(array2) + 3, r);
|
||||
|
||||
r = utils::lower_bound(std::begin(array2), std::end(array2), 2);
|
||||
EXPECT_EQ(std::begin(array2) + 6, r);
|
||||
|
||||
int arrayNPotEven[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
for (int i = 0; i < 10; i++) {
|
||||
r = utils::lower_bound(std::begin(arrayNPotEven), std::end(arrayNPotEven), arrayNPotEven[i]);
|
||||
EXPECT_EQ(i, r - std::begin(arrayNPotEven));
|
||||
}
|
||||
|
||||
int arrayNPotOdd[15] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
|
||||
for (int i = 0; i < 15; i++) {
|
||||
r = utils::lower_bound(std::begin(arrayNPotOdd), std::end(arrayNPotOdd), arrayNPotOdd[i]);
|
||||
EXPECT_EQ(i, r - std::begin(arrayNPotOdd));
|
||||
}
|
||||
|
||||
std::array<int, 4> arr = {{1, 10, 15, 50}};
|
||||
EXPECT_EQ(15, *utils::lower_bound(arr.begin(), arr.end(), 15));
|
||||
}
|
||||
|
||||
TEST(AlgorithmTest, Partition) {
|
||||
int* r;
|
||||
int array[8] = { 2, 5, 4, 8, 9, 9, 9, 9 };
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace filagui {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
project(uberz)
|
||||
|
||||
set(TARGET uberz)
|
||||
|
||||
# ==================================================================================================
|
||||
# Source files
|
||||
# ==================================================================================================
|
||||
set(SRCS
|
||||
src/main.cpp)
|
||||
|
||||
# ==================================================================================================
|
||||
# Target definitions
|
||||
# ==================================================================================================
|
||||
add_executable(${TARGET} ${SRCS})
|
||||
target_link_libraries(${TARGET} PRIVATE utils getopt uberzlib)
|
||||
|
||||
# =================================================================================================
|
||||
# Licenses
|
||||
# ==================================================================================================
|
||||
set(MODULE_LICENSES getopt)
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR}/generated)
|
||||
list_licenses(${GENERATION_ROOT}/licenses/licenses.inc ${MODULE_LICENSES})
|
||||
target_include_directories(${TARGET} PRIVATE ${GENERATION_ROOT})
|
||||
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
install(TARGETS ${TARGET} RUNTIME DESTINATION bin)
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <utils/Path.h>
|
||||
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <uberz/WritableArchive.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace utils;
|
||||
using namespace filament::uberz;
|
||||
|
||||
static std::string g_outputFile = "materials.uberz";
|
||||
static bool g_quietMode = false;
|
||||
static bool g_verboseMode = false;
|
||||
|
||||
static const char* USAGE = R"TXT(
|
||||
UBERZ aggregates and compresses a set of filamat files into a single archive file. It includes
|
||||
metadata that specifies the feature set that each material supports. By default, it generates
|
||||
a file called "materials.uberz" but this can be customized with -o.
|
||||
|
||||
Usage:
|
||||
UBERZ [options] <src_name_0> <src_name_1> ...
|
||||
|
||||
For each src_name, UBERZ looks for "src_name.filamat" and "src_name.spec" in the current
|
||||
working directory. If either of these files do not exist, an error is reported. Each
|
||||
pair of filamat/spec files corresponds to a material in the generated archive.
|
||||
|
||||
For more information on the format of the spec file, see the gltfio README.
|
||||
|
||||
Options:
|
||||
--help, -h
|
||||
Print this message
|
||||
--license, -L
|
||||
Print copyright and license information
|
||||
--output=filename, -o filename
|
||||
Specify a custom filename.
|
||||
--quiet, -q
|
||||
Suppress console output
|
||||
)TXT";
|
||||
|
||||
static void printUsage(const char* name) {
|
||||
std::string execName(Path(name).getName());
|
||||
const std::string from("UBERZ");
|
||||
std::string usage(USAGE);
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
usage.replace(pos, from.length(), execName);
|
||||
}
|
||||
puts(usage.c_str());
|
||||
}
|
||||
|
||||
static void license() {
|
||||
static const char *license[] = {
|
||||
#include "licenses/licenses.inc"
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char **p = &license[0];
|
||||
while (*p)
|
||||
std::cout << *p++ << std::endl;
|
||||
}
|
||||
|
||||
static int handleArguments(int argc, char* argv[]) {
|
||||
static constexpr const char* OPTSTR = "hLqvo:";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "license", no_argument, 0, 'L' },
|
||||
{ "quiet", no_argument, 0, 'q' },
|
||||
{ "verbose", no_argument, 0, 'v' },
|
||||
{ "output", required_argument, 0, 'o' },
|
||||
{ 0, 0, 0, 0 } // termination of the option list
|
||||
};
|
||||
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &optionIndex)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
case 'L':
|
||||
license();
|
||||
exit(0);
|
||||
case 'o':
|
||||
g_outputFile = optarg;
|
||||
break;
|
||||
case 'q':
|
||||
g_quietMode = true;
|
||||
break;
|
||||
case 'v':
|
||||
g_verboseMode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return optind;
|
||||
}
|
||||
|
||||
static size_t getFileSize(const char* filename) {
|
||||
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
|
||||
return in.tellg();
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const int optionIndex = handleArguments(argc, argv);
|
||||
const int numArgs = argc - optionIndex;
|
||||
if (numArgs < 1) {
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
WritableArchive archive(argc - optionIndex);
|
||||
|
||||
for (int argIndex = optionIndex; argIndex < argc; ++argIndex) {
|
||||
std::string name(argv[argIndex]);
|
||||
const Path filamatPath(name + ".filamat");
|
||||
if (!filamatPath.exists()) {
|
||||
cerr << "Unable to open " << filamatPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
const Path specPath(name + ".spec");
|
||||
if (!specPath.exists()) {
|
||||
cerr << "Unable to open " << specPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const size_t filamatSize = getFileSize(filamatPath.c_str());
|
||||
FixedCapacityVector<uint8_t> filamatBuffer(filamatSize);
|
||||
std::ifstream in(filamatPath.c_str(), std::ifstream::in | std::ifstream::binary);
|
||||
if (!in.read((char*) filamatBuffer.data(), filamatSize)) {
|
||||
cerr << "Unable to consume " << filamatPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// This tool is often invoked by CMake, so in verbose mode we print an index to stderr.
|
||||
// This allows us to see the spec index associated with each file for diagnostic purposes.
|
||||
if (g_verboseMode) {
|
||||
fprintf(stderr, "uberz %2d %s\n", argIndex - optionIndex, filamatPath.getName().c_str());
|
||||
}
|
||||
|
||||
archive.addMaterial(name.c_str(), filamatBuffer.data(), filamatSize);
|
||||
|
||||
std::string specLine;
|
||||
ifstream specStream(specPath.c_str());
|
||||
while (std::getline(specStream, specLine)) {
|
||||
archive.addSpecLine(specLine.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
FixedCapacityVector<uint8_t> binBuffer = archive.serialize();
|
||||
|
||||
ofstream binStream(g_outputFile, ios::binary);
|
||||
if (!binStream) {
|
||||
cerr << "Unable to open " << g_outputFile << endl;
|
||||
exit(1);
|
||||
}
|
||||
binStream.write((const char*) binBuffer.data(), binBuffer.size());
|
||||
binStream.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.23.1",
|
||||
"version": "1.23.2",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user