Compare commits

...

27 Commits

Author SHA1 Message Date
raviola
8e72e67bce Add fgdbg::Session.h, Pass.h, Resource.h dtos/models 2022-11-04 15:43:42 +11:00
raviola
a0c486f608 pr feedback 2022-10-21 14:28:59 +11:00
raviola
eb7447fd17 fgdbg client project structure + simple server connection 2022-10-20 13:48:48 +11:00
Fernando Raviola
a870d01475 Add DebugServer for fgdbg with support for Websockets (#6169)
* Add dummy DebugServer for fgdbg with support for Websockets

* PR feedback
2022-10-20 11:00:13 +11:00
raviola
83e747b732 rename debug.server to debug.matdbg 2022-10-20 11:00:13 +11:00
raviola
ca5cc32dc1 Use smart pointer 2022-10-20 11:00:13 +11:00
raviola
5c03e290c2 PR suggestions 2022-10-20 11:00:13 +11:00
Fernando Raviola
e4953ada3f Concat namespaces
Co-authored-by: Mathias Agopian <mathias@google.com>
2022-10-20 11:00:13 +11:00
Fernando Raviola
10022f983b Add FrameGraph debugger lib: package structure + CmakeLists.txt 2022-10-20 11:00:13 +11:00
Benjamin Doherty
977d369e40 Fix unused variable warnings 2022-10-19 12:44:07 -04:00
Josh Faust
5cf5b25175 Add a function to reset internal GL backend state (#6105)
This allows bug-free integration with external renderers in WebGL

See https://github.com/google/filament/issues/6091
2022-10-19 09:31:11 -07:00
Ben Doherty
e75c3020c8 Initial support for Metal compute shaders / SSBOs (#6189) 2022-10-18 16:21:57 -04:00
k1rnt
aeb0a4008d fix(ci): replace set-output to GITHUB_OUTPUT (#6195) 2022-10-18 13:41:25 -04:00
Benjamin Doherty
2d43ad59ec Revert Android API_LEVEL change
glDispatchCompute requires Android API level 21, however bumping our
required minimum from 19 to 21 caused some clients' builds to fail.
Commenting-out that line for now to proceed with the 1.28.0 upgrade.
2022-10-18 13:34:50 -04:00
daemyung jang
f86ec07c45 Fix ios hello-gltf runtime errors 2022-10-18 10:19:22 -07:00
Ben Doherty
fc012d6d65 Metal: fix use-after-free ASAN error (#6203) 2022-10-17 19:13:36 -04:00
Philip Rideout
6b71232e38 matc / matinfo / filamesh: add vulnerability message 2022-10-17 15:49:35 -07:00
Philip Rideout
e157023fc1 Use mField style for fields in FFilamentInstance 2022-10-17 15:49:19 -07:00
Philip Rideout
6e38db882f Fix installed IBL location for desktop.
Fixes #6193
2022-10-17 12:30:11 -07:00
daemyung jang
09600699cb Update README.md 2022-10-17 09:57:58 -07:00
daemyung jang
6a5e74f031 Update README for meshoptimizer 2022-10-17 09:57:58 -07:00
daemyung jang
795bd1e995 Fix build errors in ios samples 2022-10-17 09:57:58 -07:00
daemyung jang
784416f658 Support glTF EXT_meshopt_compression 2022-10-17 09:57:58 -07:00
daemyung jang
b151afe567 Update meshoptimizer to version 0.18 2022-10-17 09:57:58 -07:00
Mathias Agopian
117ae1d648 RenderPass doesn't store an FEngine anymore 2022-10-14 15:59:03 -07:00
Mathias Agopian
7165d8aa54 fix a directional light shadowing issue
the shadow frustum could be be smaller than expected.
2022-10-14 13:16:14 -07:00
Ben Doherty
6b461b2d89 Metal: fix simulator regression due to arg buffers (#6181) 2022-10-13 14:34:33 -04:00
174 changed files with 41679 additions and 2163 deletions

View File

@@ -39,8 +39,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -69,8 +69,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -97,8 +97,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -143,8 +143,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -171,8 +171,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
shell: bash
- uses: actions/checkout@v2.0.0
with:

View File

@@ -459,6 +459,13 @@ else()
option(FILAMENT_ENABLE_MATDBG "Enable the material debugger" OFF)
endif()
# By default, link in fgdbg for Desktop + Debug
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND IS_HOST_PLATFORM)
option(FILAMENT_ENABLE_FGDBG "Enable the framegraph debugger" ON)
else()
option(FILAMENT_ENABLE_FGDBG "Enable the framegraph debugger" OFF)
endif()
# Only optimize materials in Release mode (so error message lines match the source code)
if (CMAKE_BUILD_TYPE MATCHES Release)
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" OFF)
@@ -666,7 +673,7 @@ add_subdirectory(${EXTERNAL}/imgui/tnt)
add_subdirectory(${EXTERNAL}/robin-map/tnt)
add_subdirectory(${EXTERNAL}/smol-v/tnt)
add_subdirectory(${EXTERNAL}/benchmark/tnt)
add_subdirectory(${EXTERNAL}/meshoptimizer)
add_subdirectory(${EXTERNAL}/meshoptimizer/tnt)
add_subdirectory(${EXTERNAL}/cgltf/tnt)
add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
@@ -685,6 +692,10 @@ if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
if (FILAMENT_ENABLE_MATDBG OR IS_HOST_PLATFORM)
add_subdirectory(${LIBRARIES}/matdbg)
endif()
if (FILAMENT_ENABLE_FGDBG OR IS_HOST_PLATFORM)
add_subdirectory(${LIBRARIES}/fgdbg)
endif()
endif()
if (FILAMENT_SUPPORTS_VULKAN)

View File

@@ -179,6 +179,7 @@ steps:
- [x] KHR_mesh_quantization
- [x] KHR_texture_basisu
- [x] KHR_texture_transform
- [x] EXT_meshopt_compression
## Rendering with Filament
@@ -312,6 +313,7 @@ and tools.
- `bluegl`: OpenGL bindings for macOS, Linux and Windows
- `bluevk`: Vulkan bindings for macOS, Linux, Windows and Android
- `camutils`: Camera manipulation utilities
- `fgdbg`: Frame Graph inspector and debugger (debug builds only)
- `filabridge`: Library shared by the Filament engine and host tools
- `filaflat`: Serialization/deserialization library used for materials
- `filagui`: Helper library for [Dear ImGui](https://github.com/ocornut/imgui)

View File

@@ -5,6 +5,9 @@ A new header is inserted each time a *tag* is created.
## main branch
- gltfio: support EXT_meshopt_compression
- release packaging: fixed location of default IBL file
## v1.28.1
- gltfio: fix reloading crash in ubershader mode
@@ -25,6 +28,7 @@ A new header is inserted each time a *tag* is created.
- Metal: newer devices are no longer limited to 16 samplers per Material.
- gltfio: fix interpretation of occlusion strength
- engine: minsdk is now 21 instead of 19. This allows the use of OpenGL ES 3.1
- Vulkan: fix black screen regression
## v1.27.2
@@ -34,6 +38,7 @@ A new header is inserted each time a *tag* is created.
- gltfio: fix regression with meshes that have no material
- gltfio: fix regression with recomputeBoundingBoxes()
- filamesh / matinfo: fix minor ASAN issues
- engine: Added `Engine::resetBackendState()`, available only in WebGL builds
## v1.27.1

View File

@@ -72,7 +72,7 @@ buildscript {
}
ext.versions = [
'minSdk': 21,
'minSdk': 19,
'targetSdk': 33,
'compileSdk': 33,
'kotlin': '1.7.10',

View File

@@ -11,6 +11,10 @@ add_library(dracodec STATIC IMPORTED)
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
add_library(meshoptimizer STATIC IMPORTED)
set_target_properties(meshoptimizer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libmeshoptimizer.a)
add_library(ktxreader STATIC IMPORTED)
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
@@ -99,6 +103,7 @@ set(GLTFIO_INCLUDE_DIRS
../../libs/gltfio/include
../../third_party/basisu/zstd
../../third_party/cgltf
../../third_party/meshoptimizer/src
../../third_party/robin-map
../../third_party/hat-trie
../../third_party/stb
@@ -111,7 +116,7 @@ target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec)
target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)

View File

@@ -30,7 +30,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.gltf"
minSdkVersion 21
minSdkVersion 19
targetSdkVersion versions.targetSdk
missingDimensionStrategy 'functionality', 'full'
}

View File

@@ -162,6 +162,8 @@ EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
MATDBG_GRADLE_OPTION=""
FGDBG_OPTION="-DFILAMENT_ENABLE_FGDBG=OFF"
IOS_BUILD_SIMULATOR=false
BUILD_UNIVERSAL_LIBRARIES=false
@@ -221,6 +223,7 @@ function build_desktop_target {
${SWIFTSHADER_OPTION} \
${EGL_ON_LINUX_OPTION} \
${MATDBG_OPTION} \
${FGDBG_OPTION} \
${deployment_target} \
${architectures} \
../..
@@ -347,6 +350,7 @@ function build_android_target {
-DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \
-DCMAKE_TOOLCHAIN_FILE="../../build/toolchain-${arch}-linux-android.cmake" \
${MATDBG_OPTION} \
${FGDBG_OPTION} \
${VULKAN_ANDROID_OPTION} \
../..
fi
@@ -576,6 +580,7 @@ function build_ios_target {
-DIOS=1 \
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
${MATDBG_OPTION} \
${FGDBG_OPTION} \
../..
fi
@@ -760,6 +765,7 @@ while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
PRINT_MATDBG_HELP=true
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_DISABLE_MATOPT=ON, -DFILAMENT_BUILD_FILAMAT=ON"
MATDBG_GRADLE_OPTION="-Pcom.google.android.filament.matdbg"
FGDBG_OPTION="-DFILAMENT_ENABLE_FGDBG=ON"
;;
f)
ISSUE_CMAKE_ALWAYS=true

View File

@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH armv7a-linux-androideabi)

View File

@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH i686-linux-android)

View File

@@ -537,6 +537,13 @@ target_link_libraries(${TARGET} PUBLIC filaflat)
target_link_libraries(${TARGET} PUBLIC filabridge)
target_link_libraries(${TARGET} PUBLIC ibl-lite)
if (FILAMENT_ENABLE_FGDBG)
target_link_libraries(${TARGET} PUBLIC fgdbg)
add_definitions(-DFILAMENT_ENABLE_FGDBG=1)
else()
add_definitions(-DFILAMENT_ENABLE_FGDBG=0)
endif()
if (FILAMENT_ENABLE_MATDBG)
target_link_libraries(${TARGET} PUBLIC matdbg)
add_definitions(-DFILAMENT_ENABLE_MATDBG=1)

View File

@@ -158,6 +158,9 @@ DECL_DRIVER_API_0(flush)
// flush and wait for the effects to be done
DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
/*
* Creating driver objects
* -----------------------

View File

@@ -27,13 +27,15 @@
#include <utils/compiler.h>
#include <utility>
#include <memory>
namespace filament::backend {
class MetalBuffer {
public:
MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer = false);
MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
MetalBuffer(const MetalBuffer& rhs) = delete;
@@ -60,18 +62,21 @@ public:
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
enum Stage {
VERTEX = 1,
FRAGMENT = 2
enum Stage : uint8_t {
VERTEX = 1u << 0u,
FRAGMENT = 1u << 1u,
COMPUTE = 1u << 2u
};
/**
* Bind multiple buffers to pipeline stages.
*
* bindBuffers binds an array of buffers to the given stage(s) of a MTLRenderCommandEncoder's
* pipeline.
* bindBuffers binds an array of buffers to the given stage(s) of a MTLCommandEncoders's
* pipeline. The encoder must be either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
* For MTLRenderCommandEncoders, only the VERTEX and FRAGMENT stages may be specified.
* For MTLComputeCommandEncoders, only the COMPUTE stage may be specified.
*/
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
size_t count);
@@ -106,7 +111,12 @@ public:
// address space. Constant buffers have specific alignment requirements when specifying an
// offset.
#if defined(IOS)
#if TARGET_OS_SIMULATOR
// The iOS simulator has differing alignment requirements.
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256;
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 4;
#endif // TARGET_OS_SIMULATOR
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 32;
#endif
@@ -132,7 +142,7 @@ public:
* @return the id<MTLBuffer> and offset for the new allocation
*/
std::pair<id<MTLBuffer>, NSUInteger> createNewAllocation(id<MTLCommandBuffer> cmdBuffer) {
const auto occupiedSlots = mOccupiedSlots.load(std::memory_order_relaxed);
const auto occupiedSlots = mOccupiedSlots->load(std::memory_order_relaxed);
assert_invariant(occupiedSlots <= mSlotCount);
if (UTILS_UNLIKELY(occupiedSlots == mSlotCount)) {
// We don't have any room left, so we fall back to creating a one-off aux buffer.
@@ -144,14 +154,19 @@ public:
return {mAuxBuffer, 0};
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots.fetch_add(1, std::memory_order_relaxed);
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
// Release the previous allocation.
if (UTILS_UNLIKELY(mAuxBuffer)) {
mAuxBuffer = nil;
} else {
// Capture the mOccupiedSlots var via a weak_ptr because the MetalRingBuffer could be
// destructed before the block executes.
std::weak_ptr<AtomicCounterType> slots = mOccupiedSlots;
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
mOccupiedSlots.fetch_sub(1, std::memory_order_relaxed);
if (auto s = slots.lock()) {
s->fetch_sub(1, std::memory_order_relaxed);
}
}];
}
return getCurrentAllocation();
@@ -185,7 +200,8 @@ private:
NSUInteger mSlotCount;
NSUInteger mCurrentSlot = 0;
std::atomic<NSUInteger> mOccupiedSlots = 1;
using AtomicCounterType = std::atomic<NSUInteger>;
std::shared_ptr<AtomicCounterType> mOccupiedSlots = std::make_shared<AtomicCounterType>(1);
};
} // namespace filament::backend

View File

@@ -21,15 +21,17 @@
namespace filament {
namespace backend {
MetalBuffer::MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer)
: mBufferSize(size), mContext(context) {
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
if (size <= 4 * 1024 && usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
// This won't work for SSBOs, since they are read/write.
if (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE &&
usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
// Otherwise, we allocate a private GPU buffer.
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
@@ -93,9 +95,24 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
size_t count) {
// Ensure we were given the correct type of encoder:
// either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
if (stages & MetalBuffer::Stage::VERTEX || stages & MetalBuffer::Stage::FRAGMENT) {
assert_invariant([encoder respondsToSelector:@selector(setVertexBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setFragmentBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setVertexBytes:length:atIndex:)]);
assert_invariant([encoder respondsToSelector:@selector(setFragmentBytes:length:atIndex:)]);
assert_invariant(!(stages & MetalBuffer::Stage::COMPUTE));
}
if (stages & MetalBuffer::Stage::COMPUTE) {
assert_invariant([encoder respondsToSelector:@selector(setBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setBytes:length:atIndex:)]);
assert_invariant(!(stages & (MetalBuffer::Stage::FRAGMENT | MetalBuffer::Stage::VERTEX)));
}
const NSRange bufferRange = NSMakeRange(bufferStart, count);
constexpr size_t MAX_BUFFERS = 16;
@@ -120,14 +137,19 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderComman
}
if (stages & Stage::VERTEX) {
[encoder setVertexBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
[(id<MTLRenderCommandEncoder>) encoder setVertexBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
if (stages & Stage::FRAGMENT) {
[encoder setFragmentBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
[(id<MTLRenderCommandEncoder>) encoder setFragmentBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
if (stages & Stage::COMPUTE) {
[(id<MTLComputeCommandEncoder>) encoder setBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
for (size_t b = 0; b < count; b++) {
@@ -146,14 +168,22 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderComman
auto* bytes = static_cast<const uint8_t*>(cpuBuffer);
if (stages & Stage::VERTEX) {
[encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
[(id<MTLRenderCommandEncoder>) encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::FRAGMENT) {
[encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
[(id<MTLRenderCommandEncoder>) encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::COMPUTE) {
// TODO: using setBytes means the data is read-only, which currently isn't enforced.
// In practice this won't be an issue since MetalBuffer ensures all SSBOs are realized
// through actual id<MTLBuffer> allocations.
[(id<MTLComputeCommandEncoder>) encoder setBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
}
}

View File

@@ -84,7 +84,8 @@ struct MetalContext {
// State trackers.
PipelineStateTracker pipelineState;
DepthStencilStateTracker depthStencilState;
UniformBufferState uniformState[Program::UNIFORM_BINDING_COUNT];
std::array<BufferState, Program::UNIFORM_BINDING_COUNT> uniformState;
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;

View File

@@ -39,7 +39,7 @@ class MetalTexture;
struct MetalUniformBuffer;
struct MetalContext;
struct MetalProgram;
struct UniformBufferState;
struct BufferState;
#ifndef FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB
#define FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB 8
@@ -129,8 +129,8 @@ private:
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void finalizeSamplerGroup(MetalSamplerGroup* sg);
void enumerateBoundUniformBuffers(const std::function<void(const UniformBufferState&,
MetalBuffer*, uint32_t)>& f);
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
};

View File

@@ -243,7 +243,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
construct_handle<MetalBufferObject>(boh, *mContext, usage, byteCount);
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -481,10 +481,20 @@ void MetalDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
return;
}
auto* bo = handle_cast<MetalBufferObject>(boh);
// Unbind this buffer object from any uniform slots it's still bound to.
// Unbind this buffer object from any uniform / SSBO slots it's still bound to.
bo->boundUniformBuffers.forEachSetBit([this](size_t index) {
mContext->uniformState[index].buffer = nullptr;
mContext->uniformState[index].bound = false;
mContext->uniformState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
});
bo->boundSsbos.forEachSetBit([this](size_t index) {
mContext->ssboState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
});
destruct_handle<MetalBufferObject>(boh);
}
@@ -1051,7 +1061,7 @@ void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
currentBo->boundUniformBuffers.unset(index);
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = UniformBufferState{
mContext->uniformState[index] = BufferState{
.buffer = bo,
.offset = 0,
.bound = true
@@ -1064,24 +1074,81 @@ void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t inde
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* bo = handle_cast<MetalBufferObject>(boh);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
switch (bindingType) {
default:
case BufferObjectBinding::UNIFORM: {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = BufferState {
.buffer = bo,
.offset = offset,
.bound = true
};
break;
}
case BufferObjectBinding::SHADER_STORAGE: {
assert_invariant(index < MAX_SSBO_COUNT);
auto* currentBo = mContext->ssboState[index].buffer;
if (currentBo) {
currentBo->boundSsbos.unset(index);
}
bo->boundSsbos.set(index);
mContext->ssboState[index] = BufferState {
.buffer = bo,
.offset = offset,
.bound = true
};
break;
}
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = UniformBufferState{
.buffer = bo,
.offset = offset,
.bound = true
};
}
void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
// TODO: implement unbindBuffer()
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
switch (bindingType) {
default:
case BufferObjectBinding::UNIFORM: {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
mContext->uniformState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
break;
}
case BufferObjectBinding::SHADER_STORAGE: {
assert_invariant(index < MAX_SSBO_COUNT);
auto* currentBo = mContext->ssboState[index].buffer;
if (currentBo) {
currentBo->boundSsbos.unset(index);
}
mContext->ssboState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
break;
}
}
}
void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
@@ -1552,8 +1619,9 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundUniformBuffers([&uniformsToBind, &offsets](const UniformBufferState& state,
MetalBuffer* buffer, uint32_t index) {
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
@@ -1635,7 +1703,67 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute must be called outside of a render pass.");
auto mtlProgram = handle_cast<MetalProgram>(program);
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!mtlProgram->isValid)) {
return;
}
assert_invariant(mtlProgram->isValid && mtlProgram->computeFunction);
id<MTLComputeCommandEncoder> computeEncoder =
[getPendingCommandBuffer(mContext) computeCommandEncoder];
NSError* error = nil;
id<MTLComputePipelineState> computePipelineState =
[mContext->device newComputePipelineStateWithFunction:mtlProgram->computeFunction
error:&error];
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.e << description << utils::io::endl;
}
assert_invariant(!error);
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger uniformOffsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &uniformOffsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
uniformOffsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::COMPUTE, uniformsToBind,
uniformOffsets, Program::UNIFORM_BINDING_COUNT);
// Bind SSBOs.
MetalBuffer* ssbosToBind[MAX_SSBO_COUNT] = { nil };
NSUInteger ssboOffsets[MAX_SSBO_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::SHADER_STORAGE,
[&ssbosToBind, &ssboOffsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
ssbosToBind[index] = buffer;
ssboOffsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder, SSBO_BINDING_START,
MetalBuffer::Stage::COMPUTE, ssbosToBind, ssboOffsets, MAX_SSBO_COUNT);
[computeEncoder setComputePipelineState:computePipelineState];
MTLSize threadgroupsPerGrid = MTLSizeMake(workGroupCount.x, workGroupCount.y, workGroupCount.z);
// FIXME: the threadgroup size should be specified in the Program
MTLSize threadsPerThreadgroup = MTLSizeMake(16u, 1u, 1u);
[computeEncoder dispatchThreadgroups:threadgroupsPerGrid
threadsPerThreadgroup:threadsPerThreadgroup];
[computeEncoder endEncoding];
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
@@ -1652,17 +1780,35 @@ void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
mContext->timerQueryImpl->endTimeElapsedQuery(tq);
}
void MetalDriver::enumerateBoundUniformBuffers(
const std::function<void(const UniformBufferState&, MetalBuffer*, uint32_t)>& f) {
for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) {
auto& thisUniform = mContext->uniformState[i];
if (!thisUniform.bound) {
continue;
void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f) {
assert_invariant(bindingType == BufferObjectBinding::UNIFORM ||
bindingType == BufferObjectBinding::SHADER_STORAGE);
auto enumerate = [&](auto arrayType){
for (auto i = 0u; i < arrayType.size(); i++) {
const auto& thisBuffer = arrayType[i];
if (!thisBuffer.bound) {
continue;
}
f(thisBuffer, thisBuffer.buffer->getBuffer(), i);
}
f(thisUniform, thisUniform.buffer->getBuffer(), i);
};
switch (bindingType) {
default:
case (BufferObjectBinding::UNIFORM):
enumerate(mContext->uniformState);
break;
case (BufferObjectBinding::SHADER_STORAGE):
enumerate(mContext->ssboState);
break;
}
}
void MetalDriver::resetState(int) {
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<MetalDriver>;

View File

@@ -118,15 +118,18 @@ private:
class MetalBufferObject : public HwBufferObject {
public:
MetalBufferObject(MetalContext& context, BufferUsage usage, uint32_t byteCount);
MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
uint32_t byteCount);
void updateBuffer(void* data, size_t size, uint32_t byteOffset);
void updateBufferUnsynchronized(void* data, size_t size, uint32_t byteOffset);
MetalBuffer* getBuffer() { return &buffer; }
// Tracks which uniform buffers this buffer object is bound into.
// Tracks which uniform/ssbo buffers this buffer object is bound into.
static_assert(Program::UNIFORM_BINDING_COUNT <= 32);
static_assert(MAX_SSBO_COUNT <= 32);
utils::bitset32 boundUniformBuffers;
utils::bitset32 boundSsbos;
private:
MetalBuffer buffer;
@@ -163,6 +166,7 @@ struct MetalProgram : public HwProgram {
id<MTLFunction> vertexFunction;
id<MTLFunction> fragmentFunction;
id<MTLFunction> computeFunction;
Program::SamplerGroupInfo samplerGroupInfo;

View File

@@ -26,6 +26,7 @@
#include "private/backend/BackendUtils.h"
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/trap.h>
#include <utils/debug.h>
@@ -270,8 +271,9 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
}];
}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, usage, byteCount) {}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType,
BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, bindingType, usage, byteCount) {}
void MetalBufferObject::updateBuffer(void* data, size_t size, uint32_t byteOffset) {
buffer.copyIntoBuffer(data, size, byteOffset);
@@ -287,7 +289,7 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, usage, elementSize * indexCount, true) { }
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
@@ -338,13 +340,12 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcept
: HwProgram(program.getName()), vertexFunction(nil), fragmentFunction(nil),
isValid(false) {
computeFunction(nil), isValid(false) {
using MetalFunctionPtr = __strong id<MTLFunction>*;
// FIXME: rework this to handle compute
//static_assert(Program::SHADER_TYPE_COUNT == 2, "Only vertex and fragment shaders expected.");
MetalFunctionPtr shaderFunctions[2] = { &vertexFunction, &fragmentFunction };
static_assert(Program::SHADER_TYPE_COUNT == 3, "Only vertex, fragment, and/or compute shaders expected.");
MetalFunctionPtr shaderFunctions[3] = { &vertexFunction, &fragmentFunction, &computeFunction };
const auto& sources = program.getShadersSource();
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
@@ -387,12 +388,19 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
}, sc.value);
}
*shaderFunctions[i] = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
assert_invariant(function);
*shaderFunctions[i] = function;
}
UTILS_UNUSED_IN_RELEASE const bool isRasterizationProgram =
vertexFunction != nil && fragmentFunction != nil;
UTILS_UNUSED_IN_RELEASE const bool isComputeProgram = computeFunction != nil;
// The program must be either a rasterization program XOR a compute program.
assert_invariant(isRasterizationProgram != isComputeProgram);
// All stages of the program have compiled successfully, this is a valid program.
isValid = true;

View File

@@ -37,6 +37,8 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
return lhs.u == rhs.u;
}
// Rasterization Bindings
// ----------------------
// Bindings Buffer name Count
// ------------------------------------------------------
// 0 Zero buffer (placeholder vertex buffer) 1
@@ -46,6 +48,16 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
//
// Total 31
// Compute Bindings
// ----------------------
// Bindings Buffer name Count
// ------------------------------------------------------
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 18
// The total number of vertex buffer "slots" that the Metal backend can bind.
// + 1 to account for the zero buffer, a placeholder buffer used internally by the Metal backend.
// MAX_VERTEX_BUFFER_COUNT represents the max number of vertex buffers Filament can bind.
@@ -59,6 +71,7 @@ static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u;
// These constants must match the equivalent in CodeGenerator.h.
static constexpr uint32_t UNIFORM_BUFFER_BINDING_START = 17u;
static constexpr uint32_t SSBO_BINDING_START = 0u;
static constexpr uint32_t SAMPLER_GROUP_BINDING_START = 27u;
// Forward declarations necessary here, definitions at end of file.
@@ -335,27 +348,12 @@ using DepthStencilStateCache = StateCache<DepthStencilState, id<MTLDepthStencilS
class MetalBufferObject;
struct UniformBufferState {
struct BufferState {
MetalBufferObject* buffer = nullptr; // 8 bytes
uint32_t offset = 0; // 4 bytes
bool bound = false; // 1 byte
char padding[3] = { 0 }; // 3 bytes
bool operator==(const UniformBufferState& rhs) const noexcept {
return this->bound == rhs.bound &&
this->buffer == rhs.buffer &&
this->offset == rhs.offset;
}
bool operator!=(const UniformBufferState& rhs) const noexcept {
return !operator==(rhs);
}
};
static_assert(sizeof(UniformBufferState) == 16, "UniformBufferState unexpected size.");
using UniformBufferStateTracker = StateTracker<UniformBufferState>;
// Sampler states
struct SamplerState {

View File

@@ -328,4 +328,7 @@ void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
}
void NoopDriver::resetState(int) {
}
} // namespace filament

View File

@@ -61,6 +61,8 @@ OpenGLContext::OpenGLContext() noexcept {
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gets.max_draw_buffers);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &gets.max_transform_feedback_separate_attribs);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &gets.max_uniform_buffer_bindings);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &gets.uniform_buffer_offset_alignment);
constexpr auto const caps3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
@@ -247,31 +249,7 @@ OpenGLContext::OpenGLContext() noexcept {
flush(slog.v);
#endif
/*
* Set our default state
*/
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_DITHER);
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
glDisable(GL_SAMPLE_COVERAGE);
glDisable(GL_POLYGON_OFFSET_FILL);
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
enable(GL_PROGRAM_POINT_SIZE);
#endif
#ifdef GL_ARB_seamless_cube_map
enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
setDefaultState();
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
@@ -290,10 +268,6 @@ OpenGLContext::OpenGLContext() noexcept {
}
#endif
#ifdef GL_FRAGMENT_SHADER_DERIVATIVE_HINT
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if !defined(NDEBUG) && defined(GL_KHR_debug)
if (ext.KHR_debug) {
auto cb = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
@@ -325,6 +299,50 @@ OpenGLContext::OpenGLContext() noexcept {
glDebugMessageCallback(cb, nullptr);
}
#endif
}
void OpenGLContext::setDefaultState() noexcept {
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
GLenum caps[] = {
GL_BLEND,
GL_CULL_FACE,
GL_SCISSOR_TEST,
GL_DEPTH_TEST,
GL_STENCIL_TEST,
GL_DITHER,
GL_SAMPLE_ALPHA_TO_COVERAGE,
GL_SAMPLE_COVERAGE,
GL_POLYGON_OFFSET_FILL,
};
size_t capsCount = sizeof(caps) / sizeof(caps[0]);
UTILS_NOUNROLL
for (GLenum capi = 0; capi < capsCount; ++capi) {
size_t capIndex = getIndexForCap(caps[capi]);
GLenum cap = caps[capi];
if (state.enables.caps[capIndex]) {
glEnable(cap);
} else {
glDisable(cap);
}
}
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
glEnable(GL_PROGRAM_POINT_SIZE);
enable(GL_PROGRAM_POINT_SIZE);
#endif
#ifdef GL_ARB_seamless_cube_map
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
#ifdef GL_FRAGMENT_SHADER_DERIVATIVE_HINT
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if defined(GL_EXT_clip_control) || defined(GL_ARB_clip_control) || defined(GL_VERSION_4_5)
if (ext.EXT_clip_control) {
@@ -521,4 +539,161 @@ void OpenGLContext::deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept
}
}
void OpenGLContext::resetState() noexcept {
// Force GL state to match the Filament state
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, state.draw_fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, state.read_fbo);
// state.program
glUseProgram(state.program.use);
// state.vao
if (state.vao.p) {
glBindVertexArray(state.vao.p->vao);
} else {
bindVertexArray(nullptr);
}
// state.raster
glFrontFace(state.raster.frontFace);
glCullFace(state.raster.cullFace);
glBlendEquationSeparate(state.raster.blendEquationRGB, state.raster.blendEquationA);
glBlendFuncSeparate(
state.raster.blendFunctionSrcRGB,
state.raster.blendFunctionDstRGB,
state.raster.blendFunctionSrcA,
state.raster.blendFunctionDstA
);
glColorMask(
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask
);
glDepthMask(state.raster.depthMask);
glDepthFunc(state.raster.depthFunc);
// state.stencil
glStencilFuncSeparate(
GL_FRONT,
state.stencil.front.func.func,
state.stencil.front.func.ref,
state.stencil.front.func.mask
);
glStencilFuncSeparate(
GL_BACK,
state.stencil.back.func.func,
state.stencil.back.func.ref,
state.stencil.back.func.mask
);
glStencilOpSeparate(
GL_FRONT,
state.stencil.front.op.sfail,
state.stencil.front.op.dpfail,
state.stencil.front.op.dppass
);
glStencilOpSeparate(
GL_BACK,
state.stencil.back.op.sfail,
state.stencil.back.op.dpfail,
state.stencil.back.op.dppass
);
glStencilMaskSeparate(GL_FRONT, state.stencil.front.stencilMask);
glStencilMaskSeparate(GL_BACK, state.stencil.back.stencilMask);
// state.polygonOffset
glPolygonOffset(state.polygonOffset.factor, state.polygonOffset.units);
// state.enables
setDefaultState();
// state.buffers
// Reset state.buffers to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.buffers = {};
GLenum bufferTargets[] = {
GL_UNIFORM_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
#if !defined(__EMSCRIPTEN__)
GL_SHADER_STORAGE_BUFFER,
#endif
GL_ARRAY_BUFFER,
GL_COPY_READ_BUFFER,
GL_COPY_WRITE_BUFFER,
GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
};
size_t bufferTargetCount = sizeof(bufferTargets) / sizeof(bufferTargets[0]);
for (size_t targetArrayIndex = 0; targetArrayIndex < bufferTargetCount; ++targetArrayIndex) {
GLenum target = bufferTargets[targetArrayIndex];
glBindBuffer(target, 0);
}
for (size_t bufferIndex = 0; bufferIndex < MAX_BUFFER_BINDINGS; ++bufferIndex) {
if (bufferIndex < (size_t)gets.max_uniform_buffer_bindings) {
glBindBufferBase(GL_UNIFORM_BUFFER, bufferIndex, 0);
}
if (bufferIndex < (size_t)gets.max_transform_feedback_separate_attribs) {
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, bufferIndex, 0);
}
}
// state.textures
// Reset state.textures to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.textures = {};
const GLuint textureTargets[] = {
GL_TEXTURE_2D,
GL_TEXTURE_2D_ARRAY,
GL_TEXTURE_CUBE_MAP,
GL_TEXTURE_3D,
#if !defined(__EMSCRIPTEN__)
GL_TEXTURE_2D_MULTISAMPLE,
GL_TEXTURE_EXTERNAL_OES,
GL_TEXTURE_CUBE_MAP_ARRAY,
#endif
};
const size_t textureTargetCount = sizeof(textureTargets) / sizeof(textureTargets[0]);
for (GLint unit = 0; unit < gets.max_combined_texture_image_units; ++unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindSampler(unit, 0);
for (size_t textureTargetArrayIndex = 0; textureTargetArrayIndex < textureTargetCount; ++textureTargetArrayIndex) {
GLuint target = textureTargets[textureTargetArrayIndex];
glBindTexture(target, 0);
}
}
glActiveTexture(GL_TEXTURE0 + state.textures.active);
// state.unpack
glPixelStorei(GL_UNPACK_ALIGNMENT, state.unpack.alignment);
glPixelStorei(GL_UNPACK_ROW_LENGTH, state.unpack.row_length);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, state.unpack.skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_ROWS, state.unpack.skip_row);
// state.pack
glPixelStorei(GL_PACK_ALIGNMENT, state.pack.alignment);
glPixelStorei(GL_PACK_ROW_LENGTH, state.pack.row_length);
glPixelStorei(GL_PACK_SKIP_PIXELS, state.pack.skip_pixels);
glPixelStorei(GL_PACK_SKIP_ROWS, state.pack.skip_row);
// state.window
glScissor(
state.window.scissor.x,
state.window.scissor.y,
state.window.scissor.z,
state.window.scissor.w
);
glViewport(
state.window.viewport.x,
state.window.viewport.y,
state.window.viewport.z,
state.window.viewport.w
);
glDepthRangef(state.window.depthRange.x, state.window.depthRange.y);
}
} // namesapce filament

View File

@@ -73,6 +73,7 @@ public:
ShaderModel getShaderModel() const noexcept { return mShaderModel; }
void resetState() noexcept;
inline void useProgram(GLuint program) noexcept;
@@ -129,6 +130,8 @@ public:
GLint max_uniform_block_size;
GLint max_texture_image_units;
GLint max_combined_texture_image_units;
GLint max_transform_feedback_separate_attribs;
GLint max_uniform_buffer_bindings;
GLint uniform_buffer_offset_alignment;
} gets = {};
@@ -409,6 +412,8 @@ private:
}
}
void setDefaultState() noexcept;
static constexpr const size_t TEXTURE_TARGET_COUNT =
sizeof(state.textures.units[0].targets) / sizeof(state.textures.units[0].targets[0]);

