Compare commits

..

18 Commits

Author SHA1 Message Date
Benjamin Doherty
a9c5bbf185 Merge branch 'rc/1.25.2' into release 2022-07-18 15:14:02 -07:00
Philip Rideout
8dd4bff7a7 gltfio: minor fixups to prep for g3 integration. 2022-07-18 15:11:36 -07:00
Philip Rideout
77c54446af gltfio: use openLocalTransformTransaction API. 2022-07-18 15:11:30 -07:00
Benjamin Doherty
4a0bc0af57 Bump version to 1.25.2 2022-07-11 15:51:55 -07:00
Benjamin Doherty
ce33fda6ec Release Filament 1.25.1 2022-07-11 15:49:29 -07:00
Mathias Agopian
88768e8003 Fix Conflict with glibc 2.35+ macro.
Fix #5720
2022-07-11 15:25:02 -07:00
Philip Rideout
4f1dd8b304 filamat: dictionaries now store each blob once, not twice.
BlobDictionary and LineDictionary were storing blobs as map keys to
achieve simple compression, but they also stored duplicates of
in a vector for index-based lookup.

Now, the vector is storage and the map has keys that are string_view.
2022-07-11 11:00:04 -07:00
Philip Rideout
42cae27992 filamat: remove more dead codelines. 2022-07-11 11:00:04 -07:00
Mathias Agopian
13f646025b Renderer::getUserTime() now returns seconds as documented
Fixes #5722
2022-07-11 10:53:12 -07:00
Philip Rideout
e7c9197d07 matdbg: rewrite ShaderReplacer to remove bespoke chunk i/o. 2022-07-11 10:09:16 -07:00
Mathias Agopian
7afd5e5963 Camera API and documentation improvements
- getNear() and getCullingFar() now return doubles
- updated documentation
- all setProjection() calls can now throw (when enabled) and will
  do so if preconditions are not met (instead of setting a default
  projection).
