Compare commits

...
Author SHA1 Message Date
Benjamin Doherty 791f51dcb4 Update glslang to e3bca2a 2022-05-04 12:29:21 -07:00
Benjamin Doherty 4621f3ec5d Filament specific changes 2022-05-04 12:06:26 -07:00
Benjamin Doherty ab9c09d0d9 Update spirv-tools to 46492aa 2022-05-04 12:00:04 -07:00
Benjamin Doherty dbf51c1c7d Update SPIRV-Cross to 0c8a79b 2022-05-04 11:07:15 -07:00
Mathias Agopian ebd5f150c1 fix a potential threading/memory corruption
We can't call prepareProgram() from any thread, since it is calling
into the backend. When we had more than a certain number of commands,
we would use the jobsystem to generate them and so prepareProgram()
could end up being called from a jobsystem thread.

This is fixed by looping through all the commands once they're generated
and calling prepareProgram() then.
2022-05-03 15:16:11 -07:00
Philip Rideout 156dcb54e8 gltf_viewer: fix accidental removal of camera selector. 2022-05-03 14:33:43 -07:00
Mathias Agopian 88ef76989c Cleanup Renderer.cpp
The main changes are:

- ColorPass() and RefractionPass() are now static and moved into a 
  new RendererUtils file. These methods don't need FRenderer and are 
  more like a big "script".

- Cleaned-up includes

- Reformatted/reordered methods
2022-05-03 14:27:48 -07:00
Philip Rideout fc284af684 Rename SimpleViewer to ViewerGui.
The SimpleViewer C++ class was not viewer component like `ModelViewer`
and `<filament-viewer>`. It was actually just the UI builder used
in the `gltf_viewer` desktop app and the remote Android interface.
2022-05-03 08:10:53 -07:00
Philip Rideout 3a8685cd15 Upgrade emsdk to 3.1.9 2022-05-03 08:10:26 -07:00
Philip Rideout f035655f5b Add support for KHR_texture_basisu.
This adds a new implementation of the TextureProvider interface called
Ktx2Provider.

Tested using the KTX2 variant of the StainedGlassLamp model in the
Khronos samples repo.

