Compare commits

..
Author SHA1 Message Date
Juan Caldas cecf38f51e add shell script to generate tif files 2025-08-05 08:50:08 -04:00
Powei Feng 79551eacb2 Update MATERIAL_VERSION to 63 2025-08-03 22:56:09 -07:00
Powei Feng e181c74b6c Fix matp test typo 2025-08-01 16:27:35 -07:00
Powei Feng 418704dbb1 Test matp instead matc 2025-08-01 16:25:41 -07:00
Yein Jo eeb4fe219b Restore test files and moved to matp 2025-08-01 15:57:23 -07:00
Yein Jo 7d227630fc fix max line 2025-08-01 15:57:23 -07:00
Yein Jo d84e42861c Fix includes 2025-08-01 15:57:23 -07:00
Yein Jo 3a75dc6d2d Move some classes to private 2025-08-01 15:57:23 -07:00
Yein Jo 66b67849af Extract material parse logic to filament-matp 2025-08-01 15:57:23 -07:00
Powei Feng ba5900efcf webgpu: descriptor set unbind on null handle 2025-08-01 14:42:58 -07:00
Powei Feng 2d518b5fd1 ppm: ensure a renderable descriptor set is bound
- We set a dummy set at the renderable bind point for
   post-processing passes, since it is not used.
2025-08-01 14:42:58 -07:00
Mathias Agopian ee8d0e6ebf remove fake dependencies on the PER_VIEW UBO
The PER_VIEW UBO was updated by different render passes at different
times, which created synchronizations on the GPU, between unrelated
passes and prevented the vertex shader to run on parallel with another
pass for instance.

In the PR, we fix this by moving ALL the UBO set-up outside of 
render passes. It's now done early, just before creating the framegraph.

The PER_VIEW UBO is shared between several passes, themselves using
different descriptor sets; so we need to make sure all the data
necessary for these passes is computed and set in the UBO (e.g. SSR
passes).

The passes themselves (like the color pass) still set the textures and
UBOs in their descriptor sets, but then no longer updated the content
of the UBO itself.

In this PR we also remove some "fake" dependencies between passes due
to descriptor sets straddling between passes. This is done by unbinding
all descriptor sets at the end of each pass. The PER_MATERIAL descriptor
set is a bit special because it should be unbound between full screen
quads draws.

Additionally, there some places in the code attempting to update
UBOs only when the content was dirty were missing a "clean" call, to
reset the dirty bit to the unset state.

