Compare commits

..
Author SHA1 Message Date
Powei Feng 6474cb0bcd vk: fixing renderstandalone 2024-10-09 16:27:50 -07:00
57 changed files with 645 additions and 882 deletions
@@ -1,17 +0,0 @@
name: 'Android Continuous'
inputs:
build-abi:
description: 'The target platform ABI'
required: true
default: 'armeabi-v7a'
runs:
using: "composite"
steps:
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous ${{ inputs.build-abi }}
shell: bash
+22 -25
View File
@@ -8,35 +8,32 @@ on:
- rc/**
jobs:
build-android-armv7:
name: build-android-armv7
build-android:
name: build-android
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
- uses: actions/setup-java@v3
with:
build-abi: armeabi-v7a
build-android-armv8a:
name: build-android-armv8a
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
distribution: 'temurin'
java-version: '17'
- name: Run build script
run: |
cd build/android && printf "y" | ./build.sh continuous
- uses: actions/upload-artifact@v1.0.0
with:
build-abi: arm64-v8a
build-android-x86_64:
name: build-android-x86_64
runs-on: macos-14
steps:
- uses: actions/checkout@v4.1.6
- name: Run Android Continuous
uses: ./.github/actions/android-continuous
name: filament-android
path: out/filament-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
build-abi: x86_64
name: filamat-android-full
path: out/filamat-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: gltfio-android-release
path: out/gltfio-android-release.aar
- uses: actions/upload-artifact@v1.0.0
with:
name: filament-utils-android-release
path: out/filament-utils-android-release.aar
+1 -3
View File
@@ -49,10 +49,8 @@ jobs:
distribution: 'temurin'
java-version: '17'
- name: Run build script
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
run: |
cd build/android && printf "y" | ./build.sh presubmit arm64-v8a
cd build/android && printf "y" | ./build.sh presubmit
build-ios:
name: build-iOS
+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/android && printf "y" | ./build.sh release armeabi-v7a,arm64-v8a,x86,x86_64
cd build/android && printf "y" | ./build.sh release
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
+12 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.54.4'
implementation 'com.google.android.filament:filament-android:1.54.1'
}
```
@@ -51,9 +51,19 @@ 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.54.4'
pod 'Filament', '~> 1.54.1'
```
### Snapshots
If you prefer to live on the edge, you can download a continuous build by following the following
steps:
1. Find the [commit](https://github.com/google/filament/commits/main) you're interested in.
2. Click the green check mark under the commit message.
3. Click on the _Details_ link for the platform you're interested in.
4. On the top left click _Summary_, then in the _Artifacts_ section choose the desired artifact.
## Documentation
- [Filament](https://google.github.io/filament/Filament.html), an in-depth explanation of
-7
View File
@@ -7,13 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.54.4
- Add support for multi-layered render target with array textures.
## v1.54.3
## v1.54.2
- Add a `name` API to Filament objects for debugging handle use-after-free assertions
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.54.4
VERSION_NAME=1.54.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+7 -15
View File
@@ -60,7 +60,13 @@ if [[ ! -d "${ANDROID_HOME}/ndk/$FILAMENT_NDK_VERSION" ]]; then
yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --licenses
${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager "ndk;$FILAMENT_NDK_VERSION"
fi
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
# Continuous builds will build everything
ANDROID_ABIS=
if [[ "$TARGET" == "presubmit" ]]; then
ANDROID_ABIS="-q arm64-v8a"
fi
# Build the Android sample-gltf-viewer APK during release.
BUILD_SAMPLES=
@@ -68,19 +74,5 @@ if [[ "$TARGET" == "release" ]]; then
BUILD_SAMPLES="-k sample-gltf-viewer"
fi
function build_android() {
local ABI=$1
# Do the following in two steps so that we do not run out of space
if [[ -n "${BUILD_DEBUG}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_DEBUG}
rm -rf out/cmake-android-debug-*
fi
if [[ -n "${BUILD_RELEASE}" ]]; then
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android -q ${ABI} -c ${BUILD_SAMPLES} ${GENERATE_ARCHIVES} ${BUILD_RELEASE}
rm -rf out/cmake-android-release-*
fi
}
pushd `dirname $0`/../.. > /dev/null
build_android $2
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION} ./build.sh -p android $ANDROID_ABIS -c $BUILD_SAMPLES $GENERATE_ARCHIVES $BUILD_DEBUG $BUILD_RELEASE
@@ -1058,7 +1058,7 @@ struct RasterState {
bool inverseFrontFaces : 1; // 31
//! padding, must be 0
bool depthClamp : 1; // 32
uint8_t padding : 1; // 32
};
uint32_t u = 0;
};
@@ -309,7 +309,6 @@ 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)
@@ -444,10 +443,12 @@ DECL_DRIVER_API_N(setPushConstant,
backend::PushConstantVariant, value)
DECL_DRIVER_API_N(insertEventMarker,
const char*, string)
const char*, string,
uint32_t, len = 0)
DECL_DRIVER_API_N(pushGroupMarker,
const char*, string)
const char*, string,
uint32_t, len = 0)
DECL_DRIVER_API_0(popGroupMarker)
@@ -208,8 +208,14 @@ public:
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (isPoolHandle(id)) {
// Truncate the age to get the debug tag
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
// Truncate the tag's age to N bits.
constexpr uint8_t N = 2; // support a history of 4 tags
constexpr uint8_t mask = (1 << N) - 1;
uint8_t const age = (id & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
uint8_t const newAge = age & mask;
uint32_t const key = (id & ~HANDLE_AGE_MASK) | (newAge << HANDLE_AGE_SHIFT);
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
mDebugTags[key] = std::move(tag);
@@ -220,8 +226,7 @@ public:
if (!isPoolHandle(id)) {
return "(no tag)";
}
uint32_t const key = id & ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) {
if (auto pos = mDebugTags.find(id); pos != mDebugTags.end()) {
return pos->second;
}
return "(no tag)";
@@ -344,24 +349,12 @@ private:
}
}
// number if bits allotted to the handle's age (currently 4 max)
static constexpr uint32_t HANDLE_AGE_BIT_COUNT = 4;
// number if bits allotted to the handle's debug tag (HANDLE_AGE_BIT_COUNT max)
static constexpr uint32_t HANDLE_DEBUG_TAG_BIT_COUNT = 2;
// bit shift for both the age and debug tag
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
// mask for the heap (vs pool) flag
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u;
// mask for the age
static constexpr uint32_t HANDLE_AGE_MASK =
((1 << HANDLE_AGE_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the debug tag
static constexpr uint32_t HANDLE_DEBUG_TAG_MASK =
((1 << HANDLE_DEBUG_TAG_BIT_COUNT) - 1) << HANDLE_AGE_SHIFT;
// mask for the index
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu;
static_assert(HANDLE_DEBUG_TAG_BIT_COUNT <= HANDLE_AGE_BIT_COUNT);
// we handle a 4 bits age per address
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
@@ -376,7 +369,7 @@ private:
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
uint32_t const tag = id & HANDLE_AGE_MASK;
uint32_t const tag = id & HANDLE_TAG_MASK;
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
return { static_cast<void*>(base + offset), tag };
}
@@ -391,7 +384,7 @@ private:
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_AGE_MASK;
id |= tag & HANDLE_TAG_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
return id;
}
+1 -1
View File
@@ -24,7 +24,7 @@
#include <utils/debug.h>
#include <utils/ostream.h>
#if !defined(WIN32) && !defined(__EMSCRIPTEN__)
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
# include <sys/mman.h>
# include <unistd.h>
# define HAS_MMAP 1
+2 -8
View File
@@ -20,16 +20,11 @@
#include <utils/CallStack.h>
#endif
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Profiler.h>
#include <utils/Systrace.h>
#include <cstddef>
#include <functional>
#include <string>
#include <utility>
#ifdef __ANDROID__
#include <sys/system_properties.h>
@@ -79,8 +74,8 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
}
void CommandStream::execute(void* buffer) {
// NOTE: we can't use SYSTRACE_CALL() or similar here because, execute() below, also
// uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope.
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
Profiler profiler;
@@ -105,7 +100,6 @@ void CommandStream::execute(void* buffer) {
// we want to remove all this when tracing is completely disabled
profiler.stop();
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
SYSTRACE_CONTEXT();
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions());
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles());
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10);
@@ -112,7 +112,6 @@ struct MetalContext {
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
DepthClampStateTracker depthClampState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
+2 -13
View File
@@ -857,10 +857,6 @@ bool MetalDriver::isProtectedTexturesSupported() {
return false;
}
bool MetalDriver::isDepthClampSupported() {
return true;
}
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU:
@@ -1302,11 +1298,11 @@ void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
pushConstants.setPushConstant(value, index);
}
void MetalDriver::insertEventMarker(const char* string) {
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
}
void MetalDriver::pushGroupMarker(const char* string) {
void MetalDriver::pushGroupMarker(const char* string, uint32_t len) {
mContext->groupMarkers.push(string);
}
@@ -1778,13 +1774,6 @@ 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) {
-1
View File
@@ -382,7 +382,6 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
using CullModeStateTracker = StateTracker<MTLCullMode>;
using WindingStateTracker = StateTracker<MTLWinding>;
using DepthClampStateTracker = StateTracker<MTLDepthClipMode>;
// Argument encoder
+2 -6
View File
@@ -202,10 +202,6 @@ bool NoopDriver::isProtectedTexturesSupported() {
return true;
}
bool NoopDriver::isDepthClampSupported() {
return false;
}
bool NoopDriver::isWorkaroundNeeded(Workaround) {
return false;
}
@@ -320,10 +316,10 @@ void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
}
void NoopDriver::insertEventMarker(char const* string) {
void NoopDriver::insertEventMarker(char const* string, uint32_t len) {
}
void NoopDriver::pushGroupMarker(char const* string) {
void NoopDriver::pushGroupMarker(char const* string, uint32_t len) {
}
void NoopDriver::popGroupMarker(int) {
@@ -676,7 +676,6 @@ 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);
@@ -747,7 +746,6 @@ 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;
+3 -5
View File
@@ -220,9 +220,8 @@ public:
bool EXT_color_buffer_float;
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_depth_clamp;
bool EXT_discard_framebuffer;
bool EXT_disjoint_timer_query;
bool EXT_discard_framebuffer;
bool EXT_multisampled_render_to_texture2;
bool EXT_multisampled_render_to_texture;
bool EXT_protected_textures;
@@ -240,10 +239,10 @@ public:
bool KHR_parallel_shader_compile;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool OES_EGL_image_external_essl3;
bool OES_depth24;
bool OES_depth_texture;
bool OES_depth24;
bool OES_packed_depth_stencil;
bool OES_EGL_image_external_essl3;
bool OES_rgb8_rgba8;
bool OES_standard_derivatives;
bool OES_texture_npot;
@@ -628,7 +627,6 @@ 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());
+4 -16
View File
@@ -451,14 +451,6 @@ 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 {
@@ -2127,10 +2119,6 @@ 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:
@@ -3196,23 +3184,23 @@ void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::insertEventMarker(char const* string) {
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
auto& gl = mContext;
if (gl.ext.EXT_debug_marker) {
glInsertEventMarkerEXT(GLsizei(strlen(string)), string);
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
}
#endif
#endif
}
void OpenGLDriver::pushGroupMarker(char const* string) {
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
#ifndef __EMSCRIPTEN__
#ifdef GL_EXT_debug_marker
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_OPENGL
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
glPushGroupMarkerEXT(GLsizei(strlen(string)), string);
glPushGroupMarkerEXT(GLsizei(len ? len : strlen(string)), string);
}
#endif
#endif
-6
View File
@@ -201,12 +201,6 @@ 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
@@ -165,14 +165,14 @@ constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1;
// buffers that have been submitted but have not yet finished rendering. Note that Filament can
// issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on
// some platforms.
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 10;
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 3;
// Number of command buffer submissions that should occur before an unused pipeline is removed
// from the cache.
//
// If this number is low, VkPipeline construction will occur frequently, which can
// be extremely slow. If this number is high, the memory footprint will be large.
constexpr static const int FVK_MAX_PIPELINE_AGE = 10;
constexpr static const int FVK_MAX_PIPELINE_AGE = 3;
// VulkanPipelineCache does not track which command buffers contain references to which pipelines,
// instead it simply waits for at least FVK_MAX_COMMAND_BUFFERS submissions to occur before
@@ -125,10 +125,6 @@ public:
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
}
inline bool isDepthClampSupported() const noexcept {
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
}
inline bool isDebugMarkersSupported() const noexcept {
return mDebugMarkersSupported;
}
+5 -8
View File
@@ -530,6 +530,8 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels,
format, samples, w, h, depth, usage, mStagePool);
// FVK_LOGE <<"created texture: " << vktexture->getVkImage() << " dim=" <<
// vktexture->width << "x" << vktexture->height << utils::io::endl;
mResourceManager.acquire(vktexture);
}
@@ -944,10 +946,6 @@ bool VulkanDriver::isProtectedTexturesSupported() {
return false;
}
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
}
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
switch (workaround) {
case Workaround::SPLIT_EASU: {
@@ -1606,13 +1604,13 @@ void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
value);
}
void VulkanDriver::insertEventMarker(char const* string) {
void VulkanDriver::insertEventMarker(char const* string, uint32_t len) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.insertEventMarker(string, strlen(string));
mCommands.insertEventMarker(string, len);
#endif
}
void VulkanDriver::pushGroupMarker(char const* string) {
void VulkanDriver::pushGroupMarker(char const* string, uint32_t) {
// Turns out all the markers are 0-terminated, so we can just pass it without len.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
mCommands.pushGroupMarker(string);
@@ -1820,7 +1818,6 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.depthClamp = rasterState.depthClamp,
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
@@ -184,7 +184,6 @@ 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,8 +90,7 @@ public:
VkBlendFactor srcAlphaBlendFactor : 5;
VkBlendFactor dstAlphaBlendFactor : 5;
VkColorComponentFlags colorWriteMask : 4;
uint8_t rasterizationSamples : 4;// offset = 4 bytes
uint8_t depthClamp : 4;
uint8_t rasterizationSamples; // offset = 4 bytes
uint8_t colorTargetCount; // offset = 5 bytes
BlendEquation colorBlendOp : 4; // offset = 6 bytes
BlendEquation alphaBlendOp : 4;
@@ -32,8 +32,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
}
// build the push constant layout key
uint32_t const pushConstantRangeCount = program->getPushConstantRangeCount();
auto const& pushConstantRanges = program->getPushConstantRanges();
uint32_t pushConstantRangeCount = program->getPushConstantRangeCount();
auto const& pushConstantRanges = program->getPushConstantRanges();
if (pushConstantRangeCount > 0) {
assert_invariant(pushConstantRangeCount <= Program::SHADER_TYPE_COUNT);
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
@@ -52,8 +52,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
}
}
if (auto iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
PipelineLayoutCacheEntry& entry = iter->second;
if (PipelineLayoutMap::iterator iter = mPipelineLayouts.find(key); iter != mPipelineLayouts.end()) {
PipelineLayoutCacheEntry& entry = iter.value();
entry.lastUsed = mTimestamp++;
return entry.handle;
}
@@ -22,7 +22,7 @@
#include <utils/Hash.h>
#include <unordered_map>
#include <tsl/robin_map.h>
namespace filament::backend {
@@ -36,8 +36,8 @@ public:
void terminate() noexcept;
struct PushConstantKey {
uint8_t stage = 0;// We have one set of push constant per shader stage (fragment, vertex, etc).
uint8_t size = 0;
uint8_t stage;// We have one set of push constant per shader stage (fragment, vertex, etc).
uint8_t size;
// Note that there is also an offset parameter for push constants, but
// we always assume our update range will have the offset 0.
};
@@ -73,7 +73,7 @@ private:
}
};
using PipelineLayoutMap = std::unordered_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
using PipelineLayoutMap = tsl::robin_map<PipelineLayoutKey, PipelineLayoutCacheEntry,
PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>;
VkDevice mDevice;
@@ -82,6 +82,6 @@ private:
PipelineLayoutMap mPipelineLayouts;
};
} // filament::backend
}
#endif // TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
@@ -325,7 +325,6 @@ 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,
+1 -2
View File
@@ -55,8 +55,7 @@ public:
template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
// This needs to be public because it is used in the following template.
UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
-7
View File
@@ -570,13 +570,6 @@ public:
*/
void setShadowType(ShadowType shadow) noexcept;
/**
* Returns the shadow mapping technique used by this View.
*
* @return value set by setShadowType().
*/
ShadowType getShadowType() const noexcept;
/**
* Sets VSM shadowing options that apply across the entire View.
*
+2 -1
View File
@@ -31,7 +31,8 @@ using namespace utils;
using namespace backend;
FrameSkipper::FrameSkipper(size_t latency) noexcept
: mLast(std::clamp(latency, size_t(1), MAX_FRAME_LATENCY) - 1) {
: mLast(std::max(latency, MAX_FRAME_LATENCY) - 1) {
assert_invariant(latency <= MAX_FRAME_LATENCY);
}
FrameSkipper::~FrameSkipper() noexcept = default;
+1 -12
View File
@@ -32,18 +32,7 @@ namespace filament {
* outrun the GPU.
*/
class FrameSkipper {
/*
* The maximum frame latency acceptable on ANDROID is 2 because higher latencies will be
* throttled anyway in BufferQueueProducer::dequeueBuffer(), because ANDROID is generally
* triple-buffered no more; that case is actually pretty bad because the GL thread can block
* anywhere (usually inside the first draw command that touches the swapchain).
*
* A frame latency of 1 has the benefit of reducing render latency,
* but the drawback of preventing CPU / GPU overlap.
*
* Generally a frame latency of 2 is the best compromise.
*/
static constexpr size_t MAX_FRAME_LATENCY = 2;
static constexpr size_t MAX_FRAME_LATENCY = 3;
public:
/*
* The latency parameter defines how many unfinished frames we want to accept before we start
+1 -3
View File
@@ -459,12 +459,10 @@ void PerViewUniforms::bind(backend::DriverApi& driver) noexcept {
void PerViewUniforms::unbindSamplers() noexcept {
auto& samplerGroup = mSamplers;
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
samplerGroup.clearSampler(PerViewSib::IBL_SPECULAR);
samplerGroup.clearSampler(PerViewSib::SSAO);
samplerGroup.clearSampler(PerViewSib::SSR);
samplerGroup.clearSampler(PerViewSib::STRUCTURE);
samplerGroup.clearSampler(PerViewSib::FOG);
samplerGroup.clearSampler(PerViewSib::SHADOW_MAP);
}
} // namespace filament
+2 -9
View File
@@ -419,8 +419,7 @@ 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, bool hasDepthClamp) noexcept {
FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept {
FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial();
variant = Variant::filterVariant(variant, ma->isVariantLit());
@@ -461,7 +460,6 @@ 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)
}
@@ -560,9 +558,6 @@ 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>();
@@ -582,7 +577,6 @@ 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) {
@@ -697,8 +691,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.morphingOffset = primitive.getMorphingBufferOffset();
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi,
inverseFrontFaces, hasDepthClamp);
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
const bool blendPass = Pass(cmd.key & PASS_MASK) == Pass::BLENDED;
if (blendPass) {
// TODO: at least for transparent objects, AABB should be per primitive
+1 -2
View File
@@ -284,7 +284,6 @@ 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<
@@ -445,7 +444,7 @@ private:
uint8_t instancedStereoEyeCount) noexcept;
static void setupColorCommand(Command& cmdDraw, Variant variant,
FMaterialInstance const* mi, bool inverseFrontFaces, bool hasDepthClamp) noexcept;
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
static void updateSummedPrimitiveCounts(
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
+5 -8
View File
@@ -143,14 +143,12 @@ void ResourceAllocator::terminate() noexcept {
}
}
RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
RenderTargetHandle ResourceAllocator::createRenderTarget(const char*,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color, TargetBufferInfo depth,
TargetBufferInfo stencil) noexcept {
auto handle = mBackend.createRenderTarget(targetBufferFlags,
return mBackend.createRenderTarget(targetBufferFlags,
width, height, samples ? samples : 1u, layerCount, color, depth, stencil);
mBackend.setDebugTag(handle.getId(), CString{ name });
return handle;
}
void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept {
@@ -172,9 +170,9 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
// do we have a suitable texture in the cache?
TextureHandle handle;
TextureKey const key{ name, target, levels, format, samples, width, height, depth, usage, swizzle };
if constexpr (mEnabled) {
auto& textureCache = mTextureCache;
const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage, swizzle };
auto it = textureCache.find(key);
if (UTILS_LIKELY(it != textureCache.end())) {
// we do, move the entry to the in-use list, and remove from the cache
@@ -192,6 +190,7 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
} else {
if (swizzle == defaultSwizzle) {
handle = mBackend.createTexture(
@@ -202,14 +201,12 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name,
swizzle[0], swizzle[1], swizzle[2], swizzle[3]);
}
}
mDisposer->checkout(handle, key);
mBackend.setDebugTag(handle.getId(), CString{ name });
return handle;
}
void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
auto const key = mDisposer->checkin(h);
if constexpr (mEnabled) {
auto const key = mDisposer->checkin(h);
if (UTILS_LIKELY(key.has_value())) {
uint32_t const size = key.value().getSize();
mTextureCache.emplace(key.value(), TextureCachePayload{ h, mAge, size });
+6 -6
View File
@@ -829,13 +829,13 @@ ShadowMap::Corners ShadowMap::computeFrustumCorners(
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 },
}
};
+5 -24
View File
@@ -66,15 +66,15 @@ namespace filament {
using namespace backend;
using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine)
: mIsDepthClampSupported(engine.getDriverApi().isDepthClampSupported()) {
// do this only if depth-clamp is available
static constexpr bool USE_DEPTH_CLAMP = false;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
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() {
@@ -367,20 +367,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// generate and sort the commands for rendering the shadow map
RenderPass::RenderFlags renderPassFlags{};
bool const canUseDepthClamp =
shadowMap.getShadowType() == ShadowType::DIRECTIONAL &&
!view.hasVSM() &&
mIsDepthClampSupported &&
engine.debug.shadowmap.depth_clamp;
if (canUseDepthClamp) {
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
}
RenderPass const pass = passBuilder
.renderFlags(RenderPass::HAS_DEPTH_CLAMP, renderPassFlags)
.camera(cameraInfo)
.visibilityMask(entry.visibilityMask)
.geometry(scene->getRenderableData(),
@@ -654,14 +641,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
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,
canUseDepthClamp);
lightData, 0, cameraInfo, shadowMapInfo, sceneInfo, USE_DEPTH_CLAMP);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
-1
View File
@@ -232,7 +232,6 @@ private:
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
bool const mIsDepthClampSupported;
bool mInitialized = false;
ShadowMap& getShadowMap(size_t index) noexcept {
-4
View File
@@ -193,10 +193,6 @@ void View::setShadowType(View::ShadowType shadow) noexcept {
downcast(this)->setShadowType(shadow);
}
View::ShadowType View::getShadowType() const noexcept {
return downcast(this)->getShadowType();
}
void View::setVsmShadowOptions(VsmShadowOptions const& options) noexcept {
downcast(this)->setVsmShadowOptions(options);
}
-1
View File
@@ -600,7 +600,6 @@ public:
bool focus_shadowcasters = true;
bool visualize_cascades = false;
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;
+3 -9
View File
@@ -34,7 +34,7 @@ struct RenderTarget::BuilderDetails {
uint32_t mWidth{};
uint32_t mHeight{};
uint8_t mSamples = 1; // currently not settable in the public facing API
uint8_t mLayerCount = 1;
uint8_t mLayerCount = 1;// currently not settable in the public facing API
};
using BuilderType = RenderTarget;
@@ -91,28 +91,22 @@ RenderTarget* RenderTarget::Builder::build(Engine& engine) {
uint32_t maxWidth = 0;
uint32_t minHeight = std::numeric_limits<uint32_t>::max();
uint32_t maxHeight = 0;
uint32_t minDepth = std::numeric_limits<uint32_t>::max();
uint32_t maxDepth = 0;
for (auto const& attachment : mImpl->mAttachments) {
if (attachment.texture) {
const uint32_t w = attachment.texture->getWidth(attachment.mipLevel);
const uint32_t h = attachment.texture->getHeight(attachment.mipLevel);
const uint32_t d = attachment.texture->getDepth(attachment.mipLevel);
minWidth = std::min(minWidth, w);
minHeight = std::min(minHeight, h);
minDepth = std::min(minDepth, d);
maxWidth = std::max(maxWidth, w);
maxHeight = std::max(maxHeight, h);
maxDepth = std::max(maxDepth, d);
}
}
FILAMENT_CHECK_PRECONDITION(minWidth == maxWidth && minHeight == maxHeight
&& minDepth == maxDepth) << "All attachments dimensions must match";
FILAMENT_CHECK_PRECONDITION(minWidth == maxWidth && minHeight == maxHeight)
<< "All attachments dimensions must match";
mImpl->mWidth = minWidth;
mImpl->mHeight = minHeight;
mImpl->mLayerCount = minDepth;
return downcast(engine).createRenderTarget(*this);
}
+18 -28
View File
@@ -651,19 +651,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const bool isProtectedContent = mSwapChain && mSwapChain->isProtected();
// Conditions to meet to be able to use the sub-pass rendering path. This is regardless of
// whether the backend supports subpasses (or if they are disabled in the debugRegistry).
const bool isSubpassPossible =
msaaSampleCount <= 1 &&
hasColorGrading &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled;
// asSubpass is disabled with TAA (although it's supported) because performance was degraded
// on qualcomm hardware -- we might need a backend dependent toggle at some point
const PostProcessManager::ColorGradingConfig colorGradingConfig{
.asSubpass =
isSubpassPossible &&
msaaSampleCount <= 1 &&
driver.isFrameBufferFetchSupported() &&
hasColorGrading &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled &&
!engine.debug.renderer.disable_subpasses,
.customResolve =
msaaSampleCount > 1 &&
@@ -707,8 +702,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// this case, we would need an extra blit to "resolve" the buffer padding (because there are no
// other pass that can do it as a side effect). In this case, it is better to skip the padding,
// which won't be helping much.
const bool noBufferPadding = (isSubpassPossible &&
!hasFXAA && !scaled) || engine.debug.renderer.disable_buffer_padding;
const bool noBufferPadding
= (!hasFXAA && !scaled) || engine.debug.renderer.disable_buffer_padding;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
@@ -823,14 +818,14 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
blackboard["shadows"] = shadows;
}
// When we don't have a custom RenderTarget, customRenderTarget below is nullptr and is
// When we don't have a custom RenderTarget, currentRenderTarget below is nullptr and is
// recorded in the list of targets already rendered into -- this ensures that
// initializeClearFlags() is called only once for the default RenderTarget.
auto& previousRenderTargets = mPreviousRenderTargets;
FRenderTarget* const customRenderTarget = downcast(view.getRenderTarget());
FRenderTarget* const currentRenderTarget = downcast(view.getRenderTarget());
if (UTILS_LIKELY(
previousRenderTargets.find(customRenderTarget) == previousRenderTargets.end())) {
previousRenderTargets.insert(customRenderTarget);
previousRenderTargets.find(currentRenderTarget) == previousRenderTargets.end())) {
previousRenderTargets.insert(currentRenderTarget);
initializeClearFlags();
}
@@ -847,10 +842,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
const TargetBufferFlags keepOverrideStartFlags = TargetBufferFlags::ALL & ~discardStartFlags;
TargetBufferFlags keepOverrideEndFlags = TargetBufferFlags::NONE;
if (customRenderTarget) {
if (currentRenderTarget) {
// For custom RenderTarget, we look at each attachment flag and if they have their
// SAMPLEABLE usage bit set, we assume they must not be discarded after the render pass.
keepOverrideEndFlags |= customRenderTarget->getSampleableAttachmentsMask();
keepOverrideEndFlags |= currentRenderTarget->getSampleableAttachmentsMask();
}
// Renderer's ClearOptions apply once at the beginning of the frame (not for each View),
@@ -1019,10 +1014,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
{ .width = svp.width, .height = svp.height });
if (UTILS_LIKELY(reflections)) {
fg.addTrivialSideEffectPass("SSR Cleanup", [&view](DriverApi& driver) {
view.getPerViewUniforms().prepareStructure({});
view.commitUniforms(driver);
});
// generate the mipchain
PostProcessManager::generateMipmapSSR(ppm, fg,
reflections, ssrConfig.reflection, false, ssrConfig);
@@ -1138,12 +1129,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
}
}
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
view.commitUniforms(driver);
});
if (colorGradingConfig.customResolve) {
assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1);
// TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used
@@ -1182,11 +1167,16 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// this is the output of the color pass / input to post processing,
// this is only used later for comparing it with the output after post-processing
FrameGraphId<FrameGraphTexture> const postProcessInput = colorGradingConfig.asSubpass ?
colorPassOutput.tonemappedColor :
colorPassOutput.linearColor;
colorPassOutput.tonemappedColor :
colorPassOutput.linearColor;
// input can change below
FrameGraphId<FrameGraphTexture> input = postProcessInput;
fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) {
// Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass.
view.cleanupRenderPasses();
view.commitUniforms(driver);
});
// Resolve depth -- which might be needed because of TAA or DoF. This pass will be culled
// if the depth is not used below or if the depth is not MS (e.g. it could have been
-2
View File
@@ -245,8 +245,6 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) {
}
if (auto name = builder.getName(); !name.empty()) {
driver.setDebugTag(mHandle.getId(), std::move(name));
} else {
driver.setDebugTag(mHandle.getId(), CString{"FTexture"});
}
}
+8 -4
View File
@@ -1040,8 +1040,10 @@ void FView::commitFrameHistory(FEngine& engine) noexcept {
auto& frameHistory = mFrameHistory;
FrameHistoryEntry& last = frameHistory.back();
disposer.destroy(std::move(last.taa.color.handle));
disposer.destroy(std::move(last.ssr.color.handle));
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
// and then push the new history entry to the history stack
frameHistory.commit();
@@ -1053,8 +1055,10 @@ void FView::clearFrameHistory(FEngine& engine) noexcept {
auto& frameHistory = mFrameHistory;
for (size_t i = 0; i < frameHistory.size(); ++i) {
FrameHistoryEntry& last = frameHistory[i];
disposer.destroy(std::move(last.taa.color.handle));
disposer.destroy(std::move(last.ssr.color.handle));
disposer.destroy(last.taa.color.handle);
disposer.destroy(last.ssr.color.handle);
last.taa.color.handle.clear();
last.ssr.color.handle.clear();
}
}
+4 -1
View File
@@ -195,11 +195,12 @@ FrameGraph& FrameGraph::compile() noexcept {
void FrameGraph::execute(backend::DriverApi& driver) noexcept {
SYSTRACE_CALL();
bool const useProtectedMemory = mMode == Mode::PROTECTED;
auto const& passNodes = mPassNodes;
auto& resourceAllocator = mResourceAllocator;
SYSTRACE_NAME("FrameGraph");
driver.pushGroupMarker("FrameGraph");
auto first = passNodes.begin();
@@ -210,6 +211,7 @@ void FrameGraph::execute(backend::DriverApi& driver) noexcept {
assert_invariant(!node->isCulled());
SYSTRACE_NAME(node->getName());
driver.pushGroupMarker(node->getName());
// devirtualize resourcesList
@@ -227,6 +229,7 @@ void FrameGraph::execute(backend::DriverApi& driver) noexcept {
assert_invariant(resource->last == node);
resource->destroy(resourceAllocator);
}
driver.popGroupMarker();
}
driver.popGroupMarker();
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.54.4"
spec.version = "1.54.1"
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.54.4/filament-v1.54.4-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.54.1/filament-v1.54.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+9 -7
View File
@@ -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) {
+2 -2
View File
@@ -73,8 +73,8 @@ public:
using const_pointer = const TYPE*;
using reference = TYPE&;
using const_reference = const TYPE&;
using size_type = ::size_t;
using difference_type = ::ptrdiff_t;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assignment = std::true_type;
using is_always_equal = std::true_type;
+2 -3
View File
@@ -22,7 +22,6 @@ set(MATERIAL_SRCS
materials/bakedTexture.mat
materials/pointSprites.mat
materials/aoPreview.mat
materials/arrayTexture.mat
materials/groundShadow.mat
materials/heightfield.mat
materials/image.mat
@@ -40,6 +39,7 @@ set(MATERIAL_SRCS
materials/sandboxSubsurface.mat
materials/sandboxUnlit.mat
materials/texturedLit.mat
materials/iconTest.mat
)
if (CMAKE_CROSSCOMPILING)
@@ -253,7 +253,6 @@ if (NOT ANDROID)
add_demo(helloskinning)
add_demo(helloskinningbuffer)
add_demo(helloskinningbuffer_morebones)
add_demo(hellostereo)
add_demo(image_viewer)
add_demo(lightbulb)
add_demo(material_sandbox)
@@ -272,11 +271,11 @@ if (NOT ANDROID)
add_demo(viewtest)
# Sample app specific
target_link_libraries(hellotriangle PRIVATE gltf-demo-resources uberarchive gltfio)
target_link_libraries(frame_generator PRIVATE imageio)
target_link_libraries(gltf_viewer PRIVATE gltf-demo-resources uberarchive gltfio viewer)
target_link_libraries(gltf_instances PRIVATE gltf-demo-resources uberarchive gltfio viewer)
target_link_libraries(hellopbr PRIVATE filameshio suzanne-resources)
target_link_libraries(hellostereo PRIVATE filameshio suzanne-resources)
target_link_libraries(image_viewer PRIVATE viewer imageio)
target_link_libraries(multiple_windows PRIVATE filameshio suzanne-resources)
target_link_libraries(rendertarget PRIVATE filameshio suzanne-resources)
-2
View File
@@ -905,8 +905,6 @@ int main(int argc, char** argv) {
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",
-366
View File
@@ -1,366 +0,0 @@
/*
* 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.
*/
#include <filament/Camera.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/LightManager.h>
#include <filament/Material.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/RenderTarget.h>
#include <filament/Scene.h>
#include <filament/TextureSampler.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <private/filament/EngineEnums.h>
#include <utils/EntityManager.h>
#include <filameshio/MeshReader.h>
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <getopt/getopt.h>
#include <iostream>
#include <vector>
#include "generated/resources/resources.h"
#include "generated/resources/monkey.h"
using namespace filament;
using namespace filamesh;
using namespace filament::math;
struct Vertex {
float3 position;
float2 uv;
};
struct App {
Config config;
Material* monkeyMaterial;
MaterialInstance* monkeyMatInstance;
MeshReader::Mesh monkeyMesh;
mat4f monkeyTransform;
utils::Entity lightEntity;
View* stereoView = nullptr;
Scene* stereoScene = nullptr;
Camera* stereoCamera = nullptr;
Texture* stereoColorTexture = nullptr;
Texture* stereoDepthTexture = nullptr;
RenderTarget* stereoRenderTarget = nullptr;
VertexBuffer* quadVb = nullptr;
IndexBuffer* quadIb = nullptr;
Material* quadMaterial = nullptr;
std::vector<utils::Entity> quadEntities;
std::vector<MaterialInstance*> quadMatInstances;
};
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
std::string usage(
"SHOWCASE renders multiple quads displaying the contents of stereoscopic rendering\n"
"Usage:\n"
" SHOWCASE [options]\n"
"Options:\n"
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl (default), vulkan, or metal\n"
" --eyes=<stereoscopic eyes>, -y <stereoscopic eyes>\n"
" Sets the number of stereoscopic eyes (default: 2) when stereoscopic rendering is\n"
" enabled.\n\n"
);
const std::string from("SHOWCASE");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
usage.replace(pos, from.length(), exec_name);
}
std::cout << usage;
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
static constexpr const char* OPTSTR = "ha:y:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ "eyes", required_argument, nullptr, 'y' },
{ nullptr, 0, nullptr, 0 }
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printUsage(argv[0]);
exit(0);
case 'a':
if (arg == "opengl") {
app->config.backend = Engine::Backend::OPENGL;
} else if (arg == "vulkan") {
app->config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
app->config.backend = Engine::Backend::METAL;
} else {
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
exit(1);
}
break;
case 'y': {
int eyeCount = 0;
try {
eyeCount = std::stoi(arg);
} catch (std::invalid_argument &e) { }
if (eyeCount >= 2 && eyeCount <= CONFIG_MAX_STEREOSCOPIC_EYES) {
app->config.stereoscopicEyeCount = eyeCount;
} else {
std::cerr << "Eye count must be between 2 and CONFIG_MAX_STEREOSCOPIC_EYES ("
<< (int)CONFIG_MAX_STEREOSCOPIC_EYES << ") (inclusive).\n";
exit(1);
}
break;
}
}
}
return optind;
}
int main(int argc, char** argv) {
#if !defined(FILAMENT_SAMPLES_STEREO_TYPE_MULTIVIEW)
std::cerr << "This sample only works with multiview enabled.\n";
exit(1);
#endif
App app{};
app.config.title = "stereoscopic rendering";
handleCommandLineArguments(argc, argv, &app);
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
auto& tcm = engine->getTransformManager();
auto& rcm = engine->getRenderableManager();
auto& em = utils::EntityManager::get();
auto vp = view->getViewport();
constexpr float3 monkeyPosition{ 0, 0, -4};
constexpr float3 upVector{ 0, 1, 0};
const int eyeCount = app.config.stereoscopicEyeCount;
// Create a mesh material and an instance.
app.monkeyMaterial = Material::Builder()
.package(RESOURCES_AIDEFAULTMAT_DATA, RESOURCES_AIDEFAULTMAT_SIZE)
.build(*engine);
auto mi = app.monkeyMatInstance = app.monkeyMaterial->createInstance();
mi->setParameter("baseColor", RgbType::LINEAR, {0.8, 1.0, 1.0});
mi->setParameter("metallic", 0.0f);
mi->setParameter("roughness", 0.4f);
mi->setParameter("reflectance", 0.5f);
// Add a monkey and a light source into the main scene.
app.monkeyMesh = MeshReader::loadMeshFromBuffer(
engine, MONKEY_SUZANNE_DATA, nullptr, nullptr, mi);
auto ti = tcm.getInstance(app.monkeyMesh.renderable);
app.monkeyTransform = mat4f{mat3f(1), monkeyPosition } * tcm.getWorldTransform(ti);
rcm.setCastShadows(rcm.getInstance(app.monkeyMesh.renderable), false);
scene->addEntity(app.monkeyMesh.renderable);
app.lightEntity = em.create();
LightManager::Builder(LightManager::Type::SUN)
.color(Color::toLinear<ACCURATE>(sRGBColor(0.98f, 0.92f, 0.89f)))
.intensity(110000)
.direction({ 0.7, -1, -0.8 })
.sunAngularRadius(1.9f)
.castShadows(false)
.build(*engine, app.lightEntity);
scene->addEntity(app.lightEntity);
// Create a stereo render target that will be rendered as an offscreen view.
app.stereoScene = engine->createScene();
app.stereoScene->addEntity(app.monkeyMesh.renderable);
app.stereoScene->addEntity(app.lightEntity);
app.stereoView = engine->createView();
app.stereoView->setScene(app.stereoScene);
app.stereoView->setPostProcessingEnabled(false);
app.stereoColorTexture = Texture::Builder()
.width(vp.width)
.height(vp.height)
.depth(eyeCount)
.levels(1)
.sampler(Texture::Sampler::SAMPLER_2D_ARRAY)
.format(Texture::InternalFormat::RGBA8)
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
.build(*engine);
app.stereoDepthTexture = Texture::Builder()
.width(vp.width)
.height(vp.height)
.depth(eyeCount)
.levels(1)
.sampler(Texture::Sampler::SAMPLER_2D_ARRAY)
.format(Texture::InternalFormat::DEPTH24)
.usage(Texture::Usage::DEPTH_ATTACHMENT)
.build(*engine);
app.stereoRenderTarget = RenderTarget::Builder()
.texture(RenderTarget::AttachmentPoint::COLOR, app.stereoColorTexture)
.texture(RenderTarget::AttachmentPoint::DEPTH, app.stereoDepthTexture)
.build(*engine);
app.stereoView->setRenderTarget(app.stereoRenderTarget);
app.stereoView->setViewport({0, 0, vp.width, vp.height});
app.stereoCamera = engine->createCamera(em.create());
app.stereoView->setCamera(app.stereoCamera);
app.stereoView->setStereoscopicOptions({.enabled = true});
FilamentApp::get().addOffscreenView(app.stereoView);
// Camera settings for the stereo render target
constexpr double projNear = 0.1;
constexpr double projFar = 100;
mat4 projections[CONFIG_MAX_STEREOSCOPIC_EYES];
mat4 eyeModels[CONFIG_MAX_STEREOSCOPIC_EYES];
static_assert(CONFIG_MAX_STEREOSCOPIC_EYES == 4, "Update matrices");
projections[0] = Camera::projection(24, 1.0, projNear, projFar);
projections[1] = Camera::projection(70, 1.0, projNear, projFar);
projections[2] = Camera::projection(50, 1.0, projNear, projFar);
projections[3] = Camera::projection(35, 1.0, projNear, projFar);
app.stereoCamera->setCustomEyeProjection(projections, 4, projections[0], projNear, projFar);
eyeModels[0] = mat4::lookAt(float3{ -4, 0, 0 }, monkeyPosition, upVector);
eyeModels[1] = mat4::lookAt(float3{ 4, 0, 0 }, monkeyPosition, upVector);
eyeModels[2] = mat4::lookAt(float3{ 0, 3, 0 }, monkeyPosition, upVector);
eyeModels[3] = mat4::lookAt(float3{ 0, -3, 0 }, monkeyPosition, upVector);
for (int i = 0; i < eyeCount; ++i) {
app.stereoCamera->setEyeModelMatrix(i, eyeModels[i]);
}
// Create a vertex buffer and an index buffer for a quad. This will be used to display the contents
// of each layer of the stereo texture.
float3 quadCenter = {0, 0, 0};
float3 quadNormal = normalize(float3 {0, 0, 1});
float3 u = normalize(cross(quadNormal, upVector));
float3 v = cross(quadNormal, u);
static Vertex quadVertices[4] = {
{{quadCenter - u - v}, {1, 0}},
{{quadCenter + u - v}, {0, 0}},
{{quadCenter - u + v}, {1, 1}},
{{quadCenter + u + v}, {0, 1}}
};
static_assert(sizeof(Vertex) == 20, "Strange vertex size.");
app.quadVb = VertexBuffer::Builder()
.vertexCount(4)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, sizeof(Vertex))
.attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::FLOAT2, 12, sizeof(Vertex))
.build(*engine);
app.quadVb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(quadVertices, sizeof(Vertex) * 4, nullptr));
static constexpr uint16_t quadIndices[6] = { 0, 1, 2, 3, 2, 1 };
app.quadIb = IndexBuffer::Builder()
.indexCount(6)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.quadIb->setBuffer(*engine, IndexBuffer::BufferDescriptor(quadIndices, 12, nullptr));
// Create quad material instances and renderables.
app.quadMaterial = Material::Builder()
.package(RESOURCES_ARRAYTEXTURE_DATA, RESOURCES_ARRAYTEXTURE_SIZE)
.build(*engine);
for (int i = 0; i < eyeCount; ++i) {
MaterialInstance* quadMatInst = app.quadMaterial->createInstance();
TextureSampler sampler(TextureSampler::MinFilter::LINEAR, TextureSampler::MagFilter::LINEAR);
quadMatInst->setParameter("image", app.stereoColorTexture, sampler);
quadMatInst->setParameter("layerIndex", i);
quadMatInst->setParameter("borderEffect", true);
app.quadMatInstances.push_back(quadMatInst);
utils::Entity quadEntity = em.create();
app.quadEntities.push_back(quadEntity);
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, quadMatInst)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.quadVb, app.quadIb, 0, 6)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, quadEntity);
scene->addEntity(quadEntity);
// Place quads at equal intervals.
TransformManager::Instance quadTi = tcm.getInstance(quadEntity);
mat4f quadWorld = tcm.getWorldTransform(quadTi);
constexpr float leftMostPos = -4;
constexpr float rightMostPos = 4;
float xpos = leftMostPos + ( (rightMostPos - leftMostPos) / (eyeCount - 1) ) * i;
tcm.setTransform(quadTi, mat4f::translation(float3(xpos, 2, -8)) * quadWorld);
}
};
auto cleanup = [&app](Engine* engine, View*, Scene*) {
auto& em = utils::EntityManager::get();
for (MaterialInstance* mi : app.quadMatInstances) {
engine->destroy(mi);
}
for (utils::Entity e : app.quadEntities) {
engine->destroy(e);
}
engine->destroy(app.quadMaterial);
engine->destroy(app.quadIb);
engine->destroy(app.quadVb);
engine->destroy(app.stereoRenderTarget);
engine->destroy(app.stereoDepthTexture);
engine->destroy(app.stereoColorTexture);
auto camera = app.stereoCamera->getEntity();
engine->destroyCameraComponent(camera);
em.destroy(camera);
engine->destroy(app.stereoScene);
engine->destroy(app.stereoView);
engine->destroy(app.lightEntity);
engine->destroy(app.monkeyMesh.renderable);
engine->destroy(app.monkeyMesh.indexBuffer);
engine->destroy(app.monkeyMesh.vertexBuffer);
engine->destroy(app.monkeyMatInstance);
engine->destroy(app.monkeyMaterial);
};
auto preRender = [&app](Engine*, View*, Scene*, Renderer* renderer) {
renderer->setClearOptions({.clearColor = {0.1,0.2,0.4,1.0}, .clear = true});
};
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
auto& tcm = engine->getTransformManager();
// Animate the monkey by spinning and sliding back and forth along Z.
auto ti = tcm.getInstance(app.monkeyMesh.renderable);
mat4f xform = app.monkeyTransform * mat4f::rotation(now, float3{0, 1, 0 });
tcm.setTransform(ti, xform);
});
FilamentApp::get().run(app.config, setup, cleanup, FilamentApp::ImGuiCallback(), preRender);
return 0;
}
+449 -151
View File
@@ -1,192 +1,490 @@
/*
* 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.
*/
#include <fstream>
#include <iostream>
#include <filament/Camera.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/LightManager.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <filament/SwapChain.h>
#include <filament/Texture.h>
#include <filament/TransformManager.h>
#include <filament/RenderTarget.h>
#include <filamat/MaterialBuilder.h>
#include <utils/EntityManager.h>
#include <utils/Log.h>
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <math/norm.h>
#include <getopt/getopt.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/ResourceLoader.h>
#include <cmath>
#include <iostream>
#include <SDL.h>
#include <SDL_syswm.h>
#include <SDL_video.h>
#include <filamentapp/NativeWindowHelper.h>
#include "generated/resources/resources.h"
#include "materials/uberarchive.h"
#include <vector>
using namespace filament;
using utils::Entity;
using utils::EntityManager;
using namespace math;
using namespace utils;
struct App {
Config config;
VertexBuffer* vb;
IndexBuffer* ib;
Material* mat;
Camera* cam;
Entity camera;
Skybox* skybox;
Entity renderable;
namespace {
const static uint32_t gIndices[] = {0, 1, 2, 2, 3, 0};
float const QUAD_WIDTH = .5;
const static math::float3 gVertices[] = {
float3 {0, 0, 0} * QUAD_WIDTH,
float3 {0, 1, 0} * QUAD_WIDTH,
float3 {1, 1, 0} * QUAD_WIDTH,
float3 {1, 0, 0} * QUAD_WIDTH,
};
struct Vertex {
filament::math::float2 position;
uint32_t color;
const static math::float2 gUVs[] = {
{0, 0},
{0, 1},
{1, 1},
{1, 0},
};
static const Vertex TRIANGLE_VERTICES[3] = {
{{1, 0}, 0xffff0000u},
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
};
const static short4 tbn = math::packSnorm16(mat3f::packTangentFrame(
math::mat3f{float3{1.0f, 0.0f, 0.0f}, float3{0.0f, 0.0f, 1.0f},
float3{0.0f, 1.0f, 0.0f}}).xyzw);
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
const static math::short4 gNormals[]{tbn, tbn, tbn, tbn};
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
std::string usage(
"HELLOTRIANGLE renders a spinning colored triangle\n"
"Usage:\n"
" HELLOTRIANGLE [options]\n"
"Options:\n"
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, or metal\n"
);
const std::string from("HELLOTRIANGLE");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
usage.replace(pos, from.length(), exec_name);
struct TestPrimitive {
TestPrimitive(Engine* engine) {
vertexBuffer =
VertexBuffer::Builder()
.vertexCount(4)
.bufferCount(3)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT3)
.attribute(VertexAttribute::UV0, 1, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::TANGENTS, 2, VertexBuffer::AttributeType::FLOAT4)
.build(*engine);
vertexBuffer->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(gVertices,
vertexBuffer->getVertexCount() * sizeof(gVertices[0])));
vertexBuffer->setBufferAt(*engine, 1,
VertexBuffer::BufferDescriptor(gUVs,
vertexBuffer->getVertexCount() * sizeof(gUVs[0])));
vertexBuffer->setBufferAt(*engine, 2,
VertexBuffer::BufferDescriptor(gNormals,
vertexBuffer->getVertexCount() * sizeof(gNormals[0])));
indexBuffer = IndexBuffer::Builder().indexCount(6).build(*engine);
indexBuffer->setBuffer(*engine, IndexBuffer::BufferDescriptor(gIndices,
indexBuffer->getIndexCount() * sizeof(uint32_t)));
}
std::cout << usage;
VertexBuffer* verts() const { return vertexBuffer; }
IndexBuffer* inds() const { return indexBuffer; }
private:
VertexBuffer* vertexBuffer;
IndexBuffer* indexBuffer;
};
struct TestMaterial {
TestMaterial(Engine* engine) {
material = Material::Builder()
.package(RESOURCES_ICONTEST_DATA, RESOURCES_ICONTEST_SIZE)
.build(*engine);
}
MaterialInstance* instance() {
auto inst = material->createInstance();
instances.push_back(inst);
return inst;
}
private:
Material* material;
std::vector<MaterialInstance*> instances;
};
struct TestTexture {
TestTexture(Engine* engine, size_t width, size_t height, bool attachment=false) {
auto usage = Texture::Usage::DEFAULT;
if (attachment) {
usage = usage | Texture::Usage::COLOR_ATTACHMENT;
}
texture = Texture::Builder()
.width(width).height(height).levels(1)
.usage(usage)
.format(Texture::InternalFormat::RGBA8).build(*engine);
uint8_t* data = (uint8_t*) malloc(width * height * 4);
std::memset(data, 0xFF, width * height * 4);
Texture::PixelBufferDescriptor buffer(data, size_t(width * height * 4),
Texture::Format::RGBA, Texture::Type::UBYTE,
[](void* buf, size_t size, void* user) {
free(buf);
});
texture->setImage(*engine, 0, 0, 0, 0, width, height, 1, std::move(buffer));
}
Texture* get() const { return texture; }
private:
Texture* texture;
};
std::pair<SDL_Window*, void*> createSDLwindow() {
const uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
SDL_Window* win = SDL_CreateWindow("Hello World!", 0, 100, 800, 800, windowFlags);
if (win == nullptr) {
std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return {NULL, NULL};
}
void* nativeWindow = getNativeWindow(win);
#if defined(__APPLE__)
prepareNativeWindow(win);
setUpMetalLayer(nativeWindow);
#endif
return {win, nativeWindow};
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
static constexpr const char* OPTSTR = "ha:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ nullptr, 0, nullptr, 0 }
struct IconScene {
struct Option {
float border=1.4;
bool postProcessing=true;
bool useSkybox=false;
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printUsage(argv[0]);
exit(0);
case 'a':
if (arg == "opengl") {
app->config.backend = Engine::Backend::OPENGL;
} else if (arg == "vulkan") {
app->config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
app->config.backend = Engine::Backend::METAL;
} else {
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
exit(1);
}
break;
IconScene(Engine* engine, Renderer* renderer, uint32_t w, uint32_t h, Option option)
: engine(engine),
renderer(renderer),
material(engine),
primitives(engine) {
cameraEntity = EntityManager::get().create();
camera = engine->createCamera(cameraEntity);
camera->lookAt(float3(0, 0, -10.f), float3(0, 0, 0), float3(0, 1.0f, 0));
float halfWidth = (QUAD_WIDTH / 2) * option.border;
camera->setProjection(Camera::Projection::ORTHO,
-halfWidth , halfWidth, -halfWidth, halfWidth, -50, 50);
scene = engine->createScene();
view = engine->createView();
view->setViewport({0, 0, w, h});
view->setScene(scene);
view->setCamera(camera);
view->setPostProcessingEnabled(false);
Entity renderable = EntityManager::get().create();
matInstance = material.instance();
RenderableManager::Builder(1)
.boundingBox({{-10, -10, -10}, {10, 10, 10}})
.material(0, matInstance)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES,
primitives.verts(), primitives.inds(), 0, 6)
.culling(false)
.build(*engine, renderable);
scene->addEntity(renderable);
auto& tcm = engine->getTransformManager();
tcm.create(renderable);
auto inst = tcm.getInstance(renderable);
mat4f xlate = mat4f::translation(float3(QUAD_WIDTH / -2.0, QUAD_WIDTH / -2.0, 0));
tcm.setTransform(inst, xlate);
}
void render(Texture* texture, Texture* renderTargetTexture, float3 const& multiplier) {
TextureSampler sampler{TextureSampler::MinFilter::NEAREST,
TextureSampler::MagFilter::NEAREST};
matInstance->setParameter("tex", texture, sampler);
matInstance->setParameter("multiplier", multiplier);
RenderTarget* target;
if (auto itr = targets.find(renderTargetTexture); itr != targets.end()) {
target = itr->second;
} else {
target = RenderTarget::Builder()
.texture(RenderTarget::AttachmentPoint::COLOR, renderTargetTexture)
.build(*engine);
targets[renderTargetTexture] = target;
}
view->setRenderTarget(target);
renderer->renderStandaloneView(view);
}
void renderToSwapChain(SwapChain* swapChain, Texture* texture) {
TextureSampler sampler{TextureSampler::MinFilter::NEAREST,
TextureSampler::MagFilter::NEAREST};
matInstance->setParameter("tex", texture, sampler);
matInstance->setParameter("multiplier", float3 {1});
if (renderer->beginFrame(swapChain)) {
// for each View
renderer->render(view);
renderer->endFrame();
}
}
return optind;
private:
Engine* engine;
Renderer* renderer;
View* view;
Scene* scene;
Camera* camera;
TestMaterial material;
TestPrimitive primitives;
Entity cameraEntity;
Skybox* skybox;
MaterialInstance* matInstance;
std::unordered_map<Texture*, RenderTarget*> targets;
};
struct MainScene {
using TextureMatrix = std::vector<std::vector<Texture*>>;
MainScene(Engine* engine, Renderer* renderer, uint32_t w, uint32_t h, uint32_t nrows,
uint32_t ncols)
: renderer(renderer),
primitives{engine},
material(engine),
engine(engine) {
cameraEntity = EntityManager::get().create();
camera = engine->createCamera(cameraEntity);
camera->lookAt(float3(0, 0, -10.f), float3(0, 0, 0), float3(0, 1.0f, 0));
camera->setProjection(45.0, double(w) / h, 0.1, 50, Camera::Fov::VERTICAL);
scene = engine->createScene();
// skybox = Skybox::Builder().color({0.2, 0.2, 0.2, 1.0}).build(*engine);
// scene->setSkybox(skybox);
view = engine->createView();
view->setViewport({0, 0, w, h});
view->setScene(scene);
view->setCamera(camera);
view->setPostProcessingEnabled(false);
root = EntityManager::get().create();
auto& tcm = engine->getTransformManager();
tcm.create(root);
auto rootInst = tcm.getInstance(root);
mat4f xlate = mat4f::translation(float3(-QUAD_WIDTH / 2 * nrows , -QUAD_WIDTH / 2 * ncols, 0));
tcm.setTransform(rootInst, xlate);
matInst = material.instance();
renderable = EntityManager::get().create();
scene->addEntity(renderable);
RenderableManager::Builder(1)
.boundingBox({{-10, -10, -10}, {10, 10, 10}})
.material(0, matInst)
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES,
primitives.verts(), primitives.inds(), 0, 6)
.culling(false)
.build(*engine, renderable);
auto transformInst = tcm.getInstance(renderable);
tcm.setParent(transformInst, rootInst);
}
void render(Texture* texture, RenderTarget* target, uint32_t r, uint32_t c) {
auto& tcm = engine->getTransformManager();
TextureSampler sampler{TextureSampler::MinFilter::NEAREST,
TextureSampler::MagFilter::NEAREST};
matInst->setParameter("tex", texture, sampler);
matInst->setParameter("multiplier", float3 {1});
auto transformInst = tcm.getInstance(renderable);
mat4f xlate = mat4f::translation(float3(QUAD_WIDTH * r, QUAD_WIDTH * c, 0));
tcm.setTransform(transformInst, xlate);
view->setRenderTarget(target);
renderer->renderStandaloneView(view);
}
void renderToSwapChain(SwapChain* swapChain, Texture* texture, uint32_t r, uint32_t c){
auto& tcm = engine->getTransformManager();
TextureSampler sampler{TextureSampler::MinFilter::NEAREST,
TextureSampler::MagFilter::NEAREST};
matInst->setParameter("tex", texture, sampler);
matInst->setParameter("multiplier", float3 {1});
auto transformInst = tcm.getInstance(renderable);
mat4f xlate = mat4f::translation(float3(QUAD_WIDTH * r, QUAD_WIDTH * c, 0));
tcm.setTransform(transformInst, xlate);
if (renderer->beginFrame(swapChain)) {
// for each View
renderer->render(view);
renderer->endFrame();
}
}
Scene* get() const { return scene; }
Camera* getCamera() const { return camera; }
View* getView() const { return view; }
private:
Renderer* renderer;
TestMaterial material;
MaterialInstance* matInst;
TestPrimitive primitives;
TextureMatrix textures;
Scene* scene;
Entity light;
Entity root;
Entity renderable;
Camera* camera;
Entity cameraEntity;
Skybox* skybox;
View* view;
std::vector<Entity> renderables;
Engine* engine;
};
}
int main(int argc, char** argv) {
App app{};
app.config.title = "hellotriangle";
app.config.featureLevel = backend::FeatureLevel::FEATURE_LEVEL_0;
handleCommandLineArguments(argc, argv, &app);
int main() {
SDL_Init(SDL_INIT_EVERYTHING);
auto const [window, nativeWindow] = createSDLwindow();
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
app.skybox = Skybox::Builder().color({0.1, 0.125, 0.25, 1.0}).build(*engine);
scene->setSkybox(app.skybox);
view->setPostProcessingEnabled(false);
static_assert(sizeof(Vertex) == 12, "Strange vertex size.");
app.vb = VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.build(*engine);
app.vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
app.ib = IndexBuffer::Builder()
.indexCount(3)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.ib->setBuffer(*engine,
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
app.mat = Material::Builder()
.package(RESOURCES_BAKEDCOLOR_DATA, RESOURCES_BAKEDCOLOR_SIZE)
.build(*engine);
app.renderable = EntityManager::get().create();
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, app.mat->getDefaultInstance())
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, app.renderable);
scene->addEntity(app.renderable);
app.camera = utils::EntityManager::get().create();
app.cam = engine->createCamera(app.camera);
view->setCamera(app.cam);
Engine* engine = Engine::create(filament::backend::Backend::VULKAN);
SwapChain* swapChain =
engine->createSwapChain(nativeWindow, filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER);
Renderer* renderer = engine->createRenderer();
// Determine the current size of the window in physical pixels.
uint32_t w, h;
SDL_GL_GetDrawableSize(window, (int*) &w, (int*) &h);
filamat::MaterialBuilder::init();
TestTexture matTexture {engine, 27, 27};
size_t const nrow = 10, ncol = 10;
uint32_t const iconWidth = 50;
uint32_t const iconHeight = 50;
IconScene iconScene {engine, renderer, iconWidth, iconHeight, {.useSkybox=false} };
auto buildTargets = [nrow, ncol, engine]() {
std::vector<std::vector<Texture*>> targets;
for (size_t r = 0; r < nrow; r++) {
std::vector<Texture*> row;
for (size_t c = 0; c < ncol; c++) {
TestTexture ttex {engine, iconWidth, iconHeight, true };
row.push_back(ttex.get());
}
targets.push_back(row);
}
return targets;
};
auto cleanup = [&app](Engine* engine, View*, Scene*) {
engine->destroy(app.skybox);
engine->destroy(app.renderable);
engine->destroy(app.mat);
engine->destroy(app.vb);
engine->destroy(app.ib);
engine->destroyCameraComponent(app.camera);
utils::EntityManager::get().destroy(app.camera);
};
std::vector<std::vector<Texture*>> targets0 = buildTargets();
std::vector<std::vector<Texture*>> targets1 = buildTargets();
std::vector<std::vector<Texture*>> targets2 = buildTargets();
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
constexpr float ZOOM = 1.5f;
const uint32_t w = view->getViewport().width;
const uint32_t h = view->getViewport().height;
const float aspect = (float) w / h;
app.cam->setProjection(Camera::Projection::ORTHO,
-aspect * ZOOM, aspect * ZOOM,
-ZOOM, ZOOM, 0, 1);
auto& tcm = engine->getTransformManager();
tcm.setTransform(tcm.getInstance(app.renderable),
filament::math::mat4f::rotation(now, filament::math::float3{ 0, 0, 1 }));
});
MainScene scene(engine, renderer, w, h, nrow, ncol);
FilamentApp::get().run(app.config, setup, cleanup);
renderer->setClearOptions({.clear = false});
float3 const a {.7, .3, .3};
float3 const b {.3, .7, .3};
std::vector<std::pair<size_t, size_t>> coords;
for (size_t r = 0; r < nrow; r++) {
for (size_t c = 0; c < ncol; c++) {
coords.push_back({r, c});
}
}
IconScene swapChainScene{engine, renderer, w, h, {
.border = 1.0f, .postProcessing = false}};
TestTexture mainTex{engine, w, h, true};
auto mainRT = RenderTarget::Builder()
.texture(RenderTarget::AttachmentPoint::COLOR, mainTex.get())
.build(*engine);
bool closeWindow = false;
size_t count = 0;
while (!closeWindow && ++count) {
constexpr int kMaxEvents = 16;
SDL_Event events[kMaxEvents];
int nevents = 0;
while (nevents < kMaxEvents && SDL_PollEvent(&events[nevents]) != 0) {
nevents++;
}
// Now, loop over the events a second time for app-side processing.
for (int i = 0; i < nevents; i++) {
const SDL_Event& event = events[i];
switch (event.type) {
case SDL_QUIT:
closeWindow = true;
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
break;
default:
break;
}
break;
default:
break;
}
}
auto [r, c] = coords[count % coords.size()];
float3 color0 = (r + c) % 2 == 0 ? a : b;
float3 color1 = (r + c) % 2 == 0 ? b : a;
auto rt0 = targets0[r][c];
auto rt1 = targets1[r][c];
auto rt2 = targets2[r][c];
iconScene.render(matTexture.get(), rt0, color0);
iconScene.render(rt0, rt1, color1);
iconScene.render(rt1, rt2, float3 {.8});
// engine->flushAndWait();
scene.render(rt2, mainRT, r, c);
// engine->flushAndWait();
// engine->flush();
// scene.renderToSwapChain(swapChain, rt1, r, c);
if (count % 27 == 0) {
swapChainScene.renderToSwapChain(swapChain, mainTex.get());
}
SDL_Delay(16);
// if (renderer->beginFrame(swapChain)) {
// // for each View
// renderer->render(scene.getView());
// renderer->endFrame();
// }
// SDL_Delay(16);
}
return 0;
}
-39
View File
@@ -1,39 +0,0 @@
material {
name : ArrayTexture,
parameters : [
{
type : sampler2dArray,
name : image
},
{
type : int,
name : layerIndex
},
{
type : bool,
name : borderEffect
}
],
requires : [
uv0
],
shadingModel : unlit,
culling : none
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
float3 v = texture(materialParams_image, vec3(getUV0(), materialParams.layerIndex)).rgb;
material.baseColor.rgb = v;
// Add black border effect.
if (materialParams.borderEffect) {
vec2 st = getUV0();
float minDist0 = min(st.x, st.y);
float minDist1 = min(1.0 - st.x, 1.0 - st.y);
float minDist = min(minDist0, minDist1);
material.baseColor.rgb *= smoothstep(0.0, 0.1, minDist);
}
}
}
+26
View File
@@ -0,0 +1,26 @@
material {
name : Icon,
shadingModel : unlit,
requires : [
uv0
],
parameters : [
{
type : sampler2d,
name : tex
},
{
type : float3,
name : multiplier
}
],
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec3 m = materialParams.multiplier;
vec3 rgb = texture(materialParams_tex, getUV0()).rgb * m;
material.baseColor = vec4(rgb, 1.0);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.54.4",
"version": "1.54.1",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",