Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3fc8e3796 | ||
|
|
5e7106b521 | ||
|
|
30387af61c | ||
|
|
1c2ffc9ed4 | ||
|
|
574518ea74 | ||
|
|
d5a274fa25 | ||
|
|
3351db1178 | ||
|
|
b70aa43727 | ||
|
|
1f9a11802d | ||
|
|
89835a7a67 | ||
|
|
28ef805e5d | ||
|
|
6c0bd360b3 | ||
|
|
063affb612 | ||
|
|
9c857f64ae | ||
|
|
5966b5dd8f | ||
|
|
1795c40591 | ||
|
|
26f4239d8c | ||
|
|
ad29b9c70a | ||
|
|
1c817026f2 | ||
|
|
44a954b559 |
@@ -7,6 +7,9 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.54.1
|
||||
|
||||
|
||||
## v1.54.0
|
||||
|
||||
- materials: add a new `stereoscopicType` material parameter. [⚠️ **New Material Version**]
|
||||
|
||||
+11
-2
@@ -1308,7 +1308,12 @@ Description
|
||||
declare a variable called `eyeDirection` you can access it in the fragment shader using
|
||||
`variable_eyeDirection`. In the vertex shader, the interpolant name is simply a member of
|
||||
the `MaterialVertexInputs` structure (`material.eyeDirection` in your example). Each
|
||||
interpolant is of type `float4` (`vec4`) in the shaders.
|
||||
interpolant is of type `float4` (`vec4`) in the shaders. By default the precision of the
|
||||
interpolant is `highp` in *both* the vertex and fragment shaders.
|
||||
An alternate syntax can be used to specify both the name and precision of the interpolant.
|
||||
In this case the specified precision is used as-is in both fragment and vertex stages, in
|
||||
particular if `default` is specified the default precision is used is the fragment shader
|
||||
(`mediump`) and in the vertex shader (`highp`).
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
@@ -1320,7 +1325,11 @@ material {
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
eyeDirection
|
||||
eyeDirection,
|
||||
{
|
||||
name : eyeColor,
|
||||
precision : medium
|
||||
}
|
||||
],
|
||||
vertexDomain : device,
|
||||
depthWrite : false,
|
||||
|
||||
@@ -1058,7 +1058,7 @@ struct RasterState {
|
||||
bool inverseFrontFaces : 1; // 31
|
||||
|
||||
//! padding, must be 0
|
||||
uint8_t padding : 1; // 32
|
||||
bool depthClamp : 1; // 32
|
||||
};
|
||||
uint32_t u = 0;
|
||||
};
|
||||
|
||||
@@ -305,6 +305,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
|
||||
@@ -112,6 +112,7 @@ struct MetalContext {
|
||||
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
|
||||
CullModeStateTracker cullModeState;
|
||||
WindingStateTracker windingState;
|
||||
DepthClampStateTracker depthClampState;
|
||||
Handle<HwRenderPrimitive> currentRenderPrimitive;
|
||||
|
||||
// State caches.
|
||||
|
||||
@@ -834,6 +834,10 @@ bool MetalDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthClampSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -1751,6 +1755,13 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
|
||||
[mContext->currentRenderPassEncoder setFrontFacingWinding:winding];
|
||||
}
|
||||
|
||||
// depth clip mode
|
||||
MTLDepthClipMode depthClipMode = rs.depthClamp ? MTLDepthClipModeClamp : MTLDepthClipModeClip;
|
||||
mContext->depthClampState.updateState(depthClipMode);
|
||||
if (mContext->depthClampState.stateChanged()) {
|
||||
[mContext->currentRenderPassEncoder setDepthClipMode:depthClipMode];
|
||||
}
|
||||
|
||||
// Set the depth-stencil state, if a state change is needed.
|
||||
DepthStencilState depthState;
|
||||
if (depthAttachment) {
|
||||
|
||||
@@ -382,6 +382,7 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
|
||||
|
||||
using CullModeStateTracker = StateTracker<MTLCullMode>;
|
||||
using WindingStateTracker = StateTracker<MTLWinding>;
|
||||
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
|
||||
|
||||
// Argument encoder
|
||||
|
||||
|
||||
@@ -202,6 +202,10 @@ bool NoopDriver::isProtectedTexturesSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthClampSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -541,9 +541,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
bugs->delay_fbo_destruction = true;
|
||||
// PowerVR seems to have no problem with this (which is good for us)
|
||||
bugs->allow_read_only_ancillary_feedback_loop = true;
|
||||
// PowerVR doesn't respect lengths passed to glShaderSource, so concatenate them into a
|
||||
// single string.
|
||||
bugs->concatenate_shader_strings = true;
|
||||
} else if (strstr(renderer, "Apple")) {
|
||||
// Apple GPU
|
||||
} else if (strstr(renderer, "Tegra") ||
|
||||
@@ -679,6 +676,7 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
|
||||
#endif
|
||||
ext->EXT_depth_clamp = exts.has("GL_EXT_depth_clamp"sv);
|
||||
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
|
||||
@@ -749,6 +747,7 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
ext->EXT_color_buffer_half_float = true; // Assumes core profile.
|
||||
ext->EXT_clip_cull_distance = true;
|
||||
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
|
||||
ext->EXT_depth_clamp = true;
|
||||
ext->EXT_discard_framebuffer = false;
|
||||
ext->EXT_disjoint_timer_query = true;
|
||||
ext->EXT_multisampled_render_to_texture = false;
|
||||
|
||||
@@ -220,8 +220,9 @@ public:
|
||||
bool EXT_color_buffer_float;
|
||||
bool EXT_color_buffer_half_float;
|
||||
bool EXT_debug_marker;
|
||||
bool EXT_disjoint_timer_query;
|
||||
bool EXT_depth_clamp;
|
||||
bool EXT_discard_framebuffer;
|
||||
bool EXT_disjoint_timer_query;
|
||||
bool EXT_multisampled_render_to_texture2;
|
||||
bool EXT_multisampled_render_to_texture;
|
||||
bool EXT_protected_textures;
|
||||
@@ -239,10 +240,10 @@ public:
|
||||
bool KHR_parallel_shader_compile;
|
||||
bool KHR_texture_compression_astc_hdr;
|
||||
bool KHR_texture_compression_astc_ldr;
|
||||
bool OES_depth_texture;
|
||||
bool OES_depth24;
|
||||
bool OES_packed_depth_stencil;
|
||||
bool OES_EGL_image_external_essl3;
|
||||
bool OES_depth24;
|
||||
bool OES_depth_texture;
|
||||
bool OES_packed_depth_stencil;
|
||||
bool OES_rgb8_rgba8;
|
||||
bool OES_standard_derivatives;
|
||||
bool OES_texture_npot;
|
||||
@@ -315,12 +316,6 @@ public:
|
||||
// bugs or performance issues.
|
||||
bool force_feature_level0;
|
||||
|
||||
// Some drivers don't respect the length argument of glShaderSource() and (apparently)
|
||||
// require each shader source string to be null-terminated. This works around the issue by
|
||||
// concatenating the strings into a single null-terminated string before passing it to
|
||||
// glShaderSource().
|
||||
bool concatenate_shader_strings;
|
||||
|
||||
} bugs = {};
|
||||
|
||||
// state getters -- as needed.
|
||||
@@ -567,9 +562,6 @@ private:
|
||||
{ bugs.force_feature_level0,
|
||||
"force_feature_level0",
|
||||
""},
|
||||
{ bugs.concatenate_shader_strings,
|
||||
"concatenate_shader_strings",
|
||||
""},
|
||||
}};
|
||||
|
||||
// this is chosen to minimize code size
|
||||
@@ -636,6 +628,7 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
|
||||
#ifdef BACKEND_OPENGL_VERSION_GL
|
||||
case GL_PROGRAM_POINT_SIZE: index = 10; break;
|
||||
#endif
|
||||
case GL_DEPTH_CLAMP: index = 11; break;
|
||||
default: break;
|
||||
}
|
||||
assert_invariant(index < state.enables.caps.size());
|
||||
|
||||
@@ -451,6 +451,14 @@ void OpenGLDriver::setRasterState(RasterState rs) noexcept {
|
||||
} else {
|
||||
gl.disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
}
|
||||
|
||||
if (gl.ext.EXT_depth_clamp) {
|
||||
if (rs.depthClamp) {
|
||||
gl.enable(GL_DEPTH_CLAMP);
|
||||
} else {
|
||||
gl.disable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::setStencilState(StencilState ss) noexcept {
|
||||
@@ -2119,6 +2127,10 @@ bool OpenGLDriver::isProtectedTexturesSupported() {
|
||||
return getContext().ext.EXT_protected_textures;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isDepthClampSupported() {
|
||||
return getContext().ext.EXT_depth_clamp;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
|
||||
@@ -597,40 +597,30 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
version = "#version 310 es\n";
|
||||
}
|
||||
|
||||
const std::array<const char*, 5> sources = {
|
||||
version.data(),
|
||||
prolog.data(),
|
||||
specializationConstantString.c_str(),
|
||||
packingFunctions.data(),
|
||||
body.data()
|
||||
std::array<std::string_view, 5> sources = {
|
||||
version,
|
||||
prolog,
|
||||
specializationConstantString,
|
||||
packingFunctions,
|
||||
{ body.data(), body.size() - 1 } // null-terminated
|
||||
};
|
||||
|
||||
const std::array<GLint, 5> lengths = {
|
||||
(GLint)version.length(),
|
||||
(GLint)prolog.length(),
|
||||
(GLint)specializationConstantString.length(),
|
||||
(GLint)packingFunctions.length(),
|
||||
(GLint)body.length() - 1 // null terminated
|
||||
};
|
||||
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
|
||||
// zero to glShaderSource(). glShaderSource should work with lengths of zero, but some
|
||||
// drivers instead interpret zero as a sentinel for a null-terminated string.
|
||||
auto partitionPoint = std::stable_partition(
|
||||
sources.begin(), sources.end(), [](std::string_view s) { return !s.empty(); });
|
||||
size_t count = std::distance(sources.begin(), partitionPoint);
|
||||
|
||||
std::array<const char*, 5> shaderStrings;
|
||||
std::array<GLint, 5> lengths;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
shaderStrings[i] = sources[i].data();
|
||||
lengths[i] = sources[i].size();
|
||||
}
|
||||
|
||||
GLuint const shaderId = glCreateShader(glShaderType);
|
||||
|
||||
if (UTILS_UNLIKELY(context.bugs.concatenate_shader_strings)) {
|
||||
size_t totalSize = 0;
|
||||
for (size_t i = 0; i < sources.size(); i++) {
|
||||
totalSize += lengths[i];
|
||||
}
|
||||
std::string concatenatedShaderSource;
|
||||
concatenatedShaderSource.reserve(totalSize);
|
||||
for (size_t i = 0; i < sources.size(); i++) {
|
||||
concatenatedShaderSource.append(sources[i], lengths[i]);
|
||||
}
|
||||
const GLchar* ptr = concatenatedShaderSource.c_str();
|
||||
GLint length = concatenatedShaderSource.length();
|
||||
glShaderSource(shaderId, 1, &ptr, &length);
|
||||
} else {
|
||||
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
|
||||
}
|
||||
glShaderSource(shaderId, count, shaderStrings.data(), lengths.data());
|
||||
|
||||
glCompileShader(shaderId);
|
||||
|
||||
|
||||
@@ -201,6 +201,12 @@ using namespace glext;
|
||||
# define GL_CLIP_DISTANCE1 0x3001
|
||||
#endif
|
||||
|
||||
#if defined(GL_EXT_depth_clamp)
|
||||
# define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
|
||||
#else
|
||||
# define GL_DEPTH_CLAMP 0x864F
|
||||
#endif
|
||||
|
||||
#if defined(GL_KHR_debug)
|
||||
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
|
||||
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
|
||||
|
||||
@@ -125,6 +125,10 @@ public:
|
||||
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isDepthClampSupported() const noexcept {
|
||||
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isDebugMarkersSupported() const noexcept {
|
||||
return mDebugMarkersSupported;
|
||||
}
|
||||
|
||||
@@ -944,6 +944,10 @@ bool VulkanDriver::isProtectedTexturesSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isDepthClampSupported() {
|
||||
return mContext.isDepthClampSupported();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU: {
|
||||
@@ -1816,6 +1820,8 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
|
||||
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
|
||||
.rasterizationSamples = rt->getSamples(),
|
||||
.depthClamp = rasterState.depthClamp,
|
||||
.reserved = 0,
|
||||
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
|
||||
.colorBlendOp = rasterState.blendEquationRGB,
|
||||
.alphaBlendOp = rasterState.blendEquationAlpha,
|
||||
|
||||
@@ -184,6 +184,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
vkRaster.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
vkRaster.cullMode = raster.cullMode;
|
||||
vkRaster.frontFace = raster.frontFace;
|
||||
vkRaster.depthClampEnable = raster.depthClamp;
|
||||
vkRaster.depthBiasEnable = raster.depthBiasEnable;
|
||||
vkRaster.depthBiasConstantFactor = raster.depthBiasConstantFactor;
|
||||
vkRaster.depthBiasClamp = 0.0f;
|
||||
|
||||
@@ -90,7 +90,9 @@ public:
|
||||
VkBlendFactor srcAlphaBlendFactor : 5;
|
||||
VkBlendFactor dstAlphaBlendFactor : 5;
|
||||
VkColorComponentFlags colorWriteMask : 4;
|
||||
uint8_t rasterizationSamples; // offset = 4 bytes
|
||||
uint8_t rasterizationSamples : 4;// offset = 4 bytes
|
||||
uint8_t depthClamp : 1;
|
||||
uint8_t reserved : 3;
|
||||
uint8_t colorTargetCount; // offset = 5 bytes
|
||||
BlendEquation colorBlendOp : 4; // offset = 6 bytes
|
||||
BlendEquation alphaBlendOp : 4;
|
||||
|
||||
@@ -325,6 +325,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
// We could simply enable all supported features, but since that may have performance
|
||||
// consequences let's just enable the features we need.
|
||||
VkPhysicalDeviceFeatures enabledFeatures{
|
||||
.depthClamp = features.depthClamp,
|
||||
.samplerAnisotropy = features.samplerAnisotropy,
|
||||
.textureCompressionETC2 = features.textureCompressionETC2,
|
||||
.textureCompressionBC = features.textureCompressionBC,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
@@ -727,7 +728,7 @@ public:
|
||||
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
|
||||
|
||||
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
|
||||
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
|
||||
|
||||
/** Result of a picking query */
|
||||
|
||||
@@ -16,16 +16,22 @@
|
||||
|
||||
#include "FrameSkipper.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
FrameSkipper::FrameSkipper(size_t latency) noexcept
|
||||
: mLast(latency - 1) {
|
||||
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
|
||||
assert_invariant(latency <= MAX_FRAME_LATENCY);
|
||||
}
|
||||
|
||||
@@ -41,31 +47,32 @@ void FrameSkipper::terminate(DriverApi& driver) noexcept {
|
||||
|
||||
bool FrameSkipper::beginFrame(DriverApi& driver) noexcept {
|
||||
auto& fences = mDelayedFences;
|
||||
auto fence = fences.front();
|
||||
if (fence) {
|
||||
auto status = driver.getFenceStatus(fence);
|
||||
if (status == FenceStatus::TIMEOUT_EXPIRED) {
|
||||
// Sync not ready, skip frame
|
||||
if (fences.front()) {
|
||||
// Do we have a latency old fence?
|
||||
auto status = driver.getFenceStatus(fences.front());
|
||||
if (UTILS_UNLIKELY(status == FenceStatus::TIMEOUT_EXPIRED)) {
|
||||
// The fence hasn't signaled yet, skip this frame
|
||||
return false;
|
||||
}
|
||||
assert_invariant(status == FenceStatus::CONDITION_SATISFIED);
|
||||
driver.destroyFence(fence);
|
||||
}
|
||||
// shift all fences down by 1
|
||||
std::move(fences.begin() + 1, fences.end(), fences.begin());
|
||||
fences.back() = {};
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrameSkipper::endFrame(DriverApi& driver) noexcept {
|
||||
// If the user produced a new frame despite the fact that the previous one wasn't finished
|
||||
// (i.e. FrameSkipper::beginFrame() returned false), we need to make sure to replace
|
||||
// a fence that might be here already)
|
||||
auto& fence = mDelayedFences[mLast];
|
||||
if (fence) {
|
||||
driver.destroyFence(fence);
|
||||
auto& fences = mDelayedFences;
|
||||
size_t const last = mLast;
|
||||
|
||||
// pop the oldest fence and advance the other ones
|
||||
if (fences.front()) {
|
||||
driver.destroyFence(fences.front());
|
||||
}
|
||||
fence = driver.createFence();
|
||||
std::move(fences.begin() + 1, fences.end(), fences.begin());
|
||||
|
||||
// add a new fence to the end
|
||||
assert_invariant(!fences[last]);
|
||||
|
||||
fences[last] = driver.createFence();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
/*
|
||||
@@ -60,7 +63,7 @@ public:
|
||||
private:
|
||||
using Container = std::array<backend::Handle<backend::HwFence>, MAX_FRAME_LATENCY>;
|
||||
mutable Container mDelayedFences{};
|
||||
size_t mLast;
|
||||
uint8_t const mLast;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -494,7 +494,6 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
|
||||
struct StructureMipmapData {
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
uint32_t rt[8];
|
||||
};
|
||||
|
||||
fg.addPass<StructureMipmapData>("StructureMipmap",
|
||||
@@ -505,7 +504,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
.level = uint8_t(i)
|
||||
});
|
||||
out = builder.write(out, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
data.rt[i - 1] = builder.declareRenderPass("Structure mip target", {
|
||||
builder.declareRenderPass("Structure mip target", {
|
||||
.attachments = { .depth = out }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -419,7 +419,8 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine,
|
||||
UTILS_ALWAYS_INLINE // This function exists only to make the code more readable. we want it inlined.
|
||||
inline // and we don't need it in the compilation unit
|
||||
void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
|
||||
FMaterialInstance const* const UTILS_RESTRICT mi,
|
||||
bool inverseFrontFaces, bool hasDepthClamp) noexcept {
|
||||
|
||||
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
|
||||
variant = Variant::filterVariant(variant, ma->isVariantLit());
|
||||
@@ -460,6 +461,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
|
||||
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
|
||||
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
|
||||
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
|
||||
cmdDraw.info.materialVariant = variant;
|
||||
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
|
||||
}
|
||||
@@ -558,6 +560,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
bool const hasInstancedStereo =
|
||||
renderFlags & IS_INSTANCED_STEREOSCOPIC;
|
||||
|
||||
bool const hasDepthClamp =
|
||||
renderFlags & HAS_DEPTH_CLAMP;
|
||||
|
||||
float const cameraPositionDotCameraForward = dot(cameraPosition, cameraForward);
|
||||
|
||||
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
|
||||
@@ -577,6 +582,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
cmd.info.rasterState.depthWrite = true;
|
||||
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
|
||||
cmd.info.rasterState.alphaToCoverage = false;
|
||||
cmd.info.rasterState.depthClamp = hasDepthClamp;
|
||||
}
|
||||
|
||||
for (uint32_t i = range.first; i < range.last; ++i) {
|
||||
@@ -610,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
// Here, objects close to the camera (but behind) will be drawn first.
|
||||
// An alternative that keeps the mathematical ordering is given here:
|
||||
// distanceBits ^= ((int32_t(distanceBits) >> 31) | 0x80000000u);
|
||||
float const distance = -dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward;
|
||||
float const distance = -(dot(soaWorldAABBCenter[i], cameraForward) - cameraPositionDotCameraForward);
|
||||
uint32_t const distanceBits = reinterpret_cast<uint32_t const&>(distance);
|
||||
|
||||
// calculate the per-primitive face winding order inversion
|
||||
@@ -691,7 +697,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
|
||||
|
||||
if constexpr (isColorPass) {
|
||||
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
|
||||
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
|
||||
inverseFrontFaces, hasDepthClamp);
|
||||
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
|
||||
if (blendPass) {
|
||||
// TODO: at least for transparent objects, AABB should be per primitive
|
||||
|
||||
@@ -284,6 +284,7 @@ public:
|
||||
static constexpr RenderFlags HAS_SHADOWING = 0x01;
|
||||
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02;
|
||||
static constexpr RenderFlags IS_INSTANCED_STEREOSCOPIC = 0x04;
|
||||
static constexpr RenderFlags HAS_DEPTH_CLAMP = 0x08;
|
||||
|
||||
// Arena used for commands
|
||||
using Arena = utils::Arena<
|
||||
@@ -444,7 +445,7 @@ private:
|
||||
uint8_t instancedStereoEyeCount) noexcept;
|
||||
|
||||
static void setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
|
||||
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
|
||||
|
||||
static void updateSummedPrimitiveCounts(
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
|
||||
|
||||
@@ -75,7 +75,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
|
||||
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
|
||||
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
|
||||
uint8_t layerCount = 1;
|
||||
|
||||
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
|
||||
data.ssao = blackboard.get<FrameGraphTexture>("ssao");
|
||||
@@ -172,9 +171,6 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
|
||||
data.color = builder.write(data.color, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.depth = builder.write(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
if (view.hasStereo() && engine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW) {
|
||||
layerCount = engine.getConfig().stereoscopicEyeCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is a bit of magic happening here regarding the viewport used.
|
||||
@@ -196,7 +192,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
|
||||
.stencil = data.stencil },
|
||||
.clearColor = config.clearColor,
|
||||
.samples = config.msaa,
|
||||
.layerCount = layerCount,
|
||||
.layerCount = static_cast<uint8_t>(colorBufferDesc.depth),
|
||||
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags});
|
||||
blackboard["depth"] = data.depth;
|
||||
},
|
||||
|
||||
+67
-49
@@ -57,9 +57,6 @@ namespace filament {
|
||||
using namespace math;
|
||||
using namespace backend;
|
||||
|
||||
// do this only if depth-clamp is available
|
||||
static constexpr bool USE_DEPTH_CLAMP = false;
|
||||
|
||||
ShadowMap::ShadowMap(FEngine& engine) noexcept
|
||||
: mPerShadowMapUniforms(engine),
|
||||
mShadowType(ShadowType::DIRECTIONAL),
|
||||
@@ -126,7 +123,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
FScene::LightSoa const& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept {
|
||||
|
||||
// reset the visible shadow status
|
||||
mHasVisibleShadows = false;
|
||||
@@ -137,13 +135,20 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
const auto direction = lightData.elementAt<FScene::SHADOW_DIRECTION>(index);
|
||||
|
||||
auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo);
|
||||
auto const [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] =
|
||||
computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo,
|
||||
useDepthClamp);
|
||||
|
||||
if (UTILS_UNLIKELY(!visibleShadows)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// if the 2D bounds of the intersection is empty seen from the light, we have no shadow
|
||||
Aabb const bounds = compute2DBounds(Mv, lsClippedShadowVolume.data(), vertexCount);
|
||||
if (UTILS_UNLIKELY(!(bounds.min.x < bounds.max.x && bounds.min.y < bounds.max.y))) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the light's projection matrix
|
||||
* (directional/point lights, i.e. projection to use, including znear/zfar clip planes)
|
||||
@@ -161,12 +166,17 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
mat4f W, Wp, L;
|
||||
// We can't use LISPSM in stable mode
|
||||
const bool useLispsm = params.options.lispsm && !params.options.stable;
|
||||
if (useLispsm) {
|
||||
|
||||
if (useLispsm ||
|
||||
(!params.options.stable && !engine.debug.shadowmap.disable_light_frustum_align)) {
|
||||
// Orient the shadow map in the direction of the view vector by constructing a
|
||||
const float3 lsCameraFwd = Mv.upperLeft() * camera.getForwardVector();
|
||||
L = computeLightRotation(lsCameraFwd);
|
||||
LMp = L * Mp;
|
||||
LMpMv = LMp * Mv;
|
||||
}
|
||||
|
||||
if (useLispsm) {
|
||||
W = applyLISPSM(Wp, camera, params,
|
||||
LMp, Mv, LMpMv, lsClippedShadowVolume, vertexCount, direction);
|
||||
}
|
||||
@@ -184,8 +194,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
// In LiPSM mode, we're using the warped space here.
|
||||
float4 f = computeFocusParams(LMpMv, WLMp,
|
||||
lsClippedShadowVolume, vertexCount,
|
||||
camera, sceneInfo.csNearFar,
|
||||
params.options.shadowFar, params.options.stable);
|
||||
camera, params.options.shadowFar, params.options.stable);
|
||||
|
||||
if (params.options.stable) {
|
||||
const auto lsRef = lightData.elementAt<FScene::SHADOW_REF>(index);
|
||||
@@ -386,7 +395,8 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
float3 const direction,
|
||||
FLightManager::ShadowParams params,
|
||||
filament::CameraInfo const& camera,
|
||||
SceneInfo const& sceneInfo) noexcept {
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept {
|
||||
|
||||
// we use aligned_storage<> here to avoid the default initialization of std::array<>
|
||||
using Storage = std::aligned_storage<sizeof(FrustumBoxIntersection)>::type;
|
||||
@@ -448,36 +458,49 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds(
|
||||
|
||||
// Compute the intersection of the view volume with the intersection of receivers and casters
|
||||
// in light space. This returns a set of points on the convex-hull of the intersection.
|
||||
mat4f const projection = camera.cullingProjection *
|
||||
(camera.view * FCamera::rigidTransformInverse(MvAtOrigin));
|
||||
size_t const vertexCount = intersectFrustumWithBox(lsClippedShadowVolume,
|
||||
camera.cullingProjection * camera.view * FCamera::rigidTransformInverse(MvAtOrigin),
|
||||
sceneInfo.csNearFar, lsLightFrustumBounds);
|
||||
projection, lsLightFrustumBounds);
|
||||
|
||||
if (UTILS_UNLIKELY(vertexCount < 4)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
* - The Near plane is set to the shadow casters max z (i.e. closest to the light)
|
||||
* - The Far plane is set to the closest of the farthest shadow casters and receivers
|
||||
* i.e.: shadow casters behind the last receivers can't cast any shadows
|
||||
*
|
||||
* If "depth clamp" is supported, we can further tighten the near plane to the
|
||||
* shadow receiver.
|
||||
*
|
||||
* Note: L has no influence here, since we're only interested in z values
|
||||
* (L is a rotation around z)
|
||||
*/
|
||||
* Adjust the scene's zmax (i.e. Near plane) and zmin (i.e. Far plane) in light space.
|
||||
* (near/far correspond to max/min because the light looks down the -z axis).
|
||||
*
|
||||
* - For the Far plane, the light frustum is already set to the closest of receivers and
|
||||
* casters (i.e.: shadow casters behind the last receivers can't cast any shadows).
|
||||
* But it can be moved closer up to the farthest point in the camera frustum & light
|
||||
* frustum intersection.
|
||||
*
|
||||
* - The Near plane is already set to the shadow casters max z (i.e. closest to the light)
|
||||
*
|
||||
* If "depth clamp" is supported, we can further tighten the near plane to the
|
||||
* shadow receiver (i.e. to the closest point of camera frustum & light frustum intersection).
|
||||
*
|
||||
* Note: L has no influence here, since we're only interested in z values
|
||||
* (L is a rotation around z)
|
||||
*/
|
||||
|
||||
float lsClippedShadowVolumeFarthest = std::numeric_limits<float>::max();
|
||||
float lsClippedShadowVolumeNearest = std::numeric_limits<float>::lowest();
|
||||
for (size_t i = 0; i < vertexCount; ++i) {
|
||||
float3 const v = lsClippedShadowVolume[i];
|
||||
// far: figure out the farthest shadow receivers
|
||||
lsLightFrustumBounds.min.z = min(lsLightFrustumBounds.min.z, v.z);
|
||||
if constexpr (USE_DEPTH_CLAMP) {
|
||||
lsLightFrustumBounds.max.z = max(lsLightFrustumBounds.max.z, v.z);
|
||||
}
|
||||
lsClippedShadowVolumeFarthest = std::min(lsClippedShadowVolumeFarthest, v.z);
|
||||
lsClippedShadowVolumeNearest = std::max(lsClippedShadowVolumeNearest, v.z);
|
||||
}
|
||||
|
||||
lsLightFrustumBounds.min.z =
|
||||
std::max(lsLightFrustumBounds.min.z, lsClippedShadowVolumeFarthest);
|
||||
|
||||
if (useDepthClamp) {
|
||||
lsLightFrustumBounds.max.z =
|
||||
std::min(lsLightFrustumBounds.max.z, lsClippedShadowVolumeNearest);
|
||||
}
|
||||
|
||||
if (engine.debug.shadowmap.far_uses_shadowcasters) {
|
||||
// far: closest of the farthest shadow casters and receivers
|
||||
lsLightFrustumBounds.min.z =
|
||||
@@ -611,7 +634,7 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
mat4f const& LMpMv,
|
||||
mat4f const& WLMp,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera, float2 const& csNearFar,
|
||||
filament::CameraInfo const& camera,
|
||||
float shadowFar, bool stable) noexcept {
|
||||
float2 s, o;
|
||||
if (stable) {
|
||||
@@ -626,7 +649,7 @@ math::float4 ShadowMap::computeFocusParams(
|
||||
return wsViewVolumeBoundingSphere;
|
||||
} else {
|
||||
mat4f const viewFromClip = inverse(camera.cullingProjection);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar);
|
||||
Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip);
|
||||
float4 const wsViewVolumeBoundingSphere =
|
||||
computeBoundingSphere(wsFrustumVertices.vertices, 8);
|
||||
return wsViewVolumeBoundingSphere;
|
||||
@@ -794,26 +817,25 @@ Aabb ShadowMap::compute2DBounds(const mat4f& lightView,
|
||||
}
|
||||
|
||||
ShadowMap::Corners ShadowMap::computeFrustumCorners(
|
||||
mat4f const& projectionInverse,
|
||||
float2 csNearFar) noexcept {
|
||||
mat4f const& projectionInverse) noexcept {
|
||||
|
||||
Corners out;
|
||||
|
||||
// compute view frustum in world space (from its NDC)
|
||||
// matrix to convert: ndc -> camera -> world
|
||||
float const near = csNearFar.x;
|
||||
float const far = csNearFar.y;
|
||||
float const near = -1.0f;
|
||||
float const far = 1.0f;
|
||||
|
||||
Corners const csViewFrustumCorners = {
|
||||
.vertices = {
|
||||
{ -1, -1, far },
|
||||
{ 1, -1, far },
|
||||
{ -1, 1, far },
|
||||
{ 1, 1, far },
|
||||
{ 1, -1, far },
|
||||
{ -1, 1, far },
|
||||
{ 1, 1, far },
|
||||
{ -1, -1, near },
|
||||
{ 1, -1, near },
|
||||
{ -1, 1, near },
|
||||
{ 1, 1, near },
|
||||
{ 1, -1, near },
|
||||
{ -1, 1, near },
|
||||
{ 1, 1, near },
|
||||
}
|
||||
};
|
||||
|
||||
@@ -895,11 +917,11 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
|
||||
|
||||
size_t ShadowMap::intersectFrustumWithBox(
|
||||
FrustumBoxIntersection& UTILS_RESTRICT outVertices,
|
||||
mat4f const& UTILS_RESTRICT projection, math::float2 const& UTILS_RESTRICT csNearFar,
|
||||
mat4f const& UTILS_RESTRICT projection,
|
||||
Aabb const& UTILS_RESTRICT box)
|
||||
{
|
||||
Frustum const frustum{ projection };
|
||||
Corners const frustumVertices{ computeFrustumCorners(inverse(projection), csNearFar) };
|
||||
Corners const frustumVertices{ computeFrustumCorners(inverse(projection)) };
|
||||
|
||||
constexpr const float EPSILON = 1.0f / 8192.0f; // ~0.012 mm
|
||||
size_t vertexCount = 0;
|
||||
@@ -1203,9 +1225,8 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
}
|
||||
|
||||
ShadowMap::SceneInfo::SceneInfo(
|
||||
FScene const& scene, uint8_t visibleLayers, mat4f const& viewMatrix) noexcept
|
||||
: vsNearFar(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max()),
|
||||
visibleLayers(visibleLayers) {
|
||||
FScene const& scene, uint8_t visibleLayers) noexcept
|
||||
: visibleLayers(visibleLayers) {
|
||||
|
||||
// the code below only works with affine transforms
|
||||
// Filament's API requires viewMatrix to be rigid (and thus affine).
|
||||
@@ -1224,9 +1245,6 @@ ShadowMap::SceneInfo::SceneInfo(
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
wsShadowReceiversVolume.min = min(wsShadowReceiversVolume.min, receiver.min);
|
||||
wsShadowReceiversVolume.max = max(wsShadowReceiversVolume.max, receiver.max);
|
||||
auto r = Aabb::transform(viewMatrix.upperLeft(), viewMatrix[3].xyz, receiver);
|
||||
vsNearFar.x = std::max(vsNearFar.x, r.max.z);
|
||||
vsNearFar.y = std::min(vsNearFar.y, r.min.z);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,20 +109,13 @@ public:
|
||||
struct SceneInfo {
|
||||
|
||||
SceneInfo() noexcept = default;
|
||||
SceneInfo(FScene const& scene, uint8_t visibleLayers, math::mat4f const& viewMatrix) noexcept;
|
||||
|
||||
// scratch data: The near and far planes, in clip space, to use for this shadow map
|
||||
math::float2 csNearFar = { -1.0f, 1.0f };
|
||||
SceneInfo(FScene const& scene, uint8_t visibleLayers) noexcept;
|
||||
|
||||
// scratch data: light's near/far expressed in light-space, calculated from the scene's
|
||||
// content assuming the light is at the origin.
|
||||
math::float2 lsCastersNearFar;
|
||||
math::float2 lsReceiversNearFar;
|
||||
|
||||
// Viewing camera's near/far expressed in view-space, calculated from the
|
||||
// scene's content.
|
||||
math::float2 vsNearFar;
|
||||
|
||||
// World-space shadow-casters volume
|
||||
Aabb wsShadowCastersVolume;
|
||||
|
||||
@@ -154,7 +147,8 @@ public:
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
ShadowMapInfo const& shadowMapInfo,
|
||||
SceneInfo const& sceneInfo) noexcept;
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept;
|
||||
|
||||
ShaderParameters updateSpot(FEngine& engine,
|
||||
const FScene::LightSoa& lightData, size_t index,
|
||||
@@ -244,7 +238,8 @@ private:
|
||||
math::float3 direction,
|
||||
FLightManager::ShadowParams params,
|
||||
filament::CameraInfo const& camera,
|
||||
SceneInfo const& sceneInfo) noexcept;
|
||||
SceneInfo const& sceneInfo,
|
||||
bool useDepthClamp) noexcept;
|
||||
|
||||
static math::mat4f applyLISPSM(math::mat4f& Wp,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
@@ -260,7 +255,7 @@ private:
|
||||
math::mat4f const& LMpMv,
|
||||
math::mat4f const& WLMp,
|
||||
FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount,
|
||||
filament::CameraInfo const& camera, math::float2 const& csNearFar,
|
||||
filament::CameraInfo const& camera,
|
||||
float shadowFar, bool stable) noexcept;
|
||||
|
||||
static inline void snapLightFrustum(math::float2& s, math::float2& o,
|
||||
@@ -271,8 +266,7 @@ private:
|
||||
SceneInfo const& sceneInfo,
|
||||
bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept;
|
||||
|
||||
static Corners computeFrustumCorners(const math::mat4f& projectionInverse,
|
||||
math::float2 csNearFar = { -1.0f, 1.0f }) noexcept;
|
||||
static Corners computeFrustumCorners(const math::mat4f& projectionInverse) noexcept;
|
||||
|
||||
static inline math::float2 computeNearFar(math::mat4f const& view,
|
||||
math::float3 const* wsVertices, size_t count) noexcept;
|
||||
@@ -307,7 +301,7 @@ private:
|
||||
|
||||
static size_t intersectFrustumWithBox(
|
||||
FrustumBoxIntersection& outVertices,
|
||||
math::mat4f const& projection, math::float2 const& csNearFar,
|
||||
math::mat4f const& projection,
|
||||
Aabb const& box);
|
||||
|
||||
static math::mat4f warpFrustum(float n, float f) noexcept;
|
||||
|
||||
@@ -39,17 +39,20 @@
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <math/half.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec4.h>
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
@@ -63,12 +66,15 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace math;
|
||||
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine) {
|
||||
ShadowMapManager::ShadowMapManager(FEngine& engine)
|
||||
: mIsDepthClampSupported(engine.getDriverApi().isDepthClampSupported()) {
|
||||
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
|
||||
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
|
||||
&engine.debug.shadowmap.visualize_cascades);
|
||||
debugRegistry.registerProperty("d.shadowmap.tightly_bound_scene",
|
||||
&engine.debug.shadowmap.tightly_bound_scene);
|
||||
debugRegistry.registerProperty("d.shadowmap.disable_light_frustum_align",
|
||||
&engine.debug.shadowmap.disable_light_frustum_align);
|
||||
debugRegistry.registerProperty("d.shadowmap.depth_clamp",
|
||||
&engine.debug.shadowmap.depth_clamp);
|
||||
}
|
||||
|
||||
ShadowMapManager::~ShadowMapManager() {
|
||||
@@ -147,7 +153,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
|
||||
// Compute scene-dependent values shared across all shadow maps
|
||||
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers(), cameraInfo.view };
|
||||
ShadowMap::SceneInfo const info{ *view.getScene(), view.getVisibleLayers() };
|
||||
|
||||
shadowTechnique |= updateCascadeShadowMaps(
|
||||
engine, view, cameraInfo, renderableData, lightData, info);
|
||||
@@ -361,7 +367,22 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
|
||||
// generate and sort the commands for rendering the shadow map
|
||||
|
||||
RenderPass::RenderFlags renderPassFlags{};
|
||||
if (view.isFrontFaceWindingInverted()) {
|
||||
renderPassFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
|
||||
}
|
||||
|
||||
bool const canUseDepthClamp =
|
||||
!view.hasVSM() &&
|
||||
mIsDepthClampSupported &&
|
||||
engine.debug.shadowmap.depth_clamp;
|
||||
|
||||
if (canUseDepthClamp) {
|
||||
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
|
||||
}
|
||||
|
||||
RenderPass const pass = passBuilder
|
||||
.renderFlags(renderPassFlags)
|
||||
.camera(cameraInfo)
|
||||
.visibilityMask(entry.visibilityMask)
|
||||
.geometry(scene->getRenderableData(),
|
||||
@@ -543,65 +564,9 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
|
||||
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf;
|
||||
if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) {
|
||||
cameraInfo.zf = params.options.shadowFar;
|
||||
float const n = cameraInfo.zn;
|
||||
float const f = cameraInfo.zf;
|
||||
|
||||
/*
|
||||
* Updating a projection matrix near and far planes:
|
||||
*
|
||||
* We assume that the near and far plane equations are of the form:
|
||||
* N = { 0, 0, 1, n }
|
||||
* F = { 0, 0, -1, -f }
|
||||
*
|
||||
* We also assume that the lower-left 2x2 of the projection is all 0:
|
||||
* P = A 0 C 0
|
||||
* 0 B D 0
|
||||
* 0 0 E F
|
||||
* 0 0 G H
|
||||
*
|
||||
* It result that we need to calculate E and F while leaving all other parameter unchanged.
|
||||
*
|
||||
* We know that:
|
||||
* with N, F the near/far normalized plane equation parameters
|
||||
* sn, sf arbitrary scale factors (they don't affect the planes)
|
||||
* m is the transpose of projection (see Frustum.cpp)
|
||||
*
|
||||
* sn * N == -m[3] - m[2]
|
||||
* sf * F == -m[3] + m[2]
|
||||
*
|
||||
* sn * N + sf * F == -2 * m[3]
|
||||
* sn * N - sf * F == -2 * m[2]
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * m[3].z
|
||||
* sn * N.w + sf * F.w == -2 * m[3].w
|
||||
* sn * N.z - sf * F.z == -2 * m[2].z
|
||||
* sn * N.w - sf * F.w == -2 * m[2].w
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * p[2].w
|
||||
* sn * N.w + sf * F.w == -2 * p[3].w
|
||||
* sn * N.z - sf * F.z == -2 * p[2].z
|
||||
* sn * N.w - sf * F.w == -2 * p[3].z
|
||||
*
|
||||
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
|
||||
*
|
||||
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
|
||||
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
|
||||
* p[2].z == (sf * F.z - sn * N.z) / 2
|
||||
* p[3].z == (sf * F.w - sn * N.w) / 2
|
||||
*/
|
||||
auto& p = cameraInfo.cullingProjection;
|
||||
float4 const N = { 0, 0, 1, n }; // near plane equation
|
||||
float4 const F = { 0, 0, -1, -f }; // far plane equation
|
||||
// near plane equation scale factor
|
||||
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
|
||||
// far plane equation scale factor
|
||||
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
|
||||
// New values for the projection
|
||||
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
|
||||
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
|
||||
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
|
||||
}
|
||||
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
@@ -631,8 +596,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo);
|
||||
|
||||
// we always do culling without depth clamp, because objects behind the camera
|
||||
// must be rendered regardless
|
||||
shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, false);
|
||||
|
||||
hasVisibleShadows = shadowMap.hasVisibleShadows();
|
||||
|
||||
@@ -648,15 +615,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
uint32_t cascadeHasVisibleShadows = 0;
|
||||
|
||||
if (hasVisibleShadows) {
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
float vsNear = -cameraInfo.zn;
|
||||
float vsFar = -cameraInfo.zf;
|
||||
if (engine.debug.shadowmap.tightly_bound_scene && !params.options.stable) {
|
||||
vsNear = std::min(vsNear, sceneInfo.vsNearFar.x);
|
||||
vsFar = std::max(vsFar, sceneInfo.vsNearFar.y);
|
||||
}
|
||||
|
||||
const size_t cascadeCount = cascadedShadowMaps.size();
|
||||
uint32_t const cascadeCount = cascadedShadowMaps.size();
|
||||
|
||||
// We divide the camera frustum into N cascades. This gives us N + 1 split positions.
|
||||
// The first split position is the near plane; the last split position is the far plane.
|
||||
@@ -667,9 +626,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
}
|
||||
|
||||
const CascadeSplits splits({
|
||||
.proj = cameraInfo.cullingProjection,
|
||||
.near = vsNear,
|
||||
.far = vsFar,
|
||||
.near = -cameraInfo.zn,
|
||||
.far = -cameraInfo.zf,
|
||||
.cascadeCount = cascadeCount,
|
||||
.splitPositions = splitPercentages
|
||||
});
|
||||
@@ -679,10 +637,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
static_assert(CONFIG_MAX_SHADOW_CASCADES <= 5,
|
||||
"At most, a float4 can fit 4 split positions for 5 shadow cascades");
|
||||
float4 wsSplitPositionUniform{ -std::numeric_limits<float>::infinity() };
|
||||
std::copy(splits.beginWs() + 1, splits.endWs(), &wsSplitPositionUniform[0]);
|
||||
|
||||
float csSplitPosition[CONFIG_MAX_SHADOW_CASCADES + 1];
|
||||
std::copy(splits.beginCs(), splits.endCs(), csSplitPosition);
|
||||
std::copy(splits.begin() + 1, splits.end(), &wsSplitPositionUniform[0]);
|
||||
|
||||
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
|
||||
|
||||
@@ -695,10 +650,20 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
ShadowMap& shadowMap = cascadedShadowMaps[i];
|
||||
assert_invariant(shadowMap.getLightIndex() == 0);
|
||||
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
// update cameraInfo culling projection for the cascade
|
||||
float const* nearFarPlanes = splits.begin();
|
||||
cameraInfo.zn = -nearFarPlanes[i];
|
||||
cameraInfo.zf = -nearFarPlanes[i + 1];
|
||||
updateNearFarPlanes(&cameraInfo.cullingProjection, cameraInfo.zn, cameraInfo.zf);
|
||||
|
||||
bool const canUseDepthClamp =
|
||||
!view.hasVSM() &&
|
||||
mIsDepthClampSupported &&
|
||||
engine.debug.shadowmap.depth_clamp;
|
||||
|
||||
auto shaderParameters = shadowMap.updateDirectional(engine,
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo);
|
||||
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo,
|
||||
canUseDepthClamp);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
@@ -1069,8 +1034,78 @@ ShadowMapManager::CascadeSplits::CascadeSplits(Params const& params) noexcept
|
||||
: mSplitCount(params.cascadeCount + 1) {
|
||||
for (size_t s = 0; s < mSplitCount; s++) {
|
||||
mSplitsWs[s] = params.near + (params.far - params.near) * params.splitPositions[s];
|
||||
mSplitsCs[s] = mat4f::project(params.proj, float3(0.0f, 0.0f, mSplitsWs[s])).z;
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMapManager::updateNearFarPlanes(mat4f* projection,
|
||||
float nearDistance, float farDistance) noexcept {
|
||||
float const n = nearDistance;
|
||||
float const f = farDistance;
|
||||
|
||||
/*
|
||||
* Updating a projection matrix near and far planes:
|
||||
*
|
||||
* We assume that the near and far plane equations are of the form:
|
||||
* N = { 0, 0, 1, n }
|
||||
* F = { 0, 0, -1, -f }
|
||||
*
|
||||
* We also assume that the lower-left 2x2 of the projection is all 0:
|
||||
* P = A 0 C 0
|
||||
* 0 B D 0
|
||||
* 0 0 E F
|
||||
* 0 0 G H
|
||||
*
|
||||
* It result that we need to calculate E and F while leaving all other parameter unchanged.
|
||||
*
|
||||
* We know that:
|
||||
* with N, F the near/far normalized plane equation parameters
|
||||
* sn, sf arbitrary scale factors (they don't affect the planes)
|
||||
* m is the transpose of projection (see Frustum.cpp)
|
||||
*
|
||||
* sn * N == -m[3] - m[2]
|
||||
* sf * F == -m[3] + m[2]
|
||||
*
|
||||
* sn * N + sf * F == -2 * m[3]
|
||||
* sn * N - sf * F == -2 * m[2]
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * m[3].z
|
||||
* sn * N.w + sf * F.w == -2 * m[3].w
|
||||
* sn * N.z - sf * F.z == -2 * m[2].z
|
||||
* sn * N.w - sf * F.w == -2 * m[2].w
|
||||
*
|
||||
* sn * N.z + sf * F.z == -2 * p[2].w
|
||||
* sn * N.w + sf * F.w == -2 * p[3].w
|
||||
* sn * N.z - sf * F.z == -2 * p[2].z
|
||||
* sn * N.w - sf * F.w == -2 * p[3].z
|
||||
*
|
||||
* We now need to solve for { p[2].z, p[3].z, sn, sf } :
|
||||
*
|
||||
* sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z)
|
||||
* sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z)
|
||||
* p[2].z == (sf * F.z - sn * N.z) / 2
|
||||
* p[3].z == (sf * F.w - sn * N.w) / 2
|
||||
*/
|
||||
auto& p = *projection;
|
||||
float4 const N = { 0, 0, 1, n }; // near plane equation
|
||||
float4 const F = { 0, 0, -1, -f }; // far plane equation
|
||||
// near plane equation scale factor
|
||||
float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z);
|
||||
// far plane equation scale factor
|
||||
float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z);
|
||||
// New values for the projection
|
||||
p[2].z = (sf * F.z - sn * N.z) * 0.5f;
|
||||
p[3].z = (sf * F.w - sn * N.w) * 0.5f;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<Camera const*>
|
||||
ShadowMapManager::getDirectionalShadowCameras() const noexcept {
|
||||
if (!mInitialized) return {};
|
||||
auto const csm = getCascadedShadowMap();
|
||||
auto result = utils::FixedCapacityVector<Camera const*>::with_capacity(csm.size());
|
||||
for (ShadowMap const& sm : csm) {
|
||||
result.push_back(sm.hasVisibleShadows() ? sm.getDebugCamera() : nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
@@ -58,6 +59,7 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Camera;
|
||||
class FCamera;
|
||||
class FView;
|
||||
class FrameGraph;
|
||||
@@ -138,16 +140,16 @@ public:
|
||||
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
|
||||
|
||||
// for debugging only
|
||||
FCamera const* getDirectionalShadowCamera() const noexcept {
|
||||
if (!mInitialized) return nullptr;
|
||||
return getShadowMap(0).getDebugCamera();
|
||||
}
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
|
||||
private:
|
||||
explicit ShadowMapManager(FEngine& engine);
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
static void updateNearFarPlanes(math::mat4f* projection,
|
||||
float nearDistance, float farDistance) noexcept;
|
||||
|
||||
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
|
||||
@@ -186,27 +188,21 @@ private:
|
||||
constexpr static size_t SPLIT_COUNT = CONFIG_MAX_SHADOW_CASCADES + 1;
|
||||
|
||||
struct Params {
|
||||
math::mat4f proj;
|
||||
float near = 0.0f;
|
||||
float far = 0.0f;
|
||||
size_t cascadeCount = 1;
|
||||
uint32_t cascadeCount = 1;
|
||||
std::array<float, SPLIT_COUNT> splitPositions = { 0.0f };
|
||||
};
|
||||
|
||||
explicit CascadeSplits(Params const& params) noexcept;
|
||||
|
||||
// Split positions in world-space.
|
||||
const float* beginWs() const { return mSplitsWs; }
|
||||
const float* endWs() const { return mSplitsWs + mSplitCount; }
|
||||
|
||||
// Split positions in clip-space.
|
||||
const float* beginCs() const { return mSplitsCs; }
|
||||
const float* endCs() const { return mSplitsCs + mSplitCount; }
|
||||
const float* begin() const { return mSplitsWs; }
|
||||
const float* end() const { return mSplitsWs + mSplitCount; }
|
||||
|
||||
private:
|
||||
float mSplitsWs[SPLIT_COUNT];
|
||||
float mSplitsCs[SPLIT_COUNT];
|
||||
size_t mSplitCount;
|
||||
uint32_t mSplitCount;
|
||||
};
|
||||
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
@@ -236,6 +232,7 @@ private:
|
||||
ShadowMapCacheContainer mShadowMapCache;
|
||||
uint32_t mDirectionalShadowMapCount = 0;
|
||||
uint32_t mSpotShadowMapCount = 0;
|
||||
bool const mIsDepthClampSupported;
|
||||
bool mInitialized = false;
|
||||
|
||||
ShadowMap& getShadowMap(size_t index) noexcept {
|
||||
@@ -248,10 +245,14 @@ private:
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getCascadedShadowMap() noexcept {
|
||||
ShadowMap* const p = &getShadowMap(0);
|
||||
ShadowMap const* const p = &getShadowMap(0);
|
||||
return { p, mDirectionalShadowMapCount };
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getCascadedShadowMap() const noexcept {
|
||||
return const_cast<ShadowMapManager*>(this)->getCascadedShadowMap();
|
||||
}
|
||||
|
||||
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
|
||||
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
|
||||
return { p, mSpotShadowMapCount };
|
||||
|
||||
@@ -73,8 +73,8 @@ const char* View::getName() const noexcept {
|
||||
return downcast(this)->getName();
|
||||
}
|
||||
|
||||
Camera const* View::getDirectionalShadowCamera() const noexcept {
|
||||
return downcast(this)->getDirectionalShadowCamera();
|
||||
utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() const noexcept {
|
||||
return downcast(this)->getDirectionalShadowCameras();
|
||||
}
|
||||
|
||||
void View::setShadowingEnabled(bool enabled) noexcept {
|
||||
|
||||
@@ -28,12 +28,6 @@
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define DEBUG_PROPERTIES_WRITABLE true
|
||||
#else
|
||||
# define DEBUG_PROPERTIES_WRITABLE false
|
||||
#endif
|
||||
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
@@ -79,17 +73,15 @@ bool FDebugRegistry::hasProperty(const char* name) const noexcept {
|
||||
|
||||
template<typename T>
|
||||
bool FDebugRegistry::setProperty(const char* name, T v) noexcept {
|
||||
if constexpr (DEBUG_PROPERTIES_WRITABLE) {
|
||||
auto info = getPropertyInfo(name);
|
||||
T* const addr = static_cast<T*>(info.first);
|
||||
if (addr) {
|
||||
auto old = *addr;
|
||||
*addr = v;
|
||||
if (info.second && old != v) {
|
||||
info.second();
|
||||
}
|
||||
return true;
|
||||
auto info = getPropertyInfo(name);
|
||||
T* const addr = static_cast<T*>(info.first);
|
||||
if (addr) {
|
||||
auto old = *addr;
|
||||
*addr = v;
|
||||
if (info.second && old != v) {
|
||||
info.second();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -118,19 +110,32 @@ template bool FDebugRegistry::getProperty<float2>(const char* name, float2* v) c
|
||||
template bool FDebugRegistry::getProperty<float3>(const char* name, float3* v) const noexcept;
|
||||
template bool FDebugRegistry::getProperty<float4>(const char* name, float4* v) const noexcept;
|
||||
|
||||
void FDebugRegistry::registerDataSource(std::string_view name,
|
||||
bool FDebugRegistry::registerDataSource(std::string_view name,
|
||||
void const* data, size_t count) noexcept {
|
||||
auto& dataSourceMap = mDataSourceMap;
|
||||
if (dataSourceMap.find(name) == dataSourceMap.end()) {
|
||||
bool const found = dataSourceMap.find(name) == dataSourceMap.end();
|
||||
if (found) {
|
||||
dataSourceMap[name] = { data, count };
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void FDebugRegistry::registerDataSource(std::string_view name,
|
||||
bool FDebugRegistry::registerDataSource(std::string_view name,
|
||||
utils::Invocable<DataSource()>&& creator) noexcept {
|
||||
mDataSourceCreatorMap[name] = std::move(creator);
|
||||
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
|
||||
bool const found = dataSourceCreatorMap.find(name) == dataSourceCreatorMap.end();
|
||||
if (found) {
|
||||
dataSourceCreatorMap[name] = std::move(creator);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void FDebugRegistry::unregisterDataSource(std::string_view name) noexcept {
|
||||
mDataSourceCreatorMap.erase(name);
|
||||
mDataSourceMap.erase(name);
|
||||
}
|
||||
|
||||
|
||||
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
|
||||
std::string_view const key{ name };
|
||||
auto& dataSourceMap = mDataSourceMap;
|
||||
|
||||
@@ -101,12 +101,14 @@ public:
|
||||
}
|
||||
|
||||
// registers a DataSource directly
|
||||
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
|
||||
bool registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
|
||||
|
||||
// registers a DataSource lazily
|
||||
void registerDataSource(std::string_view name,
|
||||
bool registerDataSource(std::string_view name,
|
||||
utils::Invocable<DataSource()>&& creator) noexcept;
|
||||
|
||||
void unregisterDataSource(std::string_view name) noexcept;
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
private:
|
||||
#endif
|
||||
|
||||
@@ -590,7 +590,8 @@ private:
|
||||
Config mConfig;
|
||||
|
||||
public:
|
||||
// these are the debug properties used by FDebug. They're accessed directly by modules who need them.
|
||||
// These are the debug properties used by FDebug.
|
||||
// They're accessed directly by modules who need them.
|
||||
struct {
|
||||
struct {
|
||||
bool debug_directional_shadowmap = false;
|
||||
@@ -598,7 +599,8 @@ public:
|
||||
bool far_uses_shadowcasters = true;
|
||||
bool focus_shadowcasters = true;
|
||||
bool visualize_cascades = false;
|
||||
bool tightly_bound_scene = true;
|
||||
bool disable_light_frustum_align = false;
|
||||
bool depth_clamp = true;
|
||||
float dzn = -1.0f;
|
||||
float dzf = 1.0f;
|
||||
float display_shadow_texture_scale = 0.25f;
|
||||
|
||||
@@ -31,15 +31,17 @@
|
||||
#include "details/Skybox.h"
|
||||
|
||||
#include <filament/Exposure.h>
|
||||
#include <filament/DebugRegistry.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <private/filament/UibStructs.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Zip2Iterator.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
@@ -47,6 +49,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -78,21 +81,29 @@ FView::FView(FEngine& engine)
|
||||
mPidController.setOutputDeadBand(-0.01f, 0.05f);
|
||||
|
||||
#ifndef NDEBUG
|
||||
debugRegistry.registerDataSource("d.view.frame_info",
|
||||
[this]() -> DebugRegistry::DataSource {
|
||||
assert_invariant(!mDebugFrameHistory);
|
||||
mDebugFrameHistory = std::make_unique<std::array<DebugRegistry::FrameHistory, 5*60>>();
|
||||
return { mDebugFrameHistory->data(), mDebugFrameHistory->size() };
|
||||
// This can fail if another view has already registered this data source
|
||||
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
|
||||
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
|
||||
// the View could have been destroyed by the time we do this
|
||||
auto const state = weak.lock();
|
||||
if (!state) {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
// Lazily allocate the buffer for the debug data source, and mark this
|
||||
// data source as active. It can never go back to inactive.
|
||||
assert_invariant(!state->debugFrameHistory);
|
||||
state->active = true;
|
||||
state->debugFrameHistory =
|
||||
std::make_unique<std::array<DebugRegistry::FrameHistory, 5 * 60>>();
|
||||
return { state->debugFrameHistory->data(), state->debugFrameHistory->size() };
|
||||
});
|
||||
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
|
||||
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
|
||||
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
|
||||
// default parameters for debugging UI
|
||||
engine.debug.view.pid.kp = 1.0f - std::exp(-1.0f / 8.0f);
|
||||
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
|
||||
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
|
||||
mPidController.setParallelGains(
|
||||
engine.debug.view.pid.kp, engine.debug.view.pid.ki, engine.debug.view.pid.kd);
|
||||
|
||||
if (UTILS_UNLIKELY(mDebugState->owner)) {
|
||||
// publish the properties (they will be initialized in the main loop)
|
||||
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
|
||||
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
|
||||
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
|
||||
}
|
||||
#endif
|
||||
|
||||
// allocate UBOs
|
||||
@@ -126,6 +137,12 @@ void FView::terminate(FEngine& engine) {
|
||||
mFroxelizer.terminate(driver);
|
||||
|
||||
engine.getEntityManager().destroy(mFogEntity);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (UTILS_UNLIKELY(mDebugState->owner)) {
|
||||
engine.getDebugRegistry().unregisterDataSource("d.view.frame_info");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FView::setViewport(filament::Viewport const& viewport) noexcept {
|
||||
@@ -167,6 +184,16 @@ float2 FView::updateScale(FEngine& engine,
|
||||
Renderer::FrameRateOptions const& frameRateOptions,
|
||||
Renderer::DisplayInfo const& displayInfo) noexcept {
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (UTILS_LIKELY(!mDebugState->active)) {
|
||||
// if we're not active, update the debug properties with the normal values
|
||||
// and use that for configuring the PID controller.
|
||||
engine.debug.view.pid.kp = 1.0f - std::exp(-frameRateOptions.scaleRate);
|
||||
engine.debug.view.pid.ki = PID_CONTROLLER_Ki;
|
||||
engine.debug.view.pid.kd = PID_CONTROLLER_Kd;
|
||||
}
|
||||
#endif
|
||||
|
||||
DynamicResolutionOptions const& options = mDynamicResolution;
|
||||
if (options.enabled) {
|
||||
if (!UTILS_UNLIKELY(info.valid)) {
|
||||
@@ -250,14 +277,15 @@ float2 FView::updateScale(FEngine& engine,
|
||||
|
||||
#ifndef NDEBUG
|
||||
// only for debugging...
|
||||
if (mDebugFrameHistory) {
|
||||
if (UTILS_UNLIKELY(mDebugState->active && mDebugState->debugFrameHistory)) {
|
||||
auto* const debugFrameHistory = mDebugState->debugFrameHistory.get();
|
||||
using namespace std::chrono;
|
||||
using duration_ms = duration<float, std::milli>;
|
||||
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
|
||||
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
|
||||
std::move(mDebugFrameHistory->begin() + 1,
|
||||
mDebugFrameHistory->end(), mDebugFrameHistory->begin());
|
||||
mDebugFrameHistory->back() = {
|
||||
std::move(debugFrameHistory->begin() + 1,
|
||||
debugFrameHistory->end(), debugFrameHistory->begin());
|
||||
debugFrameHistory->back() = {
|
||||
.target = target,
|
||||
.targetWithHeadroom = targetWithHeadroom,
|
||||
.frameTime = duration_cast<duration_ms>(info.frameTime).count(),
|
||||
|
||||
@@ -201,9 +201,9 @@ public:
|
||||
|
||||
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
|
||||
|
||||
FCamera const* getDirectionalShadowCamera() const noexcept {
|
||||
if (!mShadowMapManager) return nullptr;
|
||||
return mShadowMapManager->getDirectionalShadowCamera();
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept {
|
||||
if (!mShadowMapManager) return {};
|
||||
return mShadowMapManager->getDirectionalShadowCameras();
|
||||
}
|
||||
|
||||
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
|
||||
@@ -576,7 +576,12 @@ private:
|
||||
}};
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> mDebugFrameHistory;
|
||||
struct DebugState {
|
||||
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> debugFrameHistory{};
|
||||
bool owner = false;
|
||||
bool active = false;
|
||||
};
|
||||
std::shared_ptr<DebugState> mDebugState{ new DebugState };
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -190,10 +190,10 @@ void RenderPassNode::resolve() noexcept {
|
||||
minHeight = std::min(minHeight, h);
|
||||
maxHeight = std::max(maxHeight, h);
|
||||
}
|
||||
// additionally, clear implies discardStart
|
||||
rt.backend.params.flags.discardStart |= (
|
||||
rt.descriptor.clearFlags & rt.targetBufferFlags);
|
||||
}
|
||||
// additionally, clear implies discardStart
|
||||
rt.backend.params.flags.discardStart |= (
|
||||
rt.descriptor.clearFlags & rt.targetBufferFlags);
|
||||
|
||||
assert_invariant(minWidth == maxWidth);
|
||||
assert_invariant(minHeight == maxHeight);
|
||||
|
||||
@@ -323,6 +323,9 @@ public:
|
||||
//! Custom variables (all float4).
|
||||
MaterialBuilder& variable(Variable v, const char* name) noexcept;
|
||||
|
||||
MaterialBuilder& variable(Variable v, const char* name,
|
||||
ParameterPrecision precision) noexcept;
|
||||
|
||||
/**
|
||||
* Require a specified attribute.
|
||||
*
|
||||
@@ -708,11 +711,17 @@ public:
|
||||
ShaderStage stage;
|
||||
};
|
||||
|
||||
struct CustomVariable {
|
||||
utils::CString name;
|
||||
Precision precision = Precision::DEFAULT;
|
||||
bool hasPrecision = false;
|
||||
};
|
||||
|
||||
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
|
||||
using Property = filament::Property;
|
||||
|
||||
using PropertyList = bool[MATERIAL_PROPERTIES_COUNT];
|
||||
using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT];
|
||||
using VariableList = CustomVariable[MATERIAL_VARIABLES_COUNT];
|
||||
using OutputList = std::vector<Output>;
|
||||
|
||||
static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
|
||||
|
||||
@@ -671,13 +671,15 @@ void GLSLPostProcessor::fixupClipDistance(
|
||||
// - triggers a crash on some Adreno drivers (b/291140208, b/289401984, b/289393290)
|
||||
// However Metal requires this pass in order to correctly generate half-precision MSL
|
||||
//
|
||||
// CreateSimplificationPass() creates a lot of problems:
|
||||
// Note: CreateSimplificationPass() used to creates a lot of problems:
|
||||
// - Adreno GPU show artifacts after running simplification passes (Vulkan)
|
||||
// - spirv-cross fails generating working glsl
|
||||
// (https://github.com/KhronosGroup/SPIRV-Cross/issues/2162)
|
||||
// - generally it makes the code more complicated, e.g.: replacing for loops with
|
||||
// while-if-break, unclear if it helps for anything.
|
||||
// However, the simplification passes below are necessary when targeting Metal, otherwise the
|
||||
//
|
||||
// However this problem was addressed in spirv-cross here:
|
||||
// https://github.com/KhronosGroup/SPIRV-Cross/pull/2163
|
||||
//
|
||||
// The simplification passes below are necessary when targeting Metal, otherwise the
|
||||
// result is mismatched half / float assignments in MSL.
|
||||
|
||||
|
||||
@@ -710,11 +712,11 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
|
||||
RegisterPass(CreateAggressiveDCEPass());
|
||||
RegisterPass(CreateRedundancyEliminationPass());
|
||||
RegisterPass(CreateCombineAccessChainsPass());
|
||||
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
RegisterPass(CreateVectorDCEPass());
|
||||
RegisterPass(CreateDeadInsertElimPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
RegisterPass(CreateIfConversionPass());
|
||||
RegisterPass(CreateCopyPropagateArraysPass());
|
||||
RegisterPass(CreateReduceLoadSizePass());
|
||||
@@ -723,7 +725,7 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
|
||||
RegisterPass(CreateRedundancyEliminationPass());
|
||||
RegisterPass(CreateDeadBranchElimPass());
|
||||
RegisterPass(CreateBlockMergePass());
|
||||
RegisterPass(CreateSimplificationPass(), MaterialBuilder::TargetApi::METAL);
|
||||
RegisterPass(CreateSimplificationPass());
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& config) {
|
||||
|
||||
@@ -234,7 +234,21 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
|
||||
case Variable::CUSTOM2:
|
||||
case Variable::CUSTOM3:
|
||||
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
|
||||
mVariables[size_t(v)] = CString(name);
|
||||
mVariables[size_t(v)] = { CString(name), Precision::DEFAULT, false };
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::variable(Variable v,
|
||||
const char* name, ParameterPrecision precision) noexcept {
|
||||
switch (v) {
|
||||
case Variable::CUSTOM0:
|
||||
case Variable::CUSTOM1:
|
||||
case Variable::CUSTOM2:
|
||||
case Variable::CUSTOM3:
|
||||
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
|
||||
mVariables[size_t(v)] = { CString(name), precision, true };
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
@@ -1383,7 +1397,7 @@ bool MaterialBuilder::checkMaterialLevelFeatures(MaterialInfo const& info) const
|
||||
|
||||
bool MaterialBuilder::hasCustomVaryings() const noexcept {
|
||||
for (const auto& variable : mVariables) {
|
||||
if (!variable.empty()) {
|
||||
if (!variable.name.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,15 +451,20 @@ io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderStag
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderStage stage,
|
||||
const CString& name, size_t index) {
|
||||
|
||||
const MaterialBuilder::CustomVariable& variable, size_t index) {
|
||||
auto const& name = variable.name;
|
||||
const char* precisionString = getPrecisionQualifier(variable.precision);
|
||||
if (!name.empty()) {
|
||||
if (stage == ShaderStage::VERTEX) {
|
||||
out << "\n#define VARIABLE_CUSTOM" << index << " " << name.c_str() << "\n";
|
||||
out << "\n#define VARIABLE_CUSTOM_AT" << index << " variable_" << name.c_str() << "\n";
|
||||
out << "LAYOUT_LOCATION(" << index << ") VARYING vec4 variable_" << name.c_str() << ";\n";
|
||||
out << "LAYOUT_LOCATION(" << index << ") VARYING " << precisionString << " vec4 variable_" << name.c_str() << ";\n";
|
||||
} else if (stage == ShaderStage::FRAGMENT) {
|
||||
out << "\nLAYOUT_LOCATION(" << index << ") VARYING highp vec4 variable_" << name.c_str() << ";\n";
|
||||
if (!variable.hasPrecision && variable.precision == Precision::DEFAULT) {
|
||||
// for backward compatibility
|
||||
precisionString = "highp";
|
||||
}
|
||||
out << "\nLAYOUT_LOCATION(" << index << ") VARYING " << precisionString << " vec4 variable_" << name.c_str() << ";\n";
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
|
||||
// generate declarations for custom interpolants
|
||||
static utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderStage stage,
|
||||
const utils::CString& name, size_t index);
|
||||
const MaterialBuilder::CustomVariable& variable, size_t index);
|
||||
|
||||
// generate declarations for non-custom "in" variables
|
||||
utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderStage type,
|
||||
|
||||
@@ -28,8 +28,14 @@
|
||||
|
||||
class Cube {
|
||||
public:
|
||||
|
||||
Cube(filament::Engine& engine, filament::Material const* material, filament::math::float3 linearColor, bool culling = true);
|
||||
|
||||
Cube(Cube const&) = delete;
|
||||
Cube& operator=(Cube const&) = delete;
|
||||
|
||||
Cube(Cube&& rhs) noexcept;
|
||||
|
||||
utils::Entity getSolidRenderable() {
|
||||
return mSolidRenderable;
|
||||
}
|
||||
@@ -55,8 +61,8 @@ private:
|
||||
filament::Material const* mMaterial = nullptr;
|
||||
filament::MaterialInstance* mMaterialInstanceSolid = nullptr;
|
||||
filament::MaterialInstance* mMaterialInstanceWireFrame = nullptr;
|
||||
utils::Entity mSolidRenderable;
|
||||
utils::Entity mWireFrameRenderable;
|
||||
utils::Entity mSolidRenderable{};
|
||||
utils::Entity mWireFrameRenderable{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -211,11 +211,11 @@ private:
|
||||
filament::Camera* mOrthoCamera;
|
||||
|
||||
std::vector<std::unique_ptr<CView>> mViews;
|
||||
CView* mMainView;
|
||||
CView* mUiView;
|
||||
CView* mMainView; // well, the main view
|
||||
CView* mUiView; // the imgui ui
|
||||
CView* mDepthView;
|
||||
GodView* mGodView;
|
||||
CView* mOrthoView;
|
||||
GodView* mGodView; // the debug view with "god" camera
|
||||
CView* mOrthoView; // directional shadow map view
|
||||
|
||||
size_t mWidth = 0;
|
||||
size_t mHeight = 0;
|
||||
|
||||
@@ -104,11 +104,23 @@ Cube::Cube(Engine& engine, filament::Material const* material, float3 linearColo
|
||||
.build(engine, mWireFrameRenderable);
|
||||
}
|
||||
|
||||
Cube::Cube(Cube&& rhs) noexcept
|
||||
: mEngine(rhs.mEngine) {
|
||||
using std::swap;
|
||||
swap(rhs.mVertexBuffer, mVertexBuffer);
|
||||
swap(rhs.mIndexBuffer, mIndexBuffer);
|
||||
swap(rhs.mMaterial, mMaterial);
|
||||
swap(rhs.mMaterialInstanceSolid, mMaterialInstanceSolid);
|
||||
swap(rhs.mMaterialInstanceWireFrame, mMaterialInstanceWireFrame);
|
||||
swap(rhs.mSolidRenderable, mSolidRenderable);
|
||||
swap(rhs.mWireFrameRenderable, mWireFrameRenderable);
|
||||
}
|
||||
|
||||
void Cube::mapFrustum(filament::Engine& engine, Camera const* camera) {
|
||||
// the Camera far plane is at infinity, but we want it closer for display
|
||||
const mat4 vm(camera->getModelMatrix());
|
||||
mat4 p(vm * inverse(camera->getCullingProjectionMatrix()));
|
||||
return mapFrustum(engine, p);
|
||||
mapFrustum(engine, p);
|
||||
}
|
||||
|
||||
void Cube::mapFrustum(filament::Engine& engine, filament::math::mat4 const& transform) {
|
||||
@@ -122,7 +134,7 @@ void Cube::mapFrustum(filament::Engine& engine, filament::math::mat4 const& tran
|
||||
|
||||
void Cube::mapAabb(filament::Engine& engine, filament::Box const& box) {
|
||||
mat4 p = mat4::translation(box.center) * mat4::scaling(box.halfExtent);
|
||||
return mapFrustum(engine, p);
|
||||
mapFrustum(engine, p);
|
||||
}
|
||||
|
||||
Cube::~Cube() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include <memory>
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
@@ -56,6 +55,13 @@
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "generated/resources/filamentapp.h"
|
||||
|
||||
using namespace filament;
|
||||
@@ -138,10 +144,17 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
.package(FILAMENTAPP_TRANSPARENTCOLOR_DATA, FILAMENTAPP_TRANSPARENTCOLOR_SIZE)
|
||||
.build(*mEngine);
|
||||
|
||||
std::unique_ptr<Cube> cameraCube(new Cube(*mEngine, mTransparentMaterial, {1,0,0}));
|
||||
std::unique_ptr<Cube> cameraCube{ new Cube(*mEngine, mTransparentMaterial, { 1, 0, 0 }) };
|
||||
|
||||
// we can't cull the light-frustum because it's not applied a rigid transform
|
||||
// and currently, filament assumes that for culling
|
||||
std::unique_ptr<Cube> lightmapCube(new Cube(*mEngine, mTransparentMaterial, {0,1,0}, false));
|
||||
std::vector<Cube> lightmapCubes;
|
||||
lightmapCubes.reserve(4);
|
||||
lightmapCubes.emplace_back(*mEngine, mTransparentMaterial, float3{ 0, 1, 0 }, false);
|
||||
lightmapCubes.emplace_back(*mEngine, mTransparentMaterial, float3{ 0, 0, 1 }, false);
|
||||
lightmapCubes.emplace_back(*mEngine, mTransparentMaterial, float3{ 1, 1, 0 }, false);
|
||||
lightmapCubes.emplace_back(*mEngine, mTransparentMaterial, float3{ 1, 0, 0 }, false);
|
||||
|
||||
mScene = mEngine->createScene();
|
||||
|
||||
window->mMainView->getView()->setVisibleLayers(0x4, 0x4);
|
||||
@@ -151,16 +164,15 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
|
||||
rcm.setLayerMask(rcm.getInstance(cameraCube->getSolidRenderable()), 0x3, 0x2);
|
||||
rcm.setLayerMask(rcm.getInstance(cameraCube->getWireFrameRenderable()), 0x3, 0x2);
|
||||
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCube->getSolidRenderable()), 0x3, 0x2);
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCube->getWireFrameRenderable()), 0x3, 0x2);
|
||||
|
||||
// Create the camera mesh
|
||||
mScene->addEntity(cameraCube->getWireFrameRenderable());
|
||||
mScene->addEntity(cameraCube->getSolidRenderable());
|
||||
|
||||
mScene->addEntity(lightmapCube->getWireFrameRenderable());
|
||||
mScene->addEntity(lightmapCube->getSolidRenderable());
|
||||
for (auto&& cube : lightmapCubes) {
|
||||
rcm.setLayerMask(rcm.getInstance(cube.getSolidRenderable()), 0x3, 0x2);
|
||||
rcm.setLayerMask(rcm.getInstance(cube.getWireFrameRenderable()), 0x3, 0x2);
|
||||
mScene->addEntity(cube.getWireFrameRenderable());
|
||||
mScene->addEntity(cube.getSolidRenderable());
|
||||
}
|
||||
|
||||
window->mDepthView->getView()->setVisibleLayers(0x4, 0x4);
|
||||
window->mGodView->getView()->setVisibleLayers(0x6, 0x6);
|
||||
@@ -425,10 +437,26 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
window->mDebugCamera->lookAt(eye, center, up);
|
||||
|
||||
// Update the cube distortion matrix used for frustum visualization.
|
||||
const Camera* lightmapCamera = window->mMainView->getView()->getDirectionalShadowCamera();
|
||||
if (lightmapCamera) {
|
||||
lightmapCube->mapFrustum(*mEngine, lightmapCamera);
|
||||
auto& rcm = mEngine->getRenderableManager();
|
||||
auto const csm = window->mMainView->getView()->getDirectionalShadowCameras();
|
||||
// show/hide the cascades
|
||||
for (size_t i = 0 ; i < 4; i++) {
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getSolidRenderable()), 0x3, 0x0);
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getWireFrameRenderable()), 0x3, 0x0);
|
||||
}
|
||||
if (!csm.empty()) {
|
||||
for (size_t i = 0, c = csm.size(); i < c; i++) {
|
||||
if (csm[i]) {
|
||||
lightmapCubes[i].mapFrustum(*mEngine, csm[i]);
|
||||
}
|
||||
uint8_t const layer = csm[i] ? 0x2 : 0x0;
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getSolidRenderable()),
|
||||
0x3, layer);
|
||||
rcm.setLayerMask(rcm.getInstance(lightmapCubes[i].getWireFrameRenderable()),
|
||||
0x3, layer);
|
||||
}
|
||||
}
|
||||
|
||||
cameraCube->mapFrustum(*mEngine, window->mMainCamera);
|
||||
|
||||
// Delay rendering for roughly one monitor refresh interval
|
||||
@@ -452,11 +480,14 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
|
||||
if (config.splitView) {
|
||||
if(!window->mOrthoView->getView()->hasCamera()) {
|
||||
Camera const* debugDirectionalShadowCamera =
|
||||
window->mMainView->getView()->getDirectionalShadowCamera();
|
||||
if (debugDirectionalShadowCamera) {
|
||||
window->mOrthoView->setCamera(
|
||||
const_cast<Camera*>(debugDirectionalShadowCamera));
|
||||
auto const csm = window->mMainView->getView()->getDirectionalShadowCameras();
|
||||
if (!csm.empty()) {
|
||||
// here we could choose the cascade
|
||||
Camera const* debugDirectionalShadowCamera = csm[0];
|
||||
if (debugDirectionalShadowCamera) {
|
||||
window->mOrthoView->setCamera(
|
||||
const_cast<Camera*>(debugDirectionalShadowCamera));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,7 +515,7 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
cleanupCallback(mEngine, window->mMainView->getView(), mScene);
|
||||
|
||||
cameraCube.reset();
|
||||
lightmapCube.reset();
|
||||
lightmapCubes.clear();
|
||||
window.reset();
|
||||
|
||||
mIBL.reset();
|
||||
@@ -736,12 +767,6 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
|
||||
mGodView->setCamera(mMainCamera);
|
||||
mGodView->setGodCamera(mDebugCamera);
|
||||
mGodView->setCameraManipulator(mDebugCameraMan);
|
||||
|
||||
// Ortho view obviously uses an ortho camera
|
||||
Camera const* debugDirectionalShadowCamera = mMainView->getView()->getDirectionalShadowCamera();
|
||||
if (debugDirectionalShadowCamera) {
|
||||
mOrthoView->setCamera(const_cast<Camera *>(debugDirectionalShadowCamera));
|
||||
}
|
||||
}
|
||||
|
||||
// configure the cameras
|
||||
@@ -907,9 +932,7 @@ void FilamentApp::Window::configureCamerasForWindow() {
|
||||
|
||||
const bool splitview = mViews.size() > 2;
|
||||
|
||||
// To trigger a floating-point exception, users could shrink the window to be smaller than
|
||||
// the sidebar. To prevent this we simply clamp the width of the main viewport.
|
||||
const uint32_t mainWidth = splitview ? width : std::max(1, (int) width - sidebar);
|
||||
const uint32_t mainWidth = std::max(2, (int) width - sidebar);
|
||||
|
||||
double near = mFilamentApp->mCameraNear;
|
||||
double far = mFilamentApp->mCameraFar;
|
||||
@@ -924,7 +947,7 @@ void FilamentApp::Window::configureCamerasForWindow() {
|
||||
} else {
|
||||
mMainCamera->setLensProjection(mFilamentApp->mCameraFocalLength, 1.0, near, far);
|
||||
}
|
||||
mDebugCamera->setProjection(45.0, double(width) / height, 0.0625, 4096, Camera::Fov::VERTICAL);
|
||||
mDebugCamera->setProjection(45.0, double(mainWidth) / height, 0.0625, 4096, Camera::Fov::VERTICAL);
|
||||
|
||||
auto aspectRatio = double(mainWidth) / height;
|
||||
if (mMainView->getView()->getStereoscopicOptions().enabled) {
|
||||
@@ -935,12 +958,12 @@ void FilamentApp::Window::configureCamerasForWindow() {
|
||||
|
||||
// We're in split view when there are more views than just the Main and UI views.
|
||||
if (splitview) {
|
||||
uint32_t vpw = width / 2;
|
||||
uint32_t vph = height / 2;
|
||||
mMainView->setViewport ({ 0, 0, vpw, vph });
|
||||
mDepthView->setViewport({ int32_t(vpw), 0, width - vpw, vph });
|
||||
mGodView->setViewport ({ int32_t(vpw), int32_t(vph), width - vpw, height - vph });
|
||||
mOrthoView->setViewport({ 0, int32_t(vph), vpw, height - vph });
|
||||
uint32_t const vpw = mainWidth / 2;
|
||||
uint32_t const vph = height / 2;
|
||||
mMainView->setViewport ({ sidebar + 0, 0, vpw, vph });
|
||||
mDepthView->setViewport({ sidebar + int32_t(vpw), 0, vpw, vph });
|
||||
mGodView->setViewport ({ sidebar + int32_t(vpw), int32_t(vph), vpw, vph });
|
||||
mOrthoView->setViewport({ sidebar + 0, int32_t(vph), vpw, vph });
|
||||
} else {
|
||||
mMainView->setViewport({ sidebar, 0, mainWidth, height });
|
||||
}
|
||||
|
||||
@@ -224,6 +224,9 @@ public:
|
||||
*/
|
||||
static void exportSettings(const Settings& settings, const char* filename);
|
||||
|
||||
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
|
||||
bool autoclose, AutomationEngine* automationEngine);
|
||||
|
||||
Options getOptions() const { return mOptions; }
|
||||
bool isRunning() const { return mIsRunning; }
|
||||
size_t currentTest() const { return mCurrentTest; }
|
||||
|
||||
@@ -56,7 +56,7 @@ static void convertRGBAtoRGB(void* buffer, uint32_t width, uint32_t height) {
|
||||
}
|
||||
}
|
||||
|
||||
static void exportScreenshot(View* view, Renderer* renderer, std::string filename,
|
||||
void AutomationEngine::exportScreenshot(View* view, Renderer* renderer, std::string filename,
|
||||
bool autoclose, AutomationEngine* automationEngine) {
|
||||
const Viewport& vp = view->getViewport();
|
||||
const size_t byteCount = vp.width * vp.height * 4;
|
||||
@@ -244,7 +244,8 @@ void AutomationEngine::tick(Engine* engine, const ViewerContent& content, float
|
||||
}
|
||||
|
||||
if (mOptions.exportScreenshots) {
|
||||
exportScreenshot(content.view, content.renderer, prefix + ".ppm", isLastTest, this);
|
||||
AutomationEngine::exportScreenshot(
|
||||
content.view, content.renderer, prefix + ".ppm", isLastTest, this);
|
||||
}
|
||||
|
||||
if (isLastTest) {
|
||||
|
||||
+25
-4
@@ -61,8 +61,10 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "generated/resources/gltf_demo.h"
|
||||
@@ -137,6 +139,8 @@ struct App {
|
||||
|
||||
AutomationSpec* automationSpec = nullptr;
|
||||
AutomationEngine* automationEngine = nullptr;
|
||||
bool screenshot = false;
|
||||
uint8_t screenshotSeq = 0;
|
||||
};
|
||||
|
||||
static const char* DEFAULT_IBL = "assets/ibl/lightroom_14b";
|
||||
@@ -877,10 +881,15 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (ImGui::CollapsingHeader("Debug")) {
|
||||
auto& debug = engine->getDebugRegistry();
|
||||
if (ImGui::Button("Capture frame")) {
|
||||
bool* captureFrame =
|
||||
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
|
||||
*captureFrame = true;
|
||||
if (engine->getBackend() == Engine::Backend::METAL) {
|
||||
if (ImGui::Button("Capture frame")) {
|
||||
bool* captureFrame =
|
||||
debug.getPropertyAddress<bool>("d.renderer.doFrameCapture");
|
||||
*captureFrame = true;
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Screenshot")) {
|
||||
app.screenshot = true;
|
||||
}
|
||||
ImGui::Checkbox("Disable buffer padding",
|
||||
debug.getPropertyAddress<bool>("d.renderer.disable_buffer_padding"));
|
||||
@@ -894,6 +903,10 @@ int main(int argc, char** argv) {
|
||||
debug.getPropertyAddress<bool>("d.shadowmap.far_uses_shadowcasters"));
|
||||
ImGui::Checkbox("Focus shadow casters",
|
||||
debug.getPropertyAddress<bool>("d.shadowmap.focus_shadowcasters"));
|
||||
ImGui::Checkbox("Disable light frustum alignment",
|
||||
debug.getPropertyAddress<bool>("d.shadowmap.disable_light_frustum_align"));
|
||||
ImGui::Checkbox("Depth clamp",
|
||||
debug.getPropertyAddress<bool>("d.shadowmap.depth_clamp"));
|
||||
|
||||
bool debugDirectionalShadowmap;
|
||||
if (debug.getProperty("d.shadowmap.debug_directional_shadowmap",
|
||||
@@ -1134,6 +1147,14 @@ int main(int argc, char** argv) {
|
||||
};
|
||||
|
||||
auto postRender = [&app](Engine* engine, View* view, Scene*, Renderer* renderer) {
|
||||
if (app.screenshot) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream << "screenshot" << std::setfill('0') << std::setw(2) << +app.screenshotSeq;
|
||||
AutomationEngine::exportScreenshot(
|
||||
view, renderer, stringStream.str() + ".ppm", false, app.automationEngine);
|
||||
++app.screenshotSeq;
|
||||
app.screenshot = false;
|
||||
}
|
||||
if (app.automationEngine->shouldClose()) {
|
||||
FilamentApp::get().close();
|
||||
return;
|
||||
|
||||
@@ -615,14 +615,51 @@ static bool processVariables(MaterialBuilder& builder, const JsonishValue& value
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < elements.size(); i++) {
|
||||
auto elementValue = elements[i];
|
||||
ParameterPrecision precision = ParameterPrecision::DEFAULT;
|
||||
MaterialBuilder::Variable v = intToVariable(i);
|
||||
if (elementValue->getType() != JsonishValue::Type::STRING) {
|
||||
std::string nameString;
|
||||
|
||||
auto elementValue = elements[i];
|
||||
if (elementValue->getType() == JsonishValue::Type::OBJECT) {
|
||||
|
||||
JsonishObject const& jsonObject = *elementValue->toJsonObject();
|
||||
|
||||
const JsonishValue* nameValue = jsonObject.getValue("name");
|
||||
if (!nameValue) {
|
||||
std::cerr << "variables: entry without 'name' key." << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (nameValue->getType() != JsonishValue::STRING) {
|
||||
std::cerr << "variables: name value must be STRING." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const JsonishValue* precisionValue = jsonObject.getValue("precision");
|
||||
if (precisionValue) {
|
||||
if (precisionValue->getType() != JsonishValue::STRING) {
|
||||
std::cerr << "variables: precision must be a STRING." << std::endl;
|
||||
return false;
|
||||
}
|
||||
auto precisionString = precisionValue->toJsonString();
|
||||
if (!Enums::isValid<ParameterPrecision>(precisionString->getString())){
|
||||
return logEnumIssue("variables", *precisionString, Enums::map<ParameterPrecision>());
|
||||
}
|
||||
}
|
||||
|
||||
nameString = nameValue->toJsonString()->getString();
|
||||
if (precisionValue) {
|
||||
precision = Enums::toEnum<ParameterPrecision>(
|
||||
precisionValue->toJsonString()->getString());
|
||||
}
|
||||
builder.variable(v, nameString.c_str(), precision);
|
||||
} else if (elementValue->getType() == JsonishValue::Type::STRING) {
|
||||
nameString = elementValue->toJsonString()->getString();
|
||||
builder.variable(v, nameString.c_str());
|
||||
} else {
|
||||
std::cerr << "variables: array index " << i << " is not a STRING. found:" <<
|
||||
JsonishValue::typeToString(elementValue->getType()) << std::endl;
|
||||
return false;
|
||||
}
|
||||
builder.variable(v, elementValue->toJsonString()->getString().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user