Compare commits

..

1 Commits

Author SHA1 Message Date
Powei Feng
bd697f667b test load image 2024-05-15 23:37:49 -07:00
37 changed files with 223 additions and 639 deletions

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.52.0'
implementation 'com.google.android.filament:filament-android:1.51.8'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.52.0'
pod 'Filament', '~> 1.51.8'
```
### Snapshots

View File

@@ -7,7 +7,7 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.52.0
## v1.51.9
## v1.51.8

View File

@@ -244,25 +244,13 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphing(JNIEnv*, jcl
builder->morphing(targetCount);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderMorphingStandard(JNIEnv*, jclass,
jlong nativeBuilder, jlong nativeMorphTargetBuffer) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(morphTargetBuffer);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferAt(JNIEnv*, jclass,
jlong nativeBuilder, int level, int primitiveIndex, jlong nativeMorphTargetBuffer,
int offset, int count) {
RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder;
if (nativeMorphTargetBuffer) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
} else {
builder->morphing(level, primitiveIndex, offset, count);
}
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count);
}
extern "C" JNIEXPORT void JNICALL
@@ -338,14 +326,9 @@ Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferAt(JNIEn
jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex,
jlong nativeMorphTargetBuffer, jint offset, jint count) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
if (nativeMorphTargetBuffer) {
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
} else {
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, (size_t) offset, (size_t) count);
}
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer;
rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level,
(size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count);
}
extern "C" JNIEXPORT jint JNICALL

View File

@@ -524,7 +524,14 @@ public class RenderableManager {
}
/**
* Controls if the renderable has legacy vertex morphing targets, zero by default.
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
*
* <p>Filament supports two morphing modes: standard (default) and legacy.</p>
*
* <p>For standard morphing, A {@link MorphTargetBuffer} must be created and provided via
* {@link RenderableManager#setMorphTargetBufferAt}. Standard morphing supports up to
* <code>CONFIG_MAX_MORPH_TARGET_COUNT</code> morph targets.</p>
*
* For legacy morphing, the attached {@link VertexBuffer} must provide data in the
* appropriate {@link VertexBuffer.VertexAttribute} slots (<code>MORPH_POSITION_0</code> etc).
@@ -542,22 +549,6 @@ public class RenderableManager {
return this;
}
/**
* Controls if the renderable has vertex morphing targets, zero by default.
*
* <p>For standard morphing, A {@link MorphTargetBuffer} must be provided.
* Standard morphing supports up to
* <code>CONFIG_MAX_MORPH_TARGET_COUNT</code> morph targets.</p>
*
* <p>See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis
* to advance the animation.</p>
*/
@NonNull
public Builder morphing(@NonNull MorphTargetBuffer morphTargetBuffer) {
nBuilderMorphingStandard(mNativeBuilder, morphTargetBuffer.getNativeObject());
return this;
}
/**
* Specifies the morph target buffer for a primitive.
*
@@ -574,17 +565,6 @@ public class RenderableManager {
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, 0, offset, count);
return this;
}
/** @deprecated */
@Deprecated
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@NonNull MorphTargetBuffer morphTargetBuffer,
@@ -595,8 +575,10 @@ public class RenderableManager {
return this;
}
/** @deprecated */
@Deprecated
/**
* Utility method to specify morph target buffer for a primitive.
* For details, see the {@link RenderableManager.Builder#morphing}.
*/
@NonNull
public Builder morphing(@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@@ -705,16 +687,6 @@ public class RenderableManager {
*
* @see Builder#morphing
*/
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@IntRange(from = 0) int offset,
@IntRange(from = 0) int count) {
nSetMorphTargetBufferAt(mNativeObject, i, level, primitiveIndex, 0, offset, count);
}
/** @deprecated */
@Deprecated
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@@ -725,8 +697,10 @@ public class RenderableManager {
morphTargetBuffer.getNativeObject(), offset, count);
}
/** @deprecated */
@Deprecated
/**
* Utility method to change morph target buffer for the given primitive.
* For details, see the {@link RenderableManager#setMorphTargetBufferAt}.
*/
public void setMorphTargetBufferAt(@EntityInstance int i,
@IntRange(from = 0) int level,
@IntRange(from = 0) int primitiveIndex,
@@ -1032,7 +1006,6 @@ public class RenderableManager {
private static native int nBuilderSkinningBones(long nativeBuilder, int boneCount, Buffer bones, int remaining);
private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset);
private static native void nBuilderMorphing(long nativeBuilder, int targetCount);
private static native void nBuilderMorphingStandard(long nativeBuilder, long nativeMorphTargetBuffer);
private static native void nBuilderSetMorphTargetBufferAt(long nativeBuilder, int level, int primitiveIndex, long nativeMorphTargetBuffer, int offset, int count);
private static native void nBuilderEnableSkinningBuffers(long nativeBuilder, boolean enabled);
private static native void nBuilderFog(long nativeBuilder, boolean enabled);

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.52.0
VERSION_NAME=1.51.8
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -417,9 +417,7 @@ if (APPLE OR LINUX)
test/test_MissingRequiredAttributes.cpp
test/test_ReadPixels.cpp
test/test_BufferUpdates.cpp
test/test_Callbacks.cpp
test/test_MRT.cpp
test/test_PushConstants.cpp
test/test_LoadImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp

View File

@@ -117,7 +117,7 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
"The number of buffer objects that can be attached to a VertexBuffer must be "
"less than or equal to the maximum number of vertex attributes.");
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
/**

View File

@@ -144,7 +144,8 @@ DECL_DRIVER_API_N(setFrameScheduledCallback,
DECL_DRIVER_API_N(setFrameCompletedCallback,
backend::SwapChainHandle, sch,
backend::CallbackHandler*, handler,
utils::Invocable<void(void)>&&, callback)
backend::CallbackHandler::Callback, callback,
void*, user)
DECL_DRIVER_API_N(setPresentationTime,
int64_t, monotonic_clock_ns)

View File

@@ -55,18 +55,6 @@ struct MetalVertexBuffer;
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
class MetalPushConstantBuffer {
public:
void setPushConstant(PushConstantVariant value, uint8_t index);
bool isDirty() const { return mDirty; }
void setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage);
void clear();
private:
std::vector<PushConstantVariant> mPushConstants;
bool mDirty = false;
};
struct MetalContext {
explicit MetalContext(size_t metalFreedTextureListSize)
: texturesToDestroy(metalFreedTextureListSize) {}
@@ -121,8 +109,6 @@ struct MetalContext {
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
std::array<MetalPushConstantBuffer, Program::SHADER_TYPE_COUNT> currentPushConstants;
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of sampler groups we've finalized for the current render pass.

View File

@@ -153,68 +153,5 @@ bool isInRenderPass(MetalContext* context) {
return context->currentRenderPassEncoder != nil;
}
void MetalPushConstantBuffer::setPushConstant(PushConstantVariant value, uint8_t index) {
if (mPushConstants.size() <= index) {
mPushConstants.resize(index + 1);
mDirty = true;
}
if (UTILS_LIKELY(mPushConstants[index] != value)) {
mDirty = true;
mPushConstants[index] = value;
}
}
void MetalPushConstantBuffer::setBytes(id<MTLCommandEncoder> encoder, ShaderStage stage) {
constexpr size_t PUSH_CONSTANT_SIZE_BYTES = 4;
constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 26;
static char buffer[MAX_PUSH_CONSTANT_COUNT * PUSH_CONSTANT_SIZE_BYTES];
assert_invariant(mPushConstants.size() <= MAX_PUSH_CONSTANT_COUNT);
size_t bufferSize = PUSH_CONSTANT_SIZE_BYTES * mPushConstants.size();
for (size_t i = 0; i < mPushConstants.size(); i++) {
const auto& constant = mPushConstants[i];
std::visit(
[i](auto arg) {
if constexpr (std::is_same_v<decltype(arg), bool>) {
// bool push constants are converted to uints in MSL.
// We must ensure we write all the bytes for boolean values to work
// correctly.
uint32_t boolAsUint = arg ? 0x00000001 : 0x00000000;
*(reinterpret_cast<uint32_t*>(buffer + PUSH_CONSTANT_SIZE_BYTES * i)) =
boolAsUint;
} else {
*(decltype(arg)*)(buffer + PUSH_CONSTANT_SIZE_BYTES * i) = arg;
}
},
constant);
}
switch (stage) {
case ShaderStage::VERTEX:
[(id<MTLRenderCommandEncoder>)encoder setVertexBytes:buffer
length:bufferSize
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
break;
case ShaderStage::FRAGMENT:
[(id<MTLRenderCommandEncoder>)encoder setFragmentBytes:buffer
length:bufferSize
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
break;
case ShaderStage::COMPUTE:
[(id<MTLComputeCommandEncoder>)encoder setBytes:buffer
length:bufferSize
atIndex:PUSH_CONSTANT_BUFFER_INDEX];
break;
}
mDirty = false;
}
void MetalPushConstantBuffer::clear() {
mPushConstants.clear();
mDirty = false;
}
} // namespace backend
} // namespace filament

View File

@@ -243,10 +243,10 @@ void MetalDriver::setFrameScheduledCallback(
swapChain->setFrameScheduledCallback(handler, std::move(callback));
}
void MetalDriver::setFrameCompletedCallback(
Handle<HwSwapChain> sch, CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
void MetalDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
auto* swapChain = handle_cast<MetalSwapChain>(sch);
swapChain->setFrameCompletedCallback(handler, std::move(callback));
swapChain->setFrameCompletedCallback(handler, callback, user);
}
void MetalDriver::execute(std::function<void(void)> const& fn) noexcept {
@@ -1109,10 +1109,6 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->currentPolygonOffset = {0.0f, 0.0f};
mContext->finalizedSamplerGroups.clear();
for (auto& pc : mContext->currentPushConstants) {
pc.clear();
}
}
void MetalDriver::nextSubpass(int dummy) {}
@@ -1261,14 +1257,7 @@ void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
}
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
ASSERT_PRECONDITION(
isInRenderPass(mContext), "setPushConstant must be called inside a render pass.");
assert_invariant(static_cast<size_t>(stage) < mContext->currentPushConstants.size());
MetalPushConstantBuffer& pushConstants =
mContext->currentPushConstants[static_cast<size_t>(stage)];
pushConstants.setPushConstant(value, index);
}
backend::PushConstantVariant value) {}
void MetalDriver::insertEventMarker(const char* string, uint32_t len) {
@@ -1876,14 +1865,6 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t inst
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
// Update push constants.
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
auto& pushConstants = mContext->currentPushConstants[i];
if (UTILS_UNLIKELY(pushConstants.isDirty())) {
pushConstants.setBytes(mContext->currentRenderPassEncoder, static_cast<ShaderStage>(i));
}
}
auto primitive = handle_cast<MetalRenderPrimitive>(mContext->currentRenderPrimitive);
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;

View File

@@ -75,7 +75,7 @@ public:
void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback);
void setFrameCompletedCallback(
CallbackHandler* handler, utils::Invocable<void(void)>&& callback);
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
// For CAMetalLayer-backed SwapChains, presents the drawable or schedules a
// FrameScheduledCallback.
@@ -119,12 +119,13 @@ private:
// PresentCallable object.
struct {
CallbackHandler* handler = nullptr;
std::shared_ptr<FrameScheduledCallback> callback = nullptr;
FrameScheduledCallback callback = {};
} frameScheduled;
struct {
CallbackHandler* handler = nullptr;
std::shared_ptr<utils::Invocable<void(void)>> callback = nullptr;
CallbackHandler::Callback callback = {};
void* user = nullptr;
} frameCompleted;
};

View File

@@ -224,13 +224,14 @@ void MetalSwapChain::ensureDepthStencilTexture() {
void MetalSwapChain::setFrameScheduledCallback(
CallbackHandler* handler, FrameScheduledCallback&& callback) {
frameScheduled.handler = handler;
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
frameScheduled.callback = std::move(callback);
}
void MetalSwapChain::setFrameCompletedCallback(
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
void MetalSwapChain::setFrameCompletedCallback(CallbackHandler* handler,
CallbackHandler::Callback callback, void* user) {
frameCompleted.handler = handler;
frameCompleted.callback = std::make_shared<utils::Invocable<void(void)>>(std::move(callback));
frameCompleted.callback = callback;
frameCompleted.user = user;
}
void MetalSwapChain::present() {
@@ -303,17 +304,17 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
assert_invariant(drawable);
struct Callback {
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
Callback(FrameScheduledCallback&& callback, id<CAMetalDrawable> drawable,
MetalDriver* driver)
: f(callback), data(PresentDrawableData::create(drawable, driver)) {}
std::shared_ptr<FrameScheduledCallback> f;
: f(std::move(callback)), data(PresentDrawableData::create(drawable, driver)) {}
FrameScheduledCallback f;
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
std::unique_ptr<PresentDrawableData> data;
static void func(void* user) {
auto* const c = reinterpret_cast<Callback*>(user);
PresentDrawableData* presentDrawableData = c->data.release();
PresentCallable presentCallable(presentDrawable, presentDrawableData);
c->f->operator()(presentCallable);
c->f(presentCallable);
delete c;
}
};
@@ -321,7 +322,7 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
// This callback pointer will be captured by the block. Even if the scheduled handler is never
// called, the unique_ptr will still ensure we don't leak memory.
__block auto callback =
std::make_unique<Callback>(frameScheduled.callback, drawable, context.driver);
std::make_unique<Callback>(std::move(frameScheduled.callback), drawable, context.driver);
backend::CallbackHandler* handler = frameScheduled.handler;
MetalDriver* driver = context.driver;
@@ -336,25 +337,13 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
return;
}
struct Callback {
Callback(std::shared_ptr<utils::Invocable<void(void)>> callback) : f(callback) {}
std::shared_ptr<utils::Invocable<void(void)>> f;
static void func(void* user) {
auto* const c = reinterpret_cast<Callback*>(user);
c->f->operator()();
delete c;
}
};
// This callback pointer will be captured by the block. Even if the completed handler is never
// called, the unique_ptr will still ensure we don't leak memory.
__block auto callback = std::make_unique<Callback>(frameCompleted.callback);
CallbackHandler* handler = frameCompleted.handler;
void* user = frameCompleted.user;
CallbackHandler::Callback callback = frameCompleted.callback;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addCompletedHandler:^(id<MTLCommandBuffer> cb) {
Callback* user = callback.release();
driver->scheduleCallback(handler, user, &Callback::func);
driver->scheduleCallback(handler, user, callback);
}];
}

View File

@@ -43,8 +43,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
// ------------------------------------------------------
// 0 Zero buffer (placeholder vertex buffer) 1
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
// 26 Push constants 1
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 31
@@ -54,8 +53,7 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
// Bindings Buffer name Count
// ------------------------------------------------------
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT
// 26 Push constants 1
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 18

View File

@@ -59,7 +59,7 @@ void NoopDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
}
void NoopDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
}

View File

@@ -3459,7 +3459,7 @@ void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
}
void OpenGLDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
DEBUG_MARKER()
}

View File

@@ -398,7 +398,7 @@ void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
}
void VulkanDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
}
void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {

View File

@@ -303,7 +303,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
VkResult status = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
// Fence hasn't been reached. Try waiting again.
if (status != VK_SUCCESS) {
utils::slog.e << "Failed to wait for readPixels fence" << utils::io::endl;
utils::slog.e << "Failed to wait for readPixels fence=" << status << utils::io::endl;
return;
}

View File

@@ -22,11 +22,14 @@
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
#include <spirv-tools/libspirv.hpp>
#include "../src/GLSLPostProcessor.h"
#include "builtinResource.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <iostream>
#include <utility>
@@ -36,6 +39,7 @@ namespace test {
using namespace glslang;
using namespace spirv_cross;
using namespace spvtools;
using namespace filament::backend;
@@ -128,6 +132,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
bool ok = tShader.parse(&DefaultTBuiltInResource, version, false, msg);
utils::slog.e <<"-----------" << shaderCString << utils::io::endl;
if (!ok) {
std::cerr << "ERROR: Unable to parse " <<
(stage == ShaderStage::VERTEX ? "vertex" : "fragment") << " shader:" << std::endl;
@@ -146,6 +152,13 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
GlslangToSpv(*program.getIntermediate(language), spirv);
SpirvTools const tools(SPV_ENV_UNIVERSAL_1_3);
std::string disassembly;
bool rr = tools.Disassemble(spirv, &disassembly);
utils::slog.e <<"dissambly=" << rr << "\n" <<
disassembly << utils::io::endl;
std::string result;
assert_invariant(backend == Backend::OPENGL ||

View File

@@ -1,122 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BackendTest.h"
using namespace filament;
using namespace filament::backend;
namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
auto& api = getDriverApi();
// Create a SwapChain.
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
// headless) so we obtain a drawable.
auto swapChain = createSwapChain();
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
int callbackCountA = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
callable();
callbackCountA++;
});
// Render the first frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, {});
api.endRenderPass(0);
api.commit(swapChain);
api.endFrame(0);
// Render the next frame. The same callback should be called.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, {});
api.endRenderPass(0);
api.commit(swapChain);
api.endFrame(0);
// Now switch out the callback.
int callbackCountB = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountB](PresentCallable callable) {
callable();
callbackCountB++;
});
// Render one final frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, {});
api.endRenderPass(0);
api.commit(swapChain);
api.endFrame(0);
api.finish();
executeCommands();
getDriver().purge();
EXPECT_EQ(callbackCountA, 2);
EXPECT_EQ(callbackCountB, 1);
}
TEST_F(BackendTest, FrameCompletedCallback) {
auto& api = getDriverApi();
// Create a SwapChain.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
int callbackCountA = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
[&callbackCountA]() { callbackCountA++; });
// Render the first frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
// Render the next frame. The same callback should be called.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
// Now switch out the callback.
int callbackCountB = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
[&callbackCountB]() { callbackCountB++; });
// Render one final frame.
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
api.finish();
executeCommands();
getDriver().purge();
EXPECT_EQ(callbackCountA, 2);
EXPECT_EQ(callbackCountB, 1);
}
} // namespace test

View File

@@ -23,11 +23,22 @@
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/SamplerGroup.h"
#include <utils/Log.h>
#include <utils/Hash.h>
#include <math/half.h>
#include <fstream>
#include <vector>
#ifndef IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
using namespace image;
#endif
using namespace filament;
using namespace filament::backend;
@@ -56,7 +67,7 @@ layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} test_tex;
layout(binding = 0, set = 1) uniform {samplerType} test_tex;
void main() {
vec2 fbsize = vec2(512);
@@ -109,6 +120,49 @@ void main() {
)");
}
static void readScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, size_t const readWidth,
size_t const readHeight, std::function<void(void*)> output) {
size_t const size = readWidth * readHeight * 4;
void* buffer = calloc(1, size);
struct ReadPixelUserData {
std::function<void(void*)> onRead;
};
ReadPixelUserData* userdata = new ReadPixelUserData{output};
auto cb = [](void* buffer, size_t size, void* user) {
ReadPixelUserData* data = (ReadPixelUserData*) user;
data->onRead(buffer);
free(buffer);
delete data;
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
userdata);
dapi.readPixels(rt, 0, 0, readWidth, readHeight, std::move(pb));
}
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, size_t const readWidth,
size_t const readHeight, test::Backend backend, std::string fname = "") {
size_t const size = readWidth * readHeight * 4;
if (fname.empty()) {
std::string backendStr = "gl";
if (backend == test::Backend::METAL) {
backendStr = "mtl";
} else if (backend == test::Backend::VULKAN) {
backendStr = "vk";
}
fname = "feedback_" + backendStr + "_" + ".png";
}
utils::slog.e << "writing: " << fname << utils::io::endl;
readScreenshot(dapi, rt, readWidth, readHeight, [w = readWidth, h = readHeight, size, fname](void* buffer) {
uint32_t const* texels = (uint32_t*) buffer;
auto hashResutl = utils::hash::murmur3(texels, size / 4, 0);
#ifndef IOS
LinearImage image(w, h, 4);
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm(fname.c_str(), std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", fname.c_str());
#endif
});
}
namespace test {
@@ -246,21 +300,22 @@ TEST_F(BackendTest, UpdateImage2D) {
std::vector<TestCase> testCases;
// Test basic upload.
testCases.emplace_back("RGBA, UBYTE -> RGBA8", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8);
// Test format conversion.
// TODO: Vulkan crashes with `Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)'
testCases.emplace_back("RGBA, FLOAT -> RGBA16F", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F);
/*
// Test texture formats not all backends support natively.
// TODO: Vulkan crashes with "VK_FORMAT_R32G32B32_SFLOAT is not supported"
testCases.emplace_back("RGB, FLOAT -> RGB32F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F);
testCases.emplace_back("RGB, FLOAT -> RGB16F", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB16F);
// Test packed format uploads.
// packed Test format uploads.
// TODO: Vulkan crashes with "Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)"
testCases.emplace_back("RGBA, UINT_2_10_10_10_REV -> RGB10_A2", PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV, TextureFormat::RGB10_A2);
testCases.emplace_back("RGBA, UINT_2_10_10_10_REV -> RGB10_A2", PixelDataFormat::RGBA, PixelDataType::UINT_2_10_10_10_REV, TextureFormat::RGB10_A2);gitgg
testCases.emplace_back("RGB, UINT_10F_11F_11F_REV -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::UINT_10F_11F_11F_REV, TextureFormat::R11F_G11F_B10F);
testCases.emplace_back("RGB, HALF -> R11F_G11F_B10F", PixelDataFormat::RGB, PixelDataType::HALF, TextureFormat::R11F_G11F_B10F);
@@ -281,8 +336,9 @@ TEST_F(BackendTest, UpdateImage2D) {
// TODO: Vulkan crashes with "Offsets not yet supported"
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 0u, true);
testCases.emplace_back("RGBA, FLOAT -> RGBA16F (subregions)", PixelDataFormat::RGBA, PixelDataType::FLOAT, TextureFormat::RGBA16F, 0u, true);
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions, buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
testCases.emplace_back("RGBA, UBYTE -> RGBA8 (subregions, buffer padding)", PixelDataFormat::RGBA, PixelDataType::UBYTE, TextureFormat::RGBA8, 64u, true);
testCases.emplace_back("RGB, FLOAT -> RGB32F (subregions, buffer padding)", PixelDataFormat::RGB, PixelDataType::FLOAT, TextureFormat::RGB32F, 64u, true);
*/
auto& api = getDriverApi();
@@ -305,6 +361,7 @@ TEST_F(BackendTest, UpdateImage2D) {
std::string const fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
utils::slog.e <<"sampler=" << fragment << utils::io::endl;
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 };
@@ -348,6 +405,8 @@ TEST_F(BackendTest, UpdateImage2D) {
api.commit(swapChain);
api.endFrame(0);
// dumpScreenshot(api, defaultRenderTarget, 512, 512, test::Backend::VULKAN, t.name);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(defaultRenderTarget);

View File

@@ -1,168 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BackendTest.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
namespace test {
using namespace filament;
using namespace filament::backend;
static constexpr struct {
size_t TRIANGLE_HIDE = 0;
size_t TRIANGLE_SCALE = 1;
size_t TRIANGLE_OFFSET_X = 2;
size_t TRIANGLE_OFFSET_Y = 3;
size_t RED = 0;
size_t GREEN = 2;
size_t BLUE = 3;
} pushConstantIndex;
static const char* const triangleVs = R"(#version 450 core
layout(push_constant) uniform Constants {
bool hideTriangle;
float triangleScale;
float triangleOffsetX;
float triangleOffsetY;
} pushConstants;
layout(location = 0) in vec4 mesh_position;
void main() {
if (pushConstants.hideTriangle) {
// Test that bools are written correctly. All bits must be 0 if the bool is false.
gl_Position = vec4(0.0);
return;
}
gl_Position = vec4(mesh_position.xy * pushConstants.triangleScale +
vec2(pushConstants.triangleOffsetX, pushConstants.triangleOffsetY), 0.0, 1.0);
#if defined(TARGET_VULKAN_ENVIRONMENT)
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
gl_Position.y = -gl_Position.y;
#endif
})";
static const char* const triangleFs = R"(#version 450 core
layout(push_constant) uniform Constants {
float red;
bool padding; // test correct bool padding
float green;
float blue;
} pushConstants;
precision mediump int; precision highp float;
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(pushConstants.red, pushConstants.green, pushConstants.blue, 1.0);
})";
TEST_F(BackendTest, PushConstants) {
auto& api = getDriverApi();
api.startCapture(0);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = api.createProgram(std::move(p));
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
TrianglePrimitive triangle(api);
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0;
params.viewport = { 0, 0, 512, 512 };
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, params);
// Set the push constants to scale the triangle in half
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_HIDE, false);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_SCALE, 0.5f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, 0.0f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, 0.0f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 0.25f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 1.0f);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
// Draw another triangle, transposed to the upper-right.
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, 0.5f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 1.00f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 0.25f);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
// Draw a final triangle, transposed to the lower-left.
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_X, -0.5f);
api.setPushConstant(ShaderStage::VERTEX, pushConstantIndex.TRIANGLE_OFFSET_Y, -0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.RED, 0.5f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.GREEN, 0.25f);
api.setPushConstant(ShaderStage::FRAGMENT, pushConstantIndex.BLUE, 1.00f);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
readPixelsAndAssertHash("pushConstants", 512, 512, renderTarget, 1957275826, true);
api.commit(swapChain);
api.endFrame(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
api.stopCapture(0);
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test

View File

@@ -464,11 +464,16 @@ public:
Builder& boneIndicesAndWeights(size_t primitiveIndex,
utils::FixedCapacityVector<
utils::FixedCapacityVector<math::float2>> indicesAndWeightsVector) noexcept;
/**
* Controls if the renderable has legacy vertex morphing targets, zero by default. This is
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
*
* Filament supports two morphing modes: standard (default) and legacy.
*
* For standard morphing, A MorphTargetBuffer must be created and provided via
* RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to
* \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
@@ -481,52 +486,26 @@ public:
Builder& morphing(size_t targetCount) noexcept;
/**
* Controls if the renderable has vertex morphing targets, zero by default. This is
* required to enable GPU morphing.
* Specifies the morph target buffer for a primitive.
*
* Filament supports two morphing modes: standard (default) and legacy.
* The morph target buffer must have an associated renderable and geometry. Two conditions
* must be met:
* 1. The number of morph targets in the buffer must equal the renderable's morph target
* count.
* 2. The vertex count of each morph target must equal the geometry's vertex count.
*
* For standard morphing, A MorphTargetBuffer must be provided.
* Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets.
*
* For legacy morphing, the attached VertexBuffer must provide data in the
* appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only
* supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must
* be enabled on the material definition: either via the legacyMorphing material attribute
* or by calling filamat::MaterialBuilder::useLegacyMorphing().
*
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param morphTargetBuffer specifies the morph target buffer
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer,
size_t offset, size_t count) noexcept;
/**
* @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead
*/
inline Builder& morphing(uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
/**
* Specifies the the range of the MorphTargetBuffer to use with this primitive.
*
* @param level the level of detail (lod), only 0 can be specified
* @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor
* @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices)
* @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3)
*/
Builder& morphing(uint8_t level, size_t primitiveIndex,
size_t offset, size_t count) noexcept;
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept;
/**
* Sets the drawing order for blended primitives. The drawing order is either global or
@@ -786,19 +765,14 @@ public:
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset, size_t count);
/** @deprecated */
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count);
/** @deprecated */
/**
* Utility method to change a MorphTargetBuffer to the given primitive
*/
inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer);
/**
* Get a MorphTargetBuffer to the given primitive or null if it doesn't exist.
@@ -932,6 +906,20 @@ protected:
~RenderableManager() = default;
};
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
return morphing(level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex,
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) {
setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0,
morphTargetBuffer->getVertexCount());
}
template<typename VECTOR, typename INDEX, typename, typename>
Box RenderableManager::computeAABB(
VECTOR const* UTILS_NONNULL vertices,

View File

@@ -684,7 +684,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
cmd.info.indexCount = primitive.getIndexCount();
cmd.info.type = primitive.getPrimitiveType();
cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle();
cmd.info.morphingOffset = morphTargets.offset;
if constexpr (isColorPass) {
RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces);
@@ -1030,9 +1029,6 @@ void RenderPass::Executor::execute(FEngine& engine,
rebindPipeline = false;
currentPipeline = pipeline;
driver.bindPipeline(pipeline);
driver.setPushConstant(ShaderStage::VERTEX,
+PushConstantIds::MORPHING_BUFFER_OFFSET, int32_t(info.morphingOffset));
}
if (info.rph != currentPrimitiveHandle) {

View File

@@ -251,7 +251,6 @@ public:
uint32_t indexCount; // 4 bytes
uint32_t index = 0; // 4 bytes
backend::SamplerGroupHandle morphTargetBuffer; // 4 bytes
uint32_t morphingOffset = 0; // 4 bytes
backend::RasterState rasterState; // 4 bytes
@@ -262,7 +261,7 @@ public:
bool hasMorphing : 1; // 1 bit
bool hasHybridInstancing : 1; // 1 bit
uint32_t rfu[2]; // 16 bytes
uint32_t rfu[3]; // 16 bytes
};
static_assert(sizeof(PrimitiveInfo) == 56);

View File

@@ -164,11 +164,6 @@ void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
downcast(morphTargetBuffer), offset, count);
}
void RenderableManager::setMorphTargetBufferAt(
Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) {
downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count);
}
MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex);

View File

@@ -86,7 +86,6 @@ struct RenderableManager::BuilderDetails {
RenderableManager::Builder::GeometryType mGeometryType : 2;
size_t mSkinningBoneCount = 0;
size_t mMorphTargetCount = 0;
FMorphTargetBuffer* mMorphTargetBuffer = nullptr;
Bone const* mUserBones = nullptr;
mat4f const* mUserBoneMatrices = nullptr;
FSkinningBuffer* mSkinningBuffer = nullptr;
@@ -274,18 +273,6 @@ RenderableManager::Builder& RenderableManager::Builder::morphing(size_t targetCo
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(
MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept {
mImpl->mMorphTargetBuffer = downcast(morphTargetBuffer);
mImpl->mMorphTargetCount = morphTargetBuffer->getCount();
return *this;
}
RenderableManager::Builder& RenderableManager::Builder::morphing(
uint8_t level, size_t primitiveIndex, size_t offset, size_t count) noexcept {
return morphing(level, primitiveIndex, mImpl->mMorphTargetBuffer, offset, count);
}
RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex,
MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept {
std::vector<Entry>& entries = mImpl->mEntries;
@@ -659,14 +646,10 @@ void FRenderableManager::create(
// Create and initialize all needed MorphTargets.
// It's required to avoid branches in hot loops.
FMorphTargetBuffer* morphTargetBuffer = builder->mMorphTargetBuffer;
if (morphTargetBuffer == nullptr) {
morphTargetBuffer = mEngine.getDummyMorphTargetBuffer();
}
MorphTargets* const morphTargets = new MorphTargets[entryCount];
std::generate_n(morphTargets, entryCount,
[morphTargetBuffer]() -> MorphTargets {
return { morphTargetBuffer, 0, 0 };
[dummy = mEngine.getDummyMorphTargetBuffer()]() -> MorphTargets {
return { dummy, 0, 0 };
});
mManager[ci].morphTargets = { morphTargets, size_type(entryCount) };
@@ -966,13 +949,15 @@ void FRenderableManager::setMorphWeights(Instance instance, float const* weights
void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) {
assert_invariant(offset == 0 && "Offset not yet supported.");
assert_invariant(count == morphTargetBuffer->getVertexCount() && "Count not yet supported.");
if (instance) {
assert_invariant(morphTargetBuffer);
MorphWeights const& morphWeights = mManager[instance].morphWeights;
ASSERT_PRECONDITION(morphWeights.count == count,
ASSERT_PRECONDITION(morphWeights.count == morphTargetBuffer->getCount(),
"Only %d morph targets can be set (count=%d)",
morphWeights.count, count);
morphWeights.count, morphTargetBuffer->getCount());
Slice<MorphTargets>& morphTargets = getMorphTargets(instance, level);
if (primitiveIndex < morphTargets.size()) {
@@ -982,17 +967,6 @@ void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level
}
}
void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex, size_t offset, size_t count) {
if (instance) {
Slice<MorphTargets>& morphTargets = getMorphTargets(instance, level);
if (primitiveIndex < morphTargets.size()) {
setMorphTargetBufferAt(instance, level,
primitiveIndex, morphTargets[primitiveIndex].buffer, offset, count);
}
}
}
MorphTargetBuffer* FRenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level,
size_t primitiveIndex) const noexcept {
if (instance) {

View File

@@ -157,8 +157,6 @@ public:
void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset);
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count);
void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex,
size_t offset, size_t count);
MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept;
size_t getMorphTargetCount(Instance instance) const noexcept;

View File

@@ -153,7 +153,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
auto size = getSize<VertexAttribute::POSITION>(count);
auto size = getSize<VertexAttribute::POSITION>(mVertexCount);
ASSERT_PRECONDITION(targetIndex < mCount,
"%d target index must be < %d", targetIndex, mCount);
@@ -177,7 +177,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex,
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
auto size = getSize<VertexAttribute::POSITION>(count);
auto size = getSize<VertexAttribute::POSITION>(mVertexCount);
ASSERT_PRECONDITION(targetIndex < mCount,
"%d target index must be < %d", targetIndex, mCount);
@@ -200,7 +200,7 @@ void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t targetIndex,
"MorphTargetBuffer (size=%lu) overflow (count=%u, offset=%u)",
(unsigned)mVertexCount, (unsigned)count, (unsigned)offset);
const auto size = getSize<VertexAttribute::TANGENTS>(count);
const auto size = getSize<VertexAttribute::TANGENTS>(mVertexCount);
ASSERT_PRECONDITION(targetIndex < mCount,
"%d target index must be < %d", targetIndex, mCount);
@@ -226,8 +226,8 @@ void FMorphTargetBuffer::updateDataAt(backend::DriverApi& driver,
size_t const xoffset = offset % MAX_MORPH_TARGET_BUFFER_WIDTH;
size_t const textureWidth = getWidth(mVertexCount);
size_t const alignment = ((textureWidth - xoffset) % textureWidth);
size_t const lineCount = (count > alignment) ? (count - alignment) / textureWidth : 0;
size_t const lastLineCount = (count > alignment) ? (count - alignment) % textureWidth : 0;
size_t const lineCount = (count - alignment) / textureWidth;
size_t const lastLineCount = (count - alignment) % textureWidth;
// 'out' buffer is going to be used up to 3 times, so for simplicity we use a shared_buffer
// to manage its lifetime. One side effect of this is that the callbacks below will allocate

View File

@@ -79,11 +79,24 @@ bool FSwapChain::isFrameScheduledCallbackSet() const noexcept {
return mFrameScheduledCallbackIsSet;
}
void FSwapChain::setFrameCompletedCallback(
backend::CallbackHandler* handler, FrameCompletedCallback&& callback) noexcept {
using namespace std::placeholders;
auto boundCallback = std::bind(std::move(callback), this);
mEngine.getDriverApi().setFrameCompletedCallback(mHwSwapChain, handler, std::move(boundCallback));
void FSwapChain::setFrameCompletedCallback(backend::CallbackHandler* handler,
utils::Invocable<void(SwapChain*)>&& callback) noexcept {
struct Callback {
utils::Invocable<void(SwapChain*)> f;
SwapChain* s;
static void func(void* user) {
auto* const c = reinterpret_cast<Callback*>(user);
c->f(c->s);
delete c;
}
};
if (callback) {
auto* const user = new(std::nothrow) Callback{ std::move(callback), this };
mEngine.getDriverApi().setFrameCompletedCallback(
mHwSwapChain, handler, &Callback::func, static_cast<void*>(user));
} else {
mEngine.getDriverApi().setFrameCompletedCallback(mHwSwapChain, nullptr, nullptr, nullptr);
}
}
bool FSwapChain::isSRGBSwapChainSupported(FEngine& engine) noexcept {

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.52.0"
spec.version = "1.51.8"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.52.0/filament-v1.52.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.8/filament-v1.51.8-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 52;
static constexpr size_t MATERIAL_VERSION = 51;
/**
* Supported shading models

View File

@@ -72,7 +72,7 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp)
};
enum class PushConstantIds : uint8_t {
enum class PushConstantIds {
MORPHING_BUFFER_OFFSET = 0,
};
@@ -143,8 +143,6 @@ struct utils::EnableIntegerOperators<filament::SamplerBindingPoints> : public st
template<>
struct utils::EnableIntegerOperators<filament::ReservedSpecializationConstants> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::PushConstantIds> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::PostProcessVariant> : public std::true_type {};
template<>

View File

@@ -255,17 +255,6 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl,
mslCompiler.add_msl_resource_binding(argBufferBinding);
}
// Bind push constants to [buffer(26)]
MSLResourceBinding pushConstantBinding;
// the baseType doesn't matter, but can't be UNKNOWN
pushConstantBinding.basetype = SPIRType::BaseType::Struct;
pushConstantBinding.stage = executionModel;
pushConstantBinding.desc_set = kPushConstDescSet;
pushConstantBinding.binding = kPushConstBinding;
pushConstantBinding.count = 1;
pushConstantBinding.msl_buffer = 26;
mslCompiler.add_msl_resource_binding(pushConstantBinding);
auto updateResourceBindingDefault = [executionModel, &mslCompiler](const auto& resource) {
auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding);

View File

@@ -54,6 +54,7 @@ struct App {
Skybox* skybox;
Entity renderable;
MorphTargetBuffer *mt1;
MorphTargetBuffer *mt2;
};
struct Vertex {
@@ -173,7 +174,12 @@ int main(int argc, char** argv) {
.build(*engine);
app.mt1 = MorphTargetBuffer::Builder()
.vertexCount(9 * 2)
.vertexCount(9)
.count(3)
.build(*engine);
app.mt2 = MorphTargetBuffer::Builder()
.vertexCount(9)
.count(3)
.build(*engine);
@@ -184,12 +190,12 @@ int main(int argc, char** argv) {
app.mt1->setTangentsAt(*engine,1, targets_tan+3, 3, 0);
app.mt1->setTangentsAt(*engine,2, targets_tan+6, 3, 0);
app.mt1->setPositionsAt(*engine,0, targets_pos2, 3, 9);
app.mt1->setPositionsAt(*engine,1, targets_pos2+3, 3, 9);
app.mt1->setPositionsAt(*engine,2, targets_pos2+6, 3, 9);
app.mt1->setTangentsAt(*engine,0, targets_tan, 3, 9);
app.mt1->setTangentsAt(*engine,1, targets_tan+3, 3, 9);
app.mt1->setTangentsAt(*engine,2, targets_tan+6, 3, 9);
app.mt2->setPositionsAt(*engine,0, targets_pos2, 3, 0);
app.mt2->setPositionsAt(*engine,1, targets_pos2+3, 3, 0);
app.mt2->setPositionsAt(*engine,2, targets_pos2+6, 3, 0);
app.mt2->setTangentsAt(*engine,0, targets_tan, 3, 0);
app.mt2->setTangentsAt(*engine,1, targets_tan+3, 3, 0);
app.mt2->setTangentsAt(*engine,2, targets_tan+6, 3, 0);
app.renderable = EntityManager::get().create();
@@ -203,8 +209,8 @@ int main(int argc, char** argv) {
.receiveShadows(false)
.castShadows(false)
.morphing(3)
.morphing(0,0,app.mt1, 0, app.mt1->getCount())
.morphing(0,1,app.mt1, 9, app.mt1->getCount())
.morphing(0,0,app.mt1)
.morphing(0,1,app.mt2)
.build(*engine, app.renderable);
scene->addEntity(app.renderable);
@@ -220,6 +226,7 @@ int main(int argc, char** argv) {
engine->destroy(app.vb);
engine->destroy(app.ib);
engine->destroy(app.mt1);
engine->destroy(app.mt2);
engine->destroyCameraComponent(app.camera);
utils::EntityManager::get().destroy(app.camera);
};

View File

@@ -146,8 +146,7 @@ void skinNormalTangent(inout vec3 n, inout vec3 t, const uvec4 ids, const vec4 w
#define MAX_MORPH_TARGET_BUFFER_WIDTH 2048
void morphPosition(inout vec4 p) {
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];
@@ -160,8 +159,7 @@ void morphPosition(inout vec4 p) {
void morphNormal(inout vec3 n) {
vec3 baseNormal = n;
int index = getVertexIndex() + pushConstants.morphingBufferOffset;
ivec3 texcoord = ivec3(index % MAX_MORPH_TARGET_BUFFER_WIDTH, index / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0);
int c = object_uniforms_morphTargetCount;
for (int i = 0; i < c; ++i) {
float w = morphingUniforms.weights[i][0];

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.52.0",
"version": "1.51.8",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",