- Frustum can now be logged on debug builds
2022-07-11 09:56:41 -07:00
Philip Rideout
d73453863d Fix swallowed errors in MaterialParser.
If `ChunkContainer::parse` failed, then `MaterialParser::parse` was
returning SUCCESS.
2022-07-07 22:34:32 -07:00
Philip Rideout
8b88638232 filamat: remove some unused code. 2022-07-07 22:34:19 -07:00
Ben Doherty
677cdc1239 Add backend test for viewport and scissor (#5767) 2022-07-07 17:22:41 -07:00
MasTraER
a4d3ffe7d4 Metal: add support for scissor (#5644)
This patch enables user scissor in Metal backend; There was no implementation for it.
While the absence of the feature in Metal does not incur serious problem, many rendering glitches were found in apps using ImGui due to it.

Co-authored-by: Benjamin Doherty <bendoherty@google.com>
2022-07-07 13:37:43 -07:00
Philip Rideout
3ada971d8a matdbg: prep for removing bespoke chunk serializer
matdbg should use Flattener / Unflattener rather than imitating them,
this is phase 1.
2022-07-07 11:09:23 -07:00
Philip Rideout
6588cc30ea Use string_view for map lookups.
We can avoid construction of std::string by using std::map with a
special comparator. For some reason, this is not supported with
unordered_map.
2022-07-07 10:07:17 -07:00
Mathias Agopian
09f188659a fix typos in Camera documentation 2022-07-07 09:37:34 -07:00
51 changed files with 609 additions and 654 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.25.1'
implementation 'com.google.android.filament:filament-android:1.25.2'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.25.1'
pod 'Filament', '~> 1.25.2'
```
### Snapshots

View File

@@ -5,6 +5,12 @@ A new header is inserted each time a *tag* is created.
## main branch
## v1.25.2
- engine: `Camera::getNear()` and `Camera::getCullingFar()` now return `doubles`
- Metal: implement scissor support.
- engine: `Renderer::getUserTime()` now returns seconds as documented (#5722) [⚠️ **API Fix**]
## v1.25.1
- engine: add support for automatic instancing. Must be enabled with `Engine::setAutomaticInstancingEnabled(bool)`

View File

@@ -84,13 +84,13 @@ Java_com_google_android_filament_Camera_nLookAt(JNIEnv*, jclass, jlong nativeCam
camera->lookAt({eye_x, eye_y, eye_z}, {center_x, center_y, center_z}, {up_x, up_y, up_z});
}
extern "C" JNIEXPORT jfloat JNICALL
extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_android_filament_Camera_nGetNear(JNIEnv*, jclass, jlong nativeCamera) {
Camera *camera = (Camera *) nativeCamera;
return camera->getNear();
}
extern "C" JNIEXPORT jfloat JNICALL
extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_android_filament_Camera_nGetCullingFar(JNIEnv*, jclass,
jlong nativeCamera) {
Camera *camera = (Camera *) nativeCamera;

View File

@@ -400,7 +400,7 @@ public class Camera {
}
/**
* Sets the camera's view matrix.
* Sets the camera's model matrix.
* <p>
* Helper method to set the camera's entity transform component.
* Remember that the Camera "looks" towards its -z axis.
@@ -412,29 +412,29 @@ public class Camera {
* engine.getTransformManager().getInstance(camera->getEntity()), viewMatrix);
* </pre>
*
* @param viewMatrix The camera position and orientation provided as a <b>rigid transform</b> matrix.
* @param modelMatrix The camera position and orientation provided as a <b>rigid transform</b> matrix.
*/
public void setModelMatrix(@NonNull @Size(min = 16) float[] viewMatrix) {
Asserts.assertMat4fIn(viewMatrix);
nSetModelMatrix(getNativeObject(), viewMatrix);
public void setModelMatrix(@NonNull @Size(min = 16) float[] modelMatrix) {
Asserts.assertMat4fIn(modelMatrix);
nSetModelMatrix(getNativeObject(), modelMatrix);
}
/**
* Sets the camera's view matrix.
* Sets the camera's model matrix.
* <p>
* Helper method to set the camera's entity transform component.
* Remember that the Camera "looks" towards its -z axis.
* <p>
*
* @param viewMatrix The camera position and orientation provided as a <b>rigid transform</b> matrix.
* @param modelMatrix The camera position and orientation provided as a <b>rigid transform</b> matrix.
*/
public void setModelMatrix(@NonNull @Size(min = 16) double[] viewMatrix) {
Asserts.assertMat4In(viewMatrix);
nSetModelMatrixFp64(getNativeObject(), viewMatrix);
public void setModelMatrix(@NonNull @Size(min = 16) double[] modelMatrix) {
Asserts.assertMat4In(modelMatrix);
nSetModelMatrixFp64(getNativeObject(), modelMatrix);
}
/**
* Sets the camera's view matrix.
* Sets the camera's model matrix.
*
* @param eyeX x-axis position of the camera in world space
* @param eyeY y-axis position of the camera in world space
@@ -456,7 +456,7 @@ public class Camera {
* @return Distance to the near plane
*/
public float getNear() {
return nGetNear(getNativeObject());
return (float)nGetNear(getNativeObject());
}
/**
@@ -464,7 +464,7 @@ public class Camera {
* @return Distance to the far plane
*/
public float getCullingFar() {
return nGetCullingFar(getNativeObject());
return (float)nGetCullingFar(getNativeObject());
}
/**
@@ -549,10 +549,10 @@ public class Camera {
/**
* Retrieves the camera's view matrix. The view matrix is the inverse of the model matrix.
*
* @param out A 16-float array where the model view will be stored, or null in which
* @param out A 16-float array where the view matrix will be stored, or null in which
* case a new array is allocated.
*
* @return A 16-float array containing the camera's view as a column-major matrix.
* @return A 16-float array containing the camera's column-major view matrix.
*/
@NonNull @Size(min = 16)
public float[] getViewMatrix(@Nullable @Size(min = 16) float[] out) {
@@ -567,7 +567,7 @@ public class Camera {
* @param out A 16-double array where the model view will be stored, or null in which
* case a new array is allocated.
*
* @return A 16-double array containing the camera's view as a column-major matrix.
* @return A 16-double array containing the camera's column-major view matrix.
*/
@NonNull @Size(min = 16)
public double[] getViewMatrix(@Nullable @Size(min = 16) double[] out) {
@@ -787,8 +787,8 @@ public class Camera {
private static native void nSetModelMatrix(long nativeCamera, float[] in);
private static native void nSetModelMatrixFp64(long nativeCamera, double[] in);
private static native void nLookAt(long nativeCamera, double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ);
private static native float nGetNear(long nativeCamera);
private static native float nGetCullingFar(long nativeCamera);
private static native double nGetNear(long nativeCamera);
private static native double nGetCullingFar(long nativeCamera);
private static native void nGetProjectionMatrix(long nativeCamera, double[] out);
private static native void nGetCullingProjectionMatrix(long nativeCamera, double[] out);
private static native void nGetScaling(long nativeCamera, double[] out);

View File

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

View File

@@ -395,6 +395,7 @@ if (APPLE)
test/test_LoadImage.cpp
test/test_RenderExternalImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp
)
target_link_libraries(backend_test PRIVATE

View File

@@ -116,6 +116,8 @@ struct MetalContext {
std::stack<const char*> groupMarkers;
MTLViewport currentViewport;
#if defined(FILAMENT_METAL_PROFILING)
// Logging and profiling.
os_log_t log;

View File

@@ -852,6 +852,8 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
};
[mContext->currentRenderPassEncoder setViewport:metalViewport];
mContext->currentViewport = metalViewport;
// Metal requires a new command encoder for each render pass, and they cannot be reused.
// We must bind certain states for each command encoder, so we dirty the states here to force a
// rebinding at the first the draw call of this pass.
@@ -1236,8 +1238,24 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
clamp:0.0];
}
// FIXME: implement take ps.scissor into account
// must be intersected with viewport (see OpenGLDriver.cpp for implementation details)
// Set scissor-rectangle.
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor);
const MTLViewport& viewport = mContext->currentViewport;
// fmax/min are used here to guard against NaN and because the MTLViewport coordinates are doubles.
const auto left = std::fmax(viewport.originX , scissor.origin.x );
const auto right = std::fmin(viewport.originX + viewport.width , scissor.origin.x + scissor.size.width );
const auto top = std::fmax(viewport.originY , scissor.origin.y );
const auto bottom = std::fmin(viewport.originY + viewport.height, scissor.origin.y + scissor.size.height );
MTLScissorRect scissorRect = {
.x = static_cast<NSUInteger>(left),
.y = static_cast<NSUInteger>(top ),
.width = static_cast<NSUInteger>(right - left),
.height = static_cast<NSUInteger>(bottom - top )
};
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::BINDING_COUNT] = { nil };

View File

@@ -293,7 +293,8 @@ public:
// RenderTarget. Metal's texture coordinates have (0, 0) at the top-left of the texture, but
// Filament's coordinates have (0, 0) at bottom-left.
return MTLRegionMake2D((NSUInteger)rect.left,
height - (NSUInteger)rect.bottom - rect.height, rect.width, rect.height);
std::max(height - (int64_t) rect.bottom - rect.height, (int64_t) 0),
rect.width, rect.height);
}
bool isDefaultRenderTarget() const { return defaultRenderTarget; }

View File

@@ -181,7 +181,7 @@ void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, si
free(c->name);
free(c);
}, (void*)c);
getDriverApi().readPixels(rt, 0, 0, 512, 512, std::move(pbd));
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
}
class Environment : public ::testing::Environment {

View File

@@ -0,0 +1,167 @@
/*
* 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 "BackendTest.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
namespace test {
using namespace filament;
using namespace filament::backend;
static const char* const triangleVs = R"(#version 450 core
layout(location = 0) in vec4 mesh_position;
void main() {
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
#if defined(TARGET_VULKAN_ENVIRONMENT)
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
gl_Position.y = -gl_Position.y;
#endif
})";
static const char* const triangleFs = R"(#version 450 core
precision mediump int; precision highp float;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(1.0f);
})";
TEST_F(BackendTest, ScissorViewportRegion) {
auto& api = getDriverApi();
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
constexpr auto kSrcTexFormat = TextureFormat::RGBA8;
constexpr int kNumLevels = 3;
constexpr int kSrcLevel = 1;
constexpr int kSrcRtWidth = 384;
constexpr int kSrcRtHeight = 384;
api.startCapture(0);
// color texture (mip level 1) 512x512 depth texture (mip level 0) 512x512
// +----------------------------------------+ +------------------------------------------+
// | | | |
// | | | |
// | RenderTarget (384x384) | | RenderTarget (384x384) |
// +------------------------------+ | +------------------------------+ |
// | | | | | |
// | +-------------------+ | | | | |
// | | viewport | | | | | |
// | | | | | | | |
// | +---+---------------+ | | | | | |
// | | | | | | | | | |
// | | | | | | | | | |
// | | | (64,64) | | | | | | |
// | | +---------------+---+ | | | | |
// | | scissor | | | | | |
// | +-------------------+ | | | | |
// | (32, 32) | | | | |
// +------------------------------+---------+ +------------------------------+-----------+
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram();
ProgramHandle program = api.createProgram(std::move(p));
// Create source color and depth textures.
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
Handle<HwTexture> depthTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH16, 1, 512, 512, 1,
TextureUsage::DEPTH_ATTACHMENT);
// Render into the bottom-left quarter of the texture.
Viewport srcRect = {
.left = 64,
.bottom = 64,
.width = kSrcRtWidth - 64 * 2,
.height = kSrcRtHeight - 64 * 2
};
Viewport scissor = {
.left = 32,
.bottom = 32,
.width = kSrcRtWidth - 64 * 2,
.height = kSrcRtHeight - 64 * 2
};
// We purposely set the render target width and height to smaller than the texture, to check
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1,
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {});
Handle<HwRenderTarget> fullRenderTarget = api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1,
{srcTexture, kSrcLevel, 0}, {}, {});
TrianglePrimitive triangle(api);
// Render a white triangle over blue.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0;
params.viewport = srcRect;
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
ps.scissor = scissor;
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0);
api.beginRenderPass(srcRenderTarget, params);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
readPixelsAndAssertHash("scissor", kSrcTexWidth >> 1, kSrcTexHeight >> 1, fullRenderTarget,
0xAB3D1C53, true);
api.commit(swapChain);
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test

View File

@@ -98,7 +98,7 @@ namespace filament {
* The *near* plane distance greatly affects the depth-buffer resolution.
*
* Example: Precision at 1m, 10m, 100m and 1Km for various near distances assuming a 32-bit float
* depth-buffer
* depth-buffer:
*
* near (m) | 1 m | 10 m | 100 m | 1 Km
* -----------:|:------:|:-------:|:--------:|:--------:
@@ -107,11 +107,31 @@ namespace filament {
* 0.1 | 3.6e-7 | 7.0e-5 | 0.0072 | 0.43
* 1.0 | 0 | 3.8e-6 | 0.0007 | 0.07
*
*
* As can be seen in the table above, the depth-buffer precision drops rapidly with the
* distance to the camera.
*
* Make sure to pick the highest *near* plane distance possible.
*
* On Vulkan and Metal platforms (or OpenGL platforms supporting either EXT_clip_control or
* ARB_clip_control extensions), the depth-buffer precision is much less dependent on the *near*
* plane value:
*
* near (m) | 1 m | 10 m | 100 m | 1 Km
* -----------:|:------:|:-------:|:--------:|:--------:
* 0.001 | 1.2e-7 | 9.5e-7 | 7.6e-6 | 6.1e-5
* 0.01 | 1.2e-7 | 9.5e-7 | 7.6e-6 | 6.1e-5
* 0.1 | 5.9e-8 | 9.5e-7 | 1.5e-5 | 1.2e-4
* 1.0 | 0 | 9.5e-7 | 7.6e-6 | 1.8e-4
*
*
* Choosing the *far* plane distance
* =================================
*
* The far plane distance is always set internally to infinity for rendering, however it is used for
* culling and shadowing calculations. It is important to keep a reasonable ratio between
* the near and far plane distances. Typically a ratio in the range 1:100 to 1:100000 is
* commanded. Larger values may causes rendering artifacts or trigger assertions in debug builds.
*
*
* Exposure
* ========
@@ -167,14 +187,12 @@ public:
* Precondition: \p far > near for PROJECTION::PERSPECTIVE or
* \p far != near for PROJECTION::ORTHO
*
* @attention these parameters are silently modified to meet the preconditions above.
*
* @see Projection, Frustum
*/
void setProjection(Projection projection,
double left, double right,
double bottom, double top,
double near, double far) noexcept;
double near, double far);
/** Sets the projection matrix from the field-of-view.
*
@@ -187,7 +205,7 @@ public:
* @see Fov.
*/
void setProjection(double fovInDegrees, double aspect, double near, double far,
Fov direction = Fov::VERTICAL) noexcept;
Fov direction = Fov::VERTICAL);
/** Sets the projection matrix from the focal length.
*
@@ -197,7 +215,7 @@ public:
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
void setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept;
double aspect, double near, double far);
/** Sets a custom projection matrix.
*
@@ -308,31 +326,31 @@ public:
//! Returns the frustum's near plane
float getNear() const noexcept;
double getNear() const noexcept;
//! Returns the frustum's far plane used for culling
float getCullingFar() const noexcept;
double getCullingFar() const noexcept;
/** Sets the camera's view matrix.
/** Sets the camera's model matrix.
*
* Helper method to set the camera's entity transform component.
* It has the same effect as calling:
*
* ~~~~~~~~~~~{.cpp}
* engine.getTransformManager().setTransform(
* engine.getTransformManager().getInstance(camera->getEntity()), view);
* engine.getTransformManager().getInstance(camera->getEntity()), model);
* ~~~~~~~~~~~
*
* @param view The camera position and orientation provided as a rigid transform matrix.
* @param model The camera position and orientation provided as a rigid transform matrix.
*
* @note The Camera "looks" towards its -z axis
*
* @warning \p view must be a rigid transform
* @warning \p model must be a rigid transform
*/
void setModelMatrix(const math::mat4& view) noexcept;
void setModelMatrix(const math::mat4f& view) noexcept; //!< \overload
void setModelMatrix(const math::mat4& model) noexcept;
void setModelMatrix(const math::mat4f& model) noexcept; //!< @overload
/** Sets the camera's view matrix
/** Sets the camera's model matrix
*
* @param eye The position of the camera in world space.
* @param center The point in world space the camera is looking at.
@@ -342,7 +360,7 @@ public:
const math::float3& center,
const math::float3& up) noexcept;
/** Sets the camera's view matrix, assuming up is along the y axis
/** Sets the camera's model matrix, assuming up is along the y axis
*
* @param eye The position of the camera in world space.
* @param center The point in world space the camera is looking at.

View File

@@ -115,7 +115,13 @@ private:
math::float4 mPlanes[6];
};
} // namespace filament
#if !defined(NDEBUG)
namespace utils::io {
class ostream;
} // namespace utils::io
utils::io::ostream& operator<<(utils::io::ostream& out, filament::Frustum const& frustum);
#endif
#endif // TNT_FILAMENT_FRUSTUM_H

View File

@@ -68,17 +68,17 @@ mat4 Camera::inverseProjection(const mat4 & p) noexcept {
}
void Camera::setProjection(Camera::Projection projection, double left, double right, double bottom,
double top, double near, double far) noexcept {
double top, double near, double far) {
upcast(this)->setProjection(projection, left, right, bottom, top, near, far);
}
void Camera::setProjection(double fovInDegrees, double aspect, double near, double far,
Camera::Fov direction) noexcept {
Camera::Fov direction) {
upcast(this)->setProjection(fovInDegrees, aspect, near, far, direction);
}
void Camera::setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept {
double aspect, double near, double far) {
upcast(this)->setLensProjection(focalLengthInMillimeters, aspect, near, far);
}
@@ -115,11 +115,11 @@ double2 Camera::getShift() const noexcept {
return upcast(this)->getShift();
}
float Camera::getNear() const noexcept {
double Camera::getNear() const noexcept {
return upcast(this)->getNear();
}
float Camera::getCullingFar() const noexcept {
double Camera::getCullingFar() const noexcept {
return upcast(this)->getCullingFar();
}

View File

@@ -19,6 +19,7 @@
#include "Culler.h"
#include <utils/compiler.h>
#include <utils/Log.h>
using namespace filament::math;
@@ -98,3 +99,19 @@ float Frustum::contains(float3 p) const noexcept {
}
} // namespace filament
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, filament::Frustum const& frustum) {
float4 planes[6];
frustum.getNormalizedPlanes(planes);
out << planes[0] << '\n'
<< planes[1] << '\n'
<< planes[2] << '\n'
<< planes[3] << '\n'
<< planes[4] << '\n'
<< planes[5] << utils::io::endl;
return out;
}
#endif

View File

@@ -96,16 +96,19 @@ ChunkContainer const& MaterialParser::getChunkContainer() const noexcept {
MaterialParser::ParseResult MaterialParser::parse() noexcept {
ChunkContainer& cc = getChunkContainer();
if (cc.parse()) {
if (!cc.hasChunk(mImpl.mMaterialTag) || !cc.hasChunk(mImpl.mDictionaryTag)) {
return ParseResult::ERROR_MISSING_BACKEND;
}
if (!DictionaryReader::unflatten(cc, mImpl.mDictionaryTag, mImpl.mBlobDictionary)) {
return ParseResult::ERROR_OTHER;
}
if (!mImpl.mMaterialChunk.readIndex(mImpl.mMaterialTag)) {
return ParseResult::ERROR_OTHER;
}
if (UTILS_UNLIKELY(!cc.parse())) {
return ParseResult::ERROR_OTHER;
}
const ChunkType matTag = mImpl.mMaterialTag;
const ChunkType dictTag = mImpl.mDictionaryTag;
if (UTILS_UNLIKELY(!cc.hasChunk(matTag) || !cc.hasChunk(dictTag))) {
return ParseResult::ERROR_MISSING_BACKEND;
}
if (UTILS_UNLIKELY(!DictionaryReader::unflatten(cc, dictTag, mImpl.mBlobDictionary))) {
return ParseResult::ERROR_OTHER;
}
if (UTILS_UNLIKELY(!mImpl.mMaterialChunk.initialize(matTag))) {
return ParseResult::ERROR_OTHER;
}
return ParseResult::SUCCESS;
}

View File

@@ -62,7 +62,7 @@ void Renderer::endFrame() {
}
double Renderer::getUserTime() const {
return upcast(this)->getUserTime().count();
return upcast(this)->getUserTime();
}
void Renderer::resetUserTime() {

View File

@@ -549,14 +549,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
auto& s = mShadowUb.edit();
const float n = shadowMap.getCamera().getNear();
const float f = shadowMap.getCamera().getCullingFar();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
s.shadows[i].direction = direction;
s.shadows[i].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[i].lightFromWorldZ = shadowMap.getLightFromWorldZ();
s.shadows[i].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[i].nearOverFarMinusNear = n / (f - n);
s.shadows[i].nearOverFarMinusNear = float(n / (f - n));
s.shadows[i].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius / wsTexelSizeAtOneMeter;

View File

@@ -48,7 +48,7 @@ FCamera::FCamera(FEngine& engine, Entity e)
}
void UTILS_NOINLINE FCamera::setProjection(double fovInDegrees, double aspect, double near, double far,
Camera::Fov direction) noexcept {
Camera::Fov direction) {
double w;
double h;
double s = std::tan(fovInDegrees * math::d::DEG_TO_RAD / 2.0) * near;
@@ -63,7 +63,7 @@ void UTILS_NOINLINE FCamera::setProjection(double fovInDegrees, double aspect, d
}
void FCamera::setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept {
double aspect, double near, double far) {
// a 35mm camera has a 36x24mm wide frame size
double h = (0.5 * near) * ((SENSOR_SIZE * 1000.0) / focalLengthInMillimeters);
double w = h * aspect;
@@ -82,31 +82,25 @@ void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p,
mat4 const& c, double near, double far) noexcept {
mProjection = p;
mProjectionForCulling = c;
mNear = (float)near;
mFar = (float)far;
mNear = near;
mFar = far;
}
void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection,
double left, double right,
double bottom, double top,
double near, double far) noexcept {
double near, double far) {
// we make sure our preconditions are verified, using default values,
// to avoid inconsistent states in the renderer later.
if (UTILS_UNLIKELY(left == right ||
bottom == top ||
(projection == Projection::PERSPECTIVE && (near <= 0 || far <= near)) ||
(projection == Projection::ORTHO && (near == far)))) {
PANIC_LOG("Camera preconditions not met. Using default projection.");
left = -0.1;
right = 0.1;
bottom = -0.1;
top = 0.1;
near = 0.1;
far = 100.0;
}
ASSERT_PRECONDITION(!(
left == right ||
bottom == top ||
(projection == Projection::PERSPECTIVE && (near <= 0 || far <= near)) ||
(projection == Projection::ORTHO && (near == far))),
"Camera preconditions not met in setProjection(%s, %f, %f, %f, %f, %f, %f)",
projection == Camera::Projection::PERSPECTIVE ? "PERSPECTIVE" : "ORTHO",
left, right, bottom, top, near, far);
mat4 p;
mat4 c, p;
switch (projection) {
case Projection::PERSPECTIVE:
/*
@@ -117,8 +111,8 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection,
* 0 0 F+N/N-F 2*F*N/N-F
* 0 0 -1 0
*/
p = mat4::frustum(left, right, bottom, top, near, far);
mProjectionForCulling = p;
c = mat4::frustum(left, right, bottom, top, near, far);
p = c;
/*
* but we're using a far plane at infinity
@@ -141,13 +135,11 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection,
* 0 0 -2/F-N - F+N/F-N
* 0 0 0 1
*/
p = mat4::ortho(left, right, bottom, top, near, far);
mProjectionForCulling = p;
c = mat4::ortho(left, right, bottom, top, near, far);
p = c;
break;
}
mProjection = p;
mNear = float(near);
mFar = float(far);
FCamera::setCustomProjection(p, c, near, far);
}
math::mat4 FCamera::getProjectionMatrix() const noexcept {
@@ -272,8 +264,8 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept {
cullingProjection = mat4f{ camera.getCullingProjectionMatrix() };
model = mat4f{ camera.getModelMatrix() };
view = mat4f{ camera.getViewMatrix() };
zn = camera.getNear();
zf = camera.getCullingFar();
zn = (float)camera.getNear();
zf = (float)camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = (float)camera.getFocalLength();
A = f / camera.getAperture();
@@ -287,8 +279,8 @@ CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamer
model = mat4f{ modelMatrix };
view = mat4f{ inverse(modelMatrix) };
worldOrigin = worldOriginCamera;
zn = camera.getNear();
zf = camera.getCullingFar();
zn = (float)camera.getNear();
zf = (float)camera.getCullingFar();
ev100 = Exposure::ev100(camera);
f = (float)camera.getFocalLength();
A = f / camera.getAperture();

View File

@@ -48,15 +48,15 @@ public:
// sets the projection matrix
void setProjection(Projection projection,
double left, double right, double bottom, double top,
double near, double far) noexcept;
double near, double far);
// sets the projection matrix
void setProjection(double fovInDegrees, double aspect, double near, double far,
Fov direction = Fov::VERTICAL) noexcept;
Fov direction = Fov::VERTICAL);
// sets the projection matrix
void setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far) noexcept;
double aspect, double near, double far);
// Sets a custom projection matrix (sets both the viewing and culling projections).
void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept;
@@ -69,7 +69,7 @@ public:
void setShift(math::double2 shift) noexcept { mShiftCS = shift * 2.0; }
const math::double2 getShift() const noexcept { return mShiftCS * 0.5; }
math::double2 getShift() const noexcept { return mShiftCS * 0.5; }
// viewing the projection matrix to be used for rendering, contains scaling/shift and possibly
// other transforms needed by the shaders
@@ -84,32 +84,32 @@ public:
// culling projection matrix set by the user
math::mat4 getUserCullingProjectionMatrix() const noexcept { return mProjectionForCulling; }
float getNear() const noexcept { return mNear; }
double getNear() const noexcept { return mNear; }
float getCullingFar() const noexcept { return mFar; }
double getCullingFar() const noexcept { return mFar; }
// sets the camera's view matrix (must be a rigid transform)
// sets the camera's model matrix (must be a rigid transform)
void setModelMatrix(const math::mat4& modelMatrix) noexcept;
void setModelMatrix(const math::mat4f& modelMatrix) noexcept;
// sets the camera's view matrix
void lookAt(const math::float3& eye, const math::float3& center, const math::float3& up = { 0, 1, 0 }) noexcept;
// sets the camera's model matrix
void lookAt(const math::float3& eye, const math::float3& center,
const math::float3& up = { 0, 1, 0 }) noexcept;
// returns the view matrix
// returns the model matrix
math::mat4 getModelMatrix() const noexcept;
// returns the inverse of the view matrix
// returns the view matrix (inverse of the model matrix)
math::mat4 getViewMatrix() const noexcept;
template <typename T>
template<typename T>
static math::details::TMat44<T> rigidTransformInverse(math::details::TMat44<T> const& v) noexcept {
// The inverse of a rigid transform can be computed from the transpose
// | R T |^-1 | Rt -Rt*T |
// | 0 1 | = | 0 1 |
const math::details::TMat33<T> rt(transpose(v.upperLeft()));
const math::details::TVec3<T> t(rt * v[3].xyz);
return math::details::TMat44<T>(rt, -t);
const auto rt(transpose(v.upperLeft()));
const auto t(rt * v[3].xyz);
return { rt, -t };
}
math::double3 getPosition() const noexcept {
@@ -192,8 +192,8 @@ private:
math::double2 mScalingCS = { 1.0 }; // additional scaling applied to projection
math::double2 mShiftCS = { 0.0 }; // additional translation applied to projection
float mNear{};
float mFar{};
double mNear{};
double mFar{};
// exposure settings
float mAperture = 16.0f;
float mShutterSpeed = 1.0f / 125.0f;

View File

@@ -140,7 +140,11 @@ private:
backend::TextureFormat getLdrFormat(bool translucent) const noexcept;
Epoch getUserEpoch() const { return mUserEpoch; }
duration getUserTime() const noexcept { return clock::now() - getUserEpoch(); }
double getUserTime() const noexcept {
duration d = clock::now() - getUserEpoch();
// convert the duration (whatever it is) to a duration in seconds encoded as double
return std::chrono::duration<double>(d).count();
}
void getRenderTarget(FView const& view,
backend::TargetBufferFlags& outAttachementMask,

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.25.1"
spec.version = "1.25.2"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.25.1/filament-v1.25.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.25.2/filament-v1.25.2-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -17,7 +17,6 @@
#ifndef TNT_FILAMAT_MATERIAL_CHUNK_H
#define TNT_FILAMAT_MATERIAL_CHUNK_H
#include <filament/MaterialChunkType.h>
#include <filaflat/ChunkContainer.h>
@@ -31,16 +30,24 @@ namespace filaflat {
class MaterialChunk {
public:
using Variant = filament::Variant;
explicit MaterialChunk(ChunkContainer const& container);
~MaterialChunk() noexcept;
// call this once after container.parse() has been called
bool readIndex(filamat::ChunkType materialTag);
bool initialize(filamat::ChunkType materialTag);
// call this as many times as needed
bool getShader(ShaderContent& shaderContent,
BlobDictionary const& dictionary,
uint8_t shaderModel, filament::Variant variant, uint8_t stage);
// populates "shaderContent" with the requested shader, or returns false on failure.
bool getShader(ShaderContent& shaderContent, BlobDictionary const& dictionary,
uint8_t shaderModel, Variant variant, uint8_t stage);
// These methods are for debugging purposes only (matdbg)
// @{
static void decodeKey(uint32_t key, uint8_t* model, Variant::type_t* variant, uint8_t* stage);
const tsl::robin_map<uint32_t, uint32_t>& getOffsets() const { return mOffsets; }
// @}
private:
ChunkContainer const& mContainer;
@@ -51,11 +58,11 @@ private:
bool getTextShader(Unflattener unflattener,
BlobDictionary const& dictionary, ShaderContent& shaderContent,
uint8_t shaderModel, filament::Variant variant, uint8_t ps);
uint8_t shaderModel, Variant variant, uint8_t stage);
bool getSpirvShader(
BlobDictionary const& dictionary, ShaderContent& shaderContent,
uint8_t shaderModel, filament::Variant variant, uint8_t stage);
uint8_t shaderModel, Variant variant, uint8_t stage);
};
} // namespace filamat

View File

@@ -21,9 +21,16 @@
namespace filaflat {
static inline uint32_t makeKey(uint8_t shaderModel, filament::Variant variant, uint8_t type) noexcept {
static inline uint32_t makeKey(uint8_t shaderModel, filament::Variant variant, uint8_t stage) noexcept {
static_assert(sizeof(variant.key) * 8 <= 8);
return (shaderModel << 16) | (type << 8) | variant.key;
return (shaderModel << 16) | (stage << 8) | variant.key;
}
void MaterialChunk::decodeKey(uint32_t key, uint8_t* model, filament::Variant::type_t* variant,
uint8_t* stage) {
*variant = key & 0xff;
*stage = (key >> 8) & 0xff;
*model = (key >> 16) & 0xff;
}
MaterialChunk::MaterialChunk(ChunkContainer const& container)
@@ -32,10 +39,10 @@ MaterialChunk::MaterialChunk(ChunkContainer const& container)
MaterialChunk::~MaterialChunk() noexcept = default;
bool MaterialChunk::readIndex(filamat::ChunkType materialTag) {
bool MaterialChunk::initialize(filamat::ChunkType materialTag) {
if (mBase != nullptr) {
// readIndex() should be called only once.
// initialize() should be called only once.
return true;
}
@@ -139,7 +146,6 @@ bool MaterialChunk::getTextShader(Unflattener unflattener, BlobDictionary const&
return true;
}
bool MaterialChunk::getSpirvShader(BlobDictionary const& dictionary,
ShaderContent& shaderContent, uint8_t shaderModel, filament::Variant variant, uint8_t stage) {

View File

@@ -21,15 +21,13 @@
namespace filamat {
size_t BlobDictionary::addBlob(const std::vector<uint32_t>& vblob) noexcept {
std::string blob((char*) vblob.data(), vblob.size() * 4);
std::string_view blob((char*) vblob.data(), vblob.size() * 4);
auto iter = mBlobIndices.find(blob);
if (iter != mBlobIndices.end()) {
return iter->second;
}
mBlobIndices[blob] = mBlobs.size();
size_t size = blob.size();
mBlobs.push_back(std::move(blob));
mStorageSize += size;
mBlobs.emplace_back(std::make_unique<std::string>(blob));
mBlobIndices.emplace(*mBlobs.back(), mBlobs.size() - 1);
return mBlobs.size() - 1;
}

View File

@@ -17,20 +17,23 @@
#ifndef TNT_FILAMAT_BLOBDICTIONARY_H
#define TNT_FILAMAT_BLOBDICTIONARY_H
#include <cassert>
#include <memory>
#include <string>
#include <string_view>
#include <memory>
#include <unordered_map>
#include <vector>
namespace filamat {
// Establish a blob <-> id mapping. Note that std::string may binary data with null characters.
// Establish a blob <-> id mapping. Note that std::string may have binary data with null characters.
class BlobDictionary {
public:
BlobDictionary() : mStorageSize(0) {
}
BlobDictionary() = default;
~BlobDictionary() = default;
// Due to the presence of unique_ptr, disallow copy construction but allow move construction.
BlobDictionary(BlobDictionary const&) = delete;
BlobDictionary(BlobDictionary&&) = default;
// Adds a blob if it's not already a duplicate and returns its index.
size_t addBlob(const std::vector<uint32_t>& blob) noexcept;
@@ -39,24 +42,17 @@ public:
return mBlobs.size();
}
// Returns the total storage size, assuming that each blob is prefixed with a 64-bit size.
size_t getSize() const noexcept {
return mStorageSize + 8 * getBlobCount();
}
bool isEmpty() const noexcept {
return mBlobs.size() == 0;
}
const std::string& getBlob(size_t index) const noexcept {
assert(index < mBlobs.size());
return mBlobs[index];
std::string_view getBlob(size_t index) const noexcept {
return *mBlobs[index];
}
private:
std::unordered_map<std::string, size_t> mBlobIndices;
std::vector<std::string> mBlobs;
size_t mStorageSize;
std::unordered_map<std::string_view, size_t> mBlobIndices;
std::vector<std::unique_ptr<std::string>> mBlobs;
};
} // namespace filamat

View File

@@ -33,23 +33,14 @@ public:
return mType;
}
size_t getFlattenedSize() const noexcept {
return mFlattenedSize;
}
void setFlattenedSize(size_t s) noexcept {
mFlattenedSize = s;
}
virtual void flatten(Flattener &f) = 0;
protected:
Chunk(ChunkType type) : mType(type), mFlattenedSize(0) {
Chunk(ChunkType type) : mType(type) {
}
private:
ChunkType mType;
size_t mFlattenedSize;
};
} // namespace filamat

View File

@@ -31,7 +31,6 @@ size_t ChunkContainer::flatten(Flattener& f) const {
f.writeSizePlaceholder();
chunk->flatten(f);
uint32_t size = f.writeSize();
chunk->setFlattenedSize(size);
}
return f.getBytesWritten();
}

View File

@@ -21,7 +21,7 @@
namespace filamat {
DictionarySpirvChunk::DictionarySpirvChunk(BlobDictionary&& dictionary, bool stripDebugInfo) :
Chunk(ChunkType::DictionarySpirv), mDictionary(dictionary), mStripDebugInfo(stripDebugInfo) {
Chunk(ChunkType::DictionarySpirv), mDictionary(std::move(dictionary)), mStripDebugInfo(stripDebugInfo) {
}
void DictionarySpirvChunk::flatten(Flattener& f) {
@@ -36,7 +36,7 @@ void DictionarySpirvChunk::flatten(Flattener& f) {
f.writeUint32(mDictionary.getBlobCount());
for (size_t i = 0 ; i < mDictionary.getBlobCount() ; i++) {
const std::string& spirv = mDictionary.getBlob(i);
std::string_view spirv = mDictionary.getBlob(i);
smolv::ByteArray compressed;
if (!smolv::Encode(spirv.data(), spirv.size(), compressed, flags)) {
utils::slog.e << "Error with SPIRV compression" << utils::io::endl;

View File

@@ -19,7 +19,7 @@
namespace filamat {
DictionaryTextChunk::DictionaryTextChunk(LineDictionary&& dictionary, ChunkType chunkType) :
Chunk(chunkType), mDictionary(dictionary) {
Chunk(chunkType), mDictionary(std::move(dictionary)) {
}
void DictionaryTextChunk::flatten(Flattener& f) {
@@ -28,7 +28,7 @@ void DictionaryTextChunk::flatten(Flattener& f) {
// Strings
for (size_t i = 0 ; i < mDictionary.getLineCount() ; i++) {
f.writeString(mDictionary.getString(i).c_str());
f.writeString(mDictionary.getString(i).data());
}
}

View File

@@ -16,34 +16,26 @@
#include "LineDictionary.h"
#include <assert.h>
namespace filamat {
LineDictionary::LineDictionary() : mStorageSize(0){
}
const std::string& LineDictionary::getString(size_t index) const noexcept {
assert(index < mStrings.size());
return mStrings[index];
std::string_view LineDictionary::getString(size_t index) const noexcept {
return *mStrings[index];
}
size_t LineDictionary::getLineCount() const {
return mStrings.size();
}
size_t LineDictionary::getIndex(const std::string& s) const noexcept {
if (mLineIndices.find(s) == mLineIndices.end()) {
return SIZE_MAX;
size_t LineDictionary::getIndex(std::string_view s) const noexcept {
if (auto iter = mLineIndices.find(s); iter != mLineIndices.end()) {
return iter->second;
}
return mLineIndices.at(s);
return SIZE_MAX;
}
void LineDictionary::addText(const std::string& line) noexcept {
const char* s = line.c_str();
assert(s != nullptr);
size_t cur = 0;
size_t pos = 0;
size_t len = 0;
@@ -66,11 +58,8 @@ void LineDictionary::addLine(const std::string&& line) noexcept {
if (mLineIndices.find(line) != mLineIndices.end()) {
return;
}
mLineIndices[line] = mStrings.size();
size_t size = line.size();
mStrings.push_back(std::move(line));
mStorageSize += size + 1;
mStrings.emplace_back(std::make_unique<std::string>(line));
mLineIndices.emplace(*mStrings.back(), mStrings.size() - 1);
}
} // namespace filamat

View File

@@ -17,7 +17,9 @@
#ifndef TNT_FILAMAT_LINEDICTIONARY_H
#define TNT_FILAMAT_LINEDICTIONARY_H
#include <memory>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
@@ -27,29 +29,27 @@ namespace filamat {
// and each line encoded into a 16 bit id.
class LineDictionary {
public:
LineDictionary();
~LineDictionary() = default;
LineDictionary() = default;
// Due to the presence of unique_ptr, disallow copy construction but allow move construction.
LineDictionary(LineDictionary const&) = delete;
LineDictionary(LineDictionary&&) = default;
void addText(const std::string& text) noexcept;
size_t getLineCount() const;
constexpr size_t getSize() const noexcept {
return mStorageSize;
}
bool isEmpty() const noexcept {
return mStrings.empty();
}
const std::string& getString(size_t index) const noexcept;
size_t getIndex(const std::string& s) const noexcept;
std::string_view getString(size_t index) const noexcept;
size_t getIndex(std::string_view s) const noexcept;
private:
void addLine(const std::string&& line) noexcept;
std::unordered_map<std::string, size_t> mLineIndices;
std::vector<std::string> mStrings;
size_t mStorageSize = 0;
std::unordered_map<std::string_view, size_t> mLineIndices;
std::vector<std::unique_ptr<std::string>> mStrings;
};
} // namespace filamat

View File

@@ -25,18 +25,15 @@ void MaterialTextChunk::writeEntryAttributes(size_t entryIndex, Flattener& f) co
f.writeUint8(entry.stage);
}
const char* MaterialTextChunk::getShaderText(size_t entryIndex) const noexcept {
return mEntries[entryIndex].shader.c_str();
}
void compressShader(const char *s, Flattener &f, const LineDictionary& dictionary) {
f.writeUint32(static_cast<uint32_t>(strlen(s) + 1));
void compressShader(std::string_view src, Flattener &f, const LineDictionary& dictionary) {
f.writeUint32(static_cast<uint32_t>(src.size() + 1));
f.writeValuePlaceholder();
size_t numLines = 0;
size_t cur = 0;
const char* s = src.data();
while (s[cur] != '\0') {
size_t pos = cur;
size_t len = 0;
@@ -46,11 +43,12 @@ void compressShader(const char *s, Flattener &f, const LineDictionary& dictionar
len++;
}
std::string newLine(s + pos, len);
std::string_view newLine(s + pos, len);
size_t index = dictionary.getIndex(newLine);
if (index > UINT16_MAX) {
slog.e << "Dictionary returned line index > UINT16_MAX" << io::endl;
assert(false);
continue;
}
@@ -70,14 +68,14 @@ void MaterialTextChunk::flatten(Flattener& f) {
mDuplicateMap.resize(mEntries.size());
// Detect duplicate;
std::unordered_map<std::string, size_t> stringToIndex;
std::unordered_map<std::string_view, size_t> stringToIndex;
for (size_t i = 0; i < mEntries.size(); i++) {
if (stringToIndex.find(getShaderText(i)) == stringToIndex.end()) { // New
stringToIndex[getShaderText(i)] = i;
const std::string& text = mEntries[i].shader;
if (auto iter = stringToIndex.find(text); iter != stringToIndex.end()) {
mDuplicateMap[i] = { true, iter->second };
} else {
stringToIndex.emplace(text, i);
mDuplicateMap[i].isDup = false;
} else { // Dup
mDuplicateMap[i].isDup = true;
mDuplicateMap[i].dupOfIndex = stringToIndex[mEntries[i].shader];
}
}
}
@@ -91,21 +89,17 @@ void MaterialTextChunk::flatten(Flattener& f) {
// Write all indexes.
for (size_t i = 0; i < mEntries.size(); i++) {
writeEntryAttributes(i, f);
// Try to reuse a shader if this is a dup.
if (mDuplicateMap[i].isDup) {
f.writeOffsetplaceholder(mDuplicateMap[i].dupOfIndex);
} else {
f.writeOffsetplaceholder(i);
}
const ShaderMapping& mapping = mDuplicateMap[i];
f.writeOffsetplaceholder(mapping.isDup ? mapping.dupOfIndex : i);
}
// Write all strings
for (size_t i = 0; i < mEntries.size(); i++) {
if (mDuplicateMap[i].isDup)
if (mDuplicateMap[i].isDup) {
continue;
}
f.writeOffsets(i);
compressShader(getShaderText(i), f, mDictionary);
compressShader(mEntries.at(i).shader, f, mDictionary);
}
}

View File

@@ -35,15 +35,14 @@ public:
private:
void flatten(Flattener& f) override;
const char* getShaderText(size_t entryIndex) const noexcept;
void writeEntryAttributes(size_t entryIndex, Flattener& f) const noexcept;
// Structure to keep track of duplicates.
struct ShaderAttribute{
struct ShaderMapping {
bool isDup = false;
size_t dupOfIndex = 0;
};
std::vector<ShaderAttribute> mDuplicateMap;
std::vector<ShaderMapping> mDuplicateMap;
const std::vector<TextEntry> mEntries;
const LineDictionary& mDictionary;

View File

@@ -19,7 +19,7 @@
#include <gltfio/FilamentAsset.h>
#include <backend/BufferDescriptor.h>
#include <filament/VertexBuffer.h>
#include <utils/compiler.h>

View File

@@ -23,6 +23,8 @@
#include <math/mat4.h>
#include <math/TVecHelpers.h>
#include <utils/compiler.h>
namespace filament::gltfio {
template <typename T>

View File

@@ -21,7 +21,7 @@
#include "FFilamentInstance.h"
#include "upcast.h"
#include <filament/MaterialEnums.h>
#include <filament/VertexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
@@ -253,9 +253,9 @@ size_t Animator::getAnimationCount() const {
void Animator::applyAnimation(size_t animationIndex, float time) const {
const Animation& anim = mImpl->animations[animationIndex];
TransformManager* transformManager = mImpl->transformManager;
RenderableManager* renderableManager = mImpl->renderableManager;
time = fmod(time, anim.duration);
TransformManager& transformManager = *mImpl->transformManager;
transformManager.openLocalTransformTransaction();
for (const auto& channel : anim.channels) {
const Sampler* sampler = channel.sourceData;
if (sampler->times.size() < 2) {
@@ -296,6 +296,7 @@ void Animator::applyAnimation(size_t animationIndex, float time) const {
mImpl->applyAnimation(channel, t, prevIndex, nextIndex);
}
transformManager.commitLocalTransformTransaction();
}
void Animator::resetBoneMatrices() {

View File

@@ -306,7 +306,6 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
}
// Build a mapping of root nodes to scene membership sets.
auto& nm = mNodeManager;
assert_invariant(srcAsset->scenes_count <= NodeManager::MAX_SCENE_COUNT);
mRootNodes.clear();
const size_t sic = std::min(srcAsset->scenes_count, NodeManager::MAX_SCENE_COUNT);
@@ -370,7 +369,7 @@ void FAssetLoader::createAsset(const cgltf_data* srcAsset, size_t numInstances)
addResourceUri(srcAsset->images[i].uri);
}
mResult->mResourceUris.reserve(resourceUris.size());
for (auto pair : resourceUris) {
for (const auto& pair : resourceUris) {
mResult->mResourceUris.push_back(pair.second);
}
@@ -815,7 +814,6 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out
const cgltf_attribute& attribute = morphTarget.attributes[aindex];
const cgltf_accessor* accessor = attribute.data;
const cgltf_attribute_type atype = attribute.type;
const int morphId = targetIndex + 1;
// The glTF normal and tangent data are ignored here, but honored in ResourceLoader.
if (atype == cgltf_attribute_type_normal || atype == cgltf_attribute_type_tangent) {

View File

@@ -53,7 +53,7 @@ void DependencyGraph::addEdge(MaterialInstance* mi, const char* parameter) {
// objects. Find all non-textured entities and immediately add mark them as ready.
void DependencyGraph::finalize() {
assert(!mFinalized);
for (auto pair : mMaterialToEntity) {
for (const auto& pair : mMaterialToEntity) {
auto mi = pair.first;
if (mMaterialToTexture.find(mi) == mMaterialToTexture.end()) {
markAsReady(mi);
@@ -64,7 +64,7 @@ void DependencyGraph::finalize() {
void DependencyGraph::refinalize() {
assert(mFinalized);
for (auto pair : mMaterialToEntity) {
for (const auto& pair : mMaterialToEntity) {
auto material = pair.first;
if (mMaterialToTexture.find(material) == mMaterialToTexture.end()) {
markAsReady(material);
@@ -85,7 +85,7 @@ void DependencyGraph::checkReadiness(Material* material) {
// Check this material's texture parameters, there are 5 in the worst case.
bool materialIsReady = true;
for (auto pair : status.params) {
for (const auto& pair : status.params) {
assert(pair.second && "Parameter-to-Texture edge is missing.");
if (!pair.second->ready) {
materialIsReady = false;

View File

@@ -22,12 +22,16 @@
#include <utils/Log.h>
#if GLTFIO_DRACO_SUPPORTED
#include <memory>
#include <vector>
using std::unique_ptr;
using std::vector;
#endif
using namespace utils;
namespace filament::gltfio {

View File

@@ -88,7 +88,6 @@ private:
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);
@@ -159,7 +158,7 @@ void Ktx2Provider::updateQueue() {
if (item->state != QueueItemState::TRANSCODING) {
continue;
}
Texture* texture = item->async->getTexture();
item->async->getTexture();
const TranscoderState state = item->transcoderState.load();
if (state != TranscoderState::NOT_STARTED) {
if (item->job) {

View File

@@ -58,7 +58,6 @@ using namespace filament;
using namespace filament::math;
using namespace utils;
using filament::geometry::Transcoder;
using filament::geometry::ComponentType;
static const auto FREE_CALLBACK = [](void* mem, size_t, void*) { free(mem); };
@@ -170,20 +169,6 @@ static void convertBytesToShorts(uint16_t* dst, const uint8_t* src, size_t count
}
}
static ComponentType getComponentType(const cgltf_accessor* accessor) {
switch (accessor->component_type) {
case cgltf_component_type_r_8: return ComponentType::BYTE;
case cgltf_component_type_r_8u: return ComponentType::UBYTE;
case cgltf_component_type_r_16: return ComponentType::SHORT;
case cgltf_component_type_r_16u: return ComponentType::USHORT;
case cgltf_component_type_r_32f: return ComponentType::FLOAT;
case cgltf_component_type_r_32u:
default:
assert_invariant(false);
return {};
}
}
static bool requiresConversion(const cgltf_accessor* accessor) {
if (UTILS_UNLIKELY(accessor->is_sparse)) {
return true;
@@ -780,7 +765,6 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
if (UTILS_UNLIKELY(!mesh || !mesh->weights_count)) {
continue;
}
cgltf_primitive const* prims = mesh->primitives;
for (cgltf_size pindex = 0, pcount = mesh->primitives_count; pindex < pcount; ++pindex) {
const cgltf_primitive& prim = mesh->primitives[pindex];
const auto& gltfioPrim = asset->mMeshCache.at(mesh)[pindex];
@@ -790,7 +774,6 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) {
bool hasNormals = false;
for (cgltf_size aindex = 0; aindex < target.attributes_count; aindex++) {
const cgltf_attribute& attribute = target.attributes[aindex];
const cgltf_accessor* accessor = attribute.data;
const cgltf_attribute_type atype = attribute.type;
if (atype != cgltf_attribute_type_tangent) {
continue;

View File

@@ -68,11 +68,11 @@ public:
mutable ArchiveCache mMaterials;
Texture* mDummyTexture = nullptr;
Engine* mEngine;
Engine* const mEngine;
};
UbershaderProvider::UbershaderProvider(Engine* engine, const void* archive, size_t archiveByteCount)
: mEngine(engine), mMaterials(*engine) {
: mMaterials(*engine), mEngine(engine) {
unsigned char texels[4] = {};
mDummyTexture = Texture::Builder()
.width(1).height(1)

View File

@@ -19,7 +19,7 @@
#include <filament/Box.h>
#include <filament/Engine.h>
#include <filament/MaterialEnums.h>
#include <filament/VertexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>

View File

@@ -32,14 +32,14 @@ using namespace image;
namespace {
using namespace filament::math;
struct FilterFunction {
float (*fn)(float) = nullptr;
float boundingRadius = 1;
bool rejectExternalSamples = true;
};
constexpr float M_PIf = float(filament::math::F_PI);
const FilterFunction Box {
.fn = [](float t) { return t <= 0.5f ? 1.0f : 0.0f; },
.boundingRadius = 1
@@ -50,7 +50,7 @@ const FilterFunction Nearest { Box.fn, 0.0f };
const FilterFunction Gaussian {
.fn = [](float t) {
if (t >= 2.0) return 0.0f;
const float scale = 1.0f / std::sqrt(0.5f * M_PIf);
const float scale = 1.0f / std::sqrt(0.5f * f::PI);
return std::exp(-2.0f * t * t) * scale;
},
.boundingRadius = 2
@@ -86,7 +86,7 @@ const FilterFunction Mitchell {
// Not bothering with a fast approximation since we cache results for each row.
float sinc(float t) {
if (t <= 0.00001f) return 1.0f;
return std::sin(M_PIf * t) / (M_PIf * t);
return std::sin(f::PI * t) / (f::PI * t);
}
const FilterFunction Lanczos {

View File

@@ -262,81 +262,6 @@ not including the terminating null.
<img width="600px" src="https://user-images.githubusercontent.com/1288904/63553241-b043ba80-c4ee-11e9-816c-c6acb1d6cdf7.png">
## Material Chunks
This section exists only to provide a reference for the `ShaderExtractor` and `ShaderReplacer`
features.
The relevant chunk types are listed here. These types are defined in the `filabridge` lib, in
the `filamat` namespace.
```c++
enum UTILS_PUBLIC ChunkType : uint64_t {
...
MaterialGlsl = charTo64bitNum("MAT_GLSL"), // MaterialTextChunk
MaterialSpirv = charTo64bitNum("MAT_SPIR"), // MaterialSpirvChunk
MaterialMetal = charTo64bitNum("MAT_METL"), // MaterialTextChunk
...
DictionaryGlsl = charTo64bitNum("DIC_GLSL"), // DictionaryTextChunk
DictionarySpirv = charTo64bitNum("DIC_SPIR"), // DictionarySpirvChunk
DictionaryMetal = charTo64bitNum("DIC_METL"), // DictionaryTextChunk
...
}
```
### MaterialTextChunk
These chunks have the following layout.
[u64] ChunkType magic string
[u32] Remaining chunk size in bytes
[u64] Shader count
for each shader:
[u8] Shader model
[u8] Shader variant
[u8] Shader stage
[u32] Offset in bytes from (and including) "Shader count" up to "Total string size"
for each unique shader:
[u32] Total string size (including null terminator)
[u32] Number of line indices
[u16 u16 u16...] Line indices
### MaterialSpirvChunk
These chunks have the following layout.
[u64] ChunkType magic string
[u32] Remaining chunk size in bytes
[u64] Shader count
for each shader:
[u8] Shader model
[u8] Shader variant
[u8] Shader stage
[u32] Index into the blob list in DictionarySpirvChunk
### DictionaryTextChunk
These chunks have the following layout.
[u64] ChunkType magic string
[u32] Remaining chunk size in bytes
[u32] Number of strings
for each string:
[u8 u8 u8 u8...] include null terminator after each string
### DictionarySpirvChunk
These chunks have the following layout.
[u64] ChunkType magic string
[u32] Remaining chunk size in bytes
[u32] Compression
[u32] Blob count
for each blob:
[u8 ...] Alignment padding
[u64] Byte count
[u8 u8 u8 ...]
[1]: https://github.com/civetweb/civetweb
[2]: https://microsoft.github.io/monaco-editor/
[3]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

View File

@@ -61,7 +61,7 @@ ShaderExtractor::ShaderExtractor(Backend backend, const void* data, size_t size)
bool ShaderExtractor::parse() noexcept {
if (mChunkContainer.parse()) {
return mMaterialChunk.readIndex(mMaterialTag);
return mMaterialChunk.initialize(mMaterialTag);
}
return false;
}

View File

@@ -20,84 +20,64 @@
#include <filamat/MaterialBuilder.h>
#include <utils/Log.h>
#include <filaflat/DictionaryReader.h>
#include <filaflat/MaterialChunk.h>
#include <tsl/robin_map.h>
#include <utils/Log.h>
#include <sstream>
#include <GlslangToSpv.h>
#include <smolv.h>
#include "sca/builtinResource.h"
#include "sca/GLSLTools.h"
namespace filament {
namespace matdbg {
#include "eiff/ChunkContainer.h"
#include "eiff/DictionarySpirvChunk.h"
#include "eiff/DictionaryTextChunk.h"
#include "eiff/MaterialSpirvChunk.h"
#include "eiff/MaterialTextChunk.h"
#include "eiff/LineDictionary.h"
namespace filament::matdbg {
using namespace backend;
using namespace filaflat;
using namespace filamat;
using namespace glslang;
using namespace std;
using namespace tsl;
using namespace utils;
using std::ostream;
using std::stringstream;
using std::streampos;
using std::vector;
// Tiny database of shader text that can import / export MaterialTextChunk and DictionaryTextChunk.
class ShaderIndex {
public:
// Consumes a chunk and builds the string list.
void addStringLines(const uint8_t* chunkContent, size_t size);
ShaderIndex(ChunkType dictTag, ChunkType matTag, const filaflat::ChunkContainer& cc);
// Consumes a chunk and builds the shader records.
void addShaderRecords(const uint8_t* chunkContent, size_t size);
// Produces a chunk holding the string list.
void writeLinesChunk(ChunkType tag, ostream& stream) const;
// Produces a chunk holding the shader records.
void writeShadersChunk(ChunkType tag, ostream& stream) const;
void writeChunks(ostream& stream);
// Replaces the specified shader text with new content.
void replaceShader(backend::ShaderModel shaderModel, Variant variant,
ShaderType stage, const char* source, size_t sourceLength);
bool isEmpty() const { return mStringLines.size() == 0 && mShaderRecords.size() == 0; }
bool isEmpty() const { return mShaderRecords.size() == 0; }
private:
struct ShaderRecord {
uint8_t model;
Variant variant;
uint8_t stage;
uint32_t offset;
vector<uint16_t> lineIndices;
string decodedShaderText;
uint32_t stringLength;
};
void decodeShadersFromIndices();
void encodeShadersToIndices();
vector<ShaderRecord> mShaderRecords;
vector<string> mStringLines;
const ChunkType mDictTag;
const ChunkType mMatTag;
vector<TextEntry> mShaderRecords;
};
// Tiny database of data blobs that can import / export MaterialSpirvChunk and DictionarySpirvChunk.
// The blobs are stored *after* they have been compressed by SMOL-V.
class BlobIndex {
public:
// Consumes a chunk and builds the blob list.
void addDataBlobs(const uint8_t* chunkContent, size_t size, streampos ptr);
BlobIndex(ChunkType dictTag, ChunkType matTag, const filaflat::ChunkContainer& cc);
// Consumes a chunk and builds the shader records.
void addShaderRecords(const uint8_t* chunkContent, size_t size);
// Produces a chunk holding the blob list.
void writeBlobsChunk(ChunkType tag, ostream& stream) const;
// Produces a chunk holding the shader records.
void writeShadersChunk(ChunkType tag, ostream& stream) const;
void writeChunks(ostream& stream);
// Replaces the specified shader with new content.
void replaceShader(backend::ShaderModel shaderModel, Variant variant,
@@ -106,17 +86,10 @@ public:
bool isEmpty() const { return mDataBlobs.size() == 0 && mShaderRecords.size() == 0; }
private:
struct ShaderRecord {
uint8_t model;
Variant variant;
uint8_t stage;
uint32_t blobIndex;
};
using SmolvBlob = vector<uint8_t>;
vector<ShaderRecord> mShaderRecords;
vector<SmolvBlob> mDataBlobs;
const ChunkType mDictTag;
const ChunkType mMatTag;
vector<SpirvEntry> mShaderRecords;
filaflat::BlobDictionary mDataBlobs;
};
ShaderReplacer::ShaderReplacer(Backend backend, const void* data, size_t size) :
@@ -159,9 +132,8 @@ bool ShaderReplacer::replaceShaderSource(ShaderModel shaderModel, Variant varian
}
// Clone all chunks except Dictionary* and Material*.
stringstream sstream(string((const char*) cc.getData(), cc.getSize()));
stringstream sstream(std::string((const char*) cc.getData(), cc.getSize()));
stringstream tstream;
ShaderIndex shaderIndex;
{
uint64_t type;
uint32_t size;
@@ -171,12 +143,7 @@ bool ShaderReplacer::replaceShaderSource(ShaderModel shaderModel, Variant varian
sstream.read((char*) &size, sizeof(size));
content.resize(size);
sstream.read((char*) content.data(), size);
if (ChunkType(type) == mDictionaryTag) {
shaderIndex.addStringLines(content.data(), size);
continue;
}
if (ChunkType(type) == mMaterialTag) {
shaderIndex.addShaderRecords(content.data(), size);
if (ChunkType(type) == mDictionaryTag|| ChunkType(type) == mMaterialTag) {
continue;
}
tstream.write((char*) &type, sizeof(type));
@@ -186,10 +153,9 @@ bool ShaderReplacer::replaceShaderSource(ShaderModel shaderModel, Variant varian
}
// Append the new chunks for Dictionary* and Material*.
if (!shaderIndex.isEmpty()) {
if (ShaderIndex shaderIndex(mDictionaryTag, mMaterialTag, cc); !shaderIndex.isEmpty()) {
shaderIndex.replaceShader(shaderModel, variant, stage, sourceString, stringLength);
shaderIndex.writeLinesChunk(mDictionaryTag, tstream);
shaderIndex.writeShadersChunk(mMaterialTag, tstream);
shaderIndex.writeChunks(tstream);
}
// Copy the new package from the stringstream into a ChunkContainer.
@@ -249,9 +215,8 @@ bool ShaderReplacer::replaceSpirv(ShaderModel shaderModel, Variant variant,
// Clone all chunks except Dictionary* and Material*.
filaflat::ChunkContainer const& cc = mOriginalPackage;
stringstream sstream(string((const char*) cc.getData(), cc.getSize()));
stringstream sstream(std::string((const char*) cc.getData(), cc.getSize()));
stringstream tstream;
BlobIndex shaderIndex;
{
uint64_t type;
uint32_t size;
@@ -259,15 +224,9 @@ bool ShaderReplacer::replaceSpirv(ShaderModel shaderModel, Variant variant,
while (sstream) {
sstream.read((char*) &type, sizeof(type));
sstream.read((char*) &size, sizeof(size));
streampos pos = sstream.tellg();
content.resize(size);
sstream.read((char*) content.data(), size);
if (ChunkType(type) == mDictionaryTag) {
shaderIndex.addDataBlobs(content.data(), size, pos);
continue;
}
if (ChunkType(type) == mMaterialTag) {
shaderIndex.addShaderRecords(content.data(), size);
if (ChunkType(type) == mDictionaryTag || ChunkType(type) == mMaterialTag) {
continue;
}
tstream.write((char*) &type, sizeof(type));
@@ -277,10 +236,9 @@ bool ShaderReplacer::replaceSpirv(ShaderModel shaderModel, Variant variant,
}
// Append the new chunks for Dictionary* and Material*.
if (!shaderIndex.isEmpty()) {
if (BlobIndex shaderIndex(mDictionaryTag, mMaterialTag, cc); !shaderIndex.isEmpty()) {
shaderIndex.replaceShader(shaderModel, variant, stage, source, sourceLength);
shaderIndex.writeBlobsChunk(mDictionaryTag, tstream);
shaderIndex.writeShadersChunk(mMaterialTag, tstream);
shaderIndex.writeChunks(tstream);
}
// Copy the new package from the stringstream into a ChunkContainer.
@@ -303,279 +261,138 @@ size_t ShaderReplacer::getEditedSize() const {
return mEditedPackage->getSize();
}
void ShaderIndex::addStringLines(const uint8_t* chunkContent, size_t size) {
uint32_t count = *((const uint32_t*) chunkContent);
mStringLines.resize(count);
const uint8_t* ptr = chunkContent + 4;
for (uint32_t i = 0; i < count; i++) {
mStringLines[i] = string((const char*) ptr);
ptr += mStringLines[i].length() + 1;
ShaderIndex::ShaderIndex(ChunkType dictTag, ChunkType matTag, const filaflat::ChunkContainer& cc) :
mDictTag(dictTag), mMatTag(matTag) {
filaflat::BlobDictionary stringBlobs;
DictionaryReader reader;
reader.unflatten(cc, dictTag, stringBlobs);
filaflat::MaterialChunk matChunk(cc);
matChunk.initialize(matTag);
const auto& offsets = matChunk.getOffsets();
mShaderRecords.reserve(offsets.size());
for (auto [key, offset] : offsets) {
TextEntry info;
filaflat::MaterialChunk::decodeKey(key, &info.shaderModel, &info.variantKey, &info.stage);
ShaderContent content;
UTILS_UNUSED_IN_RELEASE bool success = matChunk.getShader(content,
stringBlobs, info.shaderModel, Variant(info.variantKey), info.stage);
info.shader = std::string(content.data(), content.data() + content.size() - 1);
assert_invariant(success);
mShaderRecords.emplace_back(info);
}
}
void ShaderIndex::addShaderRecords(const uint8_t* chunkContent, size_t size) {
stringstream stream(string((const char*) chunkContent, size));
uint64_t recordCount;
stream.read((char*) &recordCount, sizeof(recordCount));
mShaderRecords.resize(recordCount);
for (auto& record : mShaderRecords) {
stream.read((char*) &record.model, sizeof(ShaderRecord::model));
stream.read((char*) &record.variant, sizeof(ShaderRecord::variant));
stream.read((char*) &record.stage, sizeof(ShaderRecord::stage));
stream.read((char*) &record.offset, sizeof(ShaderRecord::offset));
const auto previousPosition = stream.tellg();
stream.seekg(record.offset);
{
stream.read((char*) &record.stringLength, sizeof(ShaderRecord::stringLength));
uint32_t lineCount;
stream.read((char*) &lineCount, sizeof(lineCount));
record.lineIndices.resize(lineCount);
stream.read((char*) record.lineIndices.data(), lineCount * sizeof(uint16_t));
}
stream.seekg(previousPosition);
}
}
void ShaderIndex::writeLinesChunk(ChunkType tag, ostream& stream) const {
// First perform a prepass to compute chunk size.
uint32_t size = sizeof(uint32_t);
for (const auto& stringLine : mStringLines) {
size += stringLine.length() + 1;
}
// Serialize the chunk.
uint64_t type = tag;
stream.write((char*) &type, sizeof(type));
stream.write((char*) &size, sizeof(size));
uint32_t count = mStringLines.size();
stream.write((char*) &count, sizeof(count));
for (const auto& stringLine : mStringLines) {
stream.write(stringLine.c_str(), stringLine.length() + 1);
}
}
void ShaderIndex::writeShadersChunk(ChunkType tag, ostream& stream) const {
// First perform a prepass to compute chunk size.
uint32_t size = sizeof(uint64_t);
void ShaderIndex::writeChunks(ostream& stream) {
filamat::LineDictionary lines;
for (const auto& record : mShaderRecords) {
size += sizeof(ShaderRecord::model);
size += sizeof(ShaderRecord::variant);
size += sizeof(ShaderRecord::stage);
size += sizeof(ShaderRecord::offset);
}
for (const auto& record : mShaderRecords) {
size += sizeof(ShaderRecord::stringLength);
size += sizeof(uint32_t);
size += record.lineIndices.size() * sizeof(uint16_t);
lines.addText(record.shader);
}
// Serialize the chunk.
uint64_t type = tag;
stream.write((char*) &type, sizeof(type));
stream.write((char*) &size, sizeof(size));
uint64_t recordCount = mShaderRecords.size();
stream.write((char*) &recordCount, sizeof(recordCount));
for (const auto& record : mShaderRecords) {
stream.write((char*) &record.model, sizeof(ShaderRecord::model));
stream.write((char*) &record.variant, sizeof(ShaderRecord::variant));
stream.write((char*) &record.stage, sizeof(ShaderRecord::stage));
stream.write((char*) &record.offset, sizeof(ShaderRecord::offset));
}
for (const auto& record : mShaderRecords) {
uint32_t lineCount = record.lineIndices.size();
stream.write((char*) &record.stringLength, sizeof(ShaderRecord::stringLength));
stream.write((char*) &lineCount, sizeof(lineCount));
stream.write((char*) record.lineIndices.data(), lineCount * sizeof(uint16_t));
}
filamat::ChunkContainer cc;
const auto& dchunk = cc.addChild<DictionaryTextChunk>(std::move(lines), mDictTag);
cc.addChild<MaterialTextChunk>(std::move(mShaderRecords), dchunk.getDictionary(), mMatTag);
const size_t bufSize = cc.getSize();
auto buffer = std::make_unique<uint8_t[]>(bufSize);
Flattener writer(buffer.get());
UTILS_UNUSED_IN_RELEASE const size_t written = cc.flatten(writer);
assert_invariant(written == bufSize);
stream.write((char*)buffer.get(), bufSize);
}
void ShaderIndex::replaceShader(backend::ShaderModel shaderModel, Variant variant,
backend::ShaderType stage, const char* source, size_t sourceLength) {
decodeShadersFromIndices();
const uint8_t model = (uint8_t) shaderModel;
for (auto& record : mShaderRecords) {
if (record.model == model && record.variant == variant && record.stage == stage) {
record.decodedShaderText = std::string(source, sourceLength);
break;
if (record.shaderModel == model && record.variantKey == variant.key &&
record.stage == stage) {
record.shader = std::string(source, sourceLength);
return;
}
}
encodeShadersToIndices();
slog.e << "Failed to replace shader." << io::endl;
}
void ShaderIndex::decodeShadersFromIndices() {
BlobIndex::BlobIndex(ChunkType dictTag, ChunkType matTag, const filaflat::ChunkContainer& cc) :
mDictTag(dictTag), mMatTag(matTag) {
// Decompress SMOL-V.
DictionaryReader reader;
reader.unflatten(cc, mDictTag, mDataBlobs);
filaflat::MaterialChunk matChunk(cc);
matChunk.initialize(matTag);
const auto& offsets = matChunk.getOffsets();
mShaderRecords.reserve(offsets.size());
for (auto [key, offset] : offsets) {
SpirvEntry info;
filaflat::MaterialChunk::decodeKey(key, &info.shaderModel, &info.variantKey, &info.stage);
info.dictionaryIndex = offset;
mShaderRecords.emplace_back(info);
}
}
void BlobIndex::writeChunks(ostream& stream) {
// Convert the filaflat dictionary into a filamat dictionary.
filamat::BlobDictionary blobs;
for (auto& record : mShaderRecords) {
record.decodedShaderText.clear();
for (uint16_t index : record.lineIndices) {
if (index >= mStringLines.size()) {
slog.e << "Internal chunk decoding error." << io::endl;
return;
}
record.decodedShaderText += mStringLines[index] + "\n";
const auto& src = mDataBlobs[record.dictionaryIndex];
assert(src.size() % 4 == 0);
const uint32_t* ptr = (const uint32_t*) src.data();
record.dictionaryIndex = blobs.addBlob(vector<uint32_t>(ptr, ptr + src.size() / 4));
}
// Adjust start cursor of flatteners to match alignment of output stream.
const size_t pad = stream.tellp() % 8;
const auto initialize = [pad](Flattener& f) {
for (size_t i = 0; i < pad; i++) {
f.writeUint8(0);
}
}
}
};
void ShaderIndex::encodeShadersToIndices() {
robin_map<string, uint16_t> table;
for (size_t i = 0; i < mStringLines.size(); i++) {
table[mStringLines[i]] = uint16_t(i);
}
// Apply SMOL-V compression and write out the results.
filamat::ChunkContainer cc;
cc.addChild<MaterialSpirvChunk>(std::move(mShaderRecords));
cc.addChild<DictionarySpirvChunk>(std::move(blobs), false);
uint32_t offset = sizeof(uint64_t);
for (const auto& record : mShaderRecords) {
offset += sizeof(ShaderRecord::model);
offset += sizeof(ShaderRecord::variant);
offset += sizeof(ShaderRecord::stage);
offset += sizeof(ShaderRecord::offset);
}
Flattener prepass = Flattener::getDryRunner();
initialize(prepass);
for (auto& record : mShaderRecords) {
record.stringLength = record.decodedShaderText.length() + 1;
record.lineIndices.clear();
record.offset = offset;
const size_t bufSize = cc.flatten(prepass);
auto buffer = std::make_unique<uint8_t[]>(bufSize);
assert_invariant(intptr_t(buffer.get()) % 8 == 0);
offset += sizeof(ShaderRecord::stringLength);
offset += sizeof(uint32_t);
Flattener writer(buffer.get());
initialize(writer);
UTILS_UNUSED_IN_RELEASE const size_t written = cc.flatten(writer);
const char* const start = record.decodedShaderText.c_str();
const size_t length = record.decodedShaderText.length();
for (size_t cur = 0; cur < length; cur++) {
size_t pos = cur;
size_t len = 0;
while (start[cur] != '\n' && cur < length) {
cur++;
len++;
}
if (pos + len > length) {
slog.e << "Internal chunk encoding error." << io::endl;
return;
}
string newLine(start, pos, len);
auto iter = table.find(newLine);
if (iter == table.end()) {
size_t index = mStringLines.size();
if (index > UINT16_MAX) {
slog.e << "Chunk encoding error: too many unique codelines." << io::endl;
return;
}
record.lineIndices.push_back(index);
table[newLine] = index;
mStringLines.push_back(newLine);
continue;
}
record.lineIndices.push_back(iter->second);
}
offset += sizeof(uint16_t) * record.lineIndices.size();
}
}
void BlobIndex::addDataBlobs(const uint8_t* chunkContent, size_t size, streampos pos) {
const uint8_t* ptr = chunkContent;
const uint32_t compression = *((const uint32_t*) ptr);
ptr += 4;
const uint32_t blobCount = *((const uint32_t*) ptr);
ptr += 4;
mDataBlobs.resize(blobCount);
for (uint32_t i = 0; i < blobCount; i++) {
// Skip alignment padding.
ptr += (8 - (intptr_t(pos + ptr - chunkContent) % 8)) % 8;
// Read byte count, advance cursor, and allocate buffer.
const uint64_t byteCount = *((const uint64_t*) ptr);
ptr += sizeof(uint64_t);
mDataBlobs[i].resize(byteCount);
// Copy the buffer and advance the cursor.
memcpy(mDataBlobs[i].data(), ptr, byteCount);
ptr += byteCount;
}
}
void BlobIndex::addShaderRecords(const uint8_t* chunkContent, size_t size) {
stringstream stream(string((const char*) chunkContent, size));
uint64_t recordCount;
stream.read((char*) &recordCount, sizeof(recordCount));
mShaderRecords.resize(recordCount);
for (auto& record : mShaderRecords) {
stream.read((char*) &record.model, sizeof(ShaderRecord::model));
stream.read((char*) &record.variant, sizeof(ShaderRecord::variant));
stream.read((char*) &record.stage, sizeof(ShaderRecord::stage));
stream.read((char*) &record.blobIndex, sizeof(ShaderRecord::blobIndex));
}
}
void BlobIndex::writeBlobsChunk(ChunkType tag, ostream& stream) const {
const uint64_t type = tag;
uint32_t size = sizeof(uint32_t) + sizeof(uint32_t);
// First perform a prepass to compute chunk size.
streampos offset = stream.tellp() + streampos(sizeof(type) + sizeof(size));
for (const auto& blob : mDataBlobs) {
size += (8 - ((size + offset) % 8)) % 8;
size += sizeof(uint64_t);
size += blob.size();
}
// Serialize the chunk.
stream.write((char*) &type, sizeof(type));
stream.write((char*) &size, sizeof(size));
const uint32_t compression = 1;
stream.write((char*) &compression, sizeof(compression));
const uint32_t count = mDataBlobs.size();
stream.write((char*) &count, sizeof(count));
const char padding[8] = {};
for (const auto& blob : mDataBlobs) {
const uint64_t byteCount = blob.size();
stream.write(padding, (8 - (stream.tellp() % 8)) % 8);
stream.write((char*) &byteCount, sizeof(byteCount));
stream.write((char*) blob.data(), blob.size());
}
}
void BlobIndex::writeShadersChunk(ChunkType tag, ostream& stream) const {
// First perform a prepass to compute chunk size.
uint32_t size = sizeof(uint64_t);
for (const auto& record : mShaderRecords) {
size += sizeof(ShaderRecord::model);
size += sizeof(ShaderRecord::variant);
size += sizeof(ShaderRecord::stage);
size += sizeof(ShaderRecord::blobIndex);
}
// Serialize the chunk.
uint64_t type = tag;
stream.write((char*) &type, sizeof(type));
stream.write((char*) &size, sizeof(size));
const uint64_t recordCount = mShaderRecords.size();
stream.write((char*) &recordCount, sizeof(recordCount));
for (const auto& record : mShaderRecords) {
stream.write((char*) &record.model, sizeof(ShaderRecord::model));
stream.write((char*) &record.variant, sizeof(ShaderRecord::variant));
stream.write((char*) &record.stage, sizeof(ShaderRecord::stage));
stream.write((char*) &record.blobIndex, sizeof(ShaderRecord::blobIndex));
}
assert_invariant(written == bufSize);
stream.write((char*)buffer.get() + pad, bufSize - pad);
}
void BlobIndex::replaceShader(ShaderModel shaderModel, Variant variant,
ShaderType stage, const char* source, size_t sourceLength) {
smolv::ByteArray compressed;
if (!smolv::Encode(source, sourceLength, compressed, 0)) {
utils::slog.e << "Error with SPIRV compression" << utils::io::endl;
return;
}
const uint8_t model = (uint8_t) shaderModel;
for (auto& record : mShaderRecords) {
if (record.model == model && record.variant == variant && record.stage == stage) {
auto& blob = mDataBlobs[record.blobIndex];
blob.resize(compressed.size());
memcpy(blob.data(), compressed.data(), compressed.size());
break;
if (record.shaderModel == model && record.variantKey == variant.key &&
record.stage == stage) {
// TODO: because a single blob entry might be used by more than one variant, matdbg
// users may unwittingly edit more than 1 variant when multiple variants have the exact
// same content before the edit. In practice this is rarely problematic, but we should
// perhaps fix this one day.
auto& blob = mDataBlobs[record.dictionaryIndex];
blob.reserve(sourceLength);
blob.resize(sourceLength);
memcpy(blob.data(), source, sourceLength);
return;
}
}
slog.e << "Unable to replace shader." << io::endl;
}
} // namespace matdbg
} // namespace filament
} // namespace filament::matdbg

View File

@@ -491,11 +491,22 @@ template<typename T>
constexpr TMat44<T> TMat44<T>::frustum(T left, T right, T bottom, T top, T near, T far) noexcept {
TMat44<T> m;
m[0][0] = (2 * near) / (right - left);
// 0
// 0
// 0
// 0
m[1][1] = (2 * near) / (top - bottom);
// 0
// 0
m[2][0] = (right + left) / (right - left);
m[2][1] = (top + bottom) / (top - bottom);
m[2][2] = -(far + near) / (far - near);
m[2][3] = -1;
// 0
// 0
m[3][2] = -(2 * far * near) / (far - near);
m[3][3] = 0;
return m;

View File

@@ -360,7 +360,8 @@ int main(int argc, char** argv) {
const auto model = camera.getModelMatrix();
const auto renderingProjection = camera.getProjectionMatrix();
const auto cullingProjection = camera.getCullingProjectionMatrix();
app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection, camera.getNear(), camera.getCullingFar());
app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection,
camera.getNear(), camera.getCullingFar());
switch (app.mode) {
case App::ReflectionMode::RENDERABLES:
tcm.setTransform(tcm.getInstance(app.reflectedMonkey), reflection * xform);

View File

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