The main change here is to move the code that set the UBO content out
of the ColorPassDescriptorSet (and a few other) into View and instead of
updating the content in the color pass (and a few other), do that at
the beginning of Renderer::render().
2025-08-01 14:01:17 -07:00
Mathias Agopian b4c9b9f489 renderStandaloneView() needs a driver and engine flush 2025-07-31 12:10:23 -07:00
Mathias Agopian e59972b41f unbind descriptor-sets after renderpasses
this prevents a false dependency with the next pass and keeps us
honest about what resources we're using.
2025-07-31 12:10:06 -07:00
Powei Feng 5fb9fad3ce vk: fix broken iblprefilter path (#9019)
The issue is that we need to reset the bound info in
VulkanDescriptorSetCache when the current command buffer completes
recording.
2025-07-31 10:45:34 -07:00
Filament Bot 44bbab158b [automated] Updating /docs due to commit bbced6d
Full commit hash is bbced6d59d

DOCS_ALLOW_DIRECT_EDITS
2025-07-31 17:31:35 +00:00
73 changed files with 1407 additions and 1242 deletions
+1 -1
View File
@@ -22,7 +22,6 @@ project(TNT)
option(FILAMENT_USE_EXTERNAL_GLES3 "Experimental: Compile Filament against OpenGL ES 3" OFF)
option(FILAMENT_ENABLE_LTO "Enable link-time optimizations if supported by the compiler" OFF)
option(FILAMENT_SKIP_SAMPLES "Don't build samples" OFF)
option(FILAMENT_SUPPORTS_XCB "Include XCB support in Linux builds" ON)
@@ -871,6 +870,7 @@ if (IS_HOST_PLATFORM)
add_subdirectory(${LIBRARIES}/filamentapp)
endif()
add_subdirectory(${LIBRARIES}/imageio)
add_subdirectory(${LIBRARIES}/filament-matp)
add_subdirectory(${FILAMENT}/samples)
+3 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.63.0'
implementation 'com.google.android.filament:filament-android:1.62.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:
```shell
pod 'Filament', '~> 1.63.0'
pod 'Filament', '~> 1.62.2'
```
## Documentation
@@ -331,6 +331,7 @@ and tools.
- `filamesh`: Mesh converter
- `glslminifier`: Minifies GLSL source code
- `matc`: Material compiler
- `filament-matp`: Material parser
- `matinfo` Displays information about materials compiled with `matc`
- `mipgen` Generates a series of miplevels from a source image
- `normal-blending`: Tool to blend normal maps
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.63.0
VERSION_NAME=1.62.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -5,7 +5,7 @@ libs/math/test_math
libs/image/test_image compare libs/image/tests/reference/
libs/utils/test_utils
libs/filamat/test_filamat
tools/matc/test_matc
libs/filament-matp/test_matp
tools/cmgen/test_cmgen compare
tools/glslminifier/test_glslminifier
libs/filameshio/test_filameshio
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.62.1'
implementation 'com.google.android.filament:filament-android:1.62.2'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.62.1'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.62.2'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.62.1'
implementation 'com.google.android.filament:filament-android:1.62.2'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.62.1'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.62.2'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12 -2
View File
@@ -136,6 +136,10 @@ void WebGPUDriver::setPresentationTime(int64_t monotonic_clock_ns) {
void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
mPipelineLayoutCache.onFrameEnd();
mPipelineCache.onFrameEnd();
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
mCurrentDescriptorSets[i] = {};
}
}
void WebGPUDriver::flush(int) {
@@ -1480,8 +1484,8 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
assert_invariant(bindGroupLayouts.size() >= pipelineState.pipelineLayout.setLayout.size());
size_t bindGroupLayoutCount{ 0 };
for (size_t i{ 0 }; i < bindGroupLayouts.size(); i++) {
const auto handle{ pipelineState.pipelineLayout.setLayout[bindGroupLayoutCount] };
if (handle.getId() == HandleBase::nullid) {
const auto handle{ pipelineState.pipelineLayout.setLayout[i] };
if (!handle) {
continue;
}
bindGroupLayouts[bindGroupLayoutCount++] =
@@ -1661,6 +1665,12 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> descriptor
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> descriptorSetHandle,
const backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
assert_invariant(setIndex < MAX_DESCRIPTOR_SET_COUNT);
// An empty handle signifies we need to release this bind point
if (!descriptorSetHandle) {
mCurrentDescriptorSets[setIndex] = {};
return;
}
const auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
const auto wbg = bindGroup->lockAndReturn(mDevice);
+125 -18
View File
@@ -36,6 +36,7 @@
#include "details/Engine.h"
#include "ds/DescriptorSet.h"
#include "ds/SsrPassDescriptorSet.h"
#include "ds/TypedUniformBuffer.h"
@@ -251,6 +252,11 @@ void PostProcessManager::bindPostProcessDescriptorSet(DriverApi& driver) const n
mPostProcessDescriptorSet.bind(driver);
}
void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) noexcept {
driver.bindDescriptorSet(mDummyPerRenderableDsh, +DescriptorSetBindingPoints::PER_RENDERABLE,
{ { 0, 0 }, driver });
}
UTILS_NOINLINE
void PostProcessManager::registerPostProcessMaterial(std::string_view const name,
StaticMaterialInfo const& info) {
@@ -314,6 +320,32 @@ void PostProcessManager::init() noexcept {
mFullScreenQuadVbih = engine.getFullScreenVertexBuffer()->getVertexBufferInfoHandle();
mPerRenderableDslh = engine.getPerRenderableDescriptorSetLayout().getHandle();
mDummyPerRenderableDsh = driver.createDescriptorSet(mPerRenderableDslh);
driver.updateDescriptorSetBuffer(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::OBJECT_UNIFORMS, engine.getDummyUniformBuffer(), 0,
sizeof(PerRenderableUib));
driver.updateDescriptorSetBuffer(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::BONES_UNIFORMS, engine.getDummyUniformBuffer(), 0,
sizeof(PerRenderableBoneUib));
driver.updateDescriptorSetBuffer(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::MORPHING_UNIFORMS, engine.getDummyUniformBuffer(), 0,
sizeof(PerRenderableMorphingUib));
driver.updateDescriptorSetTexture(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::MORPH_TARGET_POSITIONS,
engine.getDummyMorphTargetBuffer()->getPositionsHandle(), {});
driver.updateDescriptorSetTexture(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::MORPH_TARGET_TANGENTS,
engine.getDummyMorphTargetBuffer()->getTangentsHandle(), {});
driver.updateDescriptorSetTexture(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, engine.getZeroTextureArray(),
{});
mSsrPassDescriptorSet.init(engine);
mPostProcessDescriptorSet.init(engine);
mStructureDescriptorSet.init(engine);
@@ -384,6 +416,8 @@ void PostProcessManager::terminate(DriverApi& driver) noexcept {
FEngine& engine = mEngine;
driver.destroyTexture(mStarburstTexture);
driver.destroyDescriptorSet(mDummyPerRenderableDsh);
// Must destroy the instances before the materials
mMaterialInstanceManager.terminate(engine);
@@ -420,6 +454,12 @@ void PostProcessManager::resetForRender() {
mFixedMaterialInstanceIndex = {};
}
void PostProcessManager::unbindAllDescriptorSets(DriverApi& driver) noexcept {
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_VIEW);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_RENDERABLE);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
}
UTILS_NOINLINE
PipelineState PostProcessManager::getPipelineState(
FMaterial const* const ma, PostProcessVariant variant) const noexcept {
@@ -483,6 +523,7 @@ void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
driver.endRenderPass();
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
}
// ------------------------------------------------------------------------------------------------
@@ -545,6 +586,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
// bind the per-view descriptorSet that is used for the structure pass
getStructureDescriptorSet().bind(driver);
bindPerRenderableDescriptorSet(driver);
passBuilder.renderFlags(structureRenderFlags);
passBuilder.variant(structureVariant);
@@ -555,6 +597,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
driver.beginRenderPass(out.target, out.params);
pass.getExecutor().execute(mEngine, driver);
driver.endRenderPass();
unbindAllDescriptorSets(driver);
});
auto const depth = structurePass->depth;
@@ -581,7 +624,8 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
}
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
getStructureDescriptorSet().bind(driver);
getStructureDescriptorSet().bind(driver);
bindPerRenderableDescriptorSet(driver);
auto in = resources.getTexture(data.depth);
auto& material = getPostProcessMaterial("mipmapDepth");
@@ -601,8 +645,10 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
mi->commit(driver);
mi->use(driver);
renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
driver.destroyTexture(th);
}
unbindAllDescriptorSets(driver);
});
return { depth, structurePass->picking };
@@ -650,6 +696,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::transparentPicking(FrameGrap
// bind the per-view descriptorSet that is used for the structure pass
getStructureDescriptorSet().bind(driver);
bindPerRenderableDescriptorSet(driver);
auto [target, params] = resources.getRenderPassInfo();
passBuilder.renderFlags(structureRenderFlags);
@@ -660,6 +707,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::transparentPicking(FrameGrap
driver.beginRenderPass(target, params);
pass.getExecutor().execute(mEngine, driver);
driver.endRenderPass();
unbindAllDescriptorSets(driver);
});
return pickingRenderPass->picking;
@@ -670,9 +718,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::transparentPicking(FrameGrap
FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
RenderPassBuilder const& passBuilder,
FrameHistory const& frameHistory,
CameraInfo const& cameraInfo,
FrameGraphId<FrameGraphTexture> const structure,
ScreenSpaceReflectionsOptions const& options,
FrameGraphTexture::Descriptor const& desc) noexcept {
struct SSRPassData {
@@ -693,8 +739,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> const history = fg.import("SSR history", previous.desc,
FrameGraphTexture::Usage::SAMPLEABLE, previous.color);
mat4 const& historyProjection = previous.projection;
auto const& uvFromClipMatrix = mEngine.getUvFromClipMatrix();
auto const& ssrPass = fg.addPass<SSRPassData>("SSR Pass",
[&](FrameGraph::Builder& builder, auto& data) {
@@ -728,28 +772,23 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
data.history = builder.sample(history);
}
},
[this, projection = cameraInfo.projection,
userViewMatrix = cameraInfo.getUserViewMatrix(), uvFromClipMatrix, historyProjection,
options, passBuilder = passBuilder]
(FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable {
[this, passBuilder = passBuilder](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) mutable {
// set structure sampler
mSsrPassDescriptorSet.prepareStructure(mEngine, data.structure ?
resources.getTexture(data.structure) : getOneTexture());
// set screen-space reflections and screen-space refractions
mat4f const uvFromViewMatrix = uvFromClipMatrix * projection;
mat4f const reprojection = uvFromClipMatrix *
mat4f{ historyProjection * inverse(userViewMatrix) };
// the history sampler is a regular texture2D
TextureHandle const history = data.history ?
resources.getTexture(data.history) : getZeroTexture();
mSsrPassDescriptorSet.prepareHistorySSR(mEngine, history, reprojection, uvFromViewMatrix, options);
mSsrPassDescriptorSet.prepareHistorySSR(mEngine, history);
mSsrPassDescriptorSet.commit(mEngine);
mSsrPassDescriptorSet.bind(driver);
bindPerRenderableDescriptorSet(driver);
auto const out = resources.getRenderPassInfo();
// Remove the HAS_SHADOWING RenderFlags, since it's irrelevant when rendering reflections
@@ -766,6 +805,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
driver.beginRenderPass(out.target, out.params);
pass.getExecutor().execute(mEngine, driver);
driver.endRenderPass();
unbindAllDescriptorSets(driver);
});
return ssrPass->reflections;
@@ -916,6 +956,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
// bind the per-view descriptorSet that is used for the structure pass
getStructureDescriptorSet().bind(driver);
bindPerRenderableDescriptorSet(driver);
auto depth = resources.getTexture(data.depth);
auto ssao = resources.getRenderPassInfo();
@@ -1031,6 +1072,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
assert_invariant(ssao.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH);
renderFullScreenQuad(ssao, pipeline, driver);
unbindAllDescriptorSets(driver);
});
FrameGraphId<FrameGraphTexture> ssao = SSAOPass->ssao;
@@ -1109,6 +1151,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
auto const& data, DriverApi& driver) {
// TODO: the structure descriptor set might not be the best fit.
getStructureDescriptorSet().bind(driver);
bindPerRenderableDescriptorSet(driver);
auto ssao = resources.getTexture(data.input);
auto blurred = resources.getRenderPassInfo();
@@ -1149,6 +1192,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
auto pipeline = getPipelineState(ma);
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
renderFullScreenQuad(blurred, pipeline, driver);
unbindAllDescriptorSets(driver);
});
return blurPass->blurred;
@@ -1274,6 +1318,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
// don't use auto for those, b/c the ide can't resolve them
using FGTD = FrameGraphTexture::Descriptor;
@@ -1365,6 +1410,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
commitAndRenderFullScreenQuad(driver, hwOutRT, mi);
}
unbindAllDescriptorSets(driver);
});
return blurPass->out;
@@ -1749,6 +1795,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& out = resources.getRenderPassInfo();
auto color = resources.getTexture(data.color);
auto depth = resources.getTexture(data.depth);
@@ -1767,6 +1815,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
1.0f / float(colorDesc.width),
1.0f / float(colorDesc.height) });
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
/*
@@ -1807,6 +1856,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto desc = resources.getDescriptor(data.inOutColor);
auto inOutColor = resources.getTexture(data.inOutColor);
@@ -1833,10 +1883,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->use(driver);
renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
driver.destroyTexture(inColor);
driver.destroyTexture(inCoc);
}
unbindAllDescriptorSets(driver);
});
/*
@@ -1882,6 +1934,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& inputDesc = resources.getDescriptor(data.inCocMinMax);
auto const& out = resources.getRenderPassInfo();
auto inCocMinMax = resources.getTexture(data.inCocMinMax);
@@ -1892,6 +1945,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->setParameter("cocMinMax", inCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
mi->setParameter("texelSize", float2{ 1.0f / inputDesc.width, 1.0f / inputDesc.height });
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
inTilesCocMinMax = ppDoFTiling->outTilesCocMinMax;
}
@@ -1918,12 +1972,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& out = resources.getRenderPassInfo();
auto inTilesCocMinMax = resources.getTexture(data.inTilesCocMinMax);
auto const& material = getPostProcessMaterial("dofDilate");
FMaterialInstance* const mi = getMaterialInstance(mEngine, material);
mi->setParameter("tiles", inTilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
return ppDoFDilate->outTilesCocMinMax;
};
@@ -1971,6 +2028,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& out = resources.getRenderPassInfo();
auto color = resources.getTexture(data.color);
@@ -2003,6 +2062,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
});
mi->setParameter("bokehAngle", bokehAngle);
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
/*
@@ -2034,6 +2094,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& out = resources.getRenderPassInfo();
auto inColor = resources.getTexture(data.inColor);
@@ -2046,6 +2108,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->setParameter("alpha", inAlpha, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
@@ -2081,6 +2144,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& out = resources.getRenderPassInfo();
auto color = resources.getTexture(data.color);
@@ -2095,6 +2160,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->setParameter("alpha", alpha, { .filterMag = SamplerMagFilter::NEAREST });
mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST });
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
return ppDoFCombine->output;
@@ -2117,6 +2183,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::downscalePass(FrameGraph& fg
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& out = resources.getRenderPassInfo();
auto const& material = getPostProcessMaterial("bloomDownsample2x");
FMaterialInstance* const mi = getMaterialInstance(mEngine, material);
@@ -2129,7 +2197,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::downscalePass(FrameGraph& fg
mi->setParameter("fireflies", fireflies ? 1.0f : 0.0f);
mi->setParameter("invHighlight", std::isinf(highlight) ? 0.0f : 1.0f / highlight);
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
return downsamplePass->output;
}
@@ -2250,6 +2318,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
// TODO: if downsampling is not exactly a multiple of two, use the 13 samples
// filter. This is generally the accepted solution, however, the 13 samples
@@ -2285,6 +2354,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
commitAndRenderFullScreenQuad(driver, hwDstRT, mi);
driver.destroyTexture(hwOutView);
}
unbindAllDescriptorSets(driver);
});
// output of bloom downsample pass becomes input of next (flare) pass
@@ -2304,6 +2374,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto hwOut = resources.getTexture(data.out);
auto const& outDesc = resources.getDescriptor(data.out);
@@ -2332,8 +2403,10 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
mi->commit(driver);
mi->use(driver);
renderFullScreenQuad(hwDstRT, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
driver.destroyTexture(hwOutView);
}
unbindAllDescriptorSets(driver);
});
return { bloomUpsamplePass->out, flare };
@@ -2363,6 +2436,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::flarePass(FrameGraph& fg,
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto in = resources.getTexture(data.in);
auto const out = resources.getRenderPassInfo(0);
const float aspectRatio = float(width) / float(height);
@@ -2387,6 +2461,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::flarePass(FrameGraph& fg,
mi->setParameter("haloThickness", bloomOptions.haloThickness);
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
constexpr float kernelWidth = 9;
@@ -2440,6 +2515,7 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
ColorGradingConfig const& colorGradingConfig) noexcept {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
PostProcessVariant const variant = colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
@@ -2471,6 +2547,7 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
FEngine const& engine = mEngine;
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
@@ -2505,6 +2582,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(
auto out = resources.getRenderPassInfo();
out.params.subpassMask = 1;
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
driver.beginRenderPass(out.target, out.params);
customResolveSubpass(driver);
driver.endRenderPass();
@@ -2579,6 +2657,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto colorTexture = resources.getTexture(data.input);
auto bloomTexture =
@@ -2645,6 +2724,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
commitAndRenderFullScreenQuad(driver, out, mi,
colorGradingConfig.translucent
? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
unbindAllDescriptorSets(driver);
}
);
@@ -2672,6 +2753,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& inDesc = resources.getDescriptor(data.input);
auto const& texture = resources.getTexture(data.input);
auto const& out = resources.getRenderPassInfo();
@@ -2697,6 +2779,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
mi->setParameter("texelSize", 1.0f / float2{ inDesc.width, inDesc.height });
commitAndRenderFullScreenQuad(driver, out, mi, variant);
unbindAllDescriptorSets(driver);
});
return ppFXAA->output;
@@ -2889,6 +2972,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
},
[=, &current](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
constexpr mat4f normalizedToClip{mat4f::row_major_init{
2, 0, 0, -1,
@@ -2981,6 +3065,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
colorGradingSubpass(driver, colorGradingConfig);
}
driver.endRenderPass();
unbindAllDescriptorSets(driver);
});
input = colorGradingConfig.asSubpass ? taaPass->tonemappedOutput : taaPass->output;
@@ -3030,6 +3115,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const input = resources.getTexture(data.input);
auto const out = resources.getRenderPassInfo();
@@ -3050,6 +3136,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
outputDesc.width, outputDesc.height,
1.0f / outputDesc.width, 1.0f / outputDesc.height });
commitAndRenderFullScreenQuad(driver, out, mi, variant);
unbindAllDescriptorSets(driver);
});
return ppFsrRcas->output;
@@ -3100,6 +3187,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
[this, vp, filter](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
@@ -3129,6 +3217,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
auto pipeline = getPipelineState(material.getMaterial(mEngine));
renderFullScreenQuad(out, pipeline, driver);
unbindAllDescriptorSets(driver);
});
auto output = ppQuadBlit->output;
@@ -3164,6 +3253,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
[this, vp, sourceHasLuminance](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
@@ -3203,9 +3293,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
mi->commit(driver);
mi->use(driver);
auto out = resources.getRenderPassInfo();
auto const out = resources.getRenderPassInfo();
commitAndRenderFullScreenQuad(driver, out, mi, variant);
unbindAllDescriptorSets(driver);
});
auto output = ppQuadBlit->output;
@@ -3251,6 +3341,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
[this, twoPassesEASU, dsrOptions, vp](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
// helper to set the EASU uniforms
auto setEasuUniforms = [vp, backend = mEngine.getBackend()](FMaterialInstance* mi,
@@ -3342,6 +3433,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
auto pipeline = getPipelineState(easuMaterial->getMaterial(mEngine));
renderFullScreenQuad(out, pipeline, driver);
}
unbindAllDescriptorSets(driver);
});
auto output = ppQuadBlit->output;
@@ -3380,6 +3472,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
auto out = resources.getRenderPassInfo();
@@ -3416,6 +3509,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
pipeline.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
}
renderFullScreenQuad(out, pipeline, driver);
unbindAllDescriptorSets(driver);
});
return ppQuadBlit->output;
@@ -3480,6 +3574,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blitDepth(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
getStructureDescriptorSet().bind(driver);
bindPerRenderableDescriptorSet(driver);
auto depth = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
auto const out = resources.getRenderPassInfo();
@@ -3499,6 +3594,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blitDepth(FrameGraph& fg,
float(vp.bottom) / inputDesc.height, float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height});
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
return ppQuadBlit->output;
@@ -3597,12 +3693,14 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolveDepth(FrameGraph& fg,
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const& input = resources.getTexture(data.input);
auto const& material = getPostProcessMaterial("resolveDepth");
FMaterialInstance* const mi =
getMaterialInstance(mEngine, material);
mi->setParameter("depth", input, {}); // NEAREST
commitAndRenderFullScreenQuad(driver, resources.getRenderPassInfo(), mi);
unbindAllDescriptorSets(driver);
});
return ppResolve->output;
@@ -3634,6 +3732,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto in = driver.createTextureView(resources.getTexture(data.in), level, 1);
auto out = resources.getRenderPassInfo();
@@ -3662,6 +3761,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
mi->use(driver);
renderFullScreenQuadWithScissor(out, pipeline, scissor, driver);
unbindAllDescriptorSets(driver);
driver.destroyTexture(in); // `in` is just a view on `data.in`
});
@@ -3689,6 +3789,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto color = resources.getTexture(data.color);
auto depth = resources.getTexture(data.depth);
auto const out = resources.getRenderPassInfo();
@@ -3698,6 +3799,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
mi->setParameter("color", color, {}); // nearest
mi->setParameter("depth", depth, {}); // nearest
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
return debugShadowCascadePass->output;
@@ -3734,6 +3836,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
auto out = resources.getRenderPassInfo();
@@ -3775,11 +3878,13 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
mi->setParameter("layerIndex", i);
mi->commit(driver);
renderFullScreenQuad(out, pipeline, driver);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
// From the second draw, don't clear the targetbuffer.
out.params.flags.clear = TargetBufferFlags::NONE;
out.params.flags.discardStart = TargetBufferFlags::NONE;
out.params.viewport.left += out.params.viewport.width;
}
unbindAllDescriptorSets(driver);
});
return ppQuadBlit->output;
@@ -3814,6 +3919,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugDisplayShadowTexture(
},
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
bindPerRenderableDescriptorSet(driver);
auto const out = resources.getRenderPassInfo();
auto in = resources.getTexture(data.depth);
auto const& material = getPostProcessMaterial("shadowmap");
@@ -3826,6 +3932,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugDisplayShadowTexture(
mi->setParameter("channel", (uint32_t)channel);
mi->setParameter("power", power);
commitAndRenderFullScreenQuad(driver, out, mi);
unbindAllDescriptorSets(driver);
});
input = shadomapDebugPass->color;
}
+8 -2
View File
@@ -114,9 +114,7 @@ public:
FrameGraphId<FrameGraphTexture> ssr(FrameGraph& fg,
RenderPassBuilder const& passBuilder,
FrameHistory const& frameHistory,
CameraInfo const& cameraInfo,
FrameGraphId<FrameGraphTexture> structure,
ScreenSpaceReflectionsOptions const& options,
FrameGraphTexture::Descriptor const& desc) noexcept;
// SSAO
@@ -395,6 +393,9 @@ public:
void resetForRender();
private:
static void unbindAllDescriptorSets(backend::DriverApi& driver) noexcept;
void bindPerRenderableDescriptorSet(backend::DriverApi& driver) noexcept;
// Helper to get a MaterialInstance from a FMaterial
// This currently just call FMaterial::getDefaultInstance().
@@ -413,6 +414,11 @@ private:
backend::VertexBufferInfoHandle mFullScreenQuadVbih;
backend::DescriptorSetLayoutHandle mPerRenderableDslh;
// We need to have a dummy descriptor set because each post processing pass is expected to have
// a descriptor set bound at the renderable bind point. But the set itself contains dummy
// values.
backend::DescriptorSetHandle mDummyPerRenderableDsh;
FEngine& mEngine;
mutable SsrPassDescriptorSet mSsrPassDescriptorSet;
+22 -27
View File
@@ -21,11 +21,15 @@
#include "details/Engine.h"
#include "details/View.h"
#include "ds/DescriptorSet.h"
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphResources.h"
#include "fg/FrameGraphTexture.h"
#include <private/filament/EngineEnums.h>
#include <filament/Options.h>
#include <filament/RenderableManager.h>
#include <filament/Viewport.h>
@@ -113,7 +117,7 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
clearDepthFlags = TargetBufferFlags::DEPTH;
clearStencilFlags = config.enabledStencilBuffer ?
TargetBufferFlags::STENCIL : TargetBufferFlags::NONE;
const char* const name = config.enabledStencilBuffer ?
const char* const textureName = config.enabledStencilBuffer ?
"Depth/Stencil Buffer" : "Depth Buffer";
bool const isES2 =
@@ -128,7 +132,7 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
TextureFormat const format = config.enabledStencilBuffer ?
stencilFormat : depthOnlyFormat;
data.depth = builder.createTexture(name, {
data.depth = builder.createTexture(textureName, {
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
// If the color attachment requested MS, we assume this means the MS
@@ -204,37 +208,23 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
view.prepareSSAO(data.ssao ?
resources.getTexture(data.ssao) : engine.getOneTextureArray());
view.prepareShadowMapping(engine, view.getVsmShadowOptions().highPrecision);
// set shadow sampler
view.prepareShadow(data.shadows ?
resources.getTexture(data.shadows) :
(view.getShadowType() != ShadowType::PCF ?
engine.getOneTextureArray() : engine.getOneTextureArrayDepth()));
// set screen-space reflections and screen-space refractions
view.prepareSSR(data.ssr ?
resources.getTexture(data.ssr) : engine.getOneTextureArray());
// set structure sampler
view.prepareStructure(data.structure ?
resources.getTexture(data.structure) : engine.getOneTexture());
// set screen-space reflections and screen-space refractions
TextureHandle const ssr = data.ssr ?
resources.getTexture(data.ssr) : engine.getOneTextureArray();
// set shadow sampler
view.prepareShadowMapping(engine,
data.shadows
? resources.getTexture(data.shadows)
: (view.getShadowType() != ShadowType::PCF
? engine.getOneTextureArray()
: engine.getOneTextureArrayDepth()));
view.prepareSSR(ssr, config.screenSpaceReflectionHistoryNotReady,
config.ssrLodOffset, view.getScreenSpaceReflectionsOptions());
// Note: here we can't use data.color's descriptor for the viewport because
// the actual viewport might be offset when the target is the swapchain.
// However, the width/height should be the same.
assert_invariant(
out.params.viewport.width == resources.getDescriptor(data.color).width);
assert_invariant(
out.params.viewport.height == resources.getDescriptor(data.color).height);
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport),
config.logicalViewport);
view.commitUniformsAndSamplers(driver);
view.commitDescriptorSet(driver);
// TODO: this should be a parameter of FrameGraphRenderPass::Descriptor
out.params.clearStencil = config.clearStencil;
@@ -254,6 +244,11 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
driver.beginRenderPass(out.target, out.params);
passExecutor.execute(engine, driver);
driver.endRenderPass();
// unbind all descriptor sets to avoid false dependencies with the next pass
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_VIEW);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_RENDERABLE);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
}
);
-4
View File
@@ -65,16 +65,12 @@ public:
math::float4 clearColor = {};
// Clear stencil
uint8_t clearStencil = 0u;
// Lod offset for the SSR passes
float ssrLodOffset;
// Contact shadow enabled?
bool hasContactShadows;
// Screen space reflections enabled
bool hasScreenSpaceReflectionsOrRefractions;
// Use a depth format with a stencil component.
bool enabledStencilBuffer;
// whether the screenspace reflections history buffer is initialized
bool screenSpaceReflectionHistoryNotReady;
};
struct ColorPassInput {
+13 -4
View File
@@ -23,7 +23,7 @@
#include <filament/LightManager.h>
#include <filament/Options.h>
#include <iterator>
#include <private/filament/EngineEnums.h>
#include "components/RenderableManager.h"
@@ -33,6 +33,8 @@
#include "details/Texture.h"
#include "details/View.h"
#include "ds/DescriptorSet.h"
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphRenderPass.h"
@@ -57,6 +59,7 @@
#include <array>
#include <cmath>
#include <functional>
#include <iterator>
#include <limits>
#include <new>
#include <memory>
@@ -433,6 +436,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// Finally update our UBO in one batch
if (mShadowUb.isDirty()) {
mShadowUb.clean();
driver.updateBufferObject(mShadowUbh,
mShadowUb.toBufferDescriptor(driver), 0);
}
@@ -560,14 +564,19 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// if we know there are no visible shadows, we can skip rendering, but
// we need the render-pass to clear/initialize the shadow-map
// Note: this is always true for directional/cascade shadows.
if (curr->shadowMap->hasVisibleShadows()) {
curr->shadowMap->bind(driver);
curr->executor.overrideScissor(curr->shadowMap->getScissor());
if (ShadowMap const* const shadowMap = curr->shadowMap; shadowMap->hasVisibleShadows()) {
shadowMap->bind(driver);
curr->executor.overrideScissor(shadowMap->getScissor());
curr->executor.execute(engine, driver);
}
}
driver.endRenderPass();
// unbind all descriptor sets to avoid false dependencies with the next pass
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_VIEW);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_RENDERABLE);
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
});
first = last;
+14 -7
View File
@@ -1235,13 +1235,20 @@ bool FEngine::destroy(const FMaterialInstance* p) {
size_t const primitiveCount = rcm.getPrimitiveCount(ri, 0);
for (size_t j = 0; j < primitiveCount; j++) {
auto const* const mi = rcm.getMaterialInstanceAt(ri, 0, j);
auto const& featureFlags = features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(
mi != p,
featureFlags.assert_material_instance_in_use)
<< "destroying MaterialInstance \"" << mi->getName()
<< "\" which is still in use by Renderable (entity=" << entity.getId()
<< ", instance=" << ri.asValue() << ", index=" << j << ")";
if (features.engine.debug.assert_material_instance_in_use) {
FILAMENT_CHECK_PRECONDITION(mi != p)
<< "destroying MaterialInstance \""
<< mi->getName() << "\" which is still in use by Renderable (entity="
<< entity.getId() << ", instance="
<< ri.asValue() << ", index=" << j << ")";
} else {
if (UTILS_UNLIKELY(mi == p)) {
LOG(ERROR) << "destroying MaterialInstance \"" << mi->getName()
<< "\" which is still in use by Renderable (entity="
<< entity.getId() << ", instance=" << ri.asValue()
<< ", index=" << j << ")";
}
}
}
}
}
-5
View File
@@ -731,8 +731,6 @@ public:
bool assert_vertex_buffer_count_exceeds_8 = CORRECTNESS_ASSERTION_DEFAULT;
bool assert_vertex_buffer_attribute_stride_mult_of_4 =
CORRECTNESS_ASSERTION_DEFAULT;
bool assert_material_instance_texture_descriptor_set_compatible =
CORRECTNESS_ASSERTION_DEFAULT;
} debug;
} engine;
struct {
@@ -785,9 +783,6 @@ public:
{ "backend.vulkan.enable_staging_buffer_bypass",
"vulkan: enable a staging bypass logic for unified memory architecture",
&features.backend.vulkan.enable_staging_buffer_bypass, false },
{ "engine.debug.assert_material_instance_texture_descriptor_set_compatible",
"Assert that the textures in a material instance are compatible with descriptor set.",
&features.engine.debug.assert_material_instance_texture_descriptor_set_compatible, false },
}};
utils::Slice<const FeatureFlag> getFeatureFlags() const noexcept {
+10 -5
View File
@@ -391,11 +391,16 @@ void FMaterial::terminate(FEngine& engine) {
auto const& materialInstanceResourceList = engine.getMaterialInstanceResourceList();
auto pos = materialInstanceResourceList.find(this);
if (UTILS_LIKELY(pos != materialInstanceResourceList.cend())) {
auto const& featureFlags = engine.features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(pos->second.empty(),
featureFlags.assert_destroy_material_before_material_instance)
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
if (engine.features.engine.debug.assert_destroy_material_before_material_instance) {
FILAMENT_CHECK_PRECONDITION(pos->second.empty())
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
} else {
if (UTILS_UNLIKELY(!pos->second.empty())) {
LOG(ERROR) << "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
}
}
}
#if FILAMENT_ENABLE_MATDBG
+4 -5
View File
@@ -222,6 +222,7 @@ void FMaterialInstance::commit(FEngine& engine) const {
void FMaterialInstance::commit(DriverApi& driver) const {
// update uniforms if needed
if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
mUniforms.clean();
driver.updateBufferObject(mUbHandle, mUniforms.toBufferDescriptor(driver), 0);
}
if (!mTextureParameters.empty()) {
@@ -285,11 +286,9 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
DescriptorType const descriptorType = descriptorSetLayout.getDescriptorType(binding);
TextureType const textureType = texture->getTextureType();
SamplerType const samplerType = texture->getTarget();
auto const& featureFlags = mMaterial->getEngine().features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(
DescriptorSet::isTextureCompatibleWithDescriptor(
textureType, samplerType, descriptorType),
featureFlags.assert_material_instance_texture_descriptor_set_compatible)
FILAMENT_CHECK_PRECONDITION(
DescriptorSet::isTextureCompatibleWithDescriptor(textureType, samplerType, descriptorType))
<< "Texture format " << int(texture->getFormat())
<< " of type " << to_string(textureType)
<< " with sampler type " << to_string(samplerType)
+57 -38
View File
@@ -574,12 +574,20 @@ void FRenderer::renderStandaloneView(FView const* view) {
1'000'000'000.0 / mDisplayInfo.refreshRate),
mFrameId);
renderInternal(view);
// because we don't have a "present" call, we use flush so the driver can submit
// the command buffer; we do this before driver.endFrame() to mimic what would
// happen with Renderer::beginFrame/endFrame.
renderInternal(view, true);
driver.endFrame(mFrameId);
// This is a workaround for internal bug b/361822355.
// TODO: properly address the bug and remove this workaround.
// engine.flush() has already been called by renderInternal(), but we need an extra one
// for endFrame() above. This operation in actually not too heavy, it just kicks the
// driver thread, which is mostlikely already running.
engine.flush();
// FIXME: This is a workaround for internal bug b/361822355.
// properly address the bug and remove this workaround.
if (engine.getBackend() == Backend::VULKAN) {
engine.flushAndWait();
}
@@ -600,11 +608,11 @@ void FRenderer::render(FView const* view) {
assert_invariant(mSwapChain);
if (UTILS_LIKELY(view && view->getScene() && view->hasCamera())) {
renderInternal(view);
renderInternal(view, false);
}
}
void FRenderer::renderInternal(FView const* view) {
void FRenderer::renderInternal(FView const* view, bool flush) {
FEngine& engine = mEngine;
FILAMENT_CHECK_PRECONDITION(!view->hasPostProcessPass() ||
@@ -621,6 +629,10 @@ void FRenderer::renderInternal(FView const* view) {
// execute the render pass
renderJob(rootArenaScope, const_cast<FView&>(*view));
if (flush) {
engine.getDriverApi().flush();
}
// make sure to flush the command buffer
engine.flush();
@@ -818,29 +830,57 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
xvp.bottom = int32_t(guardBand);
}
/*
* Frame graph
*/
FrameGraph fg(*mResourceAllocator,
isProtectedContent ? FrameGraph::Mode::PROTECTED : FrameGraph::Mode::UNPROTECTED);
auto& blackboard = fg.getBlackboard();
PostProcessManager::ScreenSpaceRefConfig const ssrConfig = PostProcessManager::prepareMipmapSSR(
fg, svp.width, svp.height,
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), scale);
/*
* Update the PER_VIEW UBO used for the color, ssr and postfx passes. This UBO is never
* updated again during the frame.
*/
auto [bias, derivativeScale] = prepareUpscaler(scale, taaOptions, dsrOptions);
view.prepare(engine, driver, rootArenaScope, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel);
view.prepareLodBias(bias, derivativeScale);
view.prepareSSAO(aoOptions);
view.prepareSSR(engine, cameraInfo, ssrConfig.lodOffset, ssReflectionsOptions);
view.prepareShadowMapping();
// There might be a bug here with svp's origin; normally the origin could offset when the
// target is the swapchain (width/height are the same though). This would mean that we'd have
// to wait for the color pass to execute to set the viewport. For now, I wasn't able to
// get into such a case though.
view.prepareViewport(svp, xvp);
view.commitUniforms(driver);
/*
* Update the PER_VIEW UBO use for the structure pass. It never updated again after this point.
*/
// Set the PER_VIEW UBO for the passes that use the user materials, but are not the color pass
// (e.g. structure, ssao). The PER_VIEW UBO may be different because these passes don't run
// at the same resolution. We set only the values that are relevant to both user-materials
// and the concerned passes. The PER_VIEW UBO contains data that is needed by the user
// materials public APIs.
StructureDescriptorSet& descriptorSet = ppm.getStructureDescriptorSet();
descriptorSet.prepareCamera(engine, cameraInfo);
descriptorSet.prepareTime(engine, getShaderUserTime());
descriptorSet.prepareViewport(svp, {
{ // the scope helps to guarantee that we're not modifying the descriptor-set later
StructureDescriptorSet& ds = ppm.getStructureDescriptorSet();
ds.prepareCamera(engine, cameraInfo);
ds.prepareTime(engine, getShaderUserTime());
ds.prepareViewport(svp, {
int32_t(float(xvp.left) * aoOptions.resolution),
int32_t(float(xvp.bottom) * aoOptions.resolution),
uint32_t(float(xvp.width) * aoOptions.resolution),
uint32_t(float(xvp.height) * aoOptions.resolution) });
descriptorSet.prepareLodBias(bias, derivativeScale);
descriptorSet.prepareMaterialGlobals(view.getMaterialGlobals());
descriptorSet.commit(driver);
ds.prepareLodBias(bias, derivativeScale);
ds.prepareMaterialGlobals(view.getMaterialGlobals());
}
/*
* Allocate command buffer
@@ -874,14 +914,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
variant.setStereo(view.hasStereo());
/*
* Frame graph
*/
FrameGraph fg(*mResourceAllocator,
isProtectedContent ? FrameGraph::Mode::PROTECTED : FrameGraph::Mode::UNPROTECTED);
auto& blackboard = fg.getBlackboard();
/*
* Shadow pass
*/
@@ -957,7 +989,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// the clearFlags and clearColor specified below will only apply when rendering into the
// temporary color buffer. In particular, they won't apply when rendering into the main
// swapchain (imported render target above)
RendererUtils::ColorPassConfig config{
RendererUtils::ColorPassConfig const config{
.physicalViewport = svp,
.logicalViewport = xvp,
.scale = scale,
@@ -966,7 +998,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
.clearFlags = getClearFlags(),
.clearColor = clearColor,
.clearStencil = clearStencil,
.ssrLodOffset = 0.0f,
.hasContactShadows = scene.hasContactShadows(),
// at this point we don't know if we have refraction, but that's handled later
.hasScreenSpaceReflectionsOrRefractions = ssReflectionsOptions.enabled,
@@ -1052,23 +1083,12 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
blackboard["ssao"] = ssao;
}
// --------------------------------------------------------------------------------------------
// prepare screen-space reflection/refraction passes
PostProcessManager::ScreenSpaceRefConfig const ssrConfig = PostProcessManager::prepareMipmapSSR(
fg, svp.width, svp.height,
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale);
config.ssrLodOffset = ssrConfig.lodOffset;
// --------------------------------------------------------------------------------------------
// screen-space reflections pass
if (ssReflectionsOptions.enabled) {
auto reflections = ppm.ssr(fg, passBuilder,
view.getFrameHistory(), cameraInfo,
structure,
ssReflectionsOptions,
view.getFrameHistory(), structure,
{ .width = svp.width, .height = svp.height });
if (UTILS_LIKELY(reflections)) {
@@ -1076,7 +1096,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
PostProcessManager::generateMipmapSSR(ppm, fg,
reflections, ssrConfig.reflection, false, ssrConfig);
}
config.screenSpaceReflectionHistoryNotReady = !reflections;
}
// --------------------------------------------------------------------------------------------
+1 -1
View File
@@ -189,7 +189,7 @@ private:
return mCommandsHighWatermark;
}
void renderInternal(FView const* view);
void renderInternal(FView const* view, bool flush);
void renderJob(RootArenaScope& rootArenaScope, FView& view);
static std::pair<float, math::float2> prepareUpscaler(math::float2 scale,
+15 -9
View File
@@ -158,11 +158,12 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
static_assert(DOCUMENTED_MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_BUFFER_COUNT);
auto const& featureFlags = static_cast<FEngine*>(&engine)->features.engine.debug;
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(
mImpl->mBufferCount <= DOCUMENTED_MAX_VERTEX_BUFFER_COUNT,
featureFlags.assert_vertex_buffer_count_exceeds_8)
<< "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
if (downcast(engine).features.engine.debug.assert_vertex_buffer_count_exceeds_8) {
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount <= DOCUMENTED_MAX_VERTEX_BUFFER_COUNT)
<< "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
} else if (mImpl->mBufferCount > DOCUMENTED_MAX_VERTEX_BUFFER_COUNT) {
LOG(WARNING) << "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
}
// Next we check if any unused buffer slots have been allocated. This helps prevent errors
// because uploading to an unused slot can trigger undefined behavior in the backend.
@@ -176,10 +177,15 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
<< "attribute " << j << " offset=" << attributes[j].offset
<< " is not multiple of 4";
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0,
featureFlags.assert_vertex_buffer_attribute_stride_mult_of_4)
<< "attribute " << j << " stride=" << +attributes[j].stride
<< " is not multiple of 4";
FEngine* fengine = static_cast<FEngine*>(&engine);
if (fengine->features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4) {
FILAMENT_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0)
<< "attribute " << j << " stride=" << attributes[j].stride
<< " is not multiple of 4";
} else if ((attributes[j].stride & 0x3u) != 0) {
LOG(WARNING) << "attribute " << j << " stride=" << attributes[j].stride
<< " is not multiple of 4";
}
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
FILAMENT_CHECK_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET))
+114 -35
View File
@@ -948,61 +948,140 @@ void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
getColorPassDescriptorSet().prepareSSAO(ssao, mAmbientOcclusionOptions);
}
void FView::prepareSSR(Handle<HwTexture> ssr,
bool const disableSSR,
float const refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept {
getColorPassDescriptorSet().prepareSSR(ssr, disableSSR, refractionLodOffset, ssrOptions);
void FView::prepareSSAO(AmbientOcclusionOptions const& options) const noexcept {
// High quality sampling is enabled only if AO itself is enabled and upsampling quality is at
// least set to high and of course only if upsampling is needed.
const bool highQualitySampling = options.upsampling >= QualityLevel::HIGH
&& options.resolution < 1.0f;
const float edgeDistance = 1.0f / options.bilateralThreshold;
auto& s = mUniforms.edit();
s.aoSamplingQualityAndEdgeDistance =
options.enabled ? (highQualitySampling ? edgeDistance : 0.0f) : -1.0f;
s.aoBentNormals = options.enabled && options.bentNormals ? 1.0f : 0.0f;
}
void FView::prepareSSR(Handle<HwTexture> ssr) const noexcept {
getColorPassDescriptorSet().prepareScreenSpaceRefraction(ssr);
}
void FView::prepareSSR(FEngine& engine, CameraInfo const& cameraInfo,
float const refractionLodOffset,
ScreenSpaceReflectionsOptions const& options) const noexcept {
auto const& ssr = getFrameHistory().getPrevious().ssr;
bool const disableSSR = !ssr.color.handle;
mat4 const& historyProjection = ssr.projection;
mat4f const& uvFromClipMatrix = engine.getUvFromClipMatrix();
mat4f const projection = cameraInfo.projection;
mat4 const userViewMatrix = cameraInfo.getUserViewMatrix();
// set screen-space reflections and screen-space refractions
mat4f const uvFromViewMatrix = uvFromClipMatrix * projection;
mat4f const reprojection = uvFromClipMatrix *
mat4f{ historyProjection * inverse(userViewMatrix) };
auto& s = mUniforms.edit();
s.ssrReprojection = reprojection;
s.ssrUvFromViewMatrix = uvFromViewMatrix;
s.ssrThickness = options.thickness;
s.ssrBias = options.bias;
s.ssrStride = options.stride;
s.refractionLodOffset = refractionLodOffset;
s.ssrDistance = (options.enabled && !disableSSR) ? options.maxDistance : 0.0f;
}
void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
// sampler must be NEAREST
getColorPassDescriptorSet().prepareStructure(structure);
}
void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
void FView::prepareShadowMapping(FEngine const& engine, Handle<HwTexture> texture) const noexcept {
// when needsShadowMap() is not set, this method only just sets a dummy texture
// in the needed samplers (in that case `texture` is actually a dummy texture).
ShadowMapManager::ShadowMappingUniforms uniforms;
if (needsShadowMap()) {
uniforms = mShadowMapManager->getShadowMappingUniforms();
}
switch (mShadowType) {
case ShadowType::PCF:
getColorPassDescriptorSet().prepareShadowPCF(texture, uniforms);
break;
case ShadowType::VSM:
getColorPassDescriptorSet().prepareShadowVSM(texture, uniforms, mVsmShadowOptions);
break;
case ShadowType::DPCF:
getColorPassDescriptorSet().prepareShadowDPCF(texture, uniforms, mSoftShadowOptions);
break;
case ShadowType::PCSS:
getColorPassDescriptorSet().prepareShadowPCSS(texture, uniforms, mSoftShadowOptions);
break;
case ShadowType::PCFd:
getColorPassDescriptorSet().prepareShadowPCFDebug(texture, uniforms);
break;
}
}
void FView::prepareShadowMapping(FEngine const& engine, bool const highPrecision) const noexcept {
BufferObjectHandle ubo{ engine.getDummyUniformBuffer() };
if (mHasShadowing) {
assert_invariant(mShadowMapManager);
ubo = mShadowMapManager->getShadowUniformsHandle();
}
getColorPassDescriptorSet().prepareShadowMapping(ubo, highPrecision);
getColorPassDescriptorSet().prepareShadowMapping(ubo);
switch (mShadowType) {
case ShadowType::PCF:
getColorPassDescriptorSet().prepareShadowPCF(texture);
break;
case ShadowType::VSM:
getColorPassDescriptorSet().prepareShadowVSM(texture, mVsmShadowOptions);
break;
case ShadowType::DPCF:
getColorPassDescriptorSet().prepareShadowDPCF(texture);
break;
case ShadowType::PCSS:
getColorPassDescriptorSet().prepareShadowPCSS(texture);
break;
case ShadowType::PCFd:
getColorPassDescriptorSet().prepareShadowPCFDebug(texture);
break;
}
}
void FView::commitUniformsAndSamplers(DriverApi& driver) const noexcept {
void FView::prepareShadowMapping() const noexcept {
ShadowMapManager::ShadowMappingUniforms uniforms{};
if (needsShadowMap()) {
uniforms = mShadowMapManager->getShadowMappingUniforms();
}
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCF = 0u;
constexpr uint32_t SHADOW_SAMPLING_RUNTIME_EVSM = 1u;
constexpr uint32_t SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
auto& s = mUniforms.edit();
s.cascadeSplits = uniforms.cascadeSplits;
s.ssContactShadowDistance = uniforms.ssContactShadowDistance;
s.directionalShadows = int32_t(uniforms.directionalShadows);
s.cascades = int32_t(uniforms.cascades);
switch (mShadowType) {
case ShadowType::PCF:
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
break;
case ShadowType::VSM:
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_EVSM;
s.vsmExponent = mVsmShadowOptions.highPrecision ? high : low;
s.vsmDepthScale = mVsmShadowOptions.minVarianceScale * 0.01f * s.vsmExponent;
s.vsmLightBleedReduction = mVsmShadowOptions.lightBleedReduction;
break;
case ShadowType::DPCF:
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_DPCF;
s.shadowPenumbraRatioScale = mSoftShadowOptions.penumbraRatioScale;
break;
case ShadowType::PCSS:
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCSS;
s.shadowPenumbraRatioScale = mSoftShadowOptions.penumbraRatioScale;
break;
case ShadowType::PCFd:
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
break;
}
}
void FView::commitUniforms(DriverApi& driver) const noexcept {
if (mUniforms.isDirty()) {
mUniforms.clean();
driver.updateBufferObject(mUniforms.getUboHandle(),
mUniforms.toBufferDescriptor(driver), 0);
}
}
void FView::commitDescriptorSet(DriverApi& driver) const noexcept {
getColorPassDescriptorSet().commit(driver);
}
void FView::unbindSamplers(FEngine& engine) noexcept {
getColorPassDescriptorSet().unbindSamplers(engine);
}
void FView::commitFroxels(DriverApi& driverApi) const noexcept {
if (mHasDynamicLighting) {
mFroxelizer.commit(driverApi);
+10 -7
View File
@@ -173,16 +173,19 @@ public:
void prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexcept;
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr, bool disableSSR,
float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
void prepareSSAO(AmbientOcclusionOptions const& options) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr) const noexcept;
void prepareSSR(FEngine& engine, CameraInfo const& cameraInfo,
float refractionLodOffset, ScreenSpaceReflectionsOptions const& options) const noexcept;
void prepareStructure(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadow(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadowMapping(FEngine const& engine, bool highPrecision) const noexcept;
void prepareShadowMapping(FEngine const& engine, backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadowMapping() const noexcept;
void commitFroxels(backend::DriverApi& driverApi) const noexcept;
void commitUniformsAndSamplers(backend::DriverApi& driver) const noexcept;
void unbindSamplers(FEngine& engine) noexcept;
void commitUniforms(backend::DriverApi& driver) const noexcept;
void commitDescriptorSet(backend::DriverApi& driver) const noexcept;
utils::JobSystem::Job* getFroxelizerSync() const noexcept { return mFroxelizerSync; }
void setFroxelizerSync(utils::JobSystem::Job* sync) noexcept { mFroxelizerSync = sync; }
+6 -85
View File
@@ -257,7 +257,6 @@ void ColorPassDescriptorSet::prepareFog(FEngine& engine, const CameraInfo& camer
void ColorPassDescriptorSet::prepareSSAO(Handle<HwTexture> ssao,
AmbientOcclusionOptions const& options) noexcept {
// High quality sampling is enabled only if AO itself is enabled and upsampling quality is at
// least set to high and of course only if upsampling is needed.
const bool highQualitySampling = options.upsampling >= QualityLevel::HIGH
@@ -268,12 +267,6 @@ void ColorPassDescriptorSet::prepareSSAO(Handle<HwTexture> ssao,
.filterMag = options.enabled && !highQualitySampling ?
SamplerMagFilter::LINEAR : SamplerMagFilter::NEAREST
});
const float edgeDistance = 1.0f / options.bilateralThreshold;
auto& s = mUniforms.edit();
s.aoSamplingQualityAndEdgeDistance =
options.enabled ? (highQualitySampling ? edgeDistance : 0.0f) : -1.0f;
s.aoBentNormals = options.enabled && options.bentNormals ? 1.0f : 0.0f;
}
void ColorPassDescriptorSet::prepareBlending(bool const needsAlphaChannel) noexcept {
@@ -285,19 +278,11 @@ void ColorPassDescriptorSet::prepareMaterialGlobals(
PerViewDescriptorSetUtils::prepareMaterialGlobals(mUniforms.edit(), materialGlobals);
}
void ColorPassDescriptorSet::prepareSSR(Handle<HwTexture> ssr,
bool const disableSSR,
float const refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
void ColorPassDescriptorSet::prepareScreenSpaceRefraction(Handle<HwTexture> ssr) noexcept {
setSampler(+PerViewBindingPoints::SSR, ssr, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR
});
auto& s = mUniforms.edit();
s.refractionLodOffset = refractionLodOffset;
s.ssrDistance = (ssrOptions.enabled && !disableSSR) ? ssrOptions.maxDistance : 0.0f;
}
void ColorPassDescriptorSet::prepareStructure(Handle<HwTexture> structure) noexcept {
@@ -379,27 +364,12 @@ void ColorPassDescriptorSet::prepareDynamicLights(Froxelizer& froxelizer) noexce
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
}
void ColorPassDescriptorSet::prepareShadowMapping(BufferObjectHandle shadowUniforms, bool const highPrecision) noexcept {
auto& s = mUniforms.edit();
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
s.vsmExponent = highPrecision ? high : low;
void ColorPassDescriptorSet::prepareShadowMapping(BufferObjectHandle shadowUniforms) noexcept {
setBuffer(+PerViewBindingPoints::SHADOWS, shadowUniforms, 0, sizeof(ShadowUib));
}
void ColorPassDescriptorSet::prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
uniforms.directionalShadows = int32_t(shadowMappingUniforms.directionalShadows);
uniforms.cascades = int32_t(shadowMappingUniforms.cascades);
}
void ColorPassDescriptorSet::prepareShadowVSM(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept {
constexpr float low = 5.54f; // ~ std::log(std::numeric_limits<math::half>::max()) * 0.5f;
constexpr float high = 42.0f; // ~ std::log(std::numeric_limits<float>::max()) * 0.5f;
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
if (options.anisotropy > 0 || options.mipmapping) {
filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR;
@@ -410,16 +380,9 @@ void ColorPassDescriptorSet::prepareShadowVSM(Handle<HwTexture> texture,
.filterMin = filterMin,
.anisotropyLog2 = options.anisotropy,
});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_EVSM;
s.vsmExponent = options.highPrecision ? high : low;
s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent;
s.vsmLightBleedReduction = options.lightBleedReduction;
prepareShadowSampling(s, shadowMappingUniforms);
}
void ColorPassDescriptorSet::prepareShadowPCF(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
void ColorPassDescriptorSet::prepareShadowPCF(Handle<HwTexture> texture) noexcept {
setSampler(+PerViewBindingPoints::SHADOW_MAP,
texture, {
.filterMag = SamplerMagFilter::LINEAR,
@@ -427,71 +390,29 @@ void ColorPassDescriptorSet::prepareShadowPCF(Handle<HwTexture> texture,
.compareMode = SamplerCompareMode::COMPARE_TO_TEXTURE,
.compareFunc = SamplerCompareFunc::GE
});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
prepareShadowSampling(s, shadowMappingUniforms);
}
void ColorPassDescriptorSet::prepareShadowDPCF(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept {
void ColorPassDescriptorSet::prepareShadowDPCF(Handle<HwTexture> texture) noexcept {
setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, {});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_DPCF;
s.shadowPenumbraRatioScale = options.penumbraRatioScale;
prepareShadowSampling(s, shadowMappingUniforms);
}
void ColorPassDescriptorSet::prepareShadowPCSS(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept {
void ColorPassDescriptorSet::prepareShadowPCSS(Handle<HwTexture> texture) noexcept {
setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, {});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCSS;
s.shadowPenumbraRatioScale = options.penumbraRatioScale;
prepareShadowSampling(s, shadowMappingUniforms);
}
void ColorPassDescriptorSet::prepareShadowPCFDebug(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
void ColorPassDescriptorSet::prepareShadowPCFDebug(Handle<HwTexture> texture) noexcept {
setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, {
.filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST
});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
prepareShadowSampling(s, shadowMappingUniforms);
}
void ColorPassDescriptorSet::commit(DriverApi& driver) noexcept {
if (mUniforms.isDirty()) {
driver.updateBufferObject(mUniforms.getUboHandle(),
mUniforms.toBufferDescriptor(driver), 0);
}
for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) {
mDescriptorSet[i].commit(mDescriptorSetLayout[i], driver);
}
}
void ColorPassDescriptorSet::unbindSamplers(FEngine& engine) noexcept {
// this needs to reset the sampler that are only set in RendererUtils::colorPass(), because
// this descriptor-set is also used for ssr/picking/structure and these could be stale
// it would be better to use a separate descriptor-set for those two cases so that we don't
// have to do this
setBuffer(+PerViewBindingPoints::SHADOWS,
engine.getDummyUniformBuffer(), 0, sizeof(ShadowUib));
setSampler(+PerViewBindingPoints::STRUCTURE,
engine.getZeroTexture(), {});
setSampler(+PerViewBindingPoints::SHADOW_MAP,
engine.getOneTextureArrayDepth(), {});
setSampler(+PerViewBindingPoints::SSAO,
engine.getZeroTextureArray(), {});
setSampler(+PerViewBindingPoints::SSR,
engine.getZeroTextureArray(), {});
}
void ColorPassDescriptorSet::setSampler(descriptor_binding_t const binding,
TextureHandle th, SamplerParams const params) noexcept {
for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) {
+7 -26
View File
@@ -23,6 +23,7 @@
#include "TypedUniformBuffer.h"
#include <private/filament/EngineEnums.h>
#include <private/filament/UibStructs.h>
#include <backend/DriverEnums.h>
@@ -66,11 +67,6 @@ class ColorPassDescriptorSet {
using LightManagerInstance = utils::EntityInstance<LightManager>;
using TextureHandle = backend::Handle<backend::HwTexture>;
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCF = 0u;
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_EVSM = 1u;
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_DPCF = 2u;
static constexpr uint32_t SHADOW_SAMPLING_RUNTIME_PCSS = 3u;
public:
static uint8_t getIndex(bool lit, bool ssr, bool fog) noexcept;
@@ -112,12 +108,9 @@ public:
void prepareMaterialGlobals(std::array<math::float4, 4> const& materialGlobals) noexcept;
// screen-space reflection and/or refraction (SSR)
void prepareSSR(TextureHandle ssr,
bool disableSSR,
float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
void prepareScreenSpaceRefraction(TextureHandle ssr) noexcept;
void prepareShadowMapping(backend::BufferObjectHandle shadowUniforms, bool highPrecision) noexcept;
void prepareShadowMapping(backend::BufferObjectHandle shadowUniforms) noexcept;
void prepareDirectionalLight(FEngine& engine, float exposure,
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
@@ -128,28 +121,19 @@ public:
void prepareDynamicLights(Froxelizer& froxelizer) noexcept;
void prepareShadowVSM(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms,
VsmShadowOptions const& options) noexcept;
void prepareShadowPCF(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
void prepareShadowPCF(TextureHandle texture) noexcept;
void prepareShadowDPCF(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept;
void prepareShadowDPCF(TextureHandle texture) noexcept;
void prepareShadowPCSS(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept;
void prepareShadowPCSS(TextureHandle texture) noexcept;
void prepareShadowPCFDebug(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
void prepareShadowPCFDebug(TextureHandle texture) noexcept;
// update local data into GPU UBO
void commit(backend::DriverApi& driver) noexcept;
void unbindSamplers(FEngine& engine) noexcept;
// bind this UBO
void bind(backend::DriverApi& driver, uint8_t const index) const noexcept {
mDescriptorSet[index].bind(driver, DescriptorSetBindingPoints::PER_VIEW);
@@ -166,9 +150,6 @@ private:
void setBuffer(backend::descriptor_binding_t binding,
backend::BufferObjectHandle boh, uint32_t offset, uint32_t size) noexcept;
static void prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;
TypedUniformBuffer<PerViewUib>& mUniforms;
std::array<DescriptorSetLayout, DESCRIPTOR_LAYOUT_COUNT> mDescriptorSetLayout;
std::array<DescriptorSet, DESCRIPTOR_LAYOUT_COUNT> mDescriptorSet;
+5
View File
@@ -139,6 +139,11 @@ void DescriptorSet::bind(FEngine::DriverApi& driver, DescriptorSetBindingPoints
driver.bindDescriptorSet(mDescriptorSetHandle, +set, std::move(dynamicOffsets));
}
void DescriptorSet::unbind(backend::DriverApi& driver,
DescriptorSetBindingPoints set) noexcept {
driver.bindDescriptorSet({}, +set, {});
}
void DescriptorSet::setBuffer(DescriptorSetLayout const& layout,
backend::descriptor_binding_t const binding,
backend::Handle<backend::HwBufferObject> boh, uint32_t const offset, uint32_t const size) {
+3
View File
@@ -62,6 +62,9 @@ public:
void bind(backend::DriverApi& driver, DescriptorSetBindingPoints set,
backend::DescriptorSetOffsetArray dynamicOffsets) const noexcept;
// unbind the descriptor set
static void unbind(backend::DriverApi& driver, DescriptorSetBindingPoints set) noexcept;
// sets a ubo/ssbo descriptor
void setBuffer(DescriptorSetLayout const& layout,
backend::descriptor_binding_t binding,
+1 -21
View File
@@ -66,29 +66,14 @@ void SsrPassDescriptorSet::setFrameUniforms(FEngine const& engine,
// and GL would be okay without this, but Vulkan's validation layer would complain.
mDescriptorSet.setBuffer(engine.getPerViewDescriptorSetLayoutSsrVariant(),
+PerViewBindingPoints::SHADOWS, mShadowUbh, 0, sizeof(ShadowUib));
mUniforms = std::addressof(uniforms);
}
void SsrPassDescriptorSet::prepareHistorySSR(FEngine const& engine, Handle<HwTexture> ssr,
mat4f const& historyProjection,
mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept {
void SsrPassDescriptorSet::prepareHistorySSR(FEngine const& engine, Handle<HwTexture> ssr) noexcept {
mDescriptorSet.setSampler(engine.getPerViewDescriptorSetLayoutSsrVariant(),
+PerViewBindingPoints::SSR_HISTORY, ssr, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR
});
assert_invariant(mUniforms);
auto& s = mUniforms->edit();
s.ssrReprojection = historyProjection;
s.ssrUvFromViewMatrix = uvFromViewMatrix;
s.ssrThickness = ssrOptions.thickness;
s.ssrBias = ssrOptions.bias;
s.ssrDistance = ssrOptions.enabled ? ssrOptions.maxDistance : 0.0f;
s.ssrStride = ssrOptions.stride;
}
void SsrPassDescriptorSet::prepareStructure(FEngine const& engine,
@@ -99,12 +84,7 @@ void SsrPassDescriptorSet::prepareStructure(FEngine const& engine,
}
void SsrPassDescriptorSet::commit(FEngine& engine) noexcept {
assert_invariant(mUniforms);
DriverApi& driver = engine.getDriverApi();
if (mUniforms->isDirty()) {
driver.updateBufferObject(mUniforms->getUboHandle(),
mUniforms->toBufferDescriptor(driver), 0);
}
mDescriptorSet.commit(engine.getPerViewDescriptorSetLayoutSsrVariant(), driver);
}
+1 -10
View File
@@ -19,8 +19,6 @@
#include "DescriptorSet.h"
#include "DescriptorSetLayout.h"
#include "TypedUniformBuffer.h"
#include <private/filament/UibStructs.h>
@@ -28,8 +26,6 @@
#include <backend/DriverApiForward.h>
#include <backend/Handle.h>
#include <math/mat4.h>
namespace filament {
class FEngine;
@@ -51,11 +47,7 @@ public:
void prepareStructure(FEngine const& engine, TextureHandle structure) noexcept;
void prepareHistorySSR(FEngine const& engine, TextureHandle ssr,
math::mat4f const& historyProjection,
math::mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) noexcept;
void prepareHistorySSR(FEngine const& engine, TextureHandle ssr) noexcept;
// update local data into GPU UBO
void commit(FEngine& engine) noexcept;
@@ -64,7 +56,6 @@ public:
void bind(backend::DriverApi& driver) noexcept;
private:
TypedUniformBuffer<PerViewUib>* mUniforms = nullptr;
DescriptorSet mDescriptorSet;
backend::BufferObjectHandle mShadowUbh;
};
+7 -7
View File
@@ -63,14 +63,14 @@ void StructureDescriptorSet::terminate(DriverApi& driver) {
mUniforms.terminate(driver);
}
void StructureDescriptorSet::commit(DriverApi& driver) noexcept {
assert_invariant(mDescriptorSetLayout);
driver.updateBufferObject(mUniforms.getUboHandle(),
mUniforms.toBufferDescriptor(driver), 0);
mDescriptorSet.commit(*mDescriptorSetLayout, driver);
}
void StructureDescriptorSet::bind(DriverApi& driver) const noexcept {
assert_invariant(mDescriptorSetLayout);
if (mUniforms.isDirty()) {
mUniforms.clean();
driver.updateBufferObject(mUniforms.getUboHandle(),
mUniforms.toBufferDescriptor(driver), 0);
mDescriptorSet.commit(*mDescriptorSetLayout, driver);
}
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_VIEW);
}
+2 -3
View File
@@ -45,8 +45,7 @@ public:
void terminate(backend::DriverApi& driver);
void commit(backend::DriverApi& driver) noexcept;
// this commits the UBO if needed and binds the descriptor set
void bind(backend::DriverApi& driver) const noexcept;
@@ -65,7 +64,7 @@ public:
private:
DescriptorSetLayout const* mDescriptorSetLayout = nullptr;
DescriptorSet mDescriptorSet;
mutable DescriptorSet mDescriptorSet;
TypedUniformBuffer<PerViewUib> mUniforms;
};
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.63.0"
spec.version = "1.62.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.63.0/filament-v1.63.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.62.2/filament-v1.62.2-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+68
View File
@@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.19)
project(matp)
set(TARGET matp)
set(PUBLIC_HDR_DIR include)
# ==================================================================================================
# Sources and headers
# ==================================================================================================
set(PUBLIC_HDRS
include/filament-matp/Config.h
include/filament-matp/MaterialParser.h
)
set(SRCS
src/DirIncluder.h
src/JsonishLexeme.h
src/JsonishLexer.h
src/JsonishParser.h
src/Lexeme.h
src/Lexer.h
src/MaterialLexeme.h
src/MaterialLexer.h
src/ParametersProcessor.h
src/DirIncluder.cpp
src/JsonishLexer.cpp
src/JsonishParser.cpp
src/MaterialLexer.cpp
src/MaterialParser.cpp
src/ParametersProcessor.cpp
)
# ==================================================================================================
# Include and target definitions
# ==================================================================================================
include_directories(${PUBLIC_HDR_DIR})
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
target_include_directories(${TARGET} PRIVATE ${filamat_SOURCE_DIR}/src)
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
target_link_libraries(${TARGET} getopt filamat filabridge utils)
# ==================================================================================================
# Installation
# ==================================================================================================
install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR})
install(DIRECTORY ${PUBLIC_HDR_DIR}/ DESTINATION include)
# ==================================================================================================
# Tests
# ==================================================================================================
project(test_matp)
set(TARGET test_matp)
set(SRCS
tests/test_matp.cpp
tests/test_includer.cpp
tests/TestMaterialParser.h
tests/test_compute_material.cpp
)
add_executable(${TARGET} ${SRCS})
target_link_libraries(${TARGET} matp gtest)
set_target_properties(test_matp PROPERTIES FOLDER Tests)
@@ -28,7 +28,7 @@
#include <utils/compiler.h>
namespace matc {
namespace matp {
class Config {
public:
@@ -167,6 +167,6 @@ protected:
bool mIncludeEssl1 = true;
};
}
} // namespace matp
#endif //TNT_CONFIG_H
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MATERIALPARSER_H
#define TNT_MATERIALPARSER_H
#include <string>
#include <unordered_map>
#include "Config.h"
namespace filamat {
class MaterialBuilder;
}
class TestMaterialParser;
namespace matp {
class JsonishValue;
class MaterialLexeme;
class MaterialParser {
public:
MaterialParser();
// Parses a string material so that it can be used in MaterialBuilder.
// Call MaterialBuilder::init before passing in the builder; call MaterialBuilder::build to
// create filamat::Package after.
bool parse(
filamat::MaterialBuilder& builder,
const Config& config,
Config::Input* input, ssize_t& size, std::unique_ptr<const char[]>& buffer);
// Replaces macro keywords with user specified ones. Must be called before parse.
bool processTemplateSubstitutions(
const Config& config, ssize_t& size, std::unique_ptr<const char[]>& buffer);
private:
friend class ::TestMaterialParser;
bool parseMaterial(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept;
bool processMaterial(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processVertexShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processFragmentShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processComputeShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool ignoreLexeme(const MaterialLexeme&, filamat::MaterialBuilder& builder) const noexcept;
bool parseMaterialAsJSON(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept;
bool processMaterialJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processVertexShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processFragmentShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processComputeShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool ignoreLexemeJSON(const JsonishValue*, filamat::MaterialBuilder& builder) const noexcept;
bool isValidJsonStart(const char* buffer, size_t size) const noexcept;
bool processMaterialParameters(filamat::MaterialBuilder& builder, const Config& config) const;
// Member function pointer type, this is used to implement a Command design
// pattern.
using MaterialConfigProcessor = bool (MaterialParser::*)
(const MaterialLexeme&, filamat::MaterialBuilder& builder) const;
// Map used to store Command pattern function pointers.
std::unordered_map<std::string, MaterialConfigProcessor> mConfigProcessor;
// The same, but for pure JSON syntax
using MaterialConfigProcessorJSON = bool (MaterialParser::*)
(const JsonishValue*, filamat::MaterialBuilder& builder) const;
std::unordered_map<std::string, MaterialConfigProcessorJSON> mConfigProcessorJSON;
};
} // namespace matp
#endif // TNT_MATERIALPARSER_H
@@ -20,7 +20,7 @@
#include <fstream>
namespace matc {
namespace matp {
bool DirIncluder::operator()(const utils::CString& includedBy, filamat::IncludeResult& result) {
auto getHeaderPath = [&result, &includedBy, this]() {
@@ -66,5 +66,5 @@ bool DirIncluder::operator()(const utils::CString& includedBy, filamat::IncludeR
return true;
}
} // namespace matc
} // namespace matp
@@ -21,7 +21,7 @@
#include <utils/Path.h>
namespace matc {
namespace matp {
// Functor callback handler used to resolve includes relative to a root include directory.
class DirIncluder {
@@ -38,6 +38,6 @@ private:
};
} // namespace matc
} // namespace matp
#endif
@@ -19,7 +19,7 @@
#include "Lexeme.h"
namespace matc {
namespace matp {
enum JsonType {
BOOLEAN,
@@ -68,6 +68,6 @@ public:
};
} // namespace matc
} // namespace matp
#endif //TNT_JSONLEXEME_H
@@ -18,7 +18,7 @@
#include <string.h>
namespace matc {
namespace matp {
JsonType JsonishLexer::readIdentifier() noexcept {
const char* lexemeStart = mCursor;
@@ -148,4 +148,4 @@ bool JsonishLexer::readLexeme() noexcept {
return true;
}
} // namespace matc
} // namespace matp
@@ -20,7 +20,7 @@
#include "Lexer.h"
#include "JsonishLexeme.h"
namespace matc {
namespace matp {
// Breaks down a stream of chars into JsonLexemes.
class JsonishLexer final: public Lexer<JsonLexeme> {
@@ -33,6 +33,6 @@ private:
JsonType readPunctuation() noexcept;
};
} // namespace matc
} // namespace matp
#endif //TNT_JSONLEXER_H
@@ -24,7 +24,7 @@
#include <utils/string.h>
namespace matc {
namespace matp {
static std::string resolveEscapes(const std::string& s) {
std::string out;
@@ -318,4 +318,4 @@ void JsonishParser::reportError(const char* message) noexcept {
mErrorReported = true;
}
} // namespace matc
} // namespace matp
@@ -23,7 +23,7 @@
#include <unordered_map>
#include <vector>
namespace matc {
namespace matp {
// Follows the LL(1) grammar described at http://json.org:
// with the following productions.
@@ -279,6 +279,6 @@ private:
bool mErrorReported = false;
};
} // namespace matc
} // namespace matp
#endif //TNT_JSONPARSER_H
@@ -21,7 +21,7 @@
#include <string>
#include <vector>
namespace matc {
namespace matp {
// A lexeme MUST be templated on the family, e.g: Material config, json or glsl.
template <class T>
@@ -65,6 +65,6 @@ protected:
T mType;
};
} // namespace matc
} // namespace matp
#endif // TNT_LEXEME_H
@@ -21,7 +21,7 @@
#include "Lexeme.h"
namespace matc {
namespace matp {
// Templated on the class of Lexemes to parse: Material lexeme, JSON Lexemes, GLSL lexemes.
template <class T>
@@ -121,6 +121,7 @@ protected:
size_t mLineCounter = 1;
size_t mLinePosition = 0;
};
}
} // namespace matp
#endif //TNT_LEXER_H
@@ -19,7 +19,7 @@
#include "Lexeme.h"
namespace matc {
namespace matp {
enum MaterialType {
BLOCK,
@@ -38,6 +38,6 @@ public:
}
};
} // namespace matc
} // namespace matp
#endif //TNT_MATERIALLEXEME_H
@@ -16,7 +16,7 @@
#include "MaterialLexer.h"
namespace matc {
namespace matp {
bool MaterialLexer::readBlock() noexcept {
size_t braceCount = 0;
@@ -97,4 +97,4 @@ bool MaterialLexer::readLexeme() noexcept {
return nextMaterialType != UNKNOWN;
}
} // namespace matc
} // namespace matp
@@ -20,7 +20,7 @@
#include "Lexer.h"
#include "MaterialLexeme.h"
namespace matc {
namespace matp {
class MaterialLexer final: public Lexer<MaterialLexeme> {
public:
@@ -35,6 +35,6 @@ private:
void readUnknown() noexcept;
};
}
} // namespace matp
#endif //TNT_MATERIALLEXER_H
+521
View File
@@ -0,0 +1,521 @@
/*
* Copyright (C) 2025 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 <filament-matp/MaterialParser.h>
#include <memory>
#include <iostream>
#include <utility>
#include <filamat/MaterialBuilder.h>
#include <filamat/Enums.h>
#include <utils/Log.h>
#include <utils/JobSystem.h>
#include "DirIncluder.h"
#include "JsonishLexer.h"
#include "JsonishParser.h"
#include "MaterialLexeme.h"
#include "MaterialLexer.h"
#include "ParametersProcessor.h"
#include <filament-matp/Config.h>
using namespace utils;
using namespace filamat;
using namespace std::placeholders;
namespace matp {
static constexpr const char* CONFIG_KEY_MATERIAL= "material";
static constexpr const char* CONFIG_KEY_VERTEX_SHADER = "vertex";
static constexpr const char* CONFIG_KEY_FRAGMENT_SHADER = "fragment";
static constexpr const char* CONFIG_KEY_COMPUTE_SHADER = "compute";
static constexpr const char* CONFIG_KEY_TOOL = "tool";
MaterialParser::MaterialParser() {
mConfigProcessor[CONFIG_KEY_MATERIAL] = &MaterialParser::processMaterial;
mConfigProcessor[CONFIG_KEY_VERTEX_SHADER] = &MaterialParser::processVertexShader;
mConfigProcessor[CONFIG_KEY_FRAGMENT_SHADER] = &MaterialParser::processFragmentShader;
mConfigProcessor[CONFIG_KEY_COMPUTE_SHADER] = &MaterialParser::processComputeShader;
mConfigProcessor[CONFIG_KEY_TOOL] = &MaterialParser::ignoreLexeme;
mConfigProcessorJSON[CONFIG_KEY_MATERIAL] = &MaterialParser::processMaterialJSON;
mConfigProcessorJSON[CONFIG_KEY_VERTEX_SHADER] = &MaterialParser::processVertexShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_FRAGMENT_SHADER] = &MaterialParser::processFragmentShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_COMPUTE_SHADER] = &MaterialParser::processComputeShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_TOOL] = &MaterialParser::ignoreLexemeJSON;
}
bool MaterialParser::processMaterial(const MaterialLexeme& jsonLexeme,
MaterialBuilder& builder) const noexcept {
JsonishLexer jlexer;
jlexer.lex(jsonLexeme.getStart(), jsonLexeme.getSize(), jsonLexeme.getLine());
JsonishParser parser(jlexer.getLexemes());
std::unique_ptr<JsonishObject> const json = parser.parse();
if (json == nullptr) {
std::cerr << "JsonishParser error (see above)." << std::endl;
return false;
}
ParametersProcessor parametersProcessor;
bool const ok = parametersProcessor.process(builder, *json);
if (!ok) {
std::cerr << "Error while processing material." << std::endl;
return false;
}
return true;
}
bool MaterialParser::processVertexShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
MaterialLexeme const trimmedLexeme = lexeme.trimBlockMarkers();
std::string const shaderStr = trimmedLexeme.getStringValue();
// getLine() returns a line number, with 1 being the first line, but .material wants a 0-based
// line number offset, where 0 is the first line.
builder.materialVertex(shaderStr.c_str(), trimmedLexeme.getLine() - 1);
return true;
}
bool MaterialParser::processFragmentShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
MaterialLexeme const trimmedLexeme = lexeme.trimBlockMarkers();
std::string const shaderStr = trimmedLexeme.getStringValue();
// getLine() returns a line number, with 1 being the first line, but .material wants a 0-based
// line number offset, where 0 is the first line.
builder.material(shaderStr.c_str(), trimmedLexeme.getLine() - 1);
return true;
}
bool MaterialParser::processComputeShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
return MaterialParser::processFragmentShader(lexeme, builder);
}
bool MaterialParser::ignoreLexeme(const MaterialLexeme&, MaterialBuilder&) const noexcept {
return true;
}
static bool reflectParameters(const MaterialBuilder& builder) {
uint8_t const count = builder.getParameterCount();
const MaterialBuilder::ParameterList& parameters = builder.getParameters();
std::cout << "{" << std::endl;
std::cout << " \"parameters\": [" << std::endl;
for (uint8_t i = 0; i < count; i++) {
const MaterialBuilder::Parameter& parameter = parameters[i];
std::cout << " {" << std::endl;
std::cout << R"( "name": ")" << parameter.name.c_str() << "\"," << std::endl;
if (parameter.isSampler()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.samplerType) << "\"," << std::endl;
std::cout << R"( "format": ")" <<
Enums::toString(parameter.format) << "\"," << std::endl;
std::cout << R"( "precision": ")" <<
Enums::toString(parameter.precision) << "\"," << std::endl;
std::cout << R"( "multisample": ")" <<
(parameter.multisample ? "true" : "false")<< "\"" << std::endl;
} else if (parameter.isUniform()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.uniformType) << "\"," << std::endl;
std::cout << R"( "size": ")" << parameter.size << "\"" << std::endl;
} else if (parameter.isSubpass()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.subpassType) << "\"," << std::endl;
std::cout << R"( "format": ")" <<
Enums::toString(parameter.format) << "\"," << std::endl;
std::cout << R"( "precision": ")" <<
Enums::toString(parameter.precision) << "\"" << std::endl;
}
std::cout << " }";
if (i < count - 1) std::cout << ",";
std::cout << std::endl;
}
std::cout << " ]" << std::endl;
std::cout << "}" << std::endl;
return true;
}
bool MaterialParser::processMaterialJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'material' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::OBJECT) {
std::cerr << "'material' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be OBJECT."
<< std::endl;
return false;
}
ParametersProcessor parametersProcessor;
bool const ok = parametersProcessor.process(builder, *value->toJsonObject());
if (!ok) {
std::cerr << "Error while processing material." << std::endl;
return false;
}
return true;
}
bool MaterialParser::processVertexShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'vertex' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'vertex' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.materialVertex(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialParser::processFragmentShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'fragment' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'fragment' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.material(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialParser::processComputeShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'compute' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'compute' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.material(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialParser::ignoreLexemeJSON(const JsonishValue*,
filamat::MaterialBuilder&) const noexcept {
return true;
}
bool MaterialParser::isValidJsonStart(const char* buffer, size_t size) const noexcept {
// Skip all whitespace characters.
const char* end = buffer + size;
while (buffer != end && isspace(buffer[0])) {
buffer++;
}
// A buffer made only of whitespace is not a valid JSON start.
if (buffer == end) {
return false;
}
const char c = buffer[0];
// Take care of block, array, and string
if (c == '{' || c == '[' || c == '"') {
return true;
}
// boolean true
if (c == 't' && (end - buffer) > 3 && strncmp(buffer, "true", 4) != 0) {
return true;
}
// boolean false
if (c == 'f' && (end - buffer) > 4 && strncmp(buffer, "false", 5) != 0) {
return true;
}
// null literal
return c == 'n' && (end - buffer) > 3 && strncmp(buffer, "null", 5) != 0;
}
bool MaterialParser::parseMaterialAsJSON(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept {
JsonishLexer jlexer;
jlexer.lex(buffer, size, 1);
JsonishParser parser(jlexer.getLexemes());
std::unique_ptr<JsonishObject> json = parser.parse();
if (json == nullptr) {
std::cerr << "Could not parse JSON material file" << std::endl;
return false;
}
for (auto& entry : json->getEntries()) {
const std::string& key = entry.first;
if (mConfigProcessorJSON.find(key) == mConfigProcessorJSON.end()) {
std::cerr << "Unknown identifier '" << key << "'" << std::endl;
return false;
}
// Retrieve function member pointer
MaterialConfigProcessorJSON const p = mConfigProcessorJSON.at(key);
// Call it.
bool const ok = (*this.*p)(entry.second, builder);
if (!ok) {
std::cerr << "Error while processing block with key:'" << key << "'" << std::endl;
return false;
}
}
return true;
}
bool MaterialParser::parseMaterial(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept {
MaterialLexer materialLexer;
materialLexer.lex(buffer, size);
auto lexemes = materialLexer.getLexemes();
// Make sure the lexer did not stumble upon unknown character. This could mean we received
// a binary file.
for (auto lexeme : lexemes) {
if (lexeme.getType() == MaterialType::UNKNOWN) {
std::cerr << "Unexpected character at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
}
// Make a first quick pass just to make sure the format was respected (the material format is
// a series of IDENTIFIER, BLOCK pairs).
if (lexemes.size() < 2) {
std::cerr << "Input MUST be an alternation of [identifier, block] pairs." << std::endl;
return false;
}
for (size_t i = 0; i < lexemes.size(); i += 2) {
auto lexeme = lexemes.at(i);
if (lexeme.getType() != MaterialType::IDENTIFIER) {
std::cerr << "An identifier was expected at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
if (i == lexemes.size() - 1) {
std::cerr << "Identifier at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition()
<< " must be followed by a block." << std::endl;
return false;
}
auto nextLexeme = lexemes.at(i + 1);
if (nextLexeme.getType() != MaterialType::BLOCK) {
std::cerr << "A block was expected at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
}
std::string identifier;
for (auto lexeme : lexemes) {
if (lexeme.getType() == MaterialType::IDENTIFIER) {
identifier = lexeme.getStringValue();
if (mConfigProcessor.find(identifier) == mConfigProcessor.end()) {
std::cerr << "Unknown identifier '"
<< lexeme.getStringValue()
<< "' at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
} else if (lexeme.getType() == MaterialType::BLOCK) {
MaterialConfigProcessor const processor = mConfigProcessor.at(identifier);
if (!(*this.*processor)(lexeme, builder)) {
std::cerr << "Error while processing block with key:'" << identifier << "'"
<< std::endl;
return false;
}
}
}
return true;
}
bool MaterialParser::processMaterialParameters(filamat::MaterialBuilder& builder,
const Config& config) const {
ParametersProcessor parametersProcessor;
bool ok = true;
for (const auto& param : config.getMaterialParameters()) {
ok &= parametersProcessor.process(builder, param.first, param.second);
}
return ok;
}
bool MaterialParser::processTemplateSubstitutions(
const Config& config, ssize_t& size, std::unique_ptr<const char[]>& buffer) {
const auto& templateMap = config.getTemplateMap();
ssize_t modifiedSize = size;
bool modified = false;
if (!templateMap.empty()) {
for (ssize_t cursor = 0, n = size - 1; cursor < n; ++cursor) {
if (UTILS_LIKELY(buffer[cursor] != '$' || buffer[cursor + 1] != '{')) {
continue;
}
cursor += 2;
ssize_t endCursor = cursor;
while (true) {
if (endCursor == size) {
std::cerr << "Unexpected end of file" << std::endl;
return false;
}
if (buffer[endCursor] == '}') {
break;
}
++endCursor;
}
// At this point, cursor points to the F in ${FOO} and endCursor points to the }.
std::string_view const macro(&buffer[cursor], endCursor - cursor);
if (auto iter = templateMap.find(macro); iter != templateMap.end()) {
modifiedSize -= macro.size() + 3;
modifiedSize += iter->second.size();
} else {
std::cerr << "Undefined template macro:" << macro << std::endl;
return false;
}
modified = true;
}
}
// Second pass of template substitution allocates a new buffer and performs the actual
// substitutions. Since the first pass did not return early, we can safely assume no errors.
if (modified) {
auto modifiedBuffer = std::make_unique<char[]>(modifiedSize);
for (ssize_t cursor = 0, dstCursor = 0; cursor < size; ++cursor) {
const ssize_t next = cursor + 1;
if (UTILS_LIKELY(buffer[cursor] != '$' || next >= size || buffer[next] != '{')) {
modifiedBuffer[dstCursor++] = buffer[cursor];
continue;
}
cursor += 2;
ssize_t endCursor = cursor;
while (buffer[endCursor] != '}') ++endCursor;
// At this point, cursor points to the F in ${FOO} and endCursor points to the }.
std::string_view const macro(&buffer[cursor], endCursor - cursor);
const std::string& val = templateMap.find(macro)->second;
for (size_t i = 0, n = val.size(); i < n; ++i, ++dstCursor) {
modifiedBuffer[dstCursor] = val[i];
}
cursor = endCursor;
}
buffer = std::move(modifiedBuffer);
size = modifiedSize;
}
return true;
}
bool MaterialParser::parse(filamat::MaterialBuilder& builder,
const Config& config,
Config::Input* input, ssize_t& size, std::unique_ptr<const char[]>& buffer) {
utils::Path const materialFilePath = utils::Path(input->getName()).getAbsolutePath();
assert(materialFilePath.isFile());
if (builder.getFeatureLevel() > config.getFeatureLevel()) {
std::cerr << "Material feature level (" << +builder.getFeatureLevel()
<< ") is higher than maximum allowed (" << +config.getFeatureLevel() << ")" << std::endl;
return false;
}
// Before attempting an expensive lex, let's find out if we were sent pure JSON.
bool parsed;
if (isValidJsonStart(buffer.get(), size_t(size))) {
parsed = parseMaterialAsJSON(buffer.get(), size_t(size), builder);
} else {
parsed = parseMaterial(buffer.get(), size_t(size), builder);
}
if (!parsed) {
return false;
}
switch (config.getReflectionTarget()) {
case Config::Metadata::NONE:
break;
case Config::Metadata::PARAMETERS:
return reflectParameters(builder);
}
// Set the root include directory to the directory containing the material file.
DirIncluder includer;
includer.setIncludeDirectory(materialFilePath.getParent());
builder
.noSamplerValidation(config.noSamplerValidation())
.includeEssl1(config.includeEssl1())
.includeCallback(includer)
.fileName(materialFilePath.getName().c_str())
.platform(config.getPlatform())
.targetApi(config.getTargetApi())
.optimization(config.getOptimizationLevel())
.printShaders(config.printShaders())
.saveRawVariants(config.saveRawVariants())
.generateDebugInfo(config.isDebug())
.variantFilter(config.getVariantFilter() | builder.getVariantFilter());
for (const auto& define : config.getDefines()) {
builder.shaderDefine(define.first.c_str(), define.second.c_str());
}
if (!processMaterialParameters(builder, config)) {
std::cerr << "Error while processing material parameters." << std::endl;
return false;
}
return true;
}
} // namespace matp
@@ -34,7 +34,7 @@
using namespace filamat;
using namespace utils;
namespace matc {
namespace matp {
template <class T>
static bool logEnumIssue(const std::string& key, const JsonishString& value,
@@ -1467,4 +1467,4 @@ bool ParametersProcessor::process(filamat::MaterialBuilder& builder, const std::
return true;
}
} // namespace matc
} // namespace matp
@@ -26,7 +26,7 @@
#include <filamat/MaterialBuilder.h>
namespace matc {
namespace matp {
class ParametersProcessor {
@@ -48,6 +48,6 @@ private:
std::unordered_map<std::string, ParameterInfo> mParameters;
};
} // namespace matc
} // namespace matp
#endif //TNT_PARAMETERSPROCESSOR_H
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_TESTMATERIALPARSER_H
#define TNT_TESTMATERIALPARSER_H
#include "filamat/MaterialBuilder.h"
#include <filament-matp/MaterialParser.h>
class TestMaterialParser {
public:
explicit TestMaterialParser(const matp::MaterialParser& materialParser) :
mMaterialParser(materialParser) {}
bool parseMaterial(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
noexcept{
return mMaterialParser.parseMaterial(buffer, size, builder);
}
bool parseMaterialAsJSON(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
noexcept{
return mMaterialParser.parseMaterialAsJSON(buffer, size, builder);
}
private:
const matp::MaterialParser mMaterialParser;
};
#endif // TNT_TESTMATERIALPARSER_H
@@ -16,11 +16,9 @@
#include <gtest/gtest.h>
#include <matc/MaterialCompiler.h>
#include "TestMaterialCompiler.h"
#include <utils/JobSystem.h>
#include "TestMaterialParser.h"
#include "utils/JobSystem.h"
#include <filament-matp/MaterialParser.h>
static std::string_view jsonMaterialSourceSimple(R"(
material {
@@ -36,14 +34,14 @@ compute {
}
)");
TEST(TestComputeMaterial, JsonMaterialCompilerSimple) {
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
TEST(TestParseAndComputeMaterial, JsonMaterialCompilerSimple) {
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder::init();
filamat::MaterialBuilder builder;
bool result = compiler.parseMaterial(jsonMaterialSourceSimple.data(), jsonMaterialSourceSimple.size(), builder);
bool result = testParser.parseMaterial(jsonMaterialSourceSimple.data(), jsonMaterialSourceSimple.size(), builder);
EXPECT_TRUE(result);
@@ -16,7 +16,7 @@
#include <gtest/gtest.h>
#include <matc/DirIncluder.h>
#include "../src/DirIncluder.h"
using namespace utils;
using namespace filamat;
@@ -26,7 +26,7 @@ const utils::Path root = utils::Path(__FILE__).getParent();
// TODO: these tests are disabled as they fail on CI, which needs investigation.
TEST(DirIncluder, DISABLED_IncludeNonexistent) {
matc::DirIncluder includer;
matp::DirIncluder includer;
{
IncludeResult i {
.includeName = CString("nonexistent.h")
@@ -45,7 +45,7 @@ TEST(DirIncluder, DISABLED_IncludeNonexistent) {
}
TEST(DirIncluder, DISABLED_IncludeFile) {
matc::DirIncluder includer;
matp::DirIncluder includer;
includer.setIncludeDirectory(root);
IncludeResult result {
@@ -63,7 +63,7 @@ TEST(DirIncluder, DISABLED_IncludeFile) {
}
TEST(DirIncluder, DISABLED_IncludeFileFromIncluder) {
matc::DirIncluder includer;
matp::DirIncluder includer;
includer.setIncludeDirectory(root);
utils::CString includerFile((root + "Dir/Baz.h").c_str());
@@ -1,28 +1,27 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
* Copyright (C) 2025 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 <gtest/gtest.h>
#include "MockConfig.h"
#include "TestMaterialCompiler.h"
#include "TestMaterialParser.h"
#include <matc/MaterialCompiler.h>
#include <matc/MaterialLexer.h>
#include <matc/JsonishLexer.h>
#include <matc/JsonishParser.h>
#include <filament-matp/MaterialParser.h>
#include "../src/JsonishLexer.h"
#include "../src/JsonishParser.h"
#include "../src/MaterialLexer.h"
class MaterialLexer: public ::testing::Test {
protected:
@@ -80,7 +79,7 @@ static std::string materialSourceWithCommentedBraces(R"(
)");
TEST_F(MaterialLexer, NormalMaterialLexing) {
matc::MaterialLexer materialLexer;
matp::MaterialLexer materialLexer;
materialLexer.lex(materialSource.c_str(), materialSource.size(), 1);
auto lexemes = materialLexer.getLexemes();
// A material grammar is made of identifiers and blocks. The source provided has one identifier,
@@ -88,27 +87,27 @@ TEST_F(MaterialLexer, NormalMaterialLexing) {
EXPECT_EQ(lexemes.size(), 4);
}
TEST_F(MaterialLexer, MaterialCompiler) {
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(materialSource.c_str(), materialSource.size(), unused);
EXPECT_EQ(result, true);
}
TEST_F(MaterialLexer, MaterialCompilerWithToolSection) {
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
TEST_F(MaterialLexer, MaterialParser) {
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(materialSourceWithTool.c_str(), materialSourceWithTool.size(), unused);
bool result = testParser.parseMaterial(materialSource.c_str(), materialSource.size(), unused);
EXPECT_EQ(result, true);
}
TEST_F(MaterialLexer, MaterialCompilerWithCommentedBraces) {
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
TEST_F(MaterialLexer, MaterialParserWithToolSection) {
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(materialSourceWithCommentedBraces.c_str(), materialSourceWithCommentedBraces.size(), unused);
bool result = testParser.parseMaterial(materialSourceWithTool.c_str(), materialSourceWithTool.size(), unused);
EXPECT_EQ(result, true);
}
TEST_F(MaterialLexer, MaterialParserWithCommentedBraces) {
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = testParser.parseMaterial(materialSourceWithCommentedBraces.c_str(), materialSourceWithCommentedBraces.size(), unused);
EXPECT_EQ(result, true);
}
@@ -116,46 +115,46 @@ TEST_F(MaterialLexer, MaterialParserErrorOnlyIdentifier) {
std::string sourceMissingIdentifier(R"(
singleIdentifier
)");
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(
bool result = testParser.parseMaterial(
sourceMissingIdentifier.c_str(), sourceMissingIdentifier.size(), unused);
EXPECT_EQ(result, false);
}
TEST_F(MaterialLexer, MaterialParserErrorMissingBlock) {
std::string sourceMissingBlock(R"(
std::string sourceMissingBlock(R"(
identifier1 {}
identifier1
)");
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(
bool result = testParser.parseMaterial(
sourceMissingBlock.c_str(), sourceMissingBlock.size(), unused);
EXPECT_EQ(result, false);
}
TEST_F(MaterialLexer, MaterialParserErrorTwoBlock) {
std::string sourceTwoBlock(R"(
std::string sourceTwoBlock(R"(
identifier1 {} {}
)");
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(sourceTwoBlock.c_str(), sourceTwoBlock.size(), unused);
EXPECT_EQ(result, false);
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = testParser.parseMaterial(sourceTwoBlock.c_str(), sourceTwoBlock.size(), unused);
EXPECT_EQ(result, false);
}
TEST_F(MaterialLexer, MaterialParserSyntaxError) {
std::string sourceSyntaxError(R"(
identifier1 {} # identified2 {}
)");
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterial(sourceSyntaxError.c_str(), sourceSyntaxError.size(), unused);
bool result = testParser.parseMaterial(sourceSyntaxError.c_str(), sourceSyntaxError.size(), unused);
EXPECT_EQ(result, false);
}
@@ -245,14 +244,14 @@ material: {
})");
TEST_F(MaterialLexer, JsonMaterialLexingAndParsing) {
matc::JsonishLexer jsonishLexer;
matp::JsonishLexer jsonishLexer;
jsonishLexer.lex(jsonMaterialSource.c_str(), jsonMaterialSource.size(), 1);
auto lexemes = jsonishLexer.getLexemes();
EXPECT_TRUE(!lexemes.empty());
matc::JsonishParser jsonishParser(lexemes);
std::unique_ptr<matc::JsonishObject> root = jsonishParser.parse();
matp::JsonishParser jsonishParser(lexemes);
std::unique_ptr<matp::JsonishObject> root = jsonishParser.parse();
auto entries = root->getEntries();
EXPECT_TRUE(entries.size() == 2);
@@ -261,29 +260,29 @@ TEST_F(MaterialLexer, JsonMaterialLexingAndParsing) {
}
TEST_F(MaterialLexer, JsonMaterialLexingAndParsingNoQuotes) {
matc::JsonishLexer jsonishLexer;
matp::JsonishLexer jsonishLexer;
jsonishLexer.lex(jsonMaterialSourceNoQuotes.c_str(), jsonMaterialSourceNoQuotes.size(), 1);
auto lexemes = jsonishLexer.getLexemes();
EXPECT_TRUE(!lexemes.empty());
matc::JsonishParser jsonishParser(lexemes);
std::unique_ptr<matc::JsonishObject> root = jsonishParser.parse();
matp::JsonishParser jsonishParser(lexemes);
std::unique_ptr<matp::JsonishObject> root = jsonishParser.parse();
auto entries = root->getEntries();
EXPECT_TRUE(entries.size() == 1);
EXPECT_TRUE(entries.find("material") != entries.end());
}
TEST_F(MaterialLexer, JsonMaterialCompiler) {
matc::MaterialCompiler rawCompiler;
TestMaterialCompiler compiler(rawCompiler);
filamat::MaterialBuilder unused;
bool result = compiler.parseMaterialAsJSON(jsonMaterialSource.c_str(), jsonMaterialSource.size(), unused);
EXPECT_EQ(result, true);
TEST_F(MaterialLexer, JsonMaterialParser) {
matp::MaterialParser parser;
TestMaterialParser testParser(parser);
filamat::MaterialBuilder unused;
bool result = testParser.parseMaterialAsJSON(jsonMaterialSource.c_str(), jsonMaterialSource.size(), unused);
EXPECT_EQ(result, true);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
}
-35
View File
@@ -544,27 +544,6 @@ public:
}
};
template<typename PanicType>
class FlagGuardedStream : public PanicStream {
public:
FlagGuardedStream(bool enable, char const* function, char const* file, int line,
char const* condition)
: PanicStream(function, file, line, condition),
mEnablePanic(enable) {}
~FlagGuardedStream() noexcept(false) {
if (mEnablePanic) {
PanicType::panic(mFunction, mFile, mLine, mLiteral, mStream.c_str());
} else {
logWarning();
}
}
private:
void logWarning() noexcept;
bool mEnablePanic;
};
} // namespace details
// -----------------------------------------------------------------------------------------------
@@ -603,20 +582,6 @@ private:
FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, ArithmeticPanic)
#endif
#define FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, TYPE) \
::utils::details::FlagGuardedStream<::utils::TYPE>(flag, PANIC_FUNCTION, PANIC_FILE(__FILE__), \
__LINE__, #condition)
#ifndef FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION
#define FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(condition, flag) \
FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PreconditionPanic)
#endif
#ifndef FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION
#define FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION(condition, flag) \
FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PostconditionPanic)
#endif
#define PANIC_PRECONDITION_IMPL(cond, format, ...) \
::utils::PreconditionPanic::panic(PANIC_FUNCTION, \
PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__)
-12
View File
@@ -21,7 +21,6 @@
#include <utils/CallStack.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/Logger.h>
#include <utils/ostream.h>
#include <stdarg.h>
@@ -353,17 +352,6 @@ PanicStream& PanicStream::operator<<(std::string_view const& value) noexcept {
return *this;
}
template<typename PanicType>
void FlagGuardedStream<PanicType>::logWarning() noexcept {
LOG(WARNING) << "Failed [" << mLiteral << "]" << "\n file=" << mFile
<< "\n function=" << mFunction << "\n line=" << mLine << "\n info=\""
<< mStream.c_str() << "\"";
}
template void FlagGuardedStream<PreconditionPanic>::logWarning() noexcept;
template void FlagGuardedStream<PostconditionPanic>::logWarning() noexcept;
} // namespace details
// -----------------------------------------------------------------------------------------------
+28
View File
@@ -0,0 +1,28 @@
# Copyright (C) 2024 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.
#!/bin/bash
source test/renderdiff/src/preamble.sh
if [[ "$GITHUB_WORKFLOW" ]]; then
echo "This is meant to run locally (not part of the CI)"
exit 1
else
GOLDEN_BRANCH=$(git log -1 | python3 test/renderdiff/src/commit_msg.py)
fi
./build.sh -f "$@" -p desktop debug gltf_viewer
# -f -W -q arm64-v8a
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
# Directory containing the GLTF files
GLTF_DIR="path/to/your/gltf/files"
GLTF_VIEWER_PATH="out/cmake-debug/samples/gltf_viewer"
GLTF_OUT_DIR="`dirname $0`/generated"
echo "The dirname is $GLTF_OUT_DIR"
# echo `dirname $0`
rm -rf $GLTF_OUT_DIR
mkdir -p $GLTF_OUT_DIR
# exit 0
# Base directory for the glTF-Sample-Assets repository
GLTF_SAMPLES_DIR="../glTF-Sample-Assets/Models"
# Loop through each subdirectory in the base directory
find "$GLTF_SAMPLES_DIR" -type f -name "*.gltf" | while read -r gltf_file; do
# Get the base name of the file (e.g., "Box")
base_name=$(basename "$gltf_file" .gltf)
echo "Processing $gltf_file..."
# Run the gltf_viewer command
# You may need to adjust this path based on your project structure
./out/cmake-debug/samples/gltf_viewer --api vulkan --batch=test/gltf-compare/test.json --headless "$gltf_file"
# Rename the generated .js and .tif files
# The generated files are assumed to be in the same directory where the script is run
mv "Base0.tif" "${base_name}.tif"
# Move the newly renamed files to the Output directory
mv "${base_name}.tif" "$GLTF_OUT_DIR/"
echo "Generated files moved to $GLTF_OUT_DIR"
echo "---"
done
echo "Script finished."
+9
View File
@@ -0,0 +1,9 @@
[
{
"name": "Base",
"base": {
"view.postProcessingEnabled": true,
"view.dithering": "NONE"
}
}
]
+2 -36
View File
@@ -12,28 +12,13 @@ set(TARGET matlang)
set(HDRS
src/matc/CommandlineConfig.h
src/matc/Compiler.h
src/matc/Config.h
src/matc/JsonishLexeme.h
src/matc/JsonishLexer.h
src/matc/JsonishParser.h
src/matc/Lexeme.h
src/matc/Lexer.h
src/matc/MaterialCompiler.h
src/matc/MaterialLexeme.h
src/matc/MaterialLexer.h
src/matc/ParametersProcessor.h
src/matc/DirIncluder.h
)
set(SRCS
src/matc/Compiler.cpp
src/matc/CommandlineConfig.cpp
src/matc/JsonishLexer.cpp
src/matc/JsonishParser.cpp
src/matc/MaterialCompiler.cpp
src/matc/MaterialLexer.cpp
src/matc/ParametersProcessor.cpp
src/matc/DirIncluder.cpp
src/matc/CommandlineConfig.cpp
)
# ==================================================================================================
@@ -44,7 +29,7 @@ add_library(${TARGET} STATIC ${SRCS} ${HDRS})
target_include_directories(${TARGET} PUBLIC src)
target_include_directories(${TARGET} PRIVATE ${filamat_SOURCE_DIR}/src)
target_link_libraries(${TARGET} getopt filamat filabridge utils)
target_link_libraries(${TARGET} getopt filamat filabridge utils matp)
set_target_properties(${TARGET} PROPERTIES FOLDER Tools)
# =================================================================================================
@@ -77,22 +62,3 @@ set_target_properties(${TARGET} PROPERTIES FOLDER Tools)
# Installation
# ==================================================================================================
install(TARGETS ${TARGET} RUNTIME DESTINATION bin)
# ==================================================================================================
# Tests
# ==================================================================================================
project(test_matc)
set(TARGET test_matc)
set(SRCS
tests/test_matc.cpp
tests/test_includer.cpp
tests/TestMaterialCompiler.h
tests/test_compute_material.cpp
tests/MockConfig.cpp
tests/MockConfig.h)
add_executable(${TARGET} ${SRCS})
target_link_libraries(${TARGET} matlang gtest)
set_target_properties(test_matc PROPERTIES FOLDER Tests)
+1 -1
View File
@@ -154,7 +154,7 @@ CommandlineConfig::CommandlineConfig(int argc, char** argv) : Config(), mArgc(ar
mIsValid = parse();
}
static void parseDefine(std::string defineString, Config::StringReplacementMap& defines) {
static void parseDefine(std::string defineString, matp::Config::StringReplacementMap& defines) {
const char* const defineArg = defineString.c_str();
const size_t length = defineString.length();
+6 -7
View File
@@ -17,15 +17,14 @@
#ifndef TNT_COMPILERPARAMETERS_H
#define TNT_COMPILERPARAMETERS_H
#include <filament-matp/Config.h>
#include <fstream>
#include <iostream>
#include <string>
#include "Config.h"
namespace matc {
class FilesystemOutput : public Config::Output {
class FilesystemOutput : public matp::Config::Output {
public:
explicit FilesystemOutput(const char* path) : mPath(path) {
}
@@ -55,7 +54,7 @@ private:
std::ofstream mFile;
};
class FilesystemInput : public Config::Input {
class FilesystemInput : public matp::Config::Input {
public:
explicit FilesystemInput(const char* path) : mPath(path) {
}
@@ -97,7 +96,7 @@ private:
ssize_t mFilesize = 0;
};
class CommandlineConfig : public Config {
class CommandlineConfig : public matp::Config {
public:
CommandlineConfig(int argc, char** argv);
@@ -106,11 +105,11 @@ public:
delete mOutput;
};
Output* getOutput() const noexcept override {
matp::Config::Output* getOutput() const noexcept override {
return mOutput;
}
Input* getInput() const noexcept override {
matp::Config::Input* getInput() const noexcept override {
return mInput;
}
+4 -4
View File
@@ -24,8 +24,8 @@ using namespace filamat;
namespace matc {
bool Compiler::writeBlob(const Package &pkg, const Config& config) const noexcept {
Config::Output* output = config.getOutput();
bool Compiler::writeBlob(const Package &pkg, const matp::Config& config) const noexcept {
matp::Config::Output* output = config.getOutput();
if (!output->open()) {
std::cerr << "Unable to create blob file." << std::endl;
return false;
@@ -37,10 +37,10 @@ bool Compiler::writeBlob(const Package &pkg, const Config& config) const noexcep
return true;
}
bool Compiler::writeBlobAsHeader(const Package &pkg, const Config& config) const noexcept {
bool Compiler::writeBlobAsHeader(const Package &pkg, const matp::Config& config) const noexcept {
uint8_t* data = pkg.getData();
Config::Output* output = config.getOutput();
matp::Config::Output* output = config.getOutput();
if (!output->open()) {
std::cerr << "Unable to create header file." << std::endl;
return false;
+7 -6
View File
@@ -18,6 +18,7 @@
#define TNT_COMPILER_H
#include "CommandlineConfig.h"
#include <filament-matp/Config.h>
#include <filamat/Package.h>
@@ -27,7 +28,7 @@ class Compiler {
public:
virtual ~Compiler() = default;
bool compile(const Config& config) {
bool compile(const matp::Config& config) {
if (!checkParameters(config)) {
return false;
}
@@ -35,22 +36,22 @@ public:
}
protected:
bool writePackage(const filamat::Package& package, const Config& config) {
bool writePackage(const filamat::Package& package, const matp::Config& config) {
if (config.getOutputFormat() == CommandlineConfig::OutputFormat::BLOB) {
return writeBlob(package, config);
} else {
return writeBlobAsHeader(package, config);
}
}
virtual bool run(const Config& config) = 0;
virtual bool checkParameters(const Config& config) = 0;
virtual bool run(const matp::Config& config) = 0;
virtual bool checkParameters(const matp::Config& config) = 0;
// Write Package as binary to target filename
bool writeBlob(const filamat::Package& pkg, const Config& config) const noexcept;
bool writeBlob(const filamat::Package& pkg, const matp::Config& config) const noexcept;
// Write package as a C++ array content. Use this to include material
// in your executable/library.
bool writeBlobAsHeader(const filamat::Package& pkg, const Config& config) const noexcept;
bool writeBlobAsHeader(const filamat::Package& pkg, const matp::Config& config) const noexcept;
};
} // namespace matc
+15 -478
View File
@@ -22,17 +22,11 @@
#include <filamat/MaterialBuilder.h>
#include <filamat/Enums.h>
#include <filament-matp/Config.h>
#include <utils/Log.h>
#include <utils/JobSystem.h>
#include "DirIncluder.h"
#include "MaterialLexeme.h"
#include "MaterialLexer.h"
#include "JsonishLexer.h"
#include "JsonishParser.h"
#include "ParametersProcessor.h"
#include <utils/Log.h>
#include <utils/Path.h>
#include <GlslangToSpv.h>
#include "glslang/Include/intermediate.h"
@@ -47,249 +41,8 @@ using namespace std::placeholders;
namespace matc {
static constexpr const char* CONFIG_KEY_MATERIAL= "material";
static constexpr const char* CONFIG_KEY_VERTEX_SHADER = "vertex";
static constexpr const char* CONFIG_KEY_FRAGMENT_SHADER = "fragment";
static constexpr const char* CONFIG_KEY_COMPUTE_SHADER = "compute";
static constexpr const char* CONFIG_KEY_TOOL = "tool";
MaterialCompiler::MaterialCompiler() {
mConfigProcessor[CONFIG_KEY_MATERIAL] = &MaterialCompiler::processMaterial;
mConfigProcessor[CONFIG_KEY_VERTEX_SHADER] = &MaterialCompiler::processVertexShader;
mConfigProcessor[CONFIG_KEY_FRAGMENT_SHADER] = &MaterialCompiler::processFragmentShader;
mConfigProcessor[CONFIG_KEY_COMPUTE_SHADER] = &MaterialCompiler::processComputeShader;
mConfigProcessor[CONFIG_KEY_TOOL] = &MaterialCompiler::ignoreLexeme;
mConfigProcessorJSON[CONFIG_KEY_MATERIAL] = &MaterialCompiler::processMaterialJSON;
mConfigProcessorJSON[CONFIG_KEY_VERTEX_SHADER] = &MaterialCompiler::processVertexShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_FRAGMENT_SHADER] = &MaterialCompiler::processFragmentShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_COMPUTE_SHADER] = &MaterialCompiler::processComputeShaderJSON;
mConfigProcessorJSON[CONFIG_KEY_TOOL] = &MaterialCompiler::ignoreLexemeJSON;
}
bool MaterialCompiler::processMaterial(const MaterialLexeme& jsonLexeme,
MaterialBuilder& builder) const noexcept {
JsonishLexer jlexer;
jlexer.lex(jsonLexeme.getStart(), jsonLexeme.getSize(), jsonLexeme.getLine());
JsonishParser parser(jlexer.getLexemes());
std::unique_ptr<JsonishObject> const json = parser.parse();
if (json == nullptr) {
std::cerr << "JsonishParser error (see above)." << std::endl;
return false;
}
ParametersProcessor parametersProcessor;
bool const ok = parametersProcessor.process(builder, *json);
if (!ok) {
std::cerr << "Error while processing material." << std::endl;
return false;
}
return true;
}
bool MaterialCompiler::processVertexShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
MaterialLexeme const trimmedLexeme = lexeme.trimBlockMarkers();
std::string const shaderStr = trimmedLexeme.getStringValue();
// getLine() returns a line number, with 1 being the first line, but .material wants a 0-based
// line number offset, where 0 is the first line.
builder.materialVertex(shaderStr.c_str(), trimmedLexeme.getLine() - 1);
return true;
}
bool MaterialCompiler::processFragmentShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
MaterialLexeme const trimmedLexeme = lexeme.trimBlockMarkers();
std::string const shaderStr = trimmedLexeme.getStringValue();
// getLine() returns a line number, with 1 being the first line, but .material wants a 0-based
// line number offset, where 0 is the first line.
builder.material(shaderStr.c_str(), trimmedLexeme.getLine() - 1);
return true;
}
bool MaterialCompiler::processComputeShader(const MaterialLexeme& lexeme,
MaterialBuilder& builder) const noexcept {
return MaterialCompiler::processFragmentShader(lexeme, builder);
}
bool MaterialCompiler::ignoreLexeme(const MaterialLexeme&, MaterialBuilder&) const noexcept {
return true;
}
bool MaterialCompiler::processMaterialJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'material' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::OBJECT) {
std::cerr << "'material' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be OBJECT."
<< std::endl;
return false;
}
ParametersProcessor parametersProcessor;
bool const ok = parametersProcessor.process(builder, *value->toJsonObject());
if (!ok) {
std::cerr << "Error while processing material." << std::endl;
return false;
}
return true;
}
bool MaterialCompiler::processVertexShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'vertex' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'vertex' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.materialVertex(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialCompiler::processFragmentShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'fragment' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'fragment' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.material(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialCompiler::processComputeShaderJSON(const JsonishValue* value,
filamat::MaterialBuilder& builder) const noexcept {
if (!value) {
std::cerr << "'compute' block does not have a value, one is required." << std::endl;
return false;
}
if (value->getType() != JsonishValue::STRING) {
std::cerr << "'compute' block has an invalid type: "
<< JsonishValue::typeToString(value->getType())
<< ", should be STRING."
<< std::endl;
return false;
}
builder.material(value->toJsonString()->getString().c_str());
return true;
}
bool MaterialCompiler::ignoreLexemeJSON(const JsonishValue*,
filamat::MaterialBuilder&) const noexcept {
return true;
}
static bool reflectParameters(const MaterialBuilder& builder) {
uint8_t const count = builder.getParameterCount();
const MaterialBuilder::ParameterList& parameters = builder.getParameters();
std::cout << "{" << std::endl;
std::cout << " \"parameters\": [" << std::endl;
for (uint8_t i = 0; i < count; i++) {
const MaterialBuilder::Parameter& parameter = parameters[i];
std::cout << " {" << std::endl;
std::cout << R"( "name": ")" << parameter.name.c_str() << "\"," << std::endl;
if (parameter.isSampler()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.samplerType) << "\"," << std::endl;
std::cout << R"( "format": ")" <<
Enums::toString(parameter.format) << "\"," << std::endl;
std::cout << R"( "precision": ")" <<
Enums::toString(parameter.precision) << "\"," << std::endl;
std::cout << R"( "multisample": ")" <<
(parameter.multisample ? "true" : "false")<< "\"" << std::endl;
} else if (parameter.isUniform()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.uniformType) << "\"," << std::endl;
std::cout << R"( "size": ")" << parameter.size << "\"" << std::endl;
} else if (parameter.isSubpass()) {
std::cout << R"( "type": ")" <<
Enums::toString(parameter.subpassType) << "\"," << std::endl;
std::cout << R"( "format": ")" <<
Enums::toString(parameter.format) << "\"," << std::endl;
std::cout << R"( "precision": ")" <<
Enums::toString(parameter.precision) << "\"" << std::endl;
}
std::cout << " }";
if (i < count - 1) std::cout << ",";
std::cout << std::endl;
}
std::cout << " ]" << std::endl;
std::cout << "}" << std::endl;
return true;
}
bool MaterialCompiler::isValidJsonStart(const char* buffer, size_t size) const noexcept {
// Skip all whitespace characters.
const char* end = buffer + size;
while (buffer != end && isspace(buffer[0])) {
buffer++;
}
// A buffer made only of whitespace is not a valid JSON start.
if (buffer == end) {
return false;
}
const char c = buffer[0];
// Take care of block, array, and string
if (c == '{' || c == '[' || c == '"') {
return true;
}
// boolean true
if (c == 't' && (end - buffer) > 3 && strncmp(buffer, "true", 4) != 0) {
return true;
}
// boolean false
if (c == 'f' && (end - buffer) > 4 && strncmp(buffer, "false", 5) != 0) {
return true;
}
// null literal
return c == 'n' && (end - buffer) > 3 && strncmp(buffer, "null", 5) != 0;
}
bool MaterialCompiler::run(const Config& config) {
Config::Input* input = config.getInput();
bool MaterialCompiler::run(const matp::Config& config) {
matp::Config::Input* input = config.getInput();
ssize_t size = input->open();
if (size <= 0) {
std::cerr << "Input file is empty" << std::endl;
@@ -297,70 +50,11 @@ bool MaterialCompiler::run(const Config& config) {
}
std::unique_ptr<const char[]> buffer = input->read();
// Perform template substitutions in two passes: the first pass determines the size of the
// modified buffer and checks for errors. The second pass rebuilds the buffer.
const auto& templateMap = config.getTemplateMap();
ssize_t modifiedSize = size;
bool modified = false;
if (!templateMap.empty()) {
for (ssize_t cursor = 0, n = size - 1; cursor < n; ++cursor) {
if (UTILS_LIKELY(buffer[cursor] != '$' || buffer[cursor + 1] != '{')) {
continue;
}
cursor += 2;
ssize_t endCursor = cursor;
while (true) {
if (endCursor == size) {
std::cerr << "Unexpected end of file" << std::endl;
return false;
}
if (buffer[endCursor] == '}') {
break;
}
++endCursor;
}
// At this point, cursor points to the F in ${FOO} and endCursor points to the }.
std::string_view const macro(&buffer[cursor], endCursor - cursor);
if (auto iter = templateMap.find(macro); iter != templateMap.end()) {
modifiedSize -= macro.size() + 3;
modifiedSize += iter->second.size();
} else {
std::cerr << "Undefined template macro:" << macro << std::endl;
return false;
}
modified = true;
}
}
// Second pass of template substitution allocates a new buffer and performs the actual
// substitutions. Since the first pass did not return early, we can safely assume no errors.
if (modified) {
auto modifiedBuffer = std::make_unique<char[]>(modifiedSize);
for (ssize_t cursor = 0, dstCursor = 0; cursor < size; ++cursor) {
const ssize_t next = cursor + 1;
if (UTILS_LIKELY(buffer[cursor] != '$' || next >= size || buffer[next] != '{')) {
modifiedBuffer[dstCursor++] = buffer[cursor];
continue;
}
cursor += 2;
ssize_t endCursor = cursor;
while (buffer[endCursor] != '}') ++endCursor;
// At this point, cursor points to the F in ${FOO} and endCursor points to the }.
std::string_view const macro(&buffer[cursor], endCursor - cursor);
const std::string& val = templateMap.find(macro)->second;
for (size_t i = 0, n = val.size(); i < n; ++i, ++dstCursor) {
modifiedBuffer[dstCursor] = val[i];
}
cursor = endCursor;
}
buffer = std::move(modifiedBuffer);
size = modifiedSize;
}
utils::Path const materialFilePath = utils::Path(input->getName()).getAbsolutePath();
assert(materialFilePath.isFile());
mParser.processTemplateSubstitutions(config, size, buffer);
if (config.rawShaderMode()) {
utils::Path const materialFilePath = utils::Path(config.getInput()->getName()).getAbsolutePath();
assert(materialFilePath.isFile());
const std::string extension = materialFilePath.getExtension();
glslang::InitializeProcess();
bool const success = compileRawShader(buffer.get(), size, config.isDebug(), config.getOutput(),
@@ -371,54 +65,8 @@ bool MaterialCompiler::run(const Config& config) {
MaterialBuilder::init();
MaterialBuilder builder;
// Before attempting an expensive lex, let's find out if we were sent pure JSON.
bool parsed;
if (isValidJsonStart(buffer.get(), size_t(size))) {
parsed = parseMaterialAsJSON(buffer.get(), size_t(size), builder);
} else {
parsed = parseMaterial(buffer.get(), size_t(size), builder);
}
if (!parsed) {
return false;
}
if (builder.getFeatureLevel() > config.getFeatureLevel()) {
std::cerr << "Material feature level (" << +builder.getFeatureLevel() << ") is higher "
"than maximum allowed (" << +config.getFeatureLevel() << ")" << std::endl;
return false;
}
switch (config.getReflectionTarget()) {
case Config::Metadata::NONE:
break;
case Config::Metadata::PARAMETERS:
return reflectParameters(builder);
}
// Set the root include directory to the directory containing the material file.
DirIncluder includer;
includer.setIncludeDirectory(materialFilePath.getParent());
builder
.noSamplerValidation(config.noSamplerValidation())
.includeEssl1(config.includeEssl1())
.includeCallback(includer)
.fileName(materialFilePath.getName().c_str())
.platform(config.getPlatform())
.targetApi(config.getTargetApi())
.optimization(config.getOptimizationLevel())
.printShaders(config.printShaders())
.saveRawVariants(config.saveRawVariants())
.generateDebugInfo(config.isDebug())
.variantFilter(config.getVariantFilter() | builder.getVariantFilter());
for (const auto& define : config.getDefines()) {
builder.shaderDefine(define.first.c_str(), define.second.c_str());
}
if (!processMaterialParameters(builder, config)) {
std::cerr << "Error while processing material parameters." << std::endl;
if (!mParser.parse(builder, config, input, size, buffer)) {
return false;
}
@@ -432,13 +80,14 @@ bool MaterialCompiler::run(const Config& config) {
MaterialBuilder::shutdown();
if (!package.isValid()) {
std::cerr << "Could not compile material " << input->getName() << std::endl;
std::cerr << "Could not compile material " << config.getInput()->getName() << std::endl;
return false;
}
return writePackage(package, config);
}
bool MaterialCompiler::checkParameters(const Config& config) {
bool MaterialCompiler::checkParameters(const matp::Config& config) {
// Check for input file.
if (config.getInput() == nullptr) {
std::cerr << "Missing input filename." << std::endl;
@@ -446,7 +95,7 @@ bool MaterialCompiler::checkParameters(const Config& config) {
}
// If we have reflection we don't need an output file
if (config.getReflectionTarget() != Config::Metadata::NONE) {
if (config.getReflectionTarget() != matp::Config::Metadata::NONE) {
return true;
}
@@ -459,111 +108,9 @@ bool MaterialCompiler::checkParameters(const Config& config) {
return true;
}
bool MaterialCompiler::parseMaterialAsJSON(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept {
JsonishLexer jlexer;
jlexer.lex(buffer, size, 1);
JsonishParser parser(jlexer.getLexemes());
std::unique_ptr<JsonishObject> json = parser.parse();
if (json == nullptr) {
std::cerr << "Could not parse JSON material file" << std::endl;
return false;
}
for (auto& entry : json->getEntries()) {
const std::string& key = entry.first;
if (mConfigProcessorJSON.find(key) == mConfigProcessorJSON.end()) {
std::cerr << "Unknown identifier '" << key << "'" << std::endl;
return false;
}
// Retrieve function member pointer
MaterialConfigProcessorJSON const p = mConfigProcessorJSON.at(key);
// Call it.
bool const ok = (*this.*p)(entry.second, builder);
if (!ok) {
std::cerr << "Error while processing block with key:'" << key << "'" << std::endl;
return false;
}
}
return true;
}
bool MaterialCompiler::parseMaterial(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept {
MaterialLexer materialLexer;
materialLexer.lex(buffer, size);
auto lexemes = materialLexer.getLexemes();
// Make sure the lexer did not stumble upon unknown character. This could mean we received
// a binary file.
for (auto lexeme : lexemes) {
if (lexeme.getType() == MaterialType::UNKNOWN) {
std::cerr << "Unexpected character at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
}
// Make a first quick pass just to make sure the format was respected (the material format is
// a series of IDENTIFIER, BLOCK pairs).
if (lexemes.size() < 2) {
std::cerr << "Input MUST be an alternation of [identifier, block] pairs." << std::endl;
return false;
}
for (size_t i = 0; i < lexemes.size(); i += 2) {
auto lexeme = lexemes.at(i);
if (lexeme.getType() != MaterialType::IDENTIFIER) {
std::cerr << "An identifier was expected at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
if (i == lexemes.size() - 1) {
std::cerr << "Identifier at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition()
<< " must be followed by a block." << std::endl;
return false;
}
auto nextLexeme = lexemes.at(i + 1);
if (nextLexeme.getType() != MaterialType::BLOCK) {
std::cerr << "A block was expected at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
}
std::string identifier;
for (auto lexeme : lexemes) {
if (lexeme.getType() == MaterialType::IDENTIFIER) {
identifier = lexeme.getStringValue();
if (mConfigProcessor.find(identifier) == mConfigProcessor.end()) {
std::cerr << "Unknown identifier '"
<< lexeme.getStringValue()
<< "' at line:" << lexeme.getLine()
<< " position:" << lexeme.getLinePosition() << std::endl;
return false;
}
} else if (lexeme.getType() == MaterialType::BLOCK) {
MaterialConfigProcessor const processor = mConfigProcessor.at(identifier);
if (!(*this.*processor)(lexeme, builder)) {
std::cerr << "Error while processing block with key:'" << identifier << "'"
<< std::endl;
return false;
}
}
}
return true;
}
// this can either be here or material parser
bool MaterialCompiler::compileRawShader(const char* glsl, size_t size, bool isDebug,
Config::Output* output, const char* ext) const noexcept {
matp::Config::Output* output, const char* ext) const noexcept {
using namespace glslang;
using namespace filament::backend;
using SpirvBlob = std::vector<uint32_t>;
@@ -627,14 +174,4 @@ bool MaterialCompiler::compileRawShader(const char* glsl, size_t size, bool isDe
return true;
}
bool MaterialCompiler::processMaterialParameters(filamat::MaterialBuilder& builder,
const Config& config) const {
ParametersProcessor parametersProcessor;
bool ok = true;
for (const auto& param : config.getMaterialParameters()) {
ok &= parametersProcessor.process(builder, param.first, param.second);
}
return ok;
}
} // namespace matc
+7 -46
View File
@@ -20,70 +20,31 @@
#include <string>
#include <unordered_map>
#include <filament-matp/Config.h>
#include <filament-matp/MaterialParser.h>
#include "Compiler.h"
#include "MaterialLexeme.h"
namespace filamat {
class MaterialBuilder;
}
class TestMaterialCompiler;
namespace matc {
class JsonishValue;
class MaterialCompiler final: public Compiler {
public:
MaterialCompiler();
bool run(const Config& config) override;
bool run(const matp::Config& config) override;
bool checkParameters(const Config& config) override;
bool checkParameters(const matp::Config& config) override;
private:
friend class ::TestMaterialCompiler;
bool parseMaterial(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept;
bool processMaterial(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processVertexShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processFragmentShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool processComputeShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
bool ignoreLexeme(const MaterialLexeme&, filamat::MaterialBuilder& builder) const noexcept;
matp::MaterialParser mParser;
bool parseMaterialAsJSON(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept;
bool processMaterialJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processVertexShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processFragmentShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool processComputeShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
bool ignoreLexemeJSON(const JsonishValue*, filamat::MaterialBuilder& builder) const noexcept;
bool isValidJsonStart(const char* buffer, size_t size) const noexcept;
bool compileRawShader(const char* glsl, size_t size, bool isDebug, Config::Output* output,
bool compileRawShader(const char* glsl, size_t size, bool isDebug, matp::Config::Output* output,
const char* ext) const noexcept;
bool processMaterialParameters(filamat::MaterialBuilder& builder, const Config& config) const;
// Member function pointer type, this is used to implement a Command design
// pattern.
using MaterialConfigProcessor = bool (MaterialCompiler::*)
(const MaterialLexeme&, filamat::MaterialBuilder& builder) const;
// Map used to store Command pattern function pointers.
std::unordered_map<std::string, MaterialConfigProcessor> mConfigProcessor;
// The same, but for pure JSON syntax
using MaterialConfigProcessorJSON = bool (MaterialCompiler::*)
(const JsonishValue*, filamat::MaterialBuilder& builder) const;
std::unordered_map<std::string, MaterialConfigProcessorJSON> mConfigProcessorJSON;
};
} // namespace matc
#endif //TNT_MATERIALCOMPILER_H
-65
View File
@@ -1,65 +0,0 @@
/*
* Copyright (C) 2017 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 "MockConfig.h"
bool NullOutput::open() noexcept {
return true;
}
bool NullOutput::write(const uint8_t* data, size_t size) noexcept {
return true;
}
std::ostream& NullOutput::getOutputStream() noexcept {
return mNullStream;
}
bool NullOutput::close() noexcept {
return true;
}
MockInput::MockInput(uint8_t* data, size_t size) : mSize(size) {
mData = new uint8_t[size];
memcpy(mData, data, size);
}
ssize_t MockInput::open() noexcept {
return mSize;
}
std::unique_ptr<const char[]> MockInput::read() noexcept {
char* rawBuffer = new char[mSize];
memcpy(rawBuffer, mData, mSize);
return std::unique_ptr<const char[]>(rawBuffer);
}
bool MockInput::close() noexcept {
return false;
}
MockInput::~MockInput() {
delete mData;
}
matc::Config::Output* MockConfig::getOutput() const noexcept {
return nullptr;
}
matc::Config::Input* MockConfig::getInput() const noexcept {
return nullptr;
}
std::string MockConfig::toString() const noexcept {
return {};
}
-65
View File
@@ -1,65 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MOCKCONFIG_H
#define TNT_MOCKCONFIG_H
#include <matc/Config.h>
class NullOutput : public matc::Config::Output {
public:
NullOutput() : mNullStream(&mNull){
}
bool open() noexcept override;
bool write(const uint8_t* data, size_t size) noexcept override;
std::ostream& getOutputStream() noexcept override;
bool close() noexcept override;
private:
class NullBuffer : public std::streambuf {
public:
int overflow(int c) override { return c; }
};
NullBuffer mNull;
std::ostream mNullStream;
};
class MockInput : public matc::Config::Input {
public:
MockInput(uint8_t* data, size_t size);
~MockInput() override;
ssize_t open() noexcept override;
std::unique_ptr<const char[]> read() noexcept override;
bool close() noexcept override;
const char* getName() const noexcept override { return "mock"; };
private:
uint8_t* mData;
size_t mSize;
};
class MockConfig : public matc::Config {
public:
Output* getOutput() const noexcept override;
Input* getInput() const noexcept override;
std::string toString() const noexcept override;
private:
};
#endif //TNT_MOCKCONFIG_H
-43
View File
@@ -1,43 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_TESTMATERIALCOMPILER_H
#define TNT_TESTMATERIALCOMPILER_H
#include <stddef.h>
#include <matc/MaterialCompiler.h>
class TestMaterialCompiler {
public:
explicit TestMaterialCompiler(const matc::MaterialCompiler& materialCompiler) :
mMaterialCompiler(materialCompiler) {}
bool parseMaterial(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
const noexcept{
return mMaterialCompiler.parseMaterial(buffer, size, builder);
}
bool parseMaterialAsJSON(const char* buffer, size_t size, filamat::MaterialBuilder& builder)
const noexcept{
return mMaterialCompiler.parseMaterialAsJSON(buffer, size, builder);
}
private:
const matc::MaterialCompiler& mMaterialCompiler;
};
#endif //TNT_TESTMATERIALCOMPILER_H
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.63.0",
"version": "1.62.2",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",