View File

@@ -235,6 +235,9 @@ ShaderModel OpenGLDriver::getShaderModel() const noexcept {
// ------------------------------------------------------------------------------------------------
// Change and track GL state
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::resetState(int) {
mContext.resetState();
}
void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
mContext.bindSampler(unit, sampler);
@@ -3185,7 +3188,8 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
useProgram(p);
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
// FIXME: this line is commented-out for now due to issues switching G3 clients to API 21.
// glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
#endif
#ifdef FILAMENT_ENABLE_MATDBG

View File

@@ -35,9 +35,11 @@
#include <jni.h>
// We require filament to be built with a API 21 toolchain, before that, OpenGLES 3.1 didn't exist
#if __ANDROID_API__ < 21
# error "__ANDROID_API__ must be at least 21"
// We require filament to be built with a API 19 toolchain, before that, OpenGLES 3.0 didn't exist
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
// the minimum for Jetpack at the time of this comment.
#if __ANDROID_API__ < 19
# error "__ANDROID_API__ must be at least 19"
#endif
using namespace utils;

View File

@@ -2012,6 +2012,9 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
#endif
}
void VulkanDriver::resetState(int) {
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;

View File

@@ -22,25 +22,29 @@ using namespace filament::backend;
TEST_F(ComputeTest, basic) {
auto& driver = getDriverApi();
std::string_view shader_gles310 = {R"(
std::string shader_gles310 = {R"(
#version 310 es
layout(local_size_x = 16) in;
void main() {
}
)"};
std::string_view shader_gl450 = {R"(
std::string shader_gl450 = {R"(
#version 450 core
layout(local_size_x = 16) in;
void main() {
}
)"};
std::string_view shader_msl = {R"(
// TODO: msl test
std::string shader_msl = {R"(
#include <simd/simd.h>
#include <metal_stdlib>
using namespace metal;
constant uint3 WorkGroupSize [[maybe_unused]] = uint3(16u, 1u, 1u);
kernel void main0() {}
)"};
std::string_view shader_spirv = {R"(
std::string shader_spirv = {R"(
// TODO: spirv test
)"};
@@ -54,7 +58,7 @@ void main() {
}
Program program;
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size());
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);
Handle<HwProgram> ph = driver.createProgram(std::move(program));
driver.dispatchCompute(ph, { 1, 1, 1 });
@@ -68,7 +72,7 @@ void main() {
TEST_F(ComputeTest, copy) {
auto& driver = getDriverApi();
std::string_view shader_gles310 = {R"(
std::string shader_gles310 = {R"(
#version 310 es
layout(local_size_x = 16) in;
layout(std430) buffer;
@@ -80,7 +84,7 @@ void main() {
}
)"};
std::string_view shader_gl450 = {R"(
std::string shader_gl450 = {R"(
#version 450 core
layout(local_size_x = 16) in;
layout(std430) buffer;
@@ -92,11 +96,25 @@ void main() {
}
)"};
std::string_view shader_msl = {R"(
// TODO: msl test
std::string shader_msl = {R"(
#include <simd/simd.h>
#include <metal_stdlib>
using namespace metal;
struct Output_data {
float elements[1];
};
struct Input_data {
float elements[1];
};
constant uint3 WorkGroupSize [[maybe_unused]] = uint3(16u, 1u, 1u);
kernel void main0(device Output_data& output_data [[buffer(0)]],
device Input_data& input_data [[buffer(1)]],
uint3 GlobalInvocationID [[thread_position_in_grid]]) {
output_data.elements[GlobalInvocationID.x] = input_data.elements[GlobalInvocationID.x];
}
)"};
std::string_view shader_spirv = {R"(
std::string shader_spirv = {R"(
// TODO: spirv test
)"};
@@ -119,12 +137,14 @@ void main() {
return v;
});
driver.startCapture(0);
auto output_data = driver.createBufferObject(size, BufferObjectBinding::SHADER_STORAGE, BufferUsage::STATIC);
auto input_data = driver.createBufferObject(size, BufferObjectBinding::SHADER_STORAGE, BufferUsage::STATIC);
driver.updateBufferObject(input_data, { data.data(), size }, 0);
Program program;
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size());
program.shader(ShaderStage::COMPUTE, shader.data(), shader.size() + 1);
Handle<HwProgram> ph = driver.createProgram(std::move(program));
@@ -144,6 +164,8 @@ void main() {
driver.destroyBufferObject(output_data);
driver.finish();
driver.stopCapture(0);
executeCommands();
getDriver().purge();

View File

@@ -725,6 +725,22 @@ public:
*/
utils::JobSystem& getJobSystem() noexcept;
#if defined(__EMSCRIPTEN__)
/**
* WebGL only: Tells the driver to reset any internal state tracking if necessary.
*
* This is only useful when integrating an external renderer into Filament on platforms
* like WebGL, where share contexts do not exist. Filament keeps track of the GL
* state it has set (like which texture is bound), and does not re-set that state if
* it does not think it needs to. However, if an external renderer has set different
* state in the mean time, Filament will use that new state unknowingly.
*
* If you are in this situation, call this function - ideally only once per frame,
* immediately after calling Engine::execute().
*/
void resetBackendState() noexcept;
#endif
DebugRegistry& getDebugRegistry() noexcept;
protected:

View File

@@ -273,4 +273,10 @@ FeatureLevel Engine::getActiveFeatureLevel() const noexcept {
return downcast(this)->getActiveFeatureLevel();
}
#if defined(__EMSCRIPTEN__)
void Engine::resetBackendState() noexcept {
downcast(this)->resetBackendState();
}
#endif
} // namespace filament

View File

@@ -399,9 +399,9 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
auto out = resources.getRenderPassInfo();
renderPass.setRenderFlags(structureRenderFlags);
renderPass.setVariant(structureVariant);
renderPass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
renderPass.sortCommands();
renderPass.execute(resources.getPassName(), out.target, out.params);
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SSAO);
renderPass.sortCommands(mEngine);
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
});
auto depth = structurePass->depth;
@@ -544,9 +544,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
// the SCREEN_SPACE ReflectionMode.
renderPass.setVariant(Variant{Variant::SPECIAL_SSR});
// generate all our drawing commands, except blended objects.
renderPass.appendCommands(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
renderPass.sortCommands();
renderPass.execute(resources.getPassName(), out.target, out.params);
renderPass.appendCommands(mEngine, RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
renderPass.sortCommands(mEngine);
renderPass.execute(mEngine, resources.getPassName(), out.target, out.params);
});
return ssrPass->reflections;

View File

@@ -39,7 +39,7 @@ using namespace backend;
RenderPass::RenderPass(FEngine& engine,
RenderPass::Arena& arena) noexcept
: mEngine(engine), mCommandArena(arena),
: mCommandArena(arena),
mCustomCommands(engine.getPerRenderPassAllocator()) {
}
@@ -98,7 +98,7 @@ void RenderPass::overrideScissor(backend::Viewport const* scissor) noexcept {
}
}
void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandTypeFlags) noexcept {
SYSTRACE_CONTEXT();
assert_invariant(mRenderableSoa);
@@ -110,7 +110,6 @@ void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcep
return;
}
FEngine& engine = mEngine;
JobSystem& js = engine.getJobSystem();
const RenderFlags renderFlags = mFlags;
const Variant variant = mVariant;
@@ -179,7 +178,7 @@ void RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t o
curr->key = cmd;
}
void RenderPass::sortCommands() noexcept {
void RenderPass::sortCommands(FEngine& engine) noexcept {
SYSTRACE_NAME("sort and trim commands");
std::sort(mCommandBegin, mCommandEnd);
@@ -192,12 +191,12 @@ void RenderPass::sortCommands() noexcept {
resize(uint32_t(last - mCommandBegin));
if (mEngine.isAutomaticInstancingEnabled()) {
instanceify();
if (engine.isAutomaticInstancingEnabled()) {
instanceify(engine);
}
}
void RenderPass::instanceify() noexcept {
void RenderPass::instanceify(FEngine& engine) noexcept {
SYSTRACE_NAME("instanceify");
// instanceify works by scanning the **sorted** command stream, looking for repeat draw
@@ -284,7 +283,7 @@ void RenderPass::instanceify() noexcept {
#endif
// we have instanced primitives
DriverApi& driver = mEngine.getDriverApi();
DriverApi& driver = engine.getDriverApi();
// TODO: maybe use a pool? so we can reuse the buffer.
// create a ubo to hold the instanced primitive data
@@ -686,10 +685,9 @@ void RenderPass::updateSummedPrimitiveCounts(
// ------------------------------------------------------------------------------------------------
void RenderPass::Executor::execute(const char* name,
void RenderPass::Executor::execute(FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams const& params) const noexcept {
FEngine& engine = mEngine;
DriverApi& driver = engine.getDriverApi();
// this is a good time to flush the CommandStream, because we're about to potentially
@@ -831,7 +829,7 @@ void RenderPass::Executor::recordDriverCommands(FEngine& engine, backend::Driver
// ------------------------------------------------------------------------------------------------
RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept
: mEngine(pass->mEngine), mBegin(b), mEnd(e),
: mBegin(b), mEnd(e),
mCustomCommands(pass->mCustomCommands),
mUboHandle(pass->mUboHandle),
mInstancedUboHandle(pass->mInstancedUboHandle),

View File

@@ -310,16 +310,16 @@ public:
// This is the main function of this class, this appends commands to the pass using
// the current camera, geometry and flags set. This can be called multiple times if needed.
void appendCommands(CommandTypeFlags commandTypeFlags) noexcept;
void appendCommands(FEngine& engine, CommandTypeFlags commandTypeFlags) noexcept;
// sorts and instancify commands then trims sentinels
void sortCommands() noexcept;
// sorts and instanceify commands then trims sentinels
void sortCommands(FEngine& engine) noexcept;
// Helper to execute all the commands generated by this RenderPass
void execute(const char* name,
void execute(FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) const noexcept {
getExecutor().execute(name, renderTarget, params);
getExecutor().execute(engine, name, renderTarget, params);
}
/*
@@ -331,7 +331,6 @@ public:
utils::STLAllocator<CustomCommandFn, LinearAllocatorArena>>;
friend class RenderPass;
FEngine& mEngine;
Command const* mBegin;
Command const* mEnd;
const CustomCommandVector mCustomCommands;
@@ -351,7 +350,7 @@ public:
public:
Executor(Executor const& rhs);
~Executor() noexcept;
void execute(const char* name,
void execute(FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams const& params) const noexcept;
};
@@ -376,7 +375,7 @@ private:
Command* append(size_t count) noexcept;
void resize(size_t count) noexcept;
void instanceify() noexcept;
void instanceify(FEngine& engine) noexcept;
// on 64-bits systems, we process batches of 256 (64 bytes) cache-lines, or 512 (32 bytes) commands
// on 32-bits systems, we process batches of 512 (32 bytes) cache-lines, or 512 (32 bytes) commands
@@ -406,7 +405,6 @@ private:
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
// a reference to the Engine, mostly to get to things like JobSystem
FEngine& mEngine;
// Arena where all Commands are allocated. The Arena owns the commands.
Arena& mCommandArena;

View File

@@ -217,7 +217,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
if (colorGradingConfig.asSubpass || colorGradingConfig.customResolve) {
out.params.subpassMask = 1;
}
passExecutor.execute(resources.getPassName(), out.target, out.params);
passExecutor.execute(engine, resources.getPassName(), out.target, out.params);
// color pass is typically heavy, and we don't have much CPU work left after
// this point, so flushing now allows us to start the GPU earlier and reduce

View File

@@ -984,9 +984,11 @@ size_t ShadowMap::intersectFrustumWithBox(
wsFrustumCorners, wsSceneReceiversCorners.vertices);
} else {
// by construction vertexCount should be 8 here, but it can be more because
// step (b) above can classify a point as inside the frustum that isn't quite.
// step (b) above can classify a point as inside the frustum that isn't quite inside.
// -> We can't blindly reset vertexCount to 8, we need to remove the "duplicate"
// vertices.
// It shouldn't cause any major issues later to have "nearly" duplicate vertices.
assert_invariant(vertexCount >= 8);
vertexCount = 8;
}
}

View File

@@ -267,8 +267,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
entry.pass.setGeometry(scene->getRenderableData(),
entry.range, scene->getRenderableUBO());
entry.pass.overrideScissor(&disabledScissor);
entry.pass.appendCommands(RenderPass::SHADOW);
entry.pass.sortCommands();
entry.pass.appendCommands(engine, RenderPass::SHADOW);
entry.pass.sortCommands(engine);
entry.cameraInfo = cameraInfo;
}
}
@@ -367,7 +367,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
// finally, create the shadowmap render target -- one per layer.
data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
},
[=, &entry, &view](FrameGraphResources const& resources,
[=, &engine, &entry, &view](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
// Note: we capture entry by reference here. That's actually okay because
@@ -411,7 +411,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FrameGraph& fg, FEngine
auto rt = resources.getRenderPassInfo(blur ? data.blurRt : data.shadowRt);
rt.params.viewport = viewport;
executor.execute("Shadow Pass", rt.target, rt.params);
executor.execute(engine, "Shadow Pass", rt.target, rt.params);
});

View File

@@ -258,6 +258,12 @@ uint32_t FEngine::getJobSystemThreadPoolSize() noexcept {
void FEngine::init() {
SYSTRACE_CALL();
#if FILAMENT_ENABLE_FGDBG
if (!debug.fgdbg) {
debug.fgdbg = std::make_unique<fgdbg::DebugServer>();
}
#endif
// this must be first.
assert_invariant( intptr_t(&mDriverApiStorage) % alignof(DriverApi) == 0 );
::new(&mDriverApiStorage) DriverApi(*mDriver, mCommandBufferQueue.getCircularBuffer());
@@ -606,16 +612,16 @@ int FEngine::loop() {
#endif
if (portString != nullptr) {
const int port = atoi(portString);
debug.server = new matdbg::DebugServer(mBackend, port);
debug.matdbg = std::make_unique<matdbg::DebugServer>(mBackend, port);
// Sometimes the server can fail to spin up (e.g. if the above port is already in use).
// When this occurs, carry onward, developers can look at civetweb.txt for details.
if (!debug.server->isReady()) {
delete debug.server;
debug.server = nullptr;
if (!debug.matdbg->isReady()) {
delete debug.matdbg.get();
debug.matdbg = nullptr;
} else {
debug.server->setEditCallback(FMaterial::onEditCallback);
debug.server->setQueryCallback(FMaterial::onQueryCallback);
debug.matdbg->setEditCallback(FMaterial::onEditCallback);
debug.matdbg->setQueryCallback(FMaterial::onQueryCallback);
}
}
#endif
@@ -640,6 +646,16 @@ int FEngine::loop() {
uint32_t id = std::thread::hardware_concurrency() - 1;
while (true) {
#if FILAMENT_ENABLE_FGDBG
// TODO (@feresr): Only for testing purposes, remove
if (debug.fgdbg) {
static int fgdbgMessage = 0;
fgdbgMessage++;
if (fgdbgMessage % 100 == 0) {
debug.fgdbg->sendMessage(fgdbgMessage/100);
}
}
#endif
// looks like thread affinity needs to be reset regularly (on Android)
JobSystem::setThreadAffinityById(id);
if (!execute()) {
@@ -1072,4 +1088,10 @@ Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
}
#if defined(__EMSCRIPTEN__)
void FEngine::resetBackendState() noexcept {
getDriverApi().resetState();
}
#endif
} // namespace filament

View File

@@ -57,6 +57,14 @@
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#if FILAMENT_ENABLE_FGDBG
#include <fgdbg/DebugServer.h>
#else
namespace filament::fgdbg {
class DebugServer;
} // namespace filament::fgdbg
#endif
#if FILAMENT_ENABLE_MATDBG
#include <matdbg/DebugServer.h>
#else
@@ -374,6 +382,10 @@ public:
return FEngine::getActiveFeatureLevel() >= neededFeatureLevel;
}
#if defined(__EMSCRIPTEN__)
void resetBackendState() noexcept;
#endif
private:
static Config validateConfig(const Config* pConfig) noexcept;
@@ -515,7 +527,8 @@ public:
// capture to file. At the moment, only supported by the Metal backend.
bool doFrameCapture = false;
} renderer;
matdbg::DebugServer* server = nullptr;
std::unique_ptr<matdbg::DebugServer> matdbg = nullptr;
std::unique_ptr<fgdbg::DebugServer> fgdbg = nullptr;
} debug;
};

View File

@@ -168,7 +168,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
#if FILAMENT_ENABLE_MATDBG
// Register the material with matdbg.
matdbg::DebugServer* server = downcast(engine).debug.server;
matdbg::DebugServer* server = downcast(engine).debug.matdbg.get();
if (UTILS_UNLIKELY(server)) {
auto details = builder.mImpl;
mDebuggerId = server->addMaterial(mName, details->mPayload, details->mSize, this);
@@ -319,7 +319,7 @@ void FMaterial::terminate(FEngine& engine) {
#if FILAMENT_ENABLE_MATDBG
// Unregister the material with matdbg.
matdbg::DebugServer* server = downcast(mEngine).debug.server;
matdbg::DebugServer* server = downcast(mEngine).debug.matdbg.get();
if (UTILS_UNLIKELY(server)) {
server->removeMaterial(mDebuggerId);
}

View File

@@ -850,8 +850,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// This one doesn't need to be a FrameGraph pass because it always happens by construction
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
pass.setVariant(variant);
pass.appendCommands(RenderPass::COLOR);
pass.sortCommands();
pass.appendCommands(engine, RenderPass::COLOR);
pass.sortCommands(engine);
FrameGraphTexture::Descriptor desc = {
.width = config.width,

View File

@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:backend-test.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:backend-test.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -123,7 +123,7 @@
};
};
buildConfigurationList = 90327DE42AE5DF12C4BD2113 /* Build configuration list for PBXProject "gltf-viewer" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -175,7 +175,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#/usr/bin/env/bash\n\nset -e\n\n# Compile resources.\n\n# The gltf-viewer app requires two resources:\n# 1. The IBL image\n# 2. The skybox image\n\nHOST_TOOLS_PATH=\"${HOST_TOOLS_PATH:-../../../out/release/filament/bin}\"\n\ncmgen_path=`find ${HOST_TOOLS_PATH} -name cmgen -type f | head -n 1`\n\n# Ensure that the required tools are present in the out/ directory.\n# These can be built by running ./build.sh -p desktop -i release at Filament's root directory.\n\nif [[ ! -e \"${cmgen_path}\" ]]; then\n echo \"No cmgen binary could be found in ${HOST_TOOLS_PATH}.\"\n echo \"Ensure Filament has been built/installed before building this app.\"\n exit 1\nfi\n\n# cmgen consumes an HDR environment map and generates two mipmapped KTX files (IBL and skybox)\n\"${cmgen_path}\" \\\n --deploy=\"${PROJECT_DIR}/generated/default_env\" \\\n --format=ktx --size=256 --extract-blur=0.1 \\\n \"${PROJECT_DIR}/../../../third_party/environments/lightroom_14b.hdr\"\n";
shellScript = "#/usr/bin/env/bash\n\nset -e\n\n# Compile resources.\n\n# The gltf-viewer app requires two resources:\n# 1. The IBL image\n# 2. The skybox image\n\nHOST_TOOLS_PATH=\"${HOST_TOOLS_PATH:-../../../out/release/filament/bin}\"\n\ncmgen_path=`find ${HOST_TOOLS_PATH} -name cmgen -type f | head -n 1`\n\n# Ensure that the required tools are present in the out/ directory.\n# These can be built by running ./build.sh -p desktop -i release at Filament's root directory.\n\nif [[ ! -e \"${cmgen_path}\" ]]; then\n echo \"No cmgen binary could be found in ${HOST_TOOLS_PATH}.\"\n echo \"Ensure Filament has been built/installed before building this app.\"\n exit 1\nfi\n\n# cmgen consumes an HDR environment map and generates two mipmapped KTX files (IBL and skybox)\n\"${cmgen_path}\" \\\n --quiet \\\n --deploy=\"${PROJECT_DIR}/generated/default_env\" \\\n --format=ktx --size=256 --extract-blur=0.1 \\\n \"${PROJECT_DIR}/../../../third_party/environments/lightroom_14b.hdr\"\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -429,6 +429,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -485,6 +486,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -603,6 +605,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",
@@ -659,6 +662,7 @@
"-lzstd",
"-lcamutils",
"-ldracodec",
"-lmeshoptimizer",
"-lviewer",
"-lcivetweb",
"-lktxreader",

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:gltf-viewer.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:gltf-viewer.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -20,7 +20,8 @@ targets:
settings:
base:
OTHER_LDFLAGS: ["-lgltfio_core", "-luberarchive", "-limage", "-lgeometry", "-lstb", "-lbasis_transcoder",
"-lzstd", "-lcamutils", "-ldracodec", "-lviewer", "-lcivetweb", "-lktxreader", "-luberzlib"]
"-lzstd", "-lcamutils", "-ldracodec", "-lmeshoptimizer", "-lviewer", "-lcivetweb", "-lktxreader",
"-luberzlib"]
preBuildScripts:
- path: build-resources.sh
name: Build Resources

View File

@@ -165,7 +165,7 @@
};
};
buildConfigurationList = 281E763643B2F1F3B903864B /* Build configuration list for PBXProject "hello-ar" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-ar.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-ar.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -138,7 +138,7 @@
};
};
buildConfigurationList = C7514DDCFDA3C5F803E239F9 /* Build configuration list for PBXProject "hello-gltf" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -328,6 +328,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -380,6 +382,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -604,6 +608,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",
@@ -656,6 +662,8 @@
"-limage",
"-lgeometry",
"-ldracodec",
"-lmeshoptimizer",
"-lbasis_transcoder",
"-lktxreader",
"-lstb",
"-lzstd",

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-gltf.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-gltf.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -23,6 +23,7 @@
#include <ktxreader/Ktx1Reader.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/TextureProvider.h>
#include <gltfio/materials/uberarchive.h>
#include <fstream>
@@ -139,10 +140,21 @@ void App::setupMesh() {
}
app.asset = app.assetLoader->createAsset(buffer.data(), static_cast<uint32_t>(size));
filament::gltfio::ResourceLoader({
auto resourceLoader = new filament::gltfio::ResourceLoader({
.engine = engine,
.normalizeSkinningWeights = true
}).loadResources(app.asset);
});
auto stbDecoder = filament::gltfio::createStbProvider(engine);
auto ktxDecoder = filament::gltfio::createKtx2Provider(engine);
resourceLoader->addTextureProvider("image/png", stbDecoder);
resourceLoader->addTextureProvider("image/jpeg", stbDecoder);
resourceLoader->addTextureProvider("image/ktx2", ktxDecoder);
resourceLoader->loadResources(app.asset);
delete resourceLoader;
delete stbDecoder;
delete ktxDecoder;
scene->addEntities(app.asset->getEntities(), app.asset->getEntityCount());
}

View File

@@ -12,7 +12,8 @@ targets:
- FilamentApp
settings:
base:
OTHER_LDFLAGS: ["-lgltfio_core", "-luberarchive", "-limage", "-lgeometry", "-ldracodec", "-lktxreader", "-lstb", "-lzstd", "-luberzlib"]
OTHER_LDFLAGS: ["-lgltfio_core", "-luberarchive", "-limage", "-lgeometry", "-ldracodec", "-lmeshoptimizer",
"-lbasis_transcoder", "-lktxreader", "-lstb", "-lzstd", "-luberzlib"]
preBuildScripts:
- path: build-resources.sh
name: Build Resources

View File

@@ -120,7 +120,7 @@
};
};
buildConfigurationList = 0479354FB8D9A411347D91A6 /* Build configuration list for PBXProject "hello-pbr" */;
compatibilityVersion = "Xcode 10.0";
compatibilityVersion = "Xcode 11.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-pbr.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-pbr.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-triangle.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:hello-triangle.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -27,10 +27,8 @@
buildConfiguration = "Metal Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:transparent-rendering.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

View File

@@ -27,10 +27,8 @@
buildConfiguration = "OpenGL Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -40,6 +38,8 @@
ReferencedContainer = "container:transparent-rendering.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>

22
libs/fgdbg/CMakeLists.txt Normal file
View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.19)
project(fgdbg)
set(TARGET fgdbg)
set(PUBLIC_HDR_DIR include)
# ==================================================================================================
# Sources and headers
# ==================================================================================================
set(PUBLIC_HDRS include/fgdbg/DebugServer.h include/fgdbg/Session.h)
set(SRCS src/DebugServer.cpp src/Session.cpp)
# ==================================================================================================
# Include and target definitions
# ==================================================================================================
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
target_link_libraries(${TARGET} PUBLIC civetweb utils)
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
include_directories(${PUBLIC_HDR_DIR})

1
libs/fgdbg/README.md Normal file
View File

@@ -0,0 +1 @@
# FrameGraph Debugger (WIP)

9
libs/fgdbg/client/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
/.idea/caches
/.idea/gradle.xml
.DS_Store
/build

View File

@@ -0,0 +1,46 @@
import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.6.10"
id("org.jetbrains.compose") version "1.1.1"
}
group = "com.google.filament"
version = "1.0"
val ktorVersion: String by project
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
maven {
url = uri("https://maven.pkg.jetbrains.space/public/p/ktor/eap")
name = "ktor-eap"
}
}
dependencies {
implementation(compose.desktop.currentOs)
dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.ktor:ktor-client-websockets:$ktorVersion")
}
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "fgdbg"
packageVersion = "1.0.0"
}
}
}

View File

@@ -0,0 +1,2 @@
kotlin.code.style=official
ktorVersion=2.1.2

Binary file not shown.

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
libs/fgdbg/client/gradlew vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# 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
#
# https://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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
libs/fgdbg/client/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -0,0 +1,9 @@
pluginManagement {
repositories {
google()
gradlePluginPortal()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
}
rootProject.name = "fgdbg"

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import androidx.compose.material.MaterialTheme
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.http.*
import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.coroutines.delay
private enum class ConnectionState { UNKNOWN, CONNECTED, CONNECTING, ERROR }
private data class Connection(
val host: String = "localhost",
val port: Int = 8082,
val path: String = "/",
val state: MutableState<ConnectionState> = mutableStateOf(ConnectionState.UNKNOWN),
val reconnectDelay: Int = 5,
)
@Composable
@Preview
fun App() {
val connection = remember { Connection() }
val client = remember { HttpClient { install(WebSockets) } }
var message by remember { mutableStateOf("") }
LaunchedEffect(Unit) {
while (true) {
connection.state.value = ConnectionState.CONNECTING
try {
client.webSocket(HttpMethod.Get,
connection.host,
connection.port,
connection.path) {
message = "Connected!"
connection.state.value = ConnectionState.CONNECTED
while (true) {
val frame = incoming.receive() as? Frame.Text ?: continue
message = frame.readText()
}
}
} catch (e: Exception) {
connection.state.value = ConnectionState.ERROR
repeat(connection.reconnectDelay) {
val remaining = connection.reconnectDelay - it
message = "Connection interrupted, will re-connect in $remaining seconds..."
delay(1000)
}
}
}
}
DisposableEffect(Unit) { onDispose { client.close() } }
MaterialTheme {
when (connection.state.value) {
ConnectionState.CONNECTED -> {
Row(Modifier.height(40.dp)) {
Text(modifier = Modifier.align(Alignment.CenterVertically),
text = "Listening on ${connection.host}:${connection.port} ")
Button(onClick = {}) { Text(message) }
}
}
ConnectionState.ERROR -> Text(text = message, color = Color.Red)
else -> Text(connection.state.value.name)
}
}
}
fun main() = application { Window(onCloseRequest = ::exitApplication) { App() } }

View File

@@ -0,0 +1,2 @@
class NavRestore {
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FGDBG_DEBUGSERVER_H
#define FGDBG_DEBUGSERVER_H
class CivetServer;
namespace filament::fgdbg {
class DebugServer {
public:
DebugServer();
~DebugServer();
void sendMessage(int number);
private:
CivetServer* mServer;
class RestRequestHandler *mRestRequestHandler = nullptr;
class WebSocketHandler* mWebSocketHandler = nullptr;
friend class RestRequestHandler;
friend class WebSocketHandler;
};
}
#endif // FGDBG_DEBUGSERVER_H

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FGDBG_PASS_H
#define FGDBG_PASS_H
#include <string>
#include <vector>
namespace filament::fgdbg {
using ResourceId = int;
struct Pass {
std::string name;
int id;
std::vector<ResourceId> reads;
std::vector<ResourceId> writes;
};
}
#endif //FGDBG_PASS_H

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FGDBG_RESOURCE_H
#define FGDBG_RESOURCE_H
#include <string>
namespace filament::fgdbg {
struct Resource {
std::string name;
int id;
int size;
};
}
#endif //FGDBG_RESOURCE_H

View File

@@ -0,0 +1,28 @@
//
// Created by Fernando Raviola on 4/11/2022.
//
#ifndef FGDBG_SESSION_H
#define FGDBG_SESSION_H
#include "DebugServer.h"
#include "Resource.h"
#include "Pass.h"
#include <memory>
namespace filament::fgdbg {
class Session {
public:
Session(std::shared_ptr<DebugServer> server) : server{ server } {}
void addPasses(std::vector<Pass> passes);
void addResources(std::vector<Resource> resources);
void update();
private:
std::shared_ptr<DebugServer> server;
};
}
#endif //FGDBG_SESSION_H

View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fgdbg/DebugServer.h"
#include <CivetServer.h>
#include <utils/Log.h>
#include <tsl/robin_set.h>
namespace filament::fgdbg {
using namespace utils;
static const std::string_view kSuccessHeader =
"HTTP/1.1 200 OK\r\nContent-Type: %s\r\n"
"Connection: close\r\n\r\n";
class RestRequestHandler : public CivetHandler {
public:
RestRequestHandler() = default;
bool handleGet(CivetServer *server, struct mg_connection *conn) override {
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "{ RestRequestHandler is working! }");
return true;
}
};
class WebSocketHandler : public CivetWebSocketHandler {
public:
WebSocketHandler() = default;
bool handleConnection(CivetServer *server, const struct mg_connection *conn) override {
return true;
}
void handleReadyState(CivetServer *server, struct mg_connection *conn) override {
mConnections.insert(conn);
}
void handleClose(CivetServer *server, const struct mg_connection *conn) override {
auto *key = const_cast<struct mg_connection *>(conn);
mConnections.erase(key);
}
void send(int number) {
auto string = std::to_string(number);
for (auto connection : mConnections) {
mg_websocket_write(connection,
MG_WEBSOCKET_OPCODE_TEXT,
string.c_str(),
string.length());
}
}
private:
tsl::robin_set<struct mg_connection*> mConnections;
};
DebugServer::DebugServer() {
slog.d << "FrameGraph debug server created" << io::endl;
//TODO (@feresr): Make port param customizable
const char* kServerOptions[] = {
"listening_ports", "8082",
"num_threads", "10",
"error_log_file", "civetweb.txt",
nullptr
};
mServer = new CivetServer(kServerOptions);
if (!mServer->getContext()) {
delete mServer;
mServer = nullptr;
slog.e << "Unable to start fgdbg DebugServer, see civetweb.txt for details." << io::endl;
return;
}
slog.i << "Server started successfully" << io::endl;
mRestRequestHandler = new RestRequestHandler();
mWebSocketHandler = new WebSocketHandler();
mServer->addHandler("", mRestRequestHandler);
mServer->addWebSocketHandler("", mWebSocketHandler);
}
void DebugServer::sendMessage(int number) {
mWebSocketHandler->send(number);
}
DebugServer::~DebugServer() {
slog.d << "FrameGraph debug server destroyed" << io::endl;
delete mRestRequestHandler;
delete mWebSocketHandler;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Session.h"
namespace filament::fgdbg {
void Session::addPasses(std::vector <Pass> passes);
void Session::addResources(std::vector <Resource> resources);
void Session::update();
}

View File

@@ -247,8 +247,12 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
mslCompiler.add_msl_resource_binding(newBinding);
};
auto resources = mslCompiler.get_shader_resources();
for (const auto& resource : resources.uniform_buffers) {
auto uniformResources = mslCompiler.get_shader_resources();
for (const auto& resource : uniformResources.uniform_buffers) {
updateResourceBindingDefault(resource);
}
auto ssboResources = mslCompiler.get_shader_resources();
for (const auto& resource : ssboResources.storage_buffers) {
updateResourceBindingDefault(resource);
}

View File

@@ -368,12 +368,22 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade
Precision uniformPrecision = getDefaultUniformPrecision();
Precision defaultPrecision = getDefaultPrecision(stage);
auto metalBufferBindingOffset = 0;
switch (uib.getTarget()) {
case BufferInterfaceBlock::Target::UNIFORM:
metalBufferBindingOffset = METAL_UNIFORM_BUFFER_BINDING_START;
break;
case BufferInterfaceBlock::Target::SSBO:
metalBufferBindingOffset = METAL_SSBO_BINDING_START;
break;
}
out << "\nlayout(";
if (mTargetLanguage == TargetLanguage::SPIRV ||
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
switch (mTargetApi) {
case TargetApi::METAL:
out << "binding = " << METAL_UNIFORM_BUFFER_BINDING_START + binding << ", ";
out << "binding = " << metalBufferBindingOffset + binding << ", ";
break;
case TargetApi::OPENGL:

View File

@@ -161,10 +161,11 @@ public:
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;
// These constants must match the equivalent in MetalState.h.
// These values represent the starting index for uniform and sampler group [[buffer(n)]]
// These values represent the starting index for uniform, ssbo, and sampler group [[buffer(n)]]
// bindings. See the chart at the top of MetalState.h.
static constexpr uint32_t METAL_UNIFORM_BUFFER_BINDING_START = 17u;
static constexpr uint32_t METAL_SAMPLER_GROUP_BINDING_START = 27u;
static constexpr uint32_t METAL_SSBO_BINDING_START = 0;
private:
filament::backend::Precision getDefaultPrecision(ShaderStage stage) const;

View File

@@ -161,7 +161,7 @@ target_include_directories(gltfio_core PUBLIC ${PUBLIC_HDR_DIR})
set_target_properties(gltfio_core PROPERTIES FOLDER Libs)
target_compile_definitions(gltfio_core PUBLIC -DGLTFIO_DRACO_SUPPORTED=1)
target_link_libraries(gltfio_core PUBLIC dracodec)
target_link_libraries(gltfio_core PUBLIC dracodec meshoptimizer)
if (WEBGL_PTHREADS)
target_compile_definitions(gltfio_core PUBLIC -DFILAMENT_WASM_THREADS)

View File

@@ -88,9 +88,16 @@ struct AnimatorImpl {
static void createSampler(const cgltf_animation_sampler& src, Sampler& dst) {
// Copy the time values into a red-black tree.
const cgltf_accessor* timelineAccessor = src.input;
const uint8_t* timelineBlob = (const uint8_t*) timelineAccessor->buffer_view->buffer->data;
const float* timelineFloats = (const float*) (timelineBlob + timelineAccessor->offset +
timelineAccessor->buffer_view->offset);
const uint8_t* timelineBlob = nullptr;
const float* timelineFloats = nullptr;
if (timelineAccessor->buffer_view->has_meshopt_compression) {
timelineBlob = (const uint8_t*) timelineAccessor->buffer_view->data;
timelineFloats = (const float*) (timelineBlob + timelineAccessor->offset);
} else {
timelineBlob = (const uint8_t*) timelineAccessor->buffer_view->buffer->data;
timelineFloats = (const float*) (timelineBlob + timelineAccessor->offset +
timelineAccessor->buffer_view->offset);
}
for (size_t i = 0, len = timelineAccessor->count; i < len; ++i) {
dst.times[timelineFloats[i]] = i;
}
@@ -219,10 +226,10 @@ Animator::Animator(FFilamentAsset const* asset, FFilamentInstance* instance) {
// Import each glTF channel into a custom data structure.
if (instance) {
mImpl->addChannels(instance->nodeMap, srcAnim, dstAnim);
mImpl->addChannels(instance->mNodeMap, srcAnim, dstAnim);
} else {
for (FFilamentInstance* instance : asset->mInstances) {
mImpl->addChannels(instance->nodeMap, srcAnim, dstAnim);
mImpl->addChannels(instance->mNodeMap, srcAnim, dstAnim);
}
}
}
@@ -240,7 +247,7 @@ void Animator::addInstance(FFilamentInstance* instance) {
for (cgltf_size i = 0, len = srcAsset->animations_count; i < len; ++i) {
const cgltf_animation& srcAnim = srcAnims[i];
Animation& dstAnim = mImpl->animations[i];
mImpl->addChannels(instance->nodeMap, srcAnim, dstAnim);
mImpl->addChannels(instance->mNodeMap, srcAnim, dstAnim);
}
}
@@ -517,7 +524,7 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn
}
void AnimatorImpl::resetBoneMatrices(FFilamentInstance* instance) {
for (const auto& skin : instance->skins) {
for (const auto& skin : instance->mSkins) {
size_t njoints = skin.joints.size();
boneMatrices.resize(njoints);
for (const auto& entity : skin.targets) {
@@ -533,9 +540,9 @@ void AnimatorImpl::resetBoneMatrices(FFilamentInstance* instance) {
}
void AnimatorImpl::updateBoneMatrices(FFilamentInstance* instance) {
assert_invariant(instance->skins.size() == asset->mSkins.size());
assert_invariant(instance->mSkins.size() == asset->mSkins.size());
size_t skinIndex = 0;
for (const auto& skin : instance->skins) {
for (const auto& skin : instance->mSkins) {
const auto& assetSkin = asset->mSkins[skinIndex++];
size_t njoints = skin.joints.size();
boneMatrices.resize(njoints);

View File

@@ -519,9 +519,9 @@ FFilamentInstance* FAssetLoader::createInstance(const cgltf_data* srcAsset) {
FFilamentInstance* instance = new FFilamentInstance(instanceRoot, mAsset);
// Check if the asset has variants.
instance->variants.reserve(srcAsset->variants_count);
instance->mVariants.reserve(srcAsset->variants_count);
for (cgltf_size i = 0, len = srcAsset->variants_count; i < len; ++i) {
instance->variants.push_back({CString(srcAsset->variants[i].name)});
instance->mVariants.push_back({CString(srcAsset->variants[i].name)});
}
// For each scene root, recursively create all entities.
@@ -540,7 +540,7 @@ FFilamentInstance* FAssetLoader::createInstance(const cgltf_data* srcAsset) {
// Bounding boxes are not shared because users might call recomputeBoundingBoxes() which can
// be affected by entity transforms. However, upon instance creation we can safely copy over
// the asset's bounding box.
instance->boundingBox = mAsset->mBoundingBox;
instance->mBoundingBox = mAsset->mBoundingBox;
mMaterialInstanceCache.flush(&instance->mMaterialInstances);
@@ -579,8 +579,8 @@ void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node*
// Update the asset's entity list and private node mapping.
mAsset->mEntities.push_back(entity);
instance->entities.push_back(entity);
instance->nodeMap[node - srcAsset->nodes] = entity;
instance->mEntities.push_back(entity);
instance->mNodeMap[node - srcAsset->nodes] = entity;
const char* name = getNodeName(node, mDefaultNodeName);
@@ -782,7 +782,7 @@ void FAssetLoader::createMaterialVariants(const cgltf_data* srcAsset, const cglt
break;
}
mAsset->mDependencyGraph.addEdge(entity, mi);
instance->variants[variantIndex].mappings.push_back({entity, prim, mi});
instance->mVariants[variantIndex].mappings.push_back({entity, prim, mi});
}
}
}
@@ -1524,19 +1524,19 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_data* srcAsse
void FAssetLoader::importSkins(FFilamentAsset* primary,
FFilamentInstance* instance, const cgltf_data* gltf) {
instance->skins.reserve(gltf->skins_count);
instance->skins.resize(gltf->skins_count);
const auto& nodeMap = instance->nodeMap;
instance->mSkins.reserve(gltf->skins_count);
instance->mSkins.resize(gltf->skins_count);
const auto& nodeMap = instance->mNodeMap;
for (cgltf_size i = 0, len = gltf->nodes_count; i < len; ++i) {
const cgltf_node& node = gltf->nodes[i];
Entity entity = nodeMap[i];
if (node.skin && entity) {
int skinIndex = node.skin - &gltf->skins[0];
instance->skins[skinIndex].targets.insert(entity);
instance->mSkins[skinIndex].targets.insert(entity);
}
}
for (cgltf_size i = 0, len = gltf->skins_count; i < len; ++i) {
FFilamentInstance::Skin& dstSkin = instance->skins[i];
FFilamentInstance::Skin& dstSkin = instance->mSkins[i];
const cgltf_skin& srcSkin = gltf->skins[i];
// Build a list of transformables for this skin, one for each joint.

View File

@@ -91,21 +91,21 @@ struct FFilamentInstance : public FilamentInstance {
tsl::robin_set<utils::Entity, utils::Entity::Hasher> targets;
};
const utils::Entity root;
FFilamentAsset const* owner;
const utils::Entity mRoot;
FFilamentAsset const* mOwner;
std::vector<utils::Entity> entities;
utils::FixedCapacityVector<Variant> variants;
Animator* animator = nullptr;
utils::FixedCapacityVector<Skin> skins;
std::vector<utils::Entity> mEntities;
utils::FixedCapacityVector<Variant> mVariants;
Animator* mAnimator = nullptr;
utils::FixedCapacityVector<Skin> mSkins;
// Note that nodeMap is yet another a vector of entities, but unlike the "entities" field, it
// may be sparsely populated. This is used as a simple mapping between cgltf_node and Entity,
// and therefore has the same size as the number of cgltf_node in the original asset. We
// considered using the ECS for this, but we need Node => Entity, not the other way around.
utils::FixedCapacityVector<utils::Entity> nodeMap;
utils::FixedCapacityVector<utils::Entity> mNodeMap;
Aabb boundingBox;
Aabb mBoundingBox;
utils::FixedCapacityVector<MaterialInstance*> mMaterialInstances;

View File

@@ -29,81 +29,81 @@ using namespace utils;
namespace filament::gltfio {
FFilamentInstance::FFilamentInstance(Entity root, FFilamentAsset const* owner) :
root(root),
owner(owner),
nodeMap(owner->mSourceAsset->hierarchy->nodes_count, Entity()) {}
mRoot(root),
mOwner(owner),
mNodeMap(owner->mSourceAsset->hierarchy->nodes_count, Entity()) {}
FFilamentInstance::~FFilamentInstance() {
delete animator;
delete mAnimator;
for (auto mi : mMaterialInstances) {
owner->mEngine->destroy(mi);
mOwner->mEngine->destroy(mi);
}
}
Animator* FFilamentInstance::getAnimator() const noexcept {
assert_invariant(animator);
return animator;
assert_invariant(mAnimator);
return mAnimator;
}
void FFilamentInstance::createAnimator() {
if (animator == nullptr && owner->mResourcesLoaded) {
animator = new Animator(owner, this);
if (mAnimator == nullptr && mOwner->mResourcesLoaded) {
mAnimator = new Animator(mOwner, this);
}
}
size_t FFilamentInstance::getSkinCount() const noexcept {
return skins.size();
return mSkins.size();
}
const char* FFilamentInstance::getSkinNameAt(size_t skinIndex) const noexcept {
if (skins.size() <= skinIndex) {
if (mSkins.size() <= skinIndex) {
return nullptr;
}
return owner->mSkins[skinIndex].name.c_str();
return mOwner->mSkins[skinIndex].name.c_str();
}
size_t FFilamentInstance::getJointCountAt(size_t skinIndex) const noexcept {
if (skins.size() <= skinIndex) {
if (mSkins.size() <= skinIndex) {
return 0;
}
return skins[skinIndex].joints.size();
return mSkins[skinIndex].joints.size();
}
const utils::Entity* FFilamentInstance::getJointsAt(size_t skinIndex) const noexcept {
if (skins.size() <= skinIndex) {
if (mSkins.size() <= skinIndex) {
return nullptr;
}
return skins[skinIndex].joints.data();
return mSkins[skinIndex].joints.data();
}
void FFilamentInstance::attachSkin(size_t skinIndex, Entity target) noexcept {
if (UTILS_UNLIKELY(skins.size() <= skinIndex || target.isNull())) {
if (UTILS_UNLIKELY(mSkins.size() <= skinIndex || target.isNull())) {
return;
}
skins[skinIndex].targets.insert(target);
mSkins[skinIndex].targets.insert(target);
}
void FFilamentInstance::detachSkin(size_t skinIndex, Entity target) noexcept {
if (UTILS_UNLIKELY(skins.size() <= skinIndex || target.isNull())) {
if (UTILS_UNLIKELY(mSkins.size() <= skinIndex || target.isNull())) {
return;
}
skins[skinIndex].targets.erase(target);
mSkins[skinIndex].targets.erase(target);
}
void FFilamentInstance::recomputeBoundingBoxes() {
ASSERT_PRECONDITION(owner->mSourceAsset,
ASSERT_PRECONDITION(mOwner->mSourceAsset,
"Do not call releaseSourceData before recomputeBoundingBoxes");
ASSERT_PRECONDITION(owner->mResourcesLoaded,
ASSERT_PRECONDITION(mOwner->mResourcesLoaded,
"Do not call recomputeBoundingBoxes before loadResources or asyncBeginLoad");
auto& rm = owner->mEngine->getRenderableManager();
auto& tm = owner->mEngine->getTransformManager();
auto& rm = mOwner->mEngine->getRenderableManager();
auto& tm = mOwner->mEngine->getTransformManager();
// The purpose of the root node is to give the client a place for custom transforms.
// Since it is not part of the source model, it should be ignored when computing the
// bounding box.
TransformManager::Instance root = tm.getInstance(owner->getRoot());
TransformManager::Instance root = tm.getInstance(mOwner->getRoot());
utils::FixedCapacityVector<Entity> modelRoots(tm.getChildCount(root));
tm.getChildren(root, modelRoots.data(), modelRoots.size());
for (auto e : modelRoots) {
@@ -169,8 +169,8 @@ void FFilamentInstance::recomputeBoundingBoxes() {
Aabb aabb;
TransformManager::Instance transformable = tm.getInstance(prim.node);
const mat4f inverseGlobalTransform = inverse(tm.getWorldTransform(transformable));
const Skin& instanceSkin = skins[prim.skinIndex];
const FFilamentAsset::Skin& assetSkin = owner->mSkins[prim.skinIndex];
const Skin& instanceSkin = mSkins[prim.skinIndex];
const FFilamentAsset::Skin& assetSkin = mOwner->mSkins[prim.skinIndex];
for (size_t i = 0, n = verts.size(); i < n; i++) {
mat4f tmp = mat4f(0.0f);
for (size_t j = 0; j < 4; j++) {
@@ -202,7 +202,7 @@ void FFilamentInstance::recomputeBoundingBoxes() {
// Collect all mesh primitives that we wish to find bounds for. For each mesh primitive, we also
// collect the skin it is bound to (nullptr if not skinned) for bounds computation.
size_t primCount = 0;
const cgltf_data* hierarchy = owner->mSourceAsset->hierarchy;
const cgltf_data* hierarchy = mOwner->mSourceAsset->hierarchy;
const cgltf_node* nodes = hierarchy->nodes;
for (size_t i = 0, n = hierarchy->nodes_count; i < n; ++i) {
if (const cgltf_mesh* mesh = nodes[i].mesh; mesh) {
@@ -213,7 +213,7 @@ void FFilamentInstance::recomputeBoundingBoxes() {
const cgltf_skin* baseSkin = &hierarchy->skins[0];
for (size_t i = 0, n = hierarchy->nodes_count; i < n; ++i) {
const cgltf_node& node = nodes[i];
const Entity entity = nodeMap[i];
const Entity entity = mNodeMap[i];
if (entity.isNull()) {
continue;
}
@@ -230,7 +230,7 @@ void FFilamentInstance::recomputeBoundingBoxes() {
// Kick off a bounding box job for every primitive.
FixedCapacityVector<Aabb> bounds(primitives.size());
JobSystem& js = owner->mEngine->getJobSystem();
JobSystem& js = mOwner->mEngine->getJobSystem();
JobSystem::Job* parent = js.createJob();
for (size_t i = 0; i < primitives.size(); ++i) {
Aabb& result = bounds[i];
@@ -250,9 +250,9 @@ void FFilamentInstance::recomputeBoundingBoxes() {
// Compute the asset-level bounding box.
size_t primIndex = 0;
Aabb assetBounds;
for (size_t i = 0, n = owner->mSourceAsset->hierarchy->nodes_count; i < n; ++i) {
for (size_t i = 0, n = mOwner->mSourceAsset->hierarchy->nodes_count; i < n; ++i) {
const cgltf_node& node = nodes[i];
const Entity entity = nodeMap[i];
const Entity entity = mNodeMap[i];
if (const cgltf_mesh* mesh = node.mesh; mesh) {
// Find the object-space bounds for the renderable by unioning the bounds of each prim.
Aabb aabb;
@@ -278,26 +278,26 @@ void FFilamentInstance::recomputeBoundingBoxes() {
tm.setParent(tm.getInstance(e), root);
}
boundingBox = assetBounds;
mBoundingBox = assetBounds;
}
size_t FFilamentInstance::getMaterialVariantCount() const noexcept {
return variants.size();
return mVariants.size();
}
const char* FFilamentInstance::getMaterialVariantName(size_t variantIndex) const noexcept {
if (variantIndex >= variants.size()) {
if (variantIndex >= mVariants.size()) {
return nullptr;
}
return variants[variantIndex].name.c_str();
return mVariants[variantIndex].name.c_str();
}
void FFilamentInstance::applyMaterialVariant(size_t variantIndex) noexcept {
if (variantIndex >= variants.size()) {
if (variantIndex >= mVariants.size()) {
return;
}
const auto& mappings = variants[variantIndex].mappings;
RenderableManager& rm = owner->mEngine->getRenderableManager();
const auto& mappings = mVariants[variantIndex].mappings;
RenderableManager& rm = mOwner->mEngine->getRenderableManager();
for (const auto& mapping : mappings) {
auto renderable = rm.getInstance(mapping.renderable);
rm.setMaterialInstanceAt(renderable, mapping.primitiveIndex, mapping.material);
@@ -333,20 +333,20 @@ size_t FilamentInstance::getMaterialVariantCount() const noexcept {
}
FilamentAsset const* FilamentInstance::getAsset() const noexcept {
return downcast(this)->owner;
return downcast(this)->mOwner;
}
size_t FilamentInstance::getEntityCount() const noexcept {
return downcast(this)->entities.size();
return downcast(this)->mEntities.size();
}
const Entity* FilamentInstance::getEntities() const noexcept {
const auto& entities = downcast(this)->entities;
const auto& entities = downcast(this)->mEntities;
return entities.empty() ? nullptr : entities.data();
}
Entity FilamentInstance::getRoot() const noexcept {
return downcast(this)->root;
return downcast(this)->mRoot;
}
Animator* FilamentInstance::getAnimator() noexcept {
@@ -382,7 +382,7 @@ void FilamentInstance::recomputeBoundingBoxes() {
}
Aabb FilamentInstance::getBoundingBox() const noexcept {
return downcast(this)->boundingBox;
return downcast(this)->mBoundingBox;
}
} // namespace filament::gltfio

View File

@@ -38,6 +38,7 @@
#include <utils/Path.h>
#include <cgltf.h>
#include <meshoptimizer.h>
#include <math/quat.h>
#include <math/vec3.h>
@@ -223,6 +224,64 @@ static void decodeDracoMeshes(FFilamentAsset* asset) {
}
}
static void decodeMeshoptCompression(cgltf_data* data) {
for (size_t i = 0; i < data->buffer_views_count; ++i) {
if (!data->buffer_views[i].has_meshopt_compression) {
continue;
}
cgltf_meshopt_compression* compression = &data->buffer_views[i].meshopt_compression;
const uint8_t* source = (const uint8_t*) compression->buffer->data;
assert_invariant(source);
source += compression->offset;
// This memory is freed by cgltf.
void* destination = malloc(compression->count * compression->stride);
assert_invariant(destination);
int error = 0;
switch (compression->mode) {
case cgltf_meshopt_compression_mode_invalid:
break;
case cgltf_meshopt_compression_mode_attributes:
error = meshopt_decodeVertexBuffer(destination, compression->count, compression->stride,
source, compression->size);
break;
case cgltf_meshopt_compression_mode_triangles:
error = meshopt_decodeIndexBuffer(destination, compression->count, compression->stride,
source, compression->size);
break;
case cgltf_meshopt_compression_mode_indices:
error = meshopt_decodeIndexSequence(destination, compression->count, compression->stride,
source, compression->size);
break;
default:
assert_invariant(false);
break;
}
assert_invariant(!error);
switch (compression->filter) {
case cgltf_meshopt_compression_filter_none:
break;
case cgltf_meshopt_compression_filter_octahedral:
meshopt_decodeFilterOct(destination, compression->count, compression->stride);
break;
case cgltf_meshopt_compression_filter_quaternion:
meshopt_decodeFilterQuat(destination, compression->count, compression->stride);
break;
case cgltf_meshopt_compression_filter_exponential:
meshopt_decodeFilterExp(destination, compression->count, compression->stride);
break;
default:
assert_invariant(false);
break;
}
data->buffer_views[i].data = destination;
}
}
// Parses a data URI and returns a blob that gets malloc'd in cgltf, which the caller must free.
// (implementation snarfed from meshoptimizer)
static const uint8_t* parseDataUri(const char* uri, std::string* mimeType, size_t* psize) {
@@ -394,10 +453,10 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
return false;
}
#endif
// Decompress Draco meshes early on, which allows us to exploit subsequent processing such as
// tangent generation.
decodeDracoMeshes(asset);
decodeMeshoptCompression((cgltf_data*) gltf);
// For each skin, optionally normalize skinning weights and store a copy of the bind matrices.
if (gltf->skins_count > 0) {
@@ -414,9 +473,16 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
const cgltf_accessor* srcMatrices = srcSkin.inverse_bind_matrices;
FixedCapacityVector<mat4f> inverseBindMatrices(srcSkin.joints_count);
if (srcMatrices) {
uint8_t* bytes = (uint8_t*) srcMatrices->buffer_view->buffer->data;
uint8_t* bytes = nullptr;
uint8_t* srcBuffer = nullptr;
if (srcMatrices->buffer_view->has_meshopt_compression) {
bytes = (uint8_t*) srcMatrices->buffer_view->data;
srcBuffer = bytes + srcMatrices->offset;
} else {
bytes = (uint8_t*) srcMatrices->buffer_view->buffer->data;
srcBuffer = bytes + srcMatrices->offset + srcMatrices->buffer_view->offset;
}
assert_invariant(bytes);
uint8_t* srcBuffer = bytes + srcMatrices->offset + srcMatrices->buffer_view->offset;
memcpy((uint8_t*) inverseBindMatrices.data(),
(const void*) srcBuffer, srcSkin.joints_count * sizeof(mat4f));
}
@@ -436,8 +502,16 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
if (!accessor->buffer_view) {
continue;
}
auto bufferData = (const uint8_t*) accessor->buffer_view->buffer->data;
const uint8_t* data = computeBindingOffset(accessor) + bufferData;
const uint8_t* bufferData = nullptr;
const uint8_t* data = nullptr;
if (accessor->buffer_view->has_meshopt_compression) {
bufferData = (const uint8_t*) accessor->buffer_view->data;
data = bufferData + accessor->offset;
} else {
bufferData = (const uint8_t*) accessor->buffer_view->buffer->data;
data = computeBindingOffset(accessor) + bufferData + accessor->offset;
}
assert_invariant(bufferData);
const uint32_t size = computeBindingSize(accessor);
if (slot.vertexBuffer) {
if (requiresConversion(accessor)) {

View File

@@ -271,7 +271,7 @@ if (NOT ANDROID)
# Install resources
install(DIRECTORY ${PROJECT_BINARY_DIR}/assets/fonts DESTINATION bin/assets)
install(DIRECTORY ${PROJECT_BINARY_DIR}/assets/ibl/ DESTINATION bin/assets)
install(DIRECTORY ${PROJECT_BINARY_DIR}/assets/ibl/ DESTINATION bin/assets/ibl)
endif()
# ==================================================================================================

View File

@@ -1,37 +1,160 @@
project(meshoptimizer)
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.0)
# option(BUILD_DEMO "Build demo" OFF)
if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW) # Enables override of options from parent CMakeLists.txt
endif()
if(POLICY CMP0091)
cmake_policy(SET CMP0091 NEW) # Enables use of MSVC_RUNTIME_LIBRARY
endif()
if(POLICY CMP0092)
cmake_policy(SET CMP0092 NEW) # Enables clean /W4 override for MSVC
endif()
project(meshoptimizer VERSION 0.18 LANGUAGES CXX)
option(MESHOPT_BUILD_DEMO "Build demo" OFF)
option(MESHOPT_BUILD_GLTFPACK "Build gltfpack" OFF)
option(MESHOPT_BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(MESHOPT_WERROR "Treat warnings as errors" OFF)
set(MESHOPT_BASISU_PATH "" CACHE STRING "")
set(SOURCES
src/meshoptimizer.h
src/allocator.cpp
src/clusterizer.cpp
src/indexcodec.cpp
src/indexgenerator.cpp
src/overdrawanalyzer.cpp
src/overdrawoptimizer.cpp
src/simplifier.cpp
src/spatialorder.cpp
src/stripifier.cpp
src/vcacheanalyzer.cpp
src/vcacheoptimizer.cpp
src/vertexcodec.cpp
src/vertexfilter.cpp
src/vfetchanalyzer.cpp
src/vfetchoptimizer.cpp
)
add_library(meshoptimizer STATIC ${SOURCES})
target_include_directories(meshoptimizer INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")
set_target_properties(meshoptimizer PROPERTIES FOLDER ThirdParty)
set(GLTF_SOURCES
gltf/animation.cpp
gltf/basisenc.cpp
gltf/basislib.cpp
gltf/fileio.cpp
gltf/gltfpack.cpp
gltf/image.cpp
gltf/json.cpp
gltf/material.cpp
gltf/mesh.cpp
gltf/node.cpp
gltf/parseobj.cpp
gltf/parsegltf.cpp
gltf/stream.cpp
gltf/write.cpp
)
if(MSVC)
target_compile_options(meshoptimizer PRIVATE /W4 /WX)
add_compile_options(/W4)
else()
target_compile_options(meshoptimizer PRIVATE -Wall -Wextra -Werror)
add_compile_options(-Wall -Wextra -Wshadow -Wno-missing-field-initializers)
endif()
if(BUILD_DEMO)
add_executable(demo demo/main.cpp demo/miniz.cpp demo/objparser.cpp)
if(MESHOPT_WERROR)
if(MSVC)
add_compile_options(/WX)
else()
add_compile_options(-Werror)
endif()
endif()
if(MESHOPT_BUILD_SHARED_LIBS)
add_library(meshoptimizer SHARED ${SOURCES})
else()
add_library(meshoptimizer STATIC ${SOURCES})
endif()
target_include_directories(meshoptimizer INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
if(MESHOPT_BUILD_SHARED_LIBS)
set_target_properties(meshoptimizer PROPERTIES CXX_VISIBILITY_PRESET hidden)
set_target_properties(meshoptimizer PROPERTIES VISIBILITY_INLINES_HIDDEN ON)
if(WIN32)
target_compile_definitions(meshoptimizer INTERFACE "MESHOPTIMIZER_API=__declspec(dllimport)")
target_compile_definitions(meshoptimizer PRIVATE "MESHOPTIMIZER_API=__declspec(dllexport)")
else()
target_compile_definitions(meshoptimizer PUBLIC "MESHOPTIMIZER_API=__attribute__((visibility(\"default\")))")
endif()
endif()
set(TARGETS meshoptimizer)
if(MESHOPT_BUILD_DEMO)
add_executable(demo demo/main.cpp demo/tests.cpp tools/meshloader.cpp)
target_link_libraries(demo meshoptimizer)
endif()
install(TARGETS meshoptimizer ARCHIVE DESTINATION lib/${DIST_DIR})
if(MESHOPT_BUILD_GLTFPACK)
add_executable(gltfpack ${GLTF_SOURCES} tools/meshloader.cpp)
set_target_properties(gltfpack PROPERTIES CXX_STANDARD 11)
target_link_libraries(gltfpack meshoptimizer)
list(APPEND TARGETS gltfpack)
if(MESHOPT_BUILD_SHARED_LIBS)
string(CONCAT RPATH "$ORIGIN/../" ${CMAKE_INSTALL_LIBDIR})
set_target_properties(gltfpack PROPERTIES INSTALL_RPATH ${RPATH})
endif()
if(NOT MESHOPT_BASISU_PATH STREQUAL "")
get_filename_component(BASISU_PATH ${MESHOPT_BASISU_PATH} ABSOLUTE)
target_compile_definitions(gltfpack PRIVATE WITH_BASISU)
set_source_files_properties(gltf/basisenc.cpp gltf/basislib.cpp PROPERTIES INCLUDE_DIRECTORIES ${BASISU_PATH})
if(NOT MSVC AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
set_source_files_properties(gltf/basislib.cpp PROPERTIES COMPILE_OPTIONS -msse4.1)
endif()
if(UNIX)
target_link_libraries(gltfpack pthread)
endif()
endif()
endif()
include(GNUInstallDirs)
install(TARGETS ${TARGETS} EXPORT meshoptimizerTargets
COMPONENT meshoptimizer
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES src/meshoptimizer.h COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(EXPORT meshoptimizerTargets COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer NAMESPACE meshoptimizer::)
# TARGET_PDB_FILE is available since 3.1
if(MSVC AND NOT (CMAKE_VERSION VERSION_LESS "3.1"))
foreach(TARGET ${TARGETS})
get_target_property(TARGET_TYPE ${TARGET} TYPE)
if(NOT ${TARGET_TYPE} STREQUAL "STATIC_LIBRARY")
install(FILES $<TARGET_PDB_FILE:${TARGET}> COMPONENT meshoptimizer DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
endif()
endforeach(TARGET)
endif()
include(CMakePackageConfigHelpers)
configure_package_config_file(config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer NO_SET_AND_CHECK_MACRO)
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake COMPATIBILITY ExactVersion)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake
COMPONENT meshoptimizer
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer)

View File

@@ -0,0 +1,54 @@
Thanks for deciding to contribute to meshoptimizer! These guidelines will try to help make the process painless and efficient.
## Questions
If you have a question regarding the library usage, please [open a GitHub issue](https://github.com/zeux/meshoptimizer/issues/new).
Some questions just need answers, but it's nice to keep them for future reference in case other people want to know the same thing.
Some questions help improve the library interface or documentation by inspiring future changes.
## Bugs
If the library doesn't compile on your system, compiles with warnings, doesn't seem to run correctly for your input data or if anything else is amiss, please [open a GitHub issue](https://github.com/zeux/meshoptimizer/issues/new).
It helps if you note the version of the library this issue happens in, the version of your compiler for compilation issues, and a reproduction case for runtime bugs.
Of course, feel free to [create a pull request](https://help.github.com/articles/about-pull-requests/) to fix the bug yourself.
## Features
New algorithms and improvements to existing algorithms are always welcome; you can open an issue or make the change yourself and submit a pull request.
For major features, consider opening an issue describing an improvement you'd like to see or make before opening a pull request.
This will give us a chance to discuss the idea before implementing it - some algorithms may not be easy to integrate into existing programs, may not be robust to arbitrary meshes or may be expensive to run or implement/maintain, so a discussion helps make sure these don't block the algorithm development.
## Code style
Contributions to this project are expected to follow the existing code style.
`.clang-format` file mostly defines syntactic styling rules (you can run `make format` to format the code accordingly).
As for naming conventions, this library uses `snake_case` for variables, `lowerCamelCase` for functions, `UpperCamelCase` for types, `kCamelCase` for global constants and `SCARY_CASE` for macros. All public functions/types must additionally have an extra `meshopt_` prefix to avoid symbol conflicts.
## Dependencies
Please note that this library uses C89 interface for all APIs and a C++98 implementation - C++11 features can not be used.
This choice is made to maximize compatibility to make sure that any toolchain, including legacy proprietary gaming console toolchains, can compile this code.
Additionally, the library code has zero external dependencies, does not depend on STL and does not use RTTI or exceptions.
This, again, maximizes compatibility and makes sure the library can be used in environments where STL use is discouraged or prohibited, as well as maximizing runtime performance and minimizing compilation times.
The demo program uses STL since it serves as an example of usage and as a test harness, not as production-ready code.
## Testing
All pull requests will run through a continuous integration pipeline using GitHub Actions that will run the built-in unit tests and integration tests on Windows, macOS and Linux with gcc, clang and msvc compilers.
You can run the tests yourself using `make test` or building the demo program with `cmake -DBUILD_DEMO=ON` and running it.
Unit tests can be found in `demo/tests.cpp` and functional tests - in `demo/main.cpp`; when making code changes please try to make sure they are covered by an existing test or add a new test accordingly.
## Documentation
Documentation for this library resides in the `meshoptimizer.h` header, with examples as part of a usage manual available in `README.md`.
Changes to documentation are always welcome and should use issues/pull requests as outlined above; please note that `README.md` only contains documentation for stable algorithms, as experimental algorithms may change the interface without concern for backwards compatibility.
## Sensitive communication
If you prefer to not disclose the issues or information relevant to the issue such as reproduction case to the public, you can always contact the author via e-mail (arseny.kapoulkine@gmail.com).

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016-2018 Arseny Kapoulkine
Copyright (c) 2016-2022 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,20 +1,73 @@
.SUFFIXES:
MAKEFLAGS+=-r
MAKEFLAGS+=-r -j
config=debug
files=demo/pirate.obj
BUILD=build/$(config)
SOURCES=$(wildcard src/*.cpp demo/*.c demo/*.cpp)
OBJECTS=$(SOURCES:%=$(BUILD)/%.o)
LIBRARY_SOURCES=$(wildcard src/*.cpp)
LIBRARY_OBJECTS=$(LIBRARY_SOURCES:%=$(BUILD)/%.o)
EXECUTABLE=$(BUILD)/meshoptimizer
DEMO_SOURCES=$(wildcard demo/*.c demo/*.cpp) tools/meshloader.cpp
DEMO_OBJECTS=$(DEMO_SOURCES:%=$(BUILD)/%.o)
GLTFPACK_SOURCES=$(wildcard gltf/*.cpp) tools/meshloader.cpp
GLTFPACK_OBJECTS=$(GLTFPACK_SOURCES:%=$(BUILD)/%.o)
OBJECTS=$(LIBRARY_OBJECTS) $(DEMO_OBJECTS) $(GLTFPACK_OBJECTS)
LIBRARY=$(BUILD)/libmeshoptimizer.a
DEMO=$(BUILD)/meshoptimizer
CFLAGS=-g -Wall -Wextra -Werror -std=c89
CXXFLAGS=-g -Wall -Wextra -Wno-missing-field-initializers -Werror -std=c++98
CXXFLAGS=-g -Wall -Wextra -Wshadow -Wno-missing-field-initializers -Werror -std=c++98
LDFLAGS=
$(GLTFPACK_OBJECTS): CXXFLAGS+=-std=c++11
ifdef BASISU
$(GLTFPACK_OBJECTS): CXXFLAGS+=-DWITH_BASISU
$(BUILD)/gltf/basis%.cpp.o: CXXFLAGS+=-I$(BASISU)
gltfpack: LDFLAGS+=-lpthread
ifeq ($(HOSTTYPE),x86_64)
$(BUILD)/gltf/basislib.cpp.o: CXXFLAGS+=-msse4.1
endif
endif
WASMCC?=$(WASI_SDK)/bin/clang++
WASIROOT?=$(WASI_SDK)/share/wasi-sysroot
WASM_FLAGS=--target=wasm32-wasi --sysroot=$(WASIROOT)
WASM_FLAGS+=-O3 -DNDEBUG -nostartfiles -nostdlib -Wl,--no-entry -Wl,-s
WASM_FLAGS+=-fno-slp-vectorize -fno-vectorize -fno-unroll-loops
WASM_FLAGS+=-Wl,-z -Wl,stack-size=24576 -Wl,--initial-memory=65536
WASM_EXPORT_PREFIX=-Wl,--export
WASM_DECODER_SOURCES=src/vertexcodec.cpp src/indexcodec.cpp src/vertexfilter.cpp tools/wasmstubs.cpp
WASM_DECODER_EXPORTS=meshopt_decodeVertexBuffer meshopt_decodeIndexBuffer meshopt_decodeIndexSequence meshopt_decodeFilterOct meshopt_decodeFilterQuat meshopt_decodeFilterExp sbrk __wasm_call_ctors
WASM_ENCODER_SOURCES=src/vertexcodec.cpp src/indexcodec.cpp src/vertexfilter.cpp src/vcacheoptimizer.cpp src/vfetchoptimizer.cpp tools/wasmstubs.cpp
WASM_ENCODER_EXPORTS=meshopt_encodeVertexBuffer meshopt_encodeVertexBufferBound meshopt_encodeIndexBuffer meshopt_encodeIndexBufferBound meshopt_encodeIndexSequence meshopt_encodeIndexSequenceBound meshopt_encodeVertexVersion meshopt_encodeIndexVersion meshopt_encodeFilterOct meshopt_encodeFilterQuat meshopt_encodeFilterExp meshopt_optimizeVertexCache meshopt_optimizeVertexCacheStrip meshopt_optimizeVertexFetchRemap sbrk __wasm_call_ctors
WASM_SIMPLIFIER_SOURCES=src/simplifier.cpp src/vfetchoptimizer.cpp tools/wasmstubs.cpp
WASM_SIMPLIFIER_EXPORTS=meshopt_simplify meshopt_simplifyScale meshopt_optimizeVertexFetchRemap sbrk __wasm_call_ctors
ifeq ($(config),iphone)
IPHONESDK=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
CFLAGS+=-arch armv7 -arch arm64 -isysroot $(IPHONESDK)
CXXFLAGS+=-arch armv7 -arch arm64 -isysroot $(IPHONESDK) -stdlib=libc++
LDFLAGS+=-arch armv7 -arch arm64 -isysroot $(IPHONESDK) -L $(IPHONESDK)/usr/lib -mios-version-min=7.0
endif
ifeq ($(config),trace)
CXXFLAGS+=-DTRACE=1
endif
ifeq ($(config),scalar)
CXXFLAGS+=-O3 -DNDEBUG -DMESHOPTIMIZER_NO_SIMD
endif
ifeq ($(config),release)
CXXFLAGS+=-O3 -DNDEBUG
endif
@@ -33,19 +86,92 @@ ifeq ($(config),analyze)
CXXFLAGS+=--analyze
endif
all: $(EXECUTABLE)
all: $(DEMO)
test: $(EXECUTABLE)
$(EXECUTABLE) $(files)
test: $(DEMO)
$(DEMO) $(files)
dev: $(EXECUTABLE)
$(EXECUTABLE) -d $(files)
check: $(DEMO)
$(DEMO)
dev: $(DEMO)
$(DEMO) -d $(files)
format:
clang-format -i $(SOURCES)
clang-format -i $(LIBRARY_SOURCES) $(DEMO_SOURCES) $(GLTFPACK_SOURCES)
$(EXECUTABLE): $(OBJECTS)
$(CXX) $(OBJECTS) $(LDFLAGS) -o $@
js: js/meshopt_decoder.js js/meshopt_decoder.module.js js/meshopt_encoder.js js/meshopt_encoder.module.js js/meshopt_simplifier.js js/meshopt_simplifier.module.js
gltfpack: $(BUILD)/gltfpack
ln -fs $^ $@
$(BUILD)/gltfpack: $(GLTFPACK_OBJECTS) $(LIBRARY)
$(CXX) $^ $(LDFLAGS) -o $@
gltfpack.wasm: gltf/library.wasm
gltf/library.wasm: ${LIBRARY_SOURCES} ${GLTFPACK_SOURCES} tools/meshloader.cpp
$(WASMCC) $^ -o $@ -Os -DNDEBUG --target=wasm32-wasi --sysroot=$(WASIROOT) -nostartfiles -Wl,--no-entry -Wl,--export=pack -Wl,--export=malloc -Wl,--export=free -Wl,--export=__wasm_call_ctors -Wl,-s -Wl,--allow-undefined-file=gltf/wasistubs.txt
build/decoder_base.wasm: $(WASM_DECODER_SOURCES)
@mkdir -p build
$(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_DECODER_EXPORTS)) -o $@
build/decoder_simd.wasm: $(WASM_DECODER_SOURCES)
@mkdir -p build
$(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_DECODER_EXPORTS)) -o $@ -msimd128 -mbulk-memory
build/encoder.wasm: $(WASM_ENCODER_SOURCES)
@mkdir -p build
$(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_ENCODER_EXPORTS)) -lc -o $@
build/simplifier.wasm: $(WASM_SIMPLIFIER_SOURCES)
@mkdir -p build
$(WASMCC) $^ $(WASM_FLAGS) $(patsubst %,$(WASM_EXPORT_PREFIX)=%,$(WASM_SIMPLIFIER_EXPORTS)) -lc -o $@
js/meshopt_decoder.js: build/decoder_base.wasm build/decoder_simd.wasm tools/wasmpack.py
sed -i "s#Built with clang.*#Built with $$($(WASMCC) --version | head -n 1 | sed 's/\s\+(.*//')#" $@
sed -i "s#\(var wasm_base = \)\".*\";#\\1\"$$(cat build/decoder_base.wasm | python3 tools/wasmpack.py)\";#" $@
sed -i "s#\(var wasm_simd = \)\".*\";#\\1\"$$(cat build/decoder_simd.wasm | python3 tools/wasmpack.py)\";#" $@
js/meshopt_encoder.js: build/encoder.wasm tools/wasmpack.py
sed -i "s#Built with clang.*#Built with $$($(WASMCC) --version | head -n 1 | sed 's/\s\+(.*//')#" $@
sed -i "s#\(var wasm = \)\".*\";#\\1\"$$(cat build/encoder.wasm | python3 tools/wasmpack.py)\";#" $@
js/meshopt_simplifier.js: build/simplifier.wasm tools/wasmpack.py
sed -i "s#Built with clang.*#Built with $$($(WASMCC) --version | head -n 1 | sed 's/\s\+(.*//')#" $@
sed -i "s#\(var wasm = \)\".*\";#\\1\"$$(cat build/simplifier.wasm | python3 tools/wasmpack.py)\";#" $@
js/%.module.js: js/%.js
sed '/UMD-style export/,$$d' <$< >$@
sed -n "s#\s*module.exports = \(.*\);#export { \\1 };#p" <$< >>$@
$(DEMO): $(DEMO_OBJECTS) $(LIBRARY)
$(CXX) $^ $(LDFLAGS) -o $@
vcachetuner: tools/vcachetuner.cpp $(BUILD)/tools/meshloader.cpp.o $(BUILD)/demo/miniz.cpp.o $(LIBRARY)
$(CXX) $^ -fopenmp $(CXXFLAGS) -std=c++11 $(LDFLAGS) -o $@
codecbench: tools/codecbench.cpp $(LIBRARY)
$(CXX) $^ $(CXXFLAGS) $(LDFLAGS) -o $@
codecbench.js: tools/codecbench.cpp ${LIBRARY_SOURCES}
emcc $^ -O3 -g -DNDEBUG -s TOTAL_MEMORY=268435456 -s SINGLE_FILE=1 -o $@
codecbench-simd.js: tools/codecbench.cpp ${LIBRARY_SOURCES}
emcc $^ -O3 -g -DNDEBUG -s TOTAL_MEMORY=268435456 -s SINGLE_FILE=1 -msimd128 -o $@
codecbench.wasm: tools/codecbench.cpp ${LIBRARY_SOURCES}
$(WASMCC) $^ -fno-exceptions --target=wasm32-wasi --sysroot=$(WASIROOT) -lc++ -lc++abi -O3 -g -DNDEBUG -o $@
codecbench-simd.wasm: tools/codecbench.cpp ${LIBRARY_SOURCES}
$(WASMCC) $^ -fno-exceptions --target=wasm32-wasi --sysroot=$(WASIROOT) -lc++ -lc++abi -O3 -g -DNDEBUG -msimd128 -o $@
codecfuzz: tools/codecfuzz.cpp src/vertexcodec.cpp src/indexcodec.cpp
$(CXX) $^ -fsanitize=fuzzer,address,undefined -O1 -g -o $@
$(LIBRARY): $(LIBRARY_OBJECTS)
ar rcs $@ $^
$(BUILD)/%.cpp.o: %.cpp
@mkdir -p $(dir $@)
@@ -56,7 +182,8 @@ $(BUILD)/%.c.o: %.c
$(CC) $< $(CFLAGS) -c -MMD -MP -o $@
-include $(OBJECTS:.o=.d)
clean:
rm -rf $(BUILD)
.PHONY: all clean format
.PHONY: all clean format js

View File

@@ -1,11 +1,33 @@
# meshoptimizer [![Build Status](https://travis-ci.org/zeux/meshoptimizer.svg?branch=master)](https://travis-ci.org/zeux/meshoptimizer) [![codecov.io](http://codecov.io/github/zeux/meshoptimizer/coverage.svg?branch=master)](http://codecov.io/github/zeux/meshoptimizer?branch=master) ![MIT](https://img.shields.io/badge/license-MIT-blue.svg)
# 🐇 meshoptimizer [![Actions Status](https://github.com/zeux/meshoptimizer/workflows/build/badge.svg)](https://github.com/zeux/meshoptimizer/actions) [![codecov.io](https://codecov.io/github/zeux/meshoptimizer/coverage.svg?branch=master)](https://codecov.io/github/zeux/meshoptimizer?branch=master) ![MIT](https://img.shields.io/badge/license-MIT-blue.svg) [![GitHub](https://img.shields.io/badge/repo-github-green.svg)](https://github.com/zeux/meshoptimizer)
## Purpose
When GPU renders triangle meshes, various stages of the GPU pipeline have to process vertex and index data. The efficiency of these stages depends on the data you feed to them; this library provides algorithms to help optimize meshes for these stages, as well as algorithms to reduce the mesh complexity and storage overhead.
When a GPU renders triangle meshes, various stages of the GPU pipeline have to process vertex and index data. The efficiency of these stages depends on the data you feed to them; this library provides algorithms to help optimize meshes for these stages, as well as algorithms to reduce the mesh complexity and storage overhead.
The library provides a C and C++ interface for all algorithms; you can use it from C/C++ or from other languages via FFI (such as P/Invoke). If you want to use this library from Rust, you should use [meshopt crate](https://crates.io/crates/meshopt).
[gltfpack](gltf), which is a tool that can automatically optimize glTF files, is developed and distributed alongside the library.
## Installing
meshoptimizer is hosted on GitHub; you can download the latest release using git:
```
git clone -b v0.18 https://github.com/zeux/meshoptimizer.git
```
Alternatively you can [download the .zip archive from GitHub](https://github.com/zeux/meshoptimizer/archive/v0.18.zip).
The library is also available as a package ([ArchLinux](https://aur.archlinux.org/packages/meshoptimizer/), [Debian](https://packages.debian.org/libmeshoptimizer), [Ubuntu](https://packages.ubuntu.com/libmeshoptimizer), [Vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/meshoptimizer)).
### Installing gltfpack
`gltfpack` is a CLI tool for optimizing meshes using meshoptimizer.
You can download a pre-built binary for gltfpack on [Releases page](https://github.com/zeux/meshoptimizer/releases), or install [npm package](https://www.npmjs.com/package/gltfpack). Native binaries are recommended over npm since they can work with larger files, run faster, and support texture compression.
[Learn more about gltfpack](./gltf/README.md)
## Building
meshoptimizer is distributed as a set of C++ source files. To include it into your project, you can use one of the two options:
@@ -15,16 +37,21 @@ meshoptimizer is distributed as a set of C++ source files. To include it into yo
The source files are organized in such a way that you don't need to change your build-system settings, and you only need to add the files for the algorithms you use.
## Installing from vcpkg
The meshoptimizer port in [vcpkg](https://github.com/Microsoft/vcpkg) is kept up to date by Microsoft team members and community contributors. You can download and install meshoptimizer using the vcpkg dependency manager, [Getting Started](https://github.com/microsoft/vcpkg#getting-started).
## Pipeline
When optimizing a mesh, you should typically feed it through a set of optimizations (the order is important!):
1. Indexing
2. Vertex cache optimization
3. Overdraw optimization
4. Vertex fetch optimization
5. Vertex quantization
6. (optional) Vertex/index buffer compression
2. (optional, discussed last) Simplification
3. Vertex cache optimization
4. Overdraw optimization
5. Vertex fetch optimization
6. Vertex quantization
7. (optional) Vertex/index buffer compression
## Indexing
@@ -38,7 +65,7 @@ std::vector<unsigned int> remap(index_count); // allocate temporary memory for t
size_t vertex_count = meshopt_generateVertexRemap(&remap[0], NULL, index_count, &unindexed_vertices[0], index_count, sizeof(Vertex));
```
Note that in this case we only have an unindexed vertex buffer; the remap table is generated based on binary equivalence of the input vertices, so the resulting mesh will render the same way.
Note that in this case we only have an unindexed vertex buffer; the remap table is generated based on binary equivalence of the input vertices, so the resulting mesh will render the same way. Binary equivalence considers all input bytes, including padding which should be zero-initialized if the vertex structure has gaps.
After generating the remap table, you can allocate space for the target vertex buffer (`vertex_count` elements) and index buffer (`index_count` elements) and generate them:
@@ -108,11 +135,13 @@ unsigned short pz = meshopt_quantizeHalf(v.z);
## Vertex/index buffer compression
After all of the above optimizations, the geometry data is optimal for GPU to consume - however, you don't have to store the data as is. In case storage size or transmission bandwidth is of importance, you might want to compress vertex and index data. While several mesh compression libraries, like Google Draco, are available, they typically are designed to maximize the compression ratio at the cost of disturbing the vertex/index order (which makes the meshes inefficient to render on GPU) or decompression performance. Additionally they frequently don't support custom game-ready quantized vertex formats and thus require to re-quantize the data after loading it, introducing extra quantization errors and making decoding slower.
In case storage size or transmission bandwidth is of importance, you might want to additionally compress vertex and index data. While several mesh compression libraries, like Google Draco, are available, they typically are designed to maximize the compression ratio at the cost of disturbing the vertex/index order (which makes the meshes inefficient to render on GPU) or decompression performance. They also frequently don't support custom game-ready quantized vertex formats and thus require to re-quantize the data after loading it, introducing extra quantization errors and making decoding slower.
Alternatively you can use general purpose compression libraries like zstd or Oodle to compress vertex/index data - however these compressors aren't designed to exploit redundancies in vertex/index data and as such compression rates can be unsatisfactory.
To that end, this library provides algorithms to "encode" vertex and index data. The result of the encoding is generally significantly smaller than initial data, and remains compressible with general purpose compressors - so you can either store encoded data directly (for modest compression ratios and maximum decoding performance), or further compress it with zstd et al, to maximize compression rate.
To that end, this library provides algorithms to "encode" vertex and index data. The result of the encoding is generally significantly smaller than initial data, and remains compressible with general purpose compressors - so you can either store encoded data directly (for modest compression ratios and maximum decoding performance), or further compress it with zstd/Oodle to maximize compression ratio.
> Note: this compression scheme is available as a glTF extension [EXT_meshopt_compression](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Vendor/EXT_meshopt_compression/README.md).
To encode, you need to allocate target buffers (preferably using the worst case bound) and call encoding functions:
@@ -128,13 +157,51 @@ You can then either serialize `vbuf`/`ibuf` as is, or compress them further. To
```c++
int resvb = meshopt_decodeVertexBuffer(vertices, vertex_count, sizeof(Vertex), &vbuf[0], vbuf.size());
int resib = meshopt_decodeIndexBuffer(indices, index_count, &buffer[0], buffer.size());
int resib = meshopt_decodeIndexBuffer(indices, index_count, &ibuf[0], ibuf.size());
assert(resvb == 0 && resib == 0);
```
Note that vertex encoding assumes that vertex buffer was optimized for vertex fetch, and that vertices are quantized; index encoding assumes that the vertex/index buffers were optimized for vertex cache and vertex fetch. Feeding unoptimized data into the encoders will produce poor compression rates. Both codecs are lossless - the only lossy step is quantization that happens before encoding.
Note that vertex encoding assumes that vertex buffer was optimized for vertex fetch, and that vertices are quantized; index encoding assumes that the vertex/index buffers were optimized for vertex cache and vertex fetch. Feeding unoptimized data into the encoders will produce poor compression ratios. Both codecs are lossless - the only lossy step is quantization that happens before encoding.
Decoding functions are heavily optimized and can directly target write-combined memory; you can expect both decoders to run at 1-2 GB/s on modern desktop CPUs. Compression ratios depend on the data; vertex data compression ratio is typically around 2-4x (compared to already quantized data), index data compression ratio is around 5-6x (compared to raw 16-bit index data). General purpose lossless compressors can further improve on these results.
To reduce the data size further, it's recommended to use `meshopt_optimizeVertexCacheStrip` instead of `meshopt_optimizeVertexCache` when optimizing for vertex cache, and to use new index codec version (`meshopt_encodeIndexVersion(1)`). This trades off some efficiency in vertex transform for smaller vertex and index data.
Decoding functions are heavily optimized and can directly target write-combined memory; you can expect both decoders to run at 1-3 GB/s on modern desktop CPUs. Compression ratios depend on the data; vertex data compression ratio is typically around 2-4x (compared to already quantized data), index data compression ratio is around 5-6x (compared to raw 16-bit index data). General purpose lossless compressors can further improve on these results.
Index buffer codec only supports triangle list topology; when encoding triangle strips or line lists, use `meshopt_encodeIndexSequence`/`meshopt_decodeIndexSequence` instead. This codec typically encodes indices into ~1 byte per index, but compressing the results further with a general purpose compressor can improve the results to 1-3 bits per index.
The following guarantees on data compatibility are provided for point releases (*no* guarantees are given for development branch):
- Data encoded with older versions of the library can always be decoded with newer versions;
- Data encoded with newer versions of the library can be decoded with older versions, provided that encoding versions are set correctly; if binary stability of encoded data is important, use `meshopt_encodeVertexVersion` and `meshopt_encodeIndexVersion` to 'pin' the data versions.
Due to a very high decoding performance and compatibility with general purpose lossless compressors, the compression is a good fit for the use on the web. To that end, meshoptimizer provides both vertex and index decoders compiled into WebAssembly and wrapped into a module with JavaScript-friendly interface, `js/meshopt_decoder.js`, that you can use to decode meshes that were encoded offline:
```js
// ready is a Promise that is resolved when (asynchronous) WebAssembly compilation finishes
await MeshoptDecoder.ready;
// decode from *Data (Uint8Array) into *Buffer (Uint8Array)
MeshoptDecoder.decodeVertexBuffer(vertexBuffer, vertexCount, vertexSize, vertexData);
MeshoptDecoder.decodeIndexBuffer(indexBuffer, indexCount, indexSize, indexData);
```
[Usage example](https://meshoptimizer.org/demo/) is available, with source in `demo/index.html`; this example uses .GLB files encoded using `gltfpack`.
## Point cloud compression
The vertex encoding algorithms can be used to compress arbitrary streams of attribute data; one other use case besides triangle meshes is point cloud data. Typically point clouds come with position, color and possibly other attributes but don't have an implied point order.
To compress point clouds efficiently, it's recommended to first preprocess the points by sorting them using the spatial sort algorithm:
```c++
std::vector<unsigned int> remap(point_count);
meshopt_spatialSortRemap(&remap[0], positions, point_count, sizeof(vec3));
// for each attribute stream
meshopt_remapVertexBuffer(positions, positions, point_count, sizeof(vec3), &remap[0]);
```
After this the resulting arrays should be quantized (e.g. using 16-bit fixed point numbers for positions and 8-bit color components), and the result can be compressed using `meshopt_encodeVertexBuffer` as described in the previous section. To decompress, `meshopt_decodeVertexBuffer` will recover the quantized data that can be used directly or converted back to original floating-point data. The compression ratio depends on the nature of source data, for colored points it's typical to get 35-40 bits per point as a result.
## Triangle strip conversion
@@ -147,10 +214,130 @@ This library provides an algorithm for converting a vertex cache optimized trian
```c++
std::vector<unsigned int> strip(meshopt_stripifyBound(index_count));
size_t strip_size = meshopt_stripify(&strip[0], indices, index_count, vertex_count);
unsigned int restart_index = ~0u;
size_t strip_size = meshopt_stripify(&strip[0], indices, index_count, vertex_count, restart_index);
```
Typically you should expect triangle strips to have ~50-60% of indices compared to triangle lists (~1.5-1.8 indices per triangle) and have ~5% worse ACMR. Note that triangle strips require restart index support for rendering; using degenerate triangles to connect strips is not supported.
Typically you should expect triangle strips to have ~50-60% of indices compared to triangle lists (~1.5-1.8 indices per triangle) and have ~5% worse ACMR.
Note that triangle strips can be stitched with or without restart index support. Using restart indices can result in ~10% smaller index buffers, but on some GPUs restart indices may result in decreased performance.
To reduce the triangle strip size further, it's recommended to use `meshopt_optimizeVertexCacheStrip` instead of `meshopt_optimizeVertexCache` when optimizing for vertex cache. This trades off some efficiency in vertex transform for smaller index buffers.
## Deinterleaved geometry
All of the examples above assume that geometry is represented as a single vertex buffer and a single index buffer. This requires storing all vertex attributes - position, normal, texture coordinate, skinning weights etc. - in a single contiguous struct. However, in some cases using multiple vertex streams may be preferable. In particular, if some passes require only positional data - such as depth pre-pass or shadow map - then it may be beneficial to split it from the rest of the vertex attributes to make sure the bandwidth use during these passes is optimal. On some mobile GPUs a position-only attribute stream also improves efficiency of tiling algorithms.
Most of the functions in this library either only need the index buffer (such as vertex cache optimization) or only need positional information (such as overdraw optimization). However, several tasks require knowledge about all vertex attributes.
For indexing, `meshopt_generateVertexRemap` assumes that there's just one vertex stream; when multiple vertex streams are used, it's necessary to use `meshopt_generateVertexRemapMulti` as follows:
```c++
meshopt_Stream streams[] = {
{&unindexed_pos[0], sizeof(float) * 3, sizeof(float) * 3},
{&unindexed_nrm[0], sizeof(float) * 3, sizeof(float) * 3},
{&unindexed_uv[0], sizeof(float) * 2, sizeof(float) * 2},
};
std::vector<unsigned int> remap(index_count);
size_t vertex_count = meshopt_generateVertexRemapMulti(&remap[0], NULL, index_count, index_count, streams, sizeof(streams) / sizeof(streams[0]));
```
After this `meshopt_remapVertexBuffer` needs to be called once for each vertex stream to produce the correctly reindexed stream.
Instead of calling `meshopt_optimizeVertexFetch` for reordering vertices in a single vertex buffer for efficiency, calling `meshopt_optimizeVertexFetchRemap` and then calling `meshopt_remapVertexBuffer` for each stream again is recommended.
Finally, when compressing vertex data, `meshopt_encodeVertexBuffer` should be used on each vertex stream separately - this allows the encoder to best utilize corellation between attribute values for different vertices.
## Simplification
All algorithms presented so far don't affect visual appearance at all, with the exception of quantization that has minimal controlled impact. However, fundamentally the most effective way at reducing the rendering or transmission cost of a mesh is to make the mesh simpler.
This library provides two simplification algorithms that reduce the number of triangles in the mesh. Given a vertex and an index buffer, they generate a second index buffer that uses existing vertices in the vertex buffer. This index buffer can be used directly for rendering with the original vertex buffer (preferably after vertex cache optimization), or a new compact vertex/index buffer can be generated using `meshopt_optimizeVertexFetch` that uses the optimal number and order of vertices.
The first simplification algorithm, `meshopt_simplify`, follows the topology of the original mesh in an attempt to preserve attribute seams, borders and overall appearance. For meshes with inconsistent topology or many seams, such as faceted meshes, it can result in simplifier getting "stuck" and not being able to simplify the mesh fully; it's recommended to preprocess the index buffer with `meshopt_generateShadowIndexBuffer` to discard any vertex attributes that aren't critical and can be rebuilt later such as normals.
```c++
float threshold = 0.2f;
size_t target_index_count = size_t(index_count * threshold);
float target_error = 1e-2f;
std::vector<unsigned int> lod(index_count);
float lod_error = 0.f;
lod.resize(meshopt_simplify(&lod[0], indices, index_count, &vertices[0].x, vertex_count, sizeof(Vertex),
target_index_count, target_error, &lod_error));
```
Target error is an approximate measure of the deviation from the original mesh using distance normalized to 0..1 (so 1e-2f means that simplifier will try to maintain the error to be below 1% of the mesh extents). Note that because of topological restrictions and error bounds simplifier isn't guaranteed to reach the target index count and can stop earlier.
The second simplification algorithm, `meshopt_simplifySloppy`, doesn't follow the topology of the original mesh. This means that it doesn't preserve attribute seams or borders, but it can collapse internal details that are too small to matter better because it can merge mesh features that are topologically disjoint but spatially close.
```c++
float threshold = 0.2f;
size_t target_index_count = size_t(index_count * threshold);
float target_error = 1e-1f;
std::vector<unsigned int> lod(index_count);
float lod_error = 0.f;
lod.resize(meshopt_simplifySloppy(&lod[0], indices, index_count, &vertices[0].x, vertex_count, sizeof(Vertex),
target_index_count, target_error, &lod_error));
```
This algorithm will not stop early due to topology restrictions but can still do so if target index count can't be reached without introducing an error larger than target. It is 5-6x faster than `meshopt_simplify` when simplification ratio is large, and is able to reach ~20M triangles/sec on a desktop CPU (`meshopt_simplify` works at ~3M triangles/sec).
When a sequence of LOD meshes is generated that all use the original vertex buffer, care must be taken to order vertices optimally to not penalize mobile GPU architectures that are only capable of transforming a sequential vertex buffer range. It's recommended in this case to first optimize each LOD for vertex cache, then assemble all LODs in one large index buffer starting from the coarsest LOD (the one with fewest triangles), and call `meshopt_optimizeVertexFetch` on the final large index buffer. This will make sure that coarser LODs require a smaller vertex range and are efficient wrt vertex fetch and transform.
Both algorithms can also return the resulting normalized deviation that can be used to choose the correct level of detail based on screen size or solid angle; the error can be converted to world space by multiplying by the scaling factor returned by `meshopt_simplifyScale`.
## Mesh shading
Modern GPUs are beginning to deviate from the traditional rasterization model. NVidia GPUs starting from Turing and AMD GPUs starting from RDNA2 provide a new programmable geometry pipeline that, instead of being built around index buffers and vertex shaders, is built around mesh shaders - a new shader type that allows to provide a batch of work to the rasterizer.
Using mesh shaders in context of traditional mesh rendering provides an opportunity to use a variety of optimization techniques, starting from more efficient vertex reuse, using various forms of culling (e.g. cluster frustum or occlusion culling) and in-memory compression to maximize the utilization of GPU hardware. Beyond traditional rendering mesh shaders provide a richer programming model that can synthesize new geometry more efficiently than common alternatives such as geometry shaders. Mesh shading can be accessed via Vulkan or Direct3D 12 APIs; please refer to [Introduction to Turing Mesh Shaders](https://developer.nvidia.com/blog/introduction-turing-mesh-shaders/) and [Mesh Shaders and Amplification Shaders: Reinventing the Geometry Pipeline](https://devblogs.microsoft.com/directx/coming-to-directx-12-mesh-shaders-and-amplification-shaders-reinventing-the-geometry-pipeline/) for additional information.
To use mesh shaders for conventional rendering efficiently, geometry needs to be converted into a series of meshlets; each meshlet represents a small subset of the original mesh and comes with a small set of vertices and a separate micro-index buffer that references vertices in the meshlet. This information can be directly fed to the rasterizer from the mesh shader. This library provides algorithms to create meshlet data for a mesh, and - assuming geometry is static - can compute bounding information that can be used to perform cluster culling, a technique that can reject a meshlet if it's invisible on screen.
To generate meshlet data, this library provides two algorithms - `meshopt_buildMeshletsScan`, which creates the meshlet data using a vertex cache-optimized index buffer as a starting point by greedily aggregating consecutive triangles until they go over the meshlet limits, and `meshopt_buildMeshlets`, which doesn't depend on any other algorithms and tries to balance topological efficiency (by maximizing vertex reuse inside meshlets) with culling efficiency (by minimizing meshlet radius and triangle direction divergence). `meshopt_buildMeshlets` is recommended in cases when the resulting meshlet data will be used in cluster culling algorithms.
```c++
const size_t max_vertices = 64;
const size_t max_triangles = 124;
const float cone_weight = 0.0f;
size_t max_meshlets = meshopt_buildMeshletsBound(indices.size(), max_vertices, max_triangles);
std::vector<meshopt_Meshlet> meshlets(max_meshlets);
std::vector<unsigned int> meshlet_vertices(max_meshlets * max_vertices);
std::vector<unsigned char> meshlet_triangles(max_meshlets * max_triangles * 3);
size_t meshlet_count = meshopt_buildMeshlets(meshlets.data(), meshlet_vertices.data(), meshlet_triangles.data(), indices.data(),
indices.size(), &vertices[0].x, vertices.size(), sizeof(Vertex), max_vertices, max_triangles, cone_weight);
```
To generate the meshlet data, `max_vertices` and `max_triangles` need to be set within limits supported by the hardware; for NVidia the values of 64 and 124 are recommended. `cone_weight` should be left as 0 if cluster cone culling is not used, and set to a value between 0 and 1 to balance cone culling efficiency with other forms of culling like frustum or occlusion culling.
Each resulting meshlet refers to a portion of `meshlet_vertices` and `meshlet_triangles` arrays; this data can be uploaded to GPU and used directly after trimming:
```c++
const meshopt_Meshlet& last = meshlets[meshlet_count - 1];
meshlet_vertices.resize(last.vertex_offset + last.vertex_count);
meshlet_triangles.resize(last.triangle_offset + ((last.triangle_count * 3 + 3) & ~3));
meshlets.resize(meshlet_count);
```
However depending on the application other strategies of storing the data can be useful; for example, `meshlet_vertices` serves as indices into the original vertex buffer but it might be worthwhile to generate a mini vertex buffer for each meshlet to remove the extra indirection when accessing vertex data, or it might be desirable to compress vertex data as vertices in each meshlet are likely to be very spatially coherent.
After generating the meshlet data, it's also possible to generate extra data for each meshlet that can be saved and used at runtime to perform cluster culling, where each meshlet can be discarded if it's guaranteed to be invisible. To generate the data, `meshlet_computeMeshletBounds` can be used:
```c++
meshopt_Bounds bounds = meshopt_computeMeshletBounds(&meshlet_vertices[m.vertex_offset], &meshlet_triangles[m.triangle_offset],
m.triangle_count, &vertices[0].x, vertices.size(), sizeof(Vertex));
```
The resulting `bounds` values can be used to perform frustum or occlusion culling using the bounding sphere, or cone culling using the cone axis/angle (which will reject the entire meshlet if all triangles are guaranteed to be back-facing from the camera point of view):
```c++
if (dot(normalize(cone_apex - camera_position), cone_axis) >= cone_cutoff) reject();
```
## Efficiency analyzers
@@ -164,6 +351,20 @@ While the only way to get precise performance data is to measure performance on
Note that all analyzers use approximate models for the relevant GPU units, so the numbers you will get as the result are only a rough approximation of the actual performance.
## Memory management
Many algorithms allocate temporary memory to store intermediate results or accelerate processing. The amount of memory allocated is a function of various input parameters such as vertex count and index count. By default memory is allocated using `operator new` and `operator delete`; if these operators are overloaded by the application, the overloads will be used instead. Alternatively it's possible to specify custom allocation/deallocation functions using `meshopt_setAllocator`, e.g.
```c++
meshopt_setAllocator(malloc, free);
```
> Note that the library expects the allocation function to either throw in case of out-of-memory (in which case the exception will propagate to the caller) or abort, so technically the use of `malloc` above isn't safe. If you want to handle out-of-memory errors without using C++ exceptions, you can use `setjmp`/`longjmp` instead.
Vertex and index decoders (`meshopt_decodeVertexBuffer`, `meshopt_decodeIndexBuffer`, `meshopt_decodeIndexSequence`) do not allocate memory and work completely within the buffer space provided via arguments.
All functions have bounded stack usage that does not exceed 32 KB for any algorithms.
## License
This library is available to anybody free of charge, under the terms of MIT License (see LICENSE.md).

View File

@@ -7,3 +7,5 @@ coverage:
ignore:
- demo
- extern
- tools

View File

@@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/meshoptimizerTargets.cmake")
check_required_components(meshoptimizer)

2
third_party/meshoptimizer/demo/ansi.c vendored Normal file
View File

@@ -0,0 +1,2 @@
/* This file makes sure the library can be used by C89 code */
#include "../src/meshoptimizer.h"

View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>meshoptimizer - demo</title>
<script src="https://preview.babylonjs.com/babylon.js"></script>
<script src="https://preview.babylonjs.com/loaders/babylon.glTF2FileLoader.js"></script>
<style>
html, body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#renderCanvas {
width: 100%;
height: 100%;
touch-action: none;
}
</style>
</head>
<body>
<canvas id="renderCanvas"></canvas>
<script>
// Optional - by default Babylon.JS will use bundled decoder module
BABYLON.GLTF2.Loader.Extensions.EXT_meshopt_compression.DecoderPath = '../js/meshopt_decoder.module.js';
var canvas = document.getElementById("renderCanvas");
var createScene = function () {
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0.8, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, false);
BABYLON.SceneLoader.Append("", "pirate.glb", scene, function (newMeshes) {
scene.activeCamera = null;
scene.createDefaultCameraOrLight(true);
scene.activeCamera.attachControl(canvas, false);
scene.activeCamera.alpha = Math.PI / 2;
});
return scene;
}
var engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
var scene = createScene();
engine.runRenderLoop(function () {
if (scene) {
scene.render();
}
});
// Resize
window.addEventListener("resize", function () {
engine.resize();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>meshoptimizer - demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #000;
color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 100;
display:block;
}
#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
</style>
</head>
<body>
<div id="info">
<a href="https://github.com/zeux/meshoptimizer" target="_blank" rel="noopener">meshoptimizer</a>
</div>
<script type="module">
import * as THREE from 'https://unpkg.com/three@0.122.0/build/three.module.js';
import { GLTFLoader } from 'https://unpkg.com/three@0.122.0/examples/jsm/loaders/GLTFLoader.js';
import { MeshoptDecoder } from '../js/meshopt_decoder.module.js';
var container;
var camera, scene, renderer, mixer, clock;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init()
{
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 100);
camera.position.y = 1.0;
camera.position.z = 3.0;
scene = new THREE.Scene();
scene.background = new THREE.Color(0x300a24);
var ambientLight = new THREE.AmbientLight(0xcccccc, 0.3);
scene.add(ambientLight);
var pointLight = new THREE.PointLight(0xffffff, 0.8);
pointLight.position.set(3, 3, 0);
camera.add(pointLight);
scene.add(camera);
var onProgress = function (xhr) {};
var onError = function (e) {
console.log(e);
};
var loader = new GLTFLoader();
loader.setMeshoptDecoder(MeshoptDecoder);
loader.load('pirate.glb', function (gltf) {
var bbox = new THREE.Box3().setFromObject(gltf.scene);
var scale = 2 / (bbox.max.y - bbox.min.y);
gltf.scene.scale.set(scale, scale, scale);
gltf.scene.position.set(0, 0, 0);
scene.add(gltf.scene);
mixer = new THREE.AnimationMixer(gltf.scene);
if (gltf.animations.length) {
mixer.clipAction(gltf.animations[gltf.animations.length - 1]).play();
}
}, onProgress, onError);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
clock = new THREE.Clock();
}
function onWindowResize()
{
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate()
{
if (mixer) {
mixer.update(clock.getDelta());
}
requestAnimationFrame(animate);
render();
}
function render()
{
renderer.render(scene, camera);
}
</script>
</body>
</html>

1249
third_party/meshoptimizer/demo/main.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More