Compare commits

...

52 Commits

Author SHA1 Message Date
Benjamin Doherty
fe23aa917d Merge branch 'rc/1.9.10' into release 2021-02-01 11:45:47 -08:00
Benjamin Doherty
eb8a29a332 Update RELEASE_NOTES for 1.9.10 2021-02-01 11:45:17 -08:00
Ben Doherty
0626902530 Fix sporatic data race warning seen in Google3 (#3462) 2021-01-27 16:40:16 -08:00
Philip Rideout
042bfe2597 Partial fix for MSVC build. 2020-12-14 12:15:06 -08:00
Ben Doherty
97133f3591 Fix Windows iterator issue in Zip2Iterator and StructureOfArrays (#3322) 2020-12-14 12:14:53 -08:00
Philip Rideout
d06cc4390e filamat: minify struct fields.
This shrinks the arm64 so file by 24 KiB.
2020-12-14 11:19:43 -08:00
Philip Rideout
6047d3235f Remove VSM variant from Skybox material. 2020-12-14 11:19:31 -08:00
Philip Rideout
2cda6e35bd math: reduce template bloat for matrices
This does not change our API, it merely reduces the number of
non-inlined function instantiations appearing in non-optimized binaries.
2020-12-14 11:19:22 -08:00
Philip Rideout
8f8d51e17b Code review fixups for libibl_lite. 2020-12-14 11:19:16 -08:00
Philip Rideout
6919e3b274 libibl: use C callback for progress 2020-12-14 11:19:09 -08:00
Philip Rideout
10bf944410 Add libibl_lite. 2020-12-14 11:19:03 -08:00
Philip Rideout
9a2f6fdb53 Vulkan: change vkWaitForFences usage for SwiftShader.
When passing only 1 fence to vkWaitForFences, the `waitAll` argument
should not have any effect, but SwiftShader seems to skip the wait
when this argument is set to VK_FALSE.

More specifically, the failure to wait in `acquireWorkCommandBuffer`
causes the subsequent destruction of an in-use fence, which causes
a TSAN failure with Google's internal tests.

I am consulting with the SwiftShader team on a real fix, meanwhile
we can commit this easy workaround.

We have 5 usages of vkWaitForFences, one of which uses multiple fences
and should have used VK_TRUE anyway.
2020-11-20 09:37:54 -08:00
Philip Rideout
761977d385 Filament should always bind an IBL texture.
This prevents a SwiftShader crash and/or a slew of "no texture bound"
warnings that would appear when the client provides an IBL without
providing reflections texture, which should be a valid thing to do.

Note that it is okay to declare a sampler in GLSL that never gets bound,
as long as it is never sampled from. Since we always sample from the
IBL specular texture, we should always bind something to it.
2020-11-19 13:28:19 -08:00
Ben Doherty
21248f15b5 Fix, matc crash when building mobile materials (#3296) 2020-11-16 14:37:55 -08:00
Benjamin Doherty
4f32817f6d Bump version to 1.9.10 2020-11-16 12:37:49 -08:00
Benjamin Doherty
cc9e05e711 Merge branch 'rc/1.9.9' into release 2020-11-16 12:34:13 -08:00
Benjamin Doherty
419d68d4db Update RELEASE_NOTES for 1.9.9 2020-11-16 12:17:02 -08:00
Philip Rideout
8450232448 Improve the "unbound texture" warnings.
With Vulkan, this warning would sometimes be a false positive. It could
trigger for internal samplers like `ssao` and `structure`, even though
they were not declared in SPIR-V.

With OpenGL, this warning would never be a false positive because it has
the luxury of calling `glGetUniformLocation`.

This adds a private attribute to our samplers called `strict` that
indicates whether or not a sampler should always have a bound texture.
For now the only strict samplers are the custom ones declared in the
user's material.

At some point I think we should consider adding `spirv-reflect` to our
tree to help with problems like this.
2020-11-12 16:21:13 -08:00
Philip Rideout
cc51726590 Vulkan: improve robustness by providing 1x1 fallback. 2020-11-12 10:36:13 -08:00
Philip Rideout
318e22af51 Fix clear behavior with RenderTarget API.
This fixes a bug seen with client applications that use ClearOptions
instead of Skybox, and one or more offscreen RenderTarget objects.
These apps would see junk pixels because Filament would only clear the
first render target in the frame.

The fix is to factor some the flag-setting logic in `beginFrame()` into
a private method, and call this method from `render()` each time
the user-level RenderTarget has been changed.

I wrote a simple C++ demo to reproduce the issue and to verify that
this fix works.
2020-11-11 09:25:36 -08:00
Philip Rideout
68ac87dc24 NOOP backend should not care about GLSL vs SPIRV.
This fixes errors that would occur when using the NOOP backend with
materials that were built without OpenGL support.
2020-11-10 15:44:01 -08:00
Benjamin Doherty
acb8f00075 Bump version to 1.9.9 2020-11-09 09:32:55 -08:00
Benjamin Doherty
06d9183aaa Merge branch 'rc/1.9.8' into release 2020-11-09 09:28:58 -08:00
Benjamin Doherty
75af25419d Update RELEASE_NOTES for 1.9.8 2020-11-09 09:26:27 -08:00
Benjamin Doherty
f6b90d2a31 Bump version to 1.9.8 2020-11-09 09:21:36 -08:00
Philip Rideout
a3822f4af0 Fix FENCE_WAIT_FOR_EVER in Linux.
The number of infinite nanoseconds was negative because we asked
chrono for a signed integer, so "wait forever" really meant "do not
wait at all".
2020-11-02 16:12:53 -08:00
Benjamin Doherty
bcdad769ff Merge branch 'rc/1.9.7' into release 2020-11-02 11:03:57 -07:00
Benjamin Doherty
be4fb4fdbb Update RELEASE_NOTES for 1.9.7 2020-11-02 10:59:19 -07:00
Benjamin Doherty
65394f6301 Bump version to 1.9.7 2020-10-26 11:34:20 -06:00
Benjamin Doherty
b0beee03bc Merge branch 'rc/1.9.6' into release 2020-10-26 11:29:45 -06:00
Benjamin Doherty
fe1de41b8e Update RELEASE_NOTES for 1.9.6 2020-10-26 11:25:31 -06:00
Benjamin Doherty
a37b431e87 Bump version to 1.9.6 2020-10-19 11:55:13 -06:00
Benjamin Doherty
98107016b9 Merge branch 'rc/1.9.5' into release 2020-10-19 11:51:30 -06:00
Benjamin Doherty
8bccfc2863 Update RELEASE_NOTES for 1.9.5 2020-10-19 11:49:05 -06:00
Benjamin Doherty
f54a0a3452 Fix CocoaPod version 2020-10-13 15:15:02 -06:00
Benjamin Doherty
6778ab0624 Fix CocoaPod version 2020-10-13 15:09:59 -06:00
Benjamin Doherty
269d636785 Bump version to 1.9.5 2020-10-12 12:03:29 -06:00
Benjamin Doherty
39862c91ce Merge branch 'rc/1.9.4' into release 2020-10-12 11:56:24 -06:00
Benjamin Doherty
523f4026b4 Update RELEASE_NOTES for 1.9.4 2020-10-12 11:52:01 -06:00
Benjamin Doherty
a6bf162431 Bump version to 1.9.4 2020-10-07 16:06:23 -06:00
Benjamin Doherty
826e8d181c Merge branch 'rc/1.9.3' into release 2020-10-05 11:36:16 -06:00
Benjamin Doherty
16dfadbba0 Update RELEASE_NOTES for 1.9.3 2020-10-05 11:29:36 -06:00
Benjamin Doherty
5cbb97551f Bump version to 1.9.3 2020-09-28 11:40:31 -06:00
Benjamin Doherty
defee767c3 Merge branch 'rc/1.9.2' into release 2020-09-28 11:27:47 -06:00
Benjamin Doherty
9560318521 Update RELEASE_NOTES for 1.9.2 2020-09-28 11:26:21 -06:00
Benjamin Doherty
ef09feb048 Bump version to 1.9.2 2020-09-21 11:16:53 -06:00
Benjamin Doherty
39f323fe09 Merge branch 'rc/1.9.1' into release 2020-09-21 11:00:07 -06:00
Benjamin Doherty
11b95304ea Merge branch 'release' into rc/1.9.1 2020-09-21 10:59:52 -06:00
Benjamin Doherty
b7c30a7916 Update RELEASE_NOTES for 1.9.1 2020-09-21 10:59:06 -06:00
Benjamin Doherty
4cae48fc77 Bump version to 1.9.1 2020-09-14 11:51:36 -07:00
Benjamin Doherty
d1a93f0557 Update release notes for 1.9.0 2020-09-14 10:54:28 -07:00
Benjamin Doherty
b93059fad7 Bump version to 1.9.0 2020-09-08 10:47:23 -07:00
31 changed files with 671 additions and 259 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.9.9'
implementation 'com.google.android.filament:filament-android:1.9.10'
}
```
@@ -63,7 +63,7 @@ A much smaller alternative to `filamat-android` that can only generate OpenGL sh
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.9.9'
pod 'Filament', '~> 1.9.10'
```
### Snapshots

View File

@@ -5,9 +5,21 @@ A new header is inserted each time a *tag* is created.
## Next release (main branch)
## v.1.9.11
## v1.9.10
- Fix EXC_BAD_INSTRUCTION seen when using headless SwapChains on macOS with OpenGL.
- Introduce libibl_lite library.
- engine: Fix EXC_BAD_INSTRUCTION seen when using headless SwapChains on macOS with OpenGL.
- engine: Add new callback API to SwapChain.
- engine: Fix SwiftShader crash when using an IBL without a reflections texture.
- filamat: Shrink internal Skybox material size.
- filamat: improvements to generated material size.
- filamat: silence spirv-opt warnings in release builds.
- matc: Add fog variant filter.
- matc: Fix crash when building mobile materials.
- math: reduce template bloat for matrices.
- Vulkan: robustness improvements.
## v1.9.9

View File

@@ -17,9 +17,9 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(ibl STATIC IMPORTED)
set_target_properties(ibl PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl.a)
add_library(ibl-lite STATIC IMPORTED)
set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a)
add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
@@ -92,7 +92,7 @@ target_link_libraries(filament-jni
PRIVATE filaflat
PRIVATE filabridge
PRIVATE geometry
PRIVATE ibl
PRIVATE ibl-lite
PRIVATE log
PRIVATE GLESv3
PRIVATE EGL

View File

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

View File

@@ -350,7 +350,7 @@ target_link_libraries(${TARGET} PUBLIC utils)
target_link_libraries(${TARGET} PUBLIC geometry) # TODO: remove this dependency after deprecating VertexBuffer::populateTangentQuaternions
target_link_libraries(${TARGET} PUBLIC filaflat)
target_link_libraries(${TARGET} PUBLIC filabridge)
target_link_libraries(${TARGET} PUBLIC ibl)
target_link_libraries(${TARGET} PUBLIC ibl-lite)
if (FILAMENT_ENABLE_MATDBG)
target_link_libraries(${TARGET} PUBLIC matdbg)

View File

@@ -77,6 +77,10 @@ struct WGLSwapChain {
};
Driver* PlatformWGL::createDriver(void* const sharedGLContext) noexcept {
int result = 0;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
int pixelFormat = 0;
mPfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
@@ -112,7 +116,7 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext) noexcept {
goto error;
}
int pixelFormat = ChoosePixelFormat(whdc, &mPfd);
pixelFormat = ChoosePixelFormat(whdc, &mPfd);
SetPixelFormat(whdc, pixelFormat, &mPfd);
// We need a tmp context to retrieve and call wglCreateContextAttribsARB.
@@ -123,7 +127,7 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext) noexcept {
goto error;
}
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs =
wglCreateContextAttribs =
(PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
mContext = wglCreateContextAttribs(whdc, (HGLRC) sharedGLContext, attribs);
if (!mContext) {
@@ -141,7 +145,7 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext) noexcept {
goto error;
}
int result = bluegl::bind();
result = bluegl::bind();
ASSERT_POSTCONDITION(!result, "Unable to load OpenGL entry points.");
return OpenGLDriverFactory::create(this, sharedGLContext);

View File

@@ -595,7 +595,7 @@ void waitForIdle(VulkanContext& context) {
}
}
if (nfences > 0) {
vkWaitForFences(context.device, nfences, fences, VK_FALSE, ~0ull);
vkWaitForFences(context.device, nfences, fences, VK_TRUE, UINT64_MAX);
}
// Next flush the active command buffer and wait for it to finish.
@@ -638,7 +638,7 @@ bool acquireSwapCommandBuffer(VulkanContext& context) {
// Ensure that the previous submission of this command buffer has finished.
auto& cmdfence = swap.commands.fence;
if (cmdfence) {
VkResult result = vkWaitForFences(context.device, 1, &cmdfence->fence, VK_FALSE, UINT64_MAX);
VkResult result = vkWaitForFences(context.device, 1, &cmdfence->fence, VK_TRUE, UINT64_MAX);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkWaitForFences error.");
}
@@ -685,7 +685,7 @@ void flushCommandBuffer(VulkanContext& context) {
cmdfence->condition.notify_all();
// Restart the command buffer.
error = vkWaitForFences(context.device, 1, &cmdfence->fence, VK_FALSE, UINT64_MAX);
error = vkWaitForFences(context.device, 1, &cmdfence->fence, VK_TRUE, UINT64_MAX);
ASSERT_POSTCONDITION(!error, "vkWaitForFences error.");
error = vkResetFences(context.device, 1, &cmdfence->fence);
ASSERT_POSTCONDITION(!error, "vkResetFences error.");
@@ -720,7 +720,7 @@ VkCommandBuffer acquireWorkCommandBuffer(VulkanContext& context) {
const VkCommandBufferBeginInfo binfo { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
if (work.fence && work.fence->submitted) {
work.fence->submitted = false;
vkWaitForFences(context.device, 1, &work.fence->fence, VK_FALSE, UINT64_MAX);
vkWaitForFences(context.device, 1, &work.fence->fence, VK_TRUE, UINT64_MAX);
vkResetCommandBuffer(work.cmdbuffer, 0);
vkBeginCommandBuffer(work.cmdbuffer, &binfo);
}

View File

@@ -757,7 +757,7 @@ FenceStatus VulkanDriver::wait(Handle<HwFence> fh, uint64_t timeout) {
if (cmdfence->swapChainDestroyed) {
return FenceStatus::ERROR;
}
VkResult result = vkWaitForFences(mContext.device, 1, &cmdfence->fence, VK_FALSE, timeout);
VkResult result = vkWaitForFences(mContext.device, 1, &cmdfence->fence, VK_TRUE, timeout);
return result == VK_SUCCESS ? FenceStatus::CONDITION_SATISFIED : FenceStatus::TIMEOUT_EXPIRED;
}

View File

@@ -437,6 +437,10 @@ struct Config {
size_t lutDimension;
};
// Inside the FColorGrading constructor, TSAN sporadically detects a data race on the config struct;
// the Filament thread writes and the Job thread reads. In practice there should be no data race, so
// we force TSAN off to silence the warning.
UTILS_NO_SANITIZE_THREAD
FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
SYSTRACE_CALL();

View File

@@ -237,7 +237,6 @@ void FEngine::init() {
// 3 bands = 9 float3
const float sh[9 * 3] = { 0.0f };
mDefaultIbl = upcast(IndirectLight::Builder()
.reflections(mDefaultIblTexture)
.irradiance(3, reinterpret_cast<const float3*>(sh))
.build(*this));

View File

@@ -398,10 +398,10 @@ void ShadowMap::computeShadowCameraDirectional(
}
const mat4f F(mat4f::row_major_init {
s.x, 0, 0, o.x,
0, s.y, 0, o.y,
0, 0, 1, 0,
0, 0, 0, 1,
s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
@@ -579,42 +579,42 @@ mat4f ShadowMap::getTextureCoordsMapping() const noexcept {
// remapping from NDC to texture coordinates (i.e. [-1,1] -> [0, 1])
// ([1, 0] for depth mapping)
const mat4f Mt(mClipSpaceFlipped ? mat4f::row_major_init{
0.5f, 0, 0, 0.5f,
0, -0.5f, 0, 0.5f,
0, 0, -0.5f, 0.5f,
0, 0, 0, 1
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, -0.5f, 0.0f, 0.5f,
0.0f, 0.0f, -0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f
} : mat4f::row_major_init{
0.5f, 0, 0, 0.5f,
0, 0.5f, 0, 0.5f,
0, 0, -0.5f, 0.5f,
0, 0, 0, 1
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, -0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f
});
// the shadow map texture might be larger than the shadow map dimension, so we add a scaling
// factor
const float v = (float) mShadowMapLayout.textureDimension / mShadowMapLayout.atlasDimension;
const mat4f Mv(mat4f::row_major_init{
v, 0, 0, 0,
0, v, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
v, 0.0f, 0.0f, 0.0f,
0.0f, v, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
});
// apply the 1-texel border viewport transform
const float o = 1.0f / mShadowMapLayout.atlasDimension;
const float s = 1.0f - 2.0f * (1.0f / mShadowMapLayout.textureDimension);
const mat4f Mb(mat4f::row_major_init{
s, 0, 0, o,
0, s, 0, o,
0, 0, 1, 0,
0, 0, 0, 1
s, 0.0f, 0.0f, o,
0.0f, s, 0.0f, o,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
});
const mat4f Mf = mTextureSpaceFlipped ? mat4f(mat4f::row_major_init{
1, 0, 0, 0,
0, -1, 0, 1,
0, 0, 1, 0,
0, 0, 0, 1
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
}) : mat4f();
// Compute shadow-map texture access transform
@@ -647,10 +647,10 @@ mat4f ShadowMap::warpFrustum(float n, float f) noexcept {
const float A = (f + n) * d;
const float B = -2 * n * f * d;
const mat4f Wp(mat4f::row_major_init{
n, 0, 0, 0,
0, A, 0, B,
0, 0, n, 0,
0, 1, 0, 0
n, 0.0f, 0.0f, 0.0f,
0.0f, A, 0.0f, B,
0.0f, 0.0f, n, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f
});
return Wp;
}
@@ -735,10 +735,10 @@ void ShadowMap::intersectWithShadowCasters(Aabb& UTILS_RESTRICT lightFrustum,
const float2 s = 2.0f / float2(lightFrustum.max.xy - lightFrustum.min.xy);
const float2 o = -s * float2(lightFrustum.max.xy + lightFrustum.min.xy) * 0.5f;
const mat4f F(mat4f::row_major_init {
s.x, 0, 0, o.x,
0, s.y, 0, o.y,
0, 0, 1, 0,
0, 0, 0, 1,
s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
});
float3 wsLightFrustumCorners[8];
const mat4f projection = F * lightView;
@@ -1043,9 +1043,9 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
const float nsxsz = n * sx * sz;
const float j = -(B * sy) / (nsxsz * dz * dz);
const mat3f J(mat3f::row_major_init{
j * dz * sz, j * X * sz, 0,
0, j * nsxsz, 0,
0, j * Z * sx, j * dz * sx
j * dz * sz, j * X * sz, 0.0f,
0.0f, j * nsxsz, 0.0f,
0.0f, j * Z * sx, j * dz * sx
});
float3 Jx = J[0] * ures;

View File

@@ -301,13 +301,16 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc
u.setUniform(offsetof(PerViewUib, iblRoughnessOneLevel), iblRoughnessOneLevel);
u.setUniform(offsetof(PerViewUib, iblLuminance), intensity * exposure);
u.setUniformArray(offsetof(PerViewUib, iblSH), ibl->getSH(), 9);
if (ibl->getReflectionHwHandle()) {
mPerViewSb.setSampler(PerViewSib::IBL_SPECULAR, {
ibl->getReflectionHwHandle(), {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR
}});
// We always sample from the reflection texture, so provide a dummy texture if necessary.
backend::Handle<backend::HwTexture> reflection = ibl->getReflectionHwHandle();
if (!reflection) {
reflection = engine.getDummyCubemap()->getHwHandle();
}
mPerViewSb.setSampler(PerViewSib::IBL_SPECULAR, { reflection, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR
}});
// Directional light (always at index 0)
auto& lcm = engine.getLightManager();

View File

@@ -24,7 +24,7 @@ material {
vertexDomain : device,
depthWrite : false,
shadingModel : unlit,
variantFilter : [ skinning, shadowReceiver ],
variantFilter : [ skinning, shadowReceiver, vsm ],
culling: none
}

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.9.9"
spec.version = "1.9.10"
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.9.9/filament-v1.9.9-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.9.10/filament-v1.9.10-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -52,6 +52,7 @@ set(PRIVATE_HDRS
src/eiff/DictionarySpirvChunk.h
src/eiff/MaterialSpirvChunk.h
src/GLSLPostProcessor.h
src/ShaderMinifier.h
src/sca/ASTHelpers.h
src/sca/GLSLTools.h
src/sca/builtinResource.h)
@@ -63,7 +64,8 @@ set(SRCS
src/eiff/MaterialSpirvChunk.cpp
src/sca/ASTHelpers.cpp
src/sca/GLSLTools.cpp
src/GLSLPostProcessor.cpp)
src/GLSLPostProcessor.cpp
src/ShaderMinifier.cpp)
# Sources and headers for filamat lite

View File

@@ -106,42 +106,8 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const
return oss.str();
}
/**
* Shrinks the specified string and returns a new string as the result.
* To shrink the string, this method performs the following transforms:
* - Remove leading white spaces at the beginning of each line
* - Remove empty lines
*/
static std::string shrinkString(const std::string& s) {
size_t cur = 0;
std::string r;
r.reserve(s.length());
while (cur < s.length()) {
size_t pos = cur;
size_t len = 0;
while (s[cur] != '\n') {
cur++;
len++;
}
size_t newPos = s.find_first_not_of(" \t", pos);
if (newPos == std::string::npos) newPos = pos;
r.append(s, newPos, len - (newPos - pos));
r += '\n';
while (s[cur] == '\n') {
cur++;
}
}
return r;
}
void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl, const GLSLPostProcessor::Config& config) {
void GLSLPostProcessor::spirvToToMsl(const SpirvBlob* spirv, std::string* outMsl,
const GLSLPostProcessor::Config& config) const {
CompilerMSL mslCompiler(*spirv);
CompilerGLSL::Options options;
mslCompiler.set_common_options(options);
@@ -184,7 +150,7 @@ void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl, const GLSLPostProcess
}
*outMsl = mslCompiler.compile();
*outMsl = shrinkString(*outMsl);
*outMsl = mShaderMinifier.removeWhitespace(*outMsl);
}
bool GLSLPostProcessor::process(const std::string& inputShader, Config const& config,
@@ -245,7 +211,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
options.generateDebugInfo = mGenerateDebugInfo;
GlslangToSpv(*program.getIntermediate(mShLang), *mSpirvOutput, &options);
if (mMslOutput) {
SpvToMsl(mSpirvOutput, mMslOutput, config);
spirvToToMsl(mSpirvOutput, mMslOutput, config);
}
} else {
utils::slog.e << "GLSL post-processor invoked with optimization level NONE"
@@ -262,7 +228,13 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
}
if (mGlslOutput) {
*mGlslOutput = shrinkString(*mGlslOutput);
*mGlslOutput = mShaderMinifier.removeWhitespace(*mGlslOutput);
// In theory this should only be enabled for SIZE, but in practice we often use PERFORMANCE.
if (mOptimization != MaterialBuilder::Optimization::NONE) {
*mGlslOutput = mShaderMinifier.renameStructFields(*mGlslOutput);
}
if (mPrintShaders) {
utils::slog.i << *mGlslOutput << utils::io::endl;
}
@@ -313,7 +285,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
}
if (mMslOutput) {
SpvToMsl(mSpirvOutput, mMslOutput, config);
spirvToToMsl(mSpirvOutput, mMslOutput, config);
}
if (mGlslOutput) {
@@ -339,7 +311,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
}
if (mMslOutput) {
SpvToMsl(&spirv, mMslOutput, config);
spirvToToMsl(&spirv, mMslOutput, config);
}
// Transpile back to GLSL

View File

@@ -24,6 +24,8 @@
#include "filamat/MaterialBuilder.h" // for MaterialBuilder:: enums
#include "ShaderMinifier.h"
#include <ShaderLang.h>
#include <spirv-tools/optimizer.hpp>
@@ -78,6 +80,8 @@ private:
static void registerPerformancePasses(spvtools::Optimizer& optimizer, Config const& config);
void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const;
void spirvToToMsl(const SpirvBlob* spirv, std::string* outMsl,
const GLSLPostProcessor::Config& config) const;
const MaterialBuilder::Optimization mOptimization;
const bool mPrintShaders;
@@ -86,6 +90,7 @@ private:
SpirvBlob* mSpirvOutput = nullptr;
std::string* mMslOutput = nullptr;
EShLanguage mShLang = EShLangFragment;
ShaderMinifier mShaderMinifier;
int mLangVersion = 0;
};

View File

@@ -640,6 +640,7 @@ bool MaterialBuilder::generateShaders(const std::vector<Variant>& variants, Chun
const bool targetApiNeedsSpirv =
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL;
std::vector<uint32_t>* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr;
std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr;
@@ -678,6 +679,11 @@ bool MaterialBuilder::generateShaders(const std::vector<Variant>& variants, Chun
#endif
}
std::string* pGlsl = nullptr;
if (targetApiNeedsGlsl) {
pGlsl = &shader;
}
#ifndef FILAMAT_LITE
GLSLPostProcessor::Config config{
@@ -690,7 +696,7 @@ bool MaterialBuilder::generateShaders(const std::vector<Variant>& variants, Chun
config.glsl.subpassInputToColorLocation.emplace_back(0, 0);
}
bool ok = postProcessor.process(shader, config, &shader, pSpirv, pMsl);
bool ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl);
#else
bool ok = true;
#endif

View File

@@ -0,0 +1,332 @@
/*
* Copyright (C) 2020 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 "ShaderMinifier.h"
#include <utils/Log.h>
namespace filamat {
static bool isIdCharNondigit(char c) {
return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
static bool isIdChar(char c) {
return isIdCharNondigit(c) || (c >= '0' && c <= '9');
};
// Checks if a GLSL identifier lives at the given index in the given codeline.
// If so, returns the identifier and moves the given index to point
// to the first character after the identifier.
static bool getId(std::string_view codeline, size_t* pindex, std::string_view* id) {
size_t index = *pindex;
if (index >= codeline.size()) {
return false;
}
if (!isIdCharNondigit(codeline[index])) {
return false;
}
++index;
while (index < codeline.size() && isIdChar(codeline[index])) {
++index;
}
*id = codeline.substr(*pindex, index - *pindex);
*pindex = index;
return true;
}
// Checks if the given string lives anywhere after the given index in the given codeline.
// If so, moves the given index to point to the first character after the string.
static bool getString(std::string_view codeline, size_t* pindex, std::string_view s) {
size_t index = codeline.find(s, *pindex);
if (index == std::string_view::npos) {
return false;
}
*pindex = index + s.size();
return true;
}
// Checks if the given character is at the last position of the string.
static bool getLastChar(std::string_view codeline, size_t index, char c) {
if (index != codeline.size() - 1) {
return false;
}
return codeline[index] == c;
}
// Checks if whitespace lives at the given position of a codeline; if so, updates the given index.
static bool getWhitespace(std::string_view codeline, size_t* pindex) {
size_t index = *pindex;
while (index < codeline.size() && (codeline[index] == ' ' || codeline[index] == '\t')) {
++index;
}
if (index == *pindex) {
return false;
}
*pindex = index;
return true;
}
// Skips past an optional precision qualifier that is possibly surrounded by whitespace.
static void ignorePrecision(std::string_view codeline, size_t* pindex) {
static std::string tokens[3] = {"lowp", "mediump", "highp"};
const size_t i = *pindex;
getWhitespace(codeline, pindex);
for (const auto& token : tokens) {
const size_t n = token.size();
if (codeline.substr(i, i + n) == token) {
*pindex += n;
break;
}
}
getWhitespace(codeline, pindex);
}
// Checks if the given string lives has an array size at the given codeline.
// If so, moves the given index to point to the first character after the array size.
// Always returns true for convenient daisy-chaining in the parser.
static bool ignoreArraySize(std::string_view codeline, size_t* pindex) {
size_t index = *pindex;
if (index >= codeline.size()) {
return true;
}
if (codeline[index] != '[') {
return true;
}
++index;
while (index < codeline.size() && codeline[index] != ']') {
++index;
}
*pindex = (index == codeline.size()) ? index : index + 1;
return true;
}
static void replaceAll(std::string& result, std::string_view from, std::string to) {
size_t n = 0;
while ((n = result.find(from, n)) != std::string::npos) {
result.replace(n, from.size(), to);
n += to.size();
}
}
namespace {
enum ParserState {
OUTSIDE,
STRUCT_OPEN,
STRUCT_DEFN,
};
}
/**
* Shrinks the specified string and returns a new string as the result.
* To shrink the string, this method performs the following transforms:
* - Remove leading white spaces at the beginning of each line
* - Remove empty lines
*/
std::string ShaderMinifier::removeWhitespace(const std::string& s) const {
size_t cur = 0;
std::string r;
r.reserve(s.length());
while (cur < s.length()) {
size_t pos = cur;
size_t len = 0;
while (s[cur] != '\n') {
cur++;
len++;
}
size_t newPos = s.find_first_not_of(" \t", pos);
if (newPos == std::string::npos) newPos = pos;
r.append(s, newPos, len - (newPos - pos));
r += '\n';
while (s[cur] == '\n') {
cur++;
}
}
return r;
}
/**
* Uniform block definitions can be quite big so this compresses them as follows.
* First, the uniform struct definitions are found, new field names are generated, and a mapping
* table is built. Second, all uses are replaced by applying the mapping table.
*
* The struct definition must be a sequence of tokens with the following pattern. This is fairly
* constrained (e.g. no comments or nesting) but this is designed to operate on generated code.
*
* "uniform" TypeIdentifier
* {
* OptionalPrecQual TypeIdentifier FieldIdentifier OptionalArraySize ;
* OptionalPrecQual TypeIdentifier FieldIdentifier OptionalArraySize ;
* OptionalPrecQual TypeIdentifier FieldIdentifier OptionalArraySize ;
* } StructIdentifier ;
*/
std::string ShaderMinifier::renameStructFields(const std::string& source) {
std::string_view sv = source;
size_t first = 0;
mCodelines.clear();
while (first < sv.size()) {
const size_t second = sv.find_first_of('\n', first);
if (first != second) {
mCodelines.emplace_back(sv.substr(first, second - first));
}
if (second == std::string_view::npos) {
break;
}
first = second + 1;
}
buildFieldMapping();
return applyFieldMapping();
}
void ShaderMinifier::buildFieldMapping() {
mStructFieldMap.clear();
mStructDefnMap.clear();
std::string currentStructPrefix;
std::vector<std::string_view> currentStructFields;
ParserState state = OUTSIDE;
for (std::string_view codeline : mCodelines) {
size_t cursor = 0;
std::string_view typeId;
std::string_view fieldName;
switch (state) {
case OUTSIDE:
if (getString(codeline, &cursor, "uniform") &&
getWhitespace(codeline, &cursor) &&
getId(codeline, &cursor, &typeId) &&
cursor == codeline.size()) {
currentStructPrefix = std::string(typeId) + ".";
state = STRUCT_OPEN;
}
continue;
case STRUCT_OPEN:
state = getLastChar(codeline, 0, '{') ? STRUCT_DEFN : OUTSIDE;
continue;
case STRUCT_DEFN: {
std::string_view structName;
if (getString(codeline, &cursor, "}")) {
if (!getWhitespace(codeline, &cursor) ||
!getId(codeline, &cursor, &structName) ||
!getLastChar(codeline, cursor, ';')) {
break;
}
const std::string structNamePrefix = std::string(structName) + ".";
std::string generatedFieldName = "a";
for (auto field : currentStructFields) {
const std::string sField(field);
const std::string key = structNamePrefix + sField;
const std::string val = structNamePrefix + generatedFieldName;
mStructFieldMap.push_back({key, val});
mStructDefnMap[currentStructPrefix + sField] = generatedFieldName;
if (generatedFieldName[0] == 'z') {
generatedFieldName = "a" + generatedFieldName;
} else {
generatedFieldName[0]++;
}
}
currentStructFields.clear();
state = OUTSIDE;
break;
}
ignorePrecision(codeline, &cursor);
if (!getId(codeline, &cursor, &typeId) ||
!getWhitespace(codeline, &cursor) ||
!getId(codeline, &cursor, &fieldName) ||
!ignoreArraySize(codeline, &cursor) ||
!getLastChar(codeline, cursor, ';')) {
break;
}
currentStructFields.push_back(fieldName);
break;
}
}
}
// Sort keys from longest to shortest because we want to replace "fogColorFromIbl" before
// replacing "fogColor".
const auto& compare = [](const RenameEntry& a, const RenameEntry& b) {
return a.first.length() > b.first.length();
};
std::sort(mStructFieldMap.begin(), mStructFieldMap.end(), compare);
}
std::string ShaderMinifier::applyFieldMapping() const {
std::string result;
ParserState state = OUTSIDE;
std::string currentStructPrefix;
for (std::string_view codeline : mCodelines) {
std::string modified(codeline);
std::string_view fieldName;
std::string_view structName;
std::string_view typeId;
size_t cursor = 0;
switch (state) {
case OUTSIDE: {
if (getString(codeline, &cursor, "uniform") &&
getWhitespace(codeline, &cursor) &&
getId(codeline, &cursor, &typeId) &&
cursor == codeline.size()) {
currentStructPrefix = std::string(typeId) + ".";
state = STRUCT_OPEN;
break;
}
for (const auto& key : mStructFieldMap) {
replaceAll(modified, key.first, key.second);
}
break;
}
case STRUCT_OPEN:
state = getLastChar(codeline, 0, '{') ? STRUCT_DEFN : OUTSIDE;
break;
case STRUCT_DEFN: {
if (getString(codeline, &cursor, "}") &&
getWhitespace(codeline, &cursor) &&
getId(codeline, &cursor, &structName) &&
getLastChar(codeline, cursor, ';')) {
state = OUTSIDE;
break;
}
ignorePrecision(codeline, &cursor);
if (!getId(codeline, &cursor, &typeId) ||
!getWhitespace(codeline, &cursor) ||
!getId(codeline, &cursor, &fieldName) ||
!ignoreArraySize(codeline, &cursor) ||
!getLastChar(codeline, cursor, ';')) {
break;
}
std::string key = currentStructPrefix + std::string(fieldName);
auto iter = mStructDefnMap.find(key);
if (iter == mStructDefnMap.end()) {
utils::slog.e << "ShaderMinifier error: " << key << utils::io::endl;
break;
}
replaceAll(modified, fieldName, iter->second);
break;
}
}
result += modified + "\n";
}
return result;
}
} // namespace filamat

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2020 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_SHADERMINIFIER_H
#define TNT_SHADERMINIFIER_H
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace filamat {
// Simple minifier for monolithic GLSL or MSL strings.
//
// Note that we already use a third party minifier, but it applies only to GLSL fragments.
// This custom minifier is designed for generated code such as uniform structs.
class ShaderMinifier {
public:
std::string removeWhitespace(const std::string& source) const;
std::string renameStructFields(const std::string& source);
private:
using RenameEntry = std::pair<std::string, std::string>;
void buildFieldMapping();
std::string applyFieldMapping() const;
// These fields do not need to be members, but they allow clients to reduce malloc churn
// by persisting the minifier object.
std::vector<std::string_view> mCodelines;
std::vector<RenameEntry> mStructFieldMap;
std::unordered_map<std::string, std::string> mStructDefnMap;
};
} // namespace filamat
#endif //TNT_SHADERMINIFIER_H

View File

@@ -118,7 +118,7 @@ inline filament::math::mat3f matrixFromUvTransform(const float offset[2], float
float sy = scale[1];
float c = cos(rotation);
float s = sin(rotation);
return filament::math::mat3f(sx * c, sx * s, tx, -sy * s, sy * c, ty, 0, 0, 1);
return filament::math::mat3f(sx * c, sx * s, tx, -sy * s, sy * c, ty, 0.0f, 0.0f, 1.0f);
};
} // namespace gltfio

View File

@@ -35,16 +35,22 @@ include_directories(${PUBLIC_HDR_DIR})
add_library(${TARGET} ${PUBLIC_HDRS} ${PRIVATE_HDRS} ${SRCS})
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
target_link_libraries(${TARGET} math utils)
add_library(${TARGET}-lite ${PUBLIC_HDRS} ${PRIVATE_HDRS} ${SRCS})
target_compile_definitions(${TARGET}-lite PUBLIC -DFILAMENT_IBL_LITE=1)
target_include_directories(${TARGET}-lite PUBLIC ${PUBLIC_HDR_DIR})
target_link_libraries(${TARGET}-lite math utils)
# ==================================================================================================
# Compile options and optimizations
# ==================================================================================================
if (MSVC)
target_compile_options(${TARGET} PRIVATE /fp:fast)
target_compile_options(${TARGET}-lite PRIVATE /fp:fast)
else()
target_compile_options(${TARGET} PRIVATE -ffast-math)
target_compile_options(${TARGET}-lite PRIVATE -ffast-math)
endif()
@@ -52,4 +58,5 @@ endif()
# Installation
# ==================================================================================================
install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR})
install(TARGETS ${TARGET}-lite ARCHIVE DESTINATION lib/${DIST_DIR})
install(DIRECTORY ${PUBLIC_HDR_DIR}/ibl DESTINATION include)

View File

@@ -19,7 +19,6 @@
#include <math/vec3.h>
#include <functional>
#include <vector>
#include <stdint.h>
@@ -40,7 +39,7 @@ class Image;
*/
class CubemapIBL {
public:
using Progress = std::function<void(size_t, float)>;
typedef void (*Progress)(size_t, float, void*);
/**
* Computes a roughness LOD using prefiltered importance sampling GGX
@@ -54,7 +53,7 @@ public:
static void roughnessFilter(
utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter,
Progress updater = {});
Progress updater = nullptr, void* userdata = nullptr);
//! Computes the "DFG" term of the "split-sum" approximation and stores it in a 2D image
static void DFG(utils::JobSystem& js, Image& dst, bool multiscatter, bool cloth);
@@ -72,7 +71,7 @@ public:
* @see CubemapSH
*/
static void diffuseIrradiance(utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
size_t maxNumSamples = 1024, Progress updater = {});
size_t maxNumSamples = 1024, Progress updater = nullptr, void* userdata = nullptr);
// for debugging. ignore.
static void brdf(utils::JobSystem& js, Cubemap& dst, float linearRoughness);

View File

@@ -65,18 +65,6 @@ public:
ReduceProc<STATE> reduce = [](STATE&) {},
const STATE& prototype = STATE());
//! Converts equirectangular Image to a Cubemap
static void equirectangularToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts a Cubemap to an equirectangular Image
static void cubemapToEquirectangular(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! Converts a Cubemap to an octahedron
static void cubemapToOctahedron(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! Converts horizontal or vertical cross Image to a Cubemap
static void crossToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! clamps image to acceptable range
static void clamp(Image& src);
@@ -88,21 +76,41 @@ public:
//! Return the name of a face (suitable for a file name)
static const char* getFaceName(Cubemap::Face face);
//! computes the solid angle of a pixel of a face of a cubemap
static float solidAngle(size_t dim, size_t u, size_t v);
//! Sets a Cubemap faces from a cross image
static void setAllFacesFromCross(Cubemap& cm, const Image& image);
private:
//move these into cmgen?
static void setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image);
static Image createCubemapImage(size_t dim, bool horizontal = true);
#ifndef FILAMENT_IBL_LITE
public:
//! Converts horizontal or vertical cross Image to a Cubemap
static void crossToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts equirectangular Image to a Cubemap
static void equirectangularToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts a Cubemap to an equirectangular Image
static void cubemapToEquirectangular(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! Converts a Cubemap to an octahedron
static void cubemapToOctahedron(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! mirror the cubemap in the horizontal direction
static void mirrorCubemap(utils::JobSystem& js, Cubemap& dst, const Cubemap& src);
//! computes the solid angle of a pixel of a face of a cubemap
static float solidAngle(size_t dim, size_t u, size_t v);
//! generates a UV grid in the cubemap -- useful for debugging.
static void generateUVGrid(utils::JobSystem& js, Cubemap& cml, size_t gridFrequencyX, size_t gridFrequencyY);
private:
static void setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image);
static Image createCubemapImage(size_t dim, bool horizontal = true);
#endif
friend class CubemapIBL;
};

View File

@@ -295,7 +295,7 @@ static float UTILS_UNUSED VisibilityAshikhmin(float NoV, float NoL, float /*a*/)
void CubemapIBL::roughnessFilter(
utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter,
Progress updater)
Progress updater, void* userdata)
{
const float numSamples = maxNumSamples;
const float inumSamples = 1.0f / numSamples;
@@ -311,7 +311,7 @@ void CubemapIBL::roughnessFilter(
(CubemapUtils::EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) {
if (UTILS_UNLIKELY(updater)) {
size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1;
updater(0, (float)p / ((float) dim * 6.0f));
updater(0, (float)p / ((float) dim * 6.0f), userdata);
}
const Cubemap& cm = levels[0];
for (size_t x = 0; x < dim; ++x, ++data) {
@@ -417,7 +417,7 @@ void CubemapIBL::roughnessFilter(
Cubemap::Face f, Cubemap::Texel* data, size_t dim) {
if (UTILS_UNLIKELY(updater)) {
size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1;
updater(0, (float) p / ((float) dim * 6.0f));
updater(0, (float) p / ((float) dim * 6.0f), userdata);
}
mat3 R;
const size_t numSamples = cache.size();
@@ -543,7 +543,7 @@ void CubemapIBL::roughnessFilter(
*/
void CubemapIBL::diffuseIrradiance(JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
size_t maxNumSamples, CubemapIBL::Progress updater)
size_t maxNumSamples, CubemapIBL::Progress updater, void* userdata)
{
const float numSamples = maxNumSamples;
const float inumSamples = 1.0f / numSamples;
@@ -595,7 +595,7 @@ void CubemapIBL::diffuseIrradiance(JobSystem& js, Cubemap& dst, const std::vecto
if (updater) {
size_t p = progress.fetch_add(1, std::memory_order_relaxed) + 1;
updater(0, (float)p / ((float) dim * 6.0f));
updater(0, (float)p / ((float) dim * 6.0f), userdata);
}
mat3 R;

View File

@@ -70,6 +70,114 @@ void CubemapUtils::highlight(Image& src) {
}
}
void CubemapUtils::downsampleCubemapLevelBoxFilter(JobSystem& js, Cubemap& dst, const Cubemap& src) {
size_t scale = src.getDimensions() / dst.getDimensions();
processSingleThreaded<EmptyState>(dst, js,
[&](EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) {
const Image& image(src.getImageForFace(f));
for (size_t x = 0; x < dim; ++x, ++data) {
Cubemap::writeAt(data, Cubemap::filterAtCenter(image, x * scale, y * scale));
}
});
}
/*
* Area of a cube face's quadrant projected onto a sphere
*
* 1 +---+----------+
* | | |
* |---+----------|
* | |(x,y) |
* | | |
* | | |
* -1 +---+----------+
* -1 1
*
*
* The quadrant (-1,1)-(x,y) is projected onto the unit sphere
*
*/
static inline float sphereQuadrantArea(float x, float y) {
return std::atan2(x*y, std::sqrt(x*x + y*y + 1));
}
float CubemapUtils::solidAngle(size_t dim, size_t u, size_t v) {
const float iDim = 1.0f / dim;
float s = ((u + 0.5f) * 2 * iDim) - 1;
float t = ((v + 0.5f) * 2 * iDim) - 1;
const float x0 = s - iDim;
const float y0 = t - iDim;
const float x1 = s + iDim;
const float y1 = t + iDim;
float solidAngle = sphereQuadrantArea(x0, y0) -
sphereQuadrantArea(x0, y1) -
sphereQuadrantArea(x1, y0) +
sphereQuadrantArea(x1, y1);
return solidAngle;
}
Cubemap CubemapUtils::create(Image& image, size_t dim, bool horizontal) {
Cubemap cm(dim);
Image temp(CubemapUtils::createCubemapImage(dim, horizontal));
CubemapUtils::setAllFacesFromCross(cm, temp);
std::swap(image, temp);
return cm;
}
void CubemapUtils::setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image) {
size_t dim = cm.getDimensions() + 2; // 2 extra per image, for seamlessness
size_t x = 0;
size_t y = 0;
switch (face) {
case Cubemap::Face::NX:
x = 0, y = dim;
break;
case Cubemap::Face::PX:
x = 2 * dim, y = dim;
break;
case Cubemap::Face::NY:
x = dim, y = 2 * dim;
break;
case Cubemap::Face::PY:
x = dim, y = 0;
break;
case Cubemap::Face::NZ:
x = 3 * dim, y = dim;
break;
case Cubemap::Face::PZ:
x = dim, y = dim;
break;
}
Image subImage;
subImage.subset(image, x + 1, y + 1, dim - 2, dim - 2);
cm.setImageForFace(face, subImage);
}
void CubemapUtils::setAllFacesFromCross(Cubemap& cm, const Image& image) {
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::NX, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::PX, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::NY, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::PY, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::NZ, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::PZ, image);
}
Image CubemapUtils::createCubemapImage(size_t dim, bool horizontal) {
// always allocate 2 extra column and row / face, to allow the cubemap to be "seamless"
size_t width = 4 * (dim + 2);
size_t height = 3 * (dim + 2);
if (!horizontal) {
std::swap(width, height);
}
Image image(width, height);
memset(image.getData(), 0, image.getBytesPerRow() * height);
return image;
}
#ifndef FILAMENT_IBL_LITE
void CubemapUtils::equirectangularToCubemap(JobSystem& js, Cubemap& dst, const Image& src) {
const size_t width = src.getWidth();
const size_t height = src.getHeight();
@@ -242,71 +350,6 @@ void CubemapUtils::crossToCubemap(JobSystem& js, Cubemap& dst, const Image& src)
});
}
void CubemapUtils::downsampleCubemapLevelBoxFilter(JobSystem& js, Cubemap& dst, const Cubemap& src) {
size_t scale = src.getDimensions() / dst.getDimensions();
processSingleThreaded<EmptyState>(dst, js,
[&](EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) {
const Image& image(src.getImageForFace(f));
for (size_t x = 0; x < dim; ++x, ++data) {
Cubemap::writeAt(data, Cubemap::filterAtCenter(image, x * scale, y * scale));
}
});
}
// ------------------------------------------------------------------------------------------------
void CubemapUtils::setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image) {
size_t dim = cm.getDimensions() + 2; // 2 extra per image, for seamlessness
size_t x = 0;
size_t y = 0;
switch (face) {
case Cubemap::Face::NX:
x = 0, y = dim;
break;
case Cubemap::Face::PX:
x = 2 * dim, y = dim;
break;
case Cubemap::Face::NY:
x = dim, y = 2 * dim;
break;
case Cubemap::Face::PY:
x = dim, y = 0;
break;
case Cubemap::Face::NZ:
x = 3 * dim, y = dim;
break;
case Cubemap::Face::PZ:
x = dim, y = dim;
break;
}
Image subImage;
subImage.subset(image, x + 1, y + 1, dim - 2, dim - 2);
cm.setImageForFace(face, subImage);
}
void CubemapUtils::setAllFacesFromCross(Cubemap& cm, const Image& image) {
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::NX, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::PX, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::NY, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::PY, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::NZ, image);
CubemapUtils::setFaceFromCross(cm, Cubemap::Face::PZ, image);
}
Image CubemapUtils::createCubemapImage(size_t dim, bool horizontal) {
// always allocate 2 extra column and row / face, to allow the cubemap to be "seamless"
size_t width = 4 * (dim + 2);
size_t height = 3 * (dim + 2);
if (!horizontal) {
std::swap(width, height);
}
Image image(width, height);
memset(image.getData(), 0, image.getBytesPerRow() * height);
return image;
}
const char* CubemapUtils::getFaceName(Cubemap::Face face) {
switch (face) {
case Cubemap::Face::NX: return "nx";
@@ -318,14 +361,6 @@ const char* CubemapUtils::getFaceName(Cubemap::Face face) {
}
}
Cubemap CubemapUtils::create(Image& image, size_t dim, bool horizontal) {
Cubemap cm(dim);
Image temp(CubemapUtils::createCubemapImage(dim, horizontal));
CubemapUtils::setAllFacesFromCross(cm, temp);
std::swap(image, temp);
return cm;
}
void CubemapUtils::mirrorCubemap(JobSystem& js, Cubemap& dst, const Cubemap& src) {
processSingleThreaded<EmptyState>(dst, js,
[&](EmptyState&, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t dim) {
@@ -358,42 +393,7 @@ void CubemapUtils::generateUVGrid(JobSystem& js, Cubemap& cml, size_t gridFreque
}
});
}
/*
* Area of a cube face's quadrant projected onto a sphere
*
* 1 +---+----------+
* | | |
* |---+----------|
* | |(x,y) |
* | | |
* | | |
* -1 +---+----------+
* -1 1
*
*
* The quadrant (-1,1)-(x,y) is projected onto the unit sphere
*
*/
static inline float sphereQuadrantArea(float x, float y) {
return std::atan2(x*y, std::sqrt(x*x + y*y + 1));
}
float CubemapUtils::solidAngle(size_t dim, size_t u, size_t v) {
const float iDim = 1.0f / dim;
float s = ((u + 0.5f) * 2 * iDim) - 1;
float t = ((v + 0.5f) * 2 * iDim) - 1;
const float x0 = s - iDim;
const float y0 = t - iDim;
const float x1 = s + iDim;
const float y1 = t + iDim;
float solidAngle = sphereQuadrantArea(x0, y0) -
sphereQuadrantArea(x0, y1) -
sphereQuadrantArea(x1, y0) +
sphereQuadrantArea(x1, y1);
return solidAngle;
}
#endif
} // namespace ibl
} // namespace filament

View File

@@ -181,7 +181,7 @@ public:
Iterator(Iterator const& rhs) noexcept = default;
Iterator& operator=(Iterator const& rhs) = default;
const reference operator*() const { return { soa, index }; }
reference operator*() const { return { soa, index }; }
reference operator*() { return { soa, index }; }
reference operator[](size_t n) { return *(*this + n); }

View File

@@ -60,8 +60,8 @@ public:
Zip2Iterator(Zip2Iterator const& rhs) noexcept = default;
Zip2Iterator& operator=(Zip2Iterator const& rhs) = default;
const value_type operator*() const { return { *mIt.first, *mIt.second }; }
reference operator*() { return { *mIt.first, *mIt.second }; }
value_type operator*() const { return { *mIt.first, *mIt.second }; }
reference operator*() { return { *mIt.first, *mIt.second }; }
const value_type operator[](size_t n) const { return *(*this + n); }
reference operator[](size_t n) { return *(*this + n); }

View File

@@ -64,6 +64,14 @@
# define UTILS_PRIVATE
#endif
#define UTILS_NO_SANITIZE_THREAD
#if defined(__has_feature)
# if __has_feature(thread_sanitizer)
# undef UTILS_NO_SANITIZE_THREAD
# define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
# endif
#endif
/*
* helps the compiler's optimizer predicting branches
*/

View File

@@ -683,11 +683,11 @@ int main(int argc, char* argv[]) {
Cubemap blurred = CubemapUtils::create(image, dim);
CubemapIBL::roughnessFilter(js, blurred, levels, linear_roughness, g_num_samples,
float3{ 1, 1, 1 }, !g_ibl_no_prefilter,
[&updater, quiet = g_quiet](size_t index, float v) {
if (!quiet) {
updater.update(index, v);
[](size_t index, float v, void* userdata) {
if (!g_quiet) {
((ProgressUpdater*) userdata)->update(index, v);
}
});
}, &updater);
if (!g_quiet) {
updater.stop();
std::cout << "Extract faces..." << std::endl;
@@ -978,11 +978,11 @@ void iblRoughnessPrefilter(
}
CubemapIBL::roughnessFilter(js, dst, levels, roughness, numSamples,
float3{ 1, 1, 1 }, prefilter,
[&updater, quiet = g_quiet](size_t index, float v) {
if (!quiet) {
updater.update(index, v);
[](size_t index, float v, void* userdata) {
if (!g_quiet) {
((ProgressUpdater*) userdata)->update(index, v);
}
});
}, &updater);
if (!g_quiet) {
updater.stop();
}
@@ -1067,11 +1067,11 @@ void iblDiffuseIrradiance(utils::JobSystem& js, const utils::Path& iname,
updater.start();
}
CubemapIBL::diffuseIrradiance(js, dst, levels, numSamples,
[&updater, quiet = g_quiet](size_t index, float v) {
if (!quiet) {
updater.update(index, v);
[](size_t index, float v, void* userdata) {
if (!g_quiet) {
((ProgressUpdater*) userdata)->update(index, v);
}
});
}, &updater);
if (!g_quiet) {
updater.stop();
}

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.9.9",
"version": "1.9.10",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",