Tested on WebGL 2.0 (Chrome v100), Android (Pixel 6 Pro), and Desktop
(Metal, OpenGL, and Vulkan via MoltenVK).
2022-05-02 13:15:56 -07:00
Ben Doherty f498e08d1e Update hello-pbr to work in all orientations (#5488) 2022-05-02 14:41:32 -04:00
Philip Rideout db3186a65f Remove etc2comp and astcenc from the repo. 2022-05-02 11:29:19 -07:00
Philip Rideout 248815a3e0 Fix regression with <filament-viewer>. 2022-05-02 11:09:26 -07:00
Benjamin Doherty ebb62b5d55 Release Filament 1.21.3 2022-05-02 13:17:33 -04:00
Philip Rideout b9a5ad205a Fix unit test failures.
Basis does not support DXT3 so in the past this unit test passed only
due to the enum translation bug that was recently fixed.
2022-05-02 09:48:36 -07:00
Philip Rideout 100986d8d9 Fix WebGL debug build (assert with 3-channel images) 2022-04-29 16:30:14 -07:00
Philip Rideout b7c9a36d57 Ktx2Reader: Fix error in enum translation table.
This caused DXT5 content to be interpreted as DXT3 by the GPU,
which seemed to manifest as visual artifacts in the alpha channel.
2022-04-29 16:29:53 -07:00
Philip Rideout d76551ab28 Add async interface to Ktx2Reader. 2022-04-29 14:20:57 -07:00
Mathias Agopian 92bc0752d1 repair VSM gaussian blur
When there was more than one shadowmap we would hit an assert. This 
bug was introduced recently and was due to incorrect use of the 
framegraph.
2022-04-29 12:46:37 -07:00
Mathias Agopian 657c251463 Ensure we always have 4 "safe" mip levels
A "safe" mip-level is one that has exactly half the dimensions of
the current level. In other words, we guarantee that we can halve the
dimensions 4 times without loss (i.e. odd dimension).

This is achieved by effectively padding the viewport (i.e. making the
viewport's dimensions multiple of 16). The post-processing passes
eventually take care of cropping the extra padding so the final
image is unchanged.

This is a more generic solution to dynamic resolution padding, helps
with memory allocations and helps all the algorithms that depend on
mipmaping (e.g. DoF, SSAO, Bloom).


One important improvement brought by this change is that all "final"
post-processing effects are now able to handle a sub-region of the
source, including: color-gradding, fxaa and upscaling.
2022-04-29 12:05:08 -07:00
Philip Rideout b6b8fee483 WebGL: fix isTextureFormatSupported for ETC2 formats.
It was assumed that ETC2 is supported on all ES3 devices, but WebGL on
desktop machines does not support ETC2.

Note that WebGL has its own special extension strings for compressed
textures.
2022-04-29 12:04:59 -07:00
Mathias Agopian be5b60f895 cleaner handling of shadow related uniforms
Some uniforms are needed during shadow map generation, other
during shadow sampling (color pass). We didn't have a clear separation
between them and ended up setting the UBOs that were needed only for
sampling multiple times during generation.
2022-04-29 12:04:47 -07:00
Mathias Agopian 681d31758f move the ShadowUib out of View into ShadowMapManager 2022-04-29 12:04:47 -07:00
Ben Doherty 80dc2f5c9d Move toCompressedFilamentEnum to Ktx1Reader header (#5493) 2022-04-29 14:49:54 -04:00
Mathias Agopian ce7dd7a1fb reorganize again our main UBO
- try to sort data by usage.
- clarify what post-process materials have access to (not enforced yet)
2022-04-28 10:13:20 -07:00
Mathias Agopian 85617c96a2 shrink post-process materials a bit
post-process materials don't need common_shadowing nor common_shading.

also don't rely on MIN_ROUGHNESS in .mat files, because it's not public.
2022-04-28 10:13:20 -07:00
Mathias Agopian 58760adb6a fix typo introduced in previous PR. fixes froxels. 2022-04-27 17:28:16 -07:00
Mathias Agopian 4ef0b72773 rearrange the per-view UBO (breaks materials)
- rename common_math and common_shadowing from .fs to .glsl
  because they were both included in both vertex and fragment
  shaders

- add getFragCoord() which always returns coordinates in the
  glsl convention. This is useful when used with RNGs to get
  consistant results accross backends.
2022-04-27 15:27:47 -07:00
Mathias Agopian 45631c3b81 update binding points from 8 to 12. 2022-04-27 15:27:03 -07:00
Mathias Agopian 6f6dde2579 Optimize custom rendertargets use in Renderer
we just store the "sampleable" attachments as a bitmask in 
RenderTarget instead of computing it each time.
2022-04-27 15:26:45 -07:00
Mathias Agopian e0acbe42cf update material version 2022-04-27 10:14:38 -07:00
Mathias Agopian 4f137ccf89 don't rely on frameUniform.resolution for froxelization
this is because this uniform is not well defined -- it's unclear if
it is set to the viewport or the buffer size.

also, it's not needed, and removes a couple multiplies from the shader.
2022-04-27 09:38:33 -07:00
Mathias Agopian 04010e7dd9 when matdbg is used don't terminate the program on errors 2022-04-27 08:20:00 -07:00
Mathias Agopian f4f9f331c0 CameraInfo cleanups
- add getUserViewMatrix() on CameraInfo, the "user" view matrix is the 
view matrix before we apply the world origin transform, it is needed in
 a few places, so we make it a  method so that in the future we could 
precompute it if we wanted to.

- remove worldOffset which is just the last column of worldOrigin
2022-04-26 11:32:41 -07:00
Mathias Agopian dada291f6b rename mScaling to mScalingCS 2022-04-26 11:32:41 -07:00
Mathias Agopian 18f2f8b6d4 remove unused parameters and state 2022-04-26 11:32:41 -07:00
Mathias Agopian d4117ac968 use interleavedGradientNoise() instead of custom implementaion
interleavedGradientNoise() is available in common_math, so use that
instead of our custom gradient noise RNG.
2022-04-26 11:32:41 -07:00
Mathias Agopian eccbc8c242 simplify FXAA setup
We now do all the setup math on the CPU side, but we also remove some
unnecessary computations based on assumptions that are no longer true.
2022-04-26 11:32:41 -07:00
Mathias Agopian 3cdf24f515 remove getUV() from post-process materials
it made it actually more confusing to have two version of the uv
(normalized and not). Also the unnormalized version was computed from
frameUniforms which is not great in the case of post-processing
(it just happened to work).
2022-04-26 11:30:35 -07:00
Mathias Agopian d91faf9df8 avoid unnecessary parameter copies 2022-04-26 11:28:36 -07:00
Brian Stringham d950efa6ea Changed ALL_SHADER_STATE_FLAGS initializer to support c++17, which is required for C++/CLI support. (#5465) 2022-04-26 08:59:44 -07:00
1417 changed files with 62541 additions and 83086 deletions
+1 -1
View File
@@ -349,7 +349,7 @@ same version that our continuous builds use.
```
cd <your chosen parent folder for the emscripten SDK>
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.5.zip > emsdk.zip
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.9.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
python ./emsdk.py install latest
python ./emsdk.py activate latest
+3 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.21.2'
implementation 'com.google.android.filament:filament-android:1.21.3'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.21.2'
pod 'Filament', '~> 1.21.3'
```
### Snapshots
@@ -177,6 +177,7 @@ steps:
- [x] KHR_materials_variants
- [x] KHR_materials_volume
- [x] KHR_mesh_quantization
- [x] KHR_texture_basisu
- [x] KHR_texture_transform
+17 -8
View File
@@ -3,16 +3,25 @@
This file contains one line summaries of commits that are worthy of mentioning in release notes.
A new header is inserted each time a *tag* is created.
## v1.21.3 (currently main branch)
## v1.22.1 (currently main branch)
- Renamed the KTXLoader Kotlin class to KTX1Loader. [⚠️ **API Change**].
- Added Ktx2Reader and BasisEncoder to support Basis-encoded KTX2 files.
- Desktop and Web Suzanne samples now use KTX2 instead of KTX1.
- Removed KTX1 compression support from mipgen. [⚠️ **API Change**].
- Added support for KTX2 to mipgen.
- gltfio: Added resetBoneMatrices() method.
## v1.22.0
- engine: Changed UBOs layout [⚠️ **Material breakage**].
- engine: Improve effects relying on mipmapping
## v1.21.3
- Java: Renamed the `KTXLoader` Kotlin class to `KTX1Loader`. [⚠️ **API Change**].
- libs: Added `Ktx2Reader` and `BasisEncoder` to support Basis-encoded KTX2 files.
- engine: Remove deprecated `Stream` APIs, see `Texture::import()` for an alternative [⚠️ **API Change**].
- tools: Removed KTX1 compression support from mipgen. [⚠️ **API Change**].
- tools: Added support for KTX2 to mipgen.
- gltfio: Added `resetBoneMatrices()` method.
- gltfio: Introduced `TextureProvider` interface. [⚠️ **API Change**].
- engine: remove deprecated `Stream` APIs, see `Texture::import()` for an alternative [⚠️ **API Change**].
- gltfio: Fix progress indicator when error occurs.
- samples: Desktop and Web Suzanne samples now use KTX2 instead of KTX1.
- samples: Fix regression with sample-gltf-viewer and zip files.
## v1.21.2
+12 -2
View File
@@ -11,10 +11,18 @@ add_library(dracodec STATIC IMPORTED)
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
add_library(ktxreader STATIC IMPORTED)
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
add_library(stb STATIC IMPORTED)
set_target_properties(stb PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libstb.a)
add_library(basis_transcoder STATIC IMPORTED)
set_target_properties(basis_transcoder PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbasis_transcoder.a)
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
@@ -50,6 +58,7 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/FFilamentInstance.h
${GLTFIO_DIR}/src/FilamentInstance.cpp
${GLTFIO_DIR}/src/GltfEnums.h
${GLTFIO_DIR}/src/Ktx2Provider.cpp
${GLTFIO_DIR}/src/MaterialProvider.cpp
${GLTFIO_DIR}/src/ResourceLoader.cpp
${GLTFIO_DIR}/src/StbProvider.cpp
@@ -86,6 +95,7 @@ set(GLTFIO_INCLUDE_DIRS
../../third_party/hat-trie
../../third_party/stb
../../libs/utils/include
../../libs/ktxreader/include
)
add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
@@ -95,9 +105,9 @@ set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_
if(GLTFIO_LITE)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_LITE=1)
target_link_libraries(gltfio-jni filament-jni utils log stb gltfio_resources_lite)
target_link_libraries(gltfio-jni filament-jni utils log stb ktxreader basis_transcoder gltfio_resources_lite)
else()
target_link_libraries(gltfio-jni filament-jni utils log stb gltfio_resources)
target_link_libraries(gltfio-jni filament-jni utils log stb ktxreader basis_transcoder gltfio_resources)
# Enable Draco in the non-lite variant of gltfio.
target_link_libraries(gltfio-jni dracodec)
@@ -117,12 +117,19 @@ Java_com_google_android_filament_gltfio_ResourceLoader_nAsyncCancelLoad(JNIEnv*,
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateTextureProvider(JNIEnv*, jclass,
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateStbProvider(JNIEnv*, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jlong) createStbProvider(engine);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateKtx2Provider(JNIEnv*, jclass,
jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jlong) createKtx2Provider(engine);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_ResourceLoader_nDestroyTextureProvider(JNIEnv*, jclass,
jlong nativeProvider) {
@@ -35,7 +35,8 @@ import java.nio.Buffer;
*/
public class ResourceLoader {
private final long mNativeObject;
private final long mNativeProvider;
private final long mNativeStbProvider;
private final long mNativeKtx2Provider;
/**
* Constructs a resource loader tied to the given Filament engine.
@@ -47,9 +48,11 @@ public class ResourceLoader {
public ResourceLoader(@NonNull Engine engine) {
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateResourceLoader(nativeEngine, false, false, false);
mNativeProvider = nCreateTextureProvider(nativeEngine);
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeProvider);
nAddTextureProvider(mNativeObject, "image/png", mNativeProvider);
mNativeStbProvider = nCreateStbProvider(nativeEngine);
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
}
/**
@@ -67,9 +70,11 @@ public class ResourceLoader {
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights,
recomputeBoundingBoxes, ignoreBindTransform);
mNativeProvider = nCreateTextureProvider(nativeEngine);
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeProvider);
nAddTextureProvider(mNativeObject, "image/png", mNativeProvider);
mNativeStbProvider = nCreateStbProvider(nativeEngine);
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
}
/**
@@ -77,7 +82,8 @@ public class ResourceLoader {
*/
public void destroy() {
nDestroyResourceLoader(mNativeObject);
nDestroyTextureProvider(mNativeProvider);
nDestroyTextureProvider(mNativeStbProvider);
nDestroyTextureProvider(mNativeKtx2Provider);
}
/**
@@ -187,7 +193,8 @@ public class ResourceLoader {
private static native void nAsyncUpdateLoad(long nativeLoader);
private static native void nAsyncCancelLoad(long nativeLoader);
private static native long nCreateTextureProvider(long nativeEngine);
private static native long nCreateStbProvider(long nativeEngine);
private static native long nCreateKtx2Provider(long nativeEngine);
private static native void nAddTextureProvider(long nativeLoader, String url, long nativeProvider);
private static native void nDestroyTextureProvider(long nativeProvider);
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.21.2
VERSION_NAME=1.21.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -17,7 +17,7 @@ export PATH="$PWD:$PATH"
# npm install -g typescript
# Install emscripten.
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.5.zip > emsdk.zip
curl -L https://github.com/emscripten-core/emsdk/archive/refs/tags/3.1.9.zip > emsdk.zip
unzip emsdk.zip ; mv emsdk-* emsdk ; cd emsdk
./emsdk install latest
./emsdk activate latest
+1 -1
View File
@@ -2169,7 +2169,7 @@ type aliases:
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getResolution()** | float4 | Resolution of the view in pixels: `width`, `height`, `1 / width`, `1 / height`
**getResolution()** | float4 | Dimensions of the view's effective viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands. This can be used in conjunction with `getNormalizedViewportCoord()` to generate pixel coordinates.
**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space
**getWorldOffset()** | float3 | The shift required to obtain API-level world space
**getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1
+2
View File
@@ -77,6 +77,7 @@ set(SRCS
src/RenderTarget.cpp
src/RenderableManager.cpp
src/Renderer.cpp
src/RendererUtils.cpp
src/ResourceAllocator.cpp
src/ResourceList.cpp
src/Scene.cpp
@@ -143,6 +144,7 @@ set(PRIVATE_HDRS
src/PerViewUniforms.h
src/PIDController.h
src/PostProcessManager.h
src/RendererUtils.h
src/RenderPass.h
src/RenderPrimitive.h
src/ResourceAllocator.h
+10 -10
View File
@@ -42,22 +42,22 @@
*/
namespace filament::backend {
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1;
static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2;
static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 32; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
"The number of buffer objects that can be attached to a VertexBuffer must be "
"less than or equal to the maximum number of vertex attributes.");
static constexpr size_t CONFIG_BINDING_COUNT = 8;
static constexpr size_t CONFIG_BINDING_COUNT = 12; // This is guaranteed by OpenGL ES.
/**
* Selects which driver a particular Engine should use.
@@ -912,7 +912,7 @@ struct ShaderStageFlags {
(fragment && type == ShaderType::FRAGMENT);
}
};
static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { .vertex = true, .fragment = true };
static constexpr ShaderStageFlags ALL_SHADER_STAGE_FLAGS = { true, true };
/**
* Selects which buffers to clear at the beginning of the render pass, as well as which buffers
+2
View File
@@ -42,9 +42,11 @@ void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char*
#ifdef NDEBUG
# define CHECK_GL_ERROR(out)
# define CHECK_GL_ERROR_NON_FATAL(out)
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target)
#else
# define CHECK_GL_ERROR(out) { GLUtils::assertGLError(out, __func__, __LINE__); }
# define CHECK_GL_ERROR_NON_FATAL(out) { GLUtils::checkGLError(out, __func__, __LINE__); }
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
#endif
@@ -306,7 +306,9 @@ void OpenGLContext::initExtensionsGLES() noexcept {
ext.EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
ext.EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
ext.EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
#if !defined(__EMSCRIPTEN__)
ext.EXT_texture_compression_etc2 = true;
#endif
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
ext.KHR_debug = exts.has("GL_KHR_debug"sv);
@@ -2986,7 +2986,11 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
instanceCount);
}
#ifdef FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR(utils::slog.e)
#endif
}
// explicit instantiation of the Dispatcher
+1 -1
View File
@@ -345,7 +345,7 @@ private:
void setViewportScissor(Viewport const& viewportScissor) noexcept;
// sampler buffer binding points (nullptr if not used)
std::array<HwSamplerGroup*, Program::BINDING_COUNT> mSamplerBindings = {}; // 8 pointers
std::array<HwSamplerGroup*, Program::BINDING_COUNT> mSamplerBindings = {}; // 12 pointers
mutable tsl::robin_map<uint32_t, GLuint> mSamplerMap;
mutable std::vector<GLTexture*> mExternalStreams;
@@ -264,7 +264,7 @@ void OpenGLProgram::initialize(OpenGLContext& context) {
mValid = OpenGLProgram::checkProgramStatus(name.c_str_safe(),
gl.program, gl.shaders, initializationData->shaderSourceCode);
if (mValid) {
if (UTILS_LIKELY(mValid)) {
initializeProgramState(context, gl.program,
initializationData->uniformBlockInfo,
initializationData->samplerGroupInfo);
+2 -2
View File
@@ -60,7 +60,7 @@ public:
// turns out the former might be relatively cheap to check, the later requires
// a bit less. Compared to what updateSamplers() actually does, which is
// pretty little, I'm not sure we'll get ahead.
// pretty little, I'm not sure if we'll get ahead.
updateSamplers(gld);
}
@@ -113,7 +113,7 @@ private:
union {
// when mInitialized == true:
// information about each USED sampler buffer per binding (no gaps)
std::array<uint8_t, Program::BINDING_COUNT> mUsedBindingPoints; // 8 bytes
std::array<uint8_t, Program::BINDING_COUNT> mUsedBindingPoints; // 12 bytes
// when mInitialized == false:
// lazy initialization data pointer
LazyInitializationData* mLazyInitializationData;
@@ -289,18 +289,17 @@ private:
// Represents all the Vulkan state that comprises a bound descriptor set.
struct DescriptorKey {
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 0
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 64
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 832
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 1024
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 1056
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 96 0
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 768 96
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 864
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 48 1056
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 48 1104
};
static_assert(offsetof(DescriptorKey, samplers) == 64);
static_assert(offsetof(DescriptorKey, inputAttachments) == 832);
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1024);
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1056);
static_assert(sizeof(DescriptorKey) == 1088, "DescriptorKey must not have implicit padding.");
static_assert(offsetof(DescriptorKey, samplers) == 96);
static_assert(offsetof(DescriptorKey, inputAttachments) == 864);
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1056);
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1104);
static_assert(sizeof(DescriptorKey) == 1152, "DescriptorKey must not have implicit padding.");
using DescHashFn = utils::hash::MurmurHashFn<DescriptorKey>;
-1
View File
@@ -244,7 +244,6 @@ bool Froxelizer::update() noexcept {
mClipToFroxelX = (0.5f * viewport.width) / froxelDimension.x;
mClipToFroxelY = (0.5f * viewport.height) / froxelDimension.y;
mOneOverDimension = 1.0f / float2(froxelDimension);
uniformsNeedUpdating = true;
#ifndef NDEBUG
+2 -5
View File
@@ -125,10 +125,8 @@ public:
void updateUniforms(PerViewUib& s) {
s.zParams = mParamsZ;
s.fParams = mParamsF.yz;
s.fParamsX = mParamsF.x;
s.oneOverFroxelDimensionX = mOneOverDimension.x;
s.oneOverFroxelDimensionY = mOneOverDimension.y;
s.fParams = mParamsF;
s.froxelCountXY = math::float2{ mViewport.width, mViewport.height } / mFroxelDimension;
}
// send froxel data to GPU
@@ -242,7 +240,6 @@ private:
float mLinearizer = 0.0f;
float mClipToFroxelX = 0.0f;
float mClipToFroxelY = 0.0f;
math::float2 mOneOverDimension = {};
backend::BufferObjectHandle mRecordsBuffer;
backend::Handle<backend::HwTexture> mFroxelTexture;
+29 -16
View File
@@ -81,7 +81,7 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
s.clipFromWorldMatrix = clipFromWorld; // projection * view
s.worldFromClipMatrix = worldFromClip; // 1/(projection * view)
s.cameraPosition = float3{ camera.getPosition() };
s.worldOffset = camera.worldOffset;
s.worldOffset = camera.getWorldOffset();
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
@@ -105,12 +105,14 @@ void PerViewUniforms::prepareExposure(float ev100) noexcept {
s.ev100 = ev100;
}
void PerViewUniforms::prepareViewport(const filament::Viewport &viewport) noexcept {
void PerViewUniforms::prepareViewport(const filament::Viewport& viewport,
uint32_t xoffset, uint32_t yoffset) noexcept {
const float w = float(viewport.width);
const float h = float(viewport.height);
auto& s = mUniforms.edit();
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
s.origin = float2{ viewport.left, viewport.bottom };
s.offset = float2{ xoffset, yoffset };
}
void PerViewUniforms::prepareTime(math::float4 const& userTime) noexcept {
@@ -283,23 +285,24 @@ void PerViewUniforms::prepareDynamicLights(Froxelizer& froxelizer) noexcept {
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
}
void PerViewUniforms::prepareShadowMapping(ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept {
void PerViewUniforms::prepareShadowMapping() noexcept {
auto& s = mUniforms.edit();
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
s.vsmLightBleedReduction = options.lightBleedReduction;
}
s.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
s.cascadeSplits = shadowMappingUniforms.cascadeSplits;
s.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
s.shadowBias = shadowMappingUniforms.shadowBias;
s.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
s.directionalShadows = shadowMappingUniforms.directionalShadows;
s.cascades = shadowMappingUniforms.cascades;
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
uniforms.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
uniforms.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
uniforms.shadowBias = shadowMappingUniforms.shadowBias;
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
uniforms.cascades = shadowMappingUniforms.cascades;
}
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept {
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
if (options.anisotropy > 0 || options.mipmapping) {
@@ -313,9 +316,14 @@ void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
}});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_VSM;
s.vsmExponent = 5.54f; // fp16: max 5.54f, fp32: max 42.0
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
s.vsmLightBleedReduction = options.lightBleedReduction;
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
}
void PerViewUniforms::prepareShadowPCF(Handle<HwTexture> texture) noexcept {
void PerViewUniforms::prepareShadowPCF(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
mSamplers.setSampler(PerViewSib::SHADOW_MAP, {
texture, {
.filterMag = SamplerMagFilter::LINEAR,
@@ -325,22 +333,27 @@ void PerViewUniforms::prepareShadowPCF(Handle<HwTexture> texture) noexcept {
}});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
}
void PerViewUniforms::prepareShadowDPCF(Handle <HwTexture> texture,
void PerViewUniforms::prepareShadowDPCF(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept {
mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {}});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_DPCF;
s.shadowPenumbraRatioScale = options.penumbraRatioScale;
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
}
void PerViewUniforms::prepareShadowPCSS(Handle <HwTexture> texture,
void PerViewUniforms::prepareShadowPCSS(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept {
mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {}});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCSS;
s.shadowPenumbraRatioScale = options.penumbraRatioScale;
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
}
void PerViewUniforms::commit(backend::DriverApi& driver) noexcept {
+27 -8
View File
@@ -65,7 +65,16 @@ public:
void prepareCamera(const CameraInfo& camera) noexcept;
void prepareUpscaler(math::float2 scale, DynamicResolutionOptions const& options) noexcept;
void prepareViewport(const filament::Viewport& viewport) noexcept;
/*
* @param viewport viewport (should be same as RenderPassParams::viewport)
* @param xoffset horizontal rendering offset *within* the viewport.
* Non-zero when we have guard bands.
* @param yoffset vertical rendering offset *within* the viewport.
* Non-zero when we have guard bands.
*/
void prepareViewport(const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
void prepareTime(math::float4 const& userTime) noexcept;
void prepareTemporalNoise(TemporalAntiAliasingOptions const& options) noexcept;
void prepareExposure(float ev100) noexcept;
@@ -84,8 +93,7 @@ public:
math::mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
void prepareShadowMapping(ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept;
void prepareShadowMapping() noexcept;
void prepareDirectionalLight(float exposure,
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
@@ -94,11 +102,20 @@ public:
void prepareDynamicLights(Froxelizer& froxelizer) noexcept;
// maybe these should have their own UBO, they're needed only when GENERATING the shadowmaps
void prepareShadowVSM(TextureHandle texture, VsmShadowOptions const& options) noexcept;
void prepareShadowPCF(TextureHandle texture) noexcept;
void prepareShadowDPCF(TextureHandle texture, SoftShadowOptions const& options) noexcept;
void prepareShadowPCSS(TextureHandle texture, SoftShadowOptions const& options) noexcept;
void prepareShadowVSM(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept;
void prepareShadowPCF(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
void prepareShadowDPCF(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept;
void prepareShadowPCSS(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept;
// update local data into GPU UBO
void commit(backend::DriverApi& driver) noexcept;
@@ -116,6 +133,8 @@ private:
backend::Handle<backend::HwBufferObject> mUniformBufferHandle;
backend::Handle<backend::HwSamplerGroup> mSamplerGroupHandle;
std::uniform_real_distribution<float> mUniformDistribution{ 0.0f, 1.0f };
static void prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
};
} // namespace filament
+58 -39
View File
@@ -531,8 +531,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
data.history = builder.sample(history);
}
},
[this, projection = cameraInfo.projection, viewMatrix = cameraInfo.view,
worldOrigin = cameraInfo.worldOrigin, uvFromClipMatrix, historyProjection,
[this, projection = cameraInfo.projection,
userViewMatrix = cameraInfo.getUserViewMatrix(), uvFromClipMatrix, historyProjection,
options, &uniforms, renderPass = pass]
(FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable {
// set structure sampler
@@ -542,7 +542,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
// set screen-space reflections and screen-space refractions
mat4f uvFromViewMatrix = uvFromClipMatrix * projection;
mat4f reprojection = mat4f{ uvFromClipMatrix * historyProjection
* inverse(viewMatrix * worldOrigin) };
* inverse(userViewMatrix) };
// the history sampler is a regular texture2D
TextureHandle history = data.history ?
@@ -805,11 +805,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
mi->setParameter("ssctConeAngleTangeant", std::tan(options.ssct.lightConeRad * 0.5f));
mi->setParameter("ssctContactDistanceMaxInv", 1.0f / options.ssct.contactDistanceMax);
// light direction in view space
// (note: this is actually equivalent to using the camera view matrix -- before the
// world matrix is accounted for)
const mat4f m{ cameraInfo.view * cameraInfo.worldOrigin };
const mat4f view{ cameraInfo.getUserViewMatrix() };
const float3 l = normalize(
mat3f::getTransformForNormals(m.upperLeft())
mat3f::getTransformForNormals(view.upperLeft())
* options.ssct.lightDirection);
mi->setParameter("ssctIntensity",
options.ssct.enabled ? options.ssct.intensity : 0.0f);
@@ -973,8 +971,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::generateGaussianMipmap(Frame
auto from = input;
for (size_t i = 0; i < levels - 1; i++) {
auto output = mipmapPass->out[i];
gaussianBlurPass(fg, from, output, reinhard, kernelWidth, sigma);
from = output;
from = gaussianBlurPass(fg, from, output, reinhard, kernelWidth, sigma);
reinhard = false; // only do the reinhard filtering on the first level
}
@@ -1035,7 +1032,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
// and because it's a separable filter, the effective 2D filter kernel size is 17*17
// The total number of samples needed over the two passes is 18.
fg.addPass<BlurPassData>("Gaussian Blur Pass (separable)",
auto& blurPass = fg.addPass<BlurPassData>("Gaussian Blur Pass (separable)",
[&](FrameGraph::Builder& builder, auto& data) {
auto inDesc = builder.getDescriptor(input);
@@ -1143,7 +1140,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
render(hwOutRT, separableGaussianBlur.getPipelineState(mEngine), driver);
});
return output;
return blurPass->out;
}
PostProcessManager::ScreenSpaceRefConfig PostProcessManager::prepareMipmapSSR(FrameGraph& fg,
@@ -1342,7 +1339,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::generateMipmapSSR(
input = ppm.resolveBaseLevel(fg, "ssr", input);
// then blit into an appropriate texture
// this handles scaling, format conversion and mipmaping
input = ppm.opaqueBlit(fg, input, outDesc);
input = ppm.opaqueBlit(fg, input, { 0, 0, desc.width, desc.height }, outDesc);
}
}
@@ -1471,13 +1468,16 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
*/
const uint32_t dofResolution = dofOptions.nativeResolution ? 1u : 2u;
constexpr const uint32_t maxMipLevels = 4u; // mip levels at full-resolution
constexpr const uint32_t maxMipLevelsMask = (1u << maxMipLevels) - 1u;
auto const& colorDesc = fg.getDescriptor(input);
const uint32_t width = ((colorDesc.width + maxMipLevelsMask) & ~maxMipLevelsMask) / dofResolution;
const uint32_t height = ((colorDesc.height + maxMipLevelsMask) & ~maxMipLevelsMask) / dofResolution;
const uint8_t maxLevelCount = FTexture::maxLevelCount(width, height);
uint8_t mipmapCount = min(maxLevelCount, uint8_t(maxMipLevels));
const uint32_t width = colorDesc.width / dofResolution;
const uint32_t height = colorDesc.height / dofResolution;
// at full resolution, 4 "safe" levels are guaranteed
constexpr const uint32_t maxMipLevels = 4u;
// compute numbers of "safe" levels (should be 4, but can be 3 at half res)
const uint8_t mipmapCount = std::min(maxMipLevels, ctz(width | height));
assert_invariant(mipmapCount == maxMipLevels || mipmapCount == maxMipLevels-1);
/*
* Setup:
@@ -1928,7 +1928,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloomPass(FrameGraph& fg
if (2 * width < desc.width || 2 * height < desc.height) {
// if we're scaling down by more than 2x, prescale the image with a blit to improve
// performance. This is important on mobile/tilers.
input = opaqueBlit(fg, input, {
input = opaqueBlit(fg, input, { 0, 0, desc.width, desc.height }, {
.width = std::max(1u, desc.width / 2),
.height = std::max(1u, desc.height / 2),
.format = outFormat
@@ -2265,14 +2265,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(
}
FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
FrameGraphId<FrameGraphTexture> bloom,
FrameGraphId<FrameGraphTexture> flare,
FColorGrading const* colorGrading,
ColorGradingConfig const& colorGradingConfig,
BloomOptions const& bloomOptions,
VignetteOptions const& vignetteOptions,
float2 scale) noexcept
VignetteOptions const& vignetteOptions) noexcept
{
FrameGraphId<FrameGraphTexture> bloomDirt;
FrameGraphId<FrameGraphTexture> starburst;
@@ -2309,11 +2308,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
auto& ppColorGrading = fg.addPass<PostProcessColorGrading>("colorGrading",
[&](FrameGraph::Builder& builder, auto& data) {
auto const& inputDesc = fg.getDescriptor(input);
data.input = builder.sample(input);
data.output = builder.createTexture("colorGrading output", {
.width = inputDesc.width,
.height = inputDesc.height,
.width = vp.width,
.height = vp.height,
.format = colorGradingConfig.ldrFormat
});
data.output = builder.declareRenderPass(data.output);
@@ -2390,6 +2388,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
bloomParameters.y = 1.0f - bloomParameters.x;
}
auto const& input = resources.getDescriptor(data.input);
auto const& output = resources.getDescriptor(data.output);
float4 vignetteParameters = getVignetteParameters(
vignetteOptions, output.width, output.height);
@@ -2402,6 +2401,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("fxaa", colorGradingConfig.fxaa);
mi->setParameter("temporalNoise", temporalNoise);
mi->setParameter("viewport", float4{
(float)vp.left / input.width,
(float)vp.bottom / input.height,
(float)vp.width / input.width,
(float)vp.height / input.height
});
const uint8_t variant = uint8_t(colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
@@ -2414,7 +2419,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
}
FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
TextureFormat outFormat, bool translucent) noexcept {
struct PostProcessFXAA {
@@ -2424,17 +2429,16 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
auto& ppFXAA = fg.addPass<PostProcessFXAA>("fxaa",
[&](FrameGraph::Builder& builder, auto& data) {
auto const& inputDesc = fg.getDescriptor(input);
data.input = builder.sample(input);
data.output = builder.createTexture("fxaa output", {
.width = inputDesc.width,
.height = inputDesc.height,
.width = vp.width,
.height = vp.height,
.format = outFormat
});
data.output = builder.declareRenderPass(data.output);
},
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto const& inDesc = resources.getDescriptor(data.input);
auto const& texture = resources.getTexture(data.input);
auto const& out = resources.getRenderPassInfo();
@@ -2444,6 +2448,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR
});
mi->setParameter("viewport", float4{
(float)vp.left / inDesc.width,
(float)vp.bottom / inDesc.height,
(float)vp.width / inDesc.width,
(float)vp.height / inDesc.height
});
mi->setParameter("texelSize", 1.0f / float2{ inDesc.width, inDesc.height });
const uint8_t variant = uint8_t(translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
@@ -2463,7 +2474,7 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
auto& current = frameHistory.getCurrent().*pTaa;
// compute projection
current.projection = mat4f{ inoutCameraInfo->projection * (inoutCameraInfo->view * inoutCameraInfo->worldOrigin) };
current.projection = mat4f{ inoutCameraInfo->projection * inoutCameraInfo->getUserViewMatrix() };
current.frameId = previous.frameId + 1;
// sample position within a pixel [-0.5, 0.5]
@@ -2560,7 +2571,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
float2 d = sampleOffsets[i] - current.jitter;
d *= 1.0f / taaOptions.filterWidth;
// this is a gaussian fit of a 3.3 Blackman Harris window
// see: "High Quality Temporal Supersampling" by Bruan Karis
// see: "High Quality Temporal Supersampling" by Brian Karis
weights[i] = std::exp2(-3.3f * (d.x * d.x + d.y * d.y));
sum += weights[i];
}
@@ -2628,7 +2639,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
}
FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor const& outDesc,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
FrameGraphTexture::Descriptor const& outDesc,
SamplerMagFilter filter) noexcept {
struct PostProcessScaling {
@@ -2654,7 +2666,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
// HwTexture handle. Using a RenderPass works because data.input will resolve
// to the actual imported render target and will have the correct viewport.
builder.declareRenderPass("opaque blit input", {
.attachments = { .color = { data.input }}});
.attachments = { .color = { data.input }},
.viewport = vp
});
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
auto out = resources.getRenderPassInfo(0);
@@ -2671,7 +2685,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::opaqueBlit(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
FrameGraph& fg, bool translucent, DynamicResolutionOptions dsrOptions,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
FrameGraphTexture::Descriptor const& outDesc) noexcept {
Handle<HwRenderPrimitive> fullScreenRenderPrimitive = mEngine.getFullScreenRenderPrimitive();
@@ -2688,13 +2702,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
|| dsrOptions.quality == QualityLevel::HIGH);
// helper to set the EASU uniforms
auto setEasuUniforms = [](FMaterialInstance* mi,
auto setEasuUniforms = [vp](FMaterialInstance* mi,
FrameGraphTexture::Descriptor const& inputDesc,
FrameGraphTexture::Descriptor const& outputDesc) {
FSRUniforms uniforms{};
FSR_ScalingSetup(&uniforms, {
.viewportWidth = inputDesc.width,
.viewportHeight = inputDesc.height,
.input = vp,
.inputWidth = inputDesc.width,
.inputHeight = inputDesc.height,
.outputWidth = outputDesc.width,
@@ -2786,6 +2799,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blendBlit(
mi->setParameter("resolution",
float4{ outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height });
mi->setParameter("viewport", float4{
(float)vp.left / inputDesc.width,
(float)vp.bottom / inputDesc.height,
(float)vp.width / inputDesc.width,
(float)vp.height / inputDesc.height
});
mi->commit(driver);
mi->use(driver);
}
+7 -7
View File
@@ -162,14 +162,13 @@ public:
// Color grading, tone mapping, dithering and bloom
FrameGraphId<FrameGraphTexture> colorGrading(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
FrameGraphId<FrameGraphTexture> bloom,
FrameGraphId<FrameGraphTexture> flare,
const FColorGrading* colorGrading,
ColorGradingConfig const& colorGradingConfig,
BloomOptions const& bloomOptions,
VignetteOptions const& vignetteOptions,
math::float2 scale) noexcept;
VignetteOptions const& vignetteOptions) noexcept;
void colorGradingPrepareSubpass(backend::DriverApi& driver, const FColorGrading* colorGrading,
ColorGradingConfig const& colorGradingConfig,
@@ -188,8 +187,8 @@ public:
// Anti-aliasing
FrameGraphId<FrameGraphTexture> fxaa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, backend::TextureFormat outFormat,
bool translucent) noexcept;
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
backend::TextureFormat outFormat, bool translucent) noexcept;
// Temporal Anti-aliasing
void prepareTaa(FrameGraph& fg, filament::Viewport const& svp,
@@ -208,12 +207,13 @@ public:
// Blit/rescaling/resolves
FrameGraphId<FrameGraphTexture> opaqueBlit(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor const& outDesc,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
FrameGraphTexture::Descriptor const& outDesc,
backend::SamplerMagFilter filter = backend::SamplerMagFilter::LINEAR) noexcept;
FrameGraphId<FrameGraphTexture> blendBlit(
FrameGraph& fg, bool translucent, DynamicResolutionOptions dsrOptions,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> input, filament::Viewport const& vp,
FrameGraphTexture::Descriptor const& outDesc) noexcept;
// resolve base level of input and outputs a 1-level texture
+10 -3
View File
@@ -138,6 +138,15 @@ void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcep
// "eof" command. these commands are guaranteed to be sorted last in the
// command buffer.
curr[commandCount - 1].key = uint64_t(Pass::SENTINEL);
// Go over all the commands and call prepareProgram().
// This must be done from the main thread.
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
auto ma = first->primitive.mi->getMaterial();
ma->prepareProgram(first->primitive.materialVariant);
}
}
}
void RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
@@ -402,7 +411,6 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
if constexpr (isColorPass) {
cmdColor.primitive.primitiveHandle = primitive.getHwHandle();
RenderPass::setupColorCommand(cmdColor, variant, mi, inverseFrontFaces);
ma->prepareProgram(cmdColor.primitive.materialVariant);
cmdColor.primitive.morphWeightBuffer = morphing.handle;
cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
@@ -487,7 +495,6 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
}
if constexpr (isDepthPass) {
ma->prepareProgram(cmdDepth.primitive.materialVariant);
const RasterState rs = ma->getRasterState();
const TransparencyMode mode = mi->getTransparencyMode();
const BlendingMode blendingMode = ma->getBlendingMode();
@@ -535,7 +542,7 @@ void RenderPass::updateSummedPrimitiveCounts(
void RenderPass::Executor::execute(const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept {
backend::RenderPassParams const& params) const noexcept {
FEngine& engine = mEngine;
DriverApi& driver = engine.getDriverApi();
+1 -1
View File
@@ -333,7 +333,7 @@ public:
~Executor() noexcept;
void execute(const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept;
backend::RenderPassParams const& params) const noexcept;
};
// returns a new executor for this pass
+327
View File
@@ -0,0 +1,327 @@
/*
* 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 "RendererUtils.h"
#include "details/Engine.h"
#include "details/View.h"
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphResources.h"
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Panic.h>
namespace filament {
using namespace backend;
using namespace math;
FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor const& passExecutor) noexcept {
struct ColorPassData {
FrameGraphId<FrameGraphTexture> shadows;
FrameGraphId<FrameGraphTexture> color;
FrameGraphId<FrameGraphTexture> output;
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> ssao;
FrameGraphId<FrameGraphTexture> ssr; // either screen-space reflections or refractions
FrameGraphId<FrameGraphTexture> structure;
float4 clearColor{};
TargetBufferFlags clearFlags{};
};
Blackboard& blackboard = fg.getBlackboard();
auto& colorPass = fg.addPass<ColorPassData>(name,
[&](FrameGraph::Builder& builder, ColorPassData& data) {
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
data.color = blackboard.get<FrameGraphTexture>("color");
data.depth = blackboard.get<FrameGraphTexture>("depth");
// Screen-space reflection or refractions
data.ssr = blackboard.get<FrameGraphTexture>("ssr");
if (data.ssr) {
data.ssr = builder.sample(data.ssr);
}
if (config.hasContactShadows) {
data.structure = blackboard.get<FrameGraphTexture>("structure");
assert_invariant(data.structure);
data.structure = builder.sample(data.structure);
}
if (data.shadows) {
data.shadows = builder.sample(data.shadows);
}
if (data.ssao) {
data.ssao = builder.sample(data.ssao);
}
if (!data.color) {
// FIXME: this works only when the viewport is full
// if (view.isSkyboxVisible()) {
// // if the skybox is visible, then we don't need to clear at all
// clearColorFlags &= ~TargetBufferFlags::COLOR;
// }
data.color = builder.createTexture("Color Buffer", colorBufferDesc);
}
const bool canResolveDepth = engine.getDriverApi().isAutoDepthResolveSupported();
if (!data.depth) {
// clear newly allocated depth buffers, regardless of given clear flags
clearDepthFlags = TargetBufferFlags::DEPTH;
data.depth = builder.createTexture("Depth Buffer", {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
// If the color attachment requested MS, we assume this means the MS buffer
// must be kept, and for that reason we allocate the depth buffer with MS
// as well. On the other hand, if the color attachment was allocated without
// MS, no need to allocate the depth buffer with MS, if the RT is MS,
// the tile depth buffer will be MS, but it'll be resolved to single
// sample automatically -- which is what we want.
.samples = canResolveDepth ? colorBufferDesc.samples : uint8_t(config.msaa),
.format = TextureFormat::DEPTH32F,
});
}
if (colorGradingConfig.asSubpass) {
data.output = builder.createTexture("Tonemapped Buffer", {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
.format = colorGradingConfig.ldrFormat
});
data.color = builder.read(data.color, FrameGraphTexture::Usage::SUBPASS_INPUT);
data.output = builder.write(data.output, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
} else if (colorGradingConfig.customResolve) {
data.color = builder.read(data.color, FrameGraphTexture::Usage::SUBPASS_INPUT);
}
// We set a "read" constraint on these attachments here because we need to preserve them
// when the color pass happens in several passes (e.g. with SSR)
data.color = builder.read(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.read(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
/*
* There is a bit of magic happening here regarding the viewport used.
* We do not specify the viewport in declareRenderPass() below, so it will be
* deduced automatically to be { 0, 0, w, h }, with w,h the min width/height of
* all the attachments. This has the side effect of moving the viewport to the
* origin and ignore the left/bottom of 'svp'. The attachment sizes are set from
* svp's width/height, however.
* But that's not all! When we're rendering directly into the swap-chain (by way
* of calling forwardResource() later), the effective viewport comes from the
* imported resource (i.e. the swap-chain) and is set to 'vp' which has its
* left/bottom honored -- the view is therefore rendered directly where it should
* be (the imported resource viewport is set to 'vp', see how 'fgViewRenderTarget'
* is initialized in this file).
*/
builder.declareRenderPass("Color Pass Target", {
.attachments = { .color = { data.color, data.output }, .depth = data.depth },
.samples = config.msaa,
.clearFlags = clearColorFlags | clearDepthFlags });
data.clearColor = config.clearColor;
data.clearFlags = clearColorFlags | clearDepthFlags;
blackboard["depth"] = data.depth;
},
[=, &view, &engine](FrameGraphResources const& resources,
ColorPassData const& data, DriverApi& driver) {
auto out = resources.getRenderPassInfo();
// set samplers and uniforms
view.prepareSSAO(data.ssao ?
resources.getTexture(data.ssao) : engine.getOneTextureArray());
view.prepareShadowMap();
// set shadow sampler
view.prepareShadow(data.shadows ?
resources.getTexture(data.shadows) : engine.getOneTextureArray());
// set structure sampler
view.prepareStructure(data.structure ?
resources.getTexture(data.structure) : engine.getOneTexture());
// set screen-space reflections and screen-space refractions
TextureHandle ssr = data.ssr ?
resources.getTexture(data.ssr) : engine.getOneTextureArray();
view.prepareSSR(ssr, config.ssrLodOffset,
view.getScreenSpaceReflectionsOptions());
// Note: here we can't use data.color's descriptor for the viewport because
// the actual viewport might be offset when the target is the swapchain.
// However, the width/height should be the same.
assert_invariant(
out.params.viewport.width == resources.getDescriptor(data.color).width);
assert_invariant(
out.params.viewport.height == resources.getDescriptor(data.color).height);
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport),
config.xoffset, config.yoffset);
view.commitUniforms(driver);
out.params.clearColor = data.clearColor;
out.params.flags.clear = data.clearFlags;
if (view.getBlendMode() == BlendMode::TRANSLUCENT) {
if (any(out.params.flags.discardStart & TargetBufferFlags::COLOR0)) {
// if the buffer is discarded (e.g. it's new) and we're blending,
// then clear it to transparent
out.params.flags.clear |= TargetBufferFlags::COLOR;
out.params.clearColor = {};
}
}
if (colorGradingConfig.asSubpass || colorGradingConfig.customResolve) {
out.params.subpassMask = 1;
}
passExecutor.execute(resources.getPassName(), out.target, out.params);
// color pass is typically heavy, and we don't have much CPU work left after
// this point, so flushing now allows us to start the GPU earlier and reduce
// latency, without creating bubbles.
driver.flush();
}
);
// when color grading is done as a subpass, the output of the color-pass is the ldr buffer
auto output = colorGradingConfig.asSubpass ? colorPass->output : colorPass->color;
blackboard["color"] = output;
return output;
}
FrameGraphId<FrameGraphTexture> RendererUtils::refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass) noexcept {
auto& blackboard = fg.getBlackboard();
auto input = blackboard.get<FrameGraphTexture>("color");
FrameGraphId<FrameGraphTexture> output;
// find the first refractive object
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
[](auto const& command) {
return (command.key & RenderPass::PASS_MASK) < uint64_t(RenderPass::Pass::REFRACT);
});
const bool hasScreenSpaceRefraction =
(refraction->key & RenderPass::PASS_MASK) == uint64_t(RenderPass::Pass::REFRACT);
// if there wasn't any refractive object, just skip everything below.
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
PostProcessManager& ppm = engine.getPostProcessManager();
// clear the color/depth buffers, which will orphan (and cull) the color pass
input.clear();
blackboard.remove("color");
blackboard.remove("depth");
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
// When rendering the opaques, we need to conserve the sample buffer,
// so create config that specifies the sample count.
.width = config.width,
.height = config.height,
.samples = config.msaa,
.format = config.hdrFormat
}, config, { .asSubpass = false },
pass.getExecutor(pass.begin(), refraction));
// generate the mipmap chain
PostProcessManager::generateMipmapSSR(ppm, fg,
input, ssrConfig.refraction, true, ssrConfig);
// Now we're doing the refraction pass proper.
// This uses the same framebuffer (color and depth) used by the opaque pass. This happens
// automatically because these are set in the Blackboard (they were set by the opaque
// pass). For this reason, `desc` below is only used in colorPass() for the width and
// height.
config.clearFlags = TargetBufferFlags::NONE;
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view,
{ .width = config.width, .height = config.height },
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
// We need to do a resolve here because later passes (such as color grading or DoF) will need
// to sample from 'output'. However, because we have MSAA, we know we're not sampleable.
// And this is because in the SSR case, we had to use a renderbuffer to conserve the
// multi-sample buffer.
output = ppm.resolveBaseLevel(fg, "Resolved Color Buffer", output);
}
} else {
output = input;
}
return output;
}
UTILS_NOINLINE
void RendererUtils::readPixels(backend::DriverApi& driver, Handle<HwRenderTarget> renderTargetHandle,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer) {
if (!ASSERT_POSTCONDITION_NON_FATAL(
buffer.type != PixelDataType::COMPRESSED,
"buffer.format cannot be COMPRESSED")) {
return;
}
if (!ASSERT_POSTCONDITION_NON_FATAL(
buffer.alignment > 0 && buffer.alignment <= 8 &&
!(buffer.alignment & (buffer.alignment - 1u)),
"buffer.alignment must be 1, 2, 4 or 8")) {
return;
}
// It's not really possible to know here which formats will be supported because
// it can vary depending on the RenderTarget, in GL the following are ALWAYS supported though:
// format: RGBA, RGBA_INTEGER
// type: UBYTE, UINT, INT, FLOAT
const size_t sizeNeeded = PixelBufferDescriptor::computeDataSize(
buffer.format, buffer.type,
buffer.stride ? buffer.stride : width,
buffer.top + height,
buffer.alignment);
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.size >= sizeNeeded,
"Pixel buffer too small: has %u bytes, needs %u bytes", buffer.size, sizeNeeded)) {
return;
}
driver.readPixels(renderTargetHandle, xoffset, yoffset, width, height, std::move(buffer));
}
} // namespace filament
+89
View File
@@ -0,0 +1,89 @@
/*
* 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_DETAILS_RENDERERUTILS_H
#define TNT_FILAMENT_DETAILS_RENDERERUTILS_H
#include "PostProcessManager.h"
#include "RenderPass.h"
#include "fg/FrameGraphId.h"
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <math/vec4.h>
#include <stdint.h>
namespace filament {
class FRenderTarget;
class FrameGraph;
class FrameGraph;
class FView;
class RendererUtils {
public:
struct ColorPassConfig {
// Rendering viewport width (e.g. scaled down viewport from dynamic resolution)
uint32_t width;
// Rendering viewport height (e.g. scaled down viewport from dynamic resolution)
uint32_t height;
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
uint32_t xoffset;
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
uint32_t yoffset;
// dynamic resolution scale
math::float2 scale;
// HDR format
backend::TextureFormat hdrFormat;
// MSAA sample count
uint8_t msaa;
// Clear flags
backend::TargetBufferFlags clearFlags;
// Clear color
math::float4 clearColor = {};
// Lod offset for the SSR passes
float ssrLodOffset;
// Contact shadow enabled?
bool hasContactShadows;
};
static FrameGraphId<FrameGraphTexture> colorPass(
FrameGraph& fg, const char* name, FEngine& engine, FView const& view,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor const& passExecutor) noexcept;
static FrameGraphId<FrameGraphTexture> refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass) noexcept;
static void readPixels(backend::DriverApi& driver,
backend::Handle<backend::HwRenderTarget> renderTargetHandle,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
};
} // namespace filament
#endif // TNT_FILAMENT_DETAILS_RENDERERUTILS_H
+4 -1
View File
@@ -60,6 +60,9 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept :
}
}
void ShadowMap::terminate(FEngine& engine) {
}
ShadowMap::~ShadowMap() {
FEngine& engine = mEngine;
Entity entities[] = { mCamera->getEntity(), mDebugCamera->getEntity() };
@@ -318,7 +321,7 @@ void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t inde
if (params.options.stable) {
// Use the world origin as reference point, fixed w.r.t. the camera
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
snapLightFrustum(s, o, Mv, -camera.getWorldOffset(),
1.0f / mShadowMapInfo.shadowDimension);
}
+2
View File
@@ -43,6 +43,8 @@ public:
~ShadowMap();
void terminate(FEngine& engine);
struct ShadowMapInfo {
// the dimension of the encompassing texture atlas
uint16_t atlasDimension = 0;
+38 -24
View File
@@ -40,6 +40,10 @@ ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguideline
for (auto& entry : mShadowMapCache) {
new (&entry) ShadowMap(engine);
}
mShadowUbh = engine.getDriverApi().createBufferObject(mShadowUb.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
@@ -47,16 +51,25 @@ ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguideline
&engine.debug.shadowmap.tightly_bound_scene);
}
void ShadowMapManager::terminate(FEngine& engine) {
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mShadowUbh);
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
std::launder(reinterpret_cast<ShadowMap*>(&entry))->terminate(engine);
}
}
ShadowMapManager::~ShadowMapManager() {
// destroy the ShadowMap array in-place
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
std::launder(reinterpret_cast<ShadowMap*>(&entry))->~ShadowMap();
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
}
}
ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo, TypedUniformBuffer<ShadowUib>& shadowUb,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept {
ShadowTechnique shadowTechnique = {};
@@ -71,7 +84,13 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FVie
engine, view, cameraInfo, renderableData, lightData, sceneInfo);
shadowTechnique |= updateSpotShadowMaps(
engine, view, cameraInfo, renderableData, lightData, sceneInfo, shadowUb);
engine, view, cameraInfo, renderableData, lightData, sceneInfo);
if (mShadowUb.isDirty()) {
DriverApi& driver = engine.getDriverApi();
driver.updateBufferObject(mShadowUbh,
mShadowUb.toBufferDescriptor(driver), 0);
}
return shadowTechnique;
}
@@ -166,8 +185,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
.format = view.hasVSM() ? vsmTextureFormat : mTextureFormat
});
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { });
[&view](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
// set uniforms needed to render this ShadowMap
view.prepareShadowMap();
});
// -------------------------------------------------------------------------------------------
@@ -177,12 +198,11 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
struct ShadowPassData {
FrameGraphId<FrameGraphTexture> tempBlurSrc{}; // temporary shadowmap when blurring
FrameGraphId<FrameGraphTexture> output;
uint32_t blurRt{};
uint32_t shadowRt{};
};
auto shadows = prepareShadowPass.getData().shadows;
auto& ppm = engine.getPostProcessManager();
for (auto const& entry : passList) {
@@ -195,7 +215,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
FrameGraphRenderPass::Descriptor renderTargetDesc{};
auto attachment = builder.createSubresource(prepareShadowPass->shadows,
data.output = builder.createSubresource(prepareShadowPass->shadows,
"Shadowmap Layer", { .layer = layer });
if (view.hasVSM()) {
@@ -220,10 +240,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
depth = builder.write(depth,
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
attachment = builder.write(attachment,
data.output = builder.write(data.output,
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
renderTargetDesc.attachments.color[0] = attachment;
renderTargetDesc.attachments.color[0] = data.output;
renderTargetDesc.attachments.depth = depth;
renderTargetDesc.clearFlags =
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
@@ -247,9 +267,9 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
}
} else {
// the shadowmap layer
attachment = builder.write(attachment,
data.output = builder.write(data.output,
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
renderTargetDesc.attachments.depth = attachment;
renderTargetDesc.attachments.depth = data.output;
renderTargetDesc.clearFlags = TargetBufferFlags::DEPTH;
}
@@ -286,12 +306,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
// than clearing.
const uint32_t dim = options->mapSize;
filament::Viewport viewport{ 1, 1, dim - 2, dim - 2 };
view.prepareViewport(viewport);
// set uniforms needed to render this ShadowMap
// Currently these uniforms are owned by View and are global, but eventually
// this will set a separate per shadowmap UBO
view.prepareShadowMap();
view.prepareViewport(viewport, 0, 0);
view.commitUniforms(driver);
@@ -314,7 +329,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
const float ratio = float(kernelWidth + 1) / sigma;
ppm.gaussianBlurPass(fg,
shadowPass->tempBlurSrc,
shadows,
shadowPass->output,
false, kernelWidth, ratio);
}
@@ -324,14 +339,14 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg,
if (textureRequirements.levels > 1) {
for (size_t level = 0; level < textureRequirements.levels - 1; level++) {
const bool finalize = level == textureRequirements.levels - 2;
shadows = ppm.vsmMipmapPass(fg, shadows, layer, level,
ppm.vsmMipmapPass(fg, prepareShadowPass->shadows, layer, level,
vsmClearColor, finalize);
}
}
}
}
return shadows;
return prepareShadowPass->shadows;
}
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine,
@@ -476,8 +491,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo,
TypedUniformBuffer<ShadowUib>& shadowUb) noexcept {
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept {
auto& lcm = engine.getLightManager();
@@ -537,7 +551,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
auto& s = shadowUb.edit();
auto& s = mShadowUb.edit();
const float n = shadowMap.getCamera().getNear();
const float f = shadowMap.getCamera().getCullingFar();
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
+11 -3
View File
@@ -74,6 +74,8 @@ public:
explicit ShadowMapManager(FEngine& engine);
~ShadowMapManager();
void terminate(FEngine& engine);
// Reset shadow map layout.
void reset() noexcept;
@@ -83,7 +85,7 @@ public:
// Updates all of the shadow maps and performs culling.
// Returns true if any of the shadow maps have visible shadows.
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo, TypedUniformBuffer<ShadowUib>& shadowUb,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
// Renders all the shadow maps.
@@ -114,6 +116,10 @@ public:
return mShadowMappingUniforms;
}
auto& getShadowUniforms() const { return mShadowUb; }
auto& getShadowUniformsHandle() const { return mShadowUbh; }
private:
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
@@ -121,8 +127,7 @@ private:
ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,
FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo,
TypedUniformBuffer<ShadowUib>& shadowUb) noexcept;
FScene::LightSoa& lightData, ShadowMap::SceneInfo& sceneInfo) noexcept;
void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept;
@@ -207,6 +212,9 @@ private:
// TODO: iOS does not support the DEPTH16 texture format.
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
backend::Handle<backend::HwBufferObject> mShadowUbh;
ShadowMappingUniforms mShadowMappingUniforms;
utils::FixedCapacityVector<ShadowMapEntry> mCascadeShadowMaps{
+7 -8
View File
@@ -156,8 +156,8 @@ math::mat4 FCamera::getProjectionMatrix() const noexcept {
// Note that this math ends up setting the projection matrix' p33 to 0, which is where we're
// getting back a lot of precision in the depth buffer.
const mat4 m{ mat4::row_major_init{
mScaling.x, 0.0, 0.0, mShiftCS.x,
0.0, mScaling.y, 0.0, mShiftCS.y,
mScalingCS.x, 0.0, 0.0, mShiftCS.x,
0.0, mScalingCS.y, 0.0, mShiftCS.y,
0.0, 0.0, -0.5, 0.5, // GL to inverted DX convention
0.0, 0.0, 0.0, 1.0
}};
@@ -167,8 +167,8 @@ math::mat4 FCamera::getProjectionMatrix() const noexcept {
math::mat4 FCamera::getCullingProjectionMatrix() const noexcept {
// The culling projection matrix stays in the GL convention
const mat4 m{ mat4::row_major_init{
mScaling.x, 0.0, 0.0, mShiftCS.x,
0.0, mScaling.y, 0.0, mShiftCS.y,
mScalingCS.x, 0.0, 0.0, mShiftCS.x,
0.0, mScalingCS.y, 0.0, mShiftCS.y,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
}};
@@ -275,7 +275,7 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
zn = camera.getNear();
zf = camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = camera.getFocalLength();
f = (float)camera.getFocalLength();
A = f / camera.getAperture();
d = std::max(zn, camera.getFocusDistance());
}
@@ -286,14 +286,13 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamer
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
model = mat4f{ modelMatrix };
view = mat4f{ inverse(modelMatrix) };
worldOrigin = worldOriginCamera;
zn = camera.getNear();
zf = camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = camera.getFocalLength();
f = (float)camera.getFocalLength();
A = f / camera.getAperture();
d = std::max(zn, camera.getFocusDistance());
worldOffset = camera.getPosition();
worldOrigin = worldOriginCamera;
}
} // namespace filament
+8 -8
View File
@@ -63,9 +63,9 @@ public:
void setCustomProjection(math::mat4 const& projection,
math::mat4 const& projectionForCulling, double near, double far) noexcept;
void setScaling(math::double2 scaling) noexcept { mScaling = scaling; }
void setScaling(math::double2 scaling) noexcept { mScalingCS = scaling; }
math::double4 getScaling() const noexcept { return math::double4{ mScaling, 1.0, 1.0 }; }
math::double4 getScaling() const noexcept { return math::double4{ mScalingCS, 1.0, 1.0 }; }
void setShift(math::double2 shift) noexcept { mShiftCS = shift * 2.0; }
@@ -189,8 +189,8 @@ private:
math::mat4 mProjection; // projection matrix (infinite far)
math::mat4 mProjectionForCulling; // projection matrix (with far plane)
math::double2 mScaling = { 1.0f }; // additional scaling applied to projection
math::double2 mShiftCS = { 0.0f }; // additional translation applied to projection
math::double2 mScalingCS = { 1.0 }; // additional scaling applied to projection
math::double2 mShiftCS = { 0.0 }; // additional translation applied to projection
float mNear{};
float mFar{};
@@ -209,18 +209,18 @@ struct CameraInfo {
math::mat4f projection; // projection matrix for drawing (infinite zfar)
math::mat4f cullingProjection; // projection matrix for culling
math::mat4f model; // camera model matrix
math::mat4f view; // camera view matrix
math::mat4f view; // camera view matrix (inverse(model))
math::mat4 worldOrigin; // world origin transform (already applied to model and view)
float zn{}; // distance (positive) to the near plane
float zf{}; // distance (positive) to the far plane
float ev100{}; // exposure
float f{}; // focal length [m]
float A{}; // f-number or f / aperture diameter [m]
float d{}; // focus distance [m]
math::float3 worldOffset{}; // world offset, API-level camera position
math::mat4 worldOrigin; // this is already applied to model and view
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); }
math::float3 getWorldOffset() const noexcept { return -worldOrigin[3].xyz; }
math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; }
};
FILAMENT_UPCAST(Camera)
-1
View File
@@ -695,7 +695,6 @@ FRenderer* FEngine::createRenderer() noexcept {
FRenderer* p = mHeapAllocator.make<FRenderer>(*this);
if (p) {
mRenderers.insert(p);
p->init();
}
return p;
}
+18 -3
View File
@@ -139,17 +139,32 @@ FRenderTarget::FRenderTarget(FEngine& engine, const RenderTarget::Builder& build
}
};
UTILS_NOUNROLL
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (mAttachments[i].texture) {
mAttachmentMask |= getTargetBufferFlagsAt(i);
Attachment const& attachment = mAttachments[i];
if (attachment.texture) {
TargetBufferFlags targetBufferBit = getTargetBufferFlagsAt(i);
mAttachmentMask |= targetBufferBit;
setAttachment(mrt[i], (AttachmentPoint)i);
if (any(attachment.texture->getUsage() &
(TextureUsage::SAMPLEABLE | Texture::Usage::SUBPASS_INPUT))) {
mSampleableAttachmentsMask |= targetBufferBit;
}
}
}
if (mAttachments[(size_t)AttachmentPoint::DEPTH].texture) {
Attachment const& depthAttachment = mAttachments[(size_t)AttachmentPoint::DEPTH];
if (depthAttachment.texture) {
mAttachmentMask |= TargetBufferFlags::DEPTH;
setAttachment(dinfo, AttachmentPoint::DEPTH);
if (any(depthAttachment.texture->getUsage() &
(TextureUsage::SAMPLEABLE | Texture::Usage::SUBPASS_INPUT))) {
mSampleableAttachmentsMask |= TargetBufferFlags::DEPTH;
}
}
// TODO: add stencil here when we support it
FEngine::DriverApi& driver = engine.getDriverApi();
mHandle = driver.createRenderTarget(mAttachmentMask,
builder.mImpl->mWidth, builder.mImpl->mHeight, builder.mImpl->mSamples, mrt, dinfo, {});
+5
View File
@@ -55,6 +55,10 @@ public:
return mAttachmentMask;
}
backend::TargetBufferFlags getSampleableAttachmentsMask() const noexcept {
return mSampleableAttachmentsMask;
}
uint8_t getSupportedColorAttachmentsCount() const noexcept {
return mSupportedColorAttachmentsCount;
}
@@ -67,6 +71,7 @@ private:
Attachment mAttachments[ATTACHMENT_COUNT];
HwHandle mHandle{};
backend::TargetBufferFlags mAttachmentMask = {};
backend::TargetBufferFlags mSampleableAttachmentsMask = {};
const uint8_t mSupportedColorAttachmentsCount;
};
File diff suppressed because it is too large Load Diff
+34 -77
View File
@@ -33,17 +33,13 @@
#include <fg/FrameGraphTexture.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PresentCallable.h>
#include <utils/compiler.h>
#include <utils/Allocator.h>
#include <utils/JobSystem.h>
#include <utils/Slice.h>
#include <tsl/robin_set.h>
@@ -53,12 +49,9 @@ namespace backend {
class Driver;
} // namespace backend
class View;
class FEngine;
class FRenderTarget;
class FView;
class ShadowMap;
/*
* A concrete implementation of the Renderer Interface.
@@ -68,39 +61,42 @@ class FRenderer : public Renderer {
public:
explicit FRenderer(FEngine& engine);
~FRenderer() noexcept;
void init() noexcept;
void terminate(FEngine& engine);
FEngine& getEngine() const noexcept { return mEngine; }
math::float4 getShaderUserTime() const { return mShaderUserTime; }
// do all the work here!
void renderJob(ArenaScope& arena, FView& view);
void resetUserTime();
// renders a single standalone view. The view must have a a custom rendertarget.
void renderStandaloneView(FView const* view);
// start a frame
bool beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeNano);
// end a frame
void endFrame();
// render a view. must be called between beginFrame/enfFrame.
void render(FView const* view);
// read pixel from the current swapchain. must be called between beginFrame/enfFrame.
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
void copyFrame(FSwapChain* dstSwapChain, Viewport const& dstViewport,
Viewport const& srcViewport, CopyFrameFlag flags);
void endFrame();
void renderStandaloneView(FView const* view);
// read pixel from a rendertarget. must be called between beginFrame/enfFrame.
void readPixels(FRenderTarget* renderTarget,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
void resetUserTime();
// blits the current swapchain to another one
void copyFrame(FSwapChain* dstSwapChain, Viewport const& dstViewport,
Viewport const& srcViewport, CopyFrameFlag flags);
// Clean-up everything, this is typically called when the client calls Engine::destroyRenderer()
void terminate(FEngine& engine);
void setDisplayInfo(DisplayInfo const& info) noexcept {
mDisplayInfo = info;
@@ -131,84 +127,45 @@ public:
private:
friend class Renderer;
using Command = RenderPass::Command;
using clock = std::chrono::steady_clock;
using Epoch = clock::time_point;
using duration = clock::duration;
void initializeClearFlags();
backend::TextureFormat getHdrFormat(const FView& view, bool translucent) const noexcept;
backend::TextureFormat getLdrFormat(bool translucent) const noexcept;
Epoch getUserEpoch() const { return mUserEpoch; }
duration getUserTime() const noexcept { return clock::now() - getUserEpoch(); }
void getRenderTarget(FView const& view,
backend::TargetBufferFlags& outAttachementMask,
backend::Handle<backend::HwRenderTarget>& outTarget) const noexcept;
void readPixels(backend::Handle<backend::HwRenderTarget> renderTargetHandle,
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer);
void renderInternal(FView const* view);
struct ColorPassConfig {
// User Viewport
Viewport vp;
// Scaled (down) viewport from dynamic resolution
Viewport svp;
// dynamic resolution scale
math::float2 scale;
// HDR format
backend::TextureFormat hdrFormat;
// MSAA sample count
uint8_t msaa;
// Clear flags
backend::TargetBufferFlags clearFlags;
// Clear color
math::float4 clearColor = {};
// Lod offset for the SSR passes
float ssrLodOffset;
// Contact shadow enabled?
bool hasContactShadows;
};
FrameGraphId<FrameGraphTexture> colorPass(FrameGraph& fg, const char* name,
FrameGraphTexture::Descriptor const& colorBufferDesc,
ColorPassConfig const& config,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor const& passExecutor, FView const& view) const noexcept;
FrameGraphId<FrameGraphTexture> refractionPass(FrameGraph& fg,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass const& pass, FView const& view) const noexcept;
void recordHighWatermark(size_t watermark) noexcept {
mCommandsHighWatermark = std::max(mCommandsHighWatermark, watermark);
}
size_t getCommandsHighWatermark() const noexcept {
return mCommandsHighWatermark * sizeof(RenderPass::Command);
return mCommandsHighWatermark;
}
backend::TextureFormat getHdrFormat(const View& view, bool translucent) const noexcept;
backend::TextureFormat getLdrFormat(bool translucent) const noexcept;
void initializeClearFlags();
using clock = std::chrono::steady_clock;
using Epoch = clock::time_point;
using duration = clock::duration;
Epoch getUserEpoch() const { return mUserEpoch; }
duration getUserTime() const noexcept {
return clock::now() - getUserEpoch();
}
void renderInternal(FView const* view);
void renderJob(ArenaScope& arena, FView& view);
// keep a reference to our engine
FEngine& mEngine;
FrameSkipper mFrameSkipper;
backend::Handle<backend::HwRenderTarget> mRenderTarget;
backend::Handle<backend::HwRenderTarget> mRenderTargetHandle;
FSwapChain* mSwapChain = nullptr;
size_t mCommandsHighWatermark = 0;
uint32_t mFrameId = 0;
uint32_t mViewRenderedCount = 0;
FrameInfoManager mFrameInfoManager;
backend::TextureFormat mHdrTranslucent{};
backend::TextureFormat mHdrQualityMedium{};
backend::TextureFormat mHdrQualityHigh{};
backend::TextureFormat mHdrTranslucent;
backend::TextureFormat mHdrQualityMedium;
backend::TextureFormat mHdrQualityHigh;
bool mIsRGB8Supported : 1;
Epoch mUserEpoch;
math::float4 mShaderUserTime{};
+39 -38
View File
@@ -92,9 +92,6 @@ FView::FView(FEngine& engine)
mLightUbh = driver.createBufferObject(CONFIG_MAX_LIGHT_COUNT * sizeof(LightsUib),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
mShadowUbh = driver.createBufferObject(mShadowUb.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
mIsDynamicResolutionSupported = driver.isFrameTimeSupported();
mDefaultColorGrading = mColorGrading = engine.getDefaultColorGrading();
@@ -114,9 +111,9 @@ void FView::terminate(FEngine& engine) {
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mLightUbh);
driver.destroyBufferObject(mShadowUbh);
driver.destroyBufferObject(mRenderableUbh);
drainFrameHistory(engine);
mShadowMapManager.terminate(engine);
mPerViewUniforms.terminate(driver);
mFroxelizer.terminate(driver);
}
@@ -160,10 +157,6 @@ float2 FView::updateScale(FEngine& engine,
Renderer::FrameRateOptions const& frameRateOptions,
Renderer::DisplayInfo const& displayInfo) noexcept {
// scale factor returned to the caller is modified so the scaled viewport is rounded to
// 8 pixels. The internal scale factor, mScale, doesn't have this rounding.
float2 roundedScale = mScale;
DynamicResolutionOptions const& options = mDynamicResolution;
if (options.enabled) {
if (!UTILS_UNLIKELY(info.valid)) {
@@ -241,14 +234,8 @@ float2 FView::updateScale(FEngine& engine,
// (i.e. we clamped). This help not to have to wait too long for the Integral term
// to kick in after a clamping event.
mPidController.setIntegralInhibitionEnabled(mScale != s);
// now tweak the scaling factor to get multiples of 8 (to help quad-shading)
// i.e. 8x8=64 fragments, to try to help with warp sizes.
roundedScale.x = mScale.x == 1.0f ? 1.0f : (std::floor(mScale.x * float{ w } / 8) * 8) / float{ w };
roundedScale.y = mScale.y == 1.0f ? 1.0f : (std::floor(mScale.y * float{ h } / 8) * 8) / float{ h };
} else {
mScale = 1.0f;
roundedScale = 1.0f;
}
#ifndef NDEBUG
@@ -270,7 +257,7 @@ float2 FView::updateScale(FEngine& engine,
};
#endif
return roundedScale;
return mScale;
}
void FView::setVisibleLayers(uint8_t select, uint8_t values) noexcept {
@@ -340,7 +327,7 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
}
auto shadowTechnique = mShadowMapManager.update(engine, *this, cameraInfo,
mShadowUb, renderableData, lightData);
renderableData, lightData);
mHasShadowing = any(shadowTechnique);
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
@@ -407,8 +394,8 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
mHasDirectionalLight = directionalLight.isValid();
}
CameraInfo FView::computeCameraInfo(FEngine& engine) noexcept {
FScene* const scene = getScene();
CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept {
FScene const* const scene = getScene();
/*
* We apply a "world origin" to "everything" in order to implement the IBL rotation.
@@ -451,11 +438,25 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* and in particular their world-space AABB.
*/
FScene* const scene = getScene();
auto getFrustum = [this, &cameraInfo]() -> Frustum {
if (UTILS_LIKELY(mViewingCamera == nullptr)) {
// In the common case when we don't have a viewing camera, cameraInfo.view is
// already the culling view matrix
return Frustum{ mat4f{ highPrecisionMultiply(cameraInfo.projection, cameraInfo.view) }};
} else {
// Otherwise, we need to recalculate it from the culling camera.
// Note: it is correct to always do the math from mCullingCamera, but it hides the
// intent of the code, which is that we should only depend on CameraInfo here.
// This is an extremely uncommon case.
const mat4 projection = mCullingCamera->getCullingProjectionMatrix();
const mat4 view = inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix());
return Frustum{ mat4f{ projection * view }};
}
};
mCullingFrustum = Frustum(mat4f{
mCullingCamera->getCullingProjectionMatrix() *
inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix()) });
const Frustum cullingFrustum = getFrustum();
FScene* const scene = getScene();
/*
* Gather all information needed to render this scene. Apply the world origin to all
@@ -470,9 +471,9 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
JobSystem::Job* prepareVisibleLightsJob = nullptr;
if (scene->getLightData().size() > FScene::DIRECTIONAL_LIGHTS_COUNT) {
prepareVisibleLightsJob = js.runAndRetain(js.createJob(nullptr,
[this, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) {
[&cullingFrustum, &engine, &arena, &cameraInfo, scene](JobSystem&, JobSystem::Job*) {
FView::prepareVisibleLights(engine.getLightManager(), arena,
cameraInfo.view, mCullingFrustum, scene->getLightData());
cameraInfo.view, cullingFrustum, scene->getLightData());
}));
}
@@ -489,7 +490,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* (this will set the VISIBLE_RENDERABLE bit)
*/
prepareVisibleRenderables(js, mCullingFrustum, renderableData);
prepareVisibleRenderables(js, cullingFrustum, renderableData);
/*
@@ -648,14 +649,17 @@ void FView::prepareUpscaler(float2 scale) const noexcept {
mPerViewUniforms.prepareUpscaler(scale, mDynamicResolution);
}
void FView::prepareCamera(const CameraInfo& camera) const noexcept {
void FView::prepareCamera(const CameraInfo& cameraInfo) const noexcept {
SYSTRACE_CALL();
mPerViewUniforms.prepareCamera(camera);
mPerViewUniforms.prepareCamera(cameraInfo);
}
void FView::prepareViewport(const filament::Viewport& viewport) const noexcept {
void FView::prepareViewport(const filament::Viewport& viewport,
uint32_t xoffset, uint32_t yoffset) const noexcept {
SYSTRACE_CALL();
mPerViewUniforms.prepareViewport(viewport);
// TODO: we should pass viewport.{left|bottom} to the backend so it can offset the
// scissor properly.
mPerViewUniforms.prepareViewport(viewport, xoffset, yoffset);
}
void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
@@ -680,25 +684,25 @@ void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
}
void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
const auto& uniforms = mShadowMapManager.getShadowMappingUniforms();
switch (mShadowType) {
case filament::ShadowType::PCF:
mPerViewUniforms.prepareShadowPCF(texture);
mPerViewUniforms.prepareShadowPCF(texture, uniforms);
break;
case filament::ShadowType::VSM:
mPerViewUniforms.prepareShadowVSM(texture, mVsmShadowOptions);
mPerViewUniforms.prepareShadowVSM(texture, uniforms, mVsmShadowOptions);
break;
case filament::ShadowType::DPCF:
mPerViewUniforms.prepareShadowDPCF(texture, mSoftShadowOptions);
mPerViewUniforms.prepareShadowDPCF(texture, uniforms, mSoftShadowOptions);
break;
case filament::ShadowType::PCSS:
mPerViewUniforms.prepareShadowPCSS(texture, mSoftShadowOptions);
mPerViewUniforms.prepareShadowPCSS(texture, uniforms, mSoftShadowOptions);
break;
}
}
void FView::prepareShadowMap() const noexcept {
mPerViewUniforms.prepareShadowMapping(
mShadowMapManager.getShadowMappingUniforms(), mVsmShadowOptions);
mPerViewUniforms.prepareShadowMapping();
}
void FView::cleanupRenderPasses() const noexcept {
@@ -713,9 +717,6 @@ void FView::froxelize(FEngine& engine, mat4f const& viewMatrix) const noexcept {
void FView::commitUniforms(DriverApi& driver) const noexcept {
mPerViewUniforms.commit(driver);
if (mShadowUb.isDirty()) {
driver.updateBufferObject(mShadowUbh, mShadowUb.toBufferDescriptor(driver), 0);
}
}
void FView::commitFroxels(DriverApi& driverApi) const noexcept {
+7 -11
View File
@@ -119,7 +119,7 @@ public:
void terminate(FEngine& engine);
CameraInfo computeCameraInfo(FEngine& engine) noexcept;
CameraInfo computeCameraInfo(FEngine& engine) const noexcept;
void prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& arena,
filament::Viewport const& viewport, CameraInfo const& cameraInfo,
@@ -161,12 +161,12 @@ public:
// returns the view's name. The pointer is owned by View.
const char* getName() const noexcept {
return mName.c_str();
return mName.c_str_safe();
}
void prepareUpscaler(math::float2 scale) const noexcept;
void prepareCamera(const CameraInfo& camera) const noexcept;
void prepareViewport(const Viewport& viewport) const noexcept;
void prepareCamera(const CameraInfo& cameraInfo) const noexcept;
void prepareViewport(const Viewport& viewport, uint32_t xoffset, uint32_t yoffset) const noexcept;
void prepareShadowing(FEngine& engine, backend::DriverApi& driver,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
CameraInfo const& cameraInfo) noexcept;
@@ -410,8 +410,6 @@ public:
static void cullRenderables(utils::JobSystem& js, FScene::RenderableSoa& renderableData,
Frustum const& frustum, size_t bit) noexcept;
auto& getShadowUniforms() const { return mShadowUb; }
PerViewUniforms const& getPerViewUniforms() const noexcept { return mPerViewUniforms; }
PerViewUniforms& getPerViewUniforms() noexcept { return mPerViewUniforms; }
@@ -479,7 +477,7 @@ private:
void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept {
mPerViewUniforms.bind(driver);
driver.bindUniformBuffer(BindingPoints::LIGHTS, mLightUbh);
driver.bindUniformBuffer(BindingPoints::SHADOW, mShadowUbh);
driver.bindUniformBuffer(BindingPoints::SHADOW, mShadowMapManager.getShadowUniformsHandle());
driver.bindUniformBuffer(BindingPoints::FROXEL_RECORDS, mFroxelizer.getRecordBuffer());
}
@@ -496,15 +494,14 @@ private:
// these are accessed in the render loop, keep together
backend::Handle<backend::HwBufferObject> mLightUbh;
backend::Handle<backend::HwBufferObject> mShadowUbh;
backend::Handle<backend::HwBufferObject> mRenderableUbh;
FScene* mScene = nullptr;
// The camera set by the user, used for culling and viewing
FCamera* mCullingCamera = nullptr;
// The optional (debug) camera, used only for viewing
FCamera* mViewingCamera = nullptr;
Frustum mCullingFrustum{};
mutable Froxelizer mFroxelizer;
Viewport mViewport;
@@ -542,7 +539,6 @@ private:
RenderQuality mRenderQuality;
mutable PerViewUniforms mPerViewUniforms;
mutable TypedUniformBuffer<ShadowUib> mShadowUb;
mutable FrameHistory mFrameHistory{};
+8 -3
View File
@@ -39,14 +39,19 @@ using namespace math;
#pragma clang diagnostic pop
void FSR_ScalingSetup(FSRUniforms* outUniforms, FSRScalingConfig config) noexcept {
FsrEasuCon( outUniforms->EasuCon0.v, outUniforms->EasuCon1.v,
FsrEasuConOffset( outUniforms->EasuCon0.v, outUniforms->EasuCon1.v,
outUniforms->EasuCon2.v, outUniforms->EasuCon3.v,
// Viewport size (top left aligned) in the input image which is to be scaled.
config.viewportWidth, config.viewportHeight,
config.input.width, config.input.height,
// The size of the input image.
config.inputWidth, config.inputHeight,
// The output resolution.
config.outputWidth, config.outputHeight);
config.outputWidth, config.outputHeight,
// Input image offset
config.input.left, config.input.bottom);
// FIXME: FsrEasu api claims it needs the top,left offset, but when using that we get
// some bad artifacts. To investigate. For reference:
// top = config.inputHeight - (config.input.bottom + config.input.height)
}
void FSR_SharpeningSetup(FSRUniforms* outUniforms, FSRSharpeningConfig config) noexcept {
+7 -6
View File
@@ -17,6 +17,8 @@
#ifndef TNT_FILAMENT_FSR_H
#define TNT_FILAMENT_FSR_H
#include <filament/Viewport.h>
#include <math/vec4.h>
#include <stdint.h>
@@ -24,12 +26,11 @@
namespace filament {
struct FSRScalingConfig {
uint32_t viewportWidth;
uint32_t viewportHeight;
uint32_t inputWidth;
uint32_t inputHeight;
uint32_t outputWidth;
uint32_t outputHeight;
filament::Viewport input; // region of source to be scaled
uint32_t inputWidth; // width of source
uint32_t inputHeight; // height of source
uint32_t outputWidth; // width of destination
uint32_t outputHeight; // height of destination
};
struct FSRSharpeningConfig {
+20 -26
View File
@@ -5,6 +5,16 @@ material {
type : sampler2d,
name : colorBuffer,
precision : medium
},
{
type : float4,
name : viewport,
precision : high
},
{
type : float2,
name : texelSize,
precision : high
}
],
depthWrite : false,
@@ -18,7 +28,7 @@ material {
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = postProcess.normalizedUV;
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
}
}
@@ -28,40 +38,24 @@ fragment {
#include "fxaa.fs"
void postProcess(inout PostProcessInputs postProcess) {
// First, compute an exact upper bound for the area we need to sample from.
// The render target may be larger than the viewport that was used for scene
// rendering, so we cannot rely on the wrap mode alone.
highp vec2 fboSize = vec2(textureSize(materialParams_colorBuffer, 0));
highp vec2 invSize = 1.0 / fboSize;
highp vec2 halfTexel = 0.5 * invSize;
highp vec2 viewportSize = frameUniforms.resolution.xy;
// The clamp needs to be over-aggressive by a half-texel due to bilinear sampling.
highp vec2 excessSize = 0.5 + fboSize - viewportSize;
highp vec2 upperBound = 1.0 - excessSize * invSize;
// Next, compute the coordinates of the texel center and its bounding box.
// There is no need to clamp the min corner since the wrap mode will do
// it automatically.
// variable_vertex is already interpolated to pixel center by the GPU
highp vec2 texelCenter = min(variable_vertex.xy, upperBound);
highp vec2 texelMaxCorner = min(variable_vertex.xy + halfTexel, upperBound);
highp vec2 texelMinCorner = variable_vertex.xy - halfTexel;
highp vec2 texelCenter = variable_vertex.xy;
highp vec2 texelMaxCorner = variable_vertex.xy + materialParams.texelSize * 0.5;
highp vec2 texelMinCorner = variable_vertex.xy - materialParams.texelSize * 0.5;
vec4 color = fxaa(
texelCenter,
vec4(texelMinCorner, texelMaxCorner),
materialParams_colorBuffer,
invSize, // FxaaFloat4 fxaaConsoleRcpFrameOpt,
2.0 * invSize, // FxaaFloat4 fxaaConsoleRcpFrameOpt2,
8.0, // FxaaFloat fxaaConsoleEdgeSharpness,
materialParams.texelSize, // FxaaFloat4 fxaaConsoleRcpFrameOpt,
2.0 * materialParams.texelSize, // FxaaFloat4 fxaaConsoleRcpFrameOpt2,
8.0, // FxaaFloat fxaaConsoleEdgeSharpness,
#if defined(G3D_FXAA_PATCHES) && G3D_FXAA_PATCHES == 1
0.08, // FxaaFloat fxaaConsoleEdgeThreshold,
0.08, // FxaaFloat fxaaConsoleEdgeThreshold,
#else
0.125, // FxaaFloat fxaaConsoleEdgeThreshold,
0.125, // FxaaFloat fxaaConsoleEdgeThreshold,
#endif
0.04 // FxaaFloat fxaaConsoleEdgeThresholdMin
0.04 // FxaaFloat fxaaConsoleEdgeThresholdMin
);
#if POST_PROCESS_OPAQUE
color.a = 1.0;
+6 -1
View File
@@ -10,6 +10,11 @@ material {
type : float4,
name : resolution,
precision: high
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
@@ -22,7 +27,7 @@ material {
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = postProcess.normalizedUV;
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
}
}
@@ -59,6 +59,11 @@ material {
{
type : float4,
name : vignetteColor
},
{
type : float4,
name : viewport,
precision : high
}
],
variables : [
@@ -71,7 +76,7 @@ material {
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = postProcess.normalizedUV;
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
}
}
@@ -98,9 +103,7 @@ float starburst(const vec2 uv) {
return saturate(mask + (1.0 - smoothstep(0.0, 0.3, d)));
}
vec3 bloom(const vec3 color) {
highp vec2 uv = variable_vertex.xy;
vec3 bloom(highp vec2 uv, const vec3 color) {
vec3 result = vec3(0.0);
if (materialParams.bloom.x > 0.0) {
@@ -124,27 +127,28 @@ vec3 bloom(const vec3 color) {
return result;
}
vec4 resolveFragment(const ivec2 uv) {
vec4 resolveFragment(const highp vec2 uv) {
#if POST_PROCESS_OPAQUE
return vec4(texelFetch(materialParams_colorBuffer, uv, 0).rgb, 1.0);
return vec4(textureLod(materialParams_colorBuffer, uv, 0.0).rgb, 1.0);
#else
vec4 color = texelFetch(materialParams_colorBuffer, uv, 0);
vec4 color = textureLod(materialParams_colorBuffer, uv, 0.0);
color.rgb *= 1.0 / (color.a + FLT_EPS);
return color;
#endif
}
void postProcess(inout PostProcessInputs postProcess) {
vec4 color = resolveFragment(ivec2(getUV()));
highp vec2 uv = variable_vertex.xy;
vec4 color = resolveFragment(uv);
// Bloom
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
color.rgb = bloom(uv, color.rgb);
}
// Vignette
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
@@ -86,11 +86,12 @@ vec4 resolveFragment() {
}
void postProcess(inout PostProcessInputs postProcess) {
highp vec2 uv = variable_vertex.xy;
vec4 color = resolveFragment();
// Vignette
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
+2 -2
View File
@@ -599,8 +599,8 @@ void postProcess(inout PostProcessInputs postProcess) {
#if KERNEL_USE_NOISE
// A good random generator is essential, this one is okay.
highp vec2 fragCoords = gl_FragCoord.xy;
float randomAngle = random(fragCoords) * (2.0 * PI);
float random01 = random(fragCoords * vec2(5099.0, 3499.0)); // large primes seem to work ok
float randomAngle = interleavedGradientNoise(fragCoords) * (2.0 * PI);
float random01 = interleavedGradientNoise(fragCoords * vec2(5099.0, 3499.0)); // large primes seem to work ok
noiseState.randomUniformDiskRadius = 0.5 * sqrt(random01);
noiseState.randomDisk = vec2(cos(randomAngle), sin(randomAngle));
#endif
-5
View File
@@ -17,11 +17,6 @@
// Currently our dilate pass is set-up for 32 max.
#define MAX_COC_RADIUS 32.0
float random(const highp vec2 w) {
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
return fract(m.z * fract(dot(w, m.xy)));
}
float min2(const vec2 v) {
return min(v.x, v.y);
}
+5
View File
@@ -35,6 +35,11 @@ material {
type : float4,
name : EasuCon3,
precision: high
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
@@ -35,6 +35,11 @@ material {
type : float4,
name : EasuCon3,
precision: high
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
@@ -35,6 +35,11 @@ material {
type : float4,
name : EasuCon3,
precision: high
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
@@ -87,7 +87,7 @@ fragment {
// simple dithering helps a lot (assumes 8 bits target)
// this is most useful with high quality/large blurs
ao += ((random(gl_FragCoord.xy) - 0.5) / 255.0);
ao += ((interleavedGradientNoise(gl_FragCoord.xy) - 0.5) / 255.0);
postProcess.color.r = ao;
postProcess.color.gb = data.gb;
@@ -127,7 +127,7 @@ fragment {
// simple dithering helps a lot (assumes 8 bits target)
// this is most useful with high quality/large blurs
ao += ((random(gl_FragCoord.xy) - 0.5) / 255.0);
ao += ((interleavedGradientNoise(gl_FragCoord.xy) - 0.5) / 255.0);
postProcess.aoData = vec3(ao, data.gb);
postProcess.bnData = packBentNormal(bn);
+1 -1
View File
@@ -107,7 +107,7 @@ void computeAmbientOcclusionSAO(inout float occlusion, inout vec3 bentNormal,
void scalableAmbientObscurance(out float obscurance, out vec3 bentNormal,
highp vec2 uv, highp vec3 origin, vec3 normal) {
float noise = random(getFragCoord(materialParams.resolution.xy));
float noise = interleavedGradientNoise(getFragCoord(materialParams.resolution.xy));
highp vec2 tapPosition = startPosition(noise);
highp mat2 angleStep = tapAngleStep();
-16
View File
@@ -19,22 +19,6 @@
#include "depthUtils.fs"
// random number between 0 and 1, using interleaved gradient noise
float random(const highp vec2 w) {
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
return fract(m.z * fract(dot(w, m.xy)));
}
// returns the frag coord in the GL convention with (0, 0) at the bottom-left
// resolution : width, height
highp vec2 getFragCoord(const highp vec2 resolution) {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
return vec2(gl_FragCoord.x, resolution.y - gl_FragCoord.y);
#else
return gl_FragCoord.xy;
#endif
}
vec2 pack(highp float normalizedDepth) {
// we need 16-bits of precision
highp float z = clamp(normalizedDepth, 0.0, 1.0);
+2 -2
View File
@@ -143,8 +143,8 @@ float ssctDominantLightShadowing(highp vec2 uv, highp vec3 origin, vec3 normal,
float occlusion = 0.0;
for (float i = 1.0; i <= rayCount.x; i += 1.0) {
cone.jitterOffset.x = random(fragCoord * i) * 2.0 - 1.0; // direction
cone.jitterOffset.y = random(fragCoord * i * vec2(3, 11)); // step
cone.jitterOffset.x = interleavedGradientNoise(fragCoord * i) * 2.0 - 1.0; // direction
cone.jitterOffset.y = interleavedGradientNoise(fragCoord * i * vec2(3, 11)); // step
occlusion += coneTraceOcclusion(cone, depthTexture);
}
return occlusion * rayCount.y;
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.21.2"
spec.version = "1.21.3"
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.21.2/filament-v1.21.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.21.3/filament-v1.21.3-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -424,6 +424,7 @@
"-limage",
"-lgeometry",
"-lstb",
"-lbasis_transcoder",
"-lcamutils",
"-ldracodec",
"-lviewer",
@@ -475,6 +476,7 @@
"-limage",
"-lgeometry",
"-lstb",
"-lbasis_transcoder",
"-lcamutils",
"-ldracodec",
"-lviewer",
@@ -588,6 +590,7 @@
"-limage",
"-lgeometry",
"-lstb",
"-lbasis_transcoder",
"-lcamutils",
"-ldracodec",
"-lviewer",
@@ -639,6 +642,7 @@
"-limage",
"-lgeometry",
"-lstb",
"-lbasis_transcoder",
"-lcamutils",
"-ldracodec",
"-lviewer",
@@ -79,7 +79,8 @@ const float kSensitivity = 100.0f;
ResourceLoader* _resourceLoader;
Manipulator<float>* _manipulator;
TextureProvider* _decoder;
TextureProvider* _stbDecoder;
TextureProvider* _ktxDecoder;
FilamentAsset* _asset;
@@ -131,9 +132,11 @@ const float kSensitivity = 100.0f;
_assetLoader = AssetLoader::create({_engine, _materialProvider, ncm, &em});
_resourceLoader = new ResourceLoader(
{.engine = _engine, .normalizeSkinningWeights = true, .recomputeBoundingBoxes = false});
_decoder = createStbProvider(_engine);
_resourceLoader->addTextureProvider("image/png", _decoder);
_resourceLoader->addTextureProvider("image/jpeg", _decoder);
_stbDecoder = createStbProvider(_engine);
_ktxDecoder = createKtx2Provider(_engine);
_resourceLoader->addTextureProvider("image/png", _stbDecoder);
_resourceLoader->addTextureProvider("image/jpeg", _stbDecoder);
_resourceLoader->addTextureProvider("image/ktx2", _ktxDecoder);
_manipulator =
Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 4.0f).build(Mode::ORBIT);
@@ -252,7 +255,8 @@ const float kSensitivity = 100.0f;
[self destroyModel];
delete _manipulator;
delete _decoder;
delete _stbDecoder;
delete _ktxDecoder;
_materialProvider->destroyMaterials();
delete _materialProvider;
+1 -1
View File
@@ -19,7 +19,7 @@ targets:
- FilamentApp
settings:
base:
OTHER_LDFLAGS: ["-lgltfio_core", "-lgltfio_resources", "-limage", "-lgeometry", "-lstb",
OTHER_LDFLAGS: ["-lgltfio_core", "-lgltfio_resources", "-limage", "-lgeometry", "-lstb", "-lbasis_transcoder",
"-lcamutils", "-ldracodec", "-lviewer", "-lcivetweb", "-lktxreader"]
preBuildScripts:
- path: build-resources.sh
@@ -34,6 +34,10 @@ using namespace ktxreader;
using namespace filament;
using namespace filamesh;
const double kFov = 60.0;
const double kNearPlane = 0.1;
const double kFarPlane = 10.0;
void FilamentApp::initialize() {
#if FILAMENT_APP_USE_OPENGL
engine = Engine::create(filament::Engine::Backend::OPENGL);
@@ -94,15 +98,9 @@ void FilamentApp::initialize() {
filaView->setScene(scene);
filaView->setCamera(camera);
filaView->setViewport(Viewport(0, 0, width, height));
const uint32_t w = filaView->getViewport().width;
const uint32_t h = filaView->getViewport().height;
const float aspect = (float) w / h;
cameraManipulator.setCamera(camera);
cameraManipulator.setViewport(w, h);
cameraManipulator.lookAt(filament::math::double3(0, 0, 3), filament::math::double3(0, 0, 0));
camera->setProjection(60, aspect, 0.1, 10);
}
void FilamentApp::render() {
@@ -116,6 +114,22 @@ void FilamentApp::pan(float deltaX, float deltaY) {
cameraManipulator.rotate(filament::math::double2(deltaX, -deltaY), 10);
}
void FilamentApp::updateViewportAndCameraProjection(uint32_t width, uint32_t height,
float scaleFactor) {
if (!filaView || !camera) {
return;
}
cameraManipulator.setViewport(width, height);
const uint32_t scaledWidth = width * scaleFactor;
const uint32_t scaledHeight = height * scaleFactor;
filaView->setViewport({0, 0, scaledWidth, scaledHeight});
const double aspect = (double)width / height;
camera->setProjection(kFov, aspect, kNearPlane, kFarPlane);
}
FilamentApp::~FilamentApp() {
engine->destroy(app.materialInstance);
engine->destroy(app.mat);
@@ -38,20 +38,20 @@ using utils::EntityManager;
class FilamentApp {
public:
FilamentApp(void* nativeLayer, uint32_t width, uint32_t height)
: nativeLayer(nativeLayer), width(width), height(height) {}
FilamentApp(void* nativeLayer)
: nativeLayer(nativeLayer) {}
~FilamentApp();
void initialize();
void render();
void pan(float deltaX, float deltaY);
void updateViewportAndCameraProjection(uint32_t width, uint32_t height, float scaleFactor);
private:
void updateRotation();
void* nativeLayer = nullptr;
uint32_t width, height;
Engine* engine = nullptr;
Renderer* renderer = nullptr;
@@ -39,10 +39,11 @@
#elif FILAMENT_APP_USE_METAL
[self initializeMetalLayer];
#endif
CGRect nativeBounds = [[UIScreen mainScreen] nativeBounds];
app = new FilamentApp((__bridge void*) self.layer, nativeBounds.size.width, nativeBounds.size.height);
app->initialize();
app = new FilamentApp((__bridge void*) self.layer);
self.contentScaleFactor = UIScreen.mainScreen.nativeScale;
app->initialize();
app->updateViewportAndCameraProjection(self.bounds.size.width, self.bounds.size.height,
self.contentScaleFactor);
}
// Call renderloop 60 times a second.
@@ -114,4 +115,32 @@
app->pan(delta.x, delta.y);
}
- (void)updateViewportAndCameraProjection {
if (!app) {
return;
}
#if FILAMENT_APP_USE_METAL
CAMetalLayer* metalLayer = (CAMetalLayer*)self.layer;
const uint32_t width = self.bounds.size.width * self.contentScaleFactor;
const uint32_t height = self.bounds.size.height * self.contentScaleFactor;
metalLayer.drawableSize = CGSizeMake(width, height);
#endif
app->updateViewportAndCameraProjection(self.bounds.size.width, self.bounds.size.height,
self.contentScaleFactor);
}
#pragma mark UIView methods
- (void)layoutSubviews {
[super layoutSubviews];
[self updateViewportAndCameraProjection];
}
- (void)setContentScaleFactor:(CGFloat)contentScaleFactor {
[super setContentScaleFactor:contentScaleFactor];
[self updateViewportAndCameraProjection];
}
@end
@@ -22,9 +22,4 @@
@implementation ViewController
- (BOOL)shouldAutorotate
{
return NO;
}
@end
@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 21;
static constexpr size_t MATERIAL_VERSION = 22;
/**
* Supported shading models
@@ -42,7 +42,7 @@ namespace BindingPoints {
constexpr uint8_t FROXEL_RECORDS = 6;
constexpr uint8_t PER_MATERIAL_INSTANCE = 7; // uniforms/samplers updates per material
constexpr uint8_t COUNT = 8;
// These are limited by Program::UNIFORM_BINDING_COUNT (currently 8)
// These are limited by CONFIG_BINDING_COUNT (currently 12)
}
static_assert(BindingPoints::COUNT <= backend::CONFIG_BINDING_COUNT);
@@ -40,69 +40,108 @@ namespace filament {
struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
static constexpr utils::StaticString _name{ "FrameUniforms" };
// --------------------------------------------------------------------------------------------
// Values that can be accessed in both surface and post-process materials
// --------------------------------------------------------------------------------------------
math::mat4f viewFromWorldMatrix;
math::mat4f worldFromViewMatrix;
math::mat4f clipFromViewMatrix;
math::mat4f viewFromClipMatrix;
math::mat4f clipFromWorldMatrix;
math::mat4f worldFromClipMatrix;
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
// position of cascade splits, in world space (not including the near plane)
// -Inf stored in unused components
math::float4 cascadeSplits;
math::float2 clipControl; // clip control
float time; // time in seconds, with a 1 second period
float temporalNoise; // noise [0,1] when TAA is used, 0 otherwise
math::float4 userTime; // time(s), (double)time - (float)time, 0, 0
math::float4 resolution; // viewport width, height, 1/width, 1/height
// --------------------------------------------------------------------------------------------
// values below should only be accessed in surface materials
// (i.e.: not in the post-processing materials)
// --------------------------------------------------------------------------------------------
math::float2 origin; // viewport left, bottom (in pixels)
math::float2 offset; // rendering offset left, bottom (in pixels)
math::float4 resolution; // viewport width, height, 1/width, 1/height
float lodBias; // load bias to apply to user materials
float refractionLodOffset;
float padding1;
float padding2;
// camera position in view space (when camera_at_origin is enabled), i.e. it's (0,0,0).
// Always add worldOffset in the shader to get the true world-space position of the camera.
math::float3 cameraPosition;
float time; // time in seconds, with a 1 second period
math::float4 lightColorIntensity; // directional light
math::float4 sun; // cos(sunAngle), sin(sunAngle), 1/(sunAngle*HALO_SIZE-sunAngle), HALO_EXP
math::float2 lightFarAttenuationParams; // a, a/far (a=1/pct-of-far)
float needsAlphaChannel;
uint32_t lightChannels;
math::float3 lightDirection;
uint32_t fParamsX; // stride-x
float shadowBulbRadiusLs; // light radius in light-space
float shadowBias; // normal bias
float shadowPenumbraRatioScale; // For DPCF or PCSS, scale penumbra ratio for artistic use
float oneOverFroxelDimensionY;
math::float4 zParams; // froxel Z parameters
math::uint2 fParams; // stride-y, stride-z
math::float2 origin; // viewport left, viewport bottom
float oneOverFroxelDimensionX;
float iblLuminance;
float oneOverFarMinusNear; // 1 / (f-n), always positive
math::float3 worldOffset; // this is (0,0,0) when camera_at_origin is disabled
float nearOverFarMinusNear; // n / (f-n), always positive
float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf)
float exposure;
float ev100;
float needsAlphaChannel;
alignas(16) math::float4 iblSH[9]; // actually float3 entries (std140 requires float4 alignment)
// AO
float aoSamplingQualityAndEdgeDistance; // 0: bilinear, !0: bilateral edge distance
float aoBentNormals; // 0: no AO bent normal, >0.0 AO bent normals
float aoReserved0;
float aoReserved1;
math::float4 userTime; // time(s), (double)time - (float)time, 0, 0
// --------------------------------------------------------------------------------------------
// Dynamic Lighting [variant: DYN]
// --------------------------------------------------------------------------------------------
math::float4 zParams; // froxel Z parameters
math::uint3 fParams; // stride-x, stride-y, stride-z
uint32_t lightChannels; // light channel bits
math::float2 froxelCountXY;
float iblRoughnessOneLevel; // level for roughness == 1
float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf)
float refractionLodOffset;
// IBL
float iblLuminance;
float iblRoughnessOneLevel; // level for roughness == 1
math::float4 iblSH[9]; // actually float3 entries (std140 requires float4 alignment)
// --------------------------------------------------------------------------------------------
// Directional Lighting [variant: DIR]
// --------------------------------------------------------------------------------------------
math::float3 lightDirection; // directional light direction
float padding0;
math::float4 lightColorIntensity; // directional light
math::float4 sun; // cos(sunAngle), sin(sunAngle), 1/(sunAngle*HALO_SIZE-sunAngle), HALO_EXP
math::float2 lightFarAttenuationParams; // a, a/far (a=1/pct-of-far)
// --------------------------------------------------------------------------------------------
// Directional light shadowing [variant: SRE | DIR]
// --------------------------------------------------------------------------------------------
// bit 0: directional (sun) shadow enabled
// bit 1: directional (sun) screen-space contact shadow enabled
// bit 8-15: screen-space contact shadows ray casting steps
uint32_t directionalShadows;
math::float3 worldOffset; // this is (0,0,0) when camera_at_origin is disabled
float ssContactShadowDistance;
// fog
// position of cascade splits, in world space (not including the near plane)
// -Inf stored in unused components
math::float4 cascadeSplits;
// bit 0-3: cascade count
// bit 4: visualize cascades
// bit 8-11: cascade has visible shadows
uint32_t cascades;
float shadowBulbRadiusLs; // light radius in light-space
float shadowBias; // normal bias
float shadowPenumbraRatioScale; // For DPCF or PCSS, scale penumbra ratio for artistic use
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
// --------------------------------------------------------------------------------------------
// VSM shadows [variant: VSM]
// --------------------------------------------------------------------------------------------
float vsmExponent;
float vsmDepthScale;
float vsmLightBleedReduction;
uint32_t shadowSamplingType; // 0: vsm, 1: dpcf
// --------------------------------------------------------------------------------------------
// Fog [variant: FOG]
// --------------------------------------------------------------------------------------------
float fogStart;
float fogMaxOpacity;
float fogHeight;
@@ -112,31 +151,11 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float fogInscatteringStart;
float fogInscatteringSize;
float fogColorFromIbl;
float fogReserved0;
// bit 0-3: cascade count
// bit 4: visualize cascades
// bit 8-11: cascade has visible shadows
uint32_t cascades;
float aoSamplingQualityAndEdgeDistance; // 0: bilinear, !0: bilateral edge distance
float aoBentNormals; // 0: no AO bent normal, >0.0 AO bent normals
float aoReserved2;
float aoReserved3;
math::float2 clipControl;
math::float2 padding1;
float vsmExponent;
float vsmDepthScale;
float vsmLightBleedReduction;
uint32_t shadowSamplingType; // 0: vsm, 1: dpcf
float lodBias;
float oneOverFarMinusNear; // 1 / (f-n), always positive
float nearOverFarMinusNear; // n / (f-n), always positive
float temporalNoise; // noise [0,1] when TAA is used, 0 otherwise
// Screen-space reflections
// --------------------------------------------------------------------------------------------
// Screen-space reflections [variant: SSR (i.e.: VSM | SRE)]
// --------------------------------------------------------------------------------------------
math::mat4f ssrReprojection;
math::mat4f ssrUvFromViewMatrix;
float ssrThickness; // ssr thickness, in world units
@@ -145,7 +164,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float ssrStride; // ssr texel stride, >= 1.0
// bring PerViewUib to 2 KiB
math::float4 padding3[49];
math::float4 reserved[48];
};
// 2 KiB == 128 float4s
+77 -62
View File
@@ -40,51 +40,85 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
.add("viewFromClipMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("clipFromWorldMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("worldFromClipMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("lightFromWorldMatrix", 4, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("cascadeSplits", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
// view
.add("resolution", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
// camera
.add("cameraPosition", 1, UniformInterfaceBlock::Type::FLOAT3, Precision::HIGH)
// time
.add("clipControl", 1, UniformInterfaceBlock::Type::FLOAT2)
.add("time", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
// directional light
.add("temporalNoise", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("userTime", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
// ------------------------------------------------------------------------------------
// values below should only be accessed in surface materials
// ------------------------------------------------------------------------------------
.add("origin", 1, UniformInterfaceBlock::Type::FLOAT2, Precision::HIGH)
.add("offset", 1, UniformInterfaceBlock::Type::FLOAT2, Precision::HIGH)
.add("resolution", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
.add("lodBias", 1, UniformInterfaceBlock::Type::FLOAT)
.add("refractionLodOffset", 1, UniformInterfaceBlock::Type::FLOAT)
.add("padding1", 1, UniformInterfaceBlock::Type::FLOAT)
.add("padding2", 1, UniformInterfaceBlock::Type::FLOAT)
.add("cameraPosition", 1, UniformInterfaceBlock::Type::FLOAT3, Precision::HIGH)
.add("oneOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("worldOffset", 1, UniformInterfaceBlock::Type::FLOAT3)
.add("nearOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("cameraFar", 1, UniformInterfaceBlock::Type::FLOAT)
.add("exposure", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH) // high precision to work around #3602 (qualcomm)
.add("ev100", 1, UniformInterfaceBlock::Type::FLOAT)
.add("needsAlphaChannel", 1, UniformInterfaceBlock::Type::FLOAT)
// AO
.add("aoSamplingQualityAndEdgeDistance", 1, UniformInterfaceBlock::Type::FLOAT)
.add("aoBentNormals", 1, UniformInterfaceBlock::Type::FLOAT)
.add("aoReserved0", 1, UniformInterfaceBlock::Type::FLOAT)
.add("aoReserved1", 1, UniformInterfaceBlock::Type::FLOAT)
// ------------------------------------------------------------------------------------
// Dynamic Lighting [variant: DYN]
// ------------------------------------------------------------------------------------
.add("zParams", 1, UniformInterfaceBlock::Type::FLOAT4)
.add("fParams", 1, UniformInterfaceBlock::Type::UINT3)
.add("lightChannels", 1, UniformInterfaceBlock::Type::UINT)
.add("froxelCountXY", 1, UniformInterfaceBlock::Type::FLOAT2)
.add("iblLuminance", 1, UniformInterfaceBlock::Type::FLOAT)
.add("iblRoughnessOneLevel", 1, UniformInterfaceBlock::Type::FLOAT)
.add("iblSH", 9, UniformInterfaceBlock::Type::FLOAT3)
// ------------------------------------------------------------------------------------
// Directional Lighting [variant: DIR]
// ------------------------------------------------------------------------------------
.add("lightDirection", 1, UniformInterfaceBlock::Type::FLOAT3)
.add("padding0", 1, UniformInterfaceBlock::Type::FLOAT)
.add("lightColorIntensity", 1, UniformInterfaceBlock::Type::FLOAT4)
.add("sun", 1, UniformInterfaceBlock::Type::FLOAT4)
.add("lightFarAttenuationParams",1, UniformInterfaceBlock::Type::FLOAT2)
.add("needsAlphaChannel", 1, UniformInterfaceBlock::Type::FLOAT)
.add("lightChannels", 1, UniformInterfaceBlock::Type::UINT)
.add("lightDirection", 1, UniformInterfaceBlock::Type::FLOAT3)
.add("fParamsX", 1, UniformInterfaceBlock::Type::UINT)
// shadow
// ------------------------------------------------------------------------------------
// Directional light shadowing [variant: SRE | DIR]
// ------------------------------------------------------------------------------------
.add("directionalShadows", 1, UniformInterfaceBlock::Type::UINT)
.add("ssContactShadowDistance", 1, UniformInterfaceBlock::Type::FLOAT)
.add("cascadeSplits", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
.add("cascades", 1, UniformInterfaceBlock::Type::UINT)
.add("shadowBulbRadiusLs", 1, UniformInterfaceBlock::Type::FLOAT)
.add("shadowBias", 1, UniformInterfaceBlock::Type::FLOAT)
.add("shadowPenumbraRatioScale",1, UniformInterfaceBlock::Type::FLOAT)
.add("oneOverFroxelDimensionY", 1, UniformInterfaceBlock::Type::FLOAT)
// froxels
.add("zParams", 1, UniformInterfaceBlock::Type::FLOAT4)
.add("fParams", 1, UniformInterfaceBlock::Type::UINT2)
.add("origin", 1, UniformInterfaceBlock::Type::FLOAT2)
// froxels (again, for alignment purposes)
.add("oneOverFroxelDimension", 1, UniformInterfaceBlock::Type::FLOAT)
// ibl
.add("iblLuminance", 1, UniformInterfaceBlock::Type::FLOAT)
// camera
.add("exposure", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH) // high precision to work around #3602 (qualcomm)
.add("ev100", 1, UniformInterfaceBlock::Type::FLOAT)
// ibl
.add("iblSH", 9, UniformInterfaceBlock::Type::FLOAT3)
// user time
.add("userTime", 1, UniformInterfaceBlock::Type::FLOAT4)
// ibl max mip level
.add("iblRoughnessOneLevel", 1, UniformInterfaceBlock::Type::FLOAT)
.add("cameraFar", 1, UniformInterfaceBlock::Type::FLOAT)
.add("refractionLodOffset", 1, UniformInterfaceBlock::Type::FLOAT)
.add("directionalShadows", 1, UniformInterfaceBlock::Type::UINT)
// view
.add("worldOffset", 1, UniformInterfaceBlock::Type::FLOAT3)
.add("ssContactShadowDistance", 1, UniformInterfaceBlock::Type::FLOAT)
// fog
.add("lightFromWorldMatrix", 4, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
// ------------------------------------------------------------------------------------
// VSM shadows [variant: VSM]
// ------------------------------------------------------------------------------------
.add("vsmExponent", 1, UniformInterfaceBlock::Type::FLOAT)
.add("vsmDepthScale", 1, UniformInterfaceBlock::Type::FLOAT)
.add("vsmLightBleedReduction", 1, UniformInterfaceBlock::Type::FLOAT)
.add("shadowSamplingType", 1, UniformInterfaceBlock::Type::UINT)
// ------------------------------------------------------------------------------------
// Fog [variant: FOG]
// ------------------------------------------------------------------------------------
.add("fogStart", 1, UniformInterfaceBlock::Type::FLOAT)
.add("fogMaxOpacity", 1, UniformInterfaceBlock::Type::FLOAT)
.add("fogHeight", 1, UniformInterfaceBlock::Type::FLOAT)
@@ -94,30 +128,11 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
.add("fogInscatteringStart", 1, UniformInterfaceBlock::Type::FLOAT)
.add("fogInscatteringSize", 1, UniformInterfaceBlock::Type::FLOAT)
.add("fogColorFromIbl", 1, UniformInterfaceBlock::Type::FLOAT)
.add("fogReserved0", 1, UniformInterfaceBlock::Type::FLOAT)
// CSM information
.add("cascades", 1, UniformInterfaceBlock::Type::UINT)
// SSAO sampling parameters
.add("aoSamplingQualityAndEdgeDistance", 1, UniformInterfaceBlock::Type::FLOAT)
.add("aoBentNormals", 1, UniformInterfaceBlock::Type::FLOAT)
.add("aoReserved2", 1, UniformInterfaceBlock::Type::FLOAT)
.add("aoReserved3", 1, UniformInterfaceBlock::Type::FLOAT)
.add("clipControl", 1, UniformInterfaceBlock::Type::FLOAT2)
.add("padding1", 1, UniformInterfaceBlock::Type::FLOAT2)
.add("vsmExponent", 1, UniformInterfaceBlock::Type::FLOAT)
.add("vsmDepthScale", 1, UniformInterfaceBlock::Type::FLOAT)
.add("vsmLightBleedReduction", 1, UniformInterfaceBlock::Type::FLOAT)
.add("shadowSamplingType", 1, UniformInterfaceBlock::Type::UINT)
.add("lodBias", 1, UniformInterfaceBlock::Type::FLOAT)
.add("oneOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("nearOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("temporalNoise", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
// Screen-space reflection parameters
// ------------------------------------------------------------------------------------
// Screen-space reflections [variant: SSR (i.e.: VSM | SRE)]
// ------------------------------------------------------------------------------------
.add("ssrReprojection", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("ssrUvFromViewMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
.add("ssrThickness", 1, UniformInterfaceBlock::Type::FLOAT)
@@ -126,7 +141,7 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
.add("ssrStride", 1, UniformInterfaceBlock::Type::FLOAT)
// bring PerViewUib to 2 KiB
.add("padding3", 49, UniformInterfaceBlock::Type::FLOAT4)
.add("reserved", sizeof(PerViewUib::reserved)/16, UniformInterfaceBlock::Type::FLOAT4)
.build();
return uib;
}
+16 -6
View File
@@ -391,8 +391,7 @@ io::sstream& CodeGenerator::generateSamplers(
return out;
}
io::sstream& CodeGenerator::generateSubpass(io::sstream& out,
SubpassInfo subpass) {
io::sstream& CodeGenerator::generateSubpass(io::sstream& out, SubpassInfo subpass) {
if (!subpass.isValid) {
return out;
}
@@ -522,8 +521,8 @@ io::sstream& CodeGenerator::generateQualityDefine(io::sstream& out, ShaderQualit
}
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) {
out << SHADERS_COMMON_MATH_FS_DATA;
out << SHADERS_COMMON_SHADOWING_FS_DATA;
out << SHADERS_COMMON_MATH_GLSL_DATA;
out << SHADERS_COMMON_SHADOWING_GLSL_DATA;
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {
out << SHADERS_COMMON_SHADING_FS_DATA;
@@ -533,6 +532,16 @@ io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) {
return out;
}
io::sstream& CodeGenerator::generatePostProcessCommon(io::sstream& out, ShaderType type) {
out << SHADERS_COMMON_MATH_GLSL_DATA;
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {
out << SHADERS_COMMON_SHADING_FS_DATA;
out << SHADERS_COMMON_GRAPHICS_FS_DATA;
}
return out;
}
io::sstream& CodeGenerator::generateFog(io::sstream& out, ShaderType type) {
if (type == ShaderType::VERTEX) {
} else if (type == ShaderType::FRAGMENT) {
@@ -561,15 +570,16 @@ io::sstream& CodeGenerator::generatePostProcessInputs(io::sstream& out, ShaderTy
io::sstream& CodeGenerator::generatePostProcessGetters(io::sstream& out,
ShaderType type) {
out << SHADERS_COMMON_GETTERS_FS_DATA;
out << SHADERS_COMMON_GETTERS_GLSL_DATA;
if (type == ShaderType::VERTEX) {
out << SHADERS_POST_PROCESS_GETTERS_VS_DATA;
} else if (type == ShaderType::FRAGMENT) {
}
return out;
}
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderType type) {
out << SHADERS_COMMON_GETTERS_FS_DATA;
out << SHADERS_COMMON_GETTERS_GLSL_DATA;
if (type == ShaderType::VERTEX) {
out << SHADERS_GETTERS_VS_DATA;
} else if (type == ShaderType::FRAGMENT) {
+1
View File
@@ -65,6 +65,7 @@ public:
// generate common functions for the given shader
static utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generatePostProcessCommon(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderType type) ;
static utils::io::sstream& generateFog(utils::io::sstream& out, ShaderType type) ;
+2 -8
View File
@@ -537,9 +537,6 @@ std::string ShaderGenerator::createPostProcessVertexProgram(
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
// The UVs are at the location immediately following the custom variables.
CodeGenerator::generateDefine(vs, "LOCATION_UVS", uint32_t(MaterialBuilder::MATERIAL_VARIABLES_COUNT));
// custom material variables
size_t variableIndex = 0;
for (const auto& variable : mVariables) {
@@ -558,7 +555,7 @@ std::string ShaderGenerator::createPostProcessVertexProgram(
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
material.sib);
CodeGenerator::generateCommon(vs, ShaderType::VERTEX);
CodeGenerator::generatePostProcessCommon(vs, ShaderType::VERTEX);
CodeGenerator::generatePostProcessGetters(vs, ShaderType::VERTEX);
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
@@ -579,9 +576,6 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(
cg.generateQualityDefine(fs, material.quality);
// The UVs are at the location immediately following the custom variables.
CodeGenerator::generateDefine(fs, "LOCATION_UVS", uint32_t(MaterialBuilder::MATERIAL_VARIABLES_COUNT));
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
// custom material variables
@@ -602,7 +596,7 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(
// subpass
CodeGenerator::generateSubpass(fs, material.subpass);
CodeGenerator::generateCommon(fs, ShaderType::FRAGMENT);
CodeGenerator::generatePostProcessCommon(fs, ShaderType::FRAGMENT);
CodeGenerator::generatePostProcessGetters(fs, ShaderType::FRAGMENT);
// Generate post-process outputs.
+2 -1
View File
@@ -30,6 +30,7 @@ set(SRCS
src/FFilamentInstance.h
src/FilamentInstance.cpp
src/GltfEnums.h
src/Ktx2Provider.cpp
src/MaterialProvider.cpp
src/ResourceLoader.cpp
src/StbProvider.cpp
@@ -165,7 +166,7 @@ add_library(gltfio_resources_lite ${DUMMY_SRC} ${RESGEN_SOURCE})
# ==================================================================================================
include_directories(${PUBLIC_HDR_DIR} ${RESOURCE_DIR})
link_libraries(math utils filament cgltf stb geometry gltfio_resources tsl trie)
link_libraries(math utils filament cgltf stb ktxreader geometry gltfio_resources tsl trie)
add_library(gltfio_core STATIC ${PUBLIC_HDRS} ${SRCS})
@@ -169,6 +169,12 @@ public:
*/
TextureProvider* createStbProvider(filament::Engine* engine);
/**
* Creates a decoder that can handle certain types of "image/ktx2" content as specified in
* the KHR_texture_basisu specification.
*/
TextureProvider* createKtx2Provider(filament::Engine* engine);
} // namespace gltfio
#endif // GLTFIO_TEXTUREPROVIDER_H
+5 -8
View File
@@ -1305,13 +1305,10 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
void FAssetLoader::addTextureBinding(MaterialInstance* materialInstance, const char* parameterName,
const cgltf_texture* srcTexture, bool srgb) {
if (!srcTexture->image) {
if (srcTexture->basisu_image) {
const char* name = srcTexture->name ? srcTexture->name : srcTexture->basisu_image->uri;
slog.w << "BasisU is not yet supported (" << name << ")." << io::endl;
} else {
slog.w << "Texture is missing image (" << srcTexture->name << ")." << io::endl;
}
if (!srcTexture->image && !srcTexture->basisu_image) {
#ifndef NDEBUG
slog.w << "Texture is missing image (" << srcTexture->name << ")." << io::endl;
#endif
return;
}
TextureSampler dstSampler;
@@ -1326,7 +1323,7 @@ void FAssetLoader::addTextureBinding(MaterialInstance* materialInstance, const c
dstSampler.setWrapModeS(TextureSampler::WrapMode::REPEAT);
dstSampler.setWrapModeT(TextureSampler::WrapMode::REPEAT);
// These defaults are up the implementation but since we generate mipmaps unconditionally,
// These defaults are up the implementation but since we try to provide mipmaps,
// we might as well use them. In practice the conformance models look awful without
// using mipmapping by default.
dstSampler.setMagFilter(TextureSampler::MagFilter::LINEAR);
+253
View File
@@ -0,0 +1,253 @@
/*
* 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 <gltfio/TextureProvider.h>
#include <string>
#include <vector>
#include <utils/JobSystem.h>
#include <filament/Engine.h>
#include <filament/Texture.h>
#include <ktxreader/Ktx2Reader.h>
using namespace filament;
using namespace utils;
using std::atomic;
using std::vector;
using std::unique_ptr;
namespace gltfio {
class Ktx2Provider final : public TextureProvider {
public:
Ktx2Provider(Engine* engine);
~Ktx2Provider();
Texture* pushTexture(const uint8_t* data, size_t byteCount,
const char* mimeType, uint64_t flags) final;
Texture* popTexture() final;
void updateQueue() final;
void waitForCompletion() final;
void cancelDecoding() final;
const char* getPushMessage() const final;
const char* getPopMessage() const final;
size_t getPushedCount() const final { return mPushedCount; }
size_t getPoppedCount() const final { return mPoppedCount; }
size_t getDecodedCount() const final { return mDecodedCount; }
private:
enum class QueueItemState {
TRANSCODING, // Texture has been pushed, mipmap levels are not yet complete.
READY, // Mipmap levels are available but texture has not been popped yet.
POPPED, // Client has popped the texture from the queue.
};
enum class TranscoderState {
NOT_STARTED,
ERROR,
SUCCESS,
};
struct QueueItem {
ktxreader::Ktx2Reader::Async* async;
QueueItemState state;
atomic<TranscoderState> transcoderState;
JobSystem::Job* job;
};
void transcodeSingleTexture();
size_t mPushedCount = 0;
size_t mPoppedCount = 0;
size_t mDecodedCount = 0;
vector<unique_ptr<QueueItem> > mQueueItems;
JobSystem::Job* mDecoderRootJob;
std::string mRecentPushMessage;
std::string mRecentPopMessage;
std::unique_ptr<ktxreader::Ktx2Reader> mKtxReader;
Engine* const mEngine;
};
Texture* Ktx2Provider::pushTexture(const uint8_t* data, size_t byteCount,
const char* mimeType, FlagBits flags) {
using InternalFormat = Texture::InternalFormat;
using TransferFunction = ktxreader::Ktx2Reader::TransferFunction;
const FlagBits sRGB = FlagBits(Flags::sRGB);
auto async = mKtxReader->asyncCreate(data, byteCount,
(flags & sRGB) ? TransferFunction::sRGB : TransferFunction::LINEAR);
if (async == nullptr) {
mRecentPushMessage = "Unable to build Texture object.";
return nullptr;
}
mRecentPushMessage.clear();
QueueItem* item = mQueueItems.emplace_back(new QueueItem).get();
++mPushedCount;
item->async = async;
item->state = QueueItemState::TRANSCODING;
item->transcoderState.store(TranscoderState::NOT_STARTED);
// On single threaded systems, it is usually fine to create jobs because the job system will
// simply execute serially. However in our case, we wish to amortize the decoder cost across
// several frames, so we instead use the updateQueue() method to perform decoding.
if constexpr (!UTILS_HAS_THREADING) {
item->job = nullptr;
return async->getTexture();
}
JobSystem* js = &mEngine->getJobSystem();
item->job = jobs::createJob(*js, mDecoderRootJob, [this, item] {
using Result = ktxreader::Ktx2Reader::Result;
const bool success = Result::SUCCESS == item->async->doTranscoding();
item->transcoderState.store(success ? TranscoderState::SUCCESS : TranscoderState::ERROR);
});
js->runAndRetain(item->job);
return async->getTexture();
}
Texture* Ktx2Provider::popTexture() {
// We don't bother shrinking the mQueueItems vector here, instead we periodically clean it up in
// the updateQueue method, since popTexture is typically called more frequently. Textures
// can become ready in non-deterministic order due to concurrency.
for (auto& item : mQueueItems) {
if (item->state == QueueItemState::READY) {
item->state = QueueItemState::POPPED;
++mPoppedCount;
const TranscoderState state = item->transcoderState.load();
if (state != TranscoderState::SUCCESS) {
mRecentPopMessage = "Texture is incomplete";
} else {
mRecentPopMessage.clear();
}
Texture* texture = item->async->getTexture();
mKtxReader->asyncDestroy(&item->async);
item->async = nullptr;
return texture;
}
}
return nullptr;
}
void Ktx2Provider::updateQueue() {
if (!UTILS_HAS_THREADING) {
transcodeSingleTexture();
}
JobSystem* js = &mEngine->getJobSystem();
for (auto& item : mQueueItems) {
if (item->state != QueueItemState::TRANSCODING) {
continue;
}
Texture* texture = item->async->getTexture();
const TranscoderState state = item->transcoderState.load();
if (state != TranscoderState::NOT_STARTED) {
if (item->job) {
js->waitAndRelease(item->job);
}
if (state == TranscoderState::ERROR) {
item->state = QueueItemState::READY;
++mDecodedCount;
continue;
}
item->async->uploadImages();
item->state = QueueItemState::READY;
++mDecodedCount;
}
}
// Here we periodically clean up the "queue" (which is really just a vector) by removing unused
// items from the front. This might ignore a popped texture that occurs in the middle of the
// vector, but that's okay, it will be cleaned up eventually.
decltype(mQueueItems)::iterator last = mQueueItems.begin();
while (last != mQueueItems.end() && (*last)->state == QueueItemState::POPPED) ++last;
mQueueItems.erase(mQueueItems.begin(), last);
}
void Ktx2Provider::waitForCompletion() {
JobSystem& js = mEngine->getJobSystem();
for (auto& item : mQueueItems) {
if (item->job) {
js.waitAndRelease(item->job);
}
}
}
void Ktx2Provider::cancelDecoding() {
// TODO: Currently, Ktx2Provider runs jobs eagerly and JobSystem does not allow cancellation of
// in-flight jobs. We should consider throttling the number of simultaneous decoder jobs, which
// would allow for actual cancellation.
waitForCompletion();
}
const char* Ktx2Provider::getPushMessage() const {
return mRecentPushMessage.empty() ? nullptr : mRecentPushMessage.c_str();
}
const char* Ktx2Provider::getPopMessage() const {
return mRecentPopMessage.empty() ? nullptr : mRecentPopMessage.c_str();
}
void Ktx2Provider::transcodeSingleTexture() {
assert_invariant(!UTILS_HAS_THREADING);
for (auto& item : mQueueItems) {
if (item->state == QueueItemState::TRANSCODING) {
using Result = ktxreader::Ktx2Reader::Result;
bool success = Result::SUCCESS == item->async->doTranscoding();
item->transcoderState.store(success ? TranscoderState::SUCCESS : TranscoderState::ERROR);
break;
}
}
}
Ktx2Provider::Ktx2Provider(Engine* engine) : mEngine(engine) {
mDecoderRootJob = mEngine->getJobSystem().createJob();
#ifdef NDEBUG
const bool quiet = true;
#else
const bool quiet = false;
#endif
mKtxReader.reset(new ktxreader::Ktx2Reader(*engine, quiet));
mKtxReader->requestFormat(Texture::InternalFormat::ETC2_EAC_SRGBA8);
mKtxReader->requestFormat(Texture::InternalFormat::DXT5_SRGBA);
mKtxReader->requestFormat(Texture::InternalFormat::DXT5_RGBA);
// Uncompressed formats are lower priority, so they get added last.
mKtxReader->requestFormat(Texture::InternalFormat::SRGB8_A8);
mKtxReader->requestFormat(Texture::InternalFormat::RGBA8);
}
Ktx2Provider::~Ktx2Provider() {
cancelDecoding();
for (auto& item : mQueueItems) {
mKtxReader->asyncDestroy(&item->async);
}
mEngine->getJobSystem().release(mDecoderRootJob);
}
TextureProvider* createKtx2Provider(Engine* engine) {
return new Ktx2Provider(engine);
}
} // namespace gltfio
+2 -1
View File
@@ -571,7 +571,8 @@ void ResourceLoader::asyncUpdateLoad() {
Texture* ResourceLoader::Impl::getOrCreateTexture(FFilamentAsset* asset, const TextureSlot& tb) {
const cgltf_texture* srcTexture = tb.texture;
const cgltf_image* image = srcTexture->image;
const cgltf_image* image = srcTexture->basisu_image ?
srcTexture->basisu_image : srcTexture->image;
const cgltf_buffer_view* bv = image->buffer_view;
const char* uri = image->uri;
+1 -1
View File
@@ -35,7 +35,7 @@ using std::unique_ptr;
namespace gltfio {
class StbProvider : public TextureProvider {
class StbProvider final : public TextureProvider {
public:
StbProvider(Engine* engine);
~StbProvider();
@@ -40,6 +40,12 @@ fragment {
void dummy() {}
#if defined(TARGET_MOBILE)
# define MIN_ROUGHNESS 0.007921
#else
# define MIN_ROUGHNESS 0.002025
#endif
// we default to highp in this shader
precision highp float;
precision highp int;
@@ -1,225 +0,0 @@
/*
* Copyright (C) 2018 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.
*/
//! \file Functions and types related to block-compressed texture formats.
#ifndef IMAGEIO_BLOCKCOMPRESSION_H_
#define IMAGEIO_BLOCKCOMPRESSION_H_
#include <image/LinearImage.h>
#include <memory>
#include <string>
#include <math/vec2.h>
#include <utils/compiler.h>
#include <stdint.h>
namespace image {
enum class CompressedFormat {
INVALID = 0,
R11_EAC = 0x9270,
SIGNED_R11_EAC = 0x9271,
RG11_EAC = 0x9272,
SIGNED_RG11_EAC = 0x9273,
RGB8_ETC2 = 0x9274,
SRGB8_ETC2 = 0x9275,
RGB8_ALPHA1_ETC2 = 0x9276,
SRGB8_ALPHA1_ETC = 0x9277,
RGBA8_ETC2_EAC = 0x9278,
SRGB8_ALPHA8_ETC2_EAC = 0x9279,
RGB_S3TC_DXT1 = 0x83F0,
RGBA_S3TC_DXT1 = 0x83F1,
RGBA_S3TC_DXT3 = 0x83F2,
RGBA_S3TC_DXT5 = 0x83F3,
SRGB_S3TC_DXT1 = 0x8C4C,
SRGB_ALPHA_S3TC_DXT1 = 0x8C4D,
SRGB_ALPHA_S3TC_DXT3 = 0x8C4E,
SRGB_ALPHA_S3TC_DXT5 = 0x8C4F,
RGBA_ASTC_4x4 = 0x93B0,
RGBA_ASTC_5x4 = 0x93B1,
RGBA_ASTC_5x5 = 0x93B2,
RGBA_ASTC_6x5 = 0x93B3,
RGBA_ASTC_6x6 = 0x93B4,
RGBA_ASTC_8x5 = 0x93B5,
RGBA_ASTC_8x6 = 0x93B6,
RGBA_ASTC_8x8 = 0x93B7,
RGBA_ASTC_10x5 = 0x93B8,
RGBA_ASTC_10x6 = 0x93B9,
RGBA_ASTC_10x8 = 0x93BA,
RGBA_ASTC_10x10 = 0x93BB,
RGBA_ASTC_12x10 = 0x93BC,
RGBA_ASTC_12x12 = 0x93BD,
SRGB8_ALPHA8_ASTC_4x4 = 0x93D0,
SRGB8_ALPHA8_ASTC_5x4 = 0x93D1,
SRGB8_ALPHA8_ASTC_5x5 = 0x93D2,
SRGB8_ALPHA8_ASTC_6x5 = 0x93D3,
SRGB8_ALPHA8_ASTC_6x6 = 0x93D4,
SRGB8_ALPHA8_ASTC_8x5 = 0x93D5,
SRGB8_ALPHA8_ASTC_8x6 = 0x93D6,
SRGB8_ALPHA8_ASTC_8x8 = 0x93D7,
SRGB8_ALPHA8_ASTC_10x5 = 0x93D8,
SRGB8_ALPHA8_ASTC_10x6 = 0x93D9,
SRGB8_ALPHA8_ASTC_10x8 = 0x93DA,
SRGB8_ALPHA8_ASTC_10x10 = 0x93DB,
SRGB8_ALPHA8_ASTC_12x10 = 0x93DC,
SRGB8_ALPHA8_ASTC_12x12 = 0x93DD,
};
// Represents the opaque result of compression and the chosen texture format.
struct CompressedTexture {
const CompressedFormat format;
const uint32_t size;
std::unique_ptr<uint8_t[]> data;
};
// ASTC ////////////////////////////////////////////////////////////////////////////////////////////
// Controls how fast compression occurs at the cost of quality in the resulting image.
enum class AstcPreset {
VERYFAST,
FAST,
MEDIUM,
THOROUGH,
EXHAUSTIVE,
};
// Informs the encoder what texels represent; this is especially crucial for normal maps.
enum class AstcSemantic {
COLORS_LDR,
COLORS_HDR,
NORMALS,
};
// The encoder configuration controls the quality and speed of compression, as well as the resulting
// format. The specified block size must be one of the 14 block sizes that can be consumed by ES 3.2
// as per https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glCompressedTexImage2D.xhtml
struct AstcConfig {
AstcPreset quality;
AstcSemantic semantic;
filament::math::ushort2 blocksize;
bool srgb;
};
// Uses the CPU to compress a linear image (1 to 4 channels) into an ASTC texture. The 16-byte
// header block that ARM uses in their file format is not included.
CompressedTexture astcCompress(const LinearImage& source, AstcConfig config);
// Parses a simple underscore-delimited string to produce an ASTC compression configuration. This
// makes it easy to incorporate the compression API into command-line tools. If the string is
// malformed, this returns a config with a 0x0 blocksize. Example strings: fast_ldr_4x4,
// thorough_normals_6x6, veryfast_hdr_12x10
AstcConfig astcParseOptionString(const std::string& options);
// ETC /////////////////////////////////////////////////////////////////////////////////////////////
enum class EtcErrorMetric {
RGBA,
RGBX,
REC709,
NUMERIC,
NORMALXYZ,
};
// Informs the ETC encoder of the desired output. Effort sets the quality / speed tradeoff with
// a number between 0 and 100.
struct EtcConfig {
CompressedFormat format;
EtcErrorMetric metric;
int effort;
};
// Uses the CPU to compress a linear image (1 to 4 channels) into an ETC texture.
CompressedTexture etcCompress(const LinearImage& source, EtcConfig config);
// Converts a string into an ETC compression configuration where the string has the form
// FORMAT_METRIC_EFFORT where:
// - FORMAT is one of: r11, signed_r11, rg11, signed_rg11, rgb8, srgb8, rgb8_alpha,
// srgb8_alpha, rgba8, and srgb8_alpha8
// - METRIC is one of: rgba, rgbx, rec709, numeric, and normalxyz
// - EFFORT is an integer between 0 and 100
EtcConfig etcParseOptionString(const std::string& options);
// S3TC ////////////////////////////////////////////////////////////////////////////////////////////
// Informs the S3TC encoder of the desired output.
struct S3tcConfig {
CompressedFormat format;
bool srgb;
};
// Uses the CPU to compress a linear image (1 to 4 channels) into an S3TC texture.
CompressedTexture s3tcCompress(const LinearImage& source, S3tcConfig config);
// Parses an underscore-delimited string to produce an S3TC compression configuration. Currently
// this only accepts "rgb_dxt1" and "rgba_dxt5". If the string is malformed, this returns a config
// with an invalid format.
S3tcConfig s3tcParseOptionString(const std::string& options);
///////////////////////////////////////////////////////////////////////////////////////////////////
struct CompressionConfig {
enum { INVALID, ASTC, S3TC, ETC } type;
AstcConfig astc;
S3tcConfig s3tc;
EtcConfig etc;
bool isLinear() const {
if (type == ASTC) {
return !astc.srgb;
}
switch (type == S3TC ? s3tc.format : etc.format) {
case CompressedFormat::R11_EAC:
case CompressedFormat::SIGNED_R11_EAC:
case CompressedFormat::RG11_EAC:
case CompressedFormat::SIGNED_RG11_EAC:
case CompressedFormat::RGB8_ETC2:
case CompressedFormat::RGB8_ALPHA1_ETC2:
case CompressedFormat::RGBA8_ETC2_EAC:
case CompressedFormat::RGB_S3TC_DXT1:
case CompressedFormat::RGBA_S3TC_DXT1:
case CompressedFormat::RGBA_S3TC_DXT3:
case CompressedFormat::RGBA_S3TC_DXT5:
return true;
case CompressedFormat::SRGB8_ETC2:
case CompressedFormat::SRGB8_ALPHA1_ETC:
case CompressedFormat::SRGB8_ALPHA8_ETC2_EAC:
case CompressedFormat::SRGB_S3TC_DXT1:
case CompressedFormat::SRGB_ALPHA_S3TC_DXT1:
case CompressedFormat::SRGB_ALPHA_S3TC_DXT3:
case CompressedFormat::SRGB_ALPHA_S3TC_DXT5:
return false;
default:
// This should be unreachable.
assert(false && "ASTC formats are already handled");
return false;
}
}
};
bool parseOptionString(const std::string& options, CompressionConfig* config);
UTILS_PUBLIC
CompressedTexture compressTexture(const CompressionConfig& config, const LinearImage& image);
} // namespace image
#endif /* IMAGEIO_BLOCKCOMPRESSION_H_ */
+9 -1
View File
@@ -94,10 +94,18 @@ Builder& Builder::miplevel(size_t level, size_t layer, const LinearImage& floatI
std::unique_ptr<uint8_t[]> data = applyTransferFunction ?
fromLinearTosRGB<uint8_t, 1>(sourceImage) : fromLinearToGrayscale<uint8_t>(sourceImage);
basisImage->init(data.get(), floatImage.getWidth(), floatImage.getHeight(), 1);
} else {
} else if (sourceImage.getChannels() == 4) {
std::unique_ptr<uint8_t[]> data = applyTransferFunction ?
fromLinearTosRGB<uint8_t, 4>(sourceImage) : fromLinearToRGB<uint8_t, 4>(sourceImage);
basisImage->init(data.get(), floatImage.getWidth(), floatImage.getHeight(), 4);
} else if (sourceImage.getChannels() == 3) {
std::unique_ptr<uint8_t[]> data = applyTransferFunction ?
fromLinearTosRGB<uint8_t, 3>(sourceImage) : fromLinearToRGB<uint8_t, 3>(sourceImage);
basisImage->init(data.get(), floatImage.getWidth(), floatImage.getHeight(), 3);
} else {
assert_invariant(false);
mImpl->error = true;
return *this;
}
return *this;
-589
View File
@@ -1,589 +0,0 @@
/*
* Copyright (C) 2018 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 <imageio/BlockCompression.h>
#include <image/ImageOps.h>
#include <algorithm>
#include <cmath>
#include <thread>
#include <astcenc.h>
#include <Etc.h>
#define STB_DXT_IMPLEMENTATION
#include <stb_dxt.h>
namespace image {
static LinearImage extendToFourChannels(LinearImage source);
CompressedTexture astcCompress(const LinearImage& original, AstcConfig config) {
// If this is the first time, initialize the ARM encoder tables.
static bool first = true;
if (first) {
test_inappropriate_extended_precision();
prepare_angular_tables();
build_quantization_mode_table();
first = false;
}
// Check the validity of the given block size.
using Format = CompressedFormat;
Format format;
if (config.blocksize == filament::math::ushort2 {4, 4}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_4x4 : Format::RGBA_ASTC_4x4;
} else if (config.blocksize == filament::math::ushort2 {5, 4}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_5x4 : Format::RGBA_ASTC_5x4;
} else if (config.blocksize == filament::math::ushort2 {5, 5}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_5x5 : Format::RGBA_ASTC_5x5;
} else if (config.blocksize == filament::math::ushort2 {6, 5}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_6x5 : Format::RGBA_ASTC_6x5;
} else if (config.blocksize == filament::math::ushort2 {6, 6}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_6x6 : Format::RGBA_ASTC_6x6;
} else if (config.blocksize == filament::math::ushort2 {8, 5}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_8x5 : Format::RGBA_ASTC_8x5;
} else if (config.blocksize == filament::math::ushort2 {8, 6}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_8x6 : Format::RGBA_ASTC_8x6;
} else if (config.blocksize == filament::math::ushort2 {8, 8}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_8x8 : Format::RGBA_ASTC_8x8;
} else if (config.blocksize == filament::math::ushort2 {10, 5}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_10x5 : Format::RGBA_ASTC_10x5;
} else if (config.blocksize == filament::math::ushort2 {10, 6}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_10x6 : Format::RGBA_ASTC_10x6;
} else if (config.blocksize == filament::math::ushort2 {10, 8}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_10x8 : Format::RGBA_ASTC_10x8;
} else if (config.blocksize == filament::math::ushort2 {10, 10}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_10x10 : Format::RGBA_ASTC_10x10;
} else if (config.blocksize == filament::math::ushort2 {12, 10}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_12x10 : Format::RGBA_ASTC_12x10;
} else if (config.blocksize == filament::math::ushort2 {12, 12}) {
format = config.srgb ? Format::SRGB8_ALPHA8_ASTC_12x12 : Format::RGBA_ASTC_12x12;
} else {
return {};
}
// Create an input image for the ARM encoder in a format that it can consume.
// It expects four-channel data, so we extend or curtail the channel count in a reasonable way.
// The encoder can take half-floats or bytes, but we always give it half-floats.
LinearImage source = extendToFourChannels(original);
const uint32_t width = source.getWidth();
const uint32_t height = source.getHeight();
astc_codec_image* input_image = allocate_image(16, width, height, 1, 0);
for (int y = 0; y < height; y++) {
auto imagedata16 = input_image->imagedata16[0][y];
float const* src = source.getPixelRef(0, y);
for (int x = 0; x < width; x++) {
imagedata16[4 * x] = float_to_sf16(src[4 * x], SF_NEARESTEVEN);
imagedata16[4 * x + 1] = float_to_sf16(src[4 * x + 1], SF_NEARESTEVEN);
imagedata16[4 * x + 2] = float_to_sf16(src[4 * x + 2], SF_NEARESTEVEN);
imagedata16[4 * x + 3] = float_to_sf16(src[4 * x + 3], SF_NEARESTEVEN);
}
}
// Determine the bitrate based on the specified block size.
int xdim_2d = config.blocksize.x, ydim_2d = config.blocksize.y;
const float log10_texels_2d = std::log((float)(xdim_2d * ydim_2d)) / std::log(10.0f);
const float bitrate = 128.0 / (xdim_2d * ydim_2d);
// We do not fully support 3D textures yet, but we include some of the 3D config params anyway.
int xdim_3d, ydim_3d, zdim_3d;
find_closest_blockdim_3d(bitrate, &xdim_3d, &ydim_3d, &zdim_3d, 0);
const float log10_texels_3d = std::log((float)(xdim_3d * ydim_3d * zdim_3d)) / log(10.0f);
// Set up presets.
int plimit_autoset;
float oplimit_autoset;
float dblimit_autoset_2d;
float dblimit_autoset_3d;
float bmc_autoset;
float mincorrel_autoset;
int maxiters_autoset;
int pcdiv;
switch (config.quality) {
case AstcPreset::VERYFAST:
plimit_autoset = 2;
oplimit_autoset = 1.0;
dblimit_autoset_2d = fmax(70 - 35 * log10_texels_2d, 53 - 19 * log10_texels_2d);
dblimit_autoset_3d = fmax(70 - 35 * log10_texels_3d, 53 - 19 * log10_texels_3d);
bmc_autoset = 25;
mincorrel_autoset = 0.5;
maxiters_autoset = 1;
switch (ydim_2d) {
case 4: pcdiv = 240; break;
case 5: pcdiv = 56; break;
case 6: pcdiv = 64; break;
case 8: pcdiv = 47; break;
case 10: pcdiv = 36; break;
case 12: pcdiv = 30; break;
default: pcdiv = 30; break;
}
break;
case AstcPreset::FAST:
plimit_autoset = 4;
oplimit_autoset = 1.0;
dblimit_autoset_2d = fmax(85 - 35 * log10_texels_2d, 63 - 19 * log10_texels_2d);
dblimit_autoset_3d = fmax(85 - 35 * log10_texels_3d, 63 - 19 * log10_texels_3d);
bmc_autoset = 50;
mincorrel_autoset = 0.5;
maxiters_autoset = 1;
switch (ydim_2d) {
case 4: pcdiv = 60; break;
case 5: pcdiv = 27; break;
case 6: pcdiv = 30; break;
case 8: pcdiv = 24; break;
case 10: pcdiv = 16; break;
case 12: pcdiv = 20; break;
default: pcdiv = 20; break;
}
break;
case AstcPreset::MEDIUM:
plimit_autoset = 25;
oplimit_autoset = 1.2;
dblimit_autoset_2d = fmax(95 - 35 * log10_texels_2d, 70 - 19 * log10_texels_2d);
dblimit_autoset_3d = fmax(95 - 35 * log10_texels_3d, 70 - 19 * log10_texels_3d);
bmc_autoset = 75;
mincorrel_autoset = 0.75;
maxiters_autoset = 2;
switch (ydim_2d) {
case 4: pcdiv = 25; break;
case 5: pcdiv = 15; break;
case 6: pcdiv = 15; break;
case 8: pcdiv = 10; break;
case 10: pcdiv = 8; break;
case 12: pcdiv = 6; break;
default: pcdiv = 6; break;
}
break;
case AstcPreset::THOROUGH:
plimit_autoset = 100;
oplimit_autoset = 2.5;
dblimit_autoset_2d = fmax(105 - 35 * log10_texels_2d, 77 - 19 * log10_texels_2d);
dblimit_autoset_3d = fmax(105 - 35 * log10_texels_3d, 77 - 19 * log10_texels_3d);
bmc_autoset = 95;
mincorrel_autoset = 0.95f;
maxiters_autoset = 4;
switch (ydim_2d) {
case 4: pcdiv = 12; break;
case 5: pcdiv = 7; break;
case 6: pcdiv = 7; break;
case 8: pcdiv = 5; break;
case 10: pcdiv = 4; break;
case 12: pcdiv = 3; break;
default: pcdiv = 3; break;
}
break;
case AstcPreset::EXHAUSTIVE:
plimit_autoset = 1 << 10;
oplimit_autoset = 1000.0;
dblimit_autoset_2d = 999.0f;
dblimit_autoset_3d = 999.0f;
bmc_autoset = 100;
mincorrel_autoset = 0.99;
maxiters_autoset = 4;
switch (ydim_2d) {
case 4: pcdiv = 3; break;
case 5: pcdiv = 1; break;
case 6: pcdiv = 1; break;
case 8: pcdiv = 1; break;
case 10: pcdiv = 1; break;
case 12: pcdiv = 1; break;
default: pcdiv = 1; break;
}
break;
}
if (plimit_autoset < 1) {
plimit_autoset = 1;
} else if (plimit_autoset > PARTITION_COUNT) {
plimit_autoset = PARTITION_COUNT;
}
error_weighting_params ewp;
ewp.rgb_power = 1.0f;
ewp.alpha_power = 1.0f;
ewp.rgb_base_weight = 1.0f;
ewp.alpha_base_weight = 1.0f;
ewp.rgb_mean_weight = 0.0f;
ewp.rgb_stdev_weight = 0.0f;
ewp.alpha_mean_weight = 0.0f;
ewp.alpha_stdev_weight = 0.0f;
ewp.rgb_mean_and_stdev_mixing = 0.0f;
ewp.mean_stdev_radius = 0;
ewp.enable_rgb_scale_with_alpha = 0;
ewp.alpha_radius = 0;
ewp.block_artifact_suppression = 0.0f;
ewp.rgba_weights[0] = 1.0f;
ewp.rgba_weights[1] = 1.0f;
ewp.rgba_weights[2] = 1.0f;
ewp.rgba_weights[3] = 1.0f;
ewp.ra_normal_angular_scale = 0;
ewp.max_refinement_iters = maxiters_autoset;
ewp.block_mode_cutoff = bmc_autoset / 100.0f;
ewp.texel_avg_error_limit = pow(0.1f, dblimit_autoset_2d * 0.1f) * 65535.0f * 65535.0f;
ewp.partition_1_to_2_limit = oplimit_autoset;
ewp.lowest_correlation_cutoff = mincorrel_autoset;
ewp.partition_search_limit = plimit_autoset;
// For now we do not support 3D textures but we keep the variable names consistent
// with what's found in the ARM standalone tool.
int xdim = xdim_2d, ydim = ydim_2d, zdim = 1;
expand_block_artifact_suppression(xdim, ydim, zdim, &ewp);
// Perform compression.
swizzlepattern swz_encode = { 0, 1, 2, 3 };
swizzlepattern swz_decode = { 0, 1, 2, 3 };
astc_decode_mode decode_mode;
switch (config.semantic) {
case AstcSemantic::COLORS_LDR:
decode_mode = config.srgb ? DECODE_LDR_SRGB : DECODE_LDR;
break;
case AstcSemantic::COLORS_HDR:
decode_mode = DECODE_HDR;
break;
case AstcSemantic::NORMALS:
decode_mode = config.srgb ? DECODE_LDR_SRGB : DECODE_LDR;
ewp.rgba_weights[0] = 1.0f;
ewp.rgba_weights[1] = 0.0f;
ewp.rgba_weights[2] = 0.0f;
ewp.rgba_weights[3] = 1.0f;
ewp.ra_normal_angular_scale = 1;
swz_encode.r = 0;
swz_encode.g = 0;
swz_encode.b = 0;
swz_encode.a = 1;
swz_decode.r = 0;
swz_decode.g = 3;
swz_decode.b = 6;
swz_decode.a = 5;
ewp.block_artifact_suppression = 1.8f;
ewp.mean_stdev_radius = 3;
ewp.rgb_mean_weight = 0;
ewp.rgb_stdev_weight = 50;
ewp.rgb_mean_and_stdev_mixing = 0.0;
ewp.alpha_mean_weight = 0;
ewp.alpha_stdev_weight = 50;
break;
}
const int threadcount = std::thread::hardware_concurrency();
const int xsize = input_image->xsize;
const int ysize = input_image->ysize;
const int zsize = input_image->zsize;
const int xblocks = (xsize + xdim - 1) / xdim;
const int yblocks = (ysize + ydim - 1) / ydim;
const int zblocks = (zsize + zdim - 1) / zdim;
uint32_t size = xblocks * yblocks * zblocks * 16;
uint8_t* buffer = new uint8_t[size];
encode_astc_image(input_image, nullptr, xdim, ydim, zdim, &ewp, decode_mode,
swz_encode, swz_decode, buffer, 0, threadcount);
destroy_image(input_image);
return {
.format = format,
.size = size,
.data = decltype(CompressedTexture::data)(buffer)
};
}
AstcConfig astcParseOptionString(const std::string& configString) {
const size_t _1 = configString.find('_');
const size_t _2 = configString.find('_', _1 + 1);
if (_1 == std::string::npos || _2 == std::string::npos) {
return {};
}
std::string quality = configString.substr(0, _1);
std::string semantic = configString.substr(_1 + 1, _2 - _1 - 1);
std::string blocksize = configString.substr(_2 + 1);
AstcConfig config;
if (quality == "veryfast") {
config.quality = AstcPreset::VERYFAST;
} else if (quality == "fast") {
config.quality = AstcPreset::FAST;
} else if (quality == "medium") {
config.quality = AstcPreset::MEDIUM;
} else if (quality == "thorough") {
config.quality = AstcPreset::THOROUGH;
} else if (quality == "exhaustive") {
config.quality = AstcPreset::EXHAUSTIVE;
} else {
return {};
}
if (semantic == "ldr") {
config.semantic = AstcSemantic::COLORS_LDR;
} else if (semantic == "hdr") {
config.semantic = AstcSemantic::COLORS_HDR;
} else if (semantic == "normals") {
config.semantic = AstcSemantic::NORMALS;
} else {
return {};
}
const size_t _x = blocksize.find('x');
if (_x == std::string::npos) {
return {};
}
config.blocksize[0] = std::stoi(blocksize.substr(0, _x));
config.blocksize[1] = std::stoi(blocksize.substr(_x + 1));
return config;
}
static uint32_t imin(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
static void extract4x4RGBA(uint8_t* dst, const LinearImage& source, uint32_t x0, uint32_t y0) {
const uint32_t maxx = source.getWidth() - 1;
const uint32_t maxy = source.getHeight() - 1;
for (uint32_t y = y0, y1 = y0 + 4; y < y1; ++y) {
for (uint32_t x = x0, x1 = x0 + 4; x < x1; ++x, dst += 4) {
int clamped_x = imin(maxx, x);
int clamped_y = imin(maxy, y);
float const* rgba = source.getPixelRef(clamped_x, clamped_y);
dst[0] = (uint8_t) std::min(255.0f, std::max(0.0f, (rgba[0] * 255.0f)));
dst[1] = (uint8_t) std::min(255.0f, std::max(0.0f, (rgba[1] * 255.0f)));
dst[2] = (uint8_t) std::min(255.0f, std::max(0.0f, (rgba[2] * 255.0f)));
dst[3] = (uint8_t) std::min(255.0f, std::max(0.0f, (rgba[3] * 255.0f)));
}
}
}
// Our S3TC / DXT encoder uses the STB implementation by Fabian Giesen.
//
// Due to limitations in STB, this only supports the following formats:
// - DXT1 with no alpha (16 input pixels in 64 bits of output, 6:1)
// - DXT5 with alpha (16 input pixels into 128 bits of output, 4:1)
//
// TODO: remove this in favor of basisu
CompressedTexture s3tcCompress(const LinearImage& original, S3tcConfig config) {
const bool dxt5 = config.format == CompressedFormat::RGBA_S3TC_DXT5;
LinearImage source = extendToFourChannels(original);
uint8_t block[64];
uint32_t xblocks = (source.getWidth() + 3) / 4;
uint32_t yblocks = (source.getHeight() + 3) / 4;
uint32_t size = xblocks * yblocks * (dxt5 ? 16 : 8);
uint8_t* buffer = new uint8_t[size];
uint8_t* dst = buffer;
for (int y = 0, h = source.getHeight(); y < h; y += 4) {
for (int x = 0, w = source.getWidth(); x < w; x += 4) {
extract4x4RGBA(block, source, x, y);
stb_compress_dxt_block(dst, block, dxt5, 8);
dst += dxt5 ? 16 : 8;
}
}
return {
.format = config.format,
.size = size,
.data = decltype(CompressedTexture::data)(buffer)
};
}
S3tcConfig s3tcParseOptionString(const std::string& options) {
if (options == "rgb_dxt1") {
return {CompressedFormat::RGB_S3TC_DXT1, false};
}
if (options == "rgba_dxt5") {
return {CompressedFormat::RGBA_S3TC_DXT5, false};
}
return {};
}
CompressedTexture etcCompress(const LinearImage& original, EtcConfig config) {
LinearImage source = extendToFourChannels(original);
const int threadcount = std::thread::hardware_concurrency();
Etc::Image::Format etcformat;
switch (config.format) {
case CompressedFormat::R11_EAC: etcformat = Etc::Image::Format::R11; break;
case CompressedFormat::SIGNED_R11_EAC: etcformat = Etc::Image::Format::SIGNED_R11; break;
case CompressedFormat::RG11_EAC: etcformat = Etc::Image::Format::RG11; break;
case CompressedFormat::SIGNED_RG11_EAC: etcformat = Etc::Image::Format::SIGNED_RG11; break;
case CompressedFormat::RGB8_ETC2: etcformat = Etc::Image::Format::RGB8; break;
case CompressedFormat::SRGB8_ETC2: etcformat = Etc::Image::Format::SRGB8; break;
case CompressedFormat::RGB8_ALPHA1_ETC2: etcformat = Etc::Image::Format::RGB8A1; break;
case CompressedFormat::SRGB8_ALPHA1_ETC: etcformat = Etc::Image::Format::SRGB8A1; break;
case CompressedFormat::RGBA8_ETC2_EAC: etcformat = Etc::Image::Format::RGBA8; break;
case CompressedFormat::SRGB8_ALPHA8_ETC2_EAC: etcformat = Etc::Image::Format::SRGBA8; break;
default: return {};
}
Etc::ErrorMetric etcmetric;
switch (config.metric) {
case EtcErrorMetric::RGBA: etcmetric = Etc::RGBA; break;
case EtcErrorMetric::RGBX: etcmetric = Etc::RGBX; break;
case EtcErrorMetric::REC709: etcmetric = Etc::REC709; break;
case EtcErrorMetric::NUMERIC: etcmetric = Etc::NUMERIC; break;
case EtcErrorMetric::NORMALXYZ: etcmetric = Etc::NORMALXYZ; break;
default: return {};
}
unsigned char *paucEncodingBits;
unsigned int uiEncodingBitsBytes;
unsigned int uiExtendedWidth;
unsigned int uiExtendedHeight;
int iEncodingTime_ms;
// The etc2comp API doesn't tell you that you need to free paucEncodingBits, but they have a
// commented-out "delete[] m_paucEncodingBits" in their Image destructor, which is essentially
// what our unique_ptr wrapper does (CompressedTexture::data).
Etc::Encode(source.getPixelRef(0, 0),
source.getWidth(), source.getHeight(),
etcformat,
etcmetric,
config.effort,
threadcount,
1024,
&paucEncodingBits, &uiEncodingBitsBytes,
&uiExtendedWidth, &uiExtendedHeight,
&iEncodingTime_ms);
return {
.format = config.format,
.size = uiEncodingBitsBytes,
.data = decltype(CompressedTexture::data)(paucEncodingBits)
};
}
EtcConfig etcParseOptionString(const std::string& options) {
EtcConfig result {};
const size_t _2 = options.rfind('_');
const size_t _1 = options.rfind('_', _2 - 1);
std::string sformat = options.substr(0, _1);
std::string smetric = options.substr(_1 + 1, _2 - _1 - 1);
std::string seffort = options.substr(_2 + 1);
if (sformat == "r11") {
result.format = CompressedFormat::R11_EAC;
} else if (sformat == "signed_r11") {
result.format = CompressedFormat::SIGNED_R11_EAC;
} else if (sformat == "rg11") {
result.format = CompressedFormat::RG11_EAC;
} else if (sformat == "signed_rg11") {
result.format = CompressedFormat::SIGNED_RG11_EAC;
} else if (sformat == "rgb8") {
result.format = CompressedFormat::RGB8_ETC2;
} else if (sformat == "srgb8") {
result.format = CompressedFormat::SRGB8_ETC2;
} else if (sformat == "rgb8_alpha") {
result.format = CompressedFormat::RGB8_ALPHA1_ETC2;
} else if (sformat == "srgb8_alpha") {
result.format = CompressedFormat::SRGB8_ALPHA1_ETC;
} else if (sformat == "rgba8") {
result.format = CompressedFormat::RGBA8_ETC2_EAC;
} else if (sformat == "srgb8_alpha8") {
result.format = CompressedFormat::SRGB8_ALPHA8_ETC2_EAC;
}
if (smetric == "rgba") {
result.metric = EtcErrorMetric::RGBA;
} else if (smetric == "rgbx") {
result.metric = EtcErrorMetric::RGBX;
} else if (smetric == "rec709") {
result.metric = EtcErrorMetric::REC709;
} else if (smetric == "numeric") {
result.metric = EtcErrorMetric::NUMERIC;
} else if (smetric == "normalxyz") {
result.metric = EtcErrorMetric::NORMALXYZ;
}
result.effort = std::stoi(seffort);
return result;
}
bool parseOptionString(const std::string& options, CompressionConfig* config) {
config->type = CompressionConfig::INVALID;
if (options.substr(0, 5) == "astc_") {
config->astc = astcParseOptionString(options.substr(5));
if (config->astc.blocksize[0] != 0) {
config->type = CompressionConfig::ASTC;
}
} else if (options.substr(0, 5) == "s3tc_") {
config->s3tc = s3tcParseOptionString(options.substr(5));
if (config->s3tc.format != CompressedFormat::INVALID) {
config->type = CompressionConfig::S3TC;
}
} else if (options.substr(0, 4) == "etc_") {
config->etc = etcParseOptionString(options.substr(4));
if (config->etc.format != CompressedFormat::INVALID) {
config->type = CompressionConfig::ETC;
}
}
return config->type != CompressionConfig::INVALID;
}
CompressedTexture compressTexture(const CompressionConfig& config, const LinearImage& image) {
if (config.type == CompressionConfig::ASTC) {
return astcCompress(image, config.astc);
}
if (config.type == CompressionConfig::S3TC) {
return s3tcCompress(image, config.s3tc);
}
if (config.type == CompressionConfig::ETC) {
return etcCompress(image, config.etc);
}
return {};
}
static LinearImage extendToFourChannels(LinearImage original) {
LinearImage source = original;
const uint32_t width = source.getWidth();
const uint32_t height = source.getHeight();
auto createEmptyImage = [width, height](float value) {
auto result = LinearImage(width, height, 1);
float* pixels = result.getPixelRef(0, 0);
std::fill(pixels, pixels + width * height, value);
return result;
};
switch (source.getChannels()) {
case 4: break;
case 1: {
auto l = image::extractChannel(source, 0);
auto a = createEmptyImage(1.0f);
source = image::combineChannels({l, l, l, a});
}
case 2: {
auto l = image::extractChannel(source, 0);
auto a = image::extractChannel(source, 1);
source = image::combineChannels({l, l, l, a});
}
case 3: {
auto r = image::extractChannel(source, 0);
auto g = image::extractChannel(source, 1);
auto b = image::extractChannel(source, 2);
auto a = createEmptyImage(1.0f);
source = image::combineChannels({r, g, b, a});
}
default: {
auto r = image::extractChannel(source, 0);
auto g = image::extractChannel(source, 1);
auto b = image::extractChannel(source, 2);
auto a = image::extractChannel(source, 3);
source = image::combineChannels({r, g, b, a});
}
}
return source;
}
} // namespace image
@@ -53,6 +53,55 @@ namespace Ktx1Reader {
TextureFormat toTextureFormat(const KtxInfo& info);
TextureFormat toSrgbTextureFormat(TextureFormat tex);
template<typename T>
T toCompressedFilamentEnum(uint32_t format) {
switch (format) {
case Ktx1Bundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
case Ktx1Bundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
case Ktx1Bundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
case Ktx1Bundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
case Ktx1Bundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
case Ktx1Bundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
case Ktx1Bundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
case Ktx1Bundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
case Ktx1Bundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
case Ktx1Bundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
case Ktx1Bundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
case Ktx1Bundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
case Ktx1Bundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
case Ktx1Bundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
case Ktx1Bundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
case Ktx1Bundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
case Ktx1Bundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
case Ktx1Bundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
case Ktx1Bundle::R11_EAC: return T::EAC_R11;
case Ktx1Bundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
case Ktx1Bundle::RG11_EAC: return T::EAC_RG11;
case Ktx1Bundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
case Ktx1Bundle::RGB8_ETC2: return T::ETC2_RGB8;
case Ktx1Bundle::SRGB8_ETC2: return T::ETC2_SRGB8;
case Ktx1Bundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
case Ktx1Bundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
case Ktx1Bundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
case Ktx1Bundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
}
return (T) 0xffff;
}
/**
* Creates a Texture object from a KTX file and populates all of its faces and miplevels.
*
+108 -8
View File
@@ -40,6 +40,14 @@ class Ktx2Reader {
using Texture = filament::Texture;
enum class TransferFunction { LINEAR, sRGB };
enum class Result {
SUCCESS,
COMPRESSED_TRANSCODE_FAILURE,
UNCOMPRESSED_TRANSCODE_FAILURE,
FORMAT_UNSUPPORTED,
FORMAT_ALREADY_REQUESTED,
};
Ktx2Reader(Engine& engine, bool quiet = false);
~Ktx2Reader();
@@ -55,18 +63,18 @@ class Ktx2Reader {
* considered higher priority.
*
* If BasisU knows a priori that the given format is not available (e.g. if the build has
* disabled it), this returns false and the format is not added to the list.
* disabled it), the format is not added and FORMAT_UNSUPPORTED is returned.
*
* Returns false if the given format has already been requested.
* Returns FORMAT_ALREADY_REQUESTED if the given format has already been requested.
*
* Hint: BasisU supports the following uncompressed formats: RGBA8, RGB565, RGBA4.
*/
bool requestFormat(Texture::InternalFormat format);
Result requestFormat(Texture::InternalFormat format) noexcept;
/**
* Removes the given format from the list, or does nothing if it hasn't been requested.
*/
void unrequestFormat(Texture::InternalFormat format);
void unrequestFormat(Texture::InternalFormat format) noexcept;
/**
* Attempts to create and load a Filament texture from the given KTX2 blob.
@@ -83,7 +91,97 @@ class Ktx2Reader {
* in the KTX2 blob. If they do not match, this method fails.
* 2) It is used as a filter when determining the final internal format.
*/
Texture* load(const uint8_t* data, size_t size, TransferFunction transfer);
Texture* load(const void* data, size_t size, TransferFunction transfer);
/**
* Asynchronous Interface
* ======================
*
* Alternative API suitable for asynchronous transcoding of mipmap levels.
* If unsure that you need to use this, then don't, just call load() instead.
* Usage pseudocode:
*
* auto async = reader->asyncCreate(data, size, TransferFunction::LINEAR);
* mTexture = async->getTexture();
* auto backgroundThread = spawnThread({ async->doTranscoding(); })
* backgroundThread.wait();
* async->uploadImages();
* reader->asyncDestroy(async);
*
* In the documentation comments, "foreground thread" refers to the thread that the
* Filament Engine was created on.
*/
class Async {
public:
/**
* Retrieves the Texture object.
*
* The texture is available immediately, but does not have its miplevels ready until
* after doTranscoding() and the subsequent uploadImages() have been completed. The
* caller has ownership over this texture and is responsible for freeing it after all
* miplevels have been uploaded.
*/
Texture* getTexture() const noexcept;
/**
* Loads all mipmaps from the KTX2 file and transcodes them to the resolved format.
*
* This does not return until all mipmaps have been transcoded. This is typically
* called from a background thread.
*/
Result doTranscoding();
/**
* Uploads pending mipmaps to the texture.
*
* This can safely be called while doTranscoding() is still working in another thread.
* Since this calls Texture::setImage(), it should be called from the foreground thread;
* see "Thread safety" in the documentation for filament::Engine.
*/
void uploadImages();
protected:
Async() noexcept = default;
~Async() = default;
public:
Async(Async const&) = delete;
Async(Async&&) = delete;
Async& operator=(Async const&) = delete;
Async& operator=(Async&&) = delete;
friend class Ktx2Reader;
};
/**
* Creates a texture without starting the transcode process.
*
* This method is an alternative to load() that allows users to populate mipmap levels
* asynchronously. The texture object however is still created synchronously.
*
* - For a usage example, see the documentation for the Async object.
* - Creates a copy of the given buffer, allowing clients to free it immediately.
* - Returns null if none of the requested formats can be extracted from the data.
*
* This method iterates through the requested format list, checking each one against the
* platform's capabilities and its availability from the transcoder. When a suitable format
* is determined, it then performs lossless decompression (zstd) before transcoding the data
* into the final format.
*
* The transfer function specified here is used in two ways:
* 1) It is checked against the transfer function that was specified as metadata
* in the KTX2 blob. If they do not match, this method fails.
* 2) It is used as a filter when determining the final internal format.
*/
Async* asyncCreate(const void* data, size_t size, TransferFunction transfer);
/**
* Frees the given async object and sets it to null.
*
* This frees the original source data (i.e. the raw content of the KTX2 file) but does not
* free the associated Texture object. This can be done after transcoding has finished.
*/
void asyncDestroy(Async** async);
private:
Ktx2Reader(const Ktx2Reader&) = delete;
@@ -91,11 +189,13 @@ class Ktx2Reader {
Ktx2Reader(Ktx2Reader&& that) noexcept = delete;
Ktx2Reader& operator=(Ktx2Reader&& that) noexcept = delete;
Engine& mEngine;
bool mQuiet;
basist::ktx2_transcoder* const mTranscoder;
Texture* createTexture(basist::ktx2_transcoder* transcoder, const void* data,
size_t size, TransferFunction transfer);
Engine& mEngine;
basist::ktx2_transcoder* const mTranscoder;
utils::FixedCapacityVector<Texture::InternalFormat> mRequestedFormats;
bool mQuiet;
};
} // namespace ktxreader
+7 -51
View File
@@ -23,55 +23,6 @@
namespace ktxreader {
namespace Ktx1Reader {
template<typename T>
T toCompressedFilamentEnum(uint32_t format) {
switch (format) {
case Ktx1Bundle::RGB_S3TC_DXT1: return T::DXT1_RGB;
case Ktx1Bundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA;
case Ktx1Bundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA;
case Ktx1Bundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA;
case Ktx1Bundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4;
case Ktx1Bundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4;
case Ktx1Bundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5;
case Ktx1Bundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5;
case Ktx1Bundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6;
case Ktx1Bundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5;
case Ktx1Bundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6;
case Ktx1Bundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8;
case Ktx1Bundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5;
case Ktx1Bundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6;
case Ktx1Bundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8;
case Ktx1Bundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10;
case Ktx1Bundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10;
case Ktx1Bundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10;
case Ktx1Bundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12;
case Ktx1Bundle::R11_EAC: return T::EAC_R11;
case Ktx1Bundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED;
case Ktx1Bundle::RG11_EAC: return T::EAC_RG11;
case Ktx1Bundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED;
case Ktx1Bundle::RGB8_ETC2: return T::ETC2_RGB8;
case Ktx1Bundle::SRGB8_ETC2: return T::ETC2_SRGB8;
case Ktx1Bundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1;
case Ktx1Bundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1;
case Ktx1Bundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8;
case Ktx1Bundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8;
}
return (T) 0xffff;
}
Texture* createTexture(Engine* engine, const Ktx1Bundle& ktx, bool srgb,
Callback callback, void* userdata) {
using Sampler = Texture::Sampler;
@@ -82,11 +33,16 @@ Texture* createTexture(Engine* engine, const Ktx1Bundle& ktx, bool srgb,
const auto dataformat = toPixelDataFormat(ktxinfo);
auto texformat = toTextureFormat(ktxinfo);
#ifndef NDEBUG
if (srgb && !isSrgbTextureFormat(texformat)) {
utils::slog.w << "Requested sRGB format but KTX contains a linear format. ";
utils::slog.w << "Requested sRGB format but KTX contains a linear format. "
<< utils::io::endl;
} else if (!srgb && isSrgbTextureFormat(texformat)) {
utils::slog.w << "Requested linear format but KTX contains a sRGB format. ";
utils::slog.w << "Requested linear format but KTX contains a sRGB format. "
<< utils::io::endl;
}
#endif
Texture* texture = Texture::Builder()
.width(ktxinfo.pixelWidth)
+232 -93
View File
@@ -21,6 +21,8 @@
#include <utils/Log.h>
#include <vector>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warray-bounds"
#include <basisu_transcoder.h>
@@ -30,9 +32,13 @@ using namespace basist;
using namespace filament;
using TransferFunction = ktxreader::Ktx2Reader::TransferFunction;
using Result = ktxreader::Ktx2Reader::Result;
using Async = ktxreader::Ktx2Reader::Async;
using Buffer = std::vector<uint8_t>;
namespace {
struct FinalFormatInfo {
const char* name; // <-- for debug purposes only
bool isSupported;
bool isCompressed;
TransferFunction transferFunction;
@@ -73,35 +79,118 @@ static FinalFormatInfo getFinalFormatInfo(Texture::InternalFormat fmt) {
using tt = Texture::Type;
using tf = Texture::Format;
using ttf = transcoder_texture_format;
const auto sRGB = ktxreader::Ktx2Reader::TransferFunction::sRGB;
const auto LINEAR = ktxreader::Ktx2Reader::TransferFunction::LINEAR;
const auto sRGB = TransferFunction::sRGB;
const auto LINEAR = TransferFunction::LINEAR;
switch (fmt) {
case tif::ETC2_EAC_SRGBA8: return {true, true, sRGB, ttf::cTFETC2_RGBA, tct::ETC2_EAC_RGBA8};
case tif::ETC2_EAC_RGBA8: return {true, true, LINEAR, ttf::cTFETC2_RGBA, tct::ETC2_EAC_SRGBA8};
case tif::DXT1_SRGB: return {true, true, sRGB, ttf::cTFBC1_RGB, tct::DXT1_RGB};
case tif::DXT1_RGB: return {true, true, LINEAR, ttf::cTFBC1_RGB, tct::DXT1_SRGB};
case tif::DXT3_SRGBA: return {true, true, sRGB, ttf::cTFBC3_RGBA, tct::DXT3_RGBA};
case tif::DXT3_RGBA: return {true, true, LINEAR, ttf::cTFBC3_RGBA, tct::DXT3_SRGBA};
case tif::SRGB8_ALPHA8_ASTC_4x4: return {true, true, sRGB, ttf::cTFASTC_4x4_RGBA, tct::RGBA_ASTC_4x4};
case tif::RGBA_ASTC_4x4: return {true, true, LINEAR, ttf::cTFASTC_4x4_RGBA, tct::SRGB8_ALPHA8_ASTC_4x4};
case tif::EAC_R11: return {true, true, LINEAR, ttf::cTFETC2_EAC_R11, tct::EAC_R11};
case tif::ETC2_EAC_SRGBA8: return {"ETC2_EAC_SRGBA8", true, true, sRGB, ttf::cTFETC2_RGBA, tct::ETC2_EAC_RGBA8};
case tif::ETC2_EAC_RGBA8: return {"ETC2_EAC_RGBA8", true, true, LINEAR, ttf::cTFETC2_RGBA, tct::ETC2_EAC_SRGBA8};
case tif::DXT1_SRGB: return {"DXT1_SRGB", true, true, sRGB, ttf::cTFBC1_RGB, tct::DXT1_RGB};
case tif::DXT1_RGB: return {"DXT1_RGB", true, true, LINEAR, ttf::cTFBC1_RGB, tct::DXT1_SRGB};
case tif::DXT5_SRGBA: return {"DXT5_SRGBA", true, true, sRGB, ttf::cTFBC3_RGBA, tct::DXT5_RGBA};
case tif::DXT5_RGBA: return {"DXT5_RGBA", true, true, LINEAR, ttf::cTFBC3_RGBA, tct::DXT5_SRGBA};
case tif::SRGB8_ALPHA8_ASTC_4x4: return {"SRGB8_ALPHA8_ASTC_4x4", true, true, sRGB, ttf::cTFASTC_4x4_RGBA, tct::RGBA_ASTC_4x4};
case tif::RGBA_ASTC_4x4: return {"RGBA_ASTC_4x4", true, true, LINEAR, ttf::cTFASTC_4x4_RGBA, tct::SRGB8_ALPHA8_ASTC_4x4};
case tif::EAC_R11: return {"EAC_R11", true, true, LINEAR, ttf::cTFETC2_EAC_R11, tct::EAC_R11};
// The following format is useful for normal maps.
// Note that BasisU supports only the unsigned variant.
case tif::EAC_RG11: return {true, true, LINEAR, ttf::cTFETC2_EAC_RG11, tct::EAC_RG11};
case tif::EAC_RG11: return {"EAC_RG11", true, true, LINEAR, ttf::cTFETC2_EAC_RG11, tct::EAC_RG11};
// Uncompressed formats.
case tif::SRGB8_A8: return {true, false, sRGB, ttf::cTFRGBA32, {}, tt::UBYTE, tf::RGBA};
case tif::RGBA8: return {true, false, LINEAR, ttf::cTFRGBA32, {}, tt::UBYTE, tf::RGBA};
case tif::RGB565: return {true, false, LINEAR, ttf::cTFRGB565, {}, tt::USHORT_565, tf::RGB};
case tif::RGBA4: return {true, false, LINEAR, ttf::cTFRGBA4444, {}, tt::USHORT, tf::RGBA};
case tif::SRGB8_A8: return {"SRGB8_A8", true, false, sRGB, ttf::cTFRGBA32, {}, tt::UBYTE, tf::RGBA};
case tif::RGBA8: return {"RGBA8", true, false, LINEAR, ttf::cTFRGBA32, {}, tt::UBYTE, tf::RGBA};
case tif::RGB565: return {"RGB565", true, false, LINEAR, ttf::cTFRGB565, {}, tt::USHORT_565, tf::RGB};
case tif::RGBA4: return {"RGBA4", true, false, LINEAR, ttf::cTFRGBA4444, {}, tt::USHORT, tf::RGBA};
default: return {false};
default: return {};
}
}
// In theory we could pass "free" directly into the callback but doing so triggers ASAN warnings.
static void freeCallback(void* buf, size_t, void* userdata) {
free(buf);
}
// This helper is used by both the asynchronous and synchronous API's.
static Result transcodeImageLevel(ktx2_transcoder& transcoder,
ktx2_transcoder_state& transcoderState, Texture::InternalFormat format,
uint32_t levelIndex, Texture::PixelBufferDescriptor** pbd) {
using basisu::texture_format;
assert_invariant(levelIndex < KTX2_MAX_SUPPORTED_LEVEL_COUNT);
const FinalFormatInfo formatInfo = getFinalFormatInfo(format);
const texture_format destFormat = basis_get_basisu_texture_format(formatInfo.basisFormat);
const uint32_t layerIndex = 0;
const uint32_t faceIndex = 0;
const uint32_t decodeFlags = 0;
const uint32_t outputRowPitch = 0;
const uint32_t outputRowCount = 0;
const int channel0 = 0;
const int channel1 = 0;
basist::ktx2_image_level_info levelInfo;
transcoder.get_image_level_info(levelInfo, levelIndex, layerIndex, faceIndex);
if (formatInfo.isCompressed) {
const uint32_t qwordsPerBlock = basisu::get_qwords_per_block(destFormat);
const size_t byteCount = sizeof(uint64_t) * qwordsPerBlock * levelInfo.m_total_blocks;
uint64_t* const blocks = (uint64_t*) malloc(byteCount);
if (!transcoder.transcode_image_level(levelIndex, layerIndex, faceIndex, blocks,
levelInfo.m_total_blocks, formatInfo.basisFormat, decodeFlags,
outputRowPitch, outputRowCount, channel0,
channel1, &transcoderState)) {
return Result::COMPRESSED_TRANSCODE_FAILURE;
}
*pbd = new Texture::PixelBufferDescriptor(blocks,
byteCount, formatInfo.compressedPixelDataType, byteCount, freeCallback);
return Result::SUCCESS;
}
const uint32_t rowCount = levelInfo.m_orig_height;
const uint32_t bytesPerPix = basis_get_bytes_per_block_or_pixel(formatInfo.basisFormat);
const size_t byteCount = bytesPerPix * levelInfo.m_orig_width * rowCount;
uint64_t* const rows = (uint64_t*) malloc(byteCount);
if (!transcoder.transcode_image_level(levelIndex, layerIndex, faceIndex, rows,
byteCount / bytesPerPix, formatInfo.basisFormat, decodeFlags,
outputRowPitch, outputRowCount, channel0, channel1, &transcoderState)) {
return Result::UNCOMPRESSED_TRANSCODE_FAILURE;
}
*pbd = new Texture::PixelBufferDescriptor(rows, byteCount,
formatInfo.pixelDataFormat, formatInfo.pixelDataType, freeCallback);
return Result::SUCCESS;
}
namespace ktxreader {
class FAsync : public Async {
public:
FAsync(Texture* texture, Engine& engine, ktx2_transcoder* transcoder, Buffer&& buf) :
mTexture(texture), mEngine(engine), mTranscoder(transcoder),
mSourceBuffer(std::move(buf)) {}
Texture* getTexture() const noexcept { return mTexture; }
Result doTranscoding();
void uploadImages();
private:
using TranscoderResult = std::atomic<Texture::PixelBufferDescriptor*>;
// After each level is transcoded, the results are stashed in the following array until the
// foreground thread calls uploadImages(). Each slot in the array corresponds to a single
// miplevel in the texture.
TranscoderResult mTranscoderResults[KTX2_MAX_SUPPORTED_LEVEL_COUNT] = {};
Texture* const mTexture;
Engine& mEngine;
// We do not share the BasisU trancoder between Async objects. The BasisU transcoder
// allows parallelization at "level" granularity, but does not permit parallelization at
// "texture" granularity. i.e. the transcode_image_level() method is thread-safe but the
// start_transcoding() method is not.
std::unique_ptr<ktx2_transcoder> const mTranscoder;
// Storage for the content of the KTX2 file.
Buffer mSourceBuffer;
};
Ktx2Reader::Ktx2Reader(Engine& engine, bool quiet) :
mEngine(engine),
mQuiet(quiet),
@@ -114,20 +203,20 @@ Ktx2Reader::~Ktx2Reader() {
delete mTranscoder;
}
bool Ktx2Reader::requestFormat(Texture::InternalFormat format) {
Result Ktx2Reader::requestFormat(Texture::InternalFormat format) noexcept {
if (!getFinalFormatInfo(format).isSupported) {
return false;
return Result::FORMAT_UNSUPPORTED;
}
for (Texture::InternalFormat fmt : mRequestedFormats) {
if (fmt == format) {
return false;
return Result::FORMAT_ALREADY_REQUESTED;
}
}
mRequestedFormats.push_back(format);
return true;
return Result::SUCCESS;
}
void Ktx2Reader::unrequestFormat(Texture::InternalFormat format) {
void Ktx2Reader::unrequestFormat(Texture::InternalFormat format) noexcept {
for (auto iter = mRequestedFormats.begin(); iter != mRequestedFormats.end(); ++iter) {
if (*iter == format) {
mRequestedFormats.erase(iter);
@@ -136,15 +225,102 @@ void Ktx2Reader::unrequestFormat(Texture::InternalFormat format) {
}
}
Texture* Ktx2Reader::load(const uint8_t* data, size_t size, TransferFunction transfer) {
if (!mTranscoder->init(data, size)) {
Texture* Ktx2Reader::load(const void* data, size_t size, TransferFunction transfer) {
Texture* texture = createTexture(mTranscoder, data, size, transfer);
if (texture == nullptr) {
return nullptr;
}
if (!mTranscoder->start_transcoding()) {
mEngine.destroy(texture);
if (!mQuiet) {
utils::slog.e << "BasisU start_transcoding failed." << utils::io::endl;
}
return nullptr;
}
ktx2_transcoder_state basisThreadState;
basisThreadState.clear();
for (uint32_t levelIndex = 0, n = mTranscoder->get_levels(); levelIndex < n; levelIndex++) {
Texture::PixelBufferDescriptor* pbd;
Result result = transcodeImageLevel(*mTranscoder, basisThreadState, texture->getFormat(),
levelIndex, &pbd);
if (UTILS_UNLIKELY(result != Result::SUCCESS)) {
mEngine.destroy(texture);
if (!mQuiet) {
utils::slog.e << "Failed to transcode level " << levelIndex << utils::io::endl;
}
return nullptr;
}
texture->setImage(mEngine, levelIndex, std::move(*pbd));
}
return texture;
}
Result FAsync::doTranscoding() {
ktx2_transcoder_state basisThreadState;
basisThreadState.clear();
for (uint32_t levelIndex = 0, n = mTranscoder->get_levels(); levelIndex < n; levelIndex++) {
Texture::PixelBufferDescriptor* pbd;
Result result = transcodeImageLevel(*mTranscoder, basisThreadState, mTexture->getFormat(),
levelIndex, &pbd);
if (UTILS_UNLIKELY(result != Result::SUCCESS)) {
return result;
}
mTranscoderResults[levelIndex].store(pbd);
}
return Result::SUCCESS;
}
void FAsync::uploadImages() {
size_t levelIndex = 0;
UTILS_NOUNROLL
for (TranscoderResult& level : mTranscoderResults) {
Texture::PixelBufferDescriptor* pbd = level.load();
if (pbd) {
level.store(nullptr);
mTexture->setImage(mEngine, levelIndex, std::move(*pbd));
delete pbd;
}
++levelIndex;
}
}
Async* Ktx2Reader::asyncCreate(const void* data, size_t size, TransferFunction transfer) {
Buffer ktx2content((uint8_t*)data, (uint8_t*)data + size);
ktx2_transcoder* transcoder = new ktx2_transcoder();
Texture* texture = createTexture(transcoder, ktx2content.data(), ktx2content.size(), transfer);
if (texture == nullptr) {
delete transcoder;
return nullptr;
}
if (!transcoder->start_transcoding()) {
delete transcoder;
mEngine.destroy(texture);
return nullptr;
}
// There's no need to do any further work at this point but it should be noted that this is the
// point at which we first come to know the number of miplevels, dimensions, etc. If we had a
// dynamically sized array to store decoder results, we would reserve it here.
return new FAsync(texture, mEngine, transcoder, std::move(ktx2content));
}
void Ktx2Reader::asyncDestroy(Async** async) {
delete *async;
*async = nullptr;
}
Texture* Ktx2Reader::createTexture(ktx2_transcoder* transcoder, const void* data, size_t size,
TransferFunction transfer) {
if (!transcoder->init(data, size)) {
if (!mQuiet) {
utils::slog.e << "BasisU transcoder init failed." << utils::io::endl;
}
return nullptr;
}
if (mTranscoder->get_dfd_transfer_func() == KTX2_KHR_DF_TRANSFER_LINEAR &&
if (transcoder->get_dfd_transfer_func() == KTX2_KHR_DF_TRANSFER_LINEAR &&
transfer == TransferFunction::sRGB) {
if (!mQuiet) {
utils::slog.e << "Source texture is marked linear, but client is requesting sRGB."
@@ -153,7 +329,7 @@ Texture* Ktx2Reader::load(const uint8_t* data, size_t size, TransferFunction tra
return nullptr;
}
if (mTranscoder->get_dfd_transfer_func() == KTX2_KHR_DF_TRANSFER_SRGB &&
if (transcoder->get_dfd_transfer_func() == KTX2_KHR_DF_TRANSFER_SRGB &&
transfer == TransferFunction::LINEAR) {
if (!mQuiet) {
utils::slog.e << "Source texture is marked sRGB, but client is requesting linear."
@@ -163,7 +339,7 @@ Texture* Ktx2Reader::load(const uint8_t* data, size_t size, TransferFunction tra
}
// TODO: support cubemaps. For now we use KTX1 for cubemaps because basisu does not support HDR.
if (mTranscoder->get_faces() == 6) {
if (transcoder->get_faces() == 6) {
if (!mQuiet) {
utils::slog.e << "Cubemaps are not yet supported." << utils::io::endl;
}
@@ -171,32 +347,33 @@ Texture* Ktx2Reader::load(const uint8_t* data, size_t size, TransferFunction tra
}
// TODO: support texture arrays.
if (mTranscoder->get_layers() > 1) {
if (transcoder->get_layers() > 1) {
if (!mQuiet) {
utils::slog.e << "Texture arrays are not yet supported." << utils::io::endl;
}
return nullptr;
}
// Fierst pass through, just to make sure we can transcode it.
// First pass through, just to make sure we can transcode it.
bool found = false;
Texture::InternalFormat resolvedFormat;
FinalFormatInfo info;
for (Texture::InternalFormat requestedFormat : mRequestedFormats) {
if (!Texture::isTextureFormatSupported(mEngine, requestedFormat)) {
continue;
}
const auto info = getFinalFormatInfo(requestedFormat);
info = getFinalFormatInfo(requestedFormat);
if (!info.isSupported || info.transferFunction != transfer) {
continue;
}
if (!basis_is_format_supported(info.basisFormat, mTranscoder->get_format())) {
if (!basis_is_format_supported(info.basisFormat, transcoder->get_format())) {
continue;
}
const uint32_t layerIndex = 0;
const uint32_t faceIndex = 0;
for (uint32_t levelIndex = 0; levelIndex < mTranscoder->get_levels(); levelIndex++) {
for (uint32_t levelIndex = 0; levelIndex < transcoder->get_levels(); levelIndex++) {
basist::ktx2_image_level_info info;
if (!mTranscoder->get_image_level_info(info, levelIndex, layerIndex, faceIndex)) {
if (!transcoder->get_image_level_info(info, levelIndex, layerIndex, faceIndex)) {
continue;
}
}
@@ -212,75 +389,37 @@ Texture* Ktx2Reader::load(const uint8_t* data, size_t size, TransferFunction tra
return nullptr;
}
const auto formatInfo = getFinalFormatInfo(resolvedFormat);
Texture* texture = Texture::Builder()
.width(mTranscoder->get_width())
.height(mTranscoder->get_height())
.levels(mTranscoder->get_levels())
.width(transcoder->get_width())
.height(transcoder->get_height())
.levels(transcoder->get_levels())
.sampler(Texture::Sampler::SAMPLER_2D)
.format(resolvedFormat)
.build(mEngine);
if (texture == nullptr) {
if (!mQuiet) {
utils::slog.e << "Unable to construct texture using BasisU info." << utils::io::endl;
}
return nullptr;
if (texture == nullptr && !mQuiet) {
utils::slog.e << "Unable to construct texture using BasisU info." << utils::io::endl;
}
if (!mTranscoder->start_transcoding()) {
mEngine.destroy(texture);
if (!mQuiet) {
utils::slog.e << "BasisU start_transcoding failed." << utils::io::endl;
}
return nullptr;
}
// In theory we could pass "free" directly into the callback but that triggers ASAN warnings.
Texture::PixelBufferDescriptor::Callback cb = [](void* buf, size_t, void* userdata) {
free(buf);
};
const uint32_t layerIndex = 0;
const uint32_t faceIndex = 0;
for (uint32_t levelIndex = 0; levelIndex < mTranscoder->get_levels(); levelIndex++) {
basist::ktx2_image_level_info levelInfo;
mTranscoder->get_image_level_info(levelInfo, levelIndex, layerIndex, faceIndex);
const basisu::texture_format destFormat =
basis_get_basisu_texture_format(formatInfo.basisFormat);
if (formatInfo.isCompressed) {
const uint32_t qwordsPerBlock = basisu::get_qwords_per_block(destFormat);
const size_t byteCount = sizeof(uint64_t) * qwordsPerBlock * levelInfo.m_total_blocks;
uint64_t* const blocks = (uint64_t*) malloc(byteCount);
const uint32_t flags = 0;
if (!mTranscoder->transcode_image_level(levelIndex, layerIndex, faceIndex, blocks,
levelInfo.m_total_blocks, formatInfo.basisFormat, flags)) {
utils::slog.e << "Failed to transcode level " << levelIndex << utils::io::endl;
return nullptr;
}
Texture::PixelBufferDescriptor pbd(blocks, byteCount,
formatInfo.compressedPixelDataType, byteCount, cb, nullptr);
texture->setImage(mEngine, levelIndex, std::move(pbd));
} else {
// The transcoder still does work even for uncompressed formats, because of zstd.
const uint32_t rowCount = levelInfo.m_orig_height;
const uint32_t bytesPerPix = basis_get_bytes_per_block_or_pixel(formatInfo.basisFormat);
const size_t byteCount = bytesPerPix * levelInfo.m_orig_width * rowCount;
uint64_t* const rows = (uint64_t*) malloc(byteCount);
const uint32_t flags = 0;
if (!mTranscoder->transcode_image_level(levelIndex, layerIndex, faceIndex, rows,
byteCount / bytesPerPix, formatInfo.basisFormat, flags)) {
utils::slog.e << "Failed to transcode level " << levelIndex << utils::io::endl;
return nullptr;
}
Texture::PixelBufferDescriptor pbd(rows, byteCount, formatInfo.pixelDataFormat,
formatInfo.pixelDataType, cb, nullptr);
texture->setImage(mEngine, levelIndex, std::move(pbd));
}
}
#if BASISU_FORCE_DEVEL_MESSAGES
utils::slog.e << "Ktx2Reader created "
<< transcoder->get_width() << "x" << transcoder->get_height() << " texture with format "
<< info.name << utils::io::endl;
#endif
return texture;
}
Texture* Async::getTexture() const noexcept {
return static_cast<FAsync const*>(this)->getTexture();
}
Result Async::doTranscoding() {
return static_cast<FAsync*>(this)->doTranscoding();
}
void Async::uploadImages() {
return static_cast<FAsync*>(this)->uploadImages();
}
} // namespace ktxreader
+3 -3
View File
@@ -73,8 +73,8 @@ TEST_F(KtxReaderTest, Ktx2) {
ktxreader::Ktx2Reader reader(*engine);
reader.requestFormat(Texture::InternalFormat::DXT3_SRGBA);
reader.requestFormat(Texture::InternalFormat::DXT3_RGBA);
reader.requestFormat(Texture::InternalFormat::DXT5_SRGBA);
reader.requestFormat(Texture::InternalFormat::DXT5_RGBA);
// Uncompressed formats are lower priority, so they get added last.
reader.requestFormat(Texture::InternalFormat::SRGB8_A8);
@@ -84,7 +84,7 @@ TEST_F(KtxReaderTest, Ktx2) {
ktxreader::Ktx2Reader::TransferFunction::sRGB);
ASSERT_NE(tex, nullptr);
ASSERT_EQ(tex->getFormat(), Texture::InternalFormat::DXT3_SRGBA);
ASSERT_EQ(tex->getFormat(), Texture::InternalFormat::DXT5_SRGBA);
ASSERT_EQ(tex->getWidth(), 1024);
engine->destroy(tex);
+1
View File
@@ -498,6 +498,7 @@ struct HighWatermark {
void onFree(void* p, size_t size) noexcept;
void onReset() noexcept;
void onRewind(void const* addr) noexcept;
uint32_t getHighWatermark() const noexcept { return mHighWaterMark; }
protected:
const char* mName = nullptr;
void* mBase = nullptr;
+2 -2
View File
@@ -12,7 +12,7 @@ set(PUBLIC_HDRS
include/viewer/AutomationSpec.h
include/viewer/RemoteServer.h
include/viewer/Settings.h
include/viewer/SimpleViewer.h
include/viewer/ViewerGui.h
)
set(SRCS
@@ -21,7 +21,7 @@ set(SRCS
src/AutomationSpec.cpp
src/RemoteServer.cpp
src/Settings.cpp
src/SimpleViewer.cpp
src/ViewerGui.cpp
)
# ==================================================================================================
@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef VIEWER_SIMPLEVIEWER_H
#define VIEWER_SIMPLEVIEWER_H
#ifndef VIEWER_VIEWERGUI_H
#define VIEWER_VIEWERGUI_H
#include <filament/Box.h>
#include <filament/DebugRegistry.h>
@@ -45,15 +45,15 @@ namespace filament {
namespace viewer {
/**
* \class SimpleViewer SimpleViewer.h viewer/SimpleViewer.h
* \brief Manages the state for a simple glTF viewer with imgui controls and a tree view.
* \class ViewerGui ViewerGui.h viewer/ViewerGui.h
* \brief Builds ImGui widgets for a simple glTF viewer and manages the associated state.
*
* This is a utility that can be used across multiple platforms, including web.
*
* \note If you don't need ImGui controls, there is no need to use this class, just use AssetLoader
* instead.
*/
class UTILS_PUBLIC SimpleViewer {
class UTILS_PUBLIC ViewerGui {
public:
using Animator = gltfio::Animator;
using FilamentAsset = gltfio::FilamentAsset;
@@ -62,18 +62,18 @@ public:
static constexpr int DEFAULT_SIDEBAR_WIDTH = 350;
/**
* Constructs a SimpleViewer that has a fixed association with the given Filament objects.
* Constructs a ViewerGui that has a fixed association with the given Filament objects.
*
* Upon construction, the simple viewer may create some additional Filament objects (such as
* light sources) that it owns.
*/
SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
ViewerGui(filament::Engine* engine, filament::Scene* scene, filament::View* view,
int sidebarWidth = DEFAULT_SIDEBAR_WIDTH);
/**
* Destroys the SimpleViewer and any Filament entities that it owns.
* Destroys the ViewerGui and any Filament entities that it owns.
*/
~SimpleViewer();
~ViewerGui();
/**
* Adds the asset's ready-to-render entities into the scene.
@@ -126,7 +126,7 @@ public:
void renderUserInterface(float timeStepInSeconds, filament::View* guiView, float pixelRatio);
/**
* Event-passing methods, useful only when SimpleViewer manages its own instance of ImGuiHelper.
* Event-passing methods, useful only when ViewerGui manages its own instance of ImGuiHelper.
* The key codes used in these methods are just normal ASCII/ANSI codes.
* @{
*/
@@ -265,4 +265,4 @@ filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds, float zoffse
} // namespace viewer
} // namespace filament
#endif // VIEWER_SIMPLEVIEWER_H
#endif // VIEWER_VIEWERGUI_H
@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include <viewer/SimpleViewer.h>
#include <viewer/ViewerGui.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
@@ -359,7 +359,7 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo
}
}
SimpleViewer::SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view,
ViewerGui::ViewerGui(filament::Engine* engine, filament::Scene* scene, filament::View* view,
int sidebarWidth) :
mEngine(engine), mScene(scene), mView(view),
mSunlight(utils::EntityManager::get().create()),
@@ -389,12 +389,12 @@ SimpleViewer::SimpleViewer(filament::Engine* engine, filament::Scene* scene, fil
view->setAmbientOcclusionOptions({ .upsampling = View::QualityLevel::HIGH });
}
SimpleViewer::~SimpleViewer() {
ViewerGui::~ViewerGui() {
mEngine->destroy(mSunlight);
delete mImGuiHelper;
}
void SimpleViewer::populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate) {
void ViewerGui::populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate) {
if (mAsset != asset) {
removeAsset();
mAsset = asset;
@@ -421,7 +421,7 @@ void SimpleViewer::populateScene(FilamentAsset* asset, FilamentInstance* instan
}
}
void SimpleViewer::removeAsset() {
void ViewerGui::removeAsset() {
if (!isRemoteMode()) {
mScene->removeEntities(mAsset->getEntities(), mAsset->getEntityCount());
mAsset = nullptr;
@@ -429,7 +429,7 @@ void SimpleViewer::removeAsset() {
}
}
void SimpleViewer::setIndirectLight(filament::IndirectLight* ibl,
void ViewerGui::setIndirectLight(filament::IndirectLight* ibl,
filament::math::float3 const* sh3) {
using namespace filament::math;
mSettings.view.fog.color = sh3[0];
@@ -446,7 +446,7 @@ void SimpleViewer::setIndirectLight(filament::IndirectLight* ibl,
}
}
void SimpleViewer::updateRootTransform() {
void ViewerGui::updateRootTransform() {
if (isRemoteMode()) {
return;
}
@@ -459,7 +459,7 @@ void SimpleViewer::updateRootTransform() {
tcm.setTransform(root, transform);
}
void SimpleViewer::updateIndirectLight() {
void ViewerGui::updateIndirectLight() {
using namespace filament::math;
if (mIndirectLight) {
mIndirectLight->setIntensity(mSettings.lighting.iblIntensity);
@@ -467,7 +467,7 @@ void SimpleViewer::updateIndirectLight() {
}
}
void SimpleViewer::applyAnimation(double currentTime) {
void ViewerGui::applyAnimation(double currentTime) {
assert_invariant(!isRemoteMode());
static double startTime = 0;
const size_t numAnimations = mAnimator->getAnimationCount();
@@ -488,7 +488,7 @@ void SimpleViewer::applyAnimation(double currentTime) {
}
}
void SimpleViewer::renderUserInterface(float timeStepInSeconds, View* guiView, float pixelRatio) {
void ViewerGui::renderUserInterface(float timeStepInSeconds, View* guiView, float pixelRatio) {
if (mImGuiHelper == nullptr) {
mImGuiHelper = new ImGuiHelper(mEngine, guiView, "");
@@ -529,7 +529,7 @@ void SimpleViewer::renderUserInterface(float timeStepInSeconds, View* guiView, f
});
}
void SimpleViewer::mouseEvent(float mouseX, float mouseY, bool mouseButton, float mouseWheelY,
void ViewerGui::mouseEvent(float mouseX, float mouseY, bool mouseButton, float mouseWheelY,
bool control) {
if (mImGuiHelper) {
ImGuiIO& io = ImGui::GetIO();
@@ -543,25 +543,25 @@ void SimpleViewer::mouseEvent(float mouseX, float mouseY, bool mouseButton, floa
}
}
void SimpleViewer::keyDownEvent(int keyCode) {
void ViewerGui::keyDownEvent(int keyCode) {
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
ImGui::GetIO().KeysDown[keyCode] = true;
}
}
void SimpleViewer::keyUpEvent(int keyCode) {
void ViewerGui::keyUpEvent(int keyCode) {
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
ImGui::GetIO().KeysDown[keyCode] = false;
}
}
void SimpleViewer::keyPressEvent(int charCode) {
void ViewerGui::keyPressEvent(int charCode) {
if (mImGuiHelper) {
ImGui::GetIO().AddInputCharacter(charCode);
}
}
void SimpleViewer::updateUserInterface() {
void ViewerGui::updateUserInterface() {
using namespace filament;
auto& tm = mEngine->getTransformManager();
@@ -906,7 +906,7 @@ void SimpleViewer::updateUserInterface() {
// We do not yet support camera selection in the remote UI. To support this feature, we
// would need to send a message from DebugServer to the WebSockets client.
if (isRemoteMode()) {
if (!isRemoteMode()) {
const utils::Entity* cameras = mAsset->getCameraEntities();
const size_t cameraCount = mAsset->getCameraEntityCount();
+3 -3
View File
@@ -28,7 +28,7 @@
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <viewer/SimpleViewer.h>
#include <viewer/ViewerGui.h>
#include <camutils/Manipulator.h>
@@ -58,7 +58,7 @@ enum MaterialSource {
struct App {
Engine* engine;
SimpleViewer* viewer;
ViewerGui* viewer;
Config config;
AssetLoader* loader;
FilamentAsset* asset = nullptr;
@@ -242,7 +242,7 @@ int main(int argc, char** argv) {
auto setup = [&](Engine* engine, View* view, Scene* scene) {
app.engine = engine;
app.names = new NameComponentManager(EntityManager::get());
app.viewer = new SimpleViewer(engine, scene, view, app.instanceToAnimate);
app.viewer = new ViewerGui(engine, scene, view, app.instanceToAnimate);
app.materials = (app.materialSource == GENERATE_SHADERS) ?
createMaterialGenerator(engine) : createUbershaderLoader(engine);
app.loader = AssetLoader::create({engine, app.materials, app.names });
+13 -8
View File
@@ -37,7 +37,7 @@
#include <viewer/AutomationEngine.h>
#include <viewer/AutomationSpec.h>
#include <viewer/SimpleViewer.h>
#include <viewer/ViewerGui.h>
#include <camutils/Manipulator.h>
@@ -73,7 +73,7 @@ enum MaterialSource {
struct App {
Engine* engine;
SimpleViewer* viewer;
ViewerGui* viewer;
Config config;
Camera* mainCamera;
@@ -85,7 +85,8 @@ struct App {
MaterialSource materialSource = GENERATE_SHADERS;
gltfio::ResourceLoader* resourceLoader = nullptr;
gltfio::TextureProvider* decoder = nullptr;
gltfio::TextureProvider* stbDecoder = nullptr;
gltfio::TextureProvider* ktxDecoder = nullptr;
bool recomputeAabb = false;
bool ignoreBindTransform = false;
@@ -425,9 +426,11 @@ int main(int argc, char** argv) {
configuration.normalizeSkinningWeights = true;
if (!app.resourceLoader) {
app.resourceLoader = new gltfio::ResourceLoader(configuration);
app.decoder = createStbProvider(app.engine);
app.resourceLoader->addTextureProvider("image/png", app.decoder);
app.resourceLoader->addTextureProvider("image/jpeg", app.decoder);
app.stbDecoder = createStbProvider(app.engine);
app.ktxDecoder = createKtx2Provider(app.engine);
app.resourceLoader->addTextureProvider("image/png", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/jpeg", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/ktx2", app.ktxDecoder);
}
app.resourceLoader->asyncBeginLoad(app.asset);
app.asset->releaseSourceData();
@@ -441,7 +444,7 @@ int main(int argc, char** argv) {
auto setup = [&](Engine* engine, View* view, Scene* scene) {
app.engine = engine;
app.names = new NameComponentManager(EntityManager::get());
app.viewer = new SimpleViewer(engine, scene, view, 410);
app.viewer = new ViewerGui(engine, scene, view, 410);
app.viewer->getSettings().viewer.autoScaleEnabled = !app.actualSize;
const bool batchMode = !app.batchFile.empty();
@@ -580,6 +583,7 @@ int main(int argc, char** argv) {
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
*captureFrame = true;
}
ImGui::Checkbox("Camera at origin", debug.getPropertyAddress<bool>("d.view.camera_at_origin"));
auto dataSource = debug.getDataSource("d.view.frame_info");
if (dataSource.data) {
ImGuiExt::PlotLinesSeries("FrameInfo", 6,
@@ -649,7 +653,8 @@ int main(int argc, char** argv) {
delete app.viewer;
delete app.materials;
delete app.names;
delete app.decoder;
delete app.stbDecoder;
delete app.ktxDecoder;
AssetLoader::destroy(&app.assetLoader);
};
+3 -3
View File
@@ -32,7 +32,7 @@
#include <utils/EntityManager.h>
#include <viewer/SimpleViewer.h>
#include <viewer/ViewerGui.h>
#include <camutils/Manipulator.h>
@@ -63,7 +63,7 @@ using namespace utils;
struct App {
Engine* engine;
SimpleViewer* viewer;
ViewerGui* viewer;
Config config;
Camera* mainCamera;
@@ -290,7 +290,7 @@ int main(int argc, char** argv) {
auto setup = [&](Engine* engine, View* view, Scene* scene) {
app.engine = engine;
app.viewer = new SimpleViewer(engine, scene, view, 410);
app.viewer = new ViewerGui(engine, scene, view, 410);
app.viewer->getSettings().viewer.autoScaleEnabled = false;
app.viewer->getSettings().view.bloom.enabled = false;
app.viewer->getSettings().view.ssao.enabled = false;
+3 -3
View File
@@ -14,13 +14,13 @@ set(SHADERS
src/ambient_occlusion.fs
src/attributes.vs
src/brdf.fs
src/common_getters.fs
src/common_getters.glsl
src/common_graphics.fs
src/common_lighting.fs
src/common_material.fs
src/common_math.fs
src/common_math.glsl
src/common_shading.fs
src/common_shadowing.fs
src/common_shadowing.glsl
src/common_types.fs
src/depth_main.fs
src/fog.fs
@@ -2,10 +2,6 @@
// Uniforms access
//------------------------------------------------------------------------------
#define FILAMENT_OBJECT_SKINNING_ENABLED_BIT 0x1u
#define FILAMENT_OBJECT_MORPHING_ENABLED_BIT 0x2u
#define FILAMENT_OBJECT_CONTACT_SHADOWS_BIT 0x4u
/** @public-api */
highp mat4 getViewFromWorldMatrix() {
return frameUniforms.viewFromWorldMatrix;
@@ -36,6 +32,27 @@ highp mat4 getWorldFromClipMatrix() {
return frameUniforms.worldFromClipMatrix;
}
/** @public-api */
float getTime() {
return frameUniforms.time;
}
/** @public-api */
highp vec4 getUserTime() {
return frameUniforms.userTime;
}
/** @public-api **/
highp float getUserTimeMod(float m) {
return mod(mod(frameUniforms.userTime.x, m) + mod(frameUniforms.userTime.y, m), m);
}
// TODO: below shouldn't be accessible from post-process materials
#define FILAMENT_OBJECT_SKINNING_ENABLED_BIT 0x1u
#define FILAMENT_OBJECT_MORPHING_ENABLED_BIT 0x2u
#define FILAMENT_OBJECT_CONTACT_SHADOWS_BIT 0x4u
/** @public-api */
highp vec4 getResolution() {
return frameUniforms.resolution;
@@ -51,21 +68,6 @@ highp vec3 getWorldOffset() {
return frameUniforms.worldOffset;
}
/** @public-api */
float getTime() {
return frameUniforms.time;
}
/** @public-api */
highp vec4 getUserTime() {
return frameUniforms.userTime;
}
/** @public-api **/
highp float getUserTimeMod(float m) {
return mod(mod(frameUniforms.userTime.x, m) + mod(frameUniforms.userTime.y, m), m);
}
/** @public-api */
float getExposure() {
// NOTE: this is a highp uniform only to work around #3602 (qualcomm)
+14
View File
@@ -88,6 +88,20 @@ vec3 decodeRGBM(vec4 c) {
return c.rgb * c.rgb;
}
//------------------------------------------------------------------------------
// Common screen-space operations
//------------------------------------------------------------------------------
// returns the frag coord in the GL convention with (0, 0) at the bottom-left
// resolution : width, height
highp vec2 getFragCoord(const highp vec2 resolution) {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
return vec2(gl_FragCoord.x, resolution.y - gl_FragCoord.y);
#else
return gl_FragCoord.xy;
#endif
}
//------------------------------------------------------------------------------
// Common debug
//------------------------------------------------------------------------------
@@ -192,7 +192,7 @@ highp mat3 cofactor(const highp mat3 m) {
* Random number between 0 and 1, using interleaved gradient noise.
* w must not be normalized (e.g. window coordinates)
*/
float interleavedGradientNoise(const highp vec2 w) {
float interleavedGradientNoise(highp vec2 w) {
const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189);
return fract(m.z * fract(dot(w, m.xy)));
}
+1 -1
View File
@@ -31,7 +31,7 @@ void main() {
#if defined(MATERIAL_HAS_TRANSPARENT_SHADOW)
// Interleaved gradient noise, see dithering.fs
float noise = fract(52.982919 * fract(dot(vec2(0.06711, 0.00584), gl_FragCoord.xy)));
float noise = interleavedGradientNoise(gl_FragCoord.xy);
if (noise >= alpha) {
discard;
}

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