Compare commits

..
Author SHA1 Message Date
Eliza Velasquez ab552d3619 wip: color space stuff 2023-11-13 14:32:31 -08:00
61 changed files with 289 additions and 743 deletions
-2
View File
@@ -41,8 +41,6 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
+8
View File
@@ -8,3 +8,11 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel`
- matc: Enable `GL_OES_standard_derivatives` extension in ESSL 1.0 shaders
- matc: Fix code generation of double sided and masked materials in ESSL 1.0 shaders
- filagui: Add support for feature level 0
- matc: Add support for post-process materials in feature level 0
- engine: Add `Material::getFeatureLevel()`
- engine: Add missing `Material::getReflectionMode()` method in Java
- engine: Support basic usage of post-processing materials on feature level 0
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.46.0'
implementation 'com.google.android.filament:filament-android:1.45.0'
}
```
@@ -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:
```shell
pod 'Filament', '~> 1.46.0'
pod 'Filament', '~> 1.45.0'
```
### Snapshots
-14
View File
@@ -7,20 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.46.0
- engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel`
- matc: Enable `GL_OES_standard_derivatives` extension in ESSL 1.0 shaders
- matc: Fix code generation of double sided and masked materials in ESSL 1.0 shaders
- filagui: Add support for feature level 0
- matc: Add support for post-process materials in feature level 0
- engine: Add `Material::getFeatureLevel()`
- engine: Add missing `Material::getReflectionMode()` method in Java
- engine: Support basic usage of post-processing materials on feature level 0
- engine: Fix critical GLES 2.0 bugs
- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament
without FL0 support.
## v1.45.1
- engine: Added parameter for configuring JobSystem thread count
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.46.0
VERSION_NAME=1.45.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -58
View File
@@ -218,6 +218,7 @@ set(MATERIAL_SRCS
src/materials/colorGrading/customResolveAsSubpass.mat
src/materials/debugShadowCascades.mat
src/materials/defaultMaterial.mat
src/materials/defaultMaterial0.mat
src/materials/dof/dof.mat
src/materials/dof/dofCoc.mat
src/materials/dof/dofDownsample.mat
@@ -228,6 +229,7 @@ set(MATERIAL_SRCS
src/materials/dof/dofMipmap.mat
src/materials/dof/dofMedian.mat
src/materials/flare/flare.mat
src/materials/blitLow.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
@@ -236,6 +238,7 @@ set(MATERIAL_SRCS
src/materials/ssao/bilateralBlurBentNormals.mat
src/materials/ssao/mipmapDepth.mat
src/materials/skybox.mat
src/materials/skybox0.mat
src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat
src/materials/separableGaussianBlur.mat
@@ -244,18 +247,6 @@ set(MATERIAL_SRCS
src/materials/vsmMipmap.mat
)
set(MATERIAL_FL0_SRCS
src/materials/defaultMaterial0.mat
src/materials/skybox0.mat
)
# TODO(exv): Replace the below duplicated materials with a command-line option
# to matc to force disable including ESSL 1.0 code in FL0 materials.
set(MATERIAL_SWITCH_FL0_SRCS_HACK
src/materials/blitLow.mat
)
# Embed the binary resource blob for materials.
get_resgen_vars(${RESOURCE_DIR} materials)
list(APPEND PRIVATE_HDRS ${RESGEN_HEADER})
@@ -280,11 +271,6 @@ if (NOT DFG_LUT_SIZE)
endif()
message(STATUS "DFG LUT size set to ${DFG_LUT_SIZE}x${DFG_LUT_SIZE}")
# Whether to include FL0 materials.
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -326,47 +312,6 @@ foreach (mat_src ${MATERIAL_SRCS})
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
# HACK: Pick between the normal and FL0-exclusionary variants of the materials
# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0.
foreach (mat_src ${MATERIAL_SWITCH_FL0_SRCS_HACK})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0)
# Pick the non-FL0 variant instead.
string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}")
string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}")
endif()
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach()
if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL)
foreach (mat_src ${MATERIAL_FL0_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
add_custom_command(
OUTPUT ${output_path}
COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
COMMENT "Compiling material ${mat_src} to ${output_path}"
)
list(APPEND MATERIAL_BINS ${output_path})
endforeach ()
endif ()
# Additional dependencies on included files for materials
add_custom_command(
@@ -356,6 +356,13 @@ enum class SamplerFormat : uint8_t {
SHADOW = 3 //!< shadow sampler (PCF)
};
//! Texture color space
enum class SamplerTransferFunction : uint8_t {
UNDEFINED = 0, //!< don't care
IDENTITY = 1, //!< sampled as-is
SRGB_TO_LINEAR = 2, //!< sampled as sRGB and converted to linear
};
/**
* Supported element types
*/
+6 -12
View File
@@ -172,7 +172,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
mShaderCompilerService(*this),
mHandleAllocator("Handles", driverConfig.handleArenaSize),
mSamplerMap(32) {
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
// set a reasonable default value for our stream array
@@ -268,9 +268,9 @@ void OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
if (UTILS_UNLIKELY(mContext.isES2())) {
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
auto [id, buffer, age] = mUniformBindings[i];
auto [buffer, age] = mUniformBindings[i];
if (buffer) {
p->updateUniforms(i, id, buffer, age);
p->updateUniforms(i, buffer, age);
}
}
// Set the output colorspace for this program (linear or rec709). This in only relevant
@@ -479,7 +479,6 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
GLBufferObject* bo = construct<GLBufferObject>(boh, byteCount, bindingType, usage);
if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
bo->gl.id = ++mLastAssignedEmulatedUboId;
bo->gl.buffer = malloc(byteCount);
memset(bo->gl.buffer, 0, byteCount);
} else {
@@ -596,9 +595,8 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
}
} else {
glTexImage2D(t->gl.target, level, GLint(t->gl.internalFormat),
std::max(GLsizei(1), GLsizei(width >> level)),
std::max(GLsizei(1), GLsizei(height >> level)),
0, format, type, nullptr);
GLsizei(width), GLsizei(height), 0,
format, type, nullptr);
}
}
}
@@ -2903,11 +2901,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
assert_invariant(offset + size <= ub->byteCount);
if (UTILS_UNLIKELY(ub->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
mUniformBindings[index] = {
ub->gl.id,
static_cast<uint8_t const*>(ub->gl.buffer) + offset,
ub->age,
};
mUniformBindings[index] = { static_cast<uint8_t const*>(ub->gl.buffer) + offset, ub->age };
} else {
GLenum const target = GLUtils::getBufferBindingType(bindingType);
+5 -4
View File
@@ -83,9 +83,11 @@ public:
}
struct {
GLuint id;
union {
GLenum binding;
struct {
GLuint id;
GLenum binding;
};
void* buffer;
};
} gl;
@@ -361,8 +363,7 @@ private:
void setScissor(Viewport const& scissor) noexcept;
// ES2 only. Uniform buffer emulation binding points
GLuint mLastAssignedEmulatedUboId = 0;
std::array<std::tuple<GLuint, void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
std::array<std::pair<void const*, uint16_t>, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {};
// sampler buffer binding points (nullptr if not used)
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
@@ -229,16 +229,15 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLProgram::updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept {
void OpenGLProgram::updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept {
assert_invariant(mUniformsRecords);
assert_invariant(buffer);
// only update the uniforms if the UBO has changed since last time we updated
UniformsRecord const& records = mUniformsRecords[index];
if (records.id == id && records.age == age) {
if (records.age == age) {
return;
}
records.id = id;
records.age = age;
assert_invariant(records.uniforms.size() == records.locations.size());
+1 -2
View File
@@ -72,7 +72,7 @@ public:
} gl; // 12 bytes
// For ES2 only
void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept;
void updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept;
void setRec709ColorSpace(bool rec709) const noexcept;
private:
@@ -98,7 +98,6 @@ private:
struct UniformsRecord {
Program::UniformInfo uniforms;
LocationInfo locations;
mutable GLuint id = 0;
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
};
UniformsRecord const* mUniformsRecords = nullptr;
+17 -9
View File
@@ -268,18 +268,13 @@ bool VulkanCommands::flush() {
VK_NULL_HANDLE,
VK_NULL_HANDLE,
};
uint32_t waitSemaphoreCount = 0;
if (mSubmissionSignal) {
signals[waitSemaphoreCount++] = mSubmissionSignal;
}
if (mInjectedSignal) {
signals[waitSemaphoreCount++] = mInjectedSignal;
}
VkCommandBuffer const cmdbuffer = currentbuf->buffer();
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = waitSemaphoreCount,
.pWaitSemaphores = waitSemaphoreCount > 0 ? signals : nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = signals,
.pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1,
.pCommandBuffers = &cmdbuffer,
@@ -287,6 +282,19 @@ bool VulkanCommands::flush() {
.pSignalSemaphores = &renderingFinished,
};
if (mSubmissionSignal) {
signals[submitInfo.waitSemaphoreCount++] = mSubmissionSignal;
}
if (mInjectedSignal) {
signals[submitInfo.waitSemaphoreCount++] = mInjectedSignal;
}
// To address a validation warning.
if (submitInfo.waitSemaphoreCount == 0) {
submitInfo.pWaitSemaphores = VK_NULL_HANDLE;
}
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
slog.i << "Submitting cmdbuffer=" << cmdbuffer
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
@@ -99,16 +99,11 @@ void VulkanSwapChain::present() {
mColors[mCurrentSwapIndex]->transitionLayout(cmdbuf, subresources, VulkanLayout::PRESENT);
}
mCommands->flush();
// We only present if it is not headless. No-op for headless (but note that we still need the
// flush() in the above line).
if (!mHeadless) {
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
result == VK_ERROR_OUT_OF_DATE_KHR,
"Cannot present in swapchain.");
}
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
ASSERT_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR
|| result == VK_ERROR_OUT_OF_DATE_KHR,
"Cannot present in swapchain.");
// We presented the last acquired buffer.
mAcquired = false;
+7 -1
View File
@@ -426,6 +426,7 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
uint8_t fieldFormat = 0;
uint8_t fieldPrecision = 0;
bool fieldMultisample = false;
uint8_t fieldTransferFunction = 0;
if (!unflattener.read(&fieldName)) {
return false;
@@ -447,10 +448,15 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener,
return false;
}
if (!unflattener.read(&fieldTransferFunction)) {
return false;
}
builder.add({ fieldName.data(), fieldName.size() }, SamplerInterfaceBlock::Type(fieldType),
SamplerInterfaceBlock::Format(fieldFormat),
SamplerInterfaceBlock::Precision(fieldPrecision),
fieldMultisample);
fieldMultisample,
SamplerInterfaceBlock::SamplerTransferFunction(fieldTransferFunction));
}
*sib = builder.build();
+1 -2
View File
@@ -2714,8 +2714,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
backend::SamplerMagFilter filter) noexcept {
if (UTILS_LIKELY(!translucent && dsrOptions.quality == QualityLevel::LOW &&
mEngine.getDriverApi().getFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) {
if (UTILS_LIKELY(!translucent && dsrOptions.quality == QualityLevel::LOW)) {
return opaqueBlit(fg, input, vp, outDesc, filter);
}
+1 -4
View File
@@ -108,13 +108,11 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
ShadowMapInfo const& shadowMapInfo,
SceneInfo const& sceneInfo) noexcept {
// reset the visible shadow status
mHasVisibleShadows = false;
FLightManager const& lcm = engine.getLightManager();
FLightManager::Instance const li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
// We can't use LISPSM in stable mode
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
@@ -139,7 +137,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
*/
mat4f W, Wp, L;
// We can't use LISPSM in stable mode
const bool useLispsm = params.options.lispsm && !params.options.stable;
if (useLispsm) {
// Orient the shadow map in the direction of the view vector by constructing a
+1 -1
View File
@@ -522,7 +522,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = cascadedShadowMaps[0];
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(0);
const auto direction = options.transform * lightData.elementAt<FScene::DIRECTION>(0);
// We compute the directional light's model matrix using the origin's as the light position.
// The choice of the light's origin initially doesn't matter for a directional light.
+1 -8
View File
@@ -244,10 +244,6 @@ void FEngine::init() {
mActiveFeatureLevel = std::min(mActiveFeatureLevel, driverApi.getFeatureLevel());
#ifndef FILAMENT_ENABLE_FEATURE_LEVEL_0
assert_invariant(mActiveFeatureLevel > FeatureLevel::FEATURE_LEVEL_0);
#endif
slog.i << "Backend feature level: " << int(driverApi.getFeatureLevel()) << io::endl;
slog.i << "FEngine feature level: " << int(mActiveFeatureLevel) << io::endl;
@@ -332,15 +328,12 @@ void FEngine::init() {
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
{ zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE });
#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0
if (UTILS_UNLIKELY(mActiveFeatureLevel == FeatureLevel::FEATURE_LEVEL_0)) {
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
defaultMaterialBuilder.package(
MATERIALS_DEFAULTMATERIAL0_DATA, MATERIALS_DEFAULTMATERIAL0_SIZE);
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast<FEngine*>(this)));
} else
#endif
{
} else {
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
FMaterial::DefaultMaterialBuilder defaultMaterialBuilder;
+8 -6
View File
@@ -267,16 +267,16 @@ void FScene::prepare(utils::JobSystem& js,
// in the code below, we only transform directions, so the translation of the
// world transform is irrelevant, and we don't need to use getWorldTransformAccurate()
mat3 const worldDirectionTransform =
mat3::getTransformForNormals(tcm.getWorldTransformAccurate(ti).upperLeft());
FLightManager::ShadowParams const params = lcm.getShadowParams(li);
float3 const localDirection = worldDirectionTransform * lcm.getLocalDirection(li);
double3 const shadowLocalDirection = params.options.transform * localDirection;
float3 const localDirection = lcm.getLocalDirection(li);
float3 const shadowLocalDirection = params.options.transform * localDirection;
mat3 const worldDirectionTransform = tcm.getWorldTransformAccurate(ti).upperLeft();
mat3 const shaderWorldTransform = worldTransform.upperLeft() * worldDirectionTransform;
// using mat3::getTransformForNormals handles non-uniform scaling
// note: in the common case of the rigid-body transform, getTransformForNormals() returns
// identity.
mat3 const worlTransformNormals = mat3::getTransformForNormals(worldTransform.upperLeft());
mat3 const worlTransformNormals = mat3::getTransformForNormals(shaderWorldTransform);
double3 const d = worlTransformNormals * localDirection;
double3 const s = worlTransformNormals * shadowLocalDirection;
@@ -290,8 +290,10 @@ void FScene::prepare(utils::JobSystem& js,
// is pointing down, which is a common case for lights. See ShadowMap.cpp.
return transpose(mat3::lookTo(direction, double3{ 1, 0, 0 }));
};
double3 const worldDirection =
mat3::getTransformForNormals(worldDirectionTransform) * shadowLocalDirection;
double3 const worldOrigin = transpose(worldTransform.upperLeft()) * worldTransform[3].xyz;
mat3 const Mv = getMv(shadowLocalDirection);
mat3 const Mv = getMv(worldDirection);
double2 const lsReferencePoint = (Mv * worldOrigin).xy;
constexpr float inf = std::numeric_limits<float>::infinity();
+1 -4
View File
@@ -118,12 +118,9 @@ FSkybox::FSkybox(FEngine& engine, const Builder& builder) noexcept
FMaterial const* FSkybox::createMaterial(FEngine& engine) {
Material::Builder builder;
#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0
if (UTILS_UNLIKELY(engine.getActiveFeatureLevel() == Engine::FeatureLevel::FEATURE_LEVEL_0)) {
builder.package(MATERIALS_SKYBOX0_DATA, MATERIALS_SKYBOX0_SIZE);
} else
#endif
{
} else {
builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE);
}
auto material = builder.build(engine);
+6 -6
View File
@@ -82,22 +82,22 @@ uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder
RenderPassData data;
data.name = name;
data.descriptor = descriptor;
FrameGraphRenderPass::Attachments const& attachments = data.descriptor.attachments;
// retrieve the ResourceNode of the attachments coming to us -- this will be used later
// to compute the discard flags.
DependencyGraph const& dependencyGraph = fg.getGraph();
auto incomingEdges = dependencyGraph.getIncomingEdges(this);
auto outgoingEdges = dependencyGraph.getOutgoingEdges(this);
for (size_t i = 0; i < RenderPassData::ATTACHMENT_COUNT; i++) {
FrameGraphId<FrameGraphTexture> const& handle =
data.descriptor.attachments.array[i];
if (handle) {
data.attachmentInfo[i] = handle;
if (descriptor.attachments.array[i]) {
data.attachmentInfo[i] = attachments.array[i];
// TODO: this is not very efficient
auto incomingPos = std::find_if(incomingEdges.begin(), incomingEdges.end(),
[&dependencyGraph, handle]
[&dependencyGraph, handle = descriptor.attachments.array[i]]
(DependencyGraph::Edge const* edge) {
ResourceNode const* node = static_cast<ResourceNode const*>(
dependencyGraph.getNode(edge->from));
@@ -111,7 +111,7 @@ uint32_t RenderPassNode::declareRenderTarget(FrameGraph& fg, FrameGraph::Builder
}
// this could be either outgoing or incoming (if there are no outgoing)
data.outgoing[i] = fg.getActiveResourceNode(handle);
data.outgoing[i] = fg.getActiveResourceNode(descriptor.attachments.array[i]);
if (data.outgoing[i] == data.incoming[i]) {
data.outgoing[i] = nullptr;
}
-39
View File
@@ -1,39 +0,0 @@
material {
name : blitLow,
parameters : [
{
type : sampler2d,
name : color,
precision: medium
},
{
type : float4,
name : resolution,
precision: high
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
vertex
],
depthWrite : false,
depthCulling : false,
domain: postprocess,
}
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy);
}
}
fragment {
void postProcess(inout PostProcessInputs postProcess) {
postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0);
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.46.0"
spec.version = "1.45.0"
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.46.0/filament-v1.46.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.45.0/filament-v1.45.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 46;
static constexpr size_t MATERIAL_VERSION = 45;
/**
* Supported shading models
@@ -46,6 +46,7 @@ public:
using Format = backend::SamplerFormat;
using Precision = backend::Precision;
using SamplerParams = backend::SamplerParams;
using SamplerTransferFunction = backend::SamplerTransferFunction;
struct SamplerInfo { // NOLINT(cppcoreguidelines-pro-type-member-init)
utils::CString name; // name of this sampler
@@ -55,6 +56,7 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample; // multisample capable
SamplerTransferFunction transferFunction;
};
class Builder {
@@ -73,6 +75,7 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample = false; // multisample capable
SamplerTransferFunction transferFunction;
};
// Give a name to this sampler interface block
@@ -83,7 +86,8 @@ public:
// Add a sampler
Builder& add(std::string_view samplerName, Type type, Format format,
Precision precision = Precision::MEDIUM,
bool multisample = false) noexcept;
bool multisample = false,
SamplerTransferFunction transferFunction = SamplerTransferFunction::UNDEFINED) noexcept;
// Add multiple samplers
Builder& add(std::initializer_list<ListEntry> list) noexcept;
@@ -44,10 +44,10 @@ SamplerInterfaceBlock::Builder::stageFlags(backend::ShaderStageFlags stageFlags)
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::string_view samplerName, Type type, Format format,
Precision precision, bool multisample) noexcept {
Precision precision, bool multisample, SamplerTransferFunction transferFunction) noexcept {
mEntries.push_back({
{ samplerName.data(), samplerName.size() }, { },
uint8_t(mEntries.size()), type, format, precision, multisample });
uint8_t(mEntries.size()), type, format, precision, multisample, transferFunction });
return *this;
}
@@ -58,7 +58,7 @@ SamplerInterfaceBlock SamplerInterfaceBlock::Builder::build() {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::initializer_list<ListEntry> list) noexcept {
for (auto& e : list) {
add(e.name, e.type, e.format, e.precision, e.multisample);
add(e.name, e.type, e.format, e.precision, e.multisample, e.transferFunction);
}
return *this;
}
-9
View File
@@ -35,19 +35,10 @@ set(MATERIAL_SRCS
file(MAKE_DIRECTORY ${MATERIAL_DIR})
# HACK: Pick between the normal and FL0-exclusionary variants of the materials
# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0.
foreach (mat_src ${MATERIAL_SRCS})
get_filename_component(localname "${mat_src}" NAME_WE)
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0)
# Pick the non-FL0 variant instead.
string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}")
string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}")
endif()
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
-28
View File
@@ -1,28 +0,0 @@
material {
name : uiBlit,
parameters : [
{
type : sampler2d,
name : albedo
}
],
requires : [
uv0,
color
],
shadingModel : unlit,
culling : none,
depthCulling: false,
blending : transparent,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec2 uv = getUV0();
uv.y = 1.0 - uv.y;
vec4 albedo = texture(materialParams_albedo, uv);
material.baseColor = getColor() * albedo;
material.baseColor.rgb *= material.baseColor.a;
}
}
+2
View File
@@ -31,6 +31,7 @@ using SamplerType = MaterialBuilder::SamplerType;
using SubpassType = MaterialBuilder::SubpassType;
using SamplerFormat = MaterialBuilder::SamplerFormat;
using ParameterPrecision = MaterialBuilder::ParameterPrecision;
using SamplerTransferFunction = MaterialBuilder::SamplerTransferFunction;
using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
@@ -75,6 +76,7 @@ private:
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string, SamplerTransferFunction> mStringToSamplerTransferFunction;
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
+12 -9
View File
@@ -239,6 +239,7 @@ public:
using SubpassType = filament::backend::SubpassType;
using SamplerFormat = filament::backend::SamplerFormat;
using ParameterPrecision = filament::backend::Precision;
using SamplerTransferFunction = filament::backend::SamplerTransferFunction;
using Precision = filament::backend::Precision;
using CullingMode = filament::backend::CullingMode;
using FeatureLevel = filament::backend::FeatureLevel;
@@ -307,11 +308,8 @@ public:
*/
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT,
SamplerTransferFunction transferFunction = SamplerTransferFunction::UNDEFINED) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -618,16 +616,20 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { }
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p,
SamplerTransferFunction tf)
: name(paramName), size(1), precision(p), samplerType(t), format(f),
parameterType(SAMPLER), transferFunction(tf) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
: name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { }
: name(paramName), size(typeSize), uniformType(t), precision(p),
parameterType(UNIFORM) { }
// Subpass
Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { }
: name(paramName), size(1), precision(p), subpassType(t), format(f),
parameterType(SUBPASS) { }
utils::CString name;
size_t size;
@@ -636,6 +638,7 @@ public:
SamplerType samplerType;
SubpassType subpassType;
SamplerFormat format;
SamplerTransferFunction transferFunction;
enum {
INVALID,
UNIFORM,
+11
View File
@@ -117,6 +117,17 @@ std::unordered_map<std::string, ParameterPrecision>& Enums::getMap<ParameterPrec
return mStringToSamplerPrecision;
};
std::unordered_map<std::string, SamplerTransferFunction> Enums::mStringToSamplerTransferFunction = {
{ "undefined", SamplerTransferFunction::UNDEFINED },
{ "identity", SamplerTransferFunction::IDENTITY },
{ "srgbToLinear", SamplerTransferFunction::SRGB_TO_LINEAR },
};
template <>
std::unordered_map<std::string, SamplerTransferFunction>& Enums::getMap<SamplerTransferFunction>() noexcept {
return mStringToSamplerTransferFunction;
};
std::unordered_map<std::string, OutputTarget> Enums::mStringToOutputTarget = {
{ "color", OutputTarget::COLOR },
{ "depth", OutputTarget::DEPTH }
+5 -9
View File
@@ -248,19 +248,14 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType type,
return parameter(name, 1, type, precision);
}
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision) noexcept {
SamplerFormat format, ParameterPrecision precision,
SamplerTransferFunction transferFunction) noexcept {
ASSERT_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT, "Too many parameters");
mParameters[mParameterCount++] = { name, samplerType, format, precision };
mParameters[mParameterCount++] = { name, samplerType, format, precision, transferFunction };
return *this;
}
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
ParameterPrecision precision) noexcept {
return parameter(name, samplerType, SamplerFormat::FLOAT, precision);
}
template<typename T, typename>
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType type, T defaultValue) {
auto result = std::find_if(mConstants.begin(), mConstants.end(), [name](const Constant& c) {
@@ -556,7 +551,8 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() },
param.samplerType, param.format, param.precision);
param.samplerType, param.format, param.precision, /*multisample=*/false,
param.transferFunction);
} else if (param.isUniform()) {
ibb.add({{{ param.name.data(), param.name.size() },
uint32_t(param.size == 1u ? 0u : param.size), param.uniformType,
@@ -65,6 +65,7 @@ void MaterialSamplerInterfaceBlockChunk::flatten(Flattener& f) {
f.writeUint8(static_cast<uint8_t>(sInfo.format));
f.writeUint8(static_cast<uint8_t>(sInfo.precision));
f.writeBool(sInfo.multisample);
f.writeUint8(static_cast<uint8_t>(sInfo.transferFunction));
}
}
+5 -6
View File
@@ -176,11 +176,6 @@ if (WEBGL_PTHREADS)
target_compile_definitions(gltfio_core PUBLIC -DFILAMENT_WASM_THREADS)
endif()
set(GLTFIO_WARNINGS -Wall -Werror)
if (NOT MSVC)
target_compile_options(gltfio_core PRIVATE ${GLTFIO_WARNINGS})
endif()
if (NOT WEBGL AND NOT ANDROID AND NOT IOS)
# ==================================================================================================
@@ -190,6 +185,10 @@ if (NOT WEBGL AND NOT ANDROID AND NOT IOS)
target_link_libraries(${TARGET} PUBLIC filamat gltfio_core)
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
# ==================================================================================================
# Compiler flags
# ==================================================================================================
if (NOT MSVC)
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)
endif()
@@ -230,7 +229,7 @@ if (TNT_DEV AND NOT WEBGL AND NOT ANDROID AND NOT IOS)
target_link_libraries(${TEST_TARGET} PRIVATE ${TARGET} gtest uberarchive)
if (NOT MSVC)
target_compile_options(${TEST_TARGET} PRIVATE ${GLTFIO_WARNINGS})
target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register)
endif()
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER Tests)
endif()
+2 -4
View File
@@ -69,12 +69,9 @@ class UTILS_PUBLIC ResourceLoader {
public:
using BufferDescriptor = filament::backend::BufferDescriptor;
explicit ResourceLoader(const ResourceConfiguration& config);
ResourceLoader(const ResourceConfiguration& config);
~ResourceLoader();
void setConfiguration(const ResourceConfiguration& config);
/**
* Feeds the binary content of an external resource into the loader's URI cache.
*
@@ -157,6 +154,7 @@ public:
private:
bool loadResources(FFilamentAsset* asset, bool async);
void normalizeSkinningWeights(FFilamentAsset* asset) const;
AssetPool* mPool;
struct Impl;
Impl* pImpl;
};
+2 -2
View File
@@ -42,7 +42,6 @@
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
#include <utils/EntityManager.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
@@ -769,6 +768,7 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const
void FAssetLoader::createMaterialVariants(const cgltf_mesh* mesh, Entity entity,
FFilamentAsset* fAsset, FFilamentInstance* instance) {
const cgltf_data* srcAsset = fAsset->mSourceAsset->hierarchy;
UvMap uvmap {};
for (cgltf_size prim = 0, n = mesh->primitives_count; prim < n; ++prim) {
const cgltf_primitive& srcPrim = mesh->primitives[prim];
@@ -1076,7 +1076,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na
.build(mEngine);
outPrim->targets = targets;
fAsset->mMorphTargetBuffers.push_back(targets);
UTILS_UNUSED_IN_RELEASE cgltf_accessor const* previous = nullptr;
const cgltf_accessor* previous = nullptr;
for (int tindex = 0; tindex < targetsCount; ++tindex) {
const cgltf_morph_target& inTarget = inPrim.targets[tindex];
for (cgltf_size aindex = 0; aindex < inTarget.attributes_count; ++aindex) {
+1 -2
View File
@@ -20,7 +20,6 @@
#include <draco/compression/decode.h>
#endif
#include <utils/compiler.h>
#include <utils/Log.h>
#if GLTFIO_DRACO_SUPPORTED
@@ -66,7 +65,7 @@ DracoMesh::~DracoMesh() {
}
// Gets the number of components in the given cgltf vector type, or -1 for matrices.
UTILS_UNUSED_IN_RELEASE static int getNumComponents(cgltf_type ctype) {
static int getNumComponents(cgltf_type ctype) {
return ((int) ctype) <= 4 ? ((int) ctype) : -1;
}
+1 -1
View File
@@ -71,7 +71,7 @@ namespace utils {
namespace filament::gltfio {
struct Wireframe;
class Wireframe;
// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer().
struct BufferSlot {
+1 -1
View File
@@ -116,7 +116,7 @@ Texture* Ktx2Provider::pushTexture(const uint8_t* data, size_t byteCount,
}
JobSystem* js = &mEngine->getJobSystem();
item->job = jobs::createJob(*js, mDecoderRootJob, [item] {
item->job = jobs::createJob(*js, mDecoderRootJob, [this, item] {
using Result = ktxreader::Ktx2Reader::Result;
const bool success = Result::SUCCESS == item->async->doTranscoding();
item->transcoderState.store(success ? TranscoderState::SUCCESS : TranscoderState::ERROR);
+4 -10
View File
@@ -32,7 +32,6 @@
#include <geometry/Transcoder.h>
#include <utils/compiler.h>
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
@@ -75,15 +74,15 @@ enum class CacheResult {
};
struct ResourceLoader::Impl {
explicit Impl(const ResourceConfiguration& config) :
Impl(const ResourceConfiguration& config) :
mEngine(config.engine),
mNormalizeSkinningWeights(config.normalizeSkinningWeights),
mGltfPath(config.gltfPath ? config.gltfPath : ""),
mUriDataCache(std::make_shared<UriDataCache>()) {}
Engine* const mEngine;
bool mNormalizeSkinningWeights;
std::string mGltfPath;
const bool mNormalizeSkinningWeights;
const std::string mGltfPath;
// User-provided resource data with URI string keys, populated with addResourceData().
// This is used on platforms without traditional file systems, such as Android, iOS, and WebGL.
@@ -244,7 +243,7 @@ static void decodeMeshoptCompression(cgltf_data* data) {
void* destination = malloc(compression->count * compression->stride);
assert_invariant(destination);
UTILS_UNUSED_IN_RELEASE int error = 0;
int error = 0;
switch (compression->mode) {
case cgltf_meshopt_compression_mode_invalid:
break;
@@ -321,11 +320,6 @@ ResourceLoader::~ResourceLoader() {
delete pImpl;
}
void ResourceLoader::setConfiguration(const ResourceConfiguration& config) {
pImpl->mNormalizeSkinningWeights = config.normalizeSkinningWeights;
pImpl->mGltfPath = config.gltfPath;
}
void ResourceLoader::addResourceData(const char* uri, BufferDescriptor&& buffer) {
pImpl->addResourceData(uri, std::move(buffer));
}
+1 -1
View File
@@ -126,7 +126,7 @@ Texture* StbProvider::pushTexture(const uint8_t* data, size_t byteCount,
}
JobSystem* js = &mEngine->getJobSystem();
info->decoderJob = jobs::createJob(*js, mDecoderRootJob, [info] {
info->decoderJob = jobs::createJob(*js, mDecoderRootJob, [this, info] {
auto& source = info->sourceBuffer;
int width, height, comp;
+4
View File
@@ -43,6 +43,9 @@ using namespace backend;
using namespace gltfio;
using namespace utils;
constexpr uint32_t WIDTH = 64;
constexpr uint32_t HEIGHT = 64;
char const* ANIMATED_MORPH_CUBE_GLB = "AnimatedMorphCube.glb";
static std::ifstream::pos_type getFileSize(const char* filename) {
@@ -175,6 +178,7 @@ do { \
TEST_F(glTFIOTest, AnimatedMorphCubeTransforms) {
FilamentAsset const& morphCubeAsset = *mData[ANIMATED_MORPH_CUBE_GLB]->getAsset();
auto const& transformManager = mEngine->getTransformManager();
auto const& renderableManager = mEngine->getRenderableManager();
Entity const* renderables = morphCubeAsset.getRenderableEntities();
EXPECT_EQ(morphCubeAsset.getRenderableEntityCount(), 1u);
+1 -1
View File
@@ -60,7 +60,7 @@ private:
// This variable is to implement a *hanging* effect for /api/status. The call to /api/status
// will always block until statusMaterialId is updated again. The client is expected to keep
// calling /api/status (a constant "pull" to simulate a push).
std::atomic<uint64_t> mCurrentStatus = 0;
std::atomic_uint64_t mCurrentStatus = 0;
};
} // filament::matdbg
-16
View File
@@ -127,19 +127,3 @@ async function statusLoop(isConnected, onStatus) {
setTimeout(() => statusLoop(isConnected, onStatus), STATUS_LOOP_TIMEOUT)
}
}
// Use browser User-agent to guess the current backend. This is mainly for matinfo which does
// not have a running backend.
function guessBackend() {
const AGENTS_TO_BACKEND = [
['Mac OS', 'metal'],
['Windows', 'opengl'],
['Linux', 'vulkan'],
];
const result = AGENTS_TO_BACKEND.filter((agent_backend) => {
return window.navigator.userAgent.search(agent_backend[0]);
}).map((agent_backend) => agent_backend[1]);
return result.length > 0 ? result[0] : null;
}
+65 -237
View File
@@ -25,8 +25,6 @@ const LANGUAGE_CHOICES = {
'metal': ['msl'],
};
const BACKENDS = Object.keys(LANGUAGE_CHOICES);
const MATERIAL_INFO_KEY_TO_STRING = {
'model': 'shading model',
'vertex_domain': 'vertex domain',
@@ -56,6 +54,19 @@ const BACKGROUND_COLOR = '#5362e5';
const HOVER_BACKGROUND_COLOR = '#b3c2ff';
const CODE_VIEWER_BOTTOM_ROW_HEIGHT = 60;
const REGULAR_FONT_SIZE = 12;
const MENU_HR = `
display: block;
height: 1px;
border: 0px;
border-top: 1px solid ${UNSELECTED_COLOR};
padding: 0;
width: 100%;
margin: 3px 0 8px 0;
`;
const MENU_SECTION_TITLE = `
font-size: 16px;
color: ${UNSELECTED_COLOR};
`;
// Set up the Monaco editor. See also CodeViewer
const kMonacoBaseUrl = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/';
@@ -74,14 +85,6 @@ window.MonacoEnvironment = {
}
};
const _validDict = (obj) => {
return obj && Object.keys(obj) > 0;
}
const _isMatInfoMode = (database) => {
return Object.keys(database).length == 1;
}
class Button extends LitElement {
static get styles() {
return css`
@@ -219,19 +222,11 @@ class CodeViewer extends LitElement {
scrollBeyondLastLine: false,
readOnly: false,
minimap: { enabled: false },
automaticLayout: true,
// Workaround see https://github.com/microsoft/monaco-editor/issues/3217
fontLigatures: '',
automaticLayout: true
});
const KeyMod = monaco.KeyMod, KeyCode = monaco.KeyCode;
this.editor.onDidChangeModelContent(this._onEdit.bind(this));
this.editor.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_S, this._rebuild.bind(this));
// It might be that the code is available before the editor has been created.
if (this.code && this.code.length > 0) {
this.editor.setValue(this.code);
}
});
}
@@ -259,17 +254,13 @@ class CodeViewer extends LitElement {
updated(props) {
if (props.has('code') && this.code.length > 0) {
// Note that the prop might have been updated before the editor is available.
if (this.editor) {
this.editor.setValue(this.code);
}
this.editor.setValue(this.code);
}
if ((props.has('expectedWidth') || props.has('expectedHeight')) &&
(this.expectedWidth > 0 && (this.expectedHeight - CODE_VIEWER_BOTTOM_ROW_HEIGHT) > 0)) {
const actualWidth = Math.floor(this.expectedWidth);
const actualHeight = (Math.floor(this.expectedHeight) - CODE_VIEWER_BOTTOM_ROW_HEIGHT);
this._editorDiv.style.width = actualWidth + 'px';
this._editorDiv.style.height = actualHeight + 'px';
this._editorDiv.style.width = Math.floor(this.expectedWidth) + 'px';
this._editorDiv.style.height =
(Math.floor(this.expectedHeight) - CODE_VIEWER_BOTTOM_ROW_HEIGHT) + 'px';
}
}
@@ -320,12 +311,12 @@ class CodeViewer extends LitElement {
}
customElements.define("code-viewer", CodeViewer);
class MenuSection extends LitElement {
class MaterialInfo extends LitElement {
static get properties() {
return {
info: {type: Object, state: true},
showing: {type: Boolean, state: true},
title: {type: String, attribute: 'title'},
};
}
}
static get styles() {
@@ -333,23 +324,18 @@ class MenuSection extends LitElement {
:host {
font-size: ${unsafeCSS(REGULAR_FONT_SIZE)}px;
color: ${unsafeCSS(UNSELECTED_COLOR)};
}
.section-title {
font-size: 16px;
color: ${unsafeCSS(UNSELECTED_COLOR)};
cursor: pointer;
}
.container {
margin-bottom: 20px;
}
.section-title {
${unsafeCSS(MENU_SECTION_TITLE)}
cursor: pointer;
}
hr {
display: block;
height: 1px;
border: 0px;
border-top: 1px solid ${unsafeCSS(UNSELECTED_COLOR)};
padding: 0;
width: 100%;
margin: 3px 0 8px 0;
${unsafeCSS(MENU_HR)}
}
.hide {
display: none;
flex-direction: column;
}
.expander {
display: flex;
@@ -360,48 +346,9 @@ class MenuSection extends LitElement {
`;
}
_showClick() {
this.showing = !this.showing;
}
constructor() {
super();
this.showing = true;
}
render() {
const expandedIcon = this.showing ? '-' : '+';
const slot = (() => html`<slot></slot>`)();
return html`
<div class="container">
<div class="section-title expander" @click="${this._showClick}">
<span>${this.title}</span> <span>${expandedIcon}</span>
</div>
<hr />
${this.showing ? slot : []}
</div>
`;
}
}
customElements.define('menu-section', MenuSection);
class MaterialInfo extends LitElement {
static get properties() {
return {
info: {type: Object, state: true},
};
}
static get styles() {
return css`
:host {
font-size: ${unsafeCSS(REGULAR_FONT_SIZE)}px;
}
`;
}
constructor() {
super();
this.info = null;
}
@@ -409,7 +356,12 @@ class MaterialInfo extends LitElement {
return this.info && Object.keys(this.info).length > 0;
}
_showClick() {
this.showing = !this.showing;
}
render() {
const expandedIcon = this._hasInfo() ? (this.showing ? '-' : '+') : '';
let infoDivs = [];
if (this._hasInfo()) {
if (this.info.shading && this.info.shading.material_domain === 'surface') {
@@ -435,122 +387,22 @@ class MaterialInfo extends LitElement {
);
}
}
const shouldHide = infoDivs.length == 0;
if (infoDivs.length > 0) {
return html`
<menu-section title="Material Details">
${infoDivs}
</menu-section>
`;
let divClass = 'container';
if (infoDivs.length == 0) {
divClass += ' hide';
}
return html``;
}
}
customElements.define('material-info', MaterialInfo);
class AdvancedOptions extends LitElement {
static get properties() {
return {
currentBackend: {type: String, attribute: 'current-backend'},
availableBackends: {type: Array, state: true},
};
}
static get styles() {
return css`
:host {
font-size: ${unsafeCSS(REGULAR_FONT_SIZE)}px;
}
.option {
border: 1px solid ${unsafeCSS(UNSELECTED_COLOR)};
border-radius: 5px;
padding: 4px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
label {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 5px;
}
label input {
margin: 0 4px 0 0;
}
form {
display: flex;
}
.option-heading {
margin-bottom: 5px;
}
`;
}
get _backendOptionForm() {
return this.renderRoot.querySelector('#backend-option-form');
}
updated(props) {
if (props.has('currentBackend') || props.has('availableBackends')) {
// Clear the radio button selections. The correct option will be selected
// in _backendOption().
if (this._backendOptionForm) {
this._backendOptionForm.reset();
}
}
}
_backendOption() {
if (this.availableBackends.length == 0) {
return null;
}
const onChange = (ev) => {
const backend = ev.currentTarget.getAttribute('name');
this.dispatchEvent(
new CustomEvent(
'option-backend',
{detail: backend, bubbles: true, composed: true}));
}
const div = this.availableBackends.map((backend) => {
const selected = backend == this.currentBackend;
return html`
<label>
<input type="radio" name="${backend}"
?checked=${selected} @change=${onChange}/>
${backend}
</label>
`;
});
return html`
<div class="option">
<div class="option-heading">Current Backend</div>
<form action="" id="backend-option-form">
${div}
</form>
<div class="${divClass}">
<div class="section-title expander" @click="${this._showClick}">
<span>Material Details</span> <span>${expandedIcon}</span>
</div>
<hr />
${this.showing ? infoDivs : []}
</div>
`;
}
constructor() {
super();
this.availableBackends = [];
}
render() {
return html`
<menu-section title="Advanced Options">
${this._backendOption() ?? nothing}
</menu-section>
`;
}
}
customElements.define('advanced-options', AdvancedOptions);
customElements.define('material-info', MaterialInfo);
class MaterialSidePanel extends LitElement {
// Setting the style in render() has poor performance implications. We use it simply to avoid
@@ -572,6 +424,9 @@ class MaterialSidePanel extends LitElement {
margin: 0 0 10px 0;
font-size: 20px;
}
.material-section {
${MENU_SECTION_TITLE}
}
.materials {
display: flex;
flex-direction: column;
@@ -603,6 +458,9 @@ class MaterialSidePanel extends LitElement {
flex-direction: row;
display: flex;
}
hr {
${MENU_HR}
}
`;
}
@@ -626,10 +484,6 @@ class MaterialSidePanel extends LitElement {
return this.renderRoot.querySelector('#material-info');
}
get _advancedOptions() {
return this.renderRoot.querySelector('#advanced-options');
}
constructor() {
super();
this.connected = false;
@@ -674,9 +528,9 @@ class MaterialSidePanel extends LitElement {
});
}
if (props.has('currentMaterial')) {
if (this.currentBackend && this.database && this.currentMaterial) {
if (this.currentBackend && this.database && this.activeShaders && this.currentMaterial) {
const material = this.database[this.currentMaterial];
const activeVariants = _validDict(this.activeShaders) ? this.activeShaders[this.currentMaterial].variants : [];
const activeVariants = this.activeShaders[this.currentMaterial].variants;
const materialShaders = material[this.currentBackend];
let variants = [];
for (const [index, shader] of materialShaders.entries()) {
@@ -692,11 +546,6 @@ class MaterialSidePanel extends LitElement {
if (this.currentMaterial && this.database) {
const material = this.database[this.currentMaterial];
this._materialInfo.info = material;
// The matinfo usecase
if (_isMatInfoMode(this.database)) {
this._advancedOptions.availableBackends = BACKENDS.filter((backend) => !!material[backend]);
}
}
}
}
@@ -801,29 +650,23 @@ class MaterialSidePanel extends LitElement {
${shaderDiv ?? nothing}
`;
});
if (mats.length > 0) {
return html`<menu-section title="${title}">${mats}</menu-section>`;
}
return null;
return html`
<div class="materials">
<div class="material-section">
${title}
</div>
<hr />
${mats}
</div>
`;
};
let advancedOptions = null;
// Currently we only have one advanced option and it's only for when we're in matinfo
if (_isMatInfoMode(this.database)) {
advancedOptions =
(() => html`
<advanced-options id="advanced-options"
current-backend=${this.currentBackend}></advanced-options>
`)();
}
return html`
<style>${this.dynamicStyle()}</style>
<div class="container">
<div class="title">matdbg</div>
${sections("Surface", "surface") ?? nothing}
${sections("Post-processing", "postpro") ?? nothing}
${sections("Surface", "surface")}
${sections("Post-processing", "postpro")}
<material-info id="material-info"></material-info>
${advancedOptions ?? nothing}
</div>
`;
}
@@ -951,12 +794,6 @@ class MatdbgViewer extends LitElement {
}
);
this.addEventListener('option-backend',
(ev) => {
this.currentBackend = ev.detail;
}
);
addEventListener('resize', this._onResize.bind(this));
}
@@ -1038,7 +875,7 @@ class MatdbgViewer extends LitElement {
material.active = material.active || shader.active;
}
}
if (_validDict(this.activeShaders)) {
if (this.activeShaders) {
let backends = {};
for (let matid in this.activeShaders) {
const backend = this.activeShaders[matid].backend;
@@ -1052,9 +889,6 @@ class MatdbgViewer extends LitElement {
if (backendList.length > 0) {
this.currentBackend = backendList[0];
}
} else if (!this.currentBackend) {
// Make a guess on the backend if one wasn't from activeShaders.
this.currentBackend = guessBackend();
}
this._sidepanel.database = this.database;
@@ -1067,12 +901,6 @@ class MatdbgViewer extends LitElement {
this.database[matInfo.matid] = matInfo;
this.database = this.database;
}
// In the `matinfo -w` usecase, we assume the current material to be the only
// material available in the database.
if (_isMatInfoMode(this.database)) {
this.currentMaterial = Object.keys(this.database)[0];
}
})();
}
}
+9 -9
View File
@@ -58,7 +58,7 @@ public:
/* compound assignment products by a scalar
*/
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
constexpr QUATERNION<T>& operator*=(U v) {
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
@@ -67,7 +67,7 @@ public:
return lhs;
}
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
constexpr QUATERNION<T>& operator/=(U v) {
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
@@ -125,25 +125,25 @@ public:
* q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
*
*/
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
friend inline constexpr
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(QUATERNION<T> q, U scalar) {
// don't pass q by reference because we need a copy anyway
return QUATERNION<arithmetic_result_t<T, U>>(q *= scalar);
return q *= scalar;
}
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
friend inline constexpr
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(U scalar, QUATERNION<T> q) {
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(T scalar, QUATERNION<U> q) {
// don't pass q by reference because we need a copy anyway
return QUATERNION<arithmetic_result_t<T, U>>(q *= scalar);
return q *= scalar;
}
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
template<typename U>
friend inline constexpr
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator/(QUATERNION<T> q, U scalar) {
// don't pass q by reference because we need a copy anyway
return QUATERNION<arithmetic_result_t<T, U>>(q /= scalar);
return q /= scalar;
}
};
-101
View File
@@ -17,7 +17,6 @@
#include <math.h>
#include <random>
#include <functional>
#include <type_traits>
#include <gtest/gtest.h>
@@ -325,103 +324,3 @@ TEST_F(QuatTest, NaN) {
EXPECT_NEAR(qs[2], 0.5, 0.1);
EXPECT_NEAR(qs[3], 0.5, 0.1);
}
TEST_F(QuatTest, Conversions) {
quat qd;
quatf qf;
float3 vf;
double3 vd;
double d = 0.0;
float f = 0.0f;
static_assert(std::is_same<details::arithmetic_result_t<float, float>, float>::value);
static_assert(std::is_same<details::arithmetic_result_t<float, double>, double>::value);
static_assert(std::is_same<details::arithmetic_result_t<double, float>, double>::value);
static_assert(std::is_same<details::arithmetic_result_t<double, double>, double>::value);
{
auto r1 = qd * d;
auto r2 = qd * f;
auto r3 = qf * d;
auto r4 = qf * f;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = qd / d;
auto r2 = qd / f;
auto r3 = qf / d;
auto r4 = qf / f;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = d * qd;
auto r2 = f * qd;
auto r3 = d * qf;
auto r4 = f * qf;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = qd * vd;
auto r2 = qf * vd;
auto r3 = qd * vf;
auto r4 = qf * vf;
static_assert(std::is_same<decltype(r1), double3>::value);
static_assert(std::is_same<decltype(r2), double3>::value);
static_assert(std::is_same<decltype(r3), double3>::value);
static_assert(std::is_same<decltype(r4), float3>::value);
}
{
auto r1 = qd * qd;
auto r2 = qf * qd;
auto r3 = qd * qf;
auto r4 = qf * qf;
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
{
auto r1 = dot(qd, qd);
auto r2 = dot(qf, qd);
auto r3 = dot(qd, qf);
auto r4 = dot(qf, qf);
static_assert(std::is_same<decltype(r1), double>::value);
static_assert(std::is_same<decltype(r2), double>::value);
static_assert(std::is_same<decltype(r3), double>::value);
static_assert(std::is_same<decltype(r4), float>::value);
}
{
auto r1 = cross(qd, qd);
auto r2 = cross(qf, qd);
auto r3 = cross(qd, qf);
auto r4 = cross(qf, qf);
static_assert(std::is_same<decltype(r1), quat>::value);
static_assert(std::is_same<decltype(r2), quat>::value);
static_assert(std::is_same<decltype(r3), quat>::value);
static_assert(std::is_same<decltype(r4), quatf>::value);
}
}
template <typename L, typename R, typename = void>
struct has_divide_assign : std::false_type {};
template <typename L, typename R>
struct has_divide_assign<L, R,
decltype(std::declval<L&>() /= std::declval<R>(), void())> : std::true_type {};
// Static assertions to validate the availability of the /= operator for specific type
// combinations. The first static_assert checks that the quat does not have a /= operator with Foo.
// This ensures that quat does not provide an inappropriate overload that could be erroneously
// selected.
struct Foo {};
static_assert(!has_divide_assign<quat, Foo>::value);
static_assert(has_divide_assign<quat, float>::value);
-2
View File
@@ -18,8 +18,6 @@
#include <utils/Panic.h>
#include <cstdlib>
namespace utils {
// we use a non-inlined, not marked as "no return" function for aborting so that we can set
+28 -74
View File
@@ -53,8 +53,6 @@
#include <imgui.h>
#include <filagui/ImGuiExtensions.h>
#include <cgltf.h>
#include <array>
#include <fstream>
#include <iostream>
@@ -487,57 +485,6 @@ static void onClick(App& app, View* view, ImVec2 pos) {
});
}
static utils::Path getPathForAsset(std::string_view string) {
utils::Path filename{ string };
if (!filename.exists()) {
std::cerr << "file " << filename << " not found!" << std::endl;
return {};
}
if (filename.isDirectory()) {
auto files = filename.listContents();
for (const auto& file: files) {
if (file.getExtension() == "gltf" || file.getExtension() == "glb") {
filename = file;
break;
}
}
if (filename.isDirectory()) {
std::cerr << "no glTF file found in " << filename << std::endl;
return {};
}
}
return filename;
}
static bool checkAsset(const utils::Path& filename) {
// Peek at the file size to allow pre-allocation.
long const contentSize = static_cast<long>(getFileSize(filename.c_str()));
if (contentSize <= 0) {
std::cerr << "Unable to open " << filename << std::endl;
return false;
}
// Consume the glTF file.
std::ifstream in(filename.c_str(), std::ifstream::binary | std::ifstream::in);
std::vector<uint8_t> buffer(static_cast<unsigned long>(contentSize));
if (!in.read((char*) buffer.data(), contentSize)) {
std::cerr << "Unable to read " << filename << std::endl;
return false;
}
// Parse the glTF file and create Filament entities.
cgltf_options options{};
cgltf_data* sourceAsset;
cgltf_result result = cgltf_parse(&options, buffer.data(), contentSize, &sourceAsset);
if (result != cgltf_result_success) {
slog.e << "Unable to parse glTF file." << io::endl;
return false;
}
return true;
};
int main(int argc, char** argv) {
App app;
@@ -549,10 +496,24 @@ int main(int argc, char** argv) {
utils::Path filename;
int const num_args = argc - optionIndex;
if (num_args >= 1) {
filename = getPathForAsset(argv[optionIndex]);
if (filename.isEmpty()) {
filename = argv[optionIndex];
if (!filename.exists()) {
std::cerr << "file " << filename << " not found!" << std::endl;
return 1;
}
if (filename.isDirectory()) {
auto files = filename.listContents();
for (const auto& file : files) {
if (file.getExtension() == "gltf" || file.getExtension() == "glb") {
filename = file;
break;
}
}
if (filename.isDirectory()) {
std::cerr << "no glTF file found in " << filename << std::endl;
return 1;
}
}
}
auto loadAsset = [&app](const utils::Path& filename) {
@@ -573,14 +534,14 @@ int main(int argc, char** argv) {
// Parse the glTF file and create Filament entities.
app.asset = app.assetLoader->createAsset(buffer.data(), buffer.size());
app.instance = app.asset->getInstance();
buffer.clear();
buffer.shrink_to_fit();
if (!app.asset) {
std::cerr << "Unable to parse " << filename << std::endl;
exit(1);
}
app.instance = app.asset->getInstance();
buffer.clear();
buffer.shrink_to_fit();
};
auto loadResources = [&app] (const utils::Path& filename) {
@@ -598,8 +559,6 @@ int main(int argc, char** argv) {
app.resourceLoader->addTextureProvider("image/png", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/jpeg", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/ktx2", app.ktxDecoder);
} else {
app.resourceLoader->setConfiguration(configuration);
}
if (!app.resourceLoader->asyncBeginLoad(app.asset)) {
@@ -1062,19 +1021,14 @@ int main(int argc, char** argv) {
filamentApp.animate(animate);
filamentApp.resize(resize);
filamentApp.setDropHandler([&](std::string_view path) {
utils::Path const filename = getPathForAsset(path);
if (!filename.isEmpty()) {
if (checkAsset(filename)) {
app.resourceLoader->asyncCancelLoad();
app.resourceLoader->evictResourceData();
app.viewer->removeAsset();
app.assetLoader->destroyAsset(app.asset);
loadAsset(filename);
loadResources(filename);
app.viewer->setAsset(app.asset, app.instance);
}
}
filamentApp.setDropHandler([&] (std::string_view path) {
app.resourceLoader->asyncCancelLoad();
app.resourceLoader->evictResourceData();
app.viewer->removeAsset();
app.assetLoader->destroyAsset(app.asset);
loadAsset(path);
loadResources(path);
app.viewer->setAsset(app.asset, app.instance);
});
filamentApp.run(app.config, setup, cleanup, gui, preRender, postRender);
+3 -3
View File
@@ -58,9 +58,9 @@ void main() {
fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F
#elif defined(VARIANT_HAS_PICKING)
#if MATERIAL_FEATURE_LEVEL == 0
outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0;
outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0;
outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0;
outPicking.a = float((object_uniforms_objectId / 65536) % 256) / 255.0;
outPicking.b = float((object_uniforms_objectId / 256) % 256) / 255.0;
outPicking.g = float( object_uniforms_objectId % 256) / 255.0;
outPicking.r = vertex_position.z / vertex_position.w;
#else
outPicking.x = intBitsToFloat(object_uniforms_objectId);
-1
View File
@@ -34,7 +34,6 @@
#include "ParametersProcessor.h"
#include <GlslangToSpv.h>
#include "glslang/Include/intermediate.h"
#include "sca/builtinResource.h"
+28 -15
View File
@@ -180,6 +180,19 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
}
}
const JsonishValue* transferFunctionValue = jsonObject.getValue("transferFunction");
if (transferFunctionValue) {
if (transferFunctionValue->getType() != JsonishValue::STRING) {
std::cerr << "parameters: transferFunction must be a STRING." << std::endl;
return false;
}
auto transferFunctionString = formatValue->toJsonString();
if (!Enums::isValid<SamplerTransferFunction>(transferFunctionString->getString())){
return logEnumIssue("parameters", *transferFunctionString, Enums::map<SamplerTransferFunction>());
}
}
auto typeString = typeValue->toJsonString()->getString();
auto nameString = nameValue->toJsonString()->getString();
@@ -205,22 +218,22 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
return false;
}
MaterialBuilder::SamplerType type = Enums::toEnum<SamplerType>(typeString);
if (precisionValue && formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, format, precision);
} else if (formatValue) {
auto format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, format);
} else if (precisionValue) {
auto precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
builder.parameter(nameString.c_str(), type, precision);
} else {
builder.parameter(nameString.c_str(), type);
SamplerType type = Enums::toEnum<SamplerType>(typeString);
SamplerFormat format = SamplerFormat::FLOAT;
if (formatValue) {
format = Enums::toEnum<SamplerFormat>(formatValue->toJsonString()->getString());
}
ParameterPrecision precision = ParameterPrecision::DEFAULT;
if (precisionValue) {
precision =
Enums::toEnum<ParameterPrecision>(precisionValue->toJsonString()->getString());
}
SamplerTransferFunction transferFunction = SamplerTransferFunction::UNDEFINED;
if (transferFunctionValue) {
transferFunction =
Enums::toEnum<SamplerTransferFunction>(transferFunctionValue->toJsonString()->getString());
}
builder.parameter(nameString.c_str(), type, format, precision, transferFunction);
} else {
std::cerr << "parameters: the type '" << typeString
<< "' for parameter with name '" << nameString << "' is neither a valid uniform "
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.46.0",
"version": "1.45.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",