Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9c5bbf185 | ||
|
|
8dd4bff7a7 | ||
|
|
77c54446af | ||
|
|
4a0bc0af57 | ||
|
|
ce33fda6ec | ||
|
|
88768e8003 | ||
|
|
4f1dd8b304 | ||
|
|
42cae27992 | ||
|
|
13f646025b | ||
|
|
e7c9197d07 | ||
|
|
7afd5e5963 | ||
|
|
d73453863d | ||
|
|
8b88638232 | ||
|
|
677cdc1239 | ||
|
|
a4d3ffe7d4 | ||
|
|
3ada971d8a | ||
|
|
6588cc30ea | ||
|
|
09f188659a |
@@ -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
|
||||
|
||||
@@ -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)`
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 {
|
||||
|
||||
167
filament/backend/test/test_Scissor.cpp
Normal file
167
filament/backend/test/test_Scissor.cpp
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ void Renderer::endFrame() {
|
||||
}
|
||||
|
||||
double Renderer::getUserTime() const {
|
||||
return upcast(this)->getUserTime().count();
|
||||
return upcast(this)->getUserTime();
|
||||
}
|
||||
|
||||
void Renderer::resetUserTime() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <math/mat4.h>
|
||||
#include <math/TVecHelpers.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament::gltfio